1 // ==========================================================================
  2 // NodeGraph.NodeGraphDelegate
  3 //
  4 // License:  PaperCube is open source software released under 
  5 //           the MIT License (see license.js)
  6 // ==========================================================================
  7 
  8 require('core');
  9 
 10 /** @class NodeGraph.animator
 11 
 12   This is the NodeGraph delegate controller. Extend this to customize
 13   functionality for your NodeGraph. 
 14   
 15   @extends SC.Object
 16   @author Peter Bergstrom
 17   @version 1.0
 18   @copyright 2008-2009 Peter Bergström.
 19   @static
 20 */
 21 
 22 NodeGraph.NodeGraphDelegate = SC.Object.extend(
 23 /** @scope NodeGraph.NodeGraphDelegate */ {
 24 
 25   /**
 26     Called by the NodeGraph instance when a mouseDown is triggered.
 27     
 28     @param {DOM Event} evt The mouseDown event.
 29     @param {NodeGraph.NodeGraphView} The NodeGraph view.
 30     @param guid {String} The guid of the node.
 31   */
 32   nodeGraphDidMouseDown: function(evt, view, guid) {
 33     // Add customized code here.
 34   },
 35 
 36   /**
 37     Allow for additional customized setup of the NodeGraph view.
 38     
 39     @param {NodeGraph.NodeGraphView} nodeGraph The NodeGraph instance.
 40   */
 41   finishInitForGraph: function(nodeGraph) {
 42     // Add customized code here.
 43   },
 44   
 45   /**
 46     This left pixel offset for the view.
 47 
 48     @property {Integer}
 49     @default 0
 50   */
 51   leftOffset: 0,
 52   
 53   /**
 54     This top pixel offset for the view.
 55 
 56     @property {Integer}
 57     @default 0
 58   */
 59   topOffset: 0,
 60 
 61   /**
 62     This bottom pixel offset for the view.
 63 
 64     @property {Integer}
 65     @default 0
 66   */
 67   bottomOffset: 0,
 68 
 69   /**
 70     This right pixel offset for the view.
 71 
 72     @property {Integer}
 73     @default 0
 74   */
 75   rightOffset: 0,
 76 
 77   /**
 78     This is the default zoom value, 1 means that it is 1x zoom
 79 
 80     @property {Integer}
 81     @default 1
 82   */
 83   zoomValue: 1,
 84 
 85   /**
 86     This is the zoom step value of the slider.
 87 
 88     @property {Integer}
 89     @default 1
 90   */
 91   zoomStep: 1,
 92 
 93   /** 
 94     Min zoom value.
 95 
 96     @property {Integer}
 97     @default 1
 98   */
 99   zoomValueMin: 1,
100 
101   /**
102     Max zoom value.
103 
104     @property {Integer}
105     @default 30
106   */
107   zoomValueMax: 30,
108 
109   /**
110     boolean signifying that one is zoomed in. 
111 
112     @property {Boolean}
113     @default NO
114   */
115   isZooming: NO,
116 
117   /**
118     Canvas dimension height, the canvas is the underlying view.
119 
120     @property {Integer}
121     @default 0px
122   */
123   canvasHeight: 0,
124 
125   /**
126     Canvas dimension width, the canvas is the underlying view.
127 
128     @property {Integer}
129     @default 0px
130   */
131   canvasWidth: 0,
132 
133   /**
134     Canvas dimension top, the canvas is the underlying view.
135 
136     @property {Integer}
137     @default 0px
138   */
139   canvasTop: 0,
140 
141   /**
142     Canvas dimension left, the canvas is the underlying view.
143 
144     @property {Integer}
145     @default 0px
146   */
147   canvasLeft: 0,
148 
149   /**
150     Portal dimension height, the portal is the "mask" of the canvas. 
151 
152     @property {Integer}
153     @default 0px
154   */
155   portalHeight: 0,
156 
157   /**
158     Portal dimension width, the portal is the "mask" of the canvas. 
159 
160     @property {Integer}
161     @default 0px
162   */
163   portalWidth: 0,
164 
165   /**
166     x-axis offet percentage, this is used to position the canvas inside the portal.
167 
168     @property {Integer}
169     @default 0.5
170   */
171   percentageX: 0.5,
172 
173   /**
174     y-axis offet percentage, this is used to position the canvas inside the portal.
175 
176     @property {Integer}
177     @default 0.5
178   */
179   percentageY: 0.5,
180 
181   /**
182     Properties that are bound to the views.
183 
184     Values 
185     {
186       height: 0,
187       width: 0,
188       left: 0,
189       top: 0,
190       zoomValue: 0,
191       portalWidth: 0,
192       portalHeight: 0
193     }
194 
195     @property {Object}
196   */
197   displayProperties: {
198     height: 0,
199     width: 0,
200     left: 0,
201     top: 0,
202     zoomValue: 0,
203     portalWidth: 0,
204     portalHeight: 0
205   },
206 
207   /**
208     The scroll zoom cool off. Record scroll time to compare
209     with the _scrollCoolDownAmount.
210 
211     @property {Integer}
212   */
213   _scrollCoolDown: 0,
214 
215   /**
216     The amount to wait between scroll zoom events. 
217 
218     @property {Integer}
219     @default 200 milliseconds
220   */
221   _scrollCoolDownAmount: 200,
222 
223   /**
224     Zoom out to the min value.
225   */
226   zoomOutFull: function()
227   {
228     this.set("zoomValue", this.get('zoomValueMin'));
229   },
230 
231   /** 
232     Zoom out towards the min value.
233   */
234   zoomOut: function()
235   {
236     this.set('zoomValue', Math.max(this.get('zoomValueMin'), (this.get('zoomValue')-this.get('zoomStep'))));
237   },
238 
239   /** 
240     Zoom in towards the max value.
241   */
242   zoomIn: function()
243   {
244     this.set('zoomValue', Math.min(this.get('zoomValueMax'), (this.get('zoomValue')+this.get('zoomStep'))));
245   },
246 
247   /** 
248     When scolling, call this function to zoom in or out.
249 
250     If there was a scroll event within the time period specified by the _scrollCoolDownAmount variable, then
251     don't do anything.
252 
253     @param {DOM Event} evt The scroll event.
254   */
255   scrollZoom: function(evt)
256   {
257     // Check if the scroll event is within the cool down time, return.
258     if((Date.now()-this._scrollCoolDown) < this._scrollCoolDownAmount) return;
259 
260     // Save a new cool down time.
261     this._scrollCoolDown = Date.now();
262 
263     // Zoom in or out to the pointer location.
264     this.setZoomToPointerLocation(Event.pointerX(evt), Event.pointerY(evt), ((evt.detail ? evt.detail * -1 : evt.wheelDelta) > 0));
265   },
266 
267   /**
268     Zoom in or out to center around where the pointer location.
269 
270     @param xPos {Integer} The x position of the mouse pointer.
271     @param yPos {Integer} The y position of the mouse pointer.
272     @param zoomIn {boolean} If YES, zoom in, otherwise, zoom out.
273 
274     @returns {Boolean} Returns NO if there is an error.
275   */
276   setZoomToPointerLocation: function(xPos,yPos, zoomIn)
277   {
278     // Calculate the direction (-/+) the zoom step.
279     var zoomStep = zoomIn ? this.zoomStep : -this.zoomStep;
280 
281     // Calculate the desired zoomValue.
282     var zoomValue = this.zoomValue+zoomStep;
283 
284     // If the zoomValue is smaller or larger then the limits or the left view is visible, bail. 
285     if(zoomValue < this.zoomValueMin || zoomValue > this.zoomValueMax || this.leftViewShowing)
286     {
287       return NO;
288     }
289 
290     this.set("zoomValue", zoomValue);
291   },
292 
293   /**
294     Zoom to a set of coordinates.
295 
296     @param xDiff {Integer} The x position of the mouse pointer.
297     @param yDiff {Integer} The y position of the mouse pointer.
298 
299     @returns {Boolean} Returns NO if there is an error.
300   */
301   panToCoordinates: function(xDiff, yDiff)
302   {
303 
304     var zoomValue  = this.get('zoomValue');
305 
306     // If the zoomValue is equal to the zoomValueMin, bail. 
307     if(zoomValue === this.zoomValueMin)
308     {
309       return NO;
310     }
311 
312     // Calculate the x and y offsets and set it back to the zoomView.
313     var xPos = this.percentageX*this.canvasWidth-xDiff;
314     var yPos = this.percentageY*this.canvasHeight-yDiff;
315 
316     var pctX =  Math.max(1- (this.canvasWidth-xPos)/this.canvasWidth,0);
317     var pctY =  Math.max(1- (this.canvasHeight-yPos)/this.canvasHeight,0);
318 
319     this.set("percentageX", pctX);
320     this.set("percentageY", pctY);
321   },
322 
323   /**
324     Zoom in or out to center around a desired x and y percentage.
325 
326     @param pctX {Integer} The x position percentage.
327     @param pctY {Integer} The y position percentage.
328     @param zoomValue {Integer} The desired zoomValue.
329 
330     @returns {Boolean} Returns NO if there is an error.
331   */
332   zoomToLocation: function(pctX, pctY, zoomValue)
333   {
334     // If the zoomValue is smaller or larger then the limits, bail. 
335     if(zoomValue < this.zoomValueMin || zoomValue > this.zoomValueMax)
336     {
337       return NO;
338     }
339 
340     this.beginPropertyChanges();
341     this.set("zoomValue", zoomValue);
342     this.set("percentageX", pctX/2);
343     this.set("percentageY", pctY/2);
344     this.endPropertyChanges();
345 
346   },
347 
348   /**
349     Get window properties and set the portal dimensions then reposition the canvas.
350   */
351   getWindowProperties: function()
352   {
353     var wH = NodeGraph.windowHeight();
354     var wW = NodeGraph.windowWidth();
355     
356     this.set("portalHeight", (wH-this.get('topOffset')-this.get('bottomOffset')));
357     this.set("portalWidth", wW-this.get('leftOffset')-this.get('rightOffset'));
358     this.repositionCanvasDidChange();
359   },
360 
361   /**
362     Reposition the canvas inside the portal if needed, This is triggered if the percentages or the zoom value changes.
363 
364     @observes percentageX
365     @observes percentageY
366     @observes zoomValue
367   */
368   repositionCanvasDidChange: function()
369   {
370     // Calculate the canvas dimensions based on the portal dimensions and zoom value.
371     this.canvasHeight = parseInt(this.portalHeight*this.zoomValue, 0);
372     this.canvasWidth = parseInt(this.portalWidth*this.zoomValue, 0);
373 
374     // Default the top and left to 0.
375     var cTop = 0;
376     var cLeft = 0;
377 
378     if(this.zoomValue != this.zoomValueMin)
379     {
380       // Calculate the pixel value of the top and left of the views if the zoom value is not equal to the minimum.
381       var cTop = -(this.percentageY*this.canvasHeight);
382       var cLeft = -(this.percentageX*this.canvasWidth);
383 
384     }
385 
386     // Set the canvasTop and canvasLeft properties to the calculated values.
387     this.canvasTop = cTop;
388     this.canvasLeft = cLeft;
389 
390     // Set the displayProperities that the views will monitor to the newly calculated values.
391     this.set('displayProperties', {height: this.canvasHeight, width: this.canvasWidth, top: this.canvasTop, left: this.canvasLeft, zoomValue: this.zoomValue, portalWidth: this.portalWidth, portalHeight: this.portalHeight});
392 
393     this.set('isZooming', this.get('zoomValue') != 1);
394 
395   }.observes('percentageX', 'percentageY', 'zoomValue'),
396 
397   /**
398     Initalization function. Create the SVG elements for the fan.
399   */
400   init: function()
401   {
402     // Set up a resize observer on the window.
403     Event.observe(window, 'resize', this.getWindowProperties.bind(this));
404 
405     // Set up te scroll observer.
406     if(SC.isFireFox())
407     {
408       Element.observe(document.body, 'DOMMouseScroll', this.scrollZoom.bind(this));
409     }
410     else
411     {
412       Element.observe(document.body, 'mousewheel', this.scrollZoom.bind(this));
413     }
414 
415     // Set up the basic window properties.
416     this.getWindowProperties();
417     sc_super();
418   }  
419 });