").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.crossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abort();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:function(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))continue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.extend({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
--------------------------------------------------------------------------------
/client/cots/sigma.forceatlas2.js:
--------------------------------------------------------------------------------
1 | // Mathieu Jacomy @ Sciences Po Médialab & WebAtlas
2 | // (requires sigma.js to be loaded)
3 | sigma.forceatlas2 = sigma.forceatlas2 || {};
4 | sigma.forceatlas2.ForceAtlas2 = function(graph) {
5 | sigma.classes.Cascade.call(this);
6 | var self = this;
7 | this.graph = graph;
8 |
9 | this.p = {
10 | linLogMode: false,
11 | outboundAttractionDistribution: false,
12 | adjustSizes: false,
13 | edgeWeightInfluence: 0,
14 | scalingRatio: 1,
15 | strongGravityMode: false,
16 | gravity: 1,
17 | jitterTolerance: 1,
18 | barnesHutOptimize: false,
19 | barnesHutTheta: 1.2,
20 | speed: 1,
21 | outboundAttCompensation: 1,
22 | totalSwinging: 0,
23 | totalEffectiveTraction: 0,
24 | complexIntervals: 500,
25 | simpleIntervals: 1000
26 | };
27 |
28 | // The state tracked from one atomic "go" to another
29 | this.state = {step: 0, index: 0};
30 | this.rootRegion;
31 |
32 | // Runtime (the ForceAtlas2 itself)
33 | this.init = function() {
34 | self.state = {step: 0, index: 0};
35 |
36 | self.graph.nodes.forEach(function(n) {
37 | n.fa2 = {
38 | mass: 1 + n.degree,
39 | old_dx: 0,
40 | old_dy: 0,
41 | dx: 0,
42 | dy: 0
43 | };
44 | });
45 |
46 | return self;
47 | }
48 |
49 | this.go = function() {
50 | while (self.atomicGo()) {}
51 | }
52 |
53 | this.atomicGo = function() {
54 | var graph = self.graph;
55 | var nodes = graph.nodes;
56 | var edges = graph.edges;
57 |
58 | var cInt = self.p.complexIntervals;
59 | var sInt = self.p.simpleIntervals;
60 |
61 | switch (self.state.step) {
62 | case 0: // Pass init
63 | // Initialise layout data
64 | nodes.forEach(function(n) {
65 | if(n.fa2) {
66 | n.fa2.mass = 1 + n.degree;
67 | n.fa2.old_dx = n.fa2.dx;
68 | n.fa2.old_dy = n.fa2.dx;
69 | n.fa2.dx = 0;
70 | n.fa2.dy = 0;
71 | } else {
72 | n.fa2 = {
73 | mass: 1 + n.degree,
74 | old_dx: 0,
75 | old_dy: 0,
76 | dx: 0,
77 | dy: 0
78 | };
79 | }
80 | });
81 |
82 | // If Barnes Hut active, initialize root region
83 | if (self.p.barnesHutOptimize) {
84 | self.rootRegion = new sigma.forceatlas2.Region(nodes, 0);
85 | self.rootRegion.buildSubRegions();
86 | }
87 |
88 | // If outboundAttractionDistribution active, compensate.
89 | if (self.p.outboundAttractionDistribution) {
90 | self.p.outboundAttCompensation = 0;
91 | nodes.forEach(function(n) {
92 | self.p.outboundAttCompensation += n.fa2.mass;
93 | });
94 | self.p.outboundAttCompensation /= nodes.length;
95 | }
96 | self.state.step = 1;
97 | self.state.index = 0;
98 | return true;
99 | break;
100 |
101 | case 1: // Repulsion
102 | var Repulsion = self.ForceFactory.buildRepulsion(
103 | self.p.adjustSizes,
104 | self.p.scalingRatio
105 | );
106 |
107 | if (self.p.barnesHutOptimize) {
108 | var rootRegion = self.rootRegion;
109 |
110 | // Pass to the scope of forEach
111 | var barnesHutTheta = self.p.barnesHutTheta;
112 | var i = self.state.index;
113 | while (i < nodes.length && i < self.state.index + cInt) {
114 | var n = nodes[i++];
115 | if(n.fa2)
116 | rootRegion.applyForce(n, Repulsion, barnesHutTheta);
117 | }
118 | if (i == nodes.length) {
119 | self.state.step = 2;
120 | self.state.index = 0;
121 | } else {
122 | self.state.index = i;
123 | }
124 | } else {
125 | var i1 = self.state.index;
126 | while (i1 < nodes.length && i1 < self.state.index + cInt) {
127 | var n1 = nodes[i1++];
128 | if(n1.fa2)
129 | nodes.forEach(function(n2, i2) {
130 | if (i2 < i1 && n2.fa2) {
131 | Repulsion.apply_nn(n1, n2);
132 | }
133 | });
134 | }
135 | if (i1 == nodes.length) {
136 | self.state.step = 2;
137 | self.state.index = 0;
138 | } else {
139 | self.state.index = i1;
140 | }
141 | }
142 | return true;
143 | break;
144 |
145 | case 2: // Gravity
146 | var Gravity = (self.p.strongGravityMode) ?
147 | (self.ForceFactory.getStrongGravity(
148 | self.p.scalingRatio
149 | )) :
150 | (self.ForceFactory.buildRepulsion(
151 | self.p.adjustSizes,
152 | self.p.scalingRatio
153 | ));
154 | // Pass gravity and scalingRatio to the scope of the function
155 | var gravity = self.p.gravity,
156 | scalingRatio = self.p.scalingRatio;
157 |
158 | var i = self.state.index;
159 | while (i < nodes.length && i < self.state.index + sInt) {
160 | var n = nodes[i++];
161 | if (n.fa2)
162 | Gravity.apply_g(n, gravity / scalingRatio);
163 | }
164 |
165 | if (i == nodes.length) {
166 | self.state.step = 3;
167 | self.state.index = 0;
168 | } else {
169 | self.state.index = i;
170 | }
171 | return true;
172 | break;
173 |
174 | case 3: // Attraction
175 | var Attraction = self.ForceFactory.buildAttraction(
176 | self.p.linLogMode,
177 | self.p.outboundAttractionDistribution,
178 | self.p.adjustSizes,
179 | 1 * ((self.p.outboundAttractionDistribution) ?
180 | (self.p.outboundAttCompensation) :
181 | (1))
182 | );
183 |
184 | var i = self.state.index;
185 | if (self.p.edgeWeightInfluence == 0) {
186 | while (i < edges.length && i < self.state.index + cInt) {
187 | var e = edges[i++];
188 | Attraction.apply_nn(e.source, e.target, 1);
189 | }
190 | } else if (self.p.edgeWeightInfluence == 1) {
191 | while (i < edges.length && i < self.state.index + cInt) {
192 | var e = edges[i++];
193 | Attraction.apply_nn(e.source, e.target, e.weight || 1);
194 | }
195 | } else {
196 | while (i < edges.length && i < self.state.index + cInt) {
197 | var e = edges[i++];
198 | Attraction.apply_nn(
199 | e.source, e.target,
200 | Math.pow(e.weight || 1, self.p.edgeWeightInfluence)
201 | );
202 | }
203 | }
204 |
205 | if (i == edges.length) {
206 | self.state.step = 4;
207 | self.state.index = 0;
208 | } else {
209 | self.state.index = i;
210 | }
211 |
212 | return true;
213 | break;
214 |
215 | case 4: // Auto adjust speed
216 | var totalSwinging = 0; // How much irregular movement
217 | var totalEffectiveTraction = 0; // Hom much useful movement
218 |
219 | nodes.forEach(function(n) {
220 | var fixed = n.fixed || false;
221 | if (!fixed && n.fa2) {
222 | var swinging = Math.sqrt(Math.pow(n.fa2.old_dx - n.fa2.dx, 2) +
223 | Math.pow(n.fa2.old_dy - n.fa2.dy, 2));
224 |
225 | // If the node has a burst change of direction,
226 | // then it's not converging.
227 | totalSwinging += n.fa2.mass * swinging;
228 | totalEffectiveTraction += n.fa2.mass *
229 | 0.5 *
230 | Math.sqrt(
231 | Math.pow(n.fa2.old_dx + n.fa2.dx, 2) +
232 | Math.pow(n.fa2.old_dy + n.fa2.dy, 2)
233 | );
234 | }
235 | });
236 |
237 | self.p.totalSwinging = totalSwinging;
238 | self.p.totalEffectiveTraction = totalEffectiveTraction;
239 |
240 | // We want that swingingMovement < tolerance * convergenceMovement
241 | var targetSpeed = Math.pow(self.p.jitterTolerance, 2) *
242 | self.p.totalEffectiveTraction /
243 | self.p.totalSwinging;
244 |
245 | // But the speed shoudn't rise too much too quickly,
246 | // since it would make the convergence drop dramatically.
247 | var maxRise = 0.5; // Max rise: 50%
248 | self.p.speed = self.p.speed +
249 | Math.min(
250 | targetSpeed - self.p.speed,
251 | maxRise * self.p.speed
252 | );
253 |
254 | // Save old coordinates
255 | nodes.forEach(function(n) {
256 | n.old_x = +n.x;
257 | n.old_y = +n.y;
258 | });
259 |
260 | self.state.step = 5;
261 | return true;
262 | break;
263 |
264 | case 5: // Apply forces
265 | var i = self.state.index;
266 | if (self.p.adjustSizes) {
267 | var speed = self.p.speed;
268 | // If nodes overlap prevention is active,
269 | // it's not possible to trust the swinging mesure.
270 | while (i < nodes.length && i < self.state.index + sInt) {
271 | var n = nodes[i++];
272 | var fixed = n.fixed || false;
273 | if (!fixed && n.fa2) {
274 | // Adaptive auto-speed: the speed of each node is lowered
275 | // when the node swings.
276 | var swinging = Math.sqrt(
277 | (n.fa2.old_dx - n.fa2.dx) *
278 | (n.fa2.old_dx - n.fa2.dx) +
279 | (n.fa2.old_dy - n.fa2.dy) *
280 | (n.fa2.old_dy - n.fa2.dy)
281 | );
282 | var factor = 0.1 * speed / (1 + speed * Math.sqrt(swinging));
283 |
284 | var df = Math.sqrt(Math.pow(n.fa2.dx, 2) +
285 | Math.pow(n.fa2.dy, 2));
286 |
287 | factor = Math.min(factor * df, 10) / df;
288 |
289 | n.x += n.fa2.dx * factor;
290 | n.y += n.fa2.dy * factor;
291 | }
292 | }
293 | } else {
294 | var speed = self.p.speed;
295 | while (i < nodes.length && i < self.state.index + sInt) {
296 | var n = nodes[i++];
297 | var fixed = n.fixed || false;
298 | if (!fixed && n.fa2) {
299 | // Adaptive auto-speed: the speed of each node is lowered
300 | // when the node swings.
301 | var swinging = Math.sqrt(
302 | (n.fa2.old_dx - n.fa2.dx) *
303 | (n.fa2.old_dx - n.fa2.dx) +
304 | (n.fa2.old_dy - n.fa2.dy) *
305 | (n.fa2.old_dy - n.fa2.dy)
306 | );
307 | var factor = speed / (1 + speed * Math.sqrt(swinging));
308 |
309 | n.x += n.fa2.dx * factor;
310 | n.y += n.fa2.dy * factor;
311 | }
312 | }
313 | }
314 |
315 | if (i == nodes.length) {
316 | self.state.step = 0;
317 | self.state.index = 0;
318 | return false;
319 | } else {
320 | self.state.index = i;
321 | return true;
322 | }
323 | break;
324 |
325 | default:
326 | throw new Error('ForceAtlas2 - atomic state error');
327 | break;
328 | }
329 | }
330 |
331 | this.end = function() {
332 | this.graph.nodes.forEach(function(n) {
333 | n.fa2 = null;
334 | });
335 | }
336 |
337 | // Auto Settings
338 | this.setAutoSettings = function() {
339 | var graph = this.graph;
340 |
341 | // Tuning
342 | if (graph.nodes.length >= 100) {
343 | this.p.scalingRatio = 2.0;
344 | } else {
345 | this.p.scalingRatio = 10.0;
346 | }
347 | this.p.strongGravityMode = false;
348 | this.p.gravity = 1;
349 |
350 | // Behavior
351 | this.p.outboundAttractionDistribution = false;
352 | this.p.linLogMode = false;
353 | this.p.adjustSizes = false;
354 | this.p.edgeWeightInfluence = 1;
355 |
356 | // Performance
357 | if (graph.nodes.length >= 50000) {
358 | this.p.jitterTolerance = 10;
359 | } else if (graph.nodes.length >= 5000) {
360 | this.p.jitterTolerance = 1;
361 | } else {
362 | this.p.jitterTolerance = 0.1;
363 | }
364 | if (graph.nodes.length >= 1000) {
365 | this.p.barnesHutOptimize = true;
366 | } else {
367 | this.p.barnesHutOptimize = false;
368 | }
369 | this.p.barnesHutTheta = 1.2;
370 |
371 | return this;
372 | }
373 |
374 | // All the different forces
375 | this.ForceFactory = {
376 | buildRepulsion: function(adjustBySize, coefficient) {
377 | if (adjustBySize) {
378 | return new this.linRepulsion_antiCollision(coefficient);
379 | } else {
380 | return new this.linRepulsion(coefficient);
381 | }
382 | },
383 |
384 | getStrongGravity: function(coefficient) {
385 | return new this.strongGravity(coefficient);
386 | },
387 |
388 | buildAttraction: function(logAttr, distributedAttr, adjustBySize, c) {
389 | if (adjustBySize) {
390 | if (logAttr) {
391 | if (distributedAttr) {
392 | return new this.logAttraction_degreeDistributed_antiCollision(c);
393 | } else {
394 | return new this.logAttraction_antiCollision(c);
395 | }
396 | } else {
397 | if (distributedAttr) {
398 | return new this.linAttraction_degreeDistributed_antiCollision(c);
399 | } else {
400 | return new this.linAttraction_antiCollision(c);
401 | }
402 | }
403 | } else {
404 | if (logAttr) {
405 | if (distributedAttr) {
406 | return new this.logAttraction_degreeDistributed(c);
407 | } else {
408 | return new this.logAttraction(c);
409 | }
410 | } else {
411 | if (distributedAttr) {
412 | return new this.linAttraction_massDistributed(c);
413 | } else {
414 | return new this.linAttraction(c);
415 | }
416 | }
417 | }
418 | },
419 |
420 | // Repulsion force: Linear
421 | linRepulsion: function(c) {
422 | this.coefficient = c;
423 | this.apply_nn = function(n1, n2) {
424 | if(n1.fa2 && n2.fa2)
425 | {
426 | // Get the distance
427 | var xDist = n1.x - n2.x;
428 | var yDist = n1.y - n2.y;
429 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
430 |
431 | if (distance > 0) {
432 | // NB: factor = force / distance
433 | var factor = this.coefficient *
434 | n1.fa2.mass *
435 | n2.fa2.mass /
436 | Math.pow(distance, 2);
437 |
438 | n1.fa2.dx += xDist * factor;
439 | n1.fa2.dy += yDist * factor;
440 |
441 | n2.fa2.dx -= xDist * factor;
442 | n2.fa2.dy -= yDist * factor;
443 | }
444 | }
445 | }
446 |
447 | this.apply_nr = function(n, r) {
448 | // Get the distance
449 | var xDist = n.x - r.config('massCenterX');
450 | var yDist = n.y - r.config('massCenterY');
451 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
452 |
453 | if (distance > 0) {
454 | // NB: factor = force / distance
455 | var factor = this.coefficient *
456 | n.fa2.mass *
457 | r.config('mass') /
458 | Math.pow(distance, 2);
459 |
460 | n.fa2.dx += xDist * factor;
461 | n.fa2.dy += yDist * factor;
462 | }
463 | }
464 |
465 | this.apply_g = function(n, g) {
466 | // Get the distance
467 | var xDist = n.x;
468 | var yDist = n.y;
469 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
470 |
471 | if (distance > 0) {
472 | // NB: factor = force / distance
473 | var factor = this.coefficient * n.fa2.mass * g / distance;
474 |
475 | n.fa2.dx -= xDist * factor;
476 | n.fa2.dy -= yDist * factor;
477 | }
478 | }
479 | },
480 |
481 | linRepulsion_antiCollision: function(c) {
482 | this.coefficient = c;
483 | this.apply_nn = function(n1, n2) {
484 | if(n1.fa2 && n2.fa2)
485 | {
486 | // Get the distance
487 | var xDist = n1.x - n2.x;
488 | var yDist = n1.y - n2.y;
489 | var distance = Math.sqrt(xDist * xDist + yDist * yDist) -
490 | n1.size -
491 | n2.size;
492 |
493 | if (distance > 0) {
494 | // NB: factor = force / distance
495 | var factor = this.coefficient *
496 | n1.fa2.mass *
497 | n2.fa2.mass /
498 | Math.pow(distance, 2);
499 |
500 | n1.fa2.dx += xDist * factor;
501 | n1.fa2.dy += yDist * factor;
502 |
503 | n2.fa2.dx -= xDist * factor;
504 | n2.fa2.dy -= yDist * factor;
505 |
506 | } else if (distance < 0) {
507 | var factor = 100 * this.coefficient * n1.fa2.mass * n2.fa2.mass;
508 |
509 | n1.fa2.dx += xDist * factor;
510 | n1.fa2.dy += yDist * factor;
511 |
512 | n2.fa2.dx -= xDist * factor;
513 | n2.fa2.dy -= yDist * factor;
514 | }
515 | }
516 | }
517 |
518 | this.apply_nr = function(n, r) {
519 | // Get the distance
520 | var xDist = n.fa2.x() - r.getMassCenterX();
521 | var yDist = n.fa2.y() - r.getMassCenterY();
522 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
523 |
524 | if (distance > 0) {
525 | // NB: factor = force / distance
526 | var factor = this.coefficient *
527 | n.fa2.mass *
528 | r.getMass() /
529 | Math.pow(distance, 2);
530 |
531 | n.fa2.dx += xDist * factor;
532 | n.fa2.dy += yDist * factor;
533 | } else if (distance < 0) {
534 | var factor = -this.coefficient * n.fa2.mass * r.getMass() / distance;
535 |
536 | n.fa2.dx += xDist * factor;
537 | n.fa2.dy += yDist * factor;
538 | }
539 | }
540 |
541 | this.apply_g = function(n, g) {
542 | // Get the distance
543 | var xDist = n.x;
544 | var yDist = n.y;
545 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
546 |
547 | if (distance > 0) {
548 | // NB: factor = force / distance
549 | var factor = this.coefficient * n.fa2.mass * g / distance;
550 |
551 | n.fa2.dx -= xDist * factor;
552 | n.fa2.dy -= yDist * factor;
553 | }
554 | }
555 | },
556 |
557 | // Repulsion force: Strong Gravity
558 | // (as a Repulsion Force because it is easier)
559 | strongGravity: function(c) {
560 | this.coefficient = c;
561 | this.apply_nn = function(n1, n2) {
562 | // Not Relevant
563 | }
564 | this.apply_nr = function(n, r) {
565 | // Not Relevant
566 | }
567 |
568 | this.apply_g = function(n, g) {
569 | // Get the distance
570 | var xDist = n.x;
571 | var yDist = n.y;
572 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
573 |
574 | if (distance > 0) {
575 | // NB: factor = force / distance
576 | var factor = this.coefficient * n.fa2.mass * g;
577 |
578 | n.fa2.dx -= xDist * factor;
579 | n.fa2.dy -= yDist * factor;
580 | }
581 | }
582 | },
583 |
584 | // Attraction force: Linear
585 | linAttraction: function(c) {
586 | this.coefficient = c;
587 |
588 | this.apply_nn = function(n1, n2, e) {
589 | if(n1.fa2 && n2.fa2)
590 | {
591 | // Get the distance
592 | var xDist = n1.x - n2.x;
593 | var yDist = n1.y - n2.y;
594 |
595 | // NB: factor = force / distance
596 | var factor = -this.coefficient * e;
597 |
598 | n1.fa2.dx += xDist * factor;
599 | n1.fa2.dy += yDist * factor;
600 |
601 | n2.fa2.dx -= xDist * factor;
602 | n2.fa2.dy -= yDist * factor;
603 | }
604 | }
605 | },
606 |
607 |
608 | // Attraction force: Linear, distributed by mass (typically, degree)
609 | linAttraction_massDistributed: function(c) {
610 | this.coefficient = c;
611 |
612 | this.apply_nn = function(n1, n2, e) {
613 | if(n1.fa2 && n2.fa2)
614 | {
615 | // Get the distance
616 | var xDist = n1.x - n2.x;
617 | var yDist = n1.y - n2.y;
618 |
619 | // NB: factor = force / distance
620 | var factor = -this.coefficient * e / n1.fa2.mass;
621 |
622 | n1.fa2.dx += xDist * factor;
623 | n1.fa2.dy += yDist * factor;
624 |
625 | n2.fa2.dx -= xDist * factor;
626 | n2.fa2.dy -= yDist * factor;
627 | }
628 | }
629 | },
630 |
631 |
632 | // Attraction force: Logarithmic
633 | logAttraction: function(c) {
634 | this.coefficient = c;
635 |
636 | this.apply_nn = function(n1, n2, e) {
637 | if(n1.fa2 && n2.fa2)
638 | {
639 | // Get the distance
640 | var xDist = n1.x - n2.x;
641 | var yDist = n1.y - n2.y;
642 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
643 |
644 | if (distance > 0) {
645 | // NB: factor = force / distance
646 | var factor = -this.coefficient *
647 | e *
648 | Math.log(1 + distance) /
649 | distance;
650 |
651 | n1.fa2.dx += xDist * factor;
652 | n1.fa2.dy += yDist * factor;
653 |
654 | n2.fa2.dx -= xDist * factor;
655 | n2.fa2.dy -= yDist * factor;
656 | }
657 | }
658 | }
659 | },
660 |
661 |
662 | // Attraction force: Linear, distributed by Degree
663 | logAttraction_degreeDistributed: function(c) {
664 | this.coefficient = c;
665 |
666 | this.apply_nn = function(n1, n2, e) {
667 | if(n1.fa2 && n2.fa2)
668 | {
669 | // Get the distance
670 | var xDist = n1.x - n2.x;
671 | var yDist = n1.y - n2.y;
672 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
673 |
674 | if (distance > 0) {
675 | // NB: factor = force / distance
676 | var factor = -this.coefficient *
677 | e *
678 | Math.log(1 + distance) /
679 | distance /
680 | n1.fa2.mass;
681 |
682 | n1.fa2.dx += xDist * factor;
683 | n1.fa2.dy += yDist * factor;
684 |
685 | n2.fa2.dx -= xDist * factor;
686 | n2.fa2.dy -= yDist * factor;
687 | }
688 | }
689 | }
690 | },
691 |
692 |
693 | // Attraction force: Linear, with Anti-Collision
694 | linAttraction_antiCollision: function(c) {
695 | this.coefficient = c;
696 |
697 | this.apply_nn = function(n1, n2, e) {
698 | if(n1.fa2 && n2.fa2)
699 | {
700 | // Get the distance
701 | var xDist = n1.x - n2.x;
702 | var yDist = n1.y - n2.y;
703 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
704 |
705 | if (distance > 0) {
706 | // NB: factor = force / distance
707 | var factor = -this.coefficient * e;
708 |
709 | n1.fa2.dx += xDist * factor;
710 | n1.fa2.dy += yDist * factor;
711 |
712 | n2.fa2.dx -= xDist * factor;
713 | n2.fa2.dy -= yDist * factor;
714 | }
715 | }
716 | }
717 | },
718 |
719 |
720 | // Attraction force: Linear, distributed by Degree, with Anti-Collision
721 | linAttraction_degreeDistributed_antiCollision: function(c) {
722 | this.coefficient = c;
723 |
724 | this.apply_nn = function(n1, n2, e) {
725 | if(n1.fa2 && n2.fa2)
726 | {
727 | // Get the distance
728 | var xDist = n1.x - n2.x;
729 | var yDist = n1.y - n2.y;
730 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
731 |
732 | if (distance > 0) {
733 | // NB: factor = force / distance
734 | var factor = -this.coefficient * e / n1.fa2.mass;
735 |
736 | n1.fa2.dx += xDist * factor;
737 | n1.fa2.dy += yDist * factor;
738 |
739 | n2.fa2.dx -= xDist * factor;
740 | n2.fa2.dy -= yDist * factor;
741 | }
742 | }
743 | }
744 | },
745 |
746 |
747 | // Attraction force: Logarithmic, with Anti-Collision
748 | logAttraction_antiCollision: function(c) {
749 | this.coefficient = c;
750 |
751 | this.apply_nn = function(n1, n2, e) {
752 | if(n1.fa2 && n2.fa2)
753 | {
754 | // Get the distance
755 | var xDist = n1.x - n2.x;
756 | var yDist = n1.y - n2.y;
757 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
758 |
759 | if (distance > 0) {
760 | // NB: factor = force / distance
761 | var factor = -this.coefficient *
762 | e *
763 | Math.log(1 + distance) /
764 | distance;
765 |
766 | n1.fa2.dx += xDist * factor;
767 | n1.fa2.dy += yDist * factor;
768 |
769 | n2.fa2.dx -= xDist * factor;
770 | n2.fa2.dy -= yDist * factor;
771 | }
772 | }
773 | }
774 | },
775 |
776 | // Attraction force: Linear, distributed by Degree, with Anti-Collision
777 | logAttraction_degreeDistributed_antiCollision: function(c) {
778 | this.coefficient = c;
779 |
780 | this.apply_nn = function(n1, n2, e) {
781 | if(n1.fa2 && n2.fa2)
782 | {
783 | // Get the distance
784 | var xDist = n1.x - n2.x;
785 | var yDist = n1.y - n2.y;
786 | var distance = Math.sqrt(xDist * xDist + yDist * yDist);
787 |
788 | if (distance > 0) {
789 | // NB: factor = force / distance
790 | var factor = -this.coefficient *
791 | e *
792 | Math.log(1 + distance) /
793 | distance /
794 | n1.fa2.mass;
795 |
796 | n1.fa2.dx += xDist * factor;
797 | n1.fa2.dy += yDist * factor;
798 |
799 | n2.fa2.dx -= xDist * factor;
800 | n2.fa2.dy -= yDist * factor;
801 | }
802 | }
803 | }
804 | }
805 | };
806 | };
807 |
808 | // The Region class, as used by the Barnes Hut optimization
809 | sigma.forceatlas2.Region = function(nodes, depth) {
810 | sigma.classes.Cascade.call(this);
811 | this.depthLimit = 20;
812 | this.size = 0;
813 | this.nodes = nodes;
814 | this.subregions = [];
815 | this.depth = depth;
816 |
817 | this.p = {
818 | mass: 0,
819 | massCenterX: 0,
820 | massCenterY: 0
821 | };
822 |
823 | this.updateMassAndGeometry();
824 | }
825 |
826 | sigma.forceatlas2.Region.prototype.updateMassAndGeometry = function() {
827 | if (this.nodes.length > 1) {
828 | // Compute Mass
829 | var mass = 0;
830 | var massSumX = 0;
831 | var massSumY = 0;
832 | this.nodes.forEach(function(n) {
833 | mass += n.fa2.mass;
834 | massSumX += n.x * n.fa2.mass;
835 | massSumY += n.y * n.fa2.mass;
836 | });
837 | var massCenterX = massSumX / mass;
838 | massCenterY = massSumY / mass;
839 |
840 | // Compute size
841 | var size;
842 | this.nodes.forEach(function(n) {
843 | var distance = Math.sqrt(
844 | (n.x - massCenterX) *
845 | (n.x - massCenterX) +
846 | (n.y - massCenterY) *
847 | (n.y - massCenterY)
848 | );
849 | size = Math.max(size || (2 * distance), 2 * distance);
850 | });
851 |
852 | this.p.mass = mass;
853 | this.p.massCenterX = massCenterX;
854 | this.p.massCenterY = massCenterY;
855 | this.size = size;
856 | }
857 | };
858 |
859 |
860 | sigma.forceatlas2.Region.prototype.buildSubRegions = function() {
861 | if (this.nodes.length > 1) {
862 | var leftNodes = [];
863 | var rightNodes = [];
864 | var subregions = [];
865 | var massCenterX = this.p.massCenterX;
866 | var massCenterY = this.p.massCenterY;
867 | var nextDepth = this.depth + 1;
868 |
869 | var self = this;
870 |
871 | this.nodes.forEach(function(n) {
872 | var nodesColumn = (n.x < massCenterX) ? (leftNodes) : (rightNodes);
873 | nodesColumn.push(n);
874 | });
875 |
876 | var tl = [], bl = [], br = [], tr = [];
877 |
878 | leftNodes.forEach(function(n) {
879 | var nodesLine = (n.y < massCenterY) ? (tl) : (bl);
880 | nodesLine.push(n);
881 | });
882 |
883 | rightNodes.forEach(function(n) {
884 | var nodesLine = (n.y < massCenterY) ? (tr) : (br);
885 | nodesLine.push(n);
886 | });
887 |
888 | [tl, bl, br, tr].filter(function(a) {
889 | return a.length;
890 | }).forEach(function(a) {
891 | if (nextDepth <= self.depthLimit && a.length < self.nodes.length) {
892 | var subregion = new sigma.forceatlas2.Region(a, nextDepth);
893 | subregions.push(subregion);
894 | } else {
895 | a.forEach(function(n) {
896 | var oneNodeList = [n];
897 | var subregion = new sigma.forceatlas2.Region(oneNodeList, nextDepth);
898 | subregions.push(subregion);
899 | });
900 | }
901 | });
902 |
903 | this.subregions = subregions;
904 |
905 | subregions.forEach(function(subregion) {
906 | subregion.buildSubRegions();
907 | });
908 | }
909 | };
910 |
911 | sigma.forceatlas2.Region.prototype.applyForce = function(n, Force, theta) {
912 | if (this.nodes.length < 2) {
913 | var regionNode = this.nodes[0];
914 | Force.apply_nn(n, regionNode);
915 | } else {
916 | var distance = Math.sqrt(
917 | (n.x - this.p.massCenterX) *
918 | (n.x - this.p.massCenterX) +
919 | (n.y - this.p.massCenterY) *
920 | (n.y - this.p.massCenterY)
921 | );
922 |
923 | if (distance * theta > this.size) {
924 | Force.apply_nr(n, this);
925 | } else {
926 | this.subregions.forEach(function(subregion) {
927 | subregion.applyForce(n, Force, theta);
928 | });
929 | }
930 | }
931 | };
932 |
933 | sigma.publicPrototype.startForceAtlas2 = function() {
934 | //if(!this.forceatlas2) {
935 | this.forceatlas2 = new sigma.forceatlas2.ForceAtlas2(this._core.graph);
936 | this.forceatlas2.setAutoSettings();
937 | this.forceatlas2.init();
938 | //}
939 |
940 | this.addGenerator('forceatlas2', this.forceatlas2.atomicGo, function(){
941 | return true;
942 | });
943 | };
944 |
945 | sigma.publicPrototype.stopForceAtlas2 = function() {
946 | this.removeGenerator('forceatlas2');
947 | };
--------------------------------------------------------------------------------
/client/cots/sigma.min.js:
--------------------------------------------------------------------------------
1 | /* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author: Alexis Jacomy - License: MIT */
2 | var sigma={tools:{},classes:{},instances:{}};
3 | (function(){if(!Array.prototype.some)Array.prototype.some=function(i,m){var f=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j
GLOBAL :";for(var b in d.p.globalProbes)a+=""+b+" : "+
21 | d.p.globalProbes[b]()+"
";a+="
LOCAL :
";for(b in d.p.localProbes)a+=""+b+" : "+d.p.localProbes[b]()+"
";d.p.dom.innerHTML=a;return d}sigma.classes.Cascade.call(this);var d=this;this.instance=b;this.monitoring=!1;this.p={fps:40,dom:l,globalProbes:{"Time (ms)":sigma.chronos.getExecutionTime,Queue:sigma.chronos.getQueuedTasksCount,Tasks:sigma.chronos.getTasksCount,FPS:sigma.chronos.getFPS},localProbes:{"Nodes count":function(){return d.instance.graph.nodes.length},"Edges count":function(){return d.instance.graph.edges.length}}};
22 | this.activate=function(){if(!d.monitoring)d.monitoring=window.setInterval(g,1E3/d.p.fps);return d};this.desactivate=function(){if(d.monitoring)window.clearInterval(d.monitoring),d.monitoring=null,d.p.dom.innerHTML="";return d}}function j(b){function l(b){if(a.p.mouseEnabled&&(g(a.mouseX,a.mouseY,a.ratio*(0<(void 0!=b.wheelDelta&&b.wheelDelta||void 0!=b.detail&&-b.detail)?a.p.zoomMultiply:1/a.p.zoomMultiply)),a.p.blockScroll))b.preventDefault?b.preventDefault():b.returnValue=!1}function g(b,c,g){if(!a.isMouseDown&&
23 | (window.clearInterval(a.interpolationID),w=void 0!=g,i=a.stageX,j=b,o=a.stageY,k=c,h=g||a.ratio,h=Math.min(Math.max(h,a.p.minRatio),a.p.maxRatio),m=a.p.directZooming?1-(w?a.p.zoomDelta:a.p.dragDelta):0,a.ratio!=h||a.stageX!=j||a.stageY!=k))d(),a.interpolationID=window.setInterval(d,50),a.dispatch("startinterpolate")}function d(){m+=w?a.p.zoomDelta:a.p.dragDelta;m=Math.min(m,1);var b=sigma.easing.quadratic.easeout(m),c=a.ratio;a.ratio=c*(1-b)+h*b;w?(a.stageX=j+(a.stageX-j)*a.ratio/c,a.stageY=k+(a.stageY-
24 | k)*a.ratio/c):(a.stageX=i*(1-b)+j*b,a.stageY=o*(1-b)+k*b);a.dispatch("interpolate");if(1<=m)window.clearInterval(a.interpolationID),b=a.ratio,w?(a.ratio=h,a.stageX=j+(a.stageX-j)*a.ratio/b,a.stageY=k+(a.stageY-k)*a.ratio/b):(a.stageX=j,a.stageY=k),a.dispatch("stopinterpolate")}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var a=this;this.p={minRatio:1,maxRatio:32,marginRatio:1,zoomDelta:0.1,dragDelta:0.3,zoomMultiply:2,directZooming:!1,blockScroll:!0,inertia:1.1,mouseEnabled:!0};
25 | var f=0,c=0,i=0,o=0,h=1,j=0,k=0,s=0,p=0,C=0,n=0,m=0,w=!1;this.stageY=this.stageX=0;this.ratio=1;this.mouseY=this.mouseX=0;this.isMouseDown=!1;b.addEventListener("DOMMouseScroll",l,!0);b.addEventListener("mousewheel",l,!0);b.addEventListener("mousemove",function(b){a.mouseX=void 0!=b.offsetX&&b.offsetX||void 0!=b.layerX&&b.layerX||void 0!=b.clientX&&b.clientX;a.mouseY=void 0!=b.offsetY&&b.offsetY||void 0!=b.layerY&&b.layerY||void 0!=b.clientY&&b.clientY;if(a.isMouseDown){var d=a.mouseX-f+i,h=a.mouseY-
26 | c+o;if(d!=a.stageX||h!=a.stageY)p=s,n=C,s=d,C=h,a.stageX=d,a.stageY=h,a.dispatch("drag")}a.dispatch("move");b.preventDefault?b.preventDefault():b.returnValue=!1},!0);b.addEventListener("mousedown",function(b){if(a.p.mouseEnabled)a.isMouseDown=!0,a.dispatch("mousedown"),i=a.stageX,o=a.stageY,f=a.mouseX,c=a.mouseY,p=s=a.stageX,n=C=a.stageY,a.dispatch("startdrag"),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);document.addEventListener("mouseup",function(b){if(a.p.mouseEnabled&&a.isMouseDown)a.isMouseDown=
27 | !1,a.dispatch("mouseup"),(i!=a.stageX||o!=a.stageY)&&g(a.stageX+a.p.inertia*(a.stageX-p),a.stageY+a.p.inertia*(a.stageY-n)),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);this.checkBorders=function(){return a};this.interpolate=g}function n(b,l,g,d,a,f,c){function i(a){var b=d,c="fixed"==h.p.labelSize?h.p.defaultLabelSize:h.p.labelSizeRatio*a.displaySize;b.font=(h.p.hoverFontStyle||h.p.fontStyle||"")+" "+c+"px "+(h.p.hoverFont||h.p.font||"");b.fillStyle="node"==h.p.labelHoverBGColor?a.color||
28 | h.p.defaultNodeColor:h.p.defaultHoverLabelBGColor;b.beginPath();if(h.p.labelHoverShadow)b.shadowOffsetX=0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=h.p.labelHoverShadowColor;sigma.tools.drawRoundRect(b,Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==h.p.nodeBorderColor?a.color||
29 | h.p.defaultNodeColor:h.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize+h.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==h.p.nodeHoverColor?a.color||h.p.defaultNodeColor:h.p.defaultNodeHoverColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==h.p.labelHoverColor?a.color||h.p.defaultNodeColor:h.p.defaultLabelHoverColor;b.fillText(a.label,Math.round(a.displayX+
30 | 1.5*a.displaySize),Math.round(a.displayY+c/2-3));return h}function o(a){if(isNaN(a.x)||isNaN(a.y))throw Error("A node's coordinate is not a number (id: "+a.id+")");return!a.hidden&&a.displayX+a.displaySize>-j/3&&a.displayX-a.displaySize<4*j/3&&a.displayY+a.displaySize>-k/3&&a.displayY-a.displaySize<4*k/3}sigma.classes.Cascade.call(this);var h=this;this.p={labelColor:"default",defaultLabelColor:"#000",labelHoverBGColor:"default",defaultHoverLabelBGColor:"#fff",labelHoverShadow:!0,labelHoverShadowColor:"#000",
31 | labelHoverColor:"default",defaultLabelHoverColor:"#000",labelActiveBGColor:"default",defaultActiveLabelBGColor:"#fff",labelActiveShadow:!0,labelActiveShadowColor:"#000",labelActiveColor:"default",defaultLabelActiveColor:"#000",labelSize:"fixed",defaultLabelSize:12,labelSizeRatio:2,labelThreshold:6,font:"Arial",hoverFont:"",activeFont:"",fontStyle:"",hoverFontStyle:"",activeFontStyle:"",edgeColor:"source",defaultEdgeColor:"#aaa",defaultEdgeType:"line",defaultNodeColor:"#aaa",nodeHoverColor:"node",
32 | defaultNodeHoverColor:"#fff",nodeActiveColor:"node",defaultNodeActiveColor:"#fff",borderSize:0,nodeBorderColor:"node",defaultNodeBorderColor:"#fff",edgesSpeed:200,nodesSpeed:200,labelsSpeed:200};var j=f,k=c;this.currentLabelIndex=this.currentNodeIndex=this.currentEdgeIndex=0;this.task_drawLabel=function(){for(var b=a.nodes.length,c=0;c++=h.p.labelThreshold||
33 | d.forceLabel){var f="fixed"==h.p.labelSize?h.p.defaultLabelSize:h.p.labelSizeRatio*d.displaySize;l.font=h.p.fontStyle+f+"px "+h.p.font;l.fillStyle="node"==h.p.labelColor?d.color||h.p.defaultNodeColor:h.p.defaultLabelColor;l.fillText(d.label,Math.round(d.displayX+1.5*d.displaySize),Math.round(d.displayY+f/2-3))}}else h.currentLabelIndex++;return h.currentLabelIndex(b*=2)?0.5*b*b:-0.5*(--b*(b-2)-1)};sigma.tools.drawRoundRect=function(b,f,g,d,a,i,c){var i=i?i:0,j=c?c:[],j="string"==typeof j?j.split(" "):j,c=i&&(0<=j.indexOf("topleft")||0<=j.indexOf("top")||0<=j.indexOf("left")),m=i&&(0<=j.indexOf("topright")||0<=j.indexOf("top")||0<=j.indexOf("right")),
61 | h=i&&(0<=j.indexOf("bottomleft")||0<=j.indexOf("bottom")||0<=j.indexOf("left")),j=i&&(0<=j.indexOf("bottomright")||0<=j.indexOf("bottom")||0<=j.indexOf("right"));b.moveTo(f,g+i);c?b.arcTo(f,g,f+i,g,i):b.lineTo(f,g);m?(b.lineTo(f+d-i,g),b.arcTo(f+d,g,f+d,g+i,i)):b.lineTo(f+d,g);j?(b.lineTo(f+d,g+a-i),b.arcTo(f+d,g+a,f+d-i,g+a,i)):b.lineTo(f+d,g+a);h?(b.lineTo(f+i,g+a),b.arcTo(f,g+a,f,g+a-i,i)):b.lineTo(f,g+a);b.lineTo(f,g+i)};sigma.tools.getRGB=function(b,f){var b=b.toString(),g={r:0,g:0,b:0};if(3<=
62 | b.length&&"#"==b.charAt(0)){var d=b.length-1;6==d?g={r:parseInt(b.charAt(1)+b.charAt(2),16),g:parseInt(b.charAt(3)+b.charAt(4),16),b:parseInt(b.charAt(5)+b.charAt(5),16)}:3==d&&(g={r:parseInt(b.charAt(1)+b.charAt(1),16),g:parseInt(b.charAt(2)+b.charAt(2),16),b:parseInt(b.charAt(3)+b.charAt(3),16)})}f&&(g=[g.r,g.g,g.b]);return g};sigma.tools.rgbToHex=function(b,f,g){return sigma.tools.toHex(b)+sigma.tools.toHex(f)+sigma.tools.toHex(g)};sigma.tools.toHex=function(b){b=parseInt(b,10);if(isNaN(b))return"00";
63 | b=Math.max(0,Math.min(b,255));return"0123456789ABCDEF".charAt((b-b%16)/16)+"0123456789ABCDEF".charAt(b%16)};sigma.publicPrototype=r.prototype})();
--------------------------------------------------------------------------------
/client/cots/sigma.parseJson.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjbriody/linkedin-neo4j/84186b1a3eacff6855853e905be8a7ade4dcb6ce/client/cots/sigma.parseJson.js
--------------------------------------------------------------------------------
/client/css/neo_sigma.css:
--------------------------------------------------------------------------------
1 | /* sigma.js context : */
2 | .sigma-parent {
3 | position: relative;
4 | border-radius: 4px;
5 | -moz-border-radius: 4px;
6 | -webkit-border-radius: 4px;
7 | background: #222;
8 | height: 800px;
9 | }
10 | .sigma-expand {
11 | position: absolute;
12 | width: 100%;
13 | height: 100%;
14 | top: 0;
15 | left: 0;
16 | color: #333;
17 | }
18 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | linkedin-neo4j
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/client/neo_sigma.js:
--------------------------------------------------------------------------------
1 | function init() {
2 | // Instanciate sigma.js and customize rendering :
3 | var sigInst = sigma.init(document.getElementById('sigma-example')).drawingProperties({
4 | defaultLabelColor: '#fff',
5 | defaultLabelSize: 14,
6 | defaultLabelBGColor: '#fff',
7 | defaultLabelHoverColor: '#000',
8 | labelThreshold: 6,
9 | defaultEdgeType: 'curve'
10 | }).graphProperties({
11 | minNodeSize: 0.5,
12 | maxNodeSize: 5,
13 | minEdgeSize: 1,
14 | maxEdgeSize: 1
15 | }).mouseProperties({
16 | maxRatio: 4
17 | });
18 |
19 | sigInst.parseJson('http://localhost:8080/', function (){
20 | // Bind events :
21 | sigInst.bind('overnodes',function(event){
22 | var nodes = event.content;
23 | var neighbors = {};
24 | sigInst.iterEdges(function(e){
25 | if(nodes.indexOf(e.source)>=0 || nodes.indexOf(e.target)>=0){
26 | neighbors[e.source] = 1;
27 | neighbors[e.target] = 1;
28 | }
29 | }).iterNodes(function(n){
30 | if(!neighbors[n.id]){
31 | n.hidden = 1;
32 | }else{
33 | n.hidden = 0;
34 | }
35 | }).draw(2,2,2);
36 | }).bind('outnodes',function(){
37 | sigInst.iterEdges(function(e){
38 | e.hidden = 0;
39 | }).iterNodes(function(n){
40 | n.hidden = 0;
41 | }).draw(2,2,2);
42 | });
43 |
44 | // Draw the graph :
45 | sigInst.draw();
46 | sigInst.startForceAtlas2();
47 |
48 | var isRunning = true;
49 | document.getElementById('stop-layout').addEventListener('click',function(){
50 | if(isRunning){
51 | isRunning = false;
52 | sigInst.stopForceAtlas2();
53 | document.getElementById('stop-layout').childNodes[0].nodeValue = 'Start Layout';
54 | }else{
55 | isRunning = true;
56 | sigInst.startForceAtlas2();
57 | document.getElementById('stop-layout').childNodes[0].nodeValue = 'Stop Layout';
58 | }
59 | },true);
60 | document.getElementById('rescale-graph').addEventListener('click',function(){
61 | sigInst.position(0,0,1).draw();
62 | },true);
63 |
64 | });
65 | }
66 |
67 | if (document.addEventListener) {
68 | document.addEventListener("DOMContentLoaded", init, false);
69 | } else {
70 | window.onload = init;
71 | }
72 |
--------------------------------------------------------------------------------
/load/load_neo4j.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # setup steps:
4 | # sudo easy_install py2neo
5 | # sudo easy_install oauth2
6 | # http://book.py2neo.org/en/latest/index.html#
7 | # https://github.com/simplegeo/python-oauth2
8 | # If you don't have easy_install (ubuntu users): sudo apt-get install python-setuptools
9 |
10 | import sys
11 | import oauth2 as oauth
12 | import simplejson
13 | import codecs
14 | import urlparse
15 | from py2neo import neo4j, cypher
16 |
17 | PERSON = "person"
18 | LINKEDIN = "linkedin"
19 |
20 | if len(sys.argv) < 4:
21 | print "Usage: All fields are required.\n\t load consumer_key consumer_secret oauth_token oauth_token_secret"
22 | sys.exit(1)
23 |
24 | KEY = sys.argv[1]
25 | SECRET = sys.argv[2]
26 | OAUTH_TOKEN = sys.argv[3]
27 | OAUTH_TOKEN_SECRET = sys.argv[4]
28 |
29 | nodes = dict()
30 |
31 | if __name__ == '__main__':
32 |
33 | # configure oauth
34 | consumer = oauth.Consumer(key=KEY, secret=SECRET)
35 | token = oauth.Token(key=OAUTH_TOKEN, secret=OAUTH_TOKEN_SECRET)
36 | client = oauth.Client(consumer, token)
37 |
38 | resp, content = client.request('http://api.linkedin.com/v1/people/~?format=json')
39 | subjectProfile = simplejson.loads(content)
40 |
41 | # get connections
42 | resp, content = client.request('http://api.linkedin.com/v1/people/~/connections?format=json')
43 | results = simplejson.loads(content)
44 |
45 | # Attach to the graph db instance
46 | graph_db = neo4j.GraphDatabaseService("http://localhost:7474/db/data/")
47 | graph_db.get_or_create_index(neo4j.Node, PERSON)
48 | graph_db.get_or_create_index(neo4j.Relationship, LINKEDIN)
49 |
50 | # Create root subject node
51 | subject = "%s %s" % (subjectProfile["firstName"].replace(",", " "), subjectProfile["lastName"].replace(",", " "))
52 | nodes[subject] = graph_db.get_or_create_indexed_node(PERSON, "name", subject, {"name": subject, "subject": True})
53 | print 'Added', subject
54 |
55 |
56 | # iterate over all the links
57 | for result in results["values"]:
58 | link = "%s %s" % (result["firstName"].replace(",", " "), result["lastName"].replace(",", " "))
59 |
60 | if link == 'private private':
61 | # skip private people
62 | continue
63 |
64 | if link not in nodes:
65 | # Create node for this connection
66 | nodes[link] = graph_db.get_or_create_indexed_node(PERSON, "name", link, {"name": link})
67 | print 'Added', link
68 |
69 | # Create path from subject to one of the subject's links
70 | nodes[subject].get_or_create_path(LINKEDIN, nodes[link])
71 | print "Linked", subject, link
72 |
73 | # Get shared connections
74 | sharedUrl = "https://api.linkedin.com/v1/people/%s:(relation-to-viewer:(related-connections))?format=json" % result["id"]
75 | resp, content = client.request(sharedUrl)
76 | rels = simplejson.loads(content)
77 | try:
78 | for rel in rels['relationToViewer']['relatedConnections']['values']:
79 | sec = "%s %s" % (rel["firstName"].replace(",", " "), rel["lastName"].replace(",", " "))
80 | if sec not in nodes:
81 | nodes[sec] = graph_db.get_or_create_indexed_node(PERSON, "name", sec, {"name": sec})
82 | print 'Added', sec
83 | # Create path among 1st degree links
84 | nodes[link].get_or_create_path(LINKEDIN, nodes[sec])
85 | print "Linked", link, sec
86 | except:
87 | pass
88 |
89 |
--------------------------------------------------------------------------------
/server/README.txt:
--------------------------------------------------------------------------------
1 | Usage:
2 | Start neo4j:
3 | neo4j start
4 |
5 | Get dependencies:
6 | sudo npm install
7 |
8 | Start the server
9 | node app.js
10 |
11 | Then you can open ../client/index.html and see what happens.
12 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | // Config
5 | app.configure(function(){
6 | app.use(express.bodyParser());
7 | app.use(express.methodOverride());
8 | app.use(app.router);
9 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
10 | });
11 |
12 | // set up the RESTful API
13 | var mainGraphApi = require('./controllers/graph.js');
14 | app.get('/', mainGraphApi.index);
15 |
16 | app.listen(8080);
17 | console.log("Express server listening at http://localhost:8080");
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/server/controllers/graph.js:
--------------------------------------------------------------------------------
1 | var request = require('superagent');
2 | var JSONStream = require('JSONStream');
3 | var es = require('event-stream');
4 |
5 | // cypher url
6 | var url = "http://localhost:7474/db/data/cypher";
7 |
8 | // sigma.js expects the response to look something like this:
9 | // {nodes: [{id: , labe: