Skip to content

Instantly share code, notes, and snippets.

@thisismattmiller
Created February 22, 2012 06:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thisismattmiller/1882090 to your computer and use it in GitHub Desktop.
Save thisismattmiller/1882090 to your computer and use it in GitHub Desktop.
Client for visualMOA.org
vmoa = {
searchMainTerm: null, //the inital search term
searchSecondTerm: null, //the refined search term
searchSecondType: null, //the refined part of speach of that search term
searchThirdYear: null, //the year selected from the n-Gram
searchThirdTerm: null, //the connected term selected from the ngram page (like the top 10 word)
searchThirdType: null, //the part of speach of the seelcted connected word
searchMainCount: 0,
authorNetworkObject: null, //the data object
authorNetworkD3Force: null, //the d3 objects to interact with the network graph
authorNetworkD3Vis: null, //the d3 vis element
authorNetworkD3Nodes: null, //the d3 nodes
authorNetworkD3Links: null, // ' ' links
authorNetworkScale: null, // the d3 scale function to size the nodes
words100k: [], //holds the 100k words
awsURI: "", //the url to the aws instance
ocrToolSkip: 0,
History: null, //holds the nav object
//lookup table for the journals
journals: [
{id: "amis", title : "American Missionary", dates : "1878 - 1901"},
{id: "amwh", title : "American Whig Review", dates: "1845 - 1852"},
{id: "atla", title : "Atlantic Monthly", dates: "1857 - 1901"},
{id: "bays", title : "Bay State Monthly", dates: "1884 - 1886"},
{id: "cent", title : "The Century", dates: "1881 - 1899"},
{id: "cont", title : "Continental Monthly", dates: "1862 - 1864"},
{id: "gala", title : "The Galaxy", dates: "1866 - 1878"},
{id: "harp", title : "Harper's New Monthly Magazine", dates: "1850 - 1899"},
{id: "intr", title : "International Monthly Magazine", dates: "1850 - 1852"},
{id: "newe", title : "The New England Magazine", dates: "1886 - 1900"},
{id: "nwen", title : "The New-England Magazine", dates: "1831 - 1835"},
{id: "livn", title : "The Living Age", dates: "1844 - 1900"},
{id: "manu", title : "Manufacturer and Builder", dates: "1869 - 1894"},
{id: "nwng", title : "New Englander", dates: "1843 - 1892"},
{id: "nora", title : "North American Review", dates: "1815 - 1900"},
{id: "oldg", title : "The Old Guard", dates: "1863 - 1867"},
{id: "punc", title : "Punchinello", dates: "1870"},
{id: "putn", title : "Putnam's Monthly", dates: "1853 - 1870"},
{id: "scia", title : "Scientific American", dates: "1846 - 1869"},
{id: "scmo", title : "Scribner's Monthly", dates: "1870 - 1881", wiki: "http://en.wikipedia.org/wiki/Scribner's_Monthly"},
{id: "scri", title : "Scribner's Magazine", dates: "1887 - 1896", wiki: "http://en.wikipedia.org/wiki/Scribner's_Magazine"},
{id: "usde", title : "The United States Democratic Review", dates: "1837 - 1859", wiki: "http://en.wikipedia.org/wiki/The_United_States_Magazine_and_Democratic_Review"}
],
months: ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
/*---------------------------
Updates the db with the user reply
-----------------------------*/
ocrToolUpdate: function(word, review, incorrect, correct){
if (typeof correct == 'undefined'){correct='null';}
if (typeof incorrect == 'undefined'){incorrect='null';}
//review: 1= needs review 2= its okay, 3 = incorrect, correct and incorrect are supplied
var req = $.getJSON( "/ocrToolUpdate/" + word + "/" + review + "/" + incorrect + "/" + correct)
.done(function(json) {
vmoa.ocrToolNext();
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
display and setup the ocr tool
-----------------------------*/
ocrToolInit: function(){
vmoa.History.pushState({state:8}, "VisualMOA | OCR Correct", "?ocr");
this.ocrToolNext();
},
/*---------------------------
loads the next OCR problem and display it
-----------------------------*/
ocrToolNext: function(){
this.toggleWorking(true);
var req = $.getJSON( "/ocrToolNext/" + vmoa.ocrToolSkip)
.done(function(json) {
$('#ocrToolErrors').hide('fast');
$('#ocrToolIncorrect').val(json[0]._id)
$('#ocrToolCorrect').val("")
$('#ocrToolWord').text(json[0]._id);
var sentence = json[0].sentence;
sentence= sentence.replace(json[0]._id,'<span style="text-decoration:underline">' + json[0]._id + '</span>');
sentence= sentence.replace(vmoa.capitaliseFirstLetter(json[0]._id),'<span style="text-decoration:underline">' + json[0]._id + '</span>');
$('#ocrToolSentence').html(sentence);
vmoa.toggleWorking(false);
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
Start of the 100k word freq
-----------------------------*/
int100kWords: function(){
this.toggleWorking(true);
vmoa.History.pushState({state:7}, "VisualMOA | 100K Words", "?100k");
//load the big json file for all the words, this is not the most optimal way to do it but I rather put
//the load of servering a larger file from the satic server (a lot of bandwidth) than 100s requests to the node server
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://www.visualmoa.org/js/words.js"; // use this for linked script
document.body.appendChild(script);
},
/*---------------------------
Start of the 100k word freq
-----------------------------*/
build100kWords: function(){
//this.toggleWorking(true);
//$("#authorsList").empty();
//$("#authorsNetworkChart svg").remove();
$(document).keydown(function(e){
if (e.keyCode == 37) {
if (parseInt($("#wordFreqRange").val())>0){
$("#wordFreqRange").val(parseInt($("#wordFreqRange").val())-1);
$("#wordFreqRange").change();
}
return false;
}
if (e.keyCode == 39) {
if (parseInt($("#wordFreqRange").val())<100000){
$("#wordFreqRange").val(parseInt($("#wordFreqRange").val())+1);
$("#wordFreqRange").change();
}
return false;
}
});
$("#wordFreqContextLink").on("click",function(event){
var req = $.getJSON( "/wordContext/" + encodeURIComponent($("#wordFreqActiveWord").text()))
.done(function(json) {
var sentence = json[0].sentence;
//clean it up a little
sentence = sentence.replace(/ /g," ");
sentence = sentence.replace(/_/g,"");
//wont catach upper case but ehhh...
sentence = sentence.replace($("#wordFreqActiveWord").text(),'<span style="text-decoration:underline">' + $("#wordFreqActiveWord").text() + "</span>");
sentence = sentence.replace(vmoa.capitaliseFirstLetter($("#wordFreqActiveWord").text()),'<span style="text-decoration:underline">' + $("#wordFreqActiveWord").text() + "</span>");
sentence = sentence.replace($("#wordFreqActiveWord").text().toUpperCase(),'<span style="text-decoration:underline">' + $("#wordFreqActiveWord").text() + "</span>");
$("#wordFreqContext").html(sentence);
$("#wordFreqContext").show('fast');
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
event.preventDefault();
return false;
});
$("#wordFreqFilter").on("keyup",function(){
if (vmoa.words100k.indexOf($(this).val())!=-1){
$("#wordFreqRange").val(parseInt(vmoa.words100k.indexOf($(this).val())));
$("#wordFreqRange").change();
}
});
$("#wordFreqRange").on("change",function(){
$("#wordFreqContext").hide('fast');
$("#wordFreqPlace").text(vmoa.numberWithCommas(parseInt($(this).val())) + vmoa.returnOrdinal(parseInt($(this).val())) );
$("#wordFreqPreviousWord").text(vmoa.words100k[parseInt($(this).val())-1]);
$("#wordFreqPreviousWord").css("left", -1*parseInt($("#wordFreqPreviousWord").width())+175);
$("#wordFreqActiveWord").text(vmoa.words100k[parseInt($(this).val())]);
$("#wordFreqNextWord").text(vmoa.words100k[parseInt($(this).val())+1]);
});
$("#wordFreqRange").change();
this.toggleWorking(false);
},
/*---------------------------
Loads and displays the authors articles
-----------------------------*/
authorNetworkBuildArticles: function(author, terms){
//make it csv
terms = terms.join(",");
$("#authorsNetworkArticlesBody").empty();
$("#authorsNetworkArticles").show('fast');
$("#authorsNetworkArticlesHeader").html('Articles by ' + author + ' about : <span>' + terms.replace(/,/g,', ') + '</span>');
var req = $.getJSON( "/authors/filter/" +author+'/'+terms)
.done(function(json) {
for (article in json){
$("#authorsNetworkArticlesBody").append(
$("<div>")
.addClass('authorsNetworkArticlesHolder')
.click(function(){
$($(this).children()[2]).show('fast');
$($(this).children()[3]).show('fast');
})
.append(
//the journal and the year
$("<div>")
.addClass('authorsNetworkArticlesMeta')
.text(vmoa.returnJournalData(json[article].journal).title + ' - ' + json[article].year),
//the title of the article
$("<div>")
.addClass('authorsNetworkArticlesTitle')
.text(json[article].title),
$("<div>")
.addClass('authorsNetworkArticlesTerms')
.text('Article is about: ' + json[article].terms.join(", ")),
$("<div>")
.addClass('authorsNetworkArticlesSentence')
.html(function(){
searchTerms = encodeURIComponent(json[article].title); //make it url safe
//try to grab the last name of the author
var lastName = author.split(' ')[author.split(' ').length-1];
var query = "http://ebooks.library.cornell.edu/cgi/t/text/text-idx?ALLSELECTED=1&xc=1&g=moagrp&type=simple&rgn=full+text&q1=" + searchTerms + "&Submit=Search&c=" + vmoa.returnJournalData(json[article].journal).id + "&cite1=" + lastName + "&cite1restrict=author&cite2=" + searchTerms + "&cite2restrict=title" ;
return "Core Sentence:<br>" + json[article].sentence + '<br><a href="' + query + '" target="_target">Search for article in MOA archive</a>';
})
)
);
}
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
Start of the author network
name = the name of the author to add to the network
onlyOneTerm = if populated it means that the author will only be added for that one term
addMoreIcon = if true it will add a plus symbol node to allow for more authors to be added to a term.
-----------------------------*/
authorNetworkAddAuthor: function(name,onlyOneTerm,addMoreIcon){
if (typeof onlyOneTerm == 'undefined'){onlyOneTerm=false;}
if (typeof addMoreIcon == 'undefined'){addMoreIcon=false;}
//is it already added?
for (aNode in this.authorNetworkD3Nodes){
//if this is only one term req then we want to add duplicate authors to the network because they can appear off of multiple subjects
if (onlyOneTerm){
//we never want to add the same author that was just linked to this subject, so make sure this is not the root author node
if (this.authorNetworkD3Nodes[aNode].id==name && this.authorNetworkD3Nodes[aNode].term == false){return false;}
//now dont add anyone again who is already attached to this node
if (this.authorNetworkD3Nodes[aNode].id==name && this.authorNetworkD3Nodes[aNode].term == onlyOneTerm){return false;}
}else{
//otherwise do not add duplicate authors
if (this.authorNetworkD3Nodes[aNode].id==name){return false;}
}
}
//find it in the data obj
for (author in this.authorNetworkObject){
if (this.authorNetworkObject[author]._id==name){
var newAuthorNode = {id: this.authorNetworkObject[author]._id, type : "author", size : this.authorNetworkObject[author].value, term: onlyOneTerm, connectedTerms : []};
vmoa.authorNetworkD3Nodes.push(newAuthorNode);
//now loop through all the topics and see if they are added and link them
var index = -1;
for (aTerm in this.authorNetworkObject[author].terms){
if (onlyOneTerm){
if (onlyOneTerm!=this.authorNetworkObject[author].terms[aTerm]){
continue;
}
}
var newSubjectNode = {id: this.authorNetworkObject[author].terms[aTerm], type : "subject"};
index = -1;
for (aNode in this.authorNetworkD3Nodes){
if (this.authorNetworkD3Nodes[aNode].id == this.authorNetworkObject[author].terms[aTerm]){
index = aNode;
}
}
if (index==-1){
//not yet added,
vmoa.authorNetworkD3Nodes.push(newSubjectNode);
vmoa.authorNetworkD3Links.push({target : newAuthorNode, source : newSubjectNode});
}else{
//already in here
vmoa.authorNetworkD3Links.push({target : this.authorNetworkD3Nodes[index], source : newAuthorNode});
}
//push this term into the authors terms
vmoa.authorNetworkD3Nodes[vmoa.authorNetworkD3Nodes.indexOf(newAuthorNode)].connectedTerms.push(this.authorNetworkObject[author].terms[aTerm]);
}
if(addMoreIcon){
var addMore = {id: "more " + this.authorNetworkD3Nodes[index].id, term : this.authorNetworkD3Nodes[index].id, type : "more", size : 5};
vmoa.authorNetworkD3Nodes.push(addMore);
vmoa.authorNetworkD3Links.push({target : this.authorNetworkD3Nodes[index], source : addMore});
addMoreIcon=false;
}
this.authorNetworkRedraw();
}
}
},
/*---------------------------
redraw the nodes and links
-----------------------------*/
authorNetworkRedraw: function(){
var link = vmoa.authorNetworkD3Vis.selectAll("line.link")
.data(vmoa.authorNetworkD3Links, function(d) { return d.source.id + "-" + d.target.id; });
link.enter().insert("svg:line", "g.node")
.style("stroke","#C8C891")
.attr("class", "link");
link.exit().remove();
var node = vmoa.authorNetworkD3Vis.selectAll("g.node")
.data(vmoa.authorNetworkD3Nodes, function(d) { return d.id;});
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.style("fill", "#444")
.on("mouseover", function(d, i){d3.select(this).style('fill',"#e3774f") ;})
.on("mouseout", function(d, i){d3.select(this).style("fill", "#444");})
.on("dblclick",function(d){
//if it is a author then load related articles
if (d.type=='author'){
vmoa.authorNetworkBuildArticles(d.id, d.connectedTerms);
}
})
.on("click",function(d){
var total = 0;
//we need to know how many there are to begin with
for (author in vmoa.authorNetworkObject){
if (vmoa.authorNetworkObject[author].terms.indexOf(d.id) != -1){ total++; }
}
if (d.type=='author'){
if (d3.event.shiftKey){
//we want to disconnect this author node from the subjects and re-add it
for (aNode in vmoa.authorNetworkD3Nodes){
if(vmoa.authorNetworkD3Nodes[aNode].id==d.id && d.term != false){
var removedNode = vmoa.authorNetworkD3Nodes.splice(aNode,1);
//now remove the link
for (aLink in vmoa.authorNetworkD3Links){
if (vmoa.authorNetworkD3Links[aLink].source.id == d.id){
vmoa.authorNetworkD3Links.splice(aLink,1);
}
}
vmoa.authorNetworkRedraw();
//add back in
vmoa.authorNetworkAddAuthor(d.id);
return;
}
}
}
}
//if it is a more, add a plus sign load some more authors node
if (d.type=='more'){
alreadyAuthors = [];
//loop through all the nodes and find the authors that are already dispalyed for this term
for (aNode in vmoa.authorNetworkD3Nodes){
if (vmoa.authorNetworkD3Nodes[aNode].term==d.term){
alreadyAuthors.push(vmoa.authorNetworkD3Nodes[aNode].id);
}
}
//now loop through and add it if it is not already added
var count = 1;
var totalCount = 1;
for (author in vmoa.authorNetworkObject){
if (count>6){break;}
//does it have this term in it?
if (vmoa.authorNetworkObject[author].terms.indexOf(d.term)!=-1){
totalCount=totalCount+1;
//make sure it is not already on there
if (alreadyAuthors.indexOf(vmoa.authorNetworkObject[author]._id)==-1){
//add it to the graph
if (count<6){
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.term,false);
}else{
//if there are more add the load more node
if (total > 11){
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.term,true);
}else{
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.term,false);
}
}
count++;
}
}
}
}
//if it is a subject expand other authors
if (d.type=='subject'){
var count = 1;
for (author in vmoa.authorNetworkObject){
//console.log(vmoa.authorNetworkObject[author]);
if (count>6){break;}
//does it have this term in it?
if (vmoa.authorNetworkObject[author].terms.indexOf(d.id)!=-1){
//add it to the graph
if (count<6){
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.id);
}else{
//if there are more add the load more node
if (total > 6){
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.id,true);
}else{
vmoa.authorNetworkAddAuthor(vmoa.authorNetworkObject[author]._id,d.id);
}
}
count++;
}
}
}
})
.call(vmoa.authorNetworkD3Force.drag);
nodeEnter.append("svg:image")
.attr("class", "circle")
.attr("cursor","pointer")
.attr("xlink:href", function(d){
if (d.type=="subject"){return "http://www.visualmoa.org/img/book.png";}
if (d.type=="author"){return "http://www.visualmoa.org/img/author.png";}
if (d.type=="more"){return "http://www.visualmoa.org/img/plus.png";}
})
.attr("x", function(d){
if (d.type=="author"){return vmoa.authorNetworkScale(d.size) / 2 * -1;}
if (d.type=="subject"){return -13.3775;}
if (d.type=="more"){return -12.5;}
})
.attr("y", function(d){
if (d.type=="author"){return vmoa.authorNetworkScale(d.size) / 1.25 * -1;}
if (d.type=="subject"){return -7.5;}
if (d.type=="more"){return -12.5;}
})
.attr("height", function(d){
if (d.type=="author"){return vmoa.authorNetworkScale(d.size);}
if (d.type=="subject"){return 15;}
if (d.type=="more"){return 25;}
})
.attr("width", function(d){
if (d.type=="author"){return vmoa.authorNetworkScale(d.size);}
if (d.type=="subject"){return 26.755;}
if (d.type=="more"){return 25;}
});
//.attr("width", "16px")
//.attr("height", "16px");
nodeEnter.append("title")
.text(function(d) {
if (d.type=="more"){return "Click to load more authors for this subject.";}
});
nodeEnter.append("svg:text")
.attr("class", "nodetext")
.style("font-size", 12)
.attr("cursor","pointer")
.style("font-weight",function(d){
if (d.type=="author"){return "bold";}
})
.attr("dx", function(d){return d.id.length * 2.8 * -1; })
.attr("dy", 20)
.text(function(d) {
if (d.type=="more"){return "";}
return d.id
});
node.exit().remove();
vmoa.authorNetworkD3Force.start();
},
/*---------------------------
Start of the author network
-----------------------------*/
buildAuthorNetwork: function(){
this.toggleWorking(true);
//$("#authorsList").empty();
//$("#authorsNetworkChart svg").remove();
vmoa.History.pushState({state:6}, "VisualMOA | Author Network", "?authorNetwork");
//if it is alreadt running don't re make it
if ($("#authorsList").children().length!=0){this.toggleWorking(false); return;}
var req = $.getJSON( "/authors/")
.done(function(json) {
$('#suthorsNetworkHelpClose').click();
vmoa.authorNetworkObject = json;
//console.log(json);
//create the list of people
maxMinRange = [];
for (author in json){
maxMinRange.push(json[author].value);
$("#authorsList").append(
$("<div>")
.addClass('authorListDivHolder')
.append(
$("<div>")
.text(json[author]._id)
.addClass('authorListDiv')
.data("_id",json[author]._id)
.click(function(){
vmoa.authorNetworkAddAuthor($(this).data('_id'));
$('#authorsNetworkHelp').hide('fast');
})
.mouseover(function(){
var domEle = $(this).parent().children()[1];
$(domEle).css('visibility','visible');
})
.mouseout(function(){
var domEle = $(this).parent().children()[1];
$(domEle).css('visibility','hidden');
})
).append(
$("<div>")
.text('')
.addClass('authorWikiLinkDiv')
.mouseover(function(){
$(this).css('visibility','visible');
})
.mouseout(function(){
$(this).css('visibility','hidden');
}).append(
$("<a>")
.attr('target','_blank')
.attr('href','http://www.google.com/search?q=' + json[author]._id.split(' ').join('+') + '+writer+wikipedia&btnI')
.attr('title','Try a Wikipedia search for this author')
.append(
$("<img>")
.attr("src","http://www.visualmoa.org/img/wikipedia-icon.gif")
.attr("border","0")
.css('height','18px').css('width','auto')
)
)
)
);
}
vmoa.authorNetworkScale = d3.scale.linear()
.domain([d3.min(maxMinRange), d3.max(maxMinRange)])
.range([15, 50]);
$("#authorsNetwork").css("height", $(window).height() - 63 + "px");
$("#authorsList").css("height", $(window).height() - 63 + "px");
$("#authorsNetworkChart").css("height", $(window).height() - 63 + "px");
$("#authorsNetworkArticles").css("height", $(window).height() - 63 + "px");
//inilaize the network graph stuff
var w = $("#authorsNetworkChart").width(),
h = $("#authorsNetworkChart").height();
vmoa.authorNetworkD3Force = d3.layout.force()
.gravity(0.05)
.distance(110)
.charge(-220)
.size([w, h]);
vmoa.authorNetworkD3Nodes = vmoa.authorNetworkD3Force.nodes();
vmoa.authorNetworkD3Links = vmoa.authorNetworkD3Force.links();
vmoa.authorNetworkD3Vis = d3.select("#authorsNetworkChart").append("svg:svg")
.attr("width", w)
.attr("height", h);
vmoa.authorNetworkD3Force.on("tick", function() {
vmoa.authorNetworkD3Vis.selectAll("g.node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
vmoa.authorNetworkD3Vis.selectAll("line.link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
});
vmoa.toggleWorking(false);
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*
when there are no results.
*/
noResults: function(spanDOM){
History.back();
$(spanDOM).fadeIn('slow', function() {
$(spanDOM).fadeOut(4000);
});
},
/*---------------------------
starts off the article network tool
-----------------------------*/
searchArticles: function(){
this.toggleWorking(true);
if ($('#inputSearchArticle').val().length==0){return false;}
$("#articleNetworkChart").empty();
$("#articleNetworkInfoTitle").empty();
$("#articleNetworkInfoTerms").empty();
$("#articleNetworkInfoSentence").empty();
$("#articleNetworkInfoJournal").empty();
this.toggleWorking(true);
//hide the search stuff
$('#search').fadeOut(100);
vmoa.History.pushState({state:5}, "VisualMOA | Article Tree", "?articleTree");
this.searchMainTerm = $.trim($('#inputSearchArticle').val());
var req = $.getJSON( "/articles/" + this.searchMainTerm.toLowerCase() )
.done(function(json) {
if (json.children.length==0){
vmoa.toggleWorking(false);
vmoa.noResults('#noResultsAT');
return false;
}
$('#articleNetworkInfo').fadeIn('slow');
$('#articleNetworkInstructions').css('display','block');
var m = [0, 120, 0, 120],
w = 1280 - m[1] - m[3],
h = (json.children.length*20) - m[0] - m[2],
i = 0,
duration = 500,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#articleNetworkChart").append("svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
root = json;
root.x0 = h / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse();
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 190; });
// Update the nodes…
var node = vis.selectAll("g.artTree")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "artTree")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "#C8C891" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".25em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6)
//provide a little handel to mouse over
nodeEnter.append("rect")
.attr("x", function(d) { return -5; })
.attr("y", function(d) { return -5; })
.attr("height", 10)
.attr("cursor","pointer")
.attr("width",100)
.style("fill-opacity", 0)
.attr("visibility", function(d) { return d._children ? "hidden" : "visible"; })
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; })
.on("mouseover", function(d){
if (typeof d.sentence == "string"){
$('#articleNetworkInstructions').css('display','none');
$('#articleNetworkInfoTitle').text(d.name + " (" + d.year + ")");
$('#articleNetworkInfoTerms').empty();
$('#articleNetworkInfoTerms').html("This article is about:<br>")
var terms = d.terms.split(",");
for (term in terms){
$('#articleNetworkInfoTerms').append(
$("<a>")
.attr("href","#")
.text(terms[term])
.data("term",terms[term])
.click(function(event){
$('#inputSearchArticle').val($(this).data('term'));
vmoa.searchArticles();
event.preventDefault();
return false;
})
).append(
$("<span>").text(" ")
);
}
$('#articleNetworkInfoSentence').html("Core Sentence:<br>");
$('#articleNetworkInfoSentence').append(
$("<span>").text(d.sentence)
);
$('#articleNetworkInfoJournal')
.text("Journal: " + vmoa.returnJournalData(d.journal).title)
.append("<br>")
.append(
$("<a>")
.text("View article at MOA Archive")
.attr("target", "_blank")
.attr('href', function(){
searchTerms = encodeURIComponent(d.name); //make it url safe
var query = "http://ebooks.library.cornell.edu/cgi/t/text/text-idx?ALLSELECTED=1&xc=1&g=moagrp&type=simple&rgn=full+text&q1=" + searchTerms + "&cite1=&cite1restrict=author&cite2=" + searchTerms + "&cite2restrict=title&Submit=Search&c=" + vmoa.returnJournalData(d.journal).id;
return query;
})
);
//$('#articleNetworkInfo').text(d.sentence);
}
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "#C8C891" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
vmoa.toggleWorking(false);
}
)
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
Builds the page view box inline with the quotes
vol = the meta id for the journal ready to be sent to the server
image = a int of the "IMG", a way to narrow down the selction, it needs to be in the formated later to make sure it is padded, etc, see below
holder = a int of the div id where we should put everything, format it as '#quote' + holder
md5 = a hash of the sentence being expanded so we can easily compare and highlight the expanded sentence in the text
-----------------------------*/
buildPageView: function(vol,image,holder, md5){
this.toggleWorking(true);
$('#quote' + holder).empty();
//pad the img number to meet the format on the server
padded_image = vmoa.padNumber(image,5);
//get the data
var req = $.getJSON( "/page/"+vol+"/"+padded_image )
.done(function(data) {
//create a place to hold the results
$('#quote' + holder).append(
$("<div>")
.addClass("pageViewer")
);
for (aSentence in data.results){
//if the title is blank it is a continuation of the article, if it isnt then we need to announce what artcile we are looking at
if (data.results[aSentence].title!=""){
$('#quote' + holder + " div").append(
$("<span>")
.text(data.results[aSentence].title)
.addClass("pageViewerTitle")
);
$('#quote' + holder + " div").append(
$("<br>")
);
}
var useClass = "pageViewerBody";
//emphisize the sentence in context
if (md5==vmoa.MD5(data.results[aSentence].sentence)){useClass = "pageViewerBodyHighLight";}
$('#quote' + holder + " div").append(
$("<span>")
.text(data.results[aSentence].sentence + " ")
.addClass(useClass)
);
}
//build the nav buttons at the bottom
$('#quote' + holder + " div").append(
$("<hr>")
);
$('#quote' + holder + " div").append(
$("<div>")
.addClass("quoteButtons")
.append(
$("<button>")
.addClass("btn")
.html("&larr; Previous Page")
.click(function(){
vmoa.buildPageView(vol,image-1,holder,md5);
})
)
.append(
$("<button>")
.addClass("btn")
.html("Close")
.click(function(){
$('#quote' + holder).animate({
height: 0,
}, 300, function() {
$('#quote' + holder).empty();
$('#quote' + holder).css("height","auto");
});
})
)
.append(
$("<button>")
.addClass("btn")
.html("Next Page &rarr;")
.click(function(){vmoa.buildPageView(vol,image+1,holder,md5); })
)
);
var offset = $('#quote' + holder).offset();
$('html, body').animate({scrollTop:offset.top-20}, 'fast');
vmoa.toggleWorking(false);
}
)
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
builds/displays the quotes from connected words from the nGram
-----------------------------*/
buildQuotes: function(){
this.toggleWorking(true);
$('#quotesHolderList').empty();
this.History.pushState({state:4}, "VisualMOA | Connected Quotes", "?quotes");
//hide the previous pge
$('#ngramHolder').fadeOut('fast');
var sentences=[];
//colllect the data
var req = $.getJSON( "/text/" + vmoa.searchThirdYear + "/" + vmoa.searchSecondTerm.toLowerCase() + "/" + vmoa.searchSecondType + "/" + vmoa.searchThirdTerm.toLowerCase() + "/" + vmoa.searchThirdType)
.done(function(data) {
//we want to group the quotes by journal and then by article if possible
for (quote in data.results){
var journal=vmoa.returnJournalData(data.results[quote].page);
var idSafeDate = data.results[quote].date.replace(".",""); //we use this to make IDs so strip out the period
//is this jounal added to the page yet?
if (!$('#journalHolder' + journal.id).length){
//nope
$('#quotesHolderList').append(
$('<div>')
.attr("id", 'journalHolder' + journal.id)
);
//add the title and links and stuff
$('#journalHolder' + journal.id).append(
$('<h4>')
.text(journal.title)
.attr("title","Journal Title : " + journal.title + " (" + journal.dates + ")")
);
//TODO
}
//is there already a section for this article?
if (!$('#articleHolder' + journal.id + idSafeDate + vmoa.MD5(data.results[quote].title)).length){
//nope
$('#journalHolder' + journal.id).append(
$("<div>")
.attr("id",'articleHolder' + journal.id + idSafeDate + vmoa.MD5(data.results[quote].title))
.addClass("articleHolder")
);
//var articleDate = Date.parse("1-" + data.results[quote].date.split(".")[1] + "-" + data.results[quote].date.split(".")[0]);
var articleDate = new Date(data.results[quote].date.split(".")[0] + '-' + data.results[quote].date.split(".")[1] + '-015')
//console.log(vmoa.months[articleDate.getMonth()] + " " + articleDate.getMonth() + " " + data.results[quote].date)
//add the title of the article
$('#articleHolder' + journal.id + idSafeDate + vmoa.MD5(data.results[quote].title)).append(
$('<h6>')
.text(data.results[quote].title + " (" + vmoa.months[articleDate.getMonth()] + " " + articleDate.getFullYear() +")")
.attr("title","Article Title")
);
}
//not sure if to enable this....
//if ($.inArray(vmoa.MD5(journal.id+data.results[quote].sentence), sentences)==-1){ //this bit just makes sure we aren't repeating setences
//we want to emphisize the matched words
var sentence = data.results[quote].sentence;
if (sentence.indexOf(vmoa.searchSecondTerm)!=-1){
sentence = sentence.replace(vmoa.searchSecondTerm,'<span class="quoteText' + vmoa.searchSecondType + '">'+ vmoa.searchSecondTerm +'</span>');
}else if (sentence.indexOf(vmoa.capitaliseFirstLetter(vmoa.searchSecondTerm))!=-1){
//take care of upper
sentence = sentence.replace(vmoa.capitaliseFirstLetter(vmoa.searchSecondTerm),'<span class="quoteText' + vmoa.searchSecondType + '">'+ vmoa.searchSecondTerm +'</span>');
}else if (sentence.indexOf(vmoa.searchSecondTerm.toLowerCase())!=-1){
//take care of lowercased
sentence = sentence.replace(vmoa.searchSecondTerm.toLowerCase(),'<span class="quoteText' + vmoa.searchSecondType + '">'+ vmoa.searchSecondTerm +'</span>');
}
if (sentence.indexOf(vmoa.searchThirdTerm)!=-1){
sentence = sentence.replace(vmoa.searchThirdTerm,'<span class="quoteText' + vmoa.searchThirdType + '">'+ vmoa.searchThirdTerm +'</span>');
}else if (sentence.indexOf(vmoa.capitaliseFirstLetter(vmoa.searchThirdTerm))!=-1){
//take care of upper
sentence = sentence.replace(vmoa.capitaliseFirstLetter(vmoa.searchThirdTerm),'<span class="quoteText' + vmoa.searchThirdType + '">'+ vmoa.capitaliseFirstLetter(vmoa.searchThirdTerm) +'</span>');
}else if (sentence.indexOf(vmoa.searchThirdTerm.toLowerCase())!=-1){
//take care of lowercased
sentence = sentence.replace(vmoa.searchThirdTerm.toLowerCase(),'<span class="quoteText' + vmoa.searchThirdType + '">'+ vmoa.searchThirdTerm +'</span>');
}
//add the text into the article
$('#articleHolder' + journal.id + idSafeDate + vmoa.MD5(data.results[quote].title)).append(
$("<div>")
.html(sentence)
.addClass("quoteText")
.append(
$("<a>")
.text("[expand context]")
.css("padding","0px 2px 0px 2px")
.css("color","#444444")
.attr("title","Expand this sentence, view more of the article")
.data("vol", data.results[quote].page.replace(/\//g,"|"))
.data("image", parseInt(data.results[quote].image.replace(/img/ig,""),10))
.data("holder", quote)
.data("md5", vmoa.MD5(data.results[quote].sentence))
.attr("href","#")
.click(function(event){
//so we want to build the preview box, but not here, tooo many nested functionnnnnsssss
vmoa.buildPageView($(this).data("vol"),$(this).data("image"),$(this).data("holder"),$(this).data("md5"));
event.preventDefault();
return false;
})
)
.append(
$("<a>")
.attr("target", "_blank")
.text("[search MOA]")
.css("color","#444444")
.css("padding","0px 2px 0px 2px")
.attr("title","Attempt to view this sentence on the MOA website")
.attr("href", function(){
var maxChar = data.results[quote].sentence.length;
if (maxChar>101){maxChar=101;} //the MOA site cuts it off
var searchTerms = data.results[quote].sentence.substring(0,maxChar)
//the MOA site does not like half formed words, so break it up by word and drop the last one
var words = searchTerms.split(" ");
searchTerms = "";
for (i=0;i<words.length-1;i++){
searchTerms = searchTerms + " " + words[i];
}
searchTerms = encodeURIComponent(searchTerms); //make it url safe
var query = "http://ebooks.library.cornell.edu/cgi/t/text/text-idx?ALLSELECTED=1&xc=1&g=moagrp&type=simple&rgn=full+text&q1=" + searchTerms + "&cite1=&cite1restrict=author&cite2=&cite2restrict=author&Submit=Search&c=" + journal.id;
return query;
})
)
);
$('#articleHolder' + journal.id + idSafeDate + vmoa.MD5(data.results[quote].title)).append(
$("<div>")
.attr("id","quote" + quote)
);
//}
//not being used right now but maybe should be.
sentences.push(vmoa.MD5(journal.id+data.results[quote].sentence));
}
$('#quotesHolder').fadeIn('fast');
vmoa.toggleWorking(false);
}
)
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
builds the details for the year for the diffrent parts of speach
-----------------------------*/
buildNgramDetails: function(useYear){
//build for the three typess
var types = ['np','nn','vb'];
this.toggleWorking(true);
for (x in types){
//empty out any current graphs
$("#ngramDetails" + types[x] + 'Chart').empty();
$("#ngramDetails" + types[x] + 'OverflowChart').empty();
$(".title").css("display","none");
//colllect the data
var req = $.getJSON( "/filter/" + vmoa.searchThirdYear + "/" + vmoa.searchSecondTerm.toLowerCase() + "/" + types[x] + "/" + vmoa.searchSecondType + "/")
.done(function(data) {
$(".title").css("display","block");
//this is just to figure out what the type of speach we are looking up,the this.url refers
//to the url of the json req which has the type in it.
var useType = this.url.substring(this.url.length-6,this.url.length-4);
//grab the data
var results = {"name":"results", "children" : []};
var resultsOverflow = {"name":"overflow", "children" : []};
var counter=0;
var maxSize = [];
//process the data
for (aType in data.children){
//set the type for the bubble bilder to know the color to use
data.children[aType].type = useType;
//bubles are hard to read, so only have the top 5 results, then list the rest below the graph
if (counter<10){
results.children.push(data.children[aType]);
counter++;
}else{
maxSize.push(parseInt(data.children[aType].size));
resultsOverflow.children.push(data.children[aType]);
}
}
//build the chart
var r = ($(document).width()/3)-75;
$('#ngramDetails' + useType + 'Chart').css('width',r+'px');
//the lastR holds the size of the last bubble, we uuse it below
var lastR = vmoa.buildBubble(results,'ngramDetails' + useType + 'Chart',r);
//now new need to list the over flow results below the chart
var currentCY = 0;
var getCY = function(plus){
currentCY = currentCY + (plus*2);
return currentCY;
}
var rad = d3.scale.linear()
.domain([0, d3.max(maxSize)])
.range([0, 30]);
//the margin to use
var m = 5;
//make the intial CY a line up at the top
currentCY = (rad(d3.max(maxSize))-10) * -1;
var vis = d3.select('#ngramDetails' + useType + 'OverflowChart').append("svg")
.attr("width", r)
.attr("height", r)
.attr("id","ngramOverflow" + useType + "Chart")
.attr("class", "bubble");
//add in a rect behind the label text to facilitate clicking
vis.selectAll(".ngramOverflowLabelRect")
.data(resultsOverflow.children)
.enter().append("rect")
.attr("width", function(d) { return r; })
.attr("height", function(d) { return rad(d.size)*2; })
.attr("y", function(d, i){return(getCY(rad(d.size)+m) - rad(d.size));})
.attr("x", function() {return rad(d3.max(maxSize));})
.style("cursor", "pointer")
.attr("opacity", 0.0)
.on("mouseover", function(d, i){d3.select("#" + useType + i).style("stroke-width", 1).style("stroke", "#444444");})
.on("mouseout", function(d, i) {d3.select("#" + useType + i).style("stroke-width", 0.000005)})
.on("click", function(d){
vmoa.searchThirdType=d.type;
vmoa.searchThirdTerm=d.name.toLowerCase();
vmoa.buildQuotes();
});
//reset the CY
currentCY = (rad(d3.max(maxSize))-10) * -1;
vis.selectAll(".circle")
.data(resultsOverflow.children)
.enter().append("circle")
.attr("r", function(d) { return rad(d.size); })
.attr("id", function(d, i) {return useType + i;})
.attr("cy", function(d, i){return(getCY(rad(d.size)+m));})
.attr("cx", function() {return rad(d3.max(maxSize));})
.style("cursor", "pointer")
.style("fill", function(d) {
if (d.type=='nn'){
return "#66c499"
}else if (d.type=='np'){
return "#e3774f";
}else{
return "#e47598";
}
})
.on("mouseover", function(d, i){d3.select(this).style("stroke-width", 1).style("stroke", "#444444");})
.on("mouseout", function(d, i) {d3.select(this).style("stroke-width", 0.000005)})
.on("click", function(d){
});
//reset the CY
currentCY = (rad(d3.max(maxSize))-10) * -1;
vis.selectAll(".ngramOverflowLabelSize")
.data(resultsOverflow.children)
.enter()
.append("text")
.attr("x", function() {return rad(d3.max(maxSize));})
.attr("y", function(d){ return( getCY(rad(d.size)+m)) })
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.attr("font-size", "14")
.style("fill","#444444")
.attr("pointer-events", "none")
.style("font-family", "Helvetica, Arial, sans-serif")
.text(function(d){ return d.size;});
//reset the CY
currentCY = (rad(d3.max(maxSize))-10) * -1;
vis.selectAll(".ngramOverflowLabelText")
.data(resultsOverflow.children)
.enter()
.append("text")
.attr("x", function() {return rad(d3.max(maxSize))*2 + m;})
.attr("y", function(d){ return( getCY(rad(d.size)+m)) })
.attr("text-anchor", "right")
.attr("dy", ".3em")
.attr("font-size", "15")
.attr("pointer-events", "none")
.style("fill","#444444")
.style("font-family", "Helvetica, Arial, sans-serif")
.text(function(d){ return d.name;});
//make sure the svg is big enough
d3.select("#ngramOverflow" + useType + "Chart").attr("height", function(){ return getCY(0) + rad(d3.max(maxSize))} );
//turn off the working spinner if this is the last one called "vb"
(useType == "vb") ? vmoa.toggleWorking(false) : null;
}
)
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
}
},
/*---------------------------
builds the ngram chart
id = the mongodb object _id string
-----------------------------*/
buildNgram: function(id){
this.toggleWorking(true);
this.History.pushState({state:3}, "VisualMOA | n-gram view", "?ngram");
//hide the search stuff
$('#mainSearchPartialOverflow').fadeOut('fast');
$('#mainSearchChartHolder').fadeOut('fast');
//clear out any old charts
$('#ngramChart').empty();
//make sure the old details are gone too
$("#ngramDetails" + "nn" + 'Chart').empty();
$("#ngramDetails" + "nn" + 'OverflowChart').empty();
$("#ngramDetails" + "np" + 'Chart').empty();
$("#ngramDetails" + "np" + 'OverflowChart').empty();
$("#ngramDetails" + "vb" + 'Chart').empty();
$("#ngramDetails" + "vb" + 'OverflowChart').empty();
$(".title").css("display","none");
nGram = [];
//colllect the data
var req = $.getJSON( "/ngram/" + id)
.done(function(data) {
//build the data object
//there is an array of number that corospond to a year 1800-1901, so the x+1800 basiclly is the year
for (x in data[0].data){
nGram.push({"year" : ( 1800 + parseInt(x)),"size" : data[0].data[x]});
}
//get an array of the size to getthe max of the Y axis
var maxH = [];
var total = 0;
for (x in nGram){
maxH.push(nGram[x].size);
total = total + nGram[x].size;
}
$('#ngramTitle').text("Found " + total + " results for '" + vmoa.searchSecondTerm + "', click a year to expand");
//figure out how wide the bars can be based on the screen width.
var w = (Math.floor(($(document).width()-100)/101)+0.5);
$("#ngramChart").css("width",(w*101)+10);
//a helper function to move the label of the year around the chart
var showLabel = function(d,pos){
//console.log(pos);
var posText = pos;
if (d.year > 1897){posText = pos - ((d.year - 1897)*8);}
if (d.year < 1805){posText = pos + ((1805 - d.year)*8);}
d3.select("#ngramLabelYear").attr("x", posText - (w*3.3)).text(d.year);
d3.select("#ngramLabelSize").attr("x", posText + 5).text(vmoa.numberWithCommas(d.size));
d3.select("#ngramLabelLine").attr("x1", pos).attr("x2", pos);
};
var h = 200,
m = 10;
var x = d3.scale.linear()
.domain([1800, 1901])
.range([0, w * nGram.length]);
var y = d3.scale.linear()
.domain([0, d3.max(maxH)+35])
.rangeRound([0, h]);
var chart = d3.select("#ngramChart").append("svg")
.attr("class", "chart")
.attr("id","ngramsvg")
.attr("width", (w * nGram.length - 1) + (m * 2))
.attr("height", h + (m*2))
.on("mouseover", function(d){
d3.select("#ngramLabelYear").attr("visibility", "visible");
d3.select("#ngramLabelSize").attr("visibility", "visible");
d3.select("#ngramLabelLine").attr("visibility", "visible");
})
.on("mouseout", function(d){
d3.select("#ngramLabelYear").attr("visibility", "hidden");
d3.select("#ngramLabelSize").attr("visibility", "hidden");
d3.select("#ngramLabelLine").attr("visibility", "hidden");
});
//add in the bars for the data
chart.selectAll("rect")
.data(nGram)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.year); })//return x(i) - .5; })
.attr("y", function(d) { return h - y(d.size) - .5; })
.attr("width", w)
.style("fill", '#4a8fbf')
.attr("id",function(d) {return "rect" + d.year}) //used to ref back to it when mouse over the trans bar
.style("stroke", '#444444')
.attr("visibility", "hidden")
.attr("height", function(d) { return y(d.size); });
//have them appear in a cascade effect
d3.selectAll("rect").transition()
.attr("visibility", "visible")
.delay(function(d,i) { return i * 7 })
.duration(3000)
.ease("elastic", 10, .45);
//now add in full size transparent bars that allow the user to click on a bar without having to click exactly on the bar
//so they can be above the acutal end of the bar and still have it seelcted,
chart.selectAll(".rect")
.data(nGram)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.year); })//return x(i) - .5; })
.attr("y", function(d) { return 0; })
.attr("width", w)
.style("fill", '#4a8fbf')
.style("stroke", 'white')
.style("cursor", "pointer")
.attr("opacity", 0.0)
.on("mouseover", function(d){
d3.select("#rect" + d.year).style("fill", "#fff");
showLabel(d, x(d.year));
})
.on("mouseout", function(d){
d3.select("#rect" + d.year).style("fill", "#4a8fbf");
})
.on("click", function(d){
if (d.size==0){return false;}
vmoa.searchThirdYear=d.year;
vmoa.buildNgramDetails(d.year);
})
.attr("height", function(d) { return 200; });
chart.append("line")
.attr("x1", 0)
.attr("x2", (w * nGram.length) + m + 0.5)
.attr("y1", h - .5)
.attr("y2", h - .5)
.style("stroke", "#444444");
chart.selectAll("line")
.data(x.ticks(10))
.enter().append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2", 200)
.attr("stroke-dasharray", "1, 3")
.style("stroke", "#ccc");
chart.selectAll(".rule")
.data(x.ticks(10))
.enter().append("text")
.attr("x", x)
.attr("y", 215)
.style("fill","#444444")
.style("font-family", "Helvetica, Arial, sans-serif")
.attr("text-anchor", "left")
.attr("pointer-events", "none")
.text(function(d){return d});
chart.selectAll(".ngramLabelYear")
.data([0])
.enter()
.append("text")
.attr("class", "rule")
.attr("id","ngramLabelYear")
.attr("x", 20)
.attr("y", 15)
.attr("font-size", "17")
.attr("pointer-events", "none")
.style("fill","#444444")
.attr("visibility", "hidden")
.attr("dy", -3)
.style("font-family", "Helvetica, Arial, sans-serif")
.attr("text-anchor", "right");
chart.selectAll(".ngramLabelSize")
.data([0])
.enter()
.append("text")
.attr("class", "rule")
.attr("id","ngramLabelSize")
.attr("x", 20)
.attr("y", 12)
.attr("font-size", "12")
.style("fill","#444444")
.attr("visibility", "hidden")
.attr("pointer-events", "none")
.attr("dy", -3)
.style("font-family", "Helvetica, Arial, sans-serif")
.attr("text-anchor", "right");
chart.append("line")
.attr("x1", 100)
.attr("x2", 100)
.attr("y1", 0)
.attr("visibility", "hidden")
.attr("y2", h)
.attr("id","ngramLabelLine")
.style("stroke", "#444444");
vmoa.toggleWorking(false);
})
.fail(function() { alert("error"); }) //TODO
.always(function() { } //TODO
);
},
/*---------------------------
A reuseable bubble builder
data = array of objects epxteded to be in the layout format
container = string of the id of the container for the chart
r = the radius in px of the chart
-----------------------------*/
buildBubble: function (data,container,r){
var lastR = 0;
var format = d3.format(",d");
var bubble = d3.layout.pack()
.sort(null)
.size([r, r]);
var vis = d3.select("#"+container).append("svg")
.attr("width", r)
.attr("height", r)
.attr("class", "bubble");
var node = vis.selectAll("g.node")
.data(bubble.nodes(vmoa.classes(data))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function(d) { lastR = d.r; return d.r; })
.style("cursor", "pointer")
.style("fill", function(d) {
if (d.type=='nn'){
return "#66c499"
}else if (d.type=='np'){
return "#e3774f";
}else{
return "#e47598";
}
})
.on("mouseover", function(){d3.select(this).style("stroke-width", 1).style("stroke", "#444444");})
.on("mouseout", function() {d3.select(this).style("stroke-width", 0.000005)})
.on("click", function(d){
//the main search bubble behavior
if (d.packageName == "matches" || d.packageName == "partials"){
vmoa.searchSecondTerm=d.className;
vmoa.buildNgram(d.id);
vmoa.searchSecondType=d.type;
}
//the ngram details bubble;
if (d.packageName == "results"){
vmoa.searchThirdType=d.type;
vmoa.searchThirdTerm=d.className.toLowerCase();
vmoa.buildQuotes();
}
});
node.append("text")
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.style("cursor", "pointer")
.attr("fill","#444444")
.attr("pointer-events", "none")
.attr("font-size", "11px")
.text(function(d) { return d.className.substring(0, d.r / 3); });
//we use this in the buildNgramDetails function, we want to know the last size of the bubble so we can pick up with that size for the
//overflow list of results
return lastR;
},
/*---------------------------
handles the history changes as a navigation method.
-----------------------------*/
route: function(){
var State = History.getState();
//History.log(State.data.state, State.title, State.url);
switch(State.data.state){
case 100: //State 100: the main search page, eg the home page
$('#mainSearchChartHolder').fadeOut(1);
$('#articleNetworkHolder').fadeOut(1);
$('#articleNetworkInfo').fadeOut(1);
$('#authorsNetwork').fadeOut(1);
$("#wordFreqHolder").fadeOut(1);
$("#ocrToolHolder").fadeOut(1);
$('#search').fadeIn('slow');
//get rid of the bindings to the keys for the 100k word freq
$(document).off("keydown");
break;
case 1: //State 1: the main search results bubble chart
$('#mainSearchPartialOverflow').fadeOut(1);
$('#search').fadeOut(1);
$('#ngramHolder').fadeOut(1)
$('#mainSearchChartHolder').fadeIn('slow');
break
case 2: //State 2: the list of the overflow partial matches
$('#mainSearchChartHolder').fadeOut(1);
$('#ngramHolder').fadeOut(1)
$('#mainSearchPartialOverflow').fadeIn('slow');
break
case 3: //State 3: the n gram viewer
$('#mainSearchChartHolder').fadeOut(1)
$('#mainSearchPartialOverflow').fadeOut(1)
$('#quotesHolder').fadeOut(1);
$('#ngramHolder').fadeIn('slow');
break
case 4: //State 3: the quotes page
$('#mainSearchChartHolder').fadeOut(1)
$('#mainSearchPartialOverflow').fadeOut(1)
$('#ngramHolder').fadeOut(1);
$('#quotesHolder').fadeIn('slow');
break
case 5: //State 5: the article tree
$('#search').fadeOut(1)
$('#ngramHolder').fadeOut(1);
$('#articleNetworkInfo').fadeIn(1);
$('#articleNetworkHolder').fadeIn('slow');
break
case 6: //State 5: the author Tree
$('#search').fadeOut(1)
$('#ngramHolder').fadeOut(1);
$('#articleNetworkInfo').fadeIn(1);
$('#authorsNetwork').fadeIn('slow');
break
case 7: //State n: the 100k word freq
$('#search').fadeOut(1)
$('#ngramHolder').fadeOut(1);
$('#authorsNetwork').fadeOut(1);
$('#articleNetworkInfo').fadeIn(1);
$("#wordFreqHolder").fadeIn('fast');
break
case 8: //State n: ocr correct tool
$('#search').fadeOut(1)
$('#ngramHolder').fadeOut(1);
$('#authorsNetwork').fadeOut(1);
$('#articleNetworkInfo').fadeOut(1);
$("#ocrToolHolder").fadeIn('fast');
break
default:
}
},
/*---------------------------
The main search action
-----------------------------*/
searchMain: function (){
if ($('#inputSearch').val().length==0){return false;}
this.toggleWorking(true);
this.searchMainTerm = $.trim($('#inputSearch').val());
this.searchMainCount =0;
//hide the search stuff
$('#search').fadeOut(500);
//clear out any old charts
$('#mainSearchChartMatchesChart').empty();
$('#mainSearchChartPartialsChart').empty();
var resultsMatchs = {"name":"matches", "children" : []};
var resultsPartials = {"name":"partials", "children" : []};
var resultsPartialsOverflow = {"name":"partialsOverflow", "children" : []};
//colllect the data
var req = $.getJSON( "/search/" + this.searchMainTerm.toLowerCase())
.done(function(data) {
vmoa.History.pushState({state:1}, "VisualMOA | Sentence Search Results", "?ssResults");
if (data.children.length==0){
vmoa.toggleWorking(false);
vmoa.noResults('#noResultsSS');
return;
}
var counter=0;
//process the data
//sort if they are a direct matach or just a partial
for (x in data.children){
vmoa.searchMainCount=vmoa.searchMainCount + parseInt(data.children[x].size);
if (data.children[x].name.toLowerCase()==vmoa.searchMainTerm.toLowerCase()){
resultsMatchs.children.push(data.children[x]);
}else{
//if it is not a direct match then it shows up in the partials, but there might be a lot. so filter it to overflow into
//another object to display as text, only show the top xx (they are being served from the server DESC)
if (counter<13){
resultsPartials.children.push(data.children[x]);
counter++;
}else{
resultsPartialsOverflow.children.push(data.children[x]);
}
}
}
var r1 = ($(document).width()/2)-300;
var r2 = ($(document).width()/2)-225;
$('#mainSearchChartMatchesChart').css('width',r1+'px');
$('#mainSearchChartPartialsChart').css('width',r2+'px');
vmoa.buildBubble(resultsMatchs,'mainSearchChartMatchesChart',r1);
vmoa.buildBubble(resultsPartials,'mainSearchChartPartialsChart',r2);
//update some labels
$('#mainSearchChartHolderHeaderCount').text("Found " + vmoa.numberWithCommas(vmoa.searchMainCount) + " results, click a term to refine.");
//the link to show the over flow text
$("#mainSearchChartPartialsMoreLabel").empty();
if (resultsPartialsOverflow.children.length>0){
$("#mainSearchChartPartialsMoreLabel").append(
$('<span>')
.text('Found ' + vmoa.numberWithCommas(resultsPartialsOverflow.children.length) + ' other partial matches: ')
.append(
$('<a>')
.text('View')
.attr('href','#')
.click(function(event){
vmoa.History.pushState({state:2}, "VisualMOA | Sentence Search Results", "?sentenceResults");
event.preventDefault();
return false;
})
)
);
}
//build the table of the overflow partial mataches
$("#mainSearchPartialOverflowList").empty();
for (x in resultsPartialsOverflow.children){
var color = (resultsPartialsOverflow.children[x].type == 'nn') ? "#66c499" : "#e3774f";
$("#mainSearchPartialOverflowList").append(
$('<tr>')
.append(
$('<td>')
.addClass('tableBubble')
.append(
$('<div>')
.addClass('mainSearchLegendBubble')
.css('background-color',color)
)
)
.append(
$('<td>')
.addClass('tableCount')
.text(resultsPartialsOverflow.children[x].size)
)
.append(
$('<td>')
.append(
$("<a>")
.attr("href","#")
.text(resultsPartialsOverflow.children[x].name)
.data("name",resultsPartialsOverflow.children[x].name)
.data("type",resultsPartialsOverflow.children[x].type)
.data("id",resultsPartialsOverflow.children[x].id)
.click(function(event){
vmoa.searchSecondTerm=$(this).data("name");
vmoa.searchSecondType=$(this).data("type");
vmoa.buildNgram($(this).data("id"));
event.preventDefault();
return false;
})
)
)
);
}
//show the charts
vmoa.toggleWorking(false);
//$('#mainSearchChartHolder').fadeIn('slow');
})
.fail(function() { }) //TODO
.always(function() { }); //TODO
},
/*---------------------------
Displays the working spinner or not
-----------------------------*/
toggleWorking: function(show){
if (show){$('#working').css('display','block');}else{$('#working').css('display','none');}
},
/*---------------------------
Makes sure we have everything we need for the app to work
-----------------------------*/
runChecks: function(){
//no SVG support, uh oh.
if(!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")){
$('#errorNoSVG').css('display','block');
}
},
/*---------------------------
Binds the dom elements using jquery
-----------------------------*/
bindControls: function(){
//turn off working spinner
this.toggleWorking(false);
//Main search controls
$('#inputSearch').keypress(
function(event){
if (event.keyCode === 13){
vmoa.searchMain();
}
}
);
$('#inputSearchArticle').keypress(
function(event){
if (event.keyCode === 13){
vmoa.searchArticles();
}
}
);
//generic back buttons
$('.btnBack').click(function(event){
History.back();
event.preventDefault();
return false;
})
//the close link on the author network article viwer
$('#authorsNetworkArticlesClose a').click(function(event){
$('#authorsNetworkArticles').hide('fast');
event.preventDefault();
return false;
})
//the reset button
$('.btnNetworkReset').click(function(){
vmoa.authorNetworkD3Nodes.splice(0, vmoa.authorNetworkD3Nodes.length);
vmoa.authorNetworkD3Links.splice(0, vmoa.authorNetworkD3Links.length);
vmoa.authorNetworkRedraw();
});
//the launch button
$('#authorNetworkLaunch').click(function(){
vmoa.buildAuthorNetwork();
});
$('#words100kLaunch').click(function(){
vmoa.int100kWords();
});
//the help button for the network
$('.btnNetworkHelp, #suthorsNetworkHelpClose').click(function(event){
if ($("#authorsNetworkHelp").css("display")=="none"){
$("#authorsNetworkHelp").show('fast');
}else{
$("#authorsNetworkHelp").hide('fast');
}
event.preventDefault();
return false;
})
//ocr tools
$('#ocrToolOkay').click(function(){
vmoa.ocrToolUpdate($('#ocrToolWord').text(),2);
});
$('#ocrToolSkip').click(function(){
vmoa.ocrToolSkip++;
vmoa.ocrToolNext();
});
$('#ocrToolError').click(function(){
$('#ocrToolErrors').show('fast');
});
$('#ocrToolSave').click(function(){
vmoa.ocrToolUpdate($('#ocrToolWord').text(),3,$('#ocrToolIncorrect').val(),$('#ocrToolCorrect').val());
});
$('#ocrToolLaunch').click(function(){
vmoa.ocrToolInit();
});
//main launch button
$('#launchVMOA').click(function(){
window.location = vmoa.awsURI;
});
//the history controls
this.History = window.History;
this.History.Adapter.bind(window,'statechange',function(){
vmoa.route();
});
this.History.pushState({state:100}, "Home", "?home");
},
/*---------------------------
utilty functions
-----------------------------*/
numberWithCommas: function (x) {
return x.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
},
capitaliseFirstLetter: function(string){
//return string.charAt(0).toUpperCase() + string.slice(1);
return string.replace( /(^|\s)([a-z])/g , function(m,p1,p2){ return p1+p2.toUpperCase(); } );
},
MD5: function(e){function h(a,b){var c,d,e,f,g;e=a&2147483648;f=b&2147483648;c=a&1073741824;d=b&1073741824;g=(a&1073741823)+(b&1073741823);return c&d?g^2147483648^e^f:c|d?g&1073741824?g^3221225472^e^f:g^1073741824^e^f:g^e^f}function i(a,b,c,d,e,f,g){a=h(a,h(h(b&c|~b&d,e),g));return h(a<<f|a>>>32-f,b)}function j(a,b,c,d,e,f,g){a=h(a,h(h(b&d|c&~d,e),g));return h(a<<f|a>>>32-f,b)}function k(a,b,d,c,e,f,g){a=h(a,h(h(b^d^c,e),g));return h(a<<f|a>>>32-f,b)}function l(a,b,d,c,e,f,g){a=h(a,h(h(d^(b|~c),
e),g));return h(a<<f|a>>>32-f,b)}function m(a){var b="",d="",c;for(c=0;3>=c;c++)d=a>>>8*c&255,d="0"+d.toString(16),b+=d.substr(d.length-2,2);return b}var f=[],n,o,p,q,a,b,c,d,e=function(a){for(var a=a.replace(/\r\n/g,"\n"),b="",d=0;d<a.length;d++){var c=a.charCodeAt(d);128>c?b+=String.fromCharCode(c):(127<c&&2048>c?b+=String.fromCharCode(c>>6|192):(b+=String.fromCharCode(c>>12|224),b+=String.fromCharCode(c>>6&63|128)),b+=String.fromCharCode(c&63|128))}return b}(e),f=function(b){var a,c=b.length;a=
c+8;for(var d=16*((a-a%64)/64+1),e=Array(d-1),f=0,g=0;g<c;)a=(g-g%4)/4,f=8*(g%4),e[a]|=b.charCodeAt(g)<<f,g++;a=(g-g%4)/4;e[a]|=128<<8*(g%4);e[d-2]=c<<3;e[d-1]=c>>>29;return e}(e);a=1732584193;b=4023233417;c=2562383102;d=271733878;for(e=0;e<f.length;e+=16)n=a,o=b,p=c,q=d,a=i(a,b,c,d,f[e+0],7,3614090360),d=i(d,a,b,c,f[e+1],12,3905402710),c=i(c,d,a,b,f[e+2],17,606105819),b=i(b,c,d,a,f[e+3],22,3250441966),a=i(a,b,c,d,f[e+4],7,4118548399),d=i(d,a,b,c,f[e+5],12,1200080426),c=i(c,d,a,b,f[e+6],17,2821735955),
b=i(b,c,d,a,f[e+7],22,4249261313),a=i(a,b,c,d,f[e+8],7,1770035416),d=i(d,a,b,c,f[e+9],12,2336552879),c=i(c,d,a,b,f[e+10],17,4294925233),b=i(b,c,d,a,f[e+11],22,2304563134),a=i(a,b,c,d,f[e+12],7,1804603682),d=i(d,a,b,c,f[e+13],12,4254626195),c=i(c,d,a,b,f[e+14],17,2792965006),b=i(b,c,d,a,f[e+15],22,1236535329),a=j(a,b,c,d,f[e+1],5,4129170786),d=j(d,a,b,c,f[e+6],9,3225465664),c=j(c,d,a,b,f[e+11],14,643717713),b=j(b,c,d,a,f[e+0],20,3921069994),a=j(a,b,c,d,f[e+5],5,3593408605),d=j(d,a,b,c,f[e+10],9,38016083),
c=j(c,d,a,b,f[e+15],14,3634488961),b=j(b,c,d,a,f[e+4],20,3889429448),a=j(a,b,c,d,f[e+9],5,568446438),d=j(d,a,b,c,f[e+14],9,3275163606),c=j(c,d,a,b,f[e+3],14,4107603335),b=j(b,c,d,a,f[e+8],20,1163531501),a=j(a,b,c,d,f[e+13],5,2850285829),d=j(d,a,b,c,f[e+2],9,4243563512),c=j(c,d,a,b,f[e+7],14,1735328473),b=j(b,c,d,a,f[e+12],20,2368359562),a=k(a,b,c,d,f[e+5],4,4294588738),d=k(d,a,b,c,f[e+8],11,2272392833),c=k(c,d,a,b,f[e+11],16,1839030562),b=k(b,c,d,a,f[e+14],23,4259657740),a=k(a,b,c,d,f[e+1],4,2763975236),
d=k(d,a,b,c,f[e+4],11,1272893353),c=k(c,d,a,b,f[e+7],16,4139469664),b=k(b,c,d,a,f[e+10],23,3200236656),a=k(a,b,c,d,f[e+13],4,681279174),d=k(d,a,b,c,f[e+0],11,3936430074),c=k(c,d,a,b,f[e+3],16,3572445317),b=k(b,c,d,a,f[e+6],23,76029189),a=k(a,b,c,d,f[e+9],4,3654602809),d=k(d,a,b,c,f[e+12],11,3873151461),c=k(c,d,a,b,f[e+15],16,530742520),b=k(b,c,d,a,f[e+2],23,3299628645),a=l(a,b,c,d,f[e+0],6,4096336452),d=l(d,a,b,c,f[e+7],10,1126891415),c=l(c,d,a,b,f[e+14],15,2878612391),b=l(b,c,d,a,f[e+5],21,4237533241),
a=l(a,b,c,d,f[e+12],6,1700485571),d=l(d,a,b,c,f[e+3],10,2399980690),c=l(c,d,a,b,f[e+10],15,4293915773),b=l(b,c,d,a,f[e+1],21,2240044497),a=l(a,b,c,d,f[e+8],6,1873313359),d=l(d,a,b,c,f[e+15],10,4264355552),c=l(c,d,a,b,f[e+6],15,2734768916),b=l(b,c,d,a,f[e+13],21,1309151649),a=l(a,b,c,d,f[e+4],6,4149444226),d=l(d,a,b,c,f[e+11],10,3174756917),c=l(c,d,a,b,f[e+2],15,718787259),b=l(b,c,d,a,f[e+9],21,3951481745),a=h(a,n),b=h(b,o),c=h(c,p),d=h(d,q);return(m(a)+m(b)+m(c)+m(d)).toLowerCase()},
returnOrdinal: function(number){
var n = number % 100;
var suffix = ['th', 'st', 'nd', 'rd', 'th'];
var ord = n < 21 ? (n < 4 ? suffix[n] : suffix[0]) : (n % 10 > 4 ? suffix[0] : suffix[n % 10]);
return ord;
},
returnJournalData: function(name){
for (x in this.journals){
if (name.indexOf(this.journals[x].id)!=-1){return this.journals[x];}
}
},
padNumber: function(number, length) {
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
},
/*---------------------------
d3 functions
-----------------------------*/
classes: function(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size, type: node.type, id: node.id});
}
recurse(null, root);
return {children: classes};
}
}
//init things when ready
$(document).ready(function(){
vmoa.runChecks();
vmoa.bindControls();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment