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