2016-04-18 12:56:49 +00:00
|
|
|
/* Gestion du SOCIOGRAMME SPATIAL
|
|
|
|
*
|
|
|
|
* [1] - Récupération du conteneur (élément du DOM/HTML)
|
|
|
|
* [2] - Construction de la classe (objet type)
|
|
|
|
* [3] - Construction de l'objet SIGMA
|
|
|
|
* [4] - Récupération des données
|
|
|
|
* [5] - Paramétrage de SIGMA
|
|
|
|
* [6] - On extrait les noeuds/liens des données reçues
|
|
|
|
* [7] - On ajoute les noeuds/liens à SIGMA
|
|
|
|
* [8] - On positionne le tout dans l'espace
|
|
|
|
* [9] - On bind() les évènements
|
|
|
|
* [10]- On affiche le rendu
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* [1] Récupération du conteneur
|
|
|
|
=========================================================*/
|
|
|
|
function sociogramClass(container){
|
|
|
|
this.container = container;
|
|
|
|
this.log('sociogram created');
|
2016-04-18 15:01:11 +00:00
|
|
|
}
|
2016-04-18 12:56:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* [2] Construction de la classe
|
|
|
|
=========================================================*/
|
|
|
|
sociogramClass.prototype = {
|
|
|
|
container: this.container,
|
|
|
|
sigma: null,
|
|
|
|
request: { path: 'charts/network_data' },
|
|
|
|
response: null,
|
|
|
|
nodes: [],
|
|
|
|
edges: [],
|
|
|
|
rad: 500,
|
|
|
|
nodeDistance: 100,
|
|
|
|
|
|
|
|
/* (0) Ajout de méthodes à @this.sigma.graph */
|
|
|
|
overloadGraph: function(){},
|
|
|
|
|
|
|
|
/* (1) Point d'amorçage */
|
|
|
|
load: function(){},
|
|
|
|
|
|
|
|
/* (2) Retourne la distance avec le noeud le plus près de la position (@x, @y) */
|
|
|
|
nodeAt: function(x, y){},
|
|
|
|
/* (3) Positionnement spatial d'un noeud d'id @nodeId à une position @pos(x,y) */
|
|
|
|
arrange: function(nodeId, pos, alone){},
|
|
|
|
|
|
|
|
/* (4) Extraction de des @this.nodes depuis @this.response */
|
|
|
|
extractNodesFromResponse: function(){},
|
|
|
|
/* (5) Extraction de des @this.edges depuis @this.response */
|
|
|
|
extractEdgesFromResponse: function(){},
|
|
|
|
|
|
|
|
/* (6) Ajout des noeuds @this.nodes au rendu */
|
|
|
|
renderNodes: function(){},
|
|
|
|
/* (7) Ajout des liens @this.edges au rendu */
|
|
|
|
renderEdges: function(){},
|
|
|
|
|
|
|
|
/* (8) Ajout des fonctions dans @this.sigma.graph */
|
|
|
|
overload: {
|
|
|
|
// {1} Renvoie la liste des voisins directs et indirects du noeud d'id @nodeId //
|
|
|
|
nodeNeighbors: function(nodeId){},
|
|
|
|
// {2} Renvoie la liste des voisins directs du noeud d'id @nodeId //
|
|
|
|
nodeDirectNeighbors: function(nodeId){}
|
|
|
|
},
|
|
|
|
|
|
|
|
/* (9) Fonctions de callback pour les évènements */
|
|
|
|
bindings: {
|
|
|
|
// {1} Action quand on clique sur un noeud //
|
|
|
|
clickNode: function(thisPtr, e){},
|
|
|
|
// {2} Action quand on clique dans le vide //
|
|
|
|
clickStage: function(thisPtr, e){}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
log: function(message){
|
|
|
|
console.log('--[SOCIOGRAM]--');
|
|
|
|
console.warn(message);
|
|
|
|
console.log('--[/SOCIOGRAM]--');
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SURCHARGE DU GRAPH (@this.sigma.graph) -> Ajout de méthodes
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.overloadGraph = function(){
|
|
|
|
sigma.classes.graph.addMethod('nodeNeighbors', this.overload.nodeNeighbors);
|
|
|
|
sigma.classes.graph.addMethod('nodeDirectNeighbors', this.overload.nodeDirectNeighbors);
|
|
|
|
this.log('graph overloaded');
|
2016-04-18 14:21:24 +00:00
|
|
|
};
|
2016-04-18 12:56:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* (1) Point d'amorçage */
|
|
|
|
sociogramClass.prototype.load = function(){
|
|
|
|
// {1} On instancie SIGMA //
|
|
|
|
this.sigma = new sigma({ renderer: { container: this.container, 'type': 'canvas' } });
|
|
|
|
|
|
|
|
var thisPtr = this;
|
|
|
|
|
|
|
|
// {2} On récupère les données via l'API //
|
|
|
|
api.send(this.request, function(response){
|
|
|
|
thisPtr.log(response);
|
|
|
|
|
|
|
|
// Si erreur, on quitte
|
2016-04-18 15:01:11 +00:00
|
|
|
if( response.ModuleError !== 0 ) return;
|
2016-04-18 12:56:49 +00:00
|
|
|
|
|
|
|
// On enregistre la réponse
|
|
|
|
thisPtr.response = response;
|
|
|
|
|
|
|
|
|
|
|
|
// {3} On paramètre SIGMA //
|
|
|
|
thisPtr.sigma.settings({
|
|
|
|
defaultNodeColor: '#348ed8',
|
|
|
|
defaultLabelSize: 14,
|
|
|
|
defaultLabelBGColor: "#ddd",
|
|
|
|
defaultHoverLabelBGColor: "#002147",
|
|
|
|
defaultLabelHoverColor: "#fff",
|
|
|
|
labelThreshold: 10,
|
|
|
|
defaultEdgeType: "line"
|
|
|
|
});
|
|
|
|
|
|
|
|
// {4} On recupere les noeuds et les liens //
|
|
|
|
thisPtr.extractNodesFromResponse();
|
|
|
|
thisPtr.extractEdgesFromResponse();
|
|
|
|
|
|
|
|
// {5} On ajoute les noeuds et les liens au rendu //
|
|
|
|
thisPtr.addNodes();
|
|
|
|
thisPtr.addEdges();
|
|
|
|
|
|
|
|
|
|
|
|
// {6} On définit les évènements //
|
|
|
|
// On affiche que les voisins d'un noeud quand on clique sur lui
|
|
|
|
thisPtr.sigma.bind('clickNode', function(e){
|
|
|
|
thisPtr.bindings.clickNode(thisPtr, e);
|
|
|
|
});
|
|
|
|
|
|
|
|
// On affiche tous les noeuds quand on clique dans le vide
|
|
|
|
thisPtr.sigma.bind('clickStage', function(e){
|
|
|
|
thisPtr.bindings.clickStage(thisPtr, e);
|
|
|
|
});
|
|
|
|
|
|
|
|
// {7} On positionne spatialement les noeuds //
|
|
|
|
thisPtr.sigma.graph.nodes().forEach(function(n){ thisPtr.arrange(n.id, null, true); });
|
|
|
|
|
|
|
|
// {8} On affiche le rendu //
|
|
|
|
thisPtr.sigma.camera.ratio = 1.2;
|
|
|
|
thisPtr.sigma.refresh();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* RETOURNE LA DISTANCE AVEC LE NOEUD LE PLUS PRES
|
|
|
|
*
|
|
|
|
* @x<float> Abscisse du point
|
|
|
|
* @y<float> Ordonnees du point
|
|
|
|
*
|
|
|
|
* @return distance<float> Retourne la distance du noeud le plus proche
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.nodeAt = function(x, y){
|
|
|
|
var nodes = this.sigma.graph.nodes();
|
|
|
|
var minDistance = null;
|
2016-04-18 15:01:11 +00:00
|
|
|
for( var nodeId in nodes ){
|
2016-04-18 12:56:49 +00:00
|
|
|
var distance = Math.sqrt( Math.pow(x-nodes[nodeId].x, 2) + Math.pow(y-nodes[nodeId].y, 2) );
|
2016-04-18 15:01:11 +00:00
|
|
|
if( minDistance === null || distance < minDistance )
|
2016-04-18 12:56:49 +00:00
|
|
|
minDistance = distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
return minDistance;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* POSITIONNE LES VOISINS AUTOUR DU NOEUD COURANT
|
|
|
|
*
|
|
|
|
* @nodeId<String> Id du noeud courant
|
|
|
|
* @pos<Object> Contient {x, y} position initiale, sinon la position actuelle du noeud
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.arrange = function(nodeId, pos, alone){
|
|
|
|
var node = this.sigma.graph.nodes(nodeId);
|
|
|
|
|
|
|
|
// Si le noeud est deja place, on ne fais rien
|
2016-04-18 15:01:11 +00:00
|
|
|
if( node.x !== 0 || node.y !== 0 ) return;
|
2016-04-18 12:56:49 +00:00
|
|
|
|
2016-04-18 15:01:11 +00:00
|
|
|
pos = (pos===null) ? {x: node.x, y: node.y} : pos; // On recupere la position
|
2016-04-18 12:56:49 +00:00
|
|
|
|
|
|
|
// Tant que le noeud est trop proche d'un autre, on l'eloigne
|
|
|
|
// UNIQUEMENT si alone n'est pas NULL
|
|
|
|
if( alone ){
|
|
|
|
while( this.nodeAt(pos.x, pos.y) < 2*this.nodeDistance ){
|
|
|
|
pos = {
|
|
|
|
x: pos.x + 2*this.nodeDistance*Math.cos(Math.random()*2*Math.PI),
|
|
|
|
y: pos.y + 2*this.nodeDistance*Math.sin(Math.random()*2*Math.PI)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// On recupere les voisins directs
|
|
|
|
var neighborsId = this.sigma.graph.nodeDirectNeighbors(nodeId);
|
|
|
|
var neighbors = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var neighborsCount = 0;
|
2016-04-18 15:01:11 +00:00
|
|
|
for( var neighborId in neighborsId ){
|
2016-04-18 12:56:49 +00:00
|
|
|
neighbors[neighborId] = this.sigma.graph.nodes(neighborId);
|
|
|
|
neighborsCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// On positionne le noeud
|
|
|
|
node.x = pos.x;
|
|
|
|
node.y = pos.y;
|
|
|
|
|
|
|
|
var angles = [];
|
|
|
|
|
|
|
|
// On positionne chaque voisin si c'est pas deja fait
|
|
|
|
for( neighborId in neighbors ){
|
|
|
|
var current = this.sigma.graph.nodes(neighborId);
|
|
|
|
// Si n'est pas deja positionne
|
2016-04-18 15:01:11 +00:00
|
|
|
if( current.x === 0 && current.y === 0 ){
|
2016-04-18 12:56:49 +00:00
|
|
|
// On cherche un angle tant qu'il est pas trop pres d'un deja pris
|
|
|
|
var angle, alreadyUsed = false;
|
|
|
|
do{
|
|
|
|
angle = Math.random()*2*Math.PI;
|
|
|
|
for( var i = 0 ; i < angles.length ; i++ )
|
|
|
|
if( Math.abs(angle-angles[i]) > Math.PI/10 ){
|
|
|
|
alreadyUsed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}while( alreadyUsed );
|
|
|
|
|
|
|
|
current.x = pos.x + this.nodeDistance*Math.cos(angle);
|
|
|
|
current.y = pos.y + this.nodeDistance*Math.sin(angle);
|
|
|
|
this.arrange(current.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.sigma.refresh();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* RENVOIE LA LISTE DES VOISINS (DIRECTS & INDIRECTS) D'UN NOEUD DONNÉ
|
|
|
|
*
|
|
|
|
* @nodeId<String> Id du noeud en question
|
|
|
|
*
|
|
|
|
* @return neighbors<array> Liste des voisins directs+indirects
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.overload.nodeNeighbors = function(nodeId){
|
|
|
|
var neighbors = this.allNeighborsIndex[nodeId];
|
|
|
|
|
|
|
|
// Pile des voisins pour lesquels il faut chercher leurs voisins
|
|
|
|
var stack = [];
|
2016-04-18 15:01:11 +00:00
|
|
|
for( var neighborId in neighbors ) stack.push(neighborId);
|
2016-04-18 12:56:49 +00:00
|
|
|
|
|
|
|
// Tant qu'il reste des voisins a trouver
|
|
|
|
while( stack.length > 0 ){
|
|
|
|
var subneighbors = this.allNeighborsIndex[stack[0]];
|
2016-04-18 15:01:11 +00:00
|
|
|
for( var subId in subneighbors )
|
2016-04-18 12:56:49 +00:00
|
|
|
// Si le voisin est pas deja dans la liste/pile, on l'ajoute a la liste des voisins
|
2016-04-18 15:01:11 +00:00
|
|
|
if( neighbors[subId] === null ){
|
2016-04-18 12:56:49 +00:00
|
|
|
stack.push(subId); // On ajoute a la pile
|
|
|
|
neighbors[subId] = subneighbors[subId]; // On ajoute a la liste complete
|
|
|
|
}
|
|
|
|
|
|
|
|
stack.shift();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// On retourne le resultat
|
|
|
|
return neighbors;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* RENVOIE LA LISTE DES VOISINS DIRECTS D'UN NOEUD DONNÉ
|
|
|
|
*
|
|
|
|
* @nodeId<String> Id du noeud en question
|
|
|
|
*
|
|
|
|
* @return neighbors<array> Liste des voisins directs
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.overload.nodeDirectNeighbors = function(nodeId){
|
|
|
|
return this.allNeighborsIndex[nodeId];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* EXTRAIT LES NOEUDS DE LA RÉPONSE DE L'API
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.extractNodesFromResponse = function(){
|
|
|
|
this.nodes = [];
|
|
|
|
|
|
|
|
// Pour chaque alter
|
|
|
|
for( var i = 0 ; i < this.response.data.alter.length ; i++ )
|
|
|
|
this.nodes.push({
|
|
|
|
'id': 'n-'+this.response.data.alter[i][0],
|
|
|
|
'label': this.response.data.alter[i][1],
|
|
|
|
'x': 0,
|
|
|
|
'y': 0,
|
|
|
|
'size': this.response.data.alter[i][2]
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* EXTRAIT LES NOEUDS DE LA RÉPONSE DE L'API
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.extractEdgesFromResponse = function(){
|
|
|
|
this.edges = [];
|
|
|
|
|
|
|
|
// Pour chaque inter
|
|
|
|
for( var i = 0 ; i < this.response.data.inter.length ; i++ )
|
|
|
|
this.edges.push({
|
|
|
|
'id': 'e-'+this.response.data.inter[i][0]+'-'+this.response.data.inter[i][1],
|
|
|
|
'source': 'n-'+this.response.data.inter[i][0],
|
|
|
|
'target': 'n-'+this.response.data.inter[i][1]
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* AJOUTE LES NOEUDS AU RENDU (SIGMA GRAPH)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.addNodes = function(){
|
|
|
|
/* (5) On ajoute nos noeuds */
|
|
|
|
for( var i = 0 ; i < this.nodes.length ; i++)
|
|
|
|
this.sigma.graph.addNode(this.nodes[i]);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* AJOUTE LES LIENS AU RENDU (SIGMA GRAPH)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.addEdges = function(){
|
|
|
|
/* (6) On ajoute nos liens */
|
|
|
|
for( var i = 0 ; i < this.edges.length ; i++)
|
|
|
|
this.sigma.graph.addEdge(this.edges[i]);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* FONCTION ACTIVÉE LORS DU CLIC SUR UN NOEUD
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.bindings.clickNode = function(thisPtr, e){
|
|
|
|
console.log( thisPtr );
|
|
|
|
var nodeId = e.data.node.id;
|
|
|
|
// On recupere les voisins
|
|
|
|
var neighborNodes = thisPtr.sigma.graph.nodeNeighbors(nodeId);
|
|
|
|
neighborNodes[nodeId] = e.data.node; // on ajoute le noeud clique
|
|
|
|
|
|
|
|
thisPtr.sigma.graph.nodes().forEach(function(n) {
|
2016-04-18 15:01:11 +00:00
|
|
|
if( neighborNodes[n.id] !== null ) n.color = n.originalColor;
|
2016-04-18 12:56:49 +00:00
|
|
|
else n.color = '#eee';
|
|
|
|
});
|
|
|
|
|
|
|
|
thisPtr.sigma.refresh();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* FONCTION ACTIVÉE LORS DU CLIC DANS LE VIDE
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
sociogramClass.prototype.bindings.clickStage = function(thisPtr, e){
|
|
|
|
thisPtr.sigma.graph.nodes().forEach(function(n){ n.color = n.originalColor; });
|
|
|
|
thisPtr.sigma.refresh();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var i,
|
|
|
|
// s,
|
|
|
|
// o,
|
|
|
|
// L = 10,
|
|
|
|
// N = 100,
|
|
|
|
// E = 500,
|
|
|
|
// g = {
|
|
|
|
// nodes: [],
|
|
|
|
// edges: []
|
|
|
|
// },
|
|
|
|
// step = 0;
|
|
|
|
// // Generate a random graph:
|
|
|
|
// for (i = 0; i < N; i++) {
|
|
|
|
// o = {
|
|
|
|
// id: 'n' + i,
|
|
|
|
// label: 'Node ' + i,
|
|
|
|
// circular_x: L * Math.cos(Math.PI * 2 * i / N - Math.PI / 2),
|
|
|
|
// circular_y: L * Math.sin(Math.PI * 2 * i / N - Math.PI / 2),
|
|
|
|
// circular_size: Math.random(),
|
|
|
|
// circular_color: '#' + (
|
|
|
|
// Math.floor(Math.random() * 16777215).toString(16) + '000000'
|
|
|
|
// ).substr(0, 6),
|
|
|
|
// grid_x: i % L,
|
|
|
|
// grid_y: Math.floor(i / L),
|
|
|
|
// grid_size: 1,
|
|
|
|
// grid_color: '#ccc'
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ['x', 'y', 'size', 'color'].forEach(function(val) {
|
|
|
|
// o[val] = o['grid_' + val];
|
|
|
|
// });
|
|
|
|
// g.nodes.push(o);
|
|
|
|
// }
|
|
|
|
// for (i = 0; i < E; i++)
|
|
|
|
// g.edges.push({
|
|
|
|
// id: 'e' + i,
|
|
|
|
// source: 'n' + (Math.random() * N | 0),
|
|
|
|
// target: 'n' + (Math.random() * N | 0),
|
|
|
|
// });
|
|
|
|
// // Instantiate sigma:
|
|
|
|
// s = new sigma({
|
|
|
|
// graph: g,
|
|
|
|
// renderer: { container: document.getElementById('graph-container'), 'type': 'canvas' },
|
|
|
|
// settings: {
|
|
|
|
// animationsTime: 1000
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
|
|
|
|
// setInterval(function(){
|
|
|
|
// var prefix = ['grid_', 'circular_'][step = +!step];
|
|
|
|
// sigma.plugins.animate(
|
|
|
|
// s,
|
|
|
|
// {
|
|
|
|
// x: prefix + 'x',
|
|
|
|
// y: prefix + 'y',
|
|
|
|
// size: prefix + 'size',
|
|
|
|
// color: prefix + 'color'
|
|
|
|
// }
|
|
|
|
// );
|
|
|
|
// }, 10000);
|