1 // ==========================================================================
  2 // Papercube.PapersPerYearView
  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 Papercube.PapersPerYearView
 11 
 12   This view shows the references or citations of a paper in chronological order,
 13   from it's publication year, into the past or the future. It uses nodes and
 14   edges to show the relationships. A paper only occurs once and there can be multiple
 15   edges going into a paper. This makes it almost the reverse of Circle View.
 16 
 17   Constants defined in core.js:
 18   
 19   var YEARLINE = 5;
 20   var YEARTEXT = 6;
 21   var PERYEAR_DEFAULT_SCALE = 20;
 22 
 23   @extends SC.View
 24   @extends NodeGraph.DragPanMixin
 25   @author Peter Bergstrom
 26   @version 1.0
 27   @copyright 2008-2009 Peter Bergström.
 28 */
 29 
 30 Papercube.PapersPerYearView = SC.View.extend(NodeGraph.DragPanMixin,
 31 /** @scope Papercube.PapersPerYearView.prototype */ {
 32 
 33   /**
 34     Bind the display properties from the canvasController.
 35 
 36     @property {Array}
 37     @binding "Papercube.canvasController.displayProperties"
 38   */
 39   displayPropertiesBinding: "Papercube.canvasController.displayProperties",
 40 
 41   /**
 42     Bind the view direction from the viewController.
 43 
 44     @property {String}
 45     @binding "Papercube.viewController.viewDirection"
 46   */
 47   viewDirectionBinding: "Papercube.viewController.viewDirection",
 48 
 49   /**
 50     Bind the cite threshold binding. Don't show papers with less cites. 
 51   
 52     @property {Integer}
 53     @binding "Papercube.papersPerYearController.citeThreshold"
 54   */
 55   citeThresholdBinding: "Papercube.papersPerYearController.citeThreshold",
 56   
 57   /**
 58     Cite threshold cached value. 
 59     
 60     @property {Integer}
 61     @default 1
 62   */
 63   _cached_citeThreshold: 1,
 64 
 65   /**
 66     Bind the ref threshold binding. Don't show papers with less refs. 
 67   
 68     @property {Integer}
 69     @binding "Papercube.papersPerYearController.refThreshold"
 70   */
 71   refThresholdBinding: "Papercube.papersPerYearController.refThreshold",
 72 
 73   /**
 74     Ref threshold cached value.
 75   
 76     @property {Integer}
 77     @default 0
 78   */
 79   _cached_refThreshold: 1,
 80 
 81   /**
 82     Bind the depth. Don't show items beyond this depth.
 83   
 84     @property {Integer}
 85     @binding "Papercube.papersPerYearController.depth"
 86   */
 87   depthBinding: "Papercube.papersPerYearController.depth",
 88 
 89   /**
 90     Bind the hide bogus year param. Don't show items that have bogus years.
 91   
 92     @property {Boolean}
 93     @binding "Papercube.papersPerYearController.hideBogusYears"
 94   */
 95   hideBogusYearsBinding: "Papercube.papersPerYearController.hideBogusYears",
 96 
 97   /**
 98     Hide bogus paper years cached value.
 99   
100     @property {Boolean}
101     @default false
102   */
103   _cached_hideBogusYears: false,
104 
105 
106   /**
107     Depth cached value.
108   
109     @property {Integer}
110     @default 8
111   */
112   _cached_depth: 8,
113 
114   /**
115     Display references or citations.
116 
117     @property {Boolean}
118     @default YES
119   */
120   _displayRefs: YES,
121 
122   /**
123     Cached width of the view's canvas, set by the displayProperties.
124 
125     @property {Integer}
126     @default 800px
127   */
128   _canvasWidth: 800,
129 
130   /**
131     Cached height of the view's canvas, set by the displayProperties.
132 
133     @property {Integer}
134     @defualt 600px
135   */
136   _canvasHeight: 600,
137 
138   /**
139     Cached height property from displayProperties.
140 
141     @property {Integer}
142   */
143   _h: 0,
144 
145   /**
146     Cached width property from displayProperties.
147 
148     @property {Integer}
149   */
150   _w: 0,
151 
152   /**
153     Cached x-axis offset property from displayProperties.
154 
155     @property {Integer}
156   */
157   _x: 0,
158 
159   /**
160     Cached y-axis offset property from displayProperties.
161 
162     @property {Integer}
163   */
164   _y: 0,
165   
166   // The height of a row in the visualization.
167   _rowHeight: 1,
168 
169   /** 
170     The deepest explored area.
171   
172     @private 
173     @property {Integer}
174     @default 0
175   */
176   _deepest: 0,
177 
178   /**
179     Keep track of the things that are already rendered to avoid cycles.
180 
181     @private 
182     @property {Object}
183   */
184   _renderTree: {},
185 
186   /** 
187     The found GUID.
188   
189     @property {String}
190   */
191   _foundGuid: null,
192 
193   /**
194     Meta data box small width. 
195     
196     @property {Integer}
197     @default 400
198   */  
199   _metaDataBoxWidthSmall: 400,
200 
201   /**
202     Meta data box small height. 
203     
204     @property {Integer}
205     @default 120
206   */  
207   _metaDataBoxHeightSmall: 120,
208 
209   /**
210     Outlets for author stat view.
211 
212     ["metaDataView"]
213   */
214   outlets: ["metaDataView"],
215 
216   /** 
217     "canvas" for the svg tag. 
218   
219     @property {DOM Element}
220   */
221   svgCanvas: null,
222 
223   /**
224     The DOM element that contains the meta data for an item. Bound to the '.meta-data?' element.
225 
226     @outlet
227   */
228   metaDataView: ".meta-data?",
229 
230   /**
231     The current selection.
232     
233     @property {String}
234   */
235   _selection: null,
236   
237   /** 
238     SVG Node cache. To prevent needless waste.
239   
240     @private 
241     @property {Object}
242   */
243   _svgNodesCache: {},
244 
245   /** 
246     SVG Year Text cache. To prevent needless waste.
247   
248     @private 
249     @property {Object}
250   */
251   _svgYearTextCache: {},
252 
253   /** 
254     SVG Year Line cache. To prevent needless waste.
255   
256     @private 
257     @property {Object}
258   */
259   _svgYearLinesCache: {},
260 
261   /** 
262     SVG Text cache. To prevent needless waste.
263   
264     @private 
265     @property {Object}
266   */
267   _svgTextCache: {},
268   
269   /** 
270     The needed guids, call the server to get them.
271   
272     @private 
273     @property {Object}
274   */
275   _guidsNeeded: {},
276 
277   /** 
278     The papers that are to be rendered, organized by year keys.
279   
280     @private 
281     @property {Object}
282   */
283   _papersPerYear: {},
284 
285   /** 
286     If YES, animate node transitions, if NO, don't.
287     
288     This is set dynamically based on paper count.
289   
290     @private 
291     @property {Boolean}
292     @default NO
293   */
294   _shouldAnimate: NO,
295   
296   /** 
297     If YES, animate year line transitions, if NO, don't.
298     
299     This is set dynamically based on paper count.
300   
301     @private 
302     @property {Boolean}
303     @default NO
304   */
305   _shouldAnimateYears: NO,
306 
307   /**
308     Hide meta data box.
309 
310   */
311   _hideMetaData: function()
312   {
313     // Hide meta data view.
314     this.metaDataView.removeClassName("show-meta-data-small");
315   },
316 
317   /**
318     Show meta data for paper!
319     
320 
321     @param coordinates {Object} The x,y coordinates of the mouse pointer.
322     @param guid {string} The guid of the item.
323   
324     @returns {Boolean} Returns NO if there are no coordinates or content.
325   */
326   _showMetaData: function(coordinates, guid)
327   {
328     if(!coordinates) return NO;
329 
330     var x = coordinates.x+10;
331     var y = coordinates.y-20;
332   
333     var wHeight = NodeGraph.windowHeight()-30;
334     var wWidth = NodeGraph.windowWidth()-10;
335     
336     if(y < 30) y = 30;
337     if(x < 20) x = 20;
338     if(y > (wHeight-this._metaDataBoxHeightSmall)) y = (wHeight-this._metaDataBoxHeightSmall)-30;
339     if(x > (wWidth-this._metaDataBoxWidthSmall)) x = (wWidth-this._metaDataBoxWidthSmall)+1;
340 
341     this.metaDataView.style.top = y + "px";
342     this.metaDataView.style.left = x + "px";
343 
344     // Show the meta data view.
345     this.metaDataView.addClassName("show-meta-data-small");
346 
347     // Get the paper content.
348     var content = Papercube.Paper.find(guid);
349 
350     if(!content) return;
351 
352     // Set the title
353     this.metaDataView.childNodes[0].innerHTML = content.get("title");
354 
355     // Set the authors
356     var authors = content.get('authorNames').join(', ');
357     var authLen = authors.length;
358     this.metaDataView.childNodes[1].innerHTML = (authLen > 150) ? (authors.substr(0,150)+"…") : authors;
359     this.metaDataView.childNodes[2].innerHTML = (content.get('publisher')) ? content.get('publisher') : '';
360 
361     // Set the date
362     this.metaDataView.childNodes[3].innerHTML = "<strong>Publication Date: </strong> " + content.get("year");
363 
364     this.metaDataView.childNodes[4].innerHTML = Papercube.pluralizeString(" reference", content.get('refCount'));
365     this.metaDataView.childNodes[5].innerHTML = Papercube.pluralizeString(" citation", content.get('citeCount'));
366 
367   },
368   
369   /** 
370     Option to keep lines pinned if desired.
371   
372     @private 
373     @property {Boolean}
374     @default NO
375   */
376   _linesPinned: NO,
377   
378   /**
379     Node background color.
380     
381     @property {String}
382     @config
383     @default '#FFCB2F'
384   */
385   nodeColor: '#FFCB2F',
386 
387   /**
388     Selected node background color.
389     
390     @property {String}
391     @config
392     @default 'yellow'
393   */
394   nodeColorSel: 'yellow',
395 
396   /**
397     Node border color.
398     
399     @property {String}
400     @config
401     @default '#EAA400'
402   */
403   nodeBorderColor: '#EAA400',
404 
405   /**
406     Selected node border color.
407     
408     @property {String}
409     @config
410     @default '#FFB60B'
411   */
412   nodeBorderColorSel: '#FFB60B',
413   
414   /**
415     Node text color.
416     
417     @property {String}
418     @config
419     @default '#666'
420   */
421   nodeTextColor: '#666',
422   
423   /**
424     Selected node opacity.
425     
426     @property {Float}
427     @config
428     @default 1.0
429   */
430   nodeOpacitySel: 1.0,
431 
432   /**
433     Node opacity.
434     
435     @property {Float}
436     @config
437     @default 0.5
438   */
439   nodeOpacity: 0.5,
440 
441   /**
442     The default width of the node border.
443     
444     @property {Integer}
445     @config
446     @default 1px
447   */
448   nodeBorderWidth: 1,
449   
450   /**
451     Ref edge color.
452     
453     @property {String}
454     @config
455     @default 'blue'
456   */
457   edgeRefColor: 'blue',
458 
459   /**
460     Cite edge color.
461     
462     @property {String}
463     @config
464     @default 'red'
465   */
466   edgeCiteColor: 'red',
467 
468   /**
469     Edge width.
470     
471     @property {String}
472     @config
473     @default 1px
474   */
475   edgeWidth: 1,
476   
477   /**
478     Year line width.
479     
480     @property {String}
481     @config
482     @default 1px
483   */
484   yearLineWidth: 1,
485   
486   /**
487     The nodeTextRatio allows the font size for the node to the calculated. The font size is calculated as radius/nodeTextRatio.
488 
489     @property {Integer}
490     @default 5
491   */
492   nodeTextRatio: 5,
493   
494   /**
495     Year line color
496     
497     @property {String}
498     @config
499     @default '#666'
500   */
501   yearLineColor: '#666',
502 
503   /**
504     Year text color
505     
506     @property {String}
507     @config
508     @default '#555'
509   */
510   yearTextColor: '#555',
511   
512   /** 
513     If the mouse is moved on top of the view, see if there is any meta data to show.
514   
515     @param {DOM Event} evt The mouseMoved event.
516   */
517   mouseMoved: function(evt)
518   {
519     var guid = evt.target.getAttribute('guid');
520     if(guid)
521     {
522       if(this._foundGuid != guid && !this._linesPinned)
523       {
524 
525         if(this._foundGuid)
526         {
527           var paper = Papercube.Paper.find(this._foundGuid);
528           if(paper)
529           {
530             var refs = paper._attributes.references;
531             var cites = paper._attributes.citations;
532           }
533           var elm = this._svgNodesCache[this._foundGuid].elm;
534           if(elm)
535           {
536             elm.removeAttributeNS(null, 'fill-opacity');
537             elm.removeAttributeNS(null, 'fill');
538             elm.removeAttributeNS(null, 'stroke-width');
539             elm.removeAttributeNS(null, 'stroke');
540           }
541 
542           var relLen = refs.length;
543           for(var i=0; i< relLen; i++)
544           {
545             var elm = (this._svgNodesCache[refs[i]]) ? this._svgNodesCache[refs[i]].elm : null;
546             if(elm)
547             {
548               elm.removeAttributeNS(null, 'fill-opacity');
549               elm.removeAttributeNS(null, 'stroke-width');
550               elm.removeAttributeNS(null, 'stroke');
551             }
552           }
553           var relLen = cites.length;
554           for(var i=0; i< relLen; i++)
555           {
556             var elm = (this._svgNodesCache[cites[i]]) ? this._svgNodesCache[cites[i]].elm : null;
557             if(elm)
558             {
559               elm.removeAttributeNS(null, 'fill-opacity');
560               elm.removeAttributeNS(null, 'stroke-width');
561               elm.removeAttributeNS(null, 'stroke');
562             }
563           }
564         }
565 
566         this._removeEdges();
567         
568         this._foundGuid = guid;
569         
570         var paper = Papercube.Paper.find(guid);
571         var cites = [];
572         var refs = [];
573 
574         // If the paper exists, then get its references or citations.
575         if(paper)
576         {
577          refs = paper._attributes.references;
578          cites = paper._attributes.citations;
579         }
580 
581         var elm = this._svgNodesCache[guid].elm;
582         if(elm)
583         {
584           elm.setAttributeNS(null, 'fill', this.nodeColorSel);          
585           elm.setAttributeNS(null, 'fill-opacity', this.nodeOpacitySel);          
586           elm.setAttributeNS(null, 'stroke-width', this.nodeBorderWidth*(PERYEAR_DEFAULT_SCALE-this._z));
587           elm.setAttributeNS(null, 'stroke', this.nodeBorderColorSel);
588         }
589 
590         var startX = parseInt(elm.attributes[0].nodeValue,0);
591         var rad = parseInt(elm.attributes[2].nodeValue,0);
592         var startY = parseInt(elm.attributes[1].nodeValue,0)+ ((this._displayRefs) ? rad : -rad);
593 
594         var borderWidth = this.edgeWidth*(PERYEAR_DEFAULT_SCALE-this._z);
595 
596         var relLen = refs.length;
597         for(var i=0; i<relLen; i++)
598         {
599           var elm = (this._svgNodesCache[refs[i]]) ? this._svgNodesCache[refs[i]].elm : null;
600           if(elm)
601           {
602             var endX = parseInt(elm.attributes[0].nodeValue,0);
603             var endY = parseInt(elm.attributes[1].nodeValue,0)-((this._displayRefs) ? parseInt(elm.attributes[2].nodeValue,0) : -parseInt(elm.attributes[2].nodeValue,0));
604             var qX = startX;
605             var qY = (startY+endY)/2;
606             this._createSVGElement('path', {d: ('M'+startX+' '+startY+' Q '+qX+' '+qY+' '+endX+' '+endY), fill: 'none', stroke: 'blue', 'stroke-width': borderWidth}, {}, EDGE);
607             elm.setAttributeNS(null, 'fill-opacity', this.nodeOpacitySel);    
608             elm.setAttributeNS(null, 'stroke-width', borderWidth);
609             elm.setAttributeNS(null, 'stroke', this.edgeRefColor);
610           }
611         }
612         var relLen = cites.length;
613         for(var i=0; i<relLen; i++)
614         {
615           var elm = (this._svgNodesCache[cites[i]]) ? this._svgNodesCache[cites[i]].elm : null;
616           if(elm)
617           {
618             var endX = parseInt(elm.attributes[0].nodeValue,0);
619             var endY = parseInt(elm.attributes[1].nodeValue,0)-((this._displayRefs) ? parseInt(elm.attributes[2].nodeValue,0) : -parseInt(elm.attributes[2].nodeValue,0));
620             var qX = startX;
621             var qY = (startY+endY)/2;
622             this._createSVGElement('path', {d: ('M'+startX+' '+startY+' Q '+qX+' '+qY+' '+endX+' '+endY), fill: 'none', stroke: 'red', 'stroke-width': borderWidth}, {}, EDGE);
623             elm.setAttributeNS(null, 'fill-opacity', this.nodeOpacitySel);    
624             elm.setAttributeNS(null, 'stroke-width', borderWidth);
625             elm.setAttributeNS(null, 'stroke', this.edgeCiteColor);
626           }
627         }
628         if(SC.isSafari())
629         {
630           this.svgCanvas.setAttributeNS(null, 'width', this._pW+1);
631           this.svgCanvas.setAttributeNS(null, 'width', this._pW);
632         }
633       }
634       // else if(this._linesPinned)
635       // {
636       //   var elm = this._svgMap[guid];
637       //   var rad = parseInt(elm.attributes[2].nodeValue,0);
638       // }
639       
640       this._showMetaData({x: Event.pointerX(evt), y: Event.pointerY(evt)}, guid);
641     }
642     else
643     {
644       this._hideMetaData();
645     }
646   },
647 
648   /** 
649     If the mouse leaves the area, hide the meta-data view.
650   
651     @param {DOM Event} evt The mouseExited event.
652   */
653   mouseExited: function(evt)
654   {
655     this._hideMetaData();
656   },
657 
658   /** 
659     Save the GUID of the element and then show the fan.
660     
661     @param {DOM Event} evt The mouseDown event.
662   */
663   mouseDown: function(evt)
664   {
665     var guid = evt.target.getAttribute('guid');
666     var type = evt.target.getAttribute('fanType');
667     if(guid && type)
668     {
669       this._mouseDownGUID = guid;
670       Papercube.canvasController.showFan(Event.pointerX(evt), Event.pointerY(evt), 'papersperyear', (type+"Fan"));
671       return YES;
672     }
673     else
674     {
675       return this.handleMouseDownDrag(evt);
676     }
677     return NO;
678 
679   },
680 
681   /**
682     Pin the lines for paper.
683   */
684   pinLinesForPaper: function()
685   {
686     this._linesPinned = !this._linesPinned;
687   },
688   
689   /**
690     When the displayProperities binding changes, update the view appropriately.
691 
692     @observes displayProperties
693   
694     @param force {boolean} If YES, force a redraw.
695     
696     @returns {Boolean} Returns NO if there is no guid of if the view is not visible.
697   */
698   displayPropertiesDidChange: function(force)
699   {
700     if(!this.get("isVisible")) return NO;
701     
702     var displayProperties = this.get('displayProperties');
703     
704     // Save display properties locally.
705     var h = displayProperties.height-20;
706     var w = displayProperties.width;
707     var x = displayProperties.left;
708     var y = displayProperties.top;
709     var z = displayProperties.zoomValue;
710     var pW = displayProperties.portalWidth;
711     var pH = displayProperties.portalHeight;
712     
713     this._linesPinned = NO;
714     
715     // Set the style and frame of the view. This replaces set('frame', {...}) due to performance reasons.
716     this.setStyle({height: (pH-20)+"px", width: pW+'px', left: '20px', top: '20px'});
717     this._frame = {height: pH-20, width: pW, x: 20, y: 20};
718 
719     // Set the canvas dimensions.
720     this.svgCanvas.setAttributeNS(null, 'height', pH);
721     this.svgCanvas.setAttributeNS(null, 'width', pW);
722 
723     // Invert the display variables here to set the view box.
724     // this.svgCanvas.setAttributeNS(null, 'viewBox', (pW+' '+pH+' '+Math.abs(x)+' '+Math.abs(y)));
725     
726     // Save the canvas height and width.
727     this._canvasHeight = h;
728     this._canvasWidth = w;
729 
730     // Force redraw when window is resized.
731     var forceRedraw = (((this._h != h || this._w != w)) && this._z == z);
732     // Save the properties as needed.
733     this._h = h;
734     this._w = w;
735     this._x = x;
736     this._y = y;
737     this._z = z;
738     this._pH = pH-20;
739     this._pW = pW;
740     
741     this._zI = z/PERYEAR_DEFAULT_SCALE;
742 
743 
744 
745     // Hide meta data as the coordinates of things may have changed.
746     this._hideMetaData();
747     
748     if(this.svgGroup)
749       this.svgGroup.setAttributeNS(null, 'transform', 'scale('+this._zI+') translate('+ (this._x/this._zI) +',' + (this._y/this._zI)+')');
750     // this.svgGroup.setAttributeNS(null, 'transform', 'scale('+this._zI+') translate('+ (this._x/this._zI) +',' + (this._y/this._zI)+')');
751 
752     // If there is a resize of the window, then you have to redraw to match the new view port.
753     if(forceRedraw && typeof force != "boolean" && this._cachedContent)  
754     {
755       Papercube.canvasController.zoomOutFull();
756       this._deepest = 1;
757       this._foundGuid =  null;
758       this._render();
759     }
760   }.observes('displayProperties'),
761   
762   /**
763     Redraw based on 'content', 'isVisible', 'viewDirection', 'refThreshold', 'depth', 'citeThreshold', 'hideBogusYears' binding changes.
764 
765     @observes content
766     @observes isVisible
767     @observes viewDirection
768     @observes refThreshold
769     @observes citeThreshold
770     @observes depth
771     @observes hideBogusYears
772   
773     @returns {Boolean} Returns NO if there is no guid of if the view is not visible.
774   */
775   redrawParamsDidChange: function()
776   {
777     var content = this.get('content');
778     // If there is no content or if you're not visible, bail.
779 
780     if(!this.get("isVisible") || !content) return NO;
781 
782     // Set the zoom values to the view defaults.
783     Papercube.canvasController.set("zoomValueMax", 20);
784     Papercube.canvasController.set("zoomStep", 1);
785 
786     // Set the view direction.
787     this._displayRefs = (this.get("viewDirection") == "References");
788 
789     this.setClassName('citations', !this._displayRefs);
790     this.setClassName('references', this._displayRefs);
791 
792     if(!this._cachedContent || this._cachedContent.get('guid') !== content.get('guid')) 
793     {
794       // Hide meta data view.
795       this._hideMetaData();
796       Papercube.canvasController.zoomOutFull();
797       this._svgNodesCache = {};
798       this._svgTextCache = {};
799       this._svgYearTextCache = {};
800       this._svgYearLinesCache = {};
801       this._svgRels = {};
802       this._clearSVG();
803       Papercube.papersPerYearController.setDefaults();
804     }
805     this._deepest = 1;
806     this._foundGuid =  null;
807     this._cachedContent = content;
808 
809     var depth = this.get('depth');
810     var citeThreshold = this.get('citeThreshold');
811     var refThreshold = this.get('refThreshold');
812     var hideBogusYears = this.get('hideBogusYears');
813 
814     // Grab the cite/ref threshold.
815     if((this._cached_citeThreshold != citeThreshold ||  this._cached_citeThreshold != refThreshold || this._cached_hideBogusYears != hideBogusYears ||
816       this._cached_depth != depth) && this.get('displayProperties').zoomValue != 1)
817       {
818         this._cached_citeThreshold = citeThreshold;
819         this._cached_refThreshold = refThreshold;
820         this._cached_depth = depth;
821         this._cached_hideBogusYears = hideBogusYears;
822         Papercube.canvasController.zoomOutFull();
823         return;
824       }
825 
826     this._cached_citeThreshold = citeThreshold;
827     this._cached_refThreshold = refThreshold;
828     this._cached_depth = depth;
829     this._cached_hideBogusYears = hideBogusYears;
830 
831     this.displayPropertiesDidChange(YES);
832     this._render();
833 
834   }.observes('content', 'isVisible', 'viewDirection', 'refThreshold', 'depth', 'citeThreshold', 'hideBogusYears'),
835 
836   /**
837     Collect what needs to be rendered.
838     
839     Then render the view.
840 
841   */
842   _render: function()
843   {
844     this._guidsNeeded = {};
845     this._renderTree = {};
846     this._papersPerYear = {};
847     this._checkForData(this._cachedContent.get('guid'), 0);
848     this._removeEdges();
849 
850     // Calculate the zoom value used.
851     var zoomVal = PERYEAR_DEFAULT_SCALE/this._z;
852     
853     this.svgYearLines.setAttributeNS(null, 'stroke-width', this.yearLineWidth*zoomVal);
854     
855     // Set the temp arrays. Used later to discard unused nodes.
856     var t_svgYearText = [];
857     var t_svgYearLines = [];
858     var t_svgNodes = [];
859     var t_svgText = [];
860 
861     this._subjectArr = [];
862 
863     // Pull the current nodes from the cache.
864     
865 //    var nodeXYRatio = this.nodeXYRatio;
866     var nodeTextRatio = this.nodeTextRatio;
867 
868     if(this._deepest < this._cached_depth)
869     {
870       // Collect any guids that we need to retrieve from the server.
871       var guids = [];
872       for(var key in this._guidsNeeded)
873       {
874         if(guids.indexOf(key) == -1)
875         {
876           guids.push(key);
877         }
878       }
879 
880       if(guids.length > 0)
881       {
882         console.log("retrieving " +guids.length + " papers");
883         Papercube.searchController.set('showRequestSpinner', YES);
884         // Now retrieve the next level as needed..
885         var callBack = function() { this.redrawParamsDidChange(); }.bind(this, this.get('content'));
886         setTimeout(this._waitToCallServer.bind(this, guids, callBack), 500); 
887         this._cachedContent.set((this._displayRefs ? "maxRefLevel" : "maxCiteLevel"), this._deepest);
888       }
889       else
890       {
891         Papercube.searchController.set('showRequestSpinner', NO);
892       }
893     }
894     else
895     {
896       Papercube.searchController.set('showRequestSpinner', NO);
897     }
898     // else
899     // {
900       var years = [];
901 
902       var numYears = 0;
903       var maxPapers = 0;
904       var totalPapers = 0;
905       for(var key in this._papersPerYear)
906       {
907         var papers = this._papersPerYear[key];
908         papers.sort(function(a, b) 
909         { 
910           var aDate = a.get('year'); 
911           var bDate = b.get('year');
912           if(aDate == bDate) 
913           {
914             return a._attributes.title < b._attributes.title;        
915           }
916           if(aDate < bDate) return -1;
917       
918           return 1;
919         });
920         years.push(papers);
921         totalPapers += papers.length;
922         if(papers.length > maxPapers) maxPapers = papers.length;
923       
924         numYears++;
925       }
926       years.sort(function(a, b) 
927       {
928         if(!a[0] || !b[0]) return 1;
929         var aDate = a[0].get('year'); 
930         var bDate = b[0].get('year');
931         if(aDate > bDate) return -1;
932         return 1;
933       });
934       var displayRefs = this._displayRefs;
935 
936 
937       this._shouldAnimate = (totalPapers < 40);
938       this._shouldAnimateYears = (totalPapers < 100);
939 
940       var w = (this._w-120)*zoomVal;
941       var h = this._h/(numYears+1)*zoomVal;
942       // var r = w/(maxPapers*3);
943       // var distX = w/maxPapers;
944       var y = (displayRefs) ? h/2 : this._h*zoomVal-h/2;
945       
946       // var group = this._createSVGElement('g', {}, {});
947 
948       var guid = this._cachedContent._attributes.guid;
949       var len = years.length;
950       var year = this._cachedContent.get('year');
951 
952       // First, create the center node. it is special.
953       // var ry = (h/4);
954       // var rx = ry*nodeXYRatio;
955       var r = (h/4);
956 
957       var x = (w/2)+70*zoomVal;
958       // var params = {cx: x, cy: y, ry: ry, rx: rx, fanType: 'focused', guid: guid};
959       var params = {cx: x, cy: y, r: r, fanType: 'focused', guid: guid};
960       var tParams = {'font-size': (h/4)/nodeTextRatio, x: x, y: y, guid: guid};
961       
962       var textLeft = 30*PERYEAR_DEFAULT_SCALE;
963       var textRight = (this._w-30)*PERYEAR_DEFAULT_SCALE;
964       var lineWidth = this._w*PERYEAR_DEFAULT_SCALE;
965 
966       var font = 12*zoomVal;
967       var lineParams = {x1: textLeft, y1: (y-h/2), x2: lineWidth, y2: (y-h/2)};
968       var yearTextLParams = {x: textLeft, y: y, 'text-anchor': 'left', 'font-size': font};
969       var yearTextRParams = {x: textRight, y: y, 'text-anchor': 'right', 'font-size': font};
970       var title = this._cachedContent._attributes.title.substr(0, 80);
971 
972       if(!this._svgNodesCache[guid])
973       {
974         this._createNode(guid, params, tParams, title);
975         this._createYearLine(guid, lineParams);
976         this._createYearText(('L'+guid), yearTextLParams, (year+" - '"+ title+"'"));
977         this._createYearText(('R'+guid), yearTextRParams, year);
978       }
979       else
980       {
981         this._handleLinesTransition(guid, lineParams, yearTextLParams, yearTextRParams);
982         this._handleNodeTransition(guid, params, tParams);
983       }
984       
985       t_svgYearText.push(('L'+guid));
986       t_svgYearText.push(('R'+guid));
987       t_svgYearLines.push(guid);
988       t_svgNodes.push(guid);
989       t_svgText.push(guid);
990      
991       y+= (displayRefs) ? h : -h;
992       //console.log(len);
993       var timesFive = 5*zoomVal;
994       var timesFifteen = 15*zoomVal;
995       for(var k=0; k<len; k++)
996       {
997         var papers = years[k];
998         var paperLen = papers.length;
999         var year = papers[0].get('year');
1000 
1001         var distX = w/(paperLen);
1002         // var ry = Math.min(h/4, distX/3);
1003         // var rx = ry*nodeXYRatio;
1004         var r = Math.min(h/4, distX/3);
1005         var fontSize = r/nodeTextRatio;
1006         // var fontSize = ry/nodeTextRatio;
1007         
1008 
1009         var x = 70*zoomVal+distX/2; //(w-(distX*paperLen))/2;
1010         // var calc1 = (y-h/2+ry+timesFive);
1011         // var calc2 = (h/2-ry-timesFive);
1012         var calc1 = (y-h/2); //+r+timesFive);
1013        // var calc2 = (h/2-r-timesFive);
1014         
1015         var lineParams = {x1: textLeft, y1: calc1, x2: lineWidth, y2: calc1};
1016         var yearTextLParams = {x: textLeft, y: calc1+timesFifteen, 'text-anchor': 'left', 'font-size': font};
1017         var yearTextRParams = {x: textRight, y: calc1+timesFifteen, 'text-anchor': 'right', 'font-size': font};
1018 
1019         var yearStr = year.toString();
1020         if(!this._svgYearLinesCache[yearStr])
1021         {
1022           //console.log('creating ' + year);
1023           this._createYearLine(yearStr, lineParams);
1024           this._createYearText(('L'+year), yearTextLParams, year);
1025           this._createYearText(('R'+year), yearTextRParams, year);
1026         }
1027         else
1028         {
1029          this._handleLinesTransition(yearStr, lineParams, yearTextLParams, yearTextRParams);
1030         }
1031         
1032         t_svgYearText.push(('L'+year));
1033         t_svgYearText.push(('R'+year));
1034         t_svgYearLines.push(yearStr);
1035   
1036         if(paperLen/300 < 1)
1037         {
1038           var locy = y; //calc1 + calc2*2;
1039           for(var i=0; i < paperLen; i++)
1040           { 
1041             var paper = papers[i];
1042             var lguid = paper._attributes.guid;
1043             if(lguid != guid)
1044             {
1045 //              var month = paper.get('cachedMonth');
1046 //              var locy = calc1 + calc2*((month/11))*2;
1047 
1048               var params = {cx: x, cy: locy, r: r, fanType: 'unfocused', guid: lguid};
1049               // var params = {cx: x, cy: locy, rx: rx, ry: ry, fanType: 'unfocused', guid: lguid};
1050               var tParams = {'font-size': fontSize, x: x, y: locy, guid: lguid};
1051               
1052               if(!this._svgNodesCache[lguid])
1053               {
1054                 this._createNode(lguid, params, tParams, paper._attributes.title);
1055               }
1056               else
1057               {
1058                 this._handleNodeTransition(lguid, params, tParams);
1059               }
1060               t_svgNodes.push(lguid);
1061               t_svgText.push(lguid);
1062 
1063               x+= distX;
1064             }
1065           }
1066         }
1067         else
1068         {
1069           var key = "ERR_"+year;
1070           var errParams = {x:(w/2)+70, y: y+5, fill: '#777', 'text-anchor': 'middle', 'font-size': 14};
1071           this._handleErrorText(key, errParams, svgText);
1072           t_svgText.push(key);
1073         }
1074         y+= (displayRefs) ? h : -h;
1075         
1076       }
1077 
1078       // Overload the NodeGraph animation controller and use it here.
1079       NodeGraph.addSubjects(this._subjectArr, this.svgCanvas, this._pH);
1080 
1081       // Clear elements as needed.
1082       this._clearSVGElms(this._svgNodesCache, t_svgNodes, this.svgNodes);
1083       this._clearSVGElms(this._svgTextCache, t_svgText, this.svgText);
1084       this._clearSVGElms(this._svgYearLinesCache, t_svgYearLines, this.svgYearLines);
1085       this._clearSVGElms(this._svgYearTextCache, t_svgYearText, this.svgYearText);
1086 
1087   },
1088   
1089   /**
1090     Call the server to retrieve papers after waiting for a while.
1091   
1092     @param guids {Array} The array of guids that need to be retrieved.
1093     @param callBack {Function} The callBack function is called when the request is successful.
1094   */
1095   _waitToCallServer: function(guids, callBack)
1096   {
1097     Papercube.adaptor.getPaperDetails(guids, callBack);
1098   },
1099   
1100   /**
1101     Create or update the error message when there are more than 300 papers in a year.
1102   
1103     @param key {string} The cache key.
1104     @param errParams {Object} Position parameters.
1105   */
1106   _handleErrorParams: function(key, errParams)
1107   {
1108     if(!this._svgTextCache[key])
1109     {
1110       this._svgTextCache[key] = {elm: this._createSVGElement('text', errParams, {}, TEXT)};     
1111       this._svgTextCache[key].elm.appendChild(document.createTextNode("{ There are more than 300 papers for this year! That's too many to display. }"));
1112     }
1113     else
1114     {
1115       var errElm = this._svgTextCache[key].elm;
1116       var oldParams = this._svgTextCache[key].params;
1117       
1118       var doAnim = NO;
1119       var anim = {};
1120       if(oldParams.x != errParams.x)
1121       {
1122         doAnim = YES;
1123         anim.x = {start: oldParams.x, end: errParams.x};
1124       }
1125       if(oldParams.y != errParams.y)
1126       {
1127         doAnim = YES;
1128         anim.y = {start: oldParams.y, end: errParams.y};
1129       }
1130       if(doAnim)
1131       {
1132         this._subjectArr.push({elm: svgText[key].elm, props: anim, duration: 250, isSVG: YES});
1133       }
1134     }    
1135   },
1136 
1137   /**
1138     Handle the node transition.
1139   
1140     @param guid {string} The cache key.
1141     @param params {Object} Node positioning parameters.
1142     @param tParams {Object} Text positioning parameters.
1143   */
1144   _handleNodeTransition: function(guid, params, tParams)
1145   {
1146     var nodeElm = this._svgNodesCache[guid].elm;
1147     var textElm = this._svgTextCache[guid].elm;
1148     var oldTextParams = this._svgTextCache[guid].params;
1149     var oldParams = this._svgNodesCache[guid].params;
1150     
1151     nodeElm.removeAttributeNS(null, 'fill-opacity');
1152     nodeElm.removeAttributeNS(null, 'fill');
1153     nodeElm.removeAttributeNS(null, 'stroke-width');
1154     nodeElm.removeAttributeNS(null, 'stroke');
1155 
1156     if(this._shouldAnimate)
1157     {
1158 
1159       var doAnim = NO;
1160       var anim = {};
1161       var tAnim = {};
1162 
1163       if(oldParams.cx != params.cx)
1164       {
1165         anim.cx = {start: oldParams.cx, end: params.cx};
1166         tAnim.x = {start: oldTextParams.x, end: tParams.x};
1167         doAnim = YES;
1168       }
1169       if(oldParams.cy != params.cy)
1170       {
1171         anim.cy = {start: oldParams.cy, end: params.cy};
1172         tAnim.y = {start: oldTextParams.y, end: tParams.y};
1173         doAnim = YES;
1174       }
1175       // if(oldParams.ry != params.ry)
1176       // {
1177       //   anim.ry = {start: oldParams.ry, end: params.ry};
1178       //   anim.rx = {start: oldParams.rx, end: params.rx};
1179       //   tAnim['font-size'] = {start: oldTextParams['font-size'], end: tParams['font-size']};
1180       //   doAnim = YES;
1181       // }
1182       if(oldParams.r != params.r)
1183       {
1184         anim.r = {start: oldParams.r, end: params.r};
1185         tAnim['font-size'] = {start: oldTextParams['font-size'], end: tParams['font-size']};
1186         doAnim = YES;
1187       }
1188       if(doAnim)
1189       {
1190       //  console.log("animateNODE");
1191         this._subjectArr.push({elm: nodeElm,props: anim, duration: 250, isSVG: YES});
1192         this._subjectArr.push({elm: textElm, props: tAnim, duration: 250, isSVG: YES});
1193       }
1194     }
1195     else
1196     {
1197       if(oldParams.cx != params.cx)
1198       {
1199         nodeElm.setAttributeNS(null, 'cx', Math.floor(params.cx));
1200         textElm.setAttributeNS(null, 'x', Math.floor(params.cx));
1201       }
1202       if(oldParams.cy != params.cy)
1203       {
1204         nodeElm.setAttributeNS(null, 'cy', Math.floor(params.cy));
1205         textElm.setAttributeNS(null, 'y', Math.floor(params.cy));
1206       }
1207       // if(oldParams.ry != params.ry)
1208       // {
1209       //   nodeElm.setAttributeNS(null, 'rx', Math.floor(params.rx));
1210       //   nodeElm.setAttributeNS(null, 'ry', Math.floor(params.ry));
1211       //   textElm.setAttributeNS(null, 'font-size', Math.floor(tParams['font-size']));
1212       // }
1213       if(oldParams.r != params.r)
1214       {
1215         nodeElm.setAttributeNS(null, 'r', Math.floor(params.r));
1216         textElm.setAttributeNS(null, 'font-size', Math.floor(tParams['font-size']));
1217       }
1218     }
1219     
1220     this._svgNodesCache[guid].params = params;
1221     this._svgTextCache[guid].params = tParams;
1222     
1223   },
1224 
1225   /**
1226     Handle the lines and year text transition.
1227   
1228     @param guid {string} The cache key.
1229     @param lineParams {Object} Line positioning parameters.
1230     @param yearTextLParams {Object} Left year text positioning parameters.
1231     @param yearTextRParams {Object} Right year text positioning parameters.
1232   */
1233   _handleLinesTransition: function(guid, lineParams, yearTextLParams, yearTextRParams)
1234   {
1235     var doLAnim = NO;
1236     var doRAnim = NO;
1237     var doYAnim = NO;
1238     var lYearTextAnim = {};
1239     var rYearTextAnim = {};
1240     var yearLineAnim = {};
1241 
1242     var lineElm = this._svgYearLinesCache[guid].elm;
1243     var oldLineParams = this._svgYearLinesCache[guid].params;
1244 
1245     var lYearTextElm = this._svgYearTextCache['L'+guid].elm;
1246     var oldLYearTextParams = this._svgYearTextCache['L'+guid].params;
1247 
1248     var rYearTextElm = this._svgYearTextCache['R'+guid].elm;
1249     var oldRYearTextParams = this._svgYearTextCache['R'+guid].params;
1250 
1251     if(this._shouldAnimateYears)
1252     {
1253       
1254       if(oldLineParams.x1 != lineParams.x1)
1255       {
1256         yearLineAnim.x1 = {start: oldLineParams.x1, end: lineParams.x1};
1257         doYAnim = YES;
1258       }
1259       if(oldLineParams.x2 != lineParams.x2)
1260       {
1261         yearLineAnim.x2 = {start: oldLineParams.x2, end: lineParams.x2};
1262         doYAnim = YES;
1263       }
1264       if(oldLineParams.y1 != lineParams.y1)
1265       {
1266         yearLineAnim.y1 = {start: oldLineParams.y1, end: lineParams.y1};
1267         doYAnim = YES;
1268       }
1269       if(oldLineParams.y2 != lineParams.y2)
1270       {
1271         yearLineAnim.y2 = {start: oldLineParams.y2, end: lineParams.y2};
1272         doYAnim = YES;
1273       }    
1274       if(oldLYearTextParams.x != yearTextLParams.x)
1275       {
1276         lYearTextAnim.x = {start: oldLYearTextParams.x, end: yearTextLParams.x};
1277         doLAnim = YES;
1278       }    
1279       if(oldLYearTextParams.y != yearTextLParams.y)
1280       {
1281         lYearTextAnim.y = {start: oldLYearTextParams.y, end: yearTextLParams.y};
1282         doLAnim = YES;
1283       }    
1284       if(oldRYearTextParams.x != yearTextRParams.x)
1285       {
1286         rYearTextAnim.x = {start: oldRYearTextParams.x, end: yearTextRParams.x};
1287         doRAnim = YES;
1288       }    
1289       if(oldRYearTextParams.y != yearTextRParams.y)
1290       {
1291         rYearTextAnim.y = {start: oldRYearTextParams.y, end: yearTextRParams.y};
1292         doRAnim = YES;
1293       } 
1294       if(doYAnim)
1295       {
1296         this._subjectArr.push({elm: lineElm, props: yearLineAnim, duration: 250, isSVG: YES});
1297       }   
1298       if(doLAnim)
1299       {
1300         this._subjectArr.push({elm: lYearTextElm, props: lYearTextAnim, duration: 250, isSVG: YES});
1301       }   
1302       if(doRAnim)
1303       {
1304         this._subjectArr.push({elm: rYearTextElm, props: rYearTextAnim, duration: 250, isSVG: YES});
1305       }   
1306     }
1307     else
1308     {
1309       if(oldLineParams.x1 != lineParams.x1)
1310       {
1311         lineElm.setAttributeNS(null, 'x1', Math.floor(lineParams.x1));
1312       }
1313       if(oldLineParams.x2 != lineParams.x2)
1314       {
1315         lineElm.setAttributeNS(null, 'x2', Math.floor(lineParams.x2));
1316       }
1317       if(oldLineParams.y1 != lineParams.y1)
1318       {
1319         lineElm.setAttributeNS(null, 'y1', Math.floor(lineParams.y1));
1320       }
1321       if(oldLineParams.y2 != lineParams.y2)
1322       {
1323         lineElm.setAttributeNS(null, 'y2', Math.floor(lineParams.y2));
1324       }    
1325       if(oldLYearTextParams.x != yearTextLParams.x)
1326       {
1327         lYearTextElm.setAttributeNS(null, 'x', Math.floor(yearTextLParams.x));
1328       }    
1329       if(oldLYearTextParams.y != yearTextLParams.y)
1330       {
1331         lYearTextElm.setAttributeNS(null, 'y', Math.floor(yearTextLParams.y));
1332       }    
1333       if(oldRYearTextParams.x != yearTextRParams.x)
1334       {
1335         rYearTextElm.setAttributeNS(null, 'x', Math.floor(yearTextRParams.x));
1336       }    
1337       if(oldRYearTextParams.y != yearTextRParams.y)
1338       {
1339         rYearTextElm.setAttributeNS(null, 'y', Math.floor(yearTextRParams.y));
1340       } 
1341       
1342     }
1343     this._svgYearLinesCache[guid].params = lineParams;
1344     this._svgYearTextCache['R'+guid].params = yearTextRParams;
1345     this._svgYearTextCache['L'+guid].params = yearTextLParams;
1346   },
1347 
1348   /**
1349     Create a new node element.
1350   
1351     @param guid {string} The cache key.
1352     @param params {Object} Node positioning parameters.
1353     @param tParams {Object} Text positioning parameters.
1354     @param title {string} The title string.
1355   */
1356   _createNode: function(guid, params, tParams, title)
1357   {
1358     this._svgNodesCache[guid] = {elm: this._createSVGElement('circle', params,{}, NODE), params: params};
1359     this._svgTextCache[guid] = {elm: this._createSVGElement('text', tParams, {}, TEXT), params: tParams};        
1360     this._svgTextCache[guid].elm.appendChild(document.createTextNode(title.substr(0,20)));
1361   },
1362   
1363   /**
1364     Create a new year line element.
1365   
1366     @param guid {string} The cache key.
1367     @param params {Object} Year line positioning parameters.
1368   */
1369   _createYearLine: function(guid, params)
1370   {
1371     this._svgYearLinesCache[guid] = {elm: this._createSVGElement('line',params,{}, YEARLINE), params: params};
1372   },
1373   
1374   /**
1375     Create a new year text element.
1376   
1377     @param guid {string} The cache key.
1378     @param params {Object} Year text positioning parameters.
1379   */
1380   _createYearText: function(guid, params, text)
1381   {
1382     this._svgYearTextCache[guid] = {elm: this._createSVGElement('text',params,{}, YEARTEXT), params: params};
1383     this._svgYearTextCache[guid].elm.appendChild(document.createTextNode(text));
1384   },
1385   
1386   /**
1387     Check what data needs to be retrieved.
1388   
1389   
1390     @param guid {string} The guid of the item.
1391     @param level {Integer} The current level.
1392   */
1393   _checkForData: function(guid, level)
1394   {
1395     // Get the paper.
1396     var paper = Papercube.Paper.find(guid);
1397     var rels = [];
1398     // If the paper exists, then get its references or citations.
1399     if(paper && this._renderTree[guid] != 1)
1400     {
1401       if(!this._renderTree[guid]) this._renderTree[guid] = 0;
1402       this._renderTree[guid]++;
1403       
1404       rels = (this._displayRefs) ? paper._attributes.references : paper._attributes.citations;
1405       var childCount = rels.length;
1406       var newRels = [];
1407       for(var i=0; i<childCount; i++)
1408       {
1409         var relGuid = rels[i];
1410         var child = Papercube.Paper.find(relGuid);
1411         if(child && (!this._cached_hideBogusYears || (this._cached_hideBogusYears && child._attributes.fixedyear==='1')))
1412         {
1413           if(this._cached_citeThreshold <= child.get('citeCount') && 
1414              this._cached_refThreshold <= child.get('refCount'))
1415           {
1416             newRels.push(relGuid);
1417           }
1418         }
1419         else if(!this._cached_hideBogusYears)
1420         {
1421             this._guidsNeeded[relGuid] = 1;
1422         }
1423       }
1424       
1425       var year = paper.get("year");
1426       if(this._cachedContent._attributes.guid != guid)
1427       {
1428         if(!this._papersPerYear[year]) // Will need to alter this at some point.
1429         {
1430           this._papersPerYear[year] = [];
1431         }
1432        this._papersPerYear[year].push(paper);
1433       }
1434 
1435       childCount = newRels.length;
1436       rels = newRels;
1437 
1438     }
1439 
1440     // Log the deepest level.
1441     if(this._deepest < level) this._deepest = level;
1442 
1443     var childCount = rels.length;
1444     var nextLvl = level+1;
1445 
1446     // If the paper has not been printed before and there is at least 0.1 pixels for each paper, draw it.
1447     if(this._renderTree[guid] == 1 && level < this._cached_depth)
1448     {
1449       for(var i=0; i<childCount; i++)
1450       {
1451         var rel = rels[i];
1452         this._checkForData(rel, nextLvl);   
1453       }
1454     }
1455   },
1456   
1457   /**
1458     Remove selected edges.
1459   
1460   */
1461   _removeEdges: function()
1462   {
1463     var svg = this.svgEdges;
1464 		while (svg.firstChild) 
1465 		{
1466 		  svg.removeChild(svg.firstChild);
1467 		}
1468   },
1469   
1470   /**
1471     Clear all SVG elements.
1472   
1473   */
1474   _clearSVG: function()
1475   {
1476     var svg = this.svgNodes;
1477 		while (svg.firstChild) 
1478 		{
1479 		  svg.removeChild(svg.firstChild);
1480 		}
1481     var svg = this.svgEdges;
1482 		while (svg.firstChild) 
1483 		{
1484 		  svg.removeChild(svg.firstChild);
1485 		}
1486     var svg = this.svgText;
1487 		while (svg.firstChild) 
1488 		{
1489 		  svg.removeChild(svg.firstChild);
1490 		}
1491     var svg = this.svgYearLines;
1492 		while (svg.firstChild) 
1493 		{
1494 		  svg.removeChild(svg.firstChild);
1495 		}
1496     var svg = this.svgYearText;
1497 		while (svg.firstChild) 
1498 		{
1499 		  svg.removeChild(svg.firstChild);
1500 		}
1501   },
1502 
1503   /**
1504     Create a new SVG element.
1505   
1506     @param type {string} The node type.
1507     @param attributes {Object} The attributes hash.
1508     @param events {Object} The events hash.
1509     @param kind {Integer} Flag that specifies what SVG element to attach the element to.
1510     
1511     @returns {Array} {DOM Element} The SVG element that was created.
1512   */
1513   _createSVGElement: function(type, attributes, events, kind)
1514   {
1515     var elm = document.createElementNS("http://www.w3.org/2000/svg", type);
1516     if(attributes)
1517     {
1518       for(var key in attributes)
1519       {
1520         if(isNaN(attributes[key]) || key.indexOf('opacity') != -1)
1521         {
1522           
1523          	elm.setAttributeNS(null, key, attributes[key]);
1524         }
1525         else
1526         {
1527          	elm.setAttributeNS(null, key, Math.floor(attributes[key]));
1528         }
1529       }
1530     }
1531 
1532     switch(kind)
1533     {
1534       case NODE:
1535         this.svgNodes.appendChild(elm);
1536         break;
1537       case EDGE:
1538         this.svgEdges.appendChild(elm);
1539         break;
1540       case TEXT:
1541         this.svgText.appendChild(elm);
1542         break;
1543       case YEARLINE:
1544         this.svgYearLines.appendChild(elm);
1545         break;
1546       case YEARTEXT:
1547         this.svgYearText.appendChild(elm);
1548         break;
1549       default:
1550         break;
1551     }
1552    	return elm;
1553   },
1554 
1555   /**
1556     Clear SVG elements selectively.
1557   
1558     @param has {Object} The old DOM node cache.
1559     @param array {Array} The array of currently rendered DOM nodes.
1560     @param {DOM Element} svg The DOM element to attach to.
1561   */
1562   _clearSVGElms: function(hash, array, svg)
1563   {
1564     for(var key in hash)
1565     {
1566       if(array.indexOf(key) == -1)
1567       {
1568         try
1569         {
1570           svg.removeChild(hash[key].elm);
1571           delete hash[key].elm;
1572           delete hash[key].params;
1573           delete hash[key];
1574         }
1575         catch(e)
1576         {
1577           try
1578           {
1579             delete hash[key];
1580           }
1581           catch(e){}
1582         }
1583       }
1584     }
1585   },
1586 
1587   /**
1588      Initalization function.
1589 
1590      Set up the SVG DOM Nodes.
1591 
1592      Set up the fan menu actions:
1593 
1594      focusedFan: {
1595        CiteSeer: citeseerFunc,
1596        Save: saveFunc,
1597        "Pin Lines": pinLinesFunc,
1598        "Zoom +": zoomInFunc,
1599        "Zoom -": zoomOutFunc
1600      }, 
1601      unfocusedFan: {
1602        CiteSeer: citeseerFunc,
1603        Save: saveFunc,
1604        "Pin Lines": pinLinesFunc,
1605        Refocus: refocusFunc,
1606        "Zoom +": zoomInFunc,
1607        "Zoom -": zoomOutFunc
1608      }     
1609   */
1610   init: function()
1611   {
1612     sc_super();
1613 
1614     // Set up svg canvas.
1615     this.svgCanvas = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
1616     this.rootElement.appendChild(this.svgCanvas);
1617     
1618     this.svgGroup = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1619     this.svgCanvas.appendChild(this.svgGroup);
1620 
1621 
1622     this.svgNodes = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1623     this.svgGroup.appendChild(this.svgNodes);
1624     
1625     this.svgText = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1626     this.svgGroup.appendChild(this.svgText);
1627 
1628     // Set up node default styling.
1629     this.svgText.setAttributeNS(null, 'fill', this.nodeTextColor);
1630     this.svgText.setAttributeNS(null, 'text-anchor', 'middle');
1631     this.svgText.setAttributeNS(null, 'style', "cursor: default");
1632 
1633     // Set up node default styling.
1634     this.svgNodes.setAttributeNS(null, 'fill', this.nodeColor);
1635     this.svgNodes.setAttributeNS(null, 'fill-opacity', this.nodeOpacity);
1636     
1637     this.svgYearText = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1638     this.svgGroup.appendChild(this.svgYearText);
1639 
1640     // Set up node default styling.
1641     this.svgYearText.setAttributeNS(null, 'fill', this.yearTextColor);
1642 
1643     this.svgYearLines = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1644     this.svgGroup.appendChild(this.svgYearLines);
1645     
1646     this.svgYearLines.setAttributeNS(null, 'stroke', this.yearLineColor);
1647     
1648     this.svgEdges = document.createElementNS("http://www.w3.org/2000/svg", 'g');
1649     this.svgGroup.appendChild(this.svgEdges);
1650 
1651 
1652     var refocusFunc = function(evt)
1653     { 
1654       Papercube.viewController.setContentToViewFromGUID(this._mouseDownGUID, 'Paper', NO);
1655     }.bind(this); 
1656 
1657     var citeseerFunc = function(evt)
1658     { 
1659       window.open(Papercube.Paper.find(this._mouseDownGUID).get('url'));
1660     }.bind(this); 
1661 
1662     var saveFunc = function(evt)
1663     { 
1664       Papercube.viewController.saveObject(this._mouseDownGUID, 'Paper');
1665     }.bind(this); 
1666 
1667     var zoomOutFunc = function(evt)
1668     { 
1669       Papercube.canvasController.setZoomToPointerLocation(Event.pointerX(evt), Event.pointerY(evt), NO);
1670     }.bind(this); 
1671     var zoomInFunc = function(evt)
1672     { 
1673       Papercube.canvasController.setZoomToPointerLocation(Event.pointerX(evt), Event.pointerY(evt), YES);
1674     }.bind(this); 
1675     
1676     var pinLinesFunc = function(evt)
1677     { 
1678       this.pinLinesForPaper();
1679     }.bind(this); 
1680 
1681     Papercube.canvasController.registerFans('papersperyear',
1682     {
1683         focusedFan: {
1684           CiteSeer: citeseerFunc,
1685           Save: saveFunc,
1686           "Pin Lines": pinLinesFunc,
1687           "Zoom +": zoomInFunc,
1688           "Zoom -": zoomOutFunc
1689         }, 
1690         unfocusedFan: {
1691           CiteSeer: citeseerFunc,
1692           Save: saveFunc,
1693           "Pin Lines": pinLinesFunc,
1694           Refocus: refocusFunc,
1695           "Zoom +": zoomInFunc,
1696           "Zoom -": zoomOutFunc
1697         }     
1698     });
1699 
1700   }
1701 }) ;
1702