1 // ========================================================================== 2 // Papercube.AuthorStatView 3 // 4 // License: PaperCube is open source software released under 5 // the MIT License (see license.js) 6 // ========================================================================== 7 8 require('core'); 9 require('views/paperstatlist'); 10 11 /** @class 12 13 This is a more classical web-based view on an author's citation and references. 14 15 @extends SC.View 16 @author Peter Bergstrom 17 @version 1.0 18 @copyright 2008-2009 Peter Bergström. 19 */ 20 Papercube.AuthorStatView = SC.View.extend( 21 /** @scope Papercube.AuthorStatView.prototype */ { 22 23 /** 24 Collaborator sort order binding. 25 26 @property {String} 27 @binding 'Papercube.authorStatController.collaboratorSortOrder' 28 */ 29 collaboratorSortOrderBinding: 'Papercube.authorStatController.collaboratorSortOrder', 30 31 /** 32 Paper sort order binding. 33 34 @property {String} 35 @binding 'Papercube.authorStatController.paperSortOrder' 36 */ 37 paperSortOrderBinding: 'Papercube.authorStatController.paperSortOrder', 38 39 /** 40 Reference author sort order binding. 41 42 @property {String} 43 @binding 'Papercube.authorStatController.refAuthorSortOrder' 44 */ 45 refAuthorSortOrderBinding: 'Papercube.authorStatController.refAuthorSortOrder', 46 47 /** 48 Citation author sort order binding. 49 50 @property {String} 51 @binding 'Papercube.authorStatController.citeAuthorSortOrder' 52 */ 53 citeAuthorSortOrderBinding: 'Papercube.authorStatController.citeAuthorSortOrder', 54 55 56 /** 57 Outlets for author stat view. 58 59 [ 60 'nameLabel', 61 'collaboratorCount', 62 'paperCount', 63 'refAuthorCount', 64 'citeAuthorCount', 65 'collaboratorList', 66 'paperList', 67 'refAuthorList', 68 'citeAuthorList', 69 'papersPerYear', 70 'graph', 71 'stats' 72 ] 73 74 */ 75 outlets: [ 76 'nameLabel', 77 'collaboratorCount', 78 'paperCount', 79 'refAuthorCount', 80 'citeAuthorCount', 81 'collaboratorList', 82 'paperList', 83 'refAuthorList', 84 'citeAuthorList', 85 'papersPerYear', 86 'graph', 87 'stats' 88 ], 89 90 /** 91 Name label. Bound to the '.name?' element. 92 93 @outlet {DOM Element} '.name?' 94 */ 95 nameLabel: '.name?', 96 97 /** 98 Collaborator count. Bound to the '.collaborator-count?' element. 99 100 @outlet {DOM Element} '.collaborator-count?' 101 */ 102 collaboratorCount: '.collaborator-count?', 103 104 /** 105 Paper count. Bound to the '.paper-count?' element. 106 107 @outlet {DOM Element} '.paper-count?' 108 */ 109 paperCount: '.paper-count?', 110 111 /** 112 refAuthor count. Bound to the '.refauthor-count?' element. 113 114 @outlet {DOM Element} '.refauthor-count?' 115 */ 116 refAuthorCount: '.refauthor-count?', 117 118 /** 119 refAuthor count. Bound to the '.citeauthor-count?' element. 120 121 @outlet {DOM Element} '.citeauthor-count?' 122 */ 123 citeAuthorCount: '.citeauthor-count?', 124 125 /** 126 papersPerYear graph box. Bound to the '.papersperyear?' element. 127 128 @outlet {DOM Element} '.papersperyear?' 129 */ 130 papersPerYear: '.papersperyear?', 131 132 /** 133 papersPerYear graph itself. Bound to the '.data?' element. 134 135 @outlet {DOM Element} '.data?' 136 */ 137 graph: '.data?', 138 139 /** 140 The container for the stat view boxes. Is set to display none by default so that the boxes don't show when there isn't any content. 141 Bound to the '.stats?' element. 142 143 @outlet {DOM Element} '.stats?' 144 */ 145 stats: '.stats?', 146 147 /** 148 paperList view. Shows the author's papers. Bound to the '.paper-list?' element. 149 150 @outlet {Papercube.PaperstatListView} '.paper-list?' 151 */ 152 paperList: Papercube.PaperstatListView.outletFor('.paper-list?'), 153 154 /** 155 collaboratorList view. Shows the author's collaborators. Bound to the '.collaborator-list?' element. 156 157 @outlet {Papercube.PaperstatListView} '.collaborator-list?' 158 */ 159 collaboratorList: Papercube.PaperstatListView.extend({isAuthorList: YES}).outletFor('.collaborator-list?'), 160 161 /** 162 refAuthorList view. Shows the authors that the author has referenced. Bound to the '.refauthor-list?' element. 163 164 @outlet {Papercube.PaperstatListView} '.refauthor-list?' 165 */ 166 refAuthorList: Papercube.PaperstatListView.extend({isAuthorList: YES}).outletFor('.refauthor-list?'), 167 168 /** 169 citeAuthorList view. Shows the authors that have cited the author. Bound to the '.refauthor-list?' element. 170 171 @outlet {Papercube.PaperstatListView} '.citeauthor-list?' 172 */ 173 citeAuthorList: Papercube.PaperstatListView.extend({isAuthorList: YES}).outletFor('.citeauthor-list?'), 174 175 /** 176 The guid of the last clicked paper or author. 177 178 @property {String} 179 */ 180 _mouseDownGUID: null, 181 182 183 /** 184 Reset when new content is set. Is set to YES once the call for the server for additional author data is initiated. 185 186 @property {Boolean} 187 @default NO 188 */ 189 _calledForAuthors: NO, 190 191 /** 192 Reset when new content is set. Is set to YES once the call for the server for additional paper data is initiated. 193 194 @property {Boolean} 195 @default NO 196 */ 197 _calledForPapers: NO, 198 199 /** 200 Re-render the view when the content, sort order changes, or view is set to be visible. 201 202 @observes content 203 @observes isVisible 204 @observes paperSortOrder 205 @observes collaboratorSortOrder 206 @observes refAuthorSortOrder 207 @observes citeAuthorSortOrder 208 */ 209 contentDidChange: function() 210 { 211 212 var content = this.get('content'); 213 214 // If there is no content or if you're not visible, bail. 215 if(!this.get("isVisible") || !content) return; 216 217 // Show the stats div. 218 this.stats.style.display = ''; 219 220 // If there is a new content, reset the server call variables, cache the content, and clear the graph. 221 if(!this._cachedContent || this._cachedContent.get('guid') != content.get('guid')) 222 { 223 this.papersPerYear.className = 'papersperyear'; 224 this.papersPerYear.className = 'papersperyear show'; 225 this._cachedContent = content; 226 this._calledForAuthors = NO; 227 this._calledForPapers = NO; 228 this._clearGraph(); 229 } 230 231 var callBack = function() { this.contentDidChange(); }.bind(this); 232 233 // Get authors for collabs, refs, and cites. 234 if(!this._calledForAuthors) 235 { 236 var authGuids = []; 237 authGuids = authGuids.concat(this._collectAuthors(content.get('collaborators'))); 238 authGuids = authGuids.concat(this._collectAuthors(content.get('refAuthors'))); 239 authGuids = authGuids.concat(this._collectAuthors(content.get('citeAuthors'))); 240 241 if(authGuids.length > 0) 242 { 243 Papercube.adaptor.getAuthorDetails(authGuids, callBack); 244 } 245 this._calledForAuthors = YES; 246 } 247 248 // Render the lists. 249 this._renderList('collaborators'); 250 this._renderList('refAuthors'); 251 this._renderList('citeAuthors'); 252 253 // Get papers for the author. 254 var shouldLoadPapers = NO; 255 if(!this._calledForPapers) 256 { 257 //console.log('called for papers'); 258 259 var paperGuids = []; 260 var papers = content.get('papers'); 261 for(var i=0; i<papers.length; i++) 262 { 263 var p = Papercube.Paper.find(papers[i]); 264 if(!p) 265 { 266 paperGuids.push(papers[i]); 267 } 268 } 269 270 var shouldLoadPapers = (paperGuids.length > 0); 271 272 if(shouldLoadPapers) 273 { 274 Papercube.adaptor.getPaperDetails(paperGuids, callBack); 275 } 276 277 this._calledForPapers = YES; 278 } 279 280 if(!shouldLoadPapers) 281 this._renderGraph(); 282 283 // Render paper list. 284 this._renderList('papers'); 285 286 // Reset zoom value. 287 Papercube.canvasController.set("zoomValueMax", 1); 288 Papercube.canvasController.set("zoomStep", 1); 289 290 // Set the title and year. 291 this.nameLabel.innerHTML = content.get('name').escapeHTML(); 292 293 }.observes('content', 'isVisible', 'paperSortOrder', 'collaboratorSortOrder', 'refAuthorSortOrder', 'citeAuthorSortOrder'), 294 295 /** 296 Create the request and call the server. 297 298 @param auths {Array} The author relation array. 299 300 @returns {Array} Returns a list of guids that are not in the data store. 301 */ 302 _collectAuthors: function(auths) 303 { 304 var authGuids = []; 305 for(var i=0; i<auths.length; i++) 306 { 307 var a = Papercube.Author.find(auths[i][0]); 308 if(!a) 309 { 310 authGuids.push(auths[i][0]); 311 } 312 } 313 return authGuids; 314 }, 315 316 /** 317 Render a list of papers or authors. 318 319 @param type {string} The type of the list shown. Either 'collaborators', 'papers', 'refAuthors', or 'citeAuthors'. 320 321 @returns {Boolean} Returns NO if there is no content. 322 */ 323 _renderList: function(type) 324 { 325 var content = this.get('content'); 326 if(!content) 327 { 328 if(type == 'collaborators') 329 { 330 this.collaboratorList.set("content", []); 331 } 332 else if(type == "papers") 333 { 334 this.paperList.set("content", []); 335 } 336 else if(type == "refAuthors") 337 { 338 this.refAuthorList.set("content", []); 339 } 340 else if(type == "citeAuthors") 341 { 342 this.citeAuthorList.set("content", []); 343 } 344 return NO; 345 } 346 347 // If the type is papers, get the papers, sort them, then set the content. 348 if(type == 'papers') 349 { 350 351 // Get the papers to sort. 352 var rel = content.get('papers'); 353 354 var papers = []; 355 for(var i=0; i<rel.length; i++) 356 { 357 var paper = Papercube.Paper.find(rel[i]); 358 if(paper) 359 { 360 papers.push(paper); 361 } 362 } 363 364 var sortType = this.get('paperSortOrder'); 365 366 // Sort the array. 367 // 'Title', 'Year', 'References', 'Citations', 'Num Authors' 368 switch(sortType) 369 { 370 case 'Title': 371 papers.sort(function(a, b) 372 { 373 var aTitle = a._attributes.title.toLowerCase(); 374 var bTitle = b._attributes.title.toLowerCase(); 375 if(aTitle == bTitle) return 0; 376 if(aTitle < bTitle) return -1; 377 return 1; 378 }); 379 break; 380 case 'Date': 381 papers.sort(function(a, b) 382 { 383 var aDate = a.get('year'); 384 var bDate = b.get('year'); 385 if(aDate == bDate) 386 return a._attributes.title.toLowerCase() < b._attributes.title.toLowerCase(); 387 if(aDate > bDate) return -1; 388 return 1; 389 }); 390 break; 391 case 'Num Refs': 392 case 'Num Cites': 393 case 'Num Authors': 394 var sortKey = (sortType == 'Num Refs') ? 'refCount' : (sortType == 'Num Cites') ? 'citeCount' : 'authorCount'; 395 papers.sort(function(a, b) 396 { 397 var aCnt = a.get(sortKey); 398 var bCnt = b.get(sortKey); 399 if(aCnt == bCnt) return 0; 400 if(aCnt > bCnt) return -1; 401 return 1; 402 }); 403 break; 404 } 405 406 this.paperCount.innerHTML = ' (' + papers.length+')'; 407 this.paperList.set("content", papers); 408 this.paperList.setClassName('show', (papers.length != 0)); 409 } 410 411 // If the type is authors, get the authors, sort them, then set the content. 412 else 413 { 414 415 // Get the authors to be sorted. 416 var auths = content.get(type); 417 418 var authors = []; 419 for(var i=0; i<auths.length; i++) 420 { 421 var author = Papercube.Author.find(auths[i][0]); 422 if(author) 423 { 424 authors.push(author); 425 } 426 } 427 428 // Pick the sort type to read from. 429 var sortType = (type == "collaborators") ? this.get('collaboratorSortOrder') : (type == "refAuthors") ? this.get('refAuthorSortOrder') : this.get("citeAuthorSortOrder"); 430 431 // Sort the array. 432 // 'Title', 'Year', 'References', 'Citations', 'Num Authors' 433 switch(sortType) 434 { 435 case 'Name': 436 authors.sort(function(a, b) 437 { 438 var aName = a._attributes.name.toLowerCase(); 439 var bName = b._attributes.name.toLowerCase(); 440 if(aName == bName) return 0; 441 if(aName < bName) return -1; 442 return 1; 443 }); 444 break; 445 case 'Num Papers': 446 authors.sort(function(a, b) 447 { 448 var aCnt = a.get('paperCount'); 449 var bCnt = b.get('paperCount'); 450 if(aCnt == bCnt) return 0; 451 if(aCnt > bCnt) return -1; 452 return 1; 453 }); 454 break; 455 case 'Num Collabs': 456 case 'Cite Count': 457 var sortKey = (sortType == 'Num Collabs') ? 'collaborators' : 'citeAuthors'; 458 authors.sort(function(a, b) 459 { 460 var aCnt = a.get(sortKey).length; 461 var bCnt = b.get(sortKey).length; 462 if(aCnt == bCnt) return 0; 463 if(aCnt > bCnt) return -1; 464 return 1; 465 }); 466 break; 467 } 468 469 // Set the content to the appropriate list and count label. 470 if(type == "collaborators") 471 { 472 this.collaboratorCount.innerHTML = ' (' + authors.length+')'; 473 this.collaboratorList.set("content", authors); 474 this.collaboratorList.setClassName('show', (authors.length != 0)); 475 476 } 477 else if(type == "refAuthors") 478 { 479 this.refAuthorCount.innerHTML = ' (' + authors.length+')'; 480 this.refAuthorList.set("content", authors); 481 this.refAuthorList.setClassName('show', (authors.length != 0)); 482 483 } 484 else 485 { 486 this.citeAuthorCount.innerHTML = ' (' + authors.length+')'; 487 this.citeAuthorList.set("content", authors); 488 this.citeAuthorList.setClassName('show', (authors.length != 0)); 489 } 490 } 491 }, 492 493 /** 494 Render the graph. 495 496 */ 497 _renderGraph: function() 498 { 499 // Clear the graph. 500 this._clearGraph(); 501 var rel = this._cachedContent.get('papers'); 502 var perYearCountHash = {}; 503 var numYears = 0; 504 var maxPapers = 0; 505 506 // Loop through the relations and collect the items by year. 507 for(var i=0; i<rel.length; i++) 508 { 509 var paper = Papercube.Paper.find(rel[i]); 510 var yr = '?'; 511 if(paper) 512 yr = paper.get('year'); 513 514 if(!perYearCountHash[yr]) 515 { 516 numYears ++; 517 perYearCountHash[yr] = 0; 518 } 519 perYearCountHash[yr]++; 520 if(perYearCountHash[yr] > maxPapers) 521 { 522 maxPapers = perYearCountHash[yr]; 523 } 524 } 525 526 // Flatten to an array. 527 var years = []; 528 for(var key in perYearCountHash) 529 { 530 years.push([key, perYearCountHash[key]]); 531 } 532 533 // Sort the years. 534 years.sort(function(a,b){ if(a[0]<b[0]) return 1; return -1;}); 535 536 var html = ''; 537 538 // Calculate the graph dimensions. 539 var height = 135; 540 var width = 500; 541 var rowHeight = height/numYears; 542 var rowWidth = width/maxPapers; 543 var barHeight = (rowHeight-2*rowHeight/3); 544 var halfBarHeight = ((barHeight/2)-12/2); 545 546 // Loop through the year array and render the HTML. 547 for(var i=0; i<years.length; i++) 548 { 549 var count = years[i][1]; 550 var str = (count*rowWidth > 50) ? Papercube.pluralizeString(' Paper', count)+' ' : ''; 551 html+= '<div class="row" style="height:'+rowHeight+'px;"><div class="year" style="margin-top:'+((rowHeight/2)-11/2)+'px; height:'+(rowHeight-2*rowHeight/3)+'px;">'+years[i][0]+ 552 '</div><div class="bar" style="padding-top: '+halfBarHeight+'px; margin-top:'+(rowHeight-2*rowHeight/3)+'px; width:'+(count*rowWidth)+'px; height:'+barHeight+'px;">'+str+'</div></div>'; 553 } 554 555 // Set the HTML. 556 this.graph.innerHTML = html; 557 }, 558 559 /** 560 Clear the graph innerHTML. 561 562 */ 563 _clearGraph: function() 564 { 565 this.graph.innerHTML = ''; 566 }, 567 568 /** 569 Save the GUID of the element and then show the fan. 570 571 @param {DOM Event} evt The mouseDown event. 572 */ 573 mouseDown: function(evt) 574 { 575 var guid = evt.target.getAttribute('guid'); 576 var type = evt.target.getAttribute('fanType'); 577 if(guid) 578 { 579 this._mouseDownGUID = guid; 580 Papercube.canvasController.showFan(Event.pointerX(evt), Event.pointerY(evt), 'paperstat', (type+"Fan")); 581 return YES; 582 } 583 return NO; 584 }, 585 586 /** 587 Initalization function. 588 589 Set up the fan menu actions: 590 591 authorFan: { 592 Save: saveFuncAuth, 593 Refocus: refocusFuncAuth 594 }, 595 paperFan: { 596 CiteSeer: citeseerFunc, 597 Save: saveFunc, 598 Refocus: refocusFunc 599 } 600 601 */ 602 init: function() 603 { 604 sc_super(); 605 606 var refocusFunc = function(evt) 607 { 608 Papercube.viewController.setContentToViewFromGUID(this._mouseDownGUID, 'Paper', NO); 609 }.bind(this); 610 611 var refocusFuncAuth = function(evt) 612 { 613 Papercube.viewController.viewAuthor(this._mouseDownGUID); 614 }.bind(this); 615 616 var citeseerFunc = function(evt) 617 { 618 window.open(Papercube.Paper.find(this._mouseDownGUID).get('url')); 619 }.bind(this); 620 621 var saveFunc = function(evt) 622 { 623 Papercube.viewController.saveObject(this._mouseDownGUID, 'Paper'); 624 }.bind(this); 625 626 627 var saveFuncAuth = function(evt) 628 { 629 Papercube.viewController.saveObject(this._mouseDownGUID, 'Author'); 630 }.bind(this); 631 632 633 // Register two fans: authors and papers. 634 Papercube.canvasController.registerFans('authorstat', 635 { 636 authorFan: { 637 Save: saveFuncAuth, 638 Refocus: refocusFuncAuth 639 }, 640 paperFan: { 641 CiteSeer: citeseerFunc, 642 Save: saveFunc, 643 Refocus: refocusFunc 644 } 645 }); 646 } 647 }) ; 648