Skip to content
Browse files

updating neovigator to newer version

  • Loading branch information...
1 parent d2df081 commit 7e67ba826d8b9b2afb0b2b5a2da2881908a47a90 @maxdemarzi committed Jan 5, 2012
View
6 .gitignore
@@ -0,0 +1,6 @@
+.project
+neo4j
+pkg/*
+*.gem
+.bundle
+log/*
View
2 Gemfile
@@ -1,7 +1,7 @@
source 'http://rubygems.org'
gem 'sinatra'
-gem 'neography'
+gem 'neography', '0.0.18'
gem 'haml'
gem 'json'
View
4 Gemfile.lock
@@ -7,7 +7,7 @@ GEM
httparty (0.7.8)
crack (= 0.1.8)
json (1.6.4)
- neography (0.0.19)
+ neography (0.0.18)
httparty (= 0.7.8)
json
os
@@ -40,7 +40,7 @@ PLATFORMS
DEPENDENCIES
haml
json
- neography
+ neography (= 0.0.18)
net-http-spy
rack-test
rspec
View
16 README.md
@@ -2,12 +2,26 @@ Description
-----------
An attempt to use [Neography](https://github.com/maxdemarzi/neography) and [processing.js](http://processingjs.org/) to navigate a Neo4j graph via its [REST API](http://components.neo4j.org/neo4j-server/milestone/rest.html).
+How To
+------
+
+Follow the steps on [Getting started with Ruby and Neo4j](http://maxdemarzi.com/2012/01/04/getting-started-with-ruby-and-neo4j/)
+to get Neo4j installed, started, and populate it with some nodes and relationships.
+
+ git clone git@github.com:maxdemarzi/neovigator.git
+ cd neovigator
+ bundle install
+ rackup
+
+Then visit [localhost://9292](localhost://9292) to see it running.
+Enter a node id in the text box and click "Load"
Credits
-----------
+-------
This project is based on [Ask Ken](http://askken.heroku.com) created by [Michael Aufreiter](http://twitter.com/_mql).
Links
-----
* [Neo4j Community](http://neo4j.org)
+* [Max De Marzi](http://maxdemarzi.com)
View
30 neovigator.rb
@@ -3,28 +3,15 @@
require 'sinatra/base'
require 'uri'
-class Neovigator < Sinatra::Base
+class Neovigator < Sinatra::Application
set :haml, :format => :html5
set :app_file, __FILE__
- include Neography
-
- configure do
- env = ENV['NEO4J_ENV'] || "development"
- $stderr.puts env
- if env == "test"
- require 'net-http-spy'
- Net::HTTP.http_logger_options = {:verbose => true}
- end
-
- Config.server = ENV['NEO4J_HOST'] || "neography.org"
-# Config.directory = '/' + (ENV['NEO4J_INSTANCE'] || "")
-# Config.authentication = "basic"
-# Config.username = ENV['NEO4J_LOGIN'] || ""
-# Config.password = ENV['NEO4J_PASSWORD']|| ""
+ configure :test do
+ require 'net-http-spy'
+ Net::HTTP.http_logger_options = {:verbose => true}
end
-
before do
@neo = Neography::Rest.new
end
@@ -40,7 +27,7 @@ def link_to(url, text=url, opts={})
def neighbours
{"order" => "depth first",
"uniqueness" => "none",
- "return filter" => {"language" => "builtin", "name" => "all but start node"},
+ "return filter" => {"language" => "builtin", "name" => "all_but_start_node"},
"depth" => 1}
end
@@ -78,7 +65,6 @@ def get_properties(node)
nodes[n["self"]] = n["data"]
end
rel = c["relationships"][0]
- $stderr.puts rel.inspect
if rel["end"] == node["self"]
incoming["Incoming:#{rel["type"]}"] << {:values => nodes[rel["start"]].merge({:id => node_id(rel["start"]) }) }
@@ -91,8 +77,10 @@ def get_properties(node)
attributes << {:id => key.split(':').last, :name => key, :values => value.collect{|v| v[:values]} }
end
+ attributes = [{"name" => "No Relationships","name" => "No Relationships","values" => [{"id" => "#{params[:id]}","name" => "No Relationships "}]}] if attributes.empty?
+
@node = {:details_html => "<h2>Neo ID: #{node_id(node)}</h2>\n<p class='summary'>\n#{get_properties(node)}</p>\n",
- :data => {:attributes => attributes,
+ :data => {:attributes => attributes,
:name => node["data"]["name"],
:id => node_id(node)}
}
@@ -106,4 +94,4 @@ def get_properties(node)
haml :index
end
-end
+end
View
118 public/js/freebase.suggest.min.js
@@ -1,118 +0,0 @@
-/* See suggest.js for license. */
-;(function($,undefined){if(!window.console){window.console={};}
-if(!window.console.log){window.console.log=function(){};}
-$.suggest=function(name,prototype){$.fn[name]=function(options){var isMethodCall=(typeof options==='string'),args=Array.prototype.slice.call(arguments,1);return this.each(function(){var instance=$.data(this,name);if(instance){instance._destroy();}
-$.data(this,name,new $.suggest[name](this,options))._init();});};$.suggest[name]=function(input,options){var self=this;this.name=name;var o=this.options=$.extend(true,{},$.suggest.defaults,$.suggest[name].defaults,options),p=o.css_prefix=o.css_prefix||"",css=o.css;$.each(css,function(k,v){css[k]=p+css[k];});o.ac_param={};$.each(["type","type_strict","mql_filter","as_of_time"],function(i,n){v=o[n];if(v===null||v===""){return;}
-if(typeof v==="object"){if(typeof JSON==="undefined"){init_JSON();}
-v=JSON.stringify(v);}
-o.ac_param[n]=v;});this._status={START:"",LOADING:"",SELECT:""};if(o.status&&o.status instanceof Array&&o.status.length==3){this._status.START=o.status[0]||"";this._status.LOADING=o.status[1]||"";this._status.SELECT=o.status[2]||"";}
-var l=this.list=$('<ul class="'+css.list+'">'),h='<div style="display:none;" class="fbs-reset '+css.pane+'">',s=this.status=$('<div style="display:none;" class="'+css.status+'">'),p=this.pane=$(h).append(s).append(l);if(o.parent){$(o.parent).append(p);}
-else{p.css("position","absolute");$(document.body).append(p);}
-p.bind("mousedown",function(e){self.dont_hide=true;e.stopPropagation();}).bind("mouseup",function(e){if(self.dont_hide){self.input.focus();}
-self.dont_hide=false;e.stopPropagation();}).bind("click",function(e){e.stopPropagation();var s=self.get_selected();if(s){self.onselect(s,true);self.hide_all();}});var hoverover=function(e){self.hoverover_list(e);};var hoverout=function(e){self.hoverout_list(e);};l.hover(hoverover,hoverout);this.input=$(input).attr("autocomplete","off").unbind(".suggest").bind("keydown.suggest",function(e){self.keydown(e);}).bind("keypress.suggest",function(e){self.keypress(e);}).bind("keyup.suggest",function(e){self.keyup(e);}).bind("blur.suggest",function(e){self.blur(e);}).bind("textchange.suggest",function(e){self.textchange();}).bind("focus.suggest",function(e){self.focus(e);});this.onresize=function(e){self.invalidate_position();if(p.is(":visible")){self.position();if(o.flyout&&self.flyoutpane&&self.flyoutpane.is(":visible")){var s=self.get_selected();if(s){self.flyout_position(s);}}}};$(window).bind("resize.suggest",this.onresize).bind("scroll.suggest",this.onresize);};$.suggest[name].prototype=$.extend({},$.suggest.prototype,prototype);};$.suggest.prototype={_init:function(){},_destroy:function(){this.pane.remove();this.list.remove();this.input.unbind(".suggest");$(window).unbind("resize.suggest",this.onresize).unbind("scroll.suggest",this.onresize);},invalidate_position:function(){self._position=null;},status_start:function(){this.hide_all();this.status.siblings().hide();if(this._status.START){this.status.text(this._status.START).show();if(!this.pane.is(":visible")){this.position();this.pane_show();}}
-if(this._status.LOADING){this.status.removeClass("loading");}},status_loading:function(){this.status.siblings().show();if(this._status.LOADING){this.status.addClass("loading").text(this._status.LOADING).show();if(!this.pane.is(":visible")){this.position();this.pane_show();}}
-else{this.status.hide();}},status_select:function(){this.status.siblings().show();if(this._status.SELECT){this.status.text(this._status.SELECT).show();}
-else{this.status.hide();}
-if(this._status.LOADING){this.status.removeClass("loading");}},focus:function(e){var o=this.options;if($.trim(this.input.val())===""){this.status_start();}
-else{this.focus_hook(e);}},focus_hook:function(e){if(!this.input.data("data.suggest")&&!this.pane.is(":visible")&&$("."+this.options.css.item,this.list).length){this.position();this.pane_show();}},keydown:function(e){var key=e.keyCode;if(key===9){this.tab(e);}
-else if(key===38||key===40){if(!e.shiftKey){e.preventDefault();}}},keypress:function(e){var key=e.keyCode;if(key===38||key===40){if(!e.shiftKey){e.preventDefault();}}
-else if(key===13){this.enter(e);}
-else if(key===27){this.escape(e);}
-else if((e.metaKey||e.ctrlKey)&&e.charCode===118){window.clearTimeout(this.keypress.timeout);var self=this;this.keypress.timeout=window.setTimeout(function(){self.textchange();},0);}},keyup:function(e){var key=e.keyCode;if(key===38){e.preventDefault();this.up(e);}
-else if(key===40){e.preventDefault();this.down(e);}
-else if(e.ctrlKey&&key===77){$(".fbs-more-link",this.pane).click();}
-else if($.suggest.is_char(e)){window.clearTimeout(this.keypress.timeout);var self=this;this.keypress.timeout=window.setTimeout(function(){self.textchange();},0);}
-return true;},blur:function(e){if(this.dont_hide){return;}
-var data=this.input.data("data.suggest");if(!data){this.check_required(e);}
-this.hide_all();},tab:function(e){if(e.shiftKey||e.metaKey||e.ctrlKey){return;}
-var o=this.options,visible=this.pane.is(":visible")&&$("."+o.css.item,this.list).length,s=this.get_selected();if(visible&&s){this.onselect(s);this.hide_all();}},enter:function(e){var o=this.options,visible=this.pane.is(":visible");if(visible){if(e.shiftKey){this.shift_enter(e);e.preventDefault();}
-else if($("."+o.css.item,this.list).length){var s=this.get_selected();if(s){this.onselect(s);this.hide_all();e.preventDefault();}
-else{var data=this.input.data("data.suggest");if(o.soft){if(!data){this.check_required(e);}}
-else{if($("."+this.options.css.item+":visible",this.list).length){this.updown(false);e.preventDefault();}
-else if(!data){this.check_required(e);}}}}}},shift_enter:function(e){},escape:function(e){this.hide_all();},up:function(e){this.updown(true,e.ctrlKey||e.shiftKey);},down:function(e){this.updown(false,null,e.ctrlKey||e.shiftKey);},updown:function(goup,gofirst,golast){var inp=this.input,o=this.options,css=o.css,p=this.pane,l=this.list;if(!p.is(":visible")){if(!goup){this.textchange();}
-return;}
-var li=$("."+css.item+":visible",l);if(!li.length){return;}
-var first=$(li[0]),last=$(li[li.length-1]),cur=this.get_selected()||[],orig=inp.data("original.suggest"),prev=next=data=undefined;window.clearTimeout(this.ignore_mouseover.timeout);this._ignore_mouseover=false;if(goup){if(gofirst){first.trigger("mouseover.suggest");data=first.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(first);}
-else if(!cur.length){last.trigger("mouseover.suggest");data=last.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(last);}
-else if(cur[0]==first[0]){first.removeClass(css.selected);inp.val(orig);this.hoverout_list();}
-else{prev=cur.prevAll("."+css.item+":visible:first").trigger("mouseover.suggest");data=prev.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(prev);}}
-else{if(golast){last.trigger("mouseover.suggest");data=last.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(last);}
-else if(!cur.length){first.trigger("mouseover.suggest");data=first.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(first);}
-else if(cur[0]==last[0]){last.removeClass(css.selected);inp.val(orig);this.hoverout_list();}
-else{next=cur.nextAll("."+css.item+":visible:first").trigger("mouseover.suggest");data=next.data("data.suggest");if(data){inp.val(data.name);}
-else{inp.val(orig);this.hoverout_list();}
-this.scroll_to(next);}}},scroll_to:function(item){var l=this.list,scrollTop=l.scrollTop(),scrollBottom=scrollTop+l.innerHeight(),item_height=item.outerHeight(),offsetTop=item.prevAll().length*item_height,offsetBottom=offsetTop+item_height;if(offsetTop<scrollTop){this.ignore_mouseover();l.scrollTop(offsetTop);}
-else if(offsetBottom>scrollBottom){this.ignore_mouseover();l.scrollTop(scrollTop+offsetBottom-scrollBottom);}},textchange:function(){this.input.removeData("data.suggest");this.input.trigger("fb-textchange",this);var val=$.trim(this.input.val());if(val===""){this.status_start();return;}
-else{this.status_loading();}
-this.request(val);},request:function(val){calls=this.input.data("request.count.suggest");if(!calls){this.trackEvent(this.name,"start_session");calls=0;}
-calls+=1;this.trackEvent(this.name,"request","count",calls);this.input.data("request.count.suggest",calls);},response:function(data){if("cost"in data){this.trackEvent(this.name,"response","cost",data.cost);}
-if(!this.check_response(data)){return;}
-var result=[];if($.isArray(data)){result=data;}
-else if("result"in data){result=data.result;}
-var args=$.map(arguments,function(a){return a;});this.response_hook.apply(this,args);var first=null,self=this,o=this.options;$.each(result,function(i,n){var li=self.create_item(n,data).bind("mouseover.suggest",function(e){self.mouseover_item(e);}).data("data.suggest",n);self.list.append(li);if(i===0){first=li;}});this.input.data("original.suggest",this.input.val());if($("."+o.css.item,this.list).length===0){var $nomatch=$('<li class="nomatch">'+o.nomatch+'</li>').bind("click.suggest",function(e){e.stopPropagation();});this.list.append($nomatch);}
-args.push(first);this.show_hook.apply(this,args);this.position();this.pane_show();},pane_show:function(){if(!this.pane.is(":visible")){if(this.options.animate){this.pane.slideDown("fast");}
-else{this.pane.show();}}
-this.input.trigger("fb-pane-show",this);},create_item:function(data,response_data){var css=this.options.css;li=$('<li class="'+css.item+'">');var label=$("<label>").text(data.name);data.name=label.text();li.append($('<div class="'+css.item_name+'">').append(label));return li;},mouseover_item:function(e){if(this._ignore_mouseover){return;}
-var target=e.target;if(target.nodeName.toLowerCase()!=="li"){target=$(target).parents("li:first");}
-var li=$(target),css=this.options.css,l=this.list;$("."+css.item,l).each(function(){if(this!==li[0]){$(this).removeClass(css.selected);}});if(!li.hasClass(css.selected)){li.addClass(css.selected);this.mouseover_item_hook(li);}},mouseover_item_hook:function($li){},hoverover_list:function(e){},hoverout_list:function(e){},check_response:function(response_data){return true;},response_hook:function(response_data){this.list.empty();},show_hook:function(response_data){this.status_select();},position:function(){var p=this.pane,o=this.options;if(o.parent){return;}
-if(!self._position){var inp=this.input,pos=inp.offset(),input_width=inp.outerWidth(true),input_height=inp.outerHeight(true);pos.top+=input_height;var pane_width=p.outerWidth(),pane_height=p.outerHeight(),pane_right=pos.left+pane_width,pane_bottom=pos.top+pane_height,pane_half=pos.top+pane_height/2,scroll_left=$(window).scrollLeft(),scroll_top=$(window).scrollTop(),window_width=$(window).width(),window_height=$(window).height(),window_right=window_width+scroll_left,window_bottom=window_height+scroll_top;var left=true;if('left'==o.align){left=true;}
-else if('right'==o.align){left=false;}
-else if(pos.left>(scroll_left+window_width/2)){left=false;}
-if(!left){left=pos.left-(pane_width-input_width);if(left>scroll_left){pos.left=left;}}
-if(pane_half>window_bottom){var top=pos.top-input_height-pane_height;if(top>scroll_top){pos.top=top;}}
-this._position=pos;}
-p.css({top:this._position.top,left:this._position.left});},ignore_mouseover:function(e){this._ignore_mouseover=true;var self=this;this.ignore_mouseover.timeout=window.setTimeout(function(){self.ignore_mouseover_reset();},1000);},ignore_mouseover_reset:function(){this._ignore_mouseover=false;},get_selected:function(){var selected=null,select_class=this.options.css.selected;$("li",this.list).each(function(){var $this=$(this);if($this.hasClass(select_class)&&$this.is(":visible")){selected=$this;return false;}});return selected;},onselect:function($selected,focus){var data=$selected.data("data.suggest");if(data){this.input.val(data.name).data("data.suggest",data).trigger("fb-select",data);this.trackEvent(this.name,"fb-select","index",$selected.prevAll().length);}
-else{}
-if(focus){}},trackEvent:function(category,action,opt_label,opt_value){if(this.options.trackEvent){this.options.trackEvent(category,action,opt_label,opt_value)}},check_required:function(e){var required=this.options.required;if(required===true){if(!$.trim(this.input.val())){this.input.trigger("fb-required",{domEvent:e});return false;}}
-else if(required==="always"){this.input.trigger("fb-required",{domEvent:e});return false;}
-return true;},hide_all:function(e){this.pane.hide();this.input.trigger("fb-pane-hide",this);}};$.extend($.suggest,{defaults:{status:['Start typing to get suggestions...','Searching...','Select an item from the list:'],required:false,soft:false,nomatch:"no matches",css:{pane:"fbs-pane",list:"fbs-list",item:"fbs-item",item_name:"fbs-item-name",selected:"fbs-selected",status:"fbs-status"},css_prefix:null,trackEvent:null,parent:null,animate:true},$$:function(cls,ctx){return $("."+cls,ctx);},use_jsonp:function(service_url){if(!service_url){return false;}
-var pathname_len=window.location.pathname.length;var hostname=window.location.href;hostname=hostname.substr(0,hostname.length-pathname_len);if(hostname===service_url){return false;}
-return true;},strongify:function(str,substr){var strong=str;var index=str.toLowerCase().indexOf(substr.toLowerCase());if(index>=0){var substr_len=substr.length;strong=$("<div>").text(str.substring(0,index)).append($("<strong>").text(str.substring(index,index+substr_len))).append(document.createTextNode(str.substring(index+substr_len))).html();}
-return strong;},keyCode:{CAPS_LOCK:20,CONTROL:17,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ENTER:108,PAGE_DOWN:34,PAGE_UP:33,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,OPTION:18,APPLE:224},is_char:function(e){if(e.type==="keypress"){if((e.metaKey||e.ctrlKey)&&e.charCode===118){return true;}
-else if("isChar"in e){return e.isChar;}}
-else{var not_char=$.suggest.keyCode.not_char;if(!not_char){not_char={};$.each($.suggest.keyCode,function(k,v){not_char[''+v]=1;});$.suggest.keyCode.not_char=not_char;}
-return!((''+e.keyCode)in not_char);}}});var base={_destroy:$.suggest.prototype._destroy,request:$.suggest.prototype.request,show_hook:$.suggest.prototype.show_hook};$.suggest("suggest",{_init:function(){var self=this,o=this.options;if(!o.flyout_service_url){o.flyout_service_url=o.service_url;}
-this.jsonp=$.suggest.use_jsonp(o.service_url);if(!$.suggest.cache){$.suggest.cache={};}
-if(o.flyout){this.flyoutpane=$('<div style="display:none;position:absolute" class="fbs-reset '+o.css_prefix+o.css.flyoutpane+'">');$(document.body).append(this.flyoutpane);var hoverover=function(e){self.hoverover_list(e);};var hoverout=function(e){self.hoverout_list(e);};this.flyoutpane.hover(hoverover,hoverout).bind("mousedown.suggest",function(e){e.stopPropagation();self.pane.click();});if(!$.suggest.flyout){$.suggest.flyout={};}
-if(!$.suggest.flyout.cache){$.suggest.flyout.cache={};}}},_destroy:function(){base._destroy.call(this);if(this.flyoutpane){this.flyoutpane.remove();}},shift_enter:function(e){if(this.options.suggest_new){this.suggest_new();this.hide_all();}
-else{this.check_required(e);}},hide_all:function(e){this.pane.hide();if(this.flyoutpane){this.flyoutpane.hide();}
-this.input.trigger("fb-pane-hide",this);},request:function(val,start){var self=this,o=this.options;base.request.call(self);if(this.ac_xhr){this.ac_xhr.abort();this.ac_xhr=null;}
-var data={prefix:val};if(start){data.start=start;}
-$.extend(data,o.ac_param);var url=o.service_url+o.service_path+"?"+$.param(data),cached=$.suggest.cache[url];if(cached){this.response(cached,start?start:-1);return;}
-window.clearTimeout(this.request.timeout);this.request.timeout=window.setTimeout(function(){self.ac_xhr=$.ajax({"type":"GET","url":o.service_url+o.service_path,"data":data,"success":function(data,status){$.suggest.cache[url]=data;self.response(data,start?start:-1);},"error":function(xhr,s,ex){self.trackEvent(self.name,"request","error",{url:this.url,response:xhr.responseText});},"dataType":self.jsonp?"jsonp":"json","cache":true});},200);},create_item:function(data,response_data){var css=this.options.css,classs=[css.item];if("class"in data){classs.push(data["class"]);}
-var li=$('<li class="'+classs.join(" ")+'">');var name=$('<div class="'+css.item_name+'"><label>'+$.suggest.strongify(data.name,response_data.prefix)+'</label></div>');data.name=name.text();li.append(name);if(data.type&&data.type.length){var notable_type=data['notable:type']||data.type[0]['id']||'/common/topic';var types=[];$.each(data.type,function(i,t){if(notable_type==t['id']){types.push(t['name']);return false;}});name.prepend($('<div class="'+css.item_type+'">').text(types.join(", ")));}
-return li;},mouseover_item_hook:function(li){var data=li.data("data.suggest");if(this.options.flyout){if(data){this.flyout_request(data);}
-else{}}},check_response:function(response_data){return response_data.prefix===$.trim(this.input.val());},response_hook:function(response_data,start){if(this.flyoutpane){this.flyoutpane.hide();}
-if(start>0){$(".fbs-more",this.pane).remove();}
-else{this.list.empty();}},show_hook:function(response_data,start,first){base.show_hook.apply(this,[response_data]);var o=this.options,self=this,p=this.pane,l=this.list,result=response_data.result;var more=$(".fbs-more",p),suggestnew=$(".fbs-suggestnew",p);if(result&&result.length&&"start"in response_data){if(!more.length){var more_link=$('<a class="fbs-more-link" href="#" title="(Ctrl+m)">view more</a>');more=$('<div class="fbs-more">').append(more_link);more_link.bind("click.suggest",function(e){e.preventDefault();e.stopPropagation();var m=$(this).parent(".fbs-more");self.more(m.data("start.suggest"));});l.after(more);}
-more.data("start.suggest",response_data.start);more.show();}
-else{more.remove();}
-if(o.suggest_new){if(!suggestnew.length){var button=$('<button class="fbs-suggestnew-button">');button.text(o.suggest_new);suggestnew=$('<div class="fbs-suggestnew">').append('<div class="fbs-suggestnew-description">Your item not in the list?</div>').append(button).append('<span class="fbs-suggestnew-shortcut">(Shift+Enter)</span>').bind("click.suggest",function(e){e.stopPropagation();self.suggest_new(e);});p.append(suggestnew);}
-suggestnew.show();}
-else{suggestnew.remove();}
-if(first&&first.length&&start>0){var top=first.prevAll().length*first.outerHeight();var scrollTop=l.scrollTop();l.animate({scrollTop:top},"slow",function(){first.trigger("mouseover.suggest");});}},suggest_new:function(e){var val=$.trim(this.input.val());if(!val){return;}
-this.input.data("data.suggest",val).trigger("fb-select-new",val);this.trackEvent(this.name,"fb-select-new","index","new");this.hide_all();},more:function(start){if(start){var orig=this.input.data("original.suggest");if(orig!==null){this.input.val(orig);}
-this.request($.trim(this.input.val()),start);this.trackEvent(this.name,"more","start",start);}
-return false;},flyout_request:function(data){var self=this;if(this.flyout_xhr){this.flyout_xhr.abort();this.flyout_xhr=null;}
-var o=this.options,sug_data=this.flyoutpane.data("data.suggest");if(sug_data&&data.id===sug_data.id){if(!this.flyoutpane.is(":visible")){var s=this.get_selected();this.flyout_position(s);this.flyoutpane.show();}
-return;}
-var cached=$.suggest.flyout.cache[data.id];if(cached){this.flyout_response(cached);return;}
-var submit_data={id:data.id};if(o.as_of_time){submit_data.as_of_time=o.as_of_time;}
-window.clearTimeout(this.flyout_request.timeout);this.flyout_request.timeout=window.setTimeout(function(){self.flyout_xhr=$.ajax({"type":"GET","url":o.flyout_service_url+o.flyout_service_path,"data":submit_data,"success":function(data,status){data=self.jsonp?data:{id:submit_data.id,html:data};$.suggest.flyout.cache[data.id]=data;self.flyout_response(data);},"error":function(xhr,s,ex){self.trackEvent(self.name,"flyout","error",{url:this.url,response:xhr.responseText});},"dataType":self.jsonp?"jsonp":"html","cache":true});},200);},flyout_response:function(data){var o=this.options,p=this.pane,s=this.get_selected()||[];if(p.is(":visible")&&s.length){var sug_data=s.data("data.suggest");if(sug_data&&data.id===sug_data.id){this.flyoutpane.html(data.html);this.flyout_position(s);this.flyoutpane.show().data("data.suggest",sug_data);}}},flyout_position:function($item){var p=this.pane,fp=this.flyoutpane,css=this.options.css,pos=undefined,old_pos={top:parseInt(fp.css("top"),10),left:parseInt(fp.css("left"),10)},pane_pos=p.offset(),pane_width=p.outerWidth(),flyout_height=fp.outerHeight(),flyout_width=fp.outerWidth();if(this.options.flyout==="bottom"){pos=pane_pos;var input_pos=this.input.offset();if(pane_pos.top<input_pos.top){pos.top-=flyout_height;}
-else{pos.top+=p.outerHeight();}
-fp.addClass(css.flyoutpane+"-bottom");}
-else{pos=$item.offset();var item_height=$item.outerHeight();pos.left+=pane_width;var flyout_right=pos.left+flyout_width,scroll_left=$(document.body).scrollLeft(),window_right=$(window).width()+scroll_left;pos.top=pos.top+item_height-flyout_height;if(pos.top<pane_pos.top){pos.top=pane_pos.top;}
-if(flyout_right>window_right){var left=pos.left-(pane_width+flyout_width);if(left>scroll_left){pos.left=left;}}
-fp.removeClass(css.flyoutpane+"-bottom");}
-if(!(pos.top===old_pos.top&&pos.left===old_pos.left)){fp.css({top:pos.top,left:pos.left});}},hoverover_list:function(e){},hoverout_list:function(e){if(this.flyoutpane&&!this.get_selected()){this.flyoutpane.hide();}}});$.extend($.suggest.suggest,{defaults:{type:null,type_strict:"any",mql_filter:null,as_of_time:null,service_url:"http://www.freebase.com",service_path:"/private/suggest",align:null,flyout:true,flyout_service_url:null,flyout_service_path:"/private/flyout",suggest_new:null,nomatch:'<em class="fbs-nomatch-text">No suggested matches.</em><h3>Tips on getting better suggestions:</h3><ul class="fbs-search-tips"><li>Enter more or fewer characters</li><li>Add words related to your original search</li><li>Try alternate spellings</li><li>Check your spelling</li></ul>',css:{item_type:"fbs-item-type",flyoutpane:"fbs-flyout-pane"}}});function init_JSON(){if(!this.JSON){JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());}})(jQuery);
-jQuery.suggest.version='Version:r79501 Built:Fri Aug 14 2009 by daepark';
View
285 public/js/pjs/Tween.lib
@@ -0,0 +1,285 @@
+ /**********************************************************************
+ TERMS OF USE - EASING EQUATIONS
+ Open source under the BSD License.
+ Copyright (c) 2001 Robert Penner
+ JavaScript version copyright (c) 2006 by Philippe Maegerman
+ Adapted to work along with Processing.js (c) 2009 by Michael Aufreiter
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the author nor the names of contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ *****************************************/
+
+Tween = function Tween(obj, prop, func, begin, finish, duration) {
+ if (!arguments.length) return;
+ this.obj = obj;
+ this.prop = prop;
+ this.begin = begin;
+ this._pos = begin;
+ this.setDuration(duration);
+ if (func!=null && func!='') {
+ this.func = func;
+ }
+ this.setFinish(finish);
+}
+
+Tween.prototype = {
+ obj: new Object(),
+ func: function (t, b, c, d) { return c*t/d + b; },
+ begin: 0,
+ change: 0,
+ prevTime: 0,
+ prevPos: 0,
+ looping: false,
+ _playing: false,
+ _duration: 0,
+ _time: 0,
+ _pos: 0,
+ _position: 0,
+ _startTime: 0,
+ _finish: 0,
+ // callbacks
+ looped: function() {},
+ finished: function() {},
+ changed: function() {},
+ started: function() {},
+ resumed: function() {},
+ stopped: function() {},
+ // methods
+ setTime: function(t) {
+ this.prevTime = this._time;
+ if (t > this.getDuration()) {
+ if (this.looping) {
+ this.rewind (t - this._duration);
+ this.update();
+ this.looped();
+ } else {
+ this.update();
+ this.stop();
+ this.finished();
+ }
+ } else if (t < 0) {
+ this.rewind();
+ this.update();
+ } else {
+ this._time = t;
+ this.update();
+ }
+ },
+ getTime: function(){
+ return this._time;
+ },
+ setDuration: function(d){
+ this._duration = (d == null || d <= 0) ? 100000 : d;
+ },
+ getDuration: function(){
+ return this._duration;
+ },
+ setPosition: function(p){
+ this.prevPos = this._pos;
+ this.obj[this.prop] = p;
+ this._pos = p;
+ this.changed();
+ },
+ getPosition: function(t) {
+ if (t == undefined) t = this._time;
+ return this.func(t, this.begin, this.change, this._duration);
+ },
+ setFinish: function(f) {
+ this.change = f - this.begin;
+ },
+ getFinish: function() {
+ return this.begin + this.change;
+ },
+ isPlaying: function() {
+ return this._playing;
+ },
+ init: function(obj, prop, func, begin, finish, duration) {
+ if (!arguments.length) return;
+ this._listeners = new Array();
+ this.addListener(this);
+ this.obj = obj;
+ this.prop = prop;
+ this.begin = begin;
+ this._pos = begin;
+ this.setDuration(duration);
+ if (func!=null && func!='') {
+ this.func = func;
+ }
+ this.setFinish(finish);
+ },
+ start: function() {
+ this.rewind();
+ this._playing = true;
+ this.started();
+ },
+ rewind: function(t) {
+ this.stop();
+ this._time = (t == undefined) ? 0 : t;
+ this.fixTime();
+ this.update();
+ },
+ fforward: function() {
+ this._time = this._duration;
+ this.fixTime();
+ this.update();
+ },
+ update: function() {
+ this.setPosition(this.getPosition(this._time));
+ },
+ tick: function() {
+ if (this._playing) {
+ this.nextFrame();
+ }
+ },
+ nextFrame: function() {
+ this.setTime((this.getTimer() - this._startTime) / 1000);
+ },
+ stop: function() {
+ this._playing = false;
+ this.stopped();
+ },
+ continueTo: function(finish, duration) {
+ this.begin = this._pos;
+ this.setFinish(finish);
+ if (this._duration != undefined) {
+ this.setDuration(duration);
+ }
+ this.start();
+ },
+ resume: function() {
+ this.fixTime();
+ this._playing = true;
+ this.resumed();
+ },
+ yoyo: function () {
+ this.continueTo(this.begin,this._time);
+ },
+ fixTime: function() {
+ this._startTime = this.getTimer() - this._time * 1000;
+ },
+ getTimer: function() {
+ return new Date().getTime() - this._time;
+ }
+};
+
+// Tweening functions
+Tween.backEaseIn = function(t,b,c,d,a,p) {
+ if (s == undefined) var s = 1.70158;
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+}
+
+Tween.backEaseOut = function(t,b,c,d,a,p) {
+ if (s == undefined) var s = 1.70158;
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+}
+
+Tween.backEaseInOut = function(t,b,c,d,a,p) {
+ if (s == undefined) var s = 1.70158;
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+}
+
+Tween.elasticEaseIn = function(t,b,c,d,a,p) {
+ if (t==0) return b;
+ if ((t/=d)==1) return b+c;
+ if (!p) p=d*.3;
+ if (!a || a < Math.abs(c)) {
+ a=c; var s=p/4;
+ }
+ else
+ var s = p/(2*Math.PI) * Math.asin (c/a);
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+}
+
+Tween.elasticEaseOut = function (t,b,c,d,a,p) {
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
+}
+
+Tween.elasticEaseInOut = function (t,b,c,d,a,p) {
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) var p=d*(.3*1.5);
+ if (!a || a < Math.abs(c)) {var a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+}
+
+Tween.bounceEaseOut = function(t,b,c,d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+}
+
+Tween.bounceEaseIn = function(t,b,c,d) {
+ return c - Tween.bounceEaseOut (d-t, 0, c, d) + b;
+}
+
+Tween.bounceEaseInOut = function(t,b,c,d) {
+ if (t < d/2) return Tween.bounceEaseIn (t*2, 0, c, d) * .5 + b;
+ else return Tween.bounceEaseOut (t*2-d, 0, c, d) * .5 + c*.5 + b;
+}
+
+Tween.strongEaseInOut = function(t,b,c,d) {
+ return c*(t/=d)*t*t*t*t + b;
+}
+
+Tween.regularEaseIn = function(t,b,c,d) {
+ return c*(t/=d)*t + b;
+}
+
+Tween.regularEaseOut = function(t,b,c,d) {
+ return -c *(t/=d)*(t-2) + b;
+}
+
+Tween.regularEaseInOut = function(t,b,c,d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+}
+
+Tween.strongEaseIn = function(t,b,c,d) {
+ return c*(t/=d)*t*t*t*t + b;
+}
+
+Tween.strongEaseOut = function(t,b,c,d) {
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
+}
+
+Tween.strongEaseInOut = function(t,b,c,d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
+}
View
104 public/js/pjs/attribute.pjs
@@ -1,104 +0,0 @@
-/***************************************************************
-/* Attribute
-/**************************************************************/
-
-public class Attribute {
- String id = "";
- String name = "";
-
- float x;
- float y;
-
- float angleStart;
- float angleStop;
-
- float targetWeight = 1.0;
- float weight = 1.0;
- float direction = 1;
- float speed = 0.3;
-
- color col;
- boolean hovering = false;
- Resource resource;
-
- ArrayList values = new ArrayList();
-
- public Attribute(String label, Resource resource) {
- this.label = label;
- this.name = label;
- this.resource = resource;
- }
-
- public void setPos(float x, float y) {
- this.x = x;
- this.y = y;
- }
-
- public void setAngles(float angleStart, float angleStop) {
- this.angleStart = angleStart;
- this.angleStop = angleStop;
- }
-
- public void setCol(color col) {
- this.col = col;
- }
-
- public void setHovering(boolean hover) {
- this.hovering = hover;
- }
-
- public boolean isHovering() {
- return this.hovering;
- }
-
- public String getLabel() {
- return this.label;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void update() {
- if (weight>targetWeight-0.1 && weight<targetWeight+0.1) {
-
- } else {
- if (targetWeight<weight)
- direction = -1;
- else
- direction = 1;
- weight += speed*direction;
- }
- }
-
- public void draw() {
- float radius = this.resource.radius;
- fill(col);
-
- textFont(defaultFont, 45);
- if (this==resource.selectedAttribute && !resource.isTransitioning()) {
- textFont(defaultFont, 45);
- text(resource.selectedAttribute.getValues().size()+"", resource.x-50, resource.y-80, 100);
- textFont(defaultFont, 13);
-
- text("Resources", resource.x-50, resource.y-30, 100);
- textFont(defaultFont, 18);
- text(this.getName(), resource.x-50, resource.y+10, 100);
-
- fill(col);
- }
- textFont(defaultFont, fontSize);
- }
-
- public void addValue(Value val) {
- values.add(val);
- }
-
- public ArrayList getValues() {
- return values;
- }
-
- public Resource getResource() {
- return resource;
- }
-}
View
346 public/js/pjs/donut.pjs
@@ -0,0 +1,346 @@
+float EPSILON = TWO_PI/100; // tolerance for applying angleAdjustment
+
+public class DonutSlice {
+ Object subject;
+
+ boolean hovering = false;
+
+ DonutSegment seg;
+
+ float angleStart;
+ float angleStop;
+
+ public DonutSlice(Object subject, DonutSegment seg) {
+ this.subject = subject;
+ this.seg = seg;
+ }
+
+ public void update() {
+ // patched by DonutSegment#update() for now
+ }
+
+ public void draw() {
+ float radius = seg.donut.radius;
+ stroke(255, 255, 255, 100);
+ strokeWeight(1);
+
+ // TODO: if (this===seg.slices[seg.slices.size()-1])
+ line(this.seg.donut.x+cos(this.angleStop)*(radius-seg.relativeBreadth()/2), this.seg.donut.y+sin(this.angleStop)*(radius-seg.relativeBreadth()/2), this.seg.donut.x+cos(this.angleStop)*(radius+seg.relativeBreadth()/2), this.seg.donut.y+sin(this.angleStop)*(radius+seg.relativeBreadth()/2));
+
+ // highlight selectedSlice
+ if (this.hovering) {
+ curContext.lineCap = "butt"; // processing.js patch!
+ noFill();
+ stroke(0,0,0,20);
+ strokeWeight(seg.relativeBreadth());
+ arc(seg.donut.x, seg.donut.y, seg.donut.radius*2, seg.donut.radius*2, angleStart, angleStop);
+ }
+
+ noStroke();
+ strokeWeight(0); // reset
+
+ if (seg.donut.selectedSegment===this.seg && !seg.fan.isPlaying() && seg.donut.selectedSlice.hovering) {
+ float theta = ((this.angleStart+this.angleStop) / 2)%TWO_PI+0.05;
+ float angle = theta;
+
+// curContext.font = "13.1px Courier";
+
+ curContext.textAlign = "left";
+ if (theta > PI/2 && theta < (PI+PI/2)-0.05) {
+ fWidth = curContext.measureText(this.subject.getName()).width;
+ angle -= 0.05;
+ } else
+ fWidth = 0;
+
+ pushMatrix();
+ translate(seg.donut.x, seg.donut.y);
+ rotate(angle);
+ pushMatrix();
+ translate(seg.donut.radius+50+fWidth,0);
+ if (this.hovering)
+ fill(HIGHLIGHTED_LABEL_COLOR);
+ else
+ fill(LABEL_COLOR);
+
+ pushMatrix();
+ if (theta > PI/2 && theta < (PI+PI/2)-0.05) {
+ rotate(PI);
+ }
+ curContext.fillText(this.subject.getName(), 0, 0);
+ popMatrix();
+ popMatrix();
+ popMatrix();
+ }
+ }
+
+ public boolean checkSelected() {
+ float disX = mouseX - seg.donut.x;
+ float disY = mouseY - seg.donut.y;
+
+ // calculate polar coordinates
+ float r = Math.sqrt(sq(disX)+sq(disY));
+ float angle = atan2(disY, disX);
+ if (angle<0) angle = TWO_PI+angle; // shift to 0-TWO_PI interval
+
+ float start = this.angleStart % TWO_PI;
+ float stop = this.angleStop % TWO_PI;
+
+ if (r >= seg.donut.radius-seg.relativeBreadth()/2 && r <= seg.donut.radius+seg.relativeBreadth()/2 &&
+ ((angle>start && angle<stop) || (start>stop && (angle > start || angle < stop)) )) {
+
+ seg.donut.setSelectedSlice(this);
+ // return true
+ subject.hovering = hovering = true;
+ } else {
+ subject.hovering = hovering = false;
+ }
+
+ if (seg.donut.totalSlices()==1) {
+ seg.donut.setSelectedSlice(this);
+ subject.hovering = hovering = true;
+ }
+
+ return subject.hovering;
+ }
+}
+
+public class DonutSegment {
+
+ float weight;
+ float angleStart;
+ float angleStop;
+ float breadth = STROKE_WEIGHT_COLLAPSED;
+ Donut donut;
+ Object subject;
+
+ ArrayList slices;
+ color col;
+ Tween fan;
+ Tween breadthTween;
+
+ public DonutSegment(Subject subject, Donut donut) {
+ this.donut = donut;
+ this.subject = subject;
+ this.slices = new ArrayList();
+
+ this.weight = 1.0;
+ this.fan = new Tween(this, "weight", Tween.strongEaseInOut, 1.0, 3.0, 0.8);
+ this.breadthTween = new Tween(this, "breadth", Tween.strongEaseInOut, STROKE_WEIGHT_COLLAPSED, STROKE_WEIGHT_EXPANDED, 0.8);
+ }
+
+ // the resulting share of the segment (in percent)
+ public float share() {
+ return (slices.size()/donut.totalSlices()); // the share of total slices
+ }
+
+ // the weighted share of the segment in respect of the segment's weight (in percent)
+ public float weightedShare() {
+ return share()*weight/donut.totalWeight();
+ }
+
+ // the resulting size of the segment (in radiants)
+ public float amount() {
+ return (TWO_PI)*weightedShare();
+ }
+
+ public void update() {
+ // update local angleStart, angleStop using the shared angleOffset
+ this.angleStart = donut.angleOffset;
+
+ float amount = this.amount();
+ float tmp = this.angleStart;
+ for (int i=0; i<slices.size(); i++) {
+ // TODO: delegate the work to DonutSlice#update() -> slices[i].update();
+ slices[i].angleStart = tmp;
+ slices[i].angleStop = tmp+=amount/slices.size();
+ }
+
+ this.angleStop = donut.angleOffset+=amount;
+ }
+
+ public void draw() {
+
+ // processing.js patch!
+ curContext.lineCap = "butt";
+ fill(DONUT_BACKGROUND);
+ noStroke();
+
+ noFill();
+ stroke(DONUT_BACKGROUND);
+ strokeWeight(relativeBreadth());
+
+ curContext.globalAlpha = 1.0;
+ arc(donut.x, donut.y, (donut.radius+OUTER_BORDER)*2, (donut.radius+OUTER_BORDER)*2, this.angleStart, this.angleStop);
+ curContext.globalAlpha = donut.alpha;
+
+ stroke(col);
+
+ arc(donut.x, donut.y, donut.radius*2, donut.radius*2, this.angleStart, this.angleStop);
+
+ curContext.textAlign = "center";
+ if (this===donut.selectedSegment && donut.showLabels) {
+ fill(col);
+ curContext.font = "40px GraublauWeb";
+ curContext.fillText(this.slices.size(), donut.x, donut.y+15);
+
+ curContext.font = "20px GraublauWeb";
+ curContext.fillText("Relationships", donut.x, donut.y+40);
+
+ curContext.font = "20px GraublauWeb";
+ curContext.fillText(this.subject.getName(), donut.x, donut.y-25, 120);
+
+ noStroke();
+ //ellipse(donut.x, donut.y, 14, 14);
+ }
+
+ // draw slices
+ for (int i=0; i<slices.size(); i++) {
+ slices[i].draw();
+ }
+ }
+
+ // ASKKEN specific use breadth relative to current radius
+ public float relativeBreadth() {
+ return (donut.radius/RADIUS_OPENED)*this.breadth;
+ }
+
+ // start expanding tween
+ public void expand() {
+ this.fan.continueTo(3.0, 0.8);
+ this.breadthTween.continueTo(STROKE_WEIGHT_EXPANDED, 0.8);
+ }
+
+ // start contracting tween
+ public void contract() {
+ this.fan.continueTo(1.0, 0.8);
+ this.breadthTween.continueTo(STROKE_WEIGHT_COLLAPSED, 0.8);
+ }
+
+ public void addSlice(Object subject) {
+ slices.add(new DonutSlice(subject, this));
+ }
+}
+
+public class Donut {
+ float x;
+ float y;
+ float radius;
+
+ float alpha; // askken-specific
+
+ float angleOffset = 0.0;
+ ArrayList segments;
+ int colorCount = 0;
+ DonutSegment selectedSegment = null;
+ DonutSlice selectedSlice = null;
+ float angleAdjustment = 0.0;
+ boolean opened = true;
+ boolean showLabels = false;
+
+ // callbacks TODO: clean this up!
+ Object segmentChanged = function() {};
+
+ public Donut(float x, float y, float r) {
+ this.x = x;
+ this.y = y;
+ this.radius = r;
+ this.segments = new ArrayList();
+ }
+
+ public setSelectedSlice(DonutSlice s) {
+ this.selectedSlice = s;
+
+ if (s.seg != this.selectedSegment) {
+ if (this.selectedSegment!=null) {
+ this.selectedSegment.contract();
+ }
+
+ this.selectedSegment = s.seg;
+ this.selectedSegment.expand();
+ this.segmentChanged();
+ }
+ }
+
+ public void update() {
+ this.angleAdjustment = 0.0;
+ this.angleOffset = 0.0;
+
+
+ if (selectedSlice != null) {
+ float targetAngle = (selectedSlice.angleStart+selectedSlice.angleStop) / 2;
+ }
+
+ // perform tweens first
+ for (int i=0; i < segments.size(); i++) {
+ segments[i].fan.tick();
+ segments[i].breadthTween.tick();
+ }
+
+ // then update the values
+ for (int i=0; i < segments.size(); i++) {
+ segments[i].update();
+ }
+
+ if (selectedSlice != null) {
+ float offset = targetAngle-((selectedSlice.angleStart+selectedSlice.angleStop) / 2);
+
+ if (Math.abs(offset)>EPSILON) {
+ float targetAdjustment = this.angleAdjustment+offset;
+ if (targetAdjustment<0) {
+ targetAdjustment = TWO_PI - targetAdjustment;
+ }
+
+ this.angleAdjustment = targetAdjustment;
+ }
+ }
+
+ // recalc again if angleAdjustment is needed.
+ if (angleAdjustment>0) {
+ this.angleOffset = this.angleAdjustment;
+ for (int i=0; i < segments.size(); i++) {
+ segments[i].update();
+ }
+ }
+ }
+
+ /* this method is probably called to often.
+ /* maybe that's what slows down the expand/contract tweening */
+ public float totalWeight() {
+ float sum = 0.0;
+ for (int i=0; i < segments.size(); i++) {
+ sum += segments[i].weight*segments[i].share();
+ }
+ return sum;
+ }
+
+ public float totalSlices() {
+ int sum = 0;
+ for (int i=0; i < segments.size(); i++) {
+ sum += segments[i].slices.size();
+ }
+ return sum;
+ }
+
+ public void draw() {
+ noStroke();
+
+ // inner spacer
+ fill(DONUT_BACKGROUND);
+ ellipse(this.x, this.y, radius*2, radius*2);
+
+ curContext.globalAlpha = this.alpha;
+ // draw attributes
+ for (int i = 0 ; i < segments.size(); i++) {
+ segments[i].draw();
+ }
+ curContext.globalAlpha = 1.0;
+ }
+
+ public void addSegment(DonutSegment s) {
+ s.col = colors[colorCount%11]; // assign a color
+ if (colorCount==0)
+ setSelectedSlice(s.slices[0]);
+ segments.add(s);
+ colorCount += 2;
+ }
+}
View
97 public/js/pjs/edge.pjs
@@ -3,12 +3,22 @@
/**************************************************************/
class Edge {
+ boolean hovering = false;
Node from;
Node to;
+ Object subject;
+
+ float radius = 30;
+
+ float x1 = 0;
+ float x2 = 0;
+ float y1 = 0;
+ float y2 = 0;
- public Edge(Node from, Node to) {
+ public Edge(Node from, Node to, Object subject) {
this.from = from;
this.to = to;
+ this.subject = subject;
}
public Node getFrom() {
@@ -18,4 +28,87 @@ class Edge {
public Node getTo() {
return to;
}
-}
+
+ public void update() {
+ // original positions
+ float ox1 = from.getParticle().position().x;
+ float ox2 = to.getParticle().position().x;
+ float oy1 = from.getParticle().position().y;
+ float oy2 = to.getParticle().position().y;
+
+ float wx = ox2-ox1;
+ float wy = oy2-oy1;
+
+ float ww = sqrt(wx*wx+wy*wy); // calculates the vectors amount
+
+ // calculate relative values for arrows
+ x1 = ox1+wx*((from.getSubject().currentRadius()-10)/ww);
+ y1 = oy1+wy*((from.getSubject().currentRadius()-10)/ww);
+
+ x2 = ox2-wx*((to.getSubject().currentRadius()+5)/ww);
+ y2 = oy2-wy*((to.getSubject().currentRadius()+5)/ww);
+
+
+ }
+
+ public void draw() {
+ stroke(255);
+
+ if (hovering)
+ fill(255);
+ else
+ fill(this.subject.col);
+
+ strokeWeight(4);
+
+ if (this.subject.subject.getName()[0] == "O")
+ arrow(x1, y1, x2, y2, 7); // Outgoing Arrows
+ else
+ arrow(x2, y2, x1, y1, 17); // Incoming Arrows
+
+ // noStroke();
+ ellipse(centerX(), centerY(), 20, 20);
+
+ textFont(defaultFont, 13);
+
+ fill(255);
+ text(this.subject.subject.getName()[0], centerX()-3, centerY()-8);
+ // fontWidth = defaultFont.width((idx+1)+". "+this.getName()) * fontSize;
+ // text((idx+1)+". "+this.getName(), this.x-fontWidth/2, this.y+10+labelOffset+30);
+ textFont(defaultFont, fontSize);
+ }
+
+ public void drawLabel() {
+ textFont(defaultFont, 13);
+ fill(255);
+ noStroke();
+ rect(centerX()-3, centerY()-12, 200, 24);
+
+ fill(100);
+ text(this.subject.subject.getName(), centerX()-3, centerY()-8);
+ // fontWidth = defaultFont.width((idx+1)+". "+this.getName()) * fontSize;
+ // text((idx+1)+". "+this.getName(), this.x-fontWidth/2, this.y+10+labelOffset+30);
+ textFont(defaultFont, fontSize);
+ }
+
+ public void centerX() {
+ return x1 + 0.5*(x2-x1);
+ }
+
+ public void centerY() {
+ return y1 + 0.5*(y2-y1);
+ }
+
+ public boolean mouseOver() {
+ float disX = centerX() - mouseX;
+ float disY = centerY() - mouseY;
+
+ if(sqrt(sq(disX) + sq(disY)) < radius/2 ) {
+ hovering = true;
+ return true;
+ } else {
+ hovering = false;
+ return false;
+ }
+ }
+}
View
121 public/js/pjs/graph.pjs
@@ -23,10 +23,11 @@ class Graph {
ParticleSystem ps;
Node selectedNode;
+ boolean dragging = false;
+
Node dragNode;
- float NODE_SIZE = 70;
- float EDGE_LENGTH = 190;
+ float EDGE_LENGTH = 300;
float EDGE_STRENGTH = 0.5;
float SPACER_STRENGTH = 1000;
@@ -39,6 +40,27 @@ class Graph {
ps.setDrag( 0.2 );
}
+ // gets node by resource id
+ void getNodeById(String id) {
+ int i = 0; Node n = null;
+ while (i < g.nodes.size()) {
+ if (this.nodes[i].subject.id==id)
+ return this.nodes[i];
+ i++;
+ }
+ return null;
+ }
+
+ void selectNode(Node n) {
+ selectedNode.subject.setClosed();
+ selectedNode = n;
+
+ $('aside').html(selectedNode.getSubject().html);
+
+ selectedNode.subject.setOpen();
+ dragging = true;
+ }
+
public void setSelectedNode(Node sNode) {
this.selectedNode = sNode;
}
@@ -59,6 +81,10 @@ class Graph {
public Node getDragNode() {
return this.dragNode;
}
+
+ public boolean isDragging() {
+ return (this.dragNode) ? this.dragging : false
+ }
public void addEdge(Edge e) {
// Edge exists?
@@ -72,7 +98,7 @@ class Graph {
edges.add(e);
// Add a spring
ps.makeSpring( e.getFrom().getParticle(), e.getTo().getParticle(), EDGE_STRENGTH, EDGE_STRENGTH, EDGE_LENGTH );
- e.getTo().getParticle().position().set( e.getFrom().getParticle().position().x + random( -1, 1 ), e.getFrom().getParticle().position().y + random( -1, 1 ), 0 );
+ e.getTo().getParticle().position().set( mouseX, mouseY, 0 );
}
}
@@ -86,9 +112,51 @@ class Graph {
if (n!=nodes[i])
ps.makeAttraction( n.getParticle(), nodes[i].getParticle(), -SPACER_STRENGTH, 15 );
}
-
}
+ public void removeEdge(Edge e) {
+ var idx = $.inArray(e, edges);
+ if (idx>=0)
+ edges.remove(idx);
+ }
+
+ public void removeNode(Node n) {
+ // delete concerning springs
+ ArrayList springs = this.ps.springs.clone();
+ for (int i=0; i<springs.size(); i++) {
+ if (n.p == springs[i].a || n.p == springs[i].b)
+ this.ps.removeSpring(springs[i]);
+ }
+ // delete concerning attractions
+ ArrayList attractions = this.ps.attractions.clone();
+ for (int i=0; i<attractions.size(); i++) {
+ if (n.p == attractions[i].a || n.p == attractions[i].b)
+ this.ps.removeAttraction(attractions[i]);
+ }
+ // remove concerning edges
+ ArrayList edgs = this.edges.clone();
+ for (int i=0; i<edgs.size(); i++) {
+ if (n == edgs[i].from || n == edgs[i].to)
+ removeEdge(edgs[i]);
+ }
+ // remove corresponding particle
+ this.ps.removeParticle(n.p);
+ // finally remove the node itself
+ var idx = $.inArray(n, nodes);
+ if (idx>=0)
+ nodes.remove(idx);
+
+ $("#path li[id=resource_"+idx+"]").remove();
+
+ if (nodes.size() == 0) {
+ console.log("TODO: there are no nodes left. clean it up!");
+ // clear workspace return;
+ }
+
+ }
+
+
+
public ArrayList getOutgoingEdges(Node n) {
ArrayList outgoingEdges = new ArrayList();
for (int i=0; i < edges.size(); i++) {
@@ -111,7 +179,7 @@ class Graph {
public void unVisitNodes() {
for(int i = 0; i < nodes.size(); i++) {
- nodes.get(i).setVisited(false);
+ nodes[i].setVisited(false);
}
}
@@ -143,25 +211,44 @@ class Graph {
}
public void draw() {
- Arraylist nodeList = this.depthFirstWalk(nodes[0]);
-
-
// Draw edges
for ( int i = 0; i < edges.size(); i++ ) {
Edge e = edges.get(i);
- stroke(255);
- strokeWeight(10);
-
- line(e.getFrom().getParticle().position().x, e.getFrom().getParticle().position().y, e.getTo().getParticle().position().x, e.getTo().getParticle().position().y)
+ if (e) {
+ e.update();
+ e.draw();
+ }
}
// Draw nodes
- for (int i=0; i < nodeList.size(); i++) {
- if (nodeList[i]!=selectedNode)
- nodeList[i].draw();
+ for (int i=0; i < nodes.size(); i++) {
+ if (nodes[i]!=selectedNode)
+ nodes[i].draw();
}
+ // Draw selected Node
selectedNode.draw();
-
+
+ // TODO: draw active edgeLabel if currently requested
+
+ // Draw edges
+ for ( int i = 0; i < edges.size(); i++ ) {
+ Edge e = edges.get(i);
+ if (e && e.hovering)
+ e.drawLabel();
+ }
+ }
+
+ public void checkSelected() {
+ dragNode = null;
+ // Iterate over nodes
+ for (int i=0; i< g.nodes.size(); i++) {
+ if (nodes[i].mouseOver() && dragNode !== nodes[i]) {
+ dragNode = nodes[i];
+ nodes[i].subject.hovering = true;
+ } else {
+ nodes[i].subject.hovering = false;
+ }
+ }
}
-}
+}
View
195 public/js/pjs/network.pjs
@@ -1,3 +1,6 @@
+import( '/js/pjs/Tween.lib' );
+import( '/js/pjs/util.lib' );
+
color[] colors = new color[12];
colors[0] = color(171, 199, 49);
@@ -13,8 +16,22 @@ colors[9] = color(131, 127, 67);
colors[10] = color(144, 150, 60);
colors[11] = color(157, 175, 55);
+color LABEL_COLOR = color(150);
+color HIGHLIGHTED_LABEL_COLOR = color(55);
+color DONUT_BACKGROUND = color(255);
+color BACKGROUND = color(234,239,224);
+
+// global constants
+float STROKE_WEIGHT_COLLAPSED = 50;
+float STROKE_WEIGHT_EXPANDED = 60;
+float OUTER_BORDER = 5;
+
+float RADIUS_OPENED = 110;
+float RADIUS_CLOSED = 50;
+
+
PFont defaultFont = loadFont("/fonts/GraublauWeb-Regular.svg");
-PFont boldFont = loadFont("/fonts/GraublauWeb-Bold.svg");
+
int fontSize = 11.3;
int fontSizeCaption = 16;
@@ -23,162 +40,130 @@ int nodeCount = 0;
Resource fetchResource(String resourceId) {
// init dummy resource
Resource resource = new Resource(resourceId, "", "");
+ nodeCount++;
resource.idx = nodeCount;
- nodeCount++;
resource.fetchData(); // start fetching data from freebase
return resource;
}
Graph g;
Resource r;
-float NODE_SIZE = 10;
-float EDGE_LENGTH = 20;
-float EDGE_STRENGTH = 0.2;
-float SPACER_STRENGTH = 1000;
-
-int selectedValueIndex;
-int selectedNodeIndex;
+int selectedEdgeIndex = null;
void resize() {
size(window.innerWidth-10, window.innerHeight-15);
}
void setup() {
- g = new Graph();
- // Activate font for output
+ myGraph = g = new Graph();
+ // myGraph exposes the graph to the outside world
textFont(defaultFont, fontSize);
-
+ nodeCount = 0;
resize();
fill(0);
smooth();
frameRate(30);
- background(234,239,224);
+ background(BACKGROUND);
if (resourceId) {
r = fetchResource(resourceId);
-
-
g.addNode(root = new Node(r));
- g.setSelectedNode(root);
- $('aside').html(r.html);
+ g.selectedNode = root;
+ $('#details').html(r.html);
}
}
void draw() {
if (g.nodes.size()>0) {
g.tick();
- background(234,239,224);
+ background(BACKGROUND);
g.draw();
}
}
void mouseMoved() {
if (!resourceId) return;
- if (g.getDragNode()==null) {
- Resource r = g.getSelectedNode().getSubject();
- ArrayList values = r.getValues();
- Attribute oldSelectedAttribute = r.getSelectedAttribute();
- int oldValueIndex = selectedValueIndex;
- selectedValueIndex = null;
-
- // Iterate over values
- for (int i = 0; i < values.size(); i++) {
- if (values[i].mouseOver()) {
- selectedValueIndex = i;
- }
- }
-
- if (selectedValueIndex!=null) {
- if (oldSelectedAttribute != r.getSelectedAttribute()) {
- float offset = 0;
- if (selectedValueIndex != null) {
-
- if (r.previousSelectedAttribute!=null) r.previousSelectedAttribute.weight = 1.0;
- r.selectedAttribute.weight = 4.0;
- // r.transitioning = true;
- float targetAngle = (values[selectedValueIndex].angleStart +values[selectedValueIndex].angleStop) / 2;
- if (oldSelectedAttribute!=null) oldSelectedAttribute.targetWeight = 1.0;
- r.getSelectedAttribute().targetWeight = 4.0;
-
- r.update();
- currentAngle = (r.getValues()[selectedValueIndex].angleStart +r.getValues()[selectedValueIndex].angleStop) / 2
-
- offset = targetAngle-currentAngle;
- float targetAdjustment = r.angleAdjustment+offset;
- if (targetAdjustment<0) {
- targetAdjustment = TWO_PI - targetAdjustment;
- }
- r.setAngleAdjustment(targetAdjustment);
- }
- }
- }
-
- if (selectedValueIndex==null && r.selectedAttribute!=null) {
- // r.selectedAttribute.weight = 1.0;
- // r.setSelectedAttribute(null);
- }
-
- selectedNodeIndex = null;
+ if (!g.isDragging()) {
+ Resource r = g.selectedNode.subject;
+ r.checkSelected();
+
+ g.checkSelected(); // checkDragged
+
+ selectedEdgeIndex = null;
// Iterate over nodes
- for (int i=0; i< g.nodes.size(); i++) {
- if (g.nodes[i].mouseOver()) {
- selectedNodeIndex = i;
+ for (int i=0; i< g.edges.size(); i++) {
+ if (g.edges[i].mouseOver()) {
+ selectedEdgeIndex = i;
}
}
}
}
+
+
+
+
+void addNode(Value val) {
+ Resource r = g.selectedNode.subject;
+ Node n = g.getNodeById(val.getId());
+
+ // close current resource
+ r.setClosed();
+
+ DonutSegment seg = r.donut.selectedSegment;
+
+ if (!n) { // not found
+ Resource r2 = fetchResource(val.getId());
+ g.addNode(newNode = new Node(r2));
+ g.addEdge(new Edge(g.selectedNode, newNode, seg));
+ g.setSelectedNode(newNode);
+ } else {
+ g.addEdge(new Edge(g.selectedNode, n, seg));
+ g.setSelectedNode(n);
+ g.selectedNode.subject.setOpen();
+ }
+}
+
+
+
+
+
void mousePressed() {
+
+ // ->> g.checkSelected()
+ // g.checkSelected(); // updates g.selectedNode if necessary
+
+ if (g.dragNode!==g.selectedNode && g.dragNode!=null) {
+ g.selectNode(g.dragNode);
+
+ return; // that's all
+ }
+
+ Resource r = g.selectedNode.subject;
+
if (!resourceId) return;
if (mouseButton == LEFT) {
- Node selectedNode = g.getSelectedNode();
- Resource r = selectedNode.getSubject();
+ Resource r = g.selectedNode.subject;
+ Value selectedValue = r.getSelectedValue();
- if (selectedValueIndex != null) {
- // Close current resource
- r.setClosed();
- r2 = fetchResource(r.getValues()[selectedValueIndex].getId());
-
- // Check if resource already exists
- int i = 0; Node n = null;
- while (i < g.nodes.size() && !n) {
- if (g.nodes[i].getSubject().getId()==r2.getId())
- n = g.nodes[i];
- i++;
- }
-
- if (!n) {
- g.addNode(newNode = new Node(r2));
- g.addEdge(new Edge(selectedNode, newNode));
- g.setSelectedNode(newNode);
-
- } else {
- g.addEdge(new Edge(selectedNode, n));
- g.setSelectedNode(g.nodes[0]);
- }
-
- } else if (selectedNodeIndex != null) {
- // TODO: Zoom to node
- selectedNode = g.nodes[selectedNodeIndex];
- g.setDragNode(selectedNode);
- if (g.getSelectedNode()!=selectedNode) {
- r.setClosed();
- selectedNode.getSubject().setOpen();
- $('aside').html(selectedNode.getSubject().html);
- g.setSelectedNode(selectedNode);
- }
+ if (selectedValue !== null && selectedValue.hovering) {
+ addNode(selectedValue);
+ } else if (g.selectedNode != null) {
+ g.dragging = true;
}
-
} else { // Right click
- Node selectedNode = g.getSelectedNode();
- Resource r = selectedNode.getSubject();
+ // removes resource from graph
+ Resource r = g.selectedNode.subject;
+ g.removeNode(g.selectedNode);
+ g.selectedNode = g.nodes[0];
}
}
+
void mouseReleased() {
- g.setDragNode(null);
+ g.dragging = false;
}
void mouseDragged() {
View
8 public/js/pjs/node.pjs
@@ -14,7 +14,7 @@ class Node {
public void setGraph(Graph g) {
this.g = g;
this.ps = g.ps;
- this.p = ps.makeParticle(1.0,300,200,0);
+ this.p = ps.makeParticle(1.0,window.innerWidth/2+150,window.innerHeight/2,0);
}
public Object getSubject() {
@@ -56,6 +56,10 @@ class Node {
public ArrayList getIncomingEdges() {
return g.getIncomingEdges(this);
}
+
+ public ArrayList getEdges() {
+ // apend incoming an outgoing edges
+ }
public Particle getParticle() {
return p;
@@ -76,4 +80,4 @@ class Node {
return subject.mouseOver();
}
-}
+}
View
36 public/js/pjs/physics.pjs
@@ -197,12 +197,10 @@ public class ModifiedEulerIntegrator
}
-
/***************************************************************
/* RungeKuttaIntegrator
/**************************************************************/
-
public class RungeKuttaIntegrator
{
ArrayList originalPositions;
@@ -957,27 +955,33 @@ public class ParticleSystem
public void removeParticle( Particle p )
{
- particles.remove( p );
+ var idx = $.inArray(p, particles);
+ if (idx>=0)
+ particles.remove(idx);
}
- public Spring removeSpring( int i )
- {
- return springs.remove( i );
- }
+ // public Spring removeSpring( int i )
+ // {
+ // return springs.remove( i );
+ // }
- public Attraction removeAttraction( int i )
- {
- return attractions.remove( i );
- }
+ // public Attraction removeAttraction( int i )
+ // {
+ // return attractions.remove( i );
+ // }
- public void removeAttraction( Attraction s )
+ public void removeAttraction( Attraction a )
{
- attractions.remove( s );
+ var idx = $.inArray(a, attractions);
+ if (idx>=0)
+ attractions.remove(idx);
}
- public void removeSpring( Spring a )
+ public void removeSpring( Spring s )
{
- springs.remove( a );
+ var idx = $.inArray(s, springs);
+ if (idx>=0)
+ springs.remove(idx);
}
public void removeCustomForce( Force f )
@@ -1058,4 +1062,4 @@ public class Vector3D
{
return x == 0 && y == 0 && z == 0;
}
-}
+}
View
535 public/js/pjs/resource.pjs
@@ -1,78 +1,124 @@
/***************************************************************
-/* Resource
+/* Attribute
/**************************************************************/
+public class Attribute {
+ String id = "";
+ String name = "";
+ String html = "";
+ Resource r;
+ ArrayList values = new ArrayList();
-// easing function
+ public Attribute(String id, String name, Resource r) {
+ this.id = id;
+ this.name = name;
+ this.r = r;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public Value addValue(String id, String name) {
+ Value val = new Value(id, name, this)
+ values.add(val);
+ return val;
+ }
+}
-function easeInOutCubic(pos) {
- if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,3);
- return 0.5 * (Math.pow((pos-2),3) + 2);
+/***************************************************************
+/* Value
+/**************************************************************/
+
+public class Value {
+ String id = "";
+ String name = "";
+ String html = "";
+ Attribute attr;
+ boolean hovering = false; // shares the hovering property from the slice
+
+ public Value(String id, String name, Attribute attr) {
+ this.id = id;
+ this.name = name;
+ this.attr = attr;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
}
+/***************************************************************
+/* Resource
+/**************************************************************/
+
public class Resource {
- boolean selected = false;
boolean hovering = false;
boolean dragging = false;
- boolean transitioning = false;
boolean loading = true;
-
- String state = "open"
- float theta = 0; // for spinning animation
-
- int tweenDuration = 15; // in frames
- int frameCount = 0;
- boolean loading = true;
-
- float targetWeight = 1.0;
- float highlightedWeight = 4.0;
+ Tween resize;
+ Donut donut;
- float diameter = 10;
+ String state = "open";
+ boolean loading = true;
+
Attribute selectedAttribute = null;
Attribute previousSelectedAttribute = null;
-
- float angleAdjustment = PI/2;
-
- boolean open = true;
+
+ boolean open = true;
int numValues = 0;
- int idx = 0;
+ int idx = 0; // the resource's unique numeric id in the graph
- int myColor = color(255);
- int myStrokeWeight = 1;
- int myStrokeColor = color(100);
- int myFillColor = color(200);
-
float x;
float y;
// internals
ArrayList attributes;
+ int radius = RADIUS_CLOSED;
- int radius = 40;
- float radiusOuter = 110;
- float targetRadiusOuter = 110;
-
-
- float radiusClosed = 40;
- int n = 40;
- float angle = 0;
+ float angle = 0; // REMOVE?
String id = "";
String name = "";
String html = "";
+
+ int NUM_LOADING_SEGMENTS = 11;
+ int progress = 0;
public Resource(String id, String name, String html) {
+ var that = this;
this.attributes = new ArrayList();
this.id = id;
this.name = name;
this.html = html;
+
+ donut = new Donut(275, 275, RADIUS_CLOSED); // TODO!
+ donut.segmentChanged = function() {
+ // this refers to the donut object
+ Attributes.select(this.selectedSegment);
+ };
+
+ // initialize tweens
+ resize = new Tween(donut, "radius", Tween.strongEaseInOut, RADIUS_CLOSED, RADIUS_OPENED, 1);
+ resize.finished = function() {
+ if (that.state=="open") {
+ that.donut.showLabels = true;
+ }
+ };
}
public void fetchData() {
-
var json;
var that = this;
@@ -82,74 +128,76 @@ public class Resource {
async: true,
type: "GET",
url: "/resources/show",
- data: {id: that.id},
+ data: {id: that.id},
dataType: 'json',
success: function(response) {
-
+
// apply fetched data
that.id = response.data.id;
that.name = response.data.name;
that.html = response.details_html;
+ $('#sidebar-content').html(response.details_html);
int numValues = 0;
- for (int i = 0 ; i < response.data.attributes.length(); i++) {
- Attribute attr = new Attribute(response.data.attributes[i].name, that);
+ for (int i = 0; i < response.data.attributes.length(); i++) {
+ Attribute attr = new Attribute(response.data.attributes[i].id, response.data.attributes[i].name, that);
+ DonutSegment seg = new DonutSegment(attr, that.donut);
for (int j = 0; j < response.data.attributes[i].values.length(); j++) {
- attr.addValue(new Value(response.data.attributes[i].values[j].id, response.data.attributes[i].values[j].name, attr));
+ Value val = attr.addValue(response.data.attributes[i].values[j].id, response.data.attributes[i].values[j].name);
+ seg.addSlice(val);
numValues++;
}
that.addAttribute(attr);
+ that.donut.addSegment(seg);
}
+ // kill this node because it's useless with no values at all
+ if (numValues==0) {
+ g.removeNode(getNodeById(that.id)); // TODO: does not work properly
+ return
+ }
+
+ // attributes are initialized based on what's in the donut
+ Attributes.init(that.donut);
+
+ // double work but necessary for now
+ Attributes.select(that.donut.selectedSegment);
+
that.numValues = numValues;
that.loading = false;
- that.radiusOuter = 40;
- that.targetRadiusOuter = 110;
- that.frameCount = 0;
- $('aside').html(g.getSelectedNode().getSubject().html);
+ that.resize.start(); // tweening
+
+ Node n = g.getNodeById(that.id);
+ Path.addNode(n);
+
},
error: function() {
that.loading = false;
+ g.removeNode(getNodeById(that.id)); // TODO: does not work properly
+ // TODO: get rid of that dead node!
}
});
}
- public boolean isTransitioning() {
- return (frameCount<tweenDuration);
- }
-
public void setPos(float x, float y) {
- this.x = x;
- this.y = y;
- }
-
- public void setAngleAdjustment(float angleAdjustment) {
- this.angleAdjustment = angleAdjustment;
- }
-
- public void setSelectedAttribute(attr) {
- this.previousSelectedAttribute = this.selectedAttribute;
- this.selectedAttribute = attr;
+ this.x = x; // TODO: do we need this.x? -> yes we do / Resource#getX() that delegates to donut.x may help
+ this.y = y;
+ this.donut.x = x;
+ this.donut.y = y;
}
- public Attribute getSelectedAttribute() {
- return this.selectedAttribute;
+ public void getSelectedAttribute() {
+ if (!this.donut.selectedSegment) return null;
+ return this.donut.selectedSegment.subject;
}
- public setRadiusOuter(float radiusOuter) {
- this.radiusOuter = radiusOuter;
+ public void getSelectedValue() {
+ if (!this.donut.selectedSlice) return null;
+ return this.donut.selectedSlice.subject;
}
- public float getRadiusOuter() {
- return this.radiusOuter;
- }
-
- public float getRadius() {
- return this.radius;
- }
-
public String getId() {
return this.id;
}
@@ -158,36 +206,28 @@ public class Resource {
return this.name;
}
- public void setFill(color c) {
- this.myColor = c;
- }
-
- public void setStroke(int weight, color c) {
- this.myStrokeWeight = weight;
- this.myStrokeColor = c;
- }
-
public void setClosed() {
this.state = "closed";
+ if (this.donut.selectedSlice)
+ this.donut.selectedSlice.hovering = false;
+ this.donut.showLabels = false;
+ this.resize.continueTo(RADIUS_CLOSED, 1);
}
public void setOpen() {
if (this.state != "open") {
this.state = "open";
- this.radiusOuter = 40;
- this.targetRadiusOuter = 110;
- this.frameCount = 0;
+ // attributes are initialized based on what's in the donut
+ Attributes.init(this.donut);
+ Attributes.select(this.donut.selectedSegment);
+ this.resize.continueTo(RADIUS_OPENED, 1);
}
}
public void setState(state) {
this.state = state;
}
- public String getId() {
- return id;
- }
-
public String getType() {
return this.type;
}
@@ -196,78 +236,13 @@ public class Resource {
this.numValues = numValues;
}
- public void update() {
- float angleStart = angleAdjustment;
- float angleStop = angleAdjustment;
-
- var selectedAttributeCount = 0;
-
- if (selectedAttribute!= null)
- selectedAttributeCount = selectedAttribute.values.size();
-
- float totalWeight = 0;
- for (int i=0; i < attributes.size(); i++) {
- totalWeight += attributes[i].weight*attributes[i].getValues().size();
- }
-
- for (int i = 0 ; i < attributes.size(); i++) {
- Attribute attribute = attributes[i];
- ArrayList values = attribute.getValues();
-
- attribute.setPos(this.x+cos(angle)*radius, this.y+sin(angle)*radius)
- attribute.setCol(colors[i%12]);
-
- float angleOuterStart = angleStop;
- float angleOuterStop = angleStop;
-
- for (int j = 0; j < values.size(); j++) {
- // outer orbit
- angleOuterStart = angleOuterStop;
-
- // calculate dependent on weight (selected attributes have more weight)
- float weight = 1;
- if (selectedAttribute!= null && selectedAttribute==attribute) {
- weight = highlightedWeight;
- }
- angleOuterStop += (attribute.weight * TWO_PI / totalWeight);
-
- values[j].setAngles(angleOuterStart, angleOuterStop);
- values[j].idx = j;
- }
-
- // calculate dimension
- percentage = values.size() / numValues;
- angleStart = angleStop;
- angleStop = angleOuterStop;
-
- attribute.setAngles(angleStart, angleStop);
- attribute.setCol(colors[i%12]);
- }
-
- if (loading)
- this.theta = (this.theta+0.07)%TWO_PI // update theta for loading animation
-
- // tweening fun
- if (isTransitioning()) {
- frameCount++;
- radiusOuter = radiusOuter + ((targetRadiusOuter - radiusOuter) * easeInOutCubic(frameCount/tweenDuration));
- }
- }
-
- void setSelected(boolean b) {
- selected = b;
- }
-
- public boolean isSelected() {
- return hovering;
- }
-
void setHovering(boolean b) {
hovering = b;
}
-
+
+ // returns true if mouse is over resource but not hovering a donut slice
public boolean isHovering() {
- return hovering;
+ return hovering && (!donut.selectedSlice || !donut.selectedSlice.hovering);
}
void setDragging(boolean b) {
@@ -281,141 +256,171 @@ public class Resource {
public void addAttribute(Attribute attr) {
attributes.add(attr);
}
-
- public ArrayList getValues() {
- ArrayList vals = new ArrayList();
- for (int i = 0; i < this.attributes.size(); i++) {
- for (int j = 0; j < this.attributes[i].values.size(); j++) {
- vals.add(this.attributes[i].values[j]);
- }
- }
- return vals;
- }
-
- public void draw() {
-
+
+ // sets the selected attribute / corresponding segment/slice by attribute id
+ public void setSelectedAttribute(String attrId) {
+ DonutSegment s;
+
+ for (int i=0; i<donut.segments.size(); i++) {
+ if (donut.segments[i].subject.id == attrId)
+ s = donut.segments[i];
+ }
+
+ if (s) {
+ donut.setSelectedSlice(s.slices[0]);
+ }
+ }
+
+ // a modulo function that behaves like that one in vvvv
+ public int vvvvmod(a,b) {
+ int res = a % b;
+ return (res>=0) ? res : 100 + res;
+ }
+
+ public int d1(int i) {
+ return vvvvmod((this.progress-(100/NUM_LOADING_SEGMENTS)*i),100);
+ }
+
+ public int d2(int i) {
+ return vvvvmod(((100/NUM_LOADING_SEGMENTS)*i-this.progress), 100);
+ }
+
+ public float alpha(int i) {
+ var a = max(-(1/25)*(d1(i))+1, -(1/25)*(d2(i))+1);
+ return max(a,0.1);
+ }
+
+ public void update() {
+ this.progress = (this.progress+3) % 100;
+ resize.tick(); // triggering the tween if active
+ donut.update();
+ }
+
+ public void draw() {
+ noStroke();
if (loading) { // draw loading
- noStroke();
- // spacer
+ // spacer
fill(255);
- ellipse(this.x, this.y, radiusClosed*2+50, radiusClosed*2+50);
-
- fill(0,0,0,0);
- stroke(111);
- strokeWeight(20);
- curContext.lineCap = "butt"; // processing.js patch!
+ ellipse(this.x, this.y, donut.radius*2+35, donut.radius*2+35);
- arc(this.x, this.y, radiusClosed*2+20, radiusClosed*2+20, 0, TWO_PI);
-
float angle = 0;
- angle += theta;
- // draw separators
- for (int i=0; i<7; i++) {
- stroke(255);
- strokeWeight(2);
- angle += TWO_PI/6;
+
+ noFill();
+ stroke(DONUT_BACKGROUND);
+
+ // segments
+ for (int i=0; i<NUM_LOADING_SEGMENTS; i++) {
+ curContext.lineCap = "butt"; // processing.js patch!
+ stroke(colors[i*2%11]);
+ curContext.globalAlpha = alpha(i);
+ strokeWeight(25);
+ float angleStart = angle;
+ angle += TWO_PI/NUM_LOADING_SEGMENTS;
+ arc(donut.x, donut.y, donut.radius*2, donut.radius*2, angleStart, angle);
+ }
+
+ curContext.globalAlpha = 0.6;
+
+ // segments
+ for (int i=0; i<NUM_LOADING_SEGMENTS; i++) {
+ angle += TWO_PI/NUM_LOADING_SEGMENTS;
- line(this.x, this.y, this.x+cos(angle)*(radiusClosed+25), this.y+sin(angle)*(radiusClosed+25));
+ stroke(255);
+ strokeWeight(1);
+ line(this.x, this.y, this.x+cos(angle)*(donut.radius+15), this.y+sin(angle)*(donut.radius+15));
}
- labelOffset = 30;
-
- } else if (state=="open") {
- noStroke();
-
- // spacer
+
+ curContext.globalAlpha = 1.0;
+
+ } else {
+ if (this === g.selectedNode.subject)
+ donut.alpha = 1.0;
+ else if (this.hovering) {
+ donut.alpha = 0.7;
+ } else {
+ donut.alpha = 0.5;
+ }
+
+ noStroke();
+ donut.draw();
+ }
+
+ if (hovering)
+ fill(0,0,0,150);
+ else
+ fill(66, 66, 66, 100);
+
+ noStroke();
+
+ curContext.textAlign = "center";
+
+ if (!loading && !(state=="open" && getSelectedValue().hovering)) {
+ pushMatrix();
+ translate(10, 0); // shift it a bit for centering
+ noStroke();
fill(255);
- ellipse(this.x, this.y, radiusOuter*2+50, radiusOuter*2+50);
-
- // draw attributes
- for (int i = 0 ; i < attributes.size(); i++) {
-
- float weight = 40;
- Attribute attribute = attributes[i];
- ArrayList values = attribute.getValues();
+ curContext.font = "16px GraublauWeb";
+
+ float fWidth = curContext.measureText(this.getName()).width+25;
+ float yOffset = donut.radius+donut.selectedSegment.relativeBreadth()/2+15;
- if (selectedAttribute==attribute) {
- weight = 50;
- fill(255);
- arc(this.x, this.y, (radiusOuter+30)*2, (radiusOuter+30)*2, attribute.angleStart, attribute.angleStop);
- }
+ // label background
+ roundedRect(this.x-fWidth/2, this.y+yOffset, fWidth, 22, 10);
+
+ if (state=="open")
+ fill(73, 71, 59);
+ else
+ fill(182,187,171);
- fill(0,0,0,0);
-
- stroke(attribute.col);
- strokeWeight(weight);
- curContext.lineCap = "butt"; // processing.js patch!
- arc(this.x, this.y, radiusOuter*2, radiusOuter*2, attribute.angleStart, attribute.angleStop);
- noStroke();
- }
-
- // draw values
- for (int i = 0 ; i < attributes.size(); i++) {
- Attribute attribute = attributes[i];
- ArrayList values = attribute.getValues();
-
- for (int j = 0; j < values.size(); j++) {
- stroke(colors[i%12]);
- strokeWeight(1);
- values[j].draw();
- }
- // draw labels
- attribute.draw();
- }
+ ellipse(this.x-fWidth/2-5, this.y+yOffset+11, 30, 30);
- labelOffset = radiusOuter;
+ // draw label
+ fill(33);
+ curContext.fillText(this.getName(), this.x+2, this.y+yOffset+15);
- } else if (state=="closed") { // draw closed
- noStroke();
- // spacer
fill(255);
- ellipse(this.x, this.y, radiusClosed*2+50, radiusClosed*2+50);
+ // draw resource index
+ curContext.font = "bold 18px Century Gothic";
+ curContext.textBaseline = "middle";
+ String idxstr = (this.idx<10) ? "0"+this.idx : this.idx;
+ curContext.fillText(idxstr, this.x-fWidth/2-5, this.y+yOffset+11);
- fill(0,0,0,0);
- stroke(222);
- strokeWeight(20);
- curContext.lineCap = "butt"; // processing.js patch!
+ noStroke();
+ curContext.textBaseline = "alphabetic";
+ popMatrix();
+ }
- arc(this.x, this.y, radiusClosed*2+20, radiusClosed*2+20, 0, TWO_PI);
-
- float angle = 0;
- // draw separators
- for (int i=0; i<7; i++) {
- stroke(255);
- strokeWeight(2);
- angle += TWO_PI/6;
- line(this.x, this.y, this.x+cos(angle)*(radiusClosed+25), this.y+sin(angle)*(radiusClosed+25));
- }
-
- labelOffset = 30;