TiledViz
Loading...
Searching...
No Matches
grid.js
1try{
2$(document).off('touchstart');
3$(document).off('touchmove');
4$(document).off('touchend');
5$(window).off('touchstart');
6$(window).off('touchmove');
7$(window).off('touchend');
8$(window).off("contextmenu");
9$(document.body).off('touchmove');
10$(window).css("touch-action","none");
11
12$(document).ready( function(){
13 $(window).on('touchmove', function(e)
14 {
15 e.preventDefault();
16 });
17
18 socket = io.connect('https://' + document.domain); // + ':' + location.port);
19 // //debug socket.io :
20 // localStorage.debug = '*';
21 console.log("my session is :", my_session);
22 socket.on("connect", function(){
23 var cdata = {"user": my_user, "project" : my_project, "session": my_session };
24 socket.emit("connected_grid",
25 cdata,
26 function(sdata){
27 console.log(sdata);
28 console.log("Socket connection running: ID:" + sdata["session_id"]+", room: "+
29 sdata["room"]);
30
31 });
32 });
33
34 $("#notifications").html("Here is the grid. You can get help with '?' button.")
35
36 socket.on("disconnect", (reason) => {
37 if (reason === "io server disconnect") {
38 // the disconnection was initiated by the server, you need to reconnect manually
39 console.log("The server has forcefully disconnected the socket");
40 socket.connect();
41 // else the socket will automatically try to reconnect
42 } else if (reason === "io client disconnect") {
43 console.log("The socket was manually disconnected.");
44 } else if (reason === "ping timeout") {
45 console.log("The server did not send a PING within the pingInterval + pingTimeout range.");
46 } else if (reason === "transport close") {
47 console.log("The connection was closed.");
48 } else if (reason === "transport error") {
49 console.log("The connection has encountered an error.");
50 }
51 });
52
53 var update_client_number = function(new_nbr){
54 var new_text = "";
55 if (new_nbr == 0){ // 0: first client (socket not yet created), 1: refresh
56 new_text = "first";
57 }
58 else if (new_nbr == 1){
59 new_text = "only one participant";
60 }
61 else{
62 var tmp_nbr = new_nbr - 1;
63 new_text = "with " + new_nbr + " other participants";
64 }
65 $('#join-clients').text(new_text);
66 console.log("Session participants: " + $('#join-clients').text());
67
68 };
69
70 htmlPrimaryParent = $('#primaryparent'); // CSS in style.css
71 primaryparent=document.getElementById("primaryparent")
72
73 // Check presence of the config file
74 try{
75 lang=json_config.language;
76 console.log(lang=="FR" ? "FR selected" : "EN selected" );
77 htmlPrimaryParent.off('mouseleave');
78 htmlPrimaryParent.off('mouseenter');
79 htmlPrimaryParent.off('mousedown');
80 htmlPrimaryParent.off('mouseup');
81 htmlPrimaryParent.off('touchstart');
82 htmlPrimaryParent.off('touchmove');
83 htmlPrimaryParent.off('touchend');
84 }
85 catch(err){
86 htmlPrimaryParent.css({
87 margin : 0,
88 padding : 0,
89 overflow : "auto"
90 });
91 htmlPrimaryParent.append('<div id=warning class="warning blink"></br></br>'+
92 'Warning, a configuration file is missing! You should have either a <b>config_default.js</b> in the folder TileViz,'+
93 ' or a <b>config.js</b> in the parent repository!</div>');
94 $('#header').css('height', 0);
95 console.log("error catched");
96 warn = $('#warning');
97 warn.css({
98 top : -250,
99 left : 0,
100 width : window.innerWidth,
101 height : 1500,
102 zIndex : 999,
103 backgroundColor : "white",
104 fontSize : 100,
105 padding : 100,
106 //verticalAlign : "middle",
107 });
108 }
109
110 // Marge from top
111 TopPP = htmlPrimaryParent.css("marginTop");
112
113 // Show new client connection
114 socket.on('new_client', function(sdata){
115 console.log(sdata, "new client");
116 update_client_number(sdata['part_nbr_update'])
117 htmlPrimaryParent.click();
118 });
119
120 // Split geometry for Anonymous user
121 console.log("my_geometry =",my_geometry);
122 get_newwindowRatio = function() {
123 if ( my_geometry["DISPLAYWIDTH"] === undefined) {
124 var grid_width = parseInt($('#main-grid').css("width"));
125 //var grid_height = parseInt($('#main-grid').css("height"));
126 var grid_height = window.innerHeight; // TODO: Find a better solution, suitable to various displays
127 my_window={"left":0,
128 "right":window.innerWidth,
129 "top":0,
130 "down":window.innerHeight}
131 } else {
132 displaywidth=parseInt(my_geometry["DISPLAYWIDTH"]);
133 displayheight=parseInt(my_geometry["DISPLAYHEIGHT"]);
134 shiftleft=parseInt(my_geometry["SHIFTLEFT"]);
135 shifttop=parseInt(my_geometry["SHIFTTOP"]);
136 var grid_width = parseInt(my_geometry["WALLWIDTH"]);
137 var grid_height = parseInt(my_geometry["WALLHEIGHT"])
138
139 // padding = 20%
140 padding_left=displaywidth/5;
141 padding_top=displayheight/5;
142 my_window={"left":-shiftleft-padding_left,
143 "right":-shiftleft+displaywidth+padding_left,
144 "top":-shifttop-padding_top,
145 "down":-shifttop+displayheight+padding_top}
146 }
147 console.log("Window computed with my_geometry : ",my_window)
148 try{
149 // Use to compute exact positions of tiles in the window for hiding some out of the real viewport.
150 relativeLeft=parseInt(htmlPrimaryParent.css('width'))/2;
151 //htmlPrimaryParent.cssClip()
152 if ( me.getSpread().X > 3840 )
153 relativeLeft=parseInt(relativeLeft/2);
154 } catch(e) {
155 relativeLeft=0
156 console.log("Error : init relativeLeft "+e.toString())
157 }
158 }
159
160 let removePixelRatio = null;
161
162 const updatePixelRatio = () => {
163 if(removePixelRatio != null) {
164 removePixelRatio();
165 }
166 let PixelRatioReq = `(resolution: ${window.devicePixelRatio}dppx)`;
167 let media = matchMedia(PixelRatioReq);
168 media.addListener(updatePixelRatio);
169
170 get_newwindowRatio();
171 console.log("detect new PixelRatio: " + window.devicePixelRatio);
172 removePixelRatio = function() {media.removeListener(updatePixelRatio)};
173 }
174 updatePixelRatio();
175
176 /* LIST OF USEFUL PARAMETERS
177 Use a research (Ctrl + F) to find related variable on the code
178
179 IMPORTANT: all parameters loaded with "config…" are defined in the corresponding part of config_default.js or config.js in the TileVizCases folder.
180
181 #001 Use json data to place the node on screen
182 #002 Define colors avalaible for the legend of the filter, the size of the table limits the number of search
183 #003 Define global parameters for the PostIt module
184 #004 Individual parameters of all node's postit
185 #005 Load all the data on start or only nodes visible on screen
186 #006 Number of nodes on the mesh
187 #007 Choose to fix the number of columns
188 #008 This number is the maximal numbers of columns if the number of columns is not fixed
189 or this number is the numbers of columns if the number of columns is fixed
190 #009 Set the lateral gap between two nodes
191 #010 Set the vertical gap between two nodes
192 #011 Size of the target zone for drag and drop
193 #012 Bool to show the zoom slider for primary section (table of tiles)
194 #013 When you choose an option, other options are automatically disabled
195 */
196
197 //Global variables
198
199 //#001
200 useJsonDataLocation=configJsonData.useLocation; //Use the json data to set location of the node
201
208 colorFilterStickersTab = configColors.FilterStickersTab;
209 colorTagStickersTab = configColors.TagStickersTab; // 10 colors
210 attributedTagsColorsArray = new Array();
211 globalTagsList =[];
212 globalTagsColors ={};
213 globalFloatingTags = {};
214 currentSelectedTag = "";
215 colorHBdefault = configColors.HBdefault;
216 colorHBonfocus = configColors.HBonfocus;
217 colorHBselected = configColors.HBselected;
218 colorHBtoZoom = configColors.HBtoZoom;
219
220
221
222 // Manipulation to get the RGB conversation of HBselected, HBtoZoom.
223 htmlPrimaryParent.append('<div id=color></div>');
224 $('#color').css('background-color', colorHBselected);
225 colorHBselectedRGB = $('#color').css('background-color');
226 $('#color').css('background-color', colorHBtoZoom);
227 colorHBtoZoomRGB = $('#color').css('background-color');
228 $('#color').remove();
229
233 $.fn.postitall.globals = {
234 prefix : '#',//Id note prefixe
235 filter : 'domain', //Options: domain, page, all
236 savable : false, //Save postit in storage
237 randomColor : false, //Random color in new postits
238 toolbar : true, //Show or hide toolbar
239 autoHideToolBar : true, //Animation effect on hover over postit showing/hiding toolbar options
240 removable : true, //Set removable feature on or off
241 askOnDelete : true, //Confirmation before note removal
242 draggable : false, //Set draggable feature on or off
243 resizable : true, //Set resizable feature on or off
244 editable : true, //Set contenteditable and enable changing note content
245 changeoptions : true, //Set options feature on or off
246 blocked : true, //Postit can not be modified
247 minimized : true, //true = minimized, false = maximixed
248 expand : false, //Expand note
249 fixed : true, //Allow to fix the note in page
250 addNew : true, //Create a new postit
251 showInfo : false, //Show info icon
252 pasteHtml : true, //Allow paste html in contenteditor
253 htmlEditor : true, //Html editor (trumbowyg)
254 autoPosition : false, //Automatic reposition of the notes when user resizes the screen
255 addArColumn : 'back' //Add an arColumn to notes : none, front, back, all
256 };
257
258 //#005
259 chargeAllContentOnStart = configBehaviour.loadAllContentOnStart;
260
261 //#005-2
262 //if distance_from_the_bottom_for_loading = 0 : nodes are loaded only when they start to be visible on the screen
263 distance_from_the_bottom_for_loading = parseInt(window.outerHeight);
264 // distance_from_the_bottom_for_loading = parseInt(window.innerHeight); this will load the node where their top-left coin is distant from down of the screen from under "distance_for_loading" px
265 //
266 // TODO : add "distance_from_the_top_for_unloading = ?" to unload nodes that are not visible anymore ?
267 //
268
269 //#006 Number of nodes on the mesh
270 mesh_cardinal = jsDataTab.length;
271 //mesh_cardinal = configBehaviour.meshCardinal; // for testing purposes !
272 //#007
273 bool_fix_number_of_columns = configBehaviour.fixColumnNumber; // True seems to not behave properly when running on WildOS :'(
274 //#008
275 maximal_number_of_columns = configBehaviour.maxNumOfColumns;
276 //#009
277 gapBetweenColumns = configBehaviour.spaceBetweenColumns;
278 //#010
279 gapBetweenLines = configBehaviour.spaceBetweenLines;
280
281 //#011 size of the targetZone for drag and drop
282 //Must be ( targetSize > 2) strict inequality is very important | the target a targetSize value close to 2 means : the size of the drop zone is equal to the size of an node
283 //The target zone is a rectangle centered on top-left of the target node.
284 // See Mesh -> RealdropNode function.
285 targetSize = configBehaviour.targetSize;
286
287 //#012 Bool to show the zoom slider for primary section (table of tiles) :
288 // WARNING : set this to True can give serious disfunctions when moving tiles.
289 primaryZoomSlider = configBehaviour.primaryZoomSlider;
290
291 //#013
292 only_one_option = configBehaviour.onlyOneOption;
293
294 // Initial zoom for grid :
295 if ( primaryZoomSlider ) {
296 $('#primarySliderLabel').show();
297 $('#primarySlider').show();
298 $('#primarySlider').click();
299 } else configBehaviour.primaryZoomSlider=false;
300
301 // MODAL
302 is_new_client_active = true // DEFAULT behavior
303 $('[id*=_new_client]').on("click", function(event){ // To select both buttons (active and passive)
304 console.log("click new client "+this.id);
305 if (this.id.match("active")) {
306 is_new_client_active = true;
307 } else if (this.id.match("passive")) {
308 is_new_client_active = false;
309 } else {
310 is_new_client_active = undefined;
311 }
312
313 $('[id*=_new_client]').removeClass("btn-selected");
314 $('#'+this.id).addClass("btn-selected");
315 });
316
317 $('#getUrl').on("click", function(){
318 // Récupérer les valeurs des champs de la modale
319 var max_uses = parseInt($('#max_uses').val());
320 var invitee_name = $('#invitee_name').val();
321
322 // Validation de max_uses
323 if (isNaN(max_uses) || max_uses < 1) {
324 $('#invite-error').text('Maximum uses must be a positive number').show();
325 return;
326 }
327
328 // Validation du nom de l'invité pour les liens actifs
329 if (is_new_client_active && (!invitee_name || invitee_name.trim() === '')) {
330 $('#invite-error').text('Please enter the invitee\'s username for active links').show();
331 return;
332 }
333
334 // Construction de l'objet à envoyer
335 cdata = {
336 "type": is_new_client_active,
337 "session": my_session,
338 "user": my_user,
339 "max_uses": max_uses,
340 "invitee_name": invitee_name
341 };
342 console.log("get url clicked", cdata);
343 socket.emit("get_link", cdata);
344 });
345 socket.on("get_link_back", function(sdata){
346 if (sdata.error) {
347 $('#invite-error').text(sdata.error).show();
348 return;
349 }
350 console.log("i am the new link", sdata["link"]);
351 $('#custom-url').val(sdata["link"]);
352 $('#invite-error').hide();
353 });
354
355 // TODO: get user input on best way to share URL: mail? copy/paste? QR code?
356 //$('#copy-button').tooltip();
357 $('#copy-button').on("click", function(){
358 var input = document.querySelector("#custom-url");
359 input.setSelectionRange(0, input.value.length + 1);
360 try {
361 var success = navigator.clipboard ? navigator.clipboard.writeText(input.value) : document.execCommand("copy");
362 if (success) {
363 console.log("copy success");
364 $('#invite-error').text('Copy done!').removeClass("alert-danger").show();
365 } else {
366 console.log("copy error");
367 }
368
369 } catch (err) {
370 console.log(err);
371 }
372 });
373
374 var cleanModal = function(){
375 $('#custom-url').val("URL");
376 console.log("url resetted");
377 };
378 $('#closeModal').on({
379 click: cleanModal
380 });
381
382 // Menu shared between clients
383 socket.on('receive_Menu_click', function(sdata){
384 console.log("receive_Menu_click",sdata);
385 if ($('#menu'+sdata.Menu+'>.'+sdata.optionButton).length > 0) {
386 var thisoption=$('#menu'+sdata.Menu+'>.'+sdata.optionButton);
387 } else {
388 var opt=sdata.optionButton;
389 var thisoption=$('#menu'+sdata.Menu+'>.close'+opt[0].toUpperCase()+opt.substring(1));
390 }
391 //$('#'+sdata.Menu+'option'+sdata.optionNumber);
392 var listClass=thisoption.attr("class").split(" ");
393 if ( listClass.find(function(s) {return s.toLowerCase().indexOf(sdata.optionButton.toLowerCase()) != -1 }) != undefined) {
394 thisoption.addClass("NotSharedAgain");
395 thisoption.click();
396 thisoption.removeClass("NotSharedAgain");
397 } else {
398 var numOfEvents=$('#menu'+sdata.Menu).children().length;
399 for(var optionNumber=0; optionNumber<numOfEvents; optionNumber++) {
400 thisoption=$('#'+sdata.Menu+'option'+optionNumber);
401 listClass=thisoption.attr("class").split(" ");
402 if (listClass.find(function(s) {return s.toLowerCase().indexOf(sdata.optionButton.toLowerCase()) != -1 }) != undefined) {
403 thisoption.addClass("NotSharedAgain");
404 thisoption.click();
405 thisoption.removeClass("NotSharedAgain");
406 break;
407 }
408 }
409 }
410 });
411
412 // Global click actions shared between clients
413 socket.on('receive_click', function(sdata){
414 console.log("receive_click",sdata);
415 $('.'+sdata.action+'#'+sdata.id).addClass("NotSharedAgain");
416 $('.'+sdata.action+'#'+sdata.id).click();
417 $('.'+sdata.action+'#'+sdata.id).removeClass("NotSharedAgain");
418 });
419 // emit function used to send shared clicks
420 emit_click=function(action,id) {
421 if (! $('.'+action+'#'+id).hasClass("NotSharedAgain") && my_user != "Anonymous" ) {
422 cdata={"room":my_session,"id":id,"action":action};
423 socket.emit("click", cdata, callback=function(sdata){
424 console.log("socket send click ", cdata);
425 });
426 return true;
427 } else
428 return false
429 }
430
431 // Global click actions shared between clients
432 socket.on('receive_click_val', function(sdata){
433 console.log("receive_click_val",sdata);
434 $('.'+sdata.action+'#'+sdata.id).addClass("NotSharedAgain");
435 $('.'+sdata.action+'#'+sdata.id).val(sdata["val"]);
436 $('.'+sdata.action+'#'+sdata.id).change();
437 $('.'+sdata.action+'#'+sdata.id).click();
438 $('.'+sdata.action+'#'+sdata.id).removeClass("NotSharedAgain");
439 });
440
441 // emit function used to send shared clicks
442 emit_click_val=function(action,id,val) {
443 if (! $('.'+action+'#'+id).hasClass("NotSharedAgain") && my_user != "Anonymous" ) {
444 cdata={"room":my_session,"id":id,"action":action,"val":val};
445 socket.emit("click_val", cdata, callback=function(sdata){
446 console.log("socket send click_val ", cdata);
447 });
448 return true;
449 } else
450 return false
451 }
452
453 // Change opacity shared between clients
454 socket.on('receive_Force_Opacity', function(sdata){
455 console.log("receive_Force_Opacity",sdata);
456 id=sdata["Id"];
457 opacity=sdata["Opacity"];
458 $('#tileOpacitySlider'+id).val(opacity*100);
459 $('#tileOpacitySlider'+id).change();
460 $('#'+id).css("opacity", opacity);
461 });
462
463 // emit function used to create new ta
464 receive_Add_Tag=false
465 emit_newTag=function(option,newTag) {
466 if (! $('#'+option).hasClass("NotSharedAgain") && my_user != "Anonymous" ) {
467 receive_Add_Tag=false;
468 cdata={"room":my_session,"option":option,"NewTag":newTag};
469 socket.emit("add_Tag", cdata, callback=function(sdata){
470 console.log("Emit add new tags : ", cdata);
471 });
472 $('#'+option).addClass("NotSharedAgain")
473 return true;
474 } else
475 return false
476 }
477
478 // Add new tag
479 received_newtag="";
480 socket.on('receive_Add_Tag', function(sdata){
481 console.log("receive_Add_Tag",sdata);
482 $('#'+sdata.option).addClass("NotSharedAgain");
483 received_newtag=sdata["NewTag"];
484 mesh.AddNewTag(received_newtag);
485 receive_Add_Tag=true;
486 $('#'+sdata.option).removeClass("NotSharedAgain");
487 });
488
489 // Add new color for a tag
490 socket.on('receive_Color_Tag', function(sdata){
491 console.log("receive_Color_Tag",sdata);
492 mesh.ChangeColorTag(sdata["OldTag"],sdata["TagColor"])
493 });
494
495 socket.on('receive_deploy_Session', function(sdata){
496 console.log("receive_deploy_Session",sdata);
497 $('#gGnewroom').val(sdata['NewRoom']);
498 $('#notifications').html('<div id=ValidateNewRoom height="10%" width="50%" style="font-size:100" ></div>');
499 $("#ValidateNewRoom").append('<h1> Are you OK to change for this room ? Yes = click "Submit"</h1>'
500 +'<button id="ChangeRoomNo" name="ChangeRoomNo" class="btn btn-info" >No</button>');
501 $("#ChangeRoomNo").on("click",function() {
502 $("#ValidateNewRoom").remove()
503 })
504 if ( my_user == "Anonymous" ) {
505 $('#gGsubmit').click()
506 }
507 });
508
509 // global bool initialized for buttonShare to deploy config but not on the user that have emited the signal.
510 myOwnConfig=false;
511 socket.on('receive_deploy_Config', function(sdata){
512 console.log("receive_deploy_Config",sdata);
513 if (myOwnConfig) {
514 myOwnConfig=false;
515 return
516 }
517 $('#notifications').html('<div id=ValidateNewConfig height="10%" width="50%" style="font-size:100" ></div>');
518 $("#ValidateNewConfig").append('<h1> Are you OK to change for the new config ?</h1>'
519 +'<button id="ChangeConfigYes" name="ChangeConfigYes" class="btn btn-info" >Yes</button>'
520 +'<button id="ChangeConfigNo" name="ChangeConfigNo" class="btn btn-info" >No</button>');
521 $("#ChangeConfigYes").on("click",function() {
522 configJson=sdata["Config"];
523 UpdateParameters(configJson);
524 $("#ValidateNewConfig").remove()
525 })
526 $("#ChangeConfigNo").on("click",function() {
527 $("#ValidateNewConfig").remove()
528 })
529 if ( my_user == "Anonymous" ) {
530 $('#ChangeConfigYes').click()
531 }
532 });
533
534 //Launch the mesh
535 mesh = new Mesh(mesh_cardinal,bool_fix_number_of_columns,maximal_number_of_columns);
536 mesh.meshEventStart();
537 mesh.startLoading();
538 $('body').css('background-image','url(../static/images/fond.png)');
539 // suggestion_list from tags (this next line), comments, titles, variables, ...
540 suggestion_list = globalTagsList
541 .concat($('.info').map(function(){ return $.trim($(this).text());}).get())
542 .concat(me.getNodesByLoc().map(function(e){return $.trim(e.getJsonData().comment); }));
543 // if ( my_user == "Anonymous" ) {
544 // MinPP=Math.min.apply(Math, $('.node').map(function(){ return parseInt($(this).css("top")); }).get());
545 // console.log("min y tile ",MinPP)
546 // MinPP=-MinPP/me.getNumOfColumns()/my_geometry.APPSCALE;
547 // console.log("min y tile with resize ",MinPP)
548 // //MinPP=-(MinPP*window.devicePixelRatio)/$('#0').height();
549 // MinPP=MinPP + ( $('#primaryparent').position().top + parseInt($('#primaryparent').css("marginTop")) );
550 // console.log("min y tile with shift ",MinPP)
551 // console.log("grid height ",grid_height)
552 // if (MinPP < grid_height) {
553 // TopPP=(parseInt(TopPP)+grid_height-MinPP);
554 // TopPP=1800
555 // htmlPrimaryParent.css("marginTop",TopPP+"px");
556 // }
557 //TopPP=1200;
558 //htmlPrimaryParent.css("marginTop",TopPP+"px");
559 TopPP = 0;
560 // } else {
561 // TopPP = 0;
562 // }
563
564 get_newwindowRatio();
565
566});
567
568} catch(err){
569 console.log(err,true);
570}