From: Alexander Sadleir
Date: Sat, 28 Jul 2012 13:08:44 +0000
Subject: Library updates
X-Git-Url: https://maxious.lambdacomplex.org/git/?p=disclosr.git&a=commitdiff&h=f9e85a3ad37f2c71f3b1f86a3a2e0fc1078facbd
---
Library updates
Former-commit-id: 6bd29a4ce0df1531423533a3653d2086d21c3a55
---
--- a/admin/exportEmployees.csv.php
+++ b/admin/exportEmployees.csv.php
@@ -5,7 +5,6 @@
$format = "csv";
//$format = "json";
if (isset($_REQUEST['format'])) $format = $_REQUEST['format'];
-
setlocale(LC_CTYPE, 'C');
if ($format == "csv") {
$headers = Array("name");
--- a/javascripts/bubbletree
+++ b/javascripts/bubbletree
--- a/javascripts/flotr2
+++ b/javascripts/flotr2
--- a/javascripts/sigma
+++ b/javascripts/sigma
--- a/javascripts/sigma.min.js
+++ b/javascripts/sigma.min.js
@@ -1,63 +1,3552 @@
-/* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author: Alexis Jacomy - License: MIT */
-var sigma={tools:{},classes:{},instances:{}};
-(function(){if(!Array.prototype.some)Array.prototype.some=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;jGLOBAL :
";for(var b in d.p.globalProbes)a+=""+b+" : "+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:h,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}}};this.activate=function(){if(!d.monitoring)d.monitoring=window.setInterval(e,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 h(b){if(a.p.mouseEnabled&&(e(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 e(b,c,e){if(!a.isMouseDown&&(window.clearInterval(a.interpolationID),n=void 0!=e,i=a.stageX,j=b,k=a.stageY,l=
-c,f=e||a.ratio,f=Math.min(Math.max(f,a.p.minRatio),a.p.maxRatio),u=a.p.directZooming?1-(n?a.p.zoomDelta:a.p.dragDelta):0,a.ratio!=f||a.stageX!=j||a.stageY!=l))d(),a.interpolationID=window.setInterval(d,50),a.dispatch("startinterpolate")}function d(){u+=n?a.p.zoomDelta:a.p.dragDelta;u=Math.min(u,1);var b=sigma.easing.quadratic.easeout(u),c=a.ratio;a.ratio=c*(1-b)+f*b;n?(a.stageX=j+(a.stageX-j)*a.ratio/c,a.stageY=l+(a.stageY-l)*a.ratio/c):(a.stageX=i*(1-b)+j*b,a.stageY=k*(1-b)+l*b);a.dispatch("interpolate");
-if(1<=u)window.clearInterval(a.interpolationID),b=a.ratio,n?(a.ratio=f,a.stageX=j+(a.stageX-j)*a.ratio/b,a.stageY=l+(a.stageY-l)*a.ratio/b):(a.stageX=j,a.stageY=l),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};var g=0,c=0,i=0,k=0,f=1,j=0,l=0,s=0,q=0,z=0,m=0,u=0,n=!1;this.stageY=this.stageX=
-0;this.ratio=1;this.mouseY=this.mouseX=0;this.isMouseDown=!1;b.addEventListener("DOMMouseScroll",h,!0);b.addEventListener("mousewheel",h,!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-g+i,f=a.mouseY-c+k;if(d!=a.stageX||f!=a.stageY)q=s,m=z,s=d,z=f,a.stageX=d,a.stageY=f,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,k=a.stageY,g=a.mouseX,c=a.mouseY,q=s=a.stageX,m=z=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=!1,a.dispatch("mouseup"),(i!=a.stageX||k!=a.stageY)&&e(a.stageX+a.p.inertia*(a.stageX-q),a.stageY+
-a.p.inertia*(a.stageY-m)),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);this.checkBorders=function(){return a};this.interpolate=e}function m(b,h,e,d,a,g,c){function i(a){var b=d,c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.hoverFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.hoverFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultHoverLabelBGColor;b.beginPath();if(f.p.labelHoverShadow)b.shadowOffsetX=
-0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.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"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),
-a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==f.p.nodeHoverColor?a.color||f.p.defaultNodeColor:f.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"==f.p.labelHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelHoverColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f}function k(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>-l/3&&a.displayY-a.displaySize<4*l/3}sigma.classes.Cascade.call(this);var f=this;this.p={labelColor:"default",defaultLabelColor:"#000",labelHoverBGColor:"default",defaultHoverLabelBGColor:"#fff",labelHoverShadow:!0,labelHoverShadowColor:"#000",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",defaultNodeHoverColor:"#fff",nodeActiveColor:"node",defaultNodeActiveColor:"#fff",borderSize:0,
-nodeBorderColor:"node",defaultNodeBorderColor:"#fff",edgesSpeed:200,nodesSpeed:200,labelsSpeed:200};var j=g,l=c;this.currentLabelIndex=this.currentNodeIndex=this.currentEdgeIndex=0;this.task_drawLabel=function(){for(var b=a.nodes.length,c=0;c++=f.p.labelThreshold){var k="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*d.displaySize;h.font=
-f.p.fontStyle+k+"px "+f.p.font;h.fillStyle="node"==f.p.labelColor?d.color||f.p.defaultNodeColor:f.p.defaultLabelColor;h.fillText(d.label,Math.round(d.displayX+1.5*d.displaySize),Math.round(d.displayY+k/2-3))}}else f.currentLabelIndex++;return f.currentLabelIndex(b*=2)?0.5*b*b:-0.5*(--b*(b-2)-1)};sigma.tools.drawRoundRect=function(b,h,e,d,a,g,c){var g=g?g:0,i=c?c:[],i="string"==typeof i?i.split(" "):i,c=g&&(0<=i.indexOf("topleft")||0<=i.indexOf("top")||0<=i.indexOf("left")),j=g&&(0<=i.indexOf("topright")||0<=i.indexOf("top")||0<=i.indexOf("right")),f=g&&(0<=i.indexOf("bottomleft")||0<=i.indexOf("bottom")||0<=i.indexOf("left")),i=g&&(0<=i.indexOf("bottomright")||
-0<=i.indexOf("bottom")||0<=i.indexOf("right"));b.moveTo(h,e+g);c?b.arcTo(h,e,h+g,e,g):b.lineTo(h,e);j?(b.lineTo(h+d-g,e),b.arcTo(h+d,e,h+d,e+g,g)):b.lineTo(h+d,e);i?(b.lineTo(h+d,e+a-g),b.arcTo(h+d,e+a,h+d-g,e+a,g)):b.lineTo(h+d,e+a);f?(b.lineTo(h+g,e+a),b.arcTo(h,e+a,h,e+a-g,g)):b.lineTo(h,e+a);b.lineTo(h,e+g)};sigma.tools.getRGB=function(b,g){var b=b.toString(),e={r:0,g:0,b:0};if(3<=b.length&&"#"==b.charAt(0)){var d=b.length-1;6==d?e={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&&(e={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)})}g&&(e=[e.r,e.g,e.b]);return e};sigma.tools.rgbToHex=function(b,g,e){return sigma.tools.toHex(b)+sigma.tools.toHex(g)+sigma.tools.toHex(e)};sigma.tools.toHex=function(b){b=parseInt(b,10);if(isNaN(b))return"00";b=Math.max(0,Math.min(b,255));return"0123456789ABCDEF".charAt((b-b%16)/16)+"0123456789ABCDEF".charAt(b%16)};
-sigma.publicPrototype=p.prototype})();
-
+// Define packages:
+var sigma = {};
+sigma.tools = {};
+sigma.classes = {};
+sigma.instances = {};
+
+// Adding Array helpers, if not present yet:
+(function() {
+ if (!Array.prototype.some) {
+ Array.prototype.some = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this &&
+ fun.call(thisp, this[i], i, this)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ if (!Array.prototype.forEach) {
+ Array.prototype.forEach = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ fun.call(thisp, this[i], i, this);
+ }
+ }
+ };
+ }
+
+ if (!Array.prototype.map) {
+ Array.prototype.map = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+ }
+
+ return res;
+ };
+ }
+
+ if (!Array.prototype.filter) {
+ Array.prototype.filter = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function')
+ throw new TypeError();
+
+ var res = new Array();
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ var val = this[i]; // in case fun mutates this
+ if (fun.call(thisp, val, i, this)) {
+ res.push(val);
+ }
+ }
+ }
+
+ return res;
+ };
+ }
+
+ if (!Object.keys) {
+ Object.keys = (function() {
+ var hasOwnProperty = Object.prototype.hasOwnProperty,
+ hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length;
+
+ return function(obj) {
+ if (typeof obj !== 'object' &&
+ typeof obj !== 'function' ||
+ obj === null
+ ) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var result = [];
+
+ for (var prop in obj) {
+ if (hasOwnProperty.call(obj, prop)) result.push(prop);
+ }
+
+ if (hasDontEnumBug) {
+ for (var i = 0; i < dontEnumsLength; i++) {
+ if (hasOwnProperty.call(obj, dontEnums[i])) {
+ result.push(dontEnums[i]);
+ }
+ }
+ }
+ return result;
+ }
+ })();
+ }
+})();
+
+/**
+ * A jQuery like properties management class. It works like jQuery .css()
+ * method: You can call it with juste one string to get the corresponding
+ * property, with a string and anything else to set the corresponding property,
+ * or directly with an object, and then each pair string / object (or any type)
+ * will be set in the properties.
+ * @constructor
+ * @this {sigma.classes.Cascade}
+ */
+sigma.classes.Cascade = function() {
+ /**
+ * This instance properties.
+ * @protected
+ * @type {Object}
+ */
+ this.p = {};
+
+ /**
+ * The method to use to set/get any property of this instance.
+ * @param {(string|Object)} a1 If it is a string and if a2 is undefined,
+ * then it will return the corresponding
+ * property.
+ * If it is a string and if a2 is set, then it
+ * will set a2 as the property corresponding to
+ * a1, and return this.
+ * If it is an object, then each pair string /
+ * object (or any other type) will be set as a
+ * property.
+ * @param {*?} a2 The new property corresponding to a1 if a1 is
+ * a string.
+ * @return {(*|sigma.classes.Cascade)} Returns itself or the corresponding
+ * property.
+ */
+ this.config = function(a1, a2) {
+ if (typeof a1 == 'string' && a2 == undefined) {
+ return this.p[a1];
+ } else {
+ var o = (typeof a1 == 'object' && a2 == undefined) ? a1 : {};
+ if (typeof a1 == 'string') {
+ o[a1] = a2;
+ }
+
+ for (var k in o) {
+ if (this.p[k] != undefined) {
+ this.p[k] = o[k];
+ }
+ }
+ return this;
+ }
+ };
+};
+
+/**
+ * sigma.js custom event dispatcher class.
+ * @constructor
+ * @this {sigma.classes.EventDispatcher}
+ */
+sigma.classes.EventDispatcher = function() {
+ /**
+ * An object containing all the different handlers bound to one or many
+ * events, indexed by these events.
+ * @private
+ * @type {Object.}
+ */
+ var _h = {};
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {sigma.classes.EventDispatcher}
+ */
+ var _self = this;
+
+ /**
+ * Will execute the handler the next (and only the next) time that the
+ * indicated event (or the indicated events) will be triggered.
+ * @param {string} events The name of the event (or the events
+ * separated by spaces).
+ * @param {function(Object)} handler The handler to bind.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function one(events, handler) {
+ if (!handler || !events) {
+ return _self;
+ }
+
+ var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+ eArray.forEach(function(event) {
+ if (!_h[event]) {
+ _h[event] = [];
+ }
+
+ _h[event].push({
+ 'h': handler,
+ 'one': true
+ });
+ });
+
+ return _self;
+ }
+
+ /**
+ * Will execute the handler everytime that the indicated event (or the
+ * indicated events) will be triggered.
+ * @param {string} events The name of the event (or the events
+ * separated by spaces).
+ * @param {function(Object)} handler The handler to bind.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function bind(events, handler) {
+ if (!handler || !events) {
+ return _self;
+ }
+
+ var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+ eArray.forEach(function(event) {
+ if (!_h[event]) {
+ _h[event] = [];
+ }
+
+ _h[event].push({
+ 'h': handler,
+ 'one': false
+ });
+ });
+
+ return _self;
+ }
+
+ /**
+ * Unbinds the handler from a specified event (or specified events).
+ * @param {?string} events The name of the event (or the events
+ * separated by spaces). If undefined,
+ * then all handlers are unbound.
+ * @param {?function(Object)} handler The handler to unbind. If undefined,
+ * each handler bound to the event or the
+ * events will be unbound.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function unbind(events, handler) {
+ if (!events) {
+ _h = {};
+ }
+
+ var eArray = typeof events == 'string' ? events.split(' ') : events;
+
+ if (handler) {
+ eArray.forEach(function(event) {
+ if (_h[event]) {
+ _h[event] = _h[event].filter(function(e) {
+ return e['h'] != handler;
+ });
+ }
+
+ if (_h[event] && _h[event].length == 0) {
+ delete _h[event];
+ }
+ });
+ }else {
+ eArray.forEach(function(event) {
+ delete _h[event];
+ });
+ }
+
+ return _self;
+ }
+
+ /**
+ * Executes each handler bound to the event
+ * @param {string} type The type of the event.
+ * @param {?Object} content The content of the event (optional).
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function dispatch(type, content) {
+ if (_h[type]) {
+ _h[type].forEach(function(e) {
+ e['h']({
+ 'type': type,
+ 'content': content,
+ 'target': _self
+ });
+ });
+
+ _h[type] = _h[type].filter(function(e) {
+ return !e['one'];
+ });
+ }
+
+ return _self;
+ }
+
+ /* PUBLIC INTERFACE: */
+ this.one = one;
+ this.bind = bind;
+ this.unbind = unbind;
+ this.dispatch = dispatch;
+};
+
+(function() {
+// Define local shortcut:
+var id = 0;
+
+// Define local package:
+var local = {};
+local.plugins = [];
+
+sigma.init = function(dom) {
+ var inst = new Sigma(dom, (++id).toString());
+ sigma.instances[id] = new SigmaPublic(inst);
+ return sigma.instances[id];
+};
+
+/**
+ * This class listen to all the different mouse events, to normalize them and
+ * dispatch action events instead (from "startinterpolate" to "isdragging",
+ * etc).
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} dom The DOM element to bind the handlers on.
+ * @this {MouseCaptor}
+ */
+function MouseCaptor(dom) {
+ sigma.classes.Cascade.call(this);
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {MouseCaptor}
+ */
+ var self = this;
+
+ /**
+ * The DOM element to bind the handlers on.
+ * @type {element}
+ */
+ var dom = dom;
+
+ /**
+ * The different parameters that define how this instance should work.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ minRatio: 1,
+ maxRatio: 32,
+ marginRatio: 1,
+ zoomDelta: 0.1,
+ dragDelta: 0.3,
+ zoomMultiply: 2,
+ directZooming: false,
+ blockScroll: true,
+ inertia: 1.1,
+ mouseEnabled: true
+ };
+
+ var oldMouseX = 0;
+ var oldMouseY = 0;
+ var startX = 0;
+ var startY = 0;
+
+ var oldStageX = 0;
+ var oldStageY = 0;
+ var oldRatio = 1;
+
+ var targetRatio = 1;
+ var targetStageX = 0;
+ var targetStageY = 0;
+
+ var lastStageX = 0;
+ var lastStageX2 = 0;
+ var lastStageY = 0;
+ var lastStageY2 = 0;
+
+ var progress = 0;
+ var isZooming = false;
+
+ this.stageX = 0;
+ this.stageY = 0;
+ this.ratio = 1;
+
+ this.mouseX = 0;
+ this.mouseY = 0;
+
+ this.isMouseDown = false;
+
+ /**
+ * Extract the local X position from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The local X value of the mouse.
+ */
+ function getX(e) {
+ return e.offsetX != undefined && e.offsetX ||
+ e.layerX != undefined && e.layerX ||
+ e.clientX != undefined && e.clientX;
+ };
+
+ /**
+ * Extract the local Y position from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The local Y value of the mouse.
+ */
+ function getY(e) {
+ return e.offsetY != undefined && e.offsetY ||
+ e.layerY != undefined && e.layerY ||
+ e.clientY != undefined && e.clientY;
+ };
+
+ /**
+ * Extract the wheel delta from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The wheel delta of the mouse.
+ */
+ function getDelta(e) {
+ return e.wheelDelta != undefined && e.wheelDelta ||
+ e.detail != undefined && -e.detail;
+ };
+
+ /**
+ * The handler listening to the 'move' mouse event. It will set the mouseX
+ * and mouseY values as the mouse position values, prevent the default event,
+ * and dispatch a 'move' event.
+ * @private
+ * @param {event} event A 'move' mouse event.
+ */
+ function moveHandler(event) {
+ oldMouseX = self.mouseX;
+ oldMouseY = self.mouseY;
+
+ self.mouseX = getX(event);
+ self.mouseY = getY(event);
+
+ self.isMouseDown && drag(event);
+ self.dispatch('move');
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ };
+
+ /**
+ * The handler listening to the 'up' mouse event. It will set the isMouseDown
+ * value as false, dispatch a 'mouseup' event, and trigger stopDrag().
+ * @private
+ * @param {event} event A 'up' mouse event.
+ */
+ function upHandler(event) {
+ if (self.p.mouseEnabled && self.isMouseDown) {
+ self.isMouseDown = false;
+ self.dispatch('mouseup');
+ stopDrag();
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ };
+
+ /**
+ * The handler listening to the 'down' mouse event. It will set the
+ * isMouseDown value as true, dispatch a 'mousedown' event, and trigger
+ * startDrag().
+ * @private
+ * @param {event} event A 'down' mouse event.
+ */
+ function downHandler(event) {
+ if (self.p.mouseEnabled) {
+ self.isMouseDown = true;
+ oldMouseX = self.mouseX;
+ oldMouseY = self.mouseY;
+
+ self.dispatch('mousedown');
+
+ startDrag();
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ };
+
+ /**
+ * The handler listening to the 'wheel' mouse event. It will trigger
+ * {@link startInterpolate} with the event delta as parameter.
+ * @private
+ * @param {event} event A 'wheel' mouse event.
+ */
+ function wheelHandler(event) {
+ if (self.p.mouseEnabled) {
+ startInterpolate(
+ self.mouseX,
+ self.mouseY,
+ self.ratio * (getDelta(event) > 0 ?
+ self.p.zoomMultiply :
+ 1 / self.p.zoomMultiply)
+ );
+
+ if (self.p['blockScroll']) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ }
+ };
+
+ /**
+ * Will start computing the scene X and Y, until {@link stopDrag} is
+ * triggered.
+ */
+ function startDrag() {
+ oldStageX = self.stageX;
+ oldStageY = self.stageY;
+ startX = self.mouseX;
+ startY = self.mouseY;
+
+ lastStageX = self.stageX;
+ lastStageX2 = self.stageX;
+ lastStageY = self.stageY;
+ lastStageY2 = self.stageY;
+
+ self.dispatch('startdrag');
+ };
+
+ /**
+ * Stops computing the scene position.
+ */
+ function stopDrag() {
+ if (oldStageX != self.stageX || oldStageY != self.stageY) {
+ startInterpolate(
+ self.stageX + self.p.inertia * (self.stageX - lastStageX2),
+ self.stageY + self.p.inertia * (self.stageY - lastStageY2)
+ );
+ }
+ };
+
+ /**
+ * Computes the position of the scene, relatively to the mouse position, and
+ * dispatches a "drag" event.
+ */
+ function drag() {
+ var newStageX = self.mouseX - startX + oldStageX;
+ var newStageY = self.mouseY - startY + oldStageY;
+
+ if (newStageX != self.stageX || newStageY != self.stageY) {
+ lastStageX2 = lastStageX;
+ lastStageY2 = lastStageY;
+
+ lastStageX = newStageX;
+ lastStageY = newStageY;
+
+ self.stageX = newStageX;
+ self.stageY = newStageY;
+ self.dispatch('drag');
+ }
+ };
+
+ /**
+ * Will start computing the scene zoom ratio, until {@link stopInterpolate} is
+ * triggered.
+ * @param {number} x The new stage X.
+ * @param {number} y The new stage Y.
+ * @param {number} ratio The new zoom ratio.
+ */
+ function startInterpolate(x, y, ratio) {
+ if (self.isMouseDown) {
+ return;
+ }
+
+ window.clearInterval(self.interpolationID);
+ isZooming = ratio != undefined;
+
+ oldStageX = self.stageX;
+ targetStageX = x;
+
+ oldStageY = self.stageY;
+ targetStageY = y;
+
+ oldRatio = self.ratio;
+ targetRatio = ratio || self.ratio;
+ targetRatio = Math.min(
+ Math.max(targetRatio, self.p.minRatio),
+ self.p.maxRatio
+ );
+
+ progress =
+ self.p.directZooming ?
+ 1 - (isZooming ? self.p.zoomDelta : self.p.dragDelta) :
+ 0;
+
+ if (
+ self.ratio != targetRatio ||
+ self.stageX != targetStageX ||
+ self.stageY != targetStageY
+ ) {
+ interpolate();
+ self.interpolationID = window.setInterval(interpolate, 50);
+ self.dispatch('startinterpolate');
+ }
+ };
+
+ /**
+ * Stops the move interpolation.
+ */
+ function stopInterpolate() {
+ var oldRatio = self.ratio;
+
+ if (isZooming) {
+ self.ratio = targetRatio;
+ self.stageX = targetStageX +
+ (self.stageX - targetStageX) *
+ self.ratio /
+ oldRatio;
+ self.stageY = targetStageY +
+ (self.stageY - targetStageY) *
+ self.ratio /
+ oldRatio;
+ }else {
+ self.stageX = targetStageX;
+ self.stageY = targetStageY;
+ }
+
+ self.dispatch('stopinterpolate');
+ };
+
+ /**
+ * Computes the interpolate ratio and the position of the scene, relatively
+ * to the last mouse event delta received, and dispatches a "interpolate"
+ * event.
+ */
+ function interpolate() {
+ progress += (isZooming ? self.p.zoomDelta : self.p.dragDelta);
+ progress = Math.min(progress, 1);
+
+ var k = sigma.easing.quadratic.easeout(progress);
+ var oldRatio = self.ratio;
+
+ self.ratio = oldRatio * (1 - k) + targetRatio * k;
+
+ if (isZooming) {
+ self.stageX = targetStageX +
+ (self.stageX - targetStageX) *
+ self.ratio /
+ oldRatio;
+
+ self.stageY = targetStageY +
+ (self.stageY - targetStageY) *
+ self.ratio /
+ oldRatio;
+ } else {
+ self.stageX = oldStageX * (1 - k) + targetStageX * k;
+ self.stageY = oldStageY * (1 - k) + targetStageY * k;
+ }
+
+ self.dispatch('interpolate');
+ if (progress >= 1) {
+ window.clearInterval(self.interpolationID);
+ stopInterpolate();
+ }
+ };
+
+ /**
+ * Checks that there is always a part of the graph that is displayed, to
+ * avoid the user to drag the graph out of the stage.
+ * @param {Object} b An object containing the borders of the graph.
+ * @param {number} width The width of the stage.
+ * @param {number} height The height of the stage.
+ * @return {MouseCaptor} Returns itself.
+ */
+ function checkBorders(b, width, height) {
+ // TODO : Find the good formula
+ /*if (!isNaN(b.minX) && !isNaN(b.maxX)) {
+ self.stageX = Math.min(
+ self.stageX = Math.max(
+ self.stageX,
+ (b.minX - width) * self.ratio +
+ self.p.marginRatio*(b.maxX - b.minX)
+ ),
+ (b.maxX - width) * self.ratio +
+ width -
+ self.p.marginRatio*(b.maxX - b.minX)
+ );
+ }
+
+ if (!isNaN(b.minY) && !isNaN(b.maxY)) {
+ self.stageY = Math.min(
+ self.stageY = Math.max(
+ self.stageY,
+ (b.minY - height) * self.ratio +
+ self.p.marginRatio*(b.maxY - b.minY)
+ ),
+ (b.maxY - height) * self.ratio +
+ height -
+ self.p.marginRatio*(b.maxY - b.minY)
+ );
+ }*/
+
+ return self;
+ };
+
+ // ADD CALLBACKS
+ dom.addEventListener('DOMMouseScroll', wheelHandler, true);
+ dom.addEventListener('mousewheel', wheelHandler, true);
+ dom.addEventListener('mousemove', moveHandler, true);
+ dom.addEventListener('mousedown', downHandler, true);
+ document.addEventListener('mouseup', upHandler, true);
+
+ this.checkBorders = checkBorders;
+ this.interpolate = startInterpolate;
+}
+
+/**
+ * A class to monitor some local / global probes directly on an instance,
+ * inside a div DOM element.
+ * It executes different methods (called "probes") regularly, and displays
+ * the results on the element.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @param {Sigma} instance The instance to monitor.
+ * @param {element} dom The div DOM element to draw write on.
+ * @this {Monitor}
+ */
+function Monitor(instance, dom) {
+ sigma.classes.Cascade.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Monitor}
+ */
+ var self = this;
+
+ /**
+ * {@link Sigma} instance owning this Monitor instance.
+ * @type {Sigma}
+ */
+ this.instance = instance;
+
+ /**
+ * Determines if the monitoring is activated or not.
+ * @type {Boolean}
+ */
+ this.monitoring = false;
+
+ /**
+ * The different parameters that define how this instance should work. It
+ * also contains the different probes.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ fps: 40,
+ dom: dom,
+ globalProbes: {
+ 'Time (ms)': sigma.chronos.getExecutionTime,
+ 'Queue': sigma.chronos.getQueuedTasksCount,
+ 'Tasks': sigma.chronos.getTasksCount,
+ 'FPS': sigma.chronos.getFPS
+ },
+ localProbes: {
+ 'Nodes count': function() { return self.instance.graph.nodes.length; },
+ 'Edges count': function() { return self.instance.graph.edges.length; }
+ }
+ };
+
+ /**
+ * Activates the monitoring: Some texts describing some values about sigma.js
+ * or the owning {@link Sigma} instance will appear over the graph, but
+ * beneath the mouse sensible DOM element.
+ * @return {Monitor} Returns itself.
+ */
+ function activate() {
+ if (!self.monitoring) {
+ self.monitoring = window.setInterval(routine, 1000 / self.p.fps);
+ }
+
+ return self;
+ }
+
+ /**
+ * Desactivates the monitoring: Will disappear, and stop computing the
+ * different probes.
+ * @return {Monitor} Returns itself.
+ */
+ function desactivate() {
+ if (self.monitoring) {
+ window.clearInterval(self.monitoring);
+ self.monitoring = null;
+
+ self.p.dom.innerHTML = '';
+ }
+
+ return self;
+ }
+
+ /**
+ * The private method dedicated to compute the different values to observe.
+ * @private
+ * @return {Monitor} Returns itself.
+ */
+ function routine() {
+ var s = '';
+
+ s += 'GLOBAL :
';
+ for (var k in self.p.globalProbes) {
+ s += '' + k + ' : ' + self.p.globalProbes[k]() + '
';
+ }
+
+ s += '
LOCAL :
';
+ for (var k in self.p.localProbes) {
+ s += '' + k + ' : ' + self.p.localProbes[k]() + '
';
+ }
+
+ self.p.dom.innerHTML = s;
+
+ return self;
+ }
+
+ this.activate = activate;
+ this.desactivate = desactivate;
+}
+
+/**
+ * Sigma is the main class. It represents the core of any instance id sigma.js.
+ * It is private and can be initialized only from inside sigma.js. To see its
+ * public interface, see {@link SigmaPublic}.
+ * It owns its own {@link Graph}, {@link MouseCaptor}, {@link Plotter}
+ * and {@link Monitor}.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} root The DOM root of this instance (a div, for example).
+ * @param {string} id The ID of this instance.
+ * @this {Sigma}
+ */
+function Sigma(root, id) {
+ sigma.classes.Cascade.call(this);
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Sigma}
+ */
+ var self = this;
+
+ /**
+ * The ID of the instance.
+ * @type {string}
+ */
+ this.id = id.toString();
+
+ /**
+ * The different parameters that define how this instance should work.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ auto: true,
+ drawNodes: 2,
+ drawEdges: 1,
+ drawLabels: 2,
+ lastNodes: 2,
+ lastEdges: 0,
+ lastLabels: 2,
+ drawHoverNodes: true,
+ drawActiveNodes: true
+ };
+
+ /**
+ * The root DOM element of this instance, containing every other elements.
+ * @type {element}
+ */
+ this.domRoot = root;
+
+ /**
+ * The width of this instance - initially, the root's width.
+ * @type {number}
+ */
+ this.width = this.domRoot.offsetWidth;
+
+ /**
+ * The height of this instance - initially, the root's height.
+ * @type {number}
+ */
+ this.height = this.domRoot.offsetHeight;
+
+ /**
+ * The graph of this instance - initiallyempty.
+ * @type {Graph}
+ */
+ this.graph = new Graph();
+
+ /**
+ * An object referencing every DOM elements used by this instance.
+ * @type {Object}
+ */
+ this.domElements = {};
+
+ initDOM('edges', 'canvas');
+ initDOM('nodes', 'canvas');
+ initDOM('labels', 'canvas');
+ initDOM('hover', 'canvas');
+ initDOM('monitor', 'div');
+ initDOM('mouse', 'canvas');
+
+ /**
+ * The class dedicated to manage the drawing process of the graph of the
+ * different canvas.
+ * @type {Plotter}
+ */
+ this.plotter = new Plotter(
+ this.domElements.nodes.getContext('2d'),
+ this.domElements.edges.getContext('2d'),
+ this.domElements.labels.getContext('2d'),
+ this.domElements.hover.getContext('2d'),
+ this.graph,
+ this.width,
+ this.height
+ );
+
+ /**
+ * The class dedicated to monitor different probes about the running
+ * processes or the data, such as the number of nodes or edges, or how
+ * many times the graph is drawn per second.
+ * @type {Monitor}
+ */
+ this.monitor = new Monitor(
+ this,
+ this.domElements.monitor
+ );
+
+ /**
+ * The class dedicated to manage the different mouse events.
+ * @type {MouseCaptor}
+ */
+ this.mousecaptor = new MouseCaptor(
+ this.domElements.mouse,
+ this.id
+ );
+
+ // Interaction listeners:
+ this.mousecaptor.bind('drag interpolate', function(e) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 0 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels,
+ true
+ );
+ }).bind('stopdrag stopinterpolate', function(e) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 1 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels,
+ true
+ );
+ }).bind('mousedown mouseup', function(e) {
+ var targeted = self.graph.nodes.filter(function(n) {
+ return !!n['hover'];
+ }).map(function(n) {
+ return n.id;
+ });
+
+ self.dispatch(
+ e['type'] == 'mousedown' ?
+ 'downgraph' :
+ 'upgraph'
+ );
+
+ if (targeted.length) {
+ self.dispatch(
+ e['type'] == 'mousedown' ?
+ 'downnodes' :
+ 'upnodes',
+ targeted
+ );
+ }
+ }).bind('move', function() {
+ self.domElements.hover.getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements.hover.width,
+ self.domElements.hover.height
+ );
+
+ drawHover();
+ drawActive();
+ });
+
+ sigma.chronos.bind('startgenerators', function() {
+ if (sigma.chronos.getGeneratorsIDs().some(function(id) {
+ return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+ })) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 0 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels
+ );
+ }
+ }).bind('stopgenerators', function() {
+ self.draw();
+ });
+
+ /**
+ * Resizes the element, and redraws the graph with the last settings.
+ * @param {?number} w The new width (if undefined, it will use the root
+ * width).
+ * @param {?number} h The new height (if undefined, it will use the root
+ * height).
+ * @return {Sigma} Returns itself.
+ */
+ function resize(w, h) {
+ var oldW = self.width, oldH = self.height;
+
+ if (w != undefined && h != undefined) {
+ self.width = w;
+ self.height = h;
+ }else {
+ self.width = self.domRoot.offsetWidth;
+ self.height = self.domRoot.offsetHeight;
+ }
+
+ if (oldW != self.width || oldH != self.height) {
+ for (var k in self.domElements) {
+ self.domElements[k].setAttribute('width', self.width + 'px');
+ self.domElements[k].setAttribute('height', self.height + 'px');
+ }
+
+ self.plotter.resize(self.width, self.height);
+
+ self.draw(
+ self.p.lastNodes,
+ self.p.lastEdges,
+ self.p.lastLabels,
+ true
+ );
+ }
+ return self;
+ };
+
+ /**
+ * Kills every drawing task currently running. Basically, it stops this
+ * instance's drawing process.
+ * @return {Sigma} Returns itself.
+ */
+ function clearSchedule() {
+ sigma.chronos.removeTask(
+ 'node_' + self.id, 2
+ ).removeTask(
+ 'edge_' + self.id, 2
+ ).removeTask(
+ 'label_' + self.id, 2
+ ).stopTasks();
+ return self;
+ };
+
+ /**
+ * Initialize a DOM element, that will be stores by this instance, to make
+ * automatic these elements resizing.
+ * @private
+ * @param {string} id The element's ID.
+ * @param {string} type The element's nodeName (Example : canvas, div, ...).
+ * @return {Sigma} Returns itself.
+ */
+ function initDOM(id, type) {
+ self.domElements[id] = document.createElement(type);
+ self.domElements[id].style.position = 'absolute';
+ self.domElements[id].setAttribute('id', 'sigma_' + id + '_' + self.id);
+ self.domElements[id].setAttribute('class', 'sigma_' + id + '_' + type);
+ self.domElements[id].setAttribute('width', self.width + 'px');
+ self.domElements[id].setAttribute('height', self.height + 'px');
+
+ self.domRoot.appendChild(self.domElements[id]);
+ return self;
+ };
+
+ /**
+ * Starts the graph drawing process. The three first parameters indicate
+ * how the different layers have to be drawn:
+ * . -1: The layer is not drawn, but it is not erased.
+ * . 0: The layer is not drawn.
+ * . 1: The layer is drawn progressively.
+ * . 2: The layer is drawn directly.
+ * @param {?number} nodes Determines if and how the nodes must be drawn.
+ * @param {?number} edges Determines if and how the edges must be drawn.
+ * @param {?number} labels Determines if and how the labels must be drawn.
+ * @param {?boolean} safe If true, nothing will happen if any generator
+ * affiliated to this instance is currently running
+ * (an iterative layout, for example).
+ * @return {Sigma} Returns itself.
+ */
+ function draw(nodes, edges, labels, safe) {
+ if (safe && sigma.chronos.getGeneratorsIDs().some(function(id) {
+ return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+ })) {
+ return self;
+ }
+
+ var n = (nodes == undefined) ? self.p.drawNodes : nodes;
+ var e = (edges == undefined) ? self.p.drawEdges : edges;
+ var l = (labels == undefined) ? self.p.drawLabels : labels;
+
+ var params = {
+ nodes: n,
+ edges: e,
+ labels: l
+ };
+
+ self.p.lastNodes = n;
+ self.p.lastEdges = e;
+ self.p.lastLabels = l;
+
+ // Remove tasks:
+ clearSchedule();
+
+ // Rescale graph:
+ self.graph.rescale(
+ self.width,
+ self.height,
+ n > 0,
+ e > 0
+ ).setBorders();
+
+ self.mousecaptor.checkBorders(
+ self.graph.borders,
+ self.width,
+ self.height
+ );
+
+ self.graph.translate(
+ self.mousecaptor.stageX,
+ self.mousecaptor.stageY,
+ self.mousecaptor.ratio,
+ n > 0,
+ e > 0
+ );
+
+ self.dispatch(
+ 'graphscaled'
+ );
+
+ // Clear scene:
+ for (var k in self.domElements) {
+ if (
+ self.domElements[k].nodeName.toLowerCase() == 'canvas' &&
+ (params[k] == undefined || params[k] >= 0)
+ ) {
+ self.domElements[k].getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements[k].width,
+ self.domElements[k].height
+ );
+ }
+ }
+
+ self.plotter.currentEdgeIndex = 0;
+ self.plotter.currentNodeIndex = 0;
+ self.plotter.currentLabelIndex = 0;
+
+ var previous = null;
+ var start = false;
+
+ if (n) {
+ if (n > 1) {
+ while (self.plotter.task_drawNode()) {}
+ }else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawNode,
+ 'node_' + self.id,
+ false
+ );
+
+ start = true;
+ previous = 'node_' + self.id;
+ }
+ }
+
+ if (l) {
+ if (l > 1) {
+ while (self.plotter.task_drawLabel()) {}
+ } else {
+ if (previous) {
+ sigma.chronos.queueTask(
+ self.plotter.task_drawLabel,
+ 'label_' + self.id,
+ previous
+ );
+ } else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawLabel,
+ 'label_' + self.id,
+ false
+ );
+ }
+
+ start = true;
+ previous = 'label_' + self.id;
+ }
+ }
+
+ if (e) {
+ if (e > 1) {
+ while (self.plotter.task_drawEdge()) {}
+ }else {
+ if (previous) {
+ sigma.chronos.queueTask(
+ self.plotter.task_drawEdge,
+ 'edge_' + self.id,
+ previous
+ );
+ }else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawEdge,
+ 'edge_' + self.id,
+ false
+ );
+ }
+
+ start = true;
+ previous = 'edge_' + self.id;
+ }
+ }
+
+ self.dispatch(
+ 'draw'
+ );
+
+ self.refresh();
+
+ start && sigma.chronos.runTasks();
+ return self;
+ };
+
+ /**
+ * Draws the hover and active nodes labels.
+ * @return {Sigma} Returns itself.
+ */
+ function refresh() {
+ self.domElements.hover.getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements.hover.width,
+ self.domElements.hover.height
+ );
+
+ drawHover();
+ drawActive();
+
+ return self;
+ }
+
+ /**
+ * Draws the hover nodes labels. This method is applied directly, and does
+ * not use the pseudo-asynchronous tasks process.
+