1 /**
  2  * Source: Class.js
  3  * Copyright (c) 2013-2014 Oculus Info Inc.
  4  * @fileOverview Implements the ability to wrap any root layer as a vizlet.
  5  *
  6  */
  7 
  8 /**
  9  * @namespace
 10  * The API for wrapping any root layer as a vizlet for insertion into the DOM.
 11  */
 12 aperture.vizlet = (
 13 /** @private */
 14 function(namespace) {
 15 
 16 	var log = aperture.log;
 17 	
 18 	/**
 19 	 * Takes a layer constructor and generates a constructor for a Vizlet version of
 20 	 * the layer.  Unlike layers which can only be used as children of other layers,
 21 	 * Vizlets can be used as root objects and connected to a DOM element.
 22 	 *
 23 	 * @param {Function} layerConstructor
 24 	 *      The constructor function for the layer for which to generate a Vizlet view.
 25 	 *
 26 	 * @param {Function} [init]
 27 	 *      An optional initialization function which will be called where this
 28 	 *      will be the newly created layer.
 29 	 *
 30 	 * @returns {Function}
 31 	 *      A constructor function for a new Vizlet version of the supplied layer
 32 	 *
 33 	 * @name aperture.vizlet.make
 34 	 * @function
 35 	 */
 36 
 37 	var make = function( layerConstructor, init ) {
 38 
 39 		// Return a constructor function for the vizlet-layer that takes an id + a spec
 40 		// The constructed object will have all methods of the layer but will take an
 41 		// additional DOM element id on construction and have custom update/animate
 42 		// methods appropriate for a top-level vizlet
 43 		return function( spec, mappings ) {
 44 			var elem,
 45 				elemId,
 46 				// Create the node that will be given to the child layer
 47 				// on every render.
 48 				node = {
 49 						uid: 0,
 50 						width: 0,		// Set at render time
 51 						height: 0,		// Set at render time
 52 						position: [0,0],
 53 						anchorPoint: [0,0],
 54 						userData: {},
 55 						graphics : aperture.canvas.NO_GRAPHICS,
 56 						kids: []
 57 					};
 58 
 59 			// an actual element?
 60 			if (spec && spec.nodeType == 1) {
 61 				elem = spec;
 62 				spec = {};
 63 
 64 			// else must be an id, either a string or embedded in an object
 65 			} else {
 66 				if( aperture.util.isString(spec) ) {
 67 					// Given an element (id) directly instead of spec obj
 68 					elemId = spec;
 69 					spec = {};
 70 				} else {
 71 					if ( !spec || !spec.id ) {
 72 						return log.error('Cannot make a vizlet from object without an id.');
 73 					}
 74 					// Contained in a spec object
 75 					elemId = spec.id;
 76 				}
 77 
 78 				if (elemId === 'body') {
 79 					elem = document.body;
 80 				} else {
 81 					// TODO: we are taking id's but no longer allowing jquery selectors,
 82 					// so only id's, without hashes, should be allowed.
 83 					if (elemId.charAt(0) === '#') {
 84 						elemId = elemId.substr(1);
 85 					}
 86 					elem = document.getElementById(elemId);
 87 				}
 88 			}
 89 
 90 			var type = aperture.canvas.type( aperture.canvas.DIV_CANVAS );
 91 
 92 			// Extend layer creation specification to include reference to this
 93 			// and canvas
 94 			aperture.util.extend( spec, {
 95 				parent: null,
 96 				rootNode: node,
 97 				parentCanvas : new type( elem )
 98 			});
 99 
100 
101 			// Instantiate the vizlet
102 			// (Technically instantiating the layer that will look like a vizlet)
103 			var vizlet = new layerConstructor(spec, mappings);
104 
105 			// Make top-level update function (will replace update in layer.prototype)
106 			// This is the key difference between a layer (calls parent's update) and
107 			// a vizlet (has a DOM element from which nodes are derived).
108 			var originalLayerUpdate = vizlet.update;
109 
110 			/**
111 			 * @private
112 			 * 
113 			 * Updates layer graphics.
114 			 *
115 			 * @param {aperture.Layer.NodeSet} nodes
116 			 *      the scope of layer nodes to be updated.
117 			 *
118 			 * @param {aperture.Transition} [transition]
119 			 *      an optional animated transition to use to ease in the changes.
120 			 *
121 			 * @returns {this}
122 			 *      this vizlet
123 			 */
124 			vizlet.redraw = function( nodes, transition ) {
125 				if (log.isLogging(log.LEVEL.DEBUG)) {
126 					log.indent(0);
127 					log.debug('------------------------------');
128 					log.debug(' UPDATE');
129 					log.debug('------------------------------');
130 				}
131 				
132 				// The root has no data and the node is very basic.  The assumption is
133 				// that either the child layer or one of its children will eventually have
134 				// a data definition.
135 				// Set the node width/height (vizlet could have been resized since last render)
136 				node.width = elem.offsetWidth;
137 				node.height = elem.offsetHeight;
138 
139 				// Top level just provides a node with the container's canvas/size
140 				// but never indicates that it's changed etc.  Root layer will
141 				// manage its own data-based add/change/remove
142 				var changeSet = {
143 					updates: [],
144 					changed: [],
145 					removed: [],
146 					properties: null, // TODO: refactor out.
147 					rootSet: nodes,
148 					transition: transition
149 				};
150 				
151 				// Render this (ie the vizlet-ized layer)
152 				this.render( this.processChangeSet(changeSet) );
153 
154 				// flush all drawing ops.
155 				spec.parentCanvas.flush();
156 
157 				return this;
158 			};
159 
160 			if (init) {
161 				init.apply( vizlet, arguments );
162 			}
163 
164 			// Return the vizlet we created (not "this")
165 			return vizlet;
166 		};
167 	};
168 
169 
170 	namespace.make = make;
171 
172 	
173 	/**
174 	 * @class Plot is a {@link aperture.PlotLayer PlotLayer} vizlet, suitable for adding to the DOM.
175 	 *
176 	 * @augments aperture.PlotLayer
177 	 * @name aperture.Plot
178 	 *
179 	 * @constructor
180 	 * @param {String|Element} parent
181 	 *      A string specifying the id of the DOM element container for the vizlet or
182 	 *      a DOM element itself.
183 	 * @param {Object} [mappings]
184 	 *      An optional initial set of property mappings.
185 	 */
186 	aperture.Plot= make( aperture.PlotLayer );
187 
188 	
189 	return namespace;
190 }(aperture.vizlet || {}));
191