not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

717 lines
22 KiB

  1. define(["sugar-web/activity/activity","sugar-web/datastore","notepalette","zoompalette","sugar-web/graphics/presencepalette","humane","tutorial","sugar-web/env"], function (activity,datastore,notepalette,zoompalette,presencepalette,humane,tutorial,env) {
  2. var defaultColor = '#FFF29F';
  3. var isShared = false;
  4. var isHost = false;
  5. var network = null;
  6. var connectedPeople = {};
  7. var xoLogo = '<?xml version="1.0" ?><!DOCTYPE svg PUBLIC \'-//W3C//DTD SVG 1.1//EN\' \'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\' [<!ENTITY stroke_color "#010101"><!ENTITY fill_color "#FFFFFF">]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="stock-xo_1_"><path d="M33.233,35.1l10.102,10.1c0.752,0.75,1.217,1.783,1.217,2.932 c0,2.287-1.855,4.143-4.146,4.143c-1.145,0-2.178-0.463-2.932-1.211L27.372,40.961l-10.1,10.1c-0.75,0.75-1.787,1.211-2.934,1.211 c-2.284,0-4.143-1.854-4.143-4.141c0-1.146,0.465-2.184,1.212-2.934l10.104-10.102L11.409,24.995 c-0.747-0.748-1.212-1.785-1.212-2.93c0-2.289,1.854-4.146,4.146-4.146c1.143,0,2.18,0.465,2.93,1.214l10.099,10.102l10.102-10.103 c0.754-0.749,1.787-1.214,2.934-1.214c2.289,0,4.146,1.856,4.146,4.145c0,1.146-0.467,2.18-1.217,2.932L33.233,35.1z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/><circle cx="27.371" cy="10.849" fill="&fill_color;" r="8.122" stroke="&stroke_color;" stroke-width="3.5"/></g></svg>';
  8. // Manipulate the DOM only when it is ready.
  9. requirejs(['domReady!'], function (doc) {
  10. // Initialize the activity.
  11. activity.setup();
  12. // Handle toolbar mode switch
  13. var currentMode = 0;
  14. var nodetextButton = document.getElementById("nodetext-button");
  15. var removeButton = document.getElementById("delete-button");
  16. var switchMode = function(newMode) {
  17. currentMode = newMode;
  18. nodetextButton.classList.remove('active');
  19. removeButton.classList.remove('active');
  20. saveAndFinishEdit();
  21. if (newMode == 0) nodetextButton.classList.add('active');
  22. else if (newMode == 1) removeButton.classList.add('active');
  23. if (lastSelected != null) {
  24. unselectAllNode();
  25. lastSelected = null;
  26. }
  27. }
  28. nodetextButton.addEventListener('click', function () { switchMode(0); }, true);
  29. removeButton.addEventListener('click', function () { switchMode(1); }, true);
  30. var colorButton = document.getElementById("color-button");
  31. colorPalette = new notepalette.NotePalette(colorButton);
  32. colorPalette.setColor('rgb(255, 242, 159)');
  33. colorPalette.addEventListener('colorChange', function(e) {
  34. if (isSelectedNode(lastSelected)) {
  35. pushState({
  36. redo: {action:"update", id:lastSelected.id(), color: e.detail.color},
  37. undo: {action:"update", id:lastSelected.id(), color: lastSelected.data('background-color')}
  38. });
  39. lastSelected.style('background-color', e.detail.color);
  40. lastSelected.data('background-color', e.detail.color);
  41. }
  42. textValue.style.backgroundColor = e.detail.color;
  43. defaultColor = e.detail.color;
  44. });
  45. var zoomButton = document.getElementById("zoom-button");
  46. zoomPalette = new zoompalette.zoomPalette(zoomButton);
  47. zoomPalette.addEventListener('pop', function(e) {
  48. });
  49. zoomPalette.addEventListener('zoom', function(e) {
  50. var action = e.detail.zoom;
  51. var currentZoom = cy.zoom();
  52. var zoomStep = 0.25;
  53. if (action == 0) {
  54. if (currentZoom != cy.minZoom() && currentZoom-zoomStep > cy.minZoom()) cy.zoom(currentZoom-zoomStep);
  55. } else if (action == 1) {
  56. if (currentZoom != cy.maxZoom()) cy.zoom(currentZoom+zoomStep);
  57. } else if (action == 2) {
  58. cy.fit();
  59. } else if (action == 3) {
  60. cy.center();
  61. }
  62. });
  63. var pngButton = document.getElementById("png-button");
  64. pngButton.addEventListener('click', function(e) {
  65. var inputData = cy.png();
  66. var mimetype = inputData.split(";")[0].split(":")[1];
  67. var type = mimetype.split("/")[0];
  68. var metadata = {
  69. mimetype: mimetype,
  70. title: type.charAt(0).toUpperCase() + type.slice(1) + " Shared Notes",
  71. activity: "org.olpcfrance.MediaViewerActivity",
  72. timestamp: new Date().getTime(),
  73. creation_time: new Date().getTime(),
  74. file_size: 0
  75. };
  76. datastore.create(metadata, function() {
  77. console.log("export done.")
  78. }, inputData);
  79. });
  80. var networkButton = document.getElementById("network-button");
  81. var presence = new presencepalette.PresencePalette(networkButton, undefined);
  82. // Handle graph save/world
  83. var stopButton = document.getElementById("stop-button");
  84. stopButton.addEventListener('click', function (event) {
  85. console.log("writing...");
  86. saveGraph(function (error) {
  87. if (error === null) {
  88. console.log("write done.");
  89. }
  90. else {
  91. console.log("write failed.");
  92. }
  93. });
  94. });
  95. // --- Node and edge handling functions
  96. var defaultFontFamily = "Arial";
  97. var defaultFontSize = 16;
  98. var lastSelected = null;
  99. var defaultText = "<Your content>";
  100. var textValue = document.getElementById("textvalue");
  101. var draggedPosition = null;
  102. textValue.addEventListener('click', function (event) {
  103. saveAndFinishEdit();
  104. });
  105. // Create a new node with text and position
  106. var createNode = function(id, text, position, color) {
  107. cy.add({
  108. group: 'nodes',
  109. nodes: [
  110. {
  111. data: {
  112. id: id,
  113. 'content': text,
  114. 'color': 'rgb(0, 0, 0)',
  115. 'background-color': color
  116. },
  117. position: {
  118. x: position.x,
  119. y: position.y
  120. }
  121. }
  122. ]
  123. });
  124. var newnode = cy.getElementById(id);
  125. newnode.style({
  126. 'content': text,
  127. 'background-color': color
  128. });
  129. newnode.addClass('standard-node');
  130. return newnode;
  131. }
  132. // Update node text and change size
  133. var updateNodeText = function(node, text) {
  134. if (node == null) return;
  135. var previous = node.data('content');
  136. if (text === undefined) text = node.style()['content'];
  137. else node.data('content', text);
  138. node.style({
  139. 'content': text
  140. });
  141. if (previous != text) {
  142. pushState({
  143. redo: {action:"update", id:node.id(), text: text},
  144. undo: {action:"update", id:node.id(), text: previous}
  145. });
  146. }
  147. }
  148. // Test if node is selected
  149. var isSelectedNode = function(node) {
  150. if (node == null) return false;
  151. return node.style()['border-style'] == 'dashed';
  152. }
  153. // Set node as selected
  154. var selectNode = function(node) {
  155. if (node == null) return;
  156. node.style({
  157. 'border-color': 'black',
  158. 'border-style': 'dashed',
  159. 'border-width': '4px'
  160. });
  161. }
  162. // Set node as unselected
  163. var unselectNode = function(node) {
  164. if (node == null) return;
  165. node.style({
  166. 'border-color': 'darkgray',
  167. 'border-style': 'solid',
  168. 'border-width': '1px'
  169. });
  170. }
  171. // Unselect all node
  172. var unselectAllNode = function() {
  173. var nodes = cy.collection("node");
  174. for (var i = 0 ; i < nodes.length ; i++) {
  175. unselectNode(nodes[i]);
  176. }
  177. }
  178. // Delete node, linked edges are removed too
  179. var deleteNode = function(node) {
  180. if (node == null) return;
  181. cy.remove(node);
  182. }
  183. // --- Utility functions
  184. // Show edit field
  185. var showEditField = function(node) {
  186. var position = node.renderedPosition();
  187. var zoom = cy.zoom();
  188. textValue.value = node.data('content');
  189. textValue.style.visibility = "visible";
  190. textValue.style.backgroundColor = node.style().backgroundColor;
  191. var delta = 100 * zoom - 200 * zoom;
  192. textValue.style.left = (position.x + delta) + "px";
  193. textValue.style.top = (55 + position.y + delta) + "px";
  194. textValue.style.width = 190 * zoom + "px";
  195. textValue.style.height = 190 * zoom + "px";
  196. if (textValue.value == defaultText)
  197. textValue.setSelectionRange(0, textValue.value.length);
  198. else
  199. textValue.setSelectionRange(textValue.value.length, textValue.value.length);
  200. textValue.focus();
  201. }
  202. // Hide edit field
  203. var hideEditField = function() {
  204. textvalue.style.visibility = "hidden";
  205. }
  206. var saveAndFinishEdit = function() {
  207. if (lastSelected != null && isSelectedNode(lastSelected)) {
  208. updateNodeText(lastSelected, textValue.value);
  209. hideEditField();
  210. unselectNode(lastSelected);
  211. }
  212. }
  213. // Get center of drawing zone
  214. var getCenter = function() {
  215. var canvas = document.getElementById("canvas");
  216. var center = {x: canvas.clientWidth/2, y: canvas.clientHeight/2};
  217. return center;
  218. }
  219. // Generate a new id
  220. var newId = function() {
  221. var s = [];
  222. var hexDigits = "0123456789abcdef";
  223. for (var i = 0; i < 36; i++) {
  224. s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  225. }
  226. s[14] = "4";
  227. s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
  228. s[8] = s[13] = s[18] = s[23] = "-";
  229. var uuid = s.join("");
  230. return uuid;
  231. }
  232. // Handle an update command from history or from the network
  233. var doAction = function(command) {
  234. if (command.action === undefined) return;
  235. else if (command.action == 'create') {
  236. // Create a new node
  237. createNode(command.id, command.text, command.position, command.color);
  238. } else if (command.action == 'delete') {
  239. // Get the node
  240. var node = cy.getElementById(command.id);
  241. if (node == null) return;
  242. // Delete it
  243. cy.remove(node);
  244. } else if (command.action == 'update') {
  245. // Get the node
  246. var node = cy.getElementById(command.id);
  247. if (node == null) return;
  248. // Update it
  249. if (command.text !== undefined) {
  250. node.data('content', command.text);
  251. node.style({'content': command.text});
  252. }
  253. if (command.color !== undefined) {
  254. node.data('background-color', command.color);
  255. node.style({'background-color': command.color});
  256. }
  257. if (command.position !== undefined) {
  258. node.position({
  259. x: command.position.x,
  260. y: command.position.y
  261. });
  262. }
  263. }
  264. }
  265. // Load graph from datastore
  266. var initGraph = function(graph) {
  267. if (graph == null)
  268. return;
  269. cy.remove("node");
  270. lastSelected = null;
  271. for(var i = 0 ; i < graph.length ; i++) {
  272. doAction(graph[i]);
  273. }
  274. hideEditField();
  275. reinitState();
  276. }
  277. var loadGraph = function() {
  278. var datastoreObject = activity.getDatastoreObject();
  279. datastoreObject.loadAsText(function (error, metadata, data) {
  280. initGraph(data);
  281. });
  282. }
  283. // Save graph to datastore, generate command to rebuild each node
  284. var getGraph = function() {
  285. var commands = [];
  286. var nodes = cy.elements("node");
  287. for(var i = 0; i < nodes.length ; i++) {
  288. var node = nodes[i];
  289. commands.push({
  290. action:"create", id:node.id(), text: node.data("content"), position: {x: node.position().x, y: node.position().y}, color: node.data("background-color")
  291. });
  292. }
  293. return commands;
  294. }
  295. var saveGraph = function(callback) {
  296. var datastoreObject = activity.getDatastoreObject();
  297. var commands = getGraph();
  298. datastoreObject.setDataAsText(commands);
  299. datastoreObject.save(callback);
  300. }
  301. // Do/Undo handling
  302. var stateHistory = [];
  303. var stateIndex = 0;
  304. var maxHistory = 30;
  305. var undoButton = document.getElementById("undo-button");
  306. undoButton.addEventListener('click', function () { saveAndFinishEdit(); undoState(); }, true);
  307. var redoButton = document.getElementById("redo-button");
  308. redoButton.addEventListener('click', function () { saveAndFinishEdit(); redoState(); }, true);
  309. var reinitState = function() {
  310. stateHistory = [];
  311. stateIndex = 0;
  312. }
  313. var pushState = function(state, fromNetwork) {
  314. if (stateIndex < stateHistory.length - 1) {
  315. var stateCopy = [];
  316. for (var i = 0 ; i < stateIndex + 1; i++)
  317. stateCopy.push(stateHistory[i]);
  318. stateHistory = stateCopy;
  319. }
  320. var stateLength = stateHistory.length - 1;
  321. var currentState = state;
  322. if (stateLength < maxHistory) stateHistory.push(currentState);
  323. else {
  324. for (var i = 0 ; i < stateLength ; i++) {
  325. stateHistory[i] = stateHistory[i+1];
  326. }
  327. stateHistory[stateHistory.length-1] = currentState;
  328. }
  329. stateIndex = stateHistory.length - 1;
  330. if (isShared && !fromNetwork) {
  331. sendMessage({
  332. action: 'updateBoard',
  333. data: state
  334. });
  335. }
  336. updateStateButtons();
  337. }
  338. var undoState = function(fromNetwork) {
  339. if (stateHistory.length < 1 || stateIndex < 0) return;
  340. var undo = stateHistory[stateIndex--].undo;
  341. doAction(undo);
  342. if (isShared && !fromNetwork) {
  343. sendMessage({
  344. action: 'undoBoard'
  345. });
  346. }
  347. updateStateButtons();
  348. }
  349. var redoState = function(fromNetwork) {
  350. if (stateIndex+1 >= stateHistory.length) return;
  351. var redo = stateHistory[++stateIndex].redo;
  352. doAction(redo);
  353. if (isShared && !fromNetwork) {
  354. sendMessage({
  355. action: 'redoBoard'
  356. });
  357. }
  358. updateStateButtons();
  359. }
  360. var updateStateButtons = function() {
  361. var stateLength = stateHistory.length;
  362. undoButton.disabled = (stateHistory.length < 1 || stateIndex < 0);
  363. redoButton.disabled = (stateIndex+1 >= stateLength);
  364. }
  365. // Users network list functions
  366. var generateXOLogoWithColor = function(color) {
  367. var coloredLogo = xoLogo;
  368. coloredLogo = coloredLogo.replace("#010101", color.stroke)
  369. coloredLogo = coloredLogo.replace("#FFFFFF", color.fill)
  370. return "data:image/svg+xml;base64," + btoa(coloredLogo);
  371. }
  372. var displayConnectedPeopleHtml = function() {
  373. var presenceUsersDiv = document.getElementById("presence-users");
  374. var html = "<hr><ul style='list-style: none; padding:0;'>"
  375. for (var key in connectedPeople) {
  376. html += "<li><img style='height:30px;' src='" + generateXOLogoWithColor(connectedPeople[key].colorvalue) + "'>" + connectedPeople[key].name + "</li>"
  377. }
  378. html += "</ul>"
  379. presenceUsersDiv.innerHTML = html
  380. }
  381. var displayConnectedPeople = function(users) {
  382. var presenceUsersDiv = document.getElementById("presence-users");
  383. if (!users || !presenceUsersDiv) {
  384. return;
  385. }
  386. network.listSharedActivityUsers(network.getSharedInfo().id, function(usersConnected) {
  387. connectedPeople = {};
  388. for (var i = 0; i < usersConnected.length; i++) {
  389. var userConnected = usersConnected[i];
  390. connectedPeople[userConnected.networkId] = userConnected;
  391. }
  392. displayConnectedPeopleHtml();
  393. });
  394. }
  395. // Handle activity sharing
  396. var shareActivity = function() {
  397. network = activity.getPresenceObject(function(error, network) {
  398. // Unable to join
  399. if (error) {
  400. console.log("error");
  401. return;
  402. }
  403. isShared = true;
  404. // Store settings
  405. userSettings = network.getUserInfo();
  406. console.log("connected");
  407. // Not found, create a new shared activity
  408. if (!window.top.sugar.environment.sharedId) {
  409. isHost = true;
  410. network.createSharedActivity('org.olpcfrance.sharednotes', function(groupId) {});
  411. }
  412. // Show a disconnected message when the WebSocket is closed.
  413. network.onConnectionClosed(function(event) {
  414. console.log(event);
  415. console.log("Connection closed");
  416. });
  417. // Display connection changed
  418. network.onSharedActivityUserChanged(function(msg) {
  419. onNetworkUserChanged(msg);
  420. });
  421. // Handle messages received
  422. network.onDataReceived(onNetworkDataReceived);
  423. });
  424. }
  425. var sendMessage = function(content) {
  426. try {
  427. network.sendMessage(network.getSharedInfo().id, {
  428. user: network.getUserInfo(),
  429. content: content
  430. });
  431. } catch (e) {}
  432. }
  433. var onNetworkDataReceived = function(msg) {
  434. // Ignore messages coming from ourselves
  435. if (network.getUserInfo().networkId === msg.user.networkId) {
  436. return;
  437. }
  438. switch (msg.content.action) {
  439. case 'initialBoard':
  440. // Receive initial board from the host
  441. initGraph(msg.content.data);
  442. break;
  443. case 'updateBoard':
  444. // Board change received
  445. doAction(msg.content.data.redo);
  446. pushState(msg.content.data, true);
  447. break
  448. case 'undoBoard':
  449. // Undo board
  450. undoState(true);
  451. break;
  452. case 'redoBoard':
  453. // Redo board
  454. redoState(true);
  455. break;
  456. }
  457. }
  458. var onNetworkUserChanged = function(msg) {
  459. var userName = msg.user.name.replace('<', '&lt;').replace('>', '&gt;');
  460. var html = "<img style='height:30px;' src='" + generateXOLogoWithColor(msg.user.colorvalue) + "'>";
  461. if (msg.move === 1) {
  462. humane.log(html + l10n_s.get("PlayerJoin",{user: userName}));
  463. if (isHost) {
  464. sendMessage({
  465. action: 'initialBoard',
  466. data: getGraph()
  467. });
  468. }
  469. } else if (msg.move === -1) {
  470. humane.log(html + l10n_s.get("PlayerLeave",{user: userName}));
  471. }
  472. network.listSharedActivities(function(activities) {
  473. for (var i = 0; i < activities.length; i++) {
  474. if (activities[i].id === network.getSharedInfo().id) {
  475. displayConnectedPeople(activities[i].users);
  476. }
  477. }
  478. });
  479. }
  480. // Handle presence palette
  481. presence.addEventListener('shared', function() {
  482. presence.popDown();
  483. shareActivity();
  484. });
  485. if (window.top && window.top.sugar && window.top.sugar.environment && window.top.sugar.environment.sharedId) {
  486. shareActivity();
  487. presence.setShared(true);
  488. }
  489. // Handle help
  490. var helpButton = document.getElementById("help-button");
  491. tutorial.setElement("activity", document.getElementById("activity-button"));
  492. tutorial.setElement("title", document.getElementById("title"));
  493. tutorial.setElement("network", networkButton);
  494. tutorial.setElement("help", helpButton);
  495. tutorial.setElement("shared", document.getElementById("shared-button"));
  496. tutorial.setElement("png", pngButton);
  497. tutorial.setElement("zoom", zoomButton);
  498. tutorial.setElement("color", colorButton);
  499. tutorial.setElement("add", nodetextButton);
  500. tutorial.setElement("remove", removeButton);
  501. tutorial.setElement("undo", document.getElementById("undo-button"));
  502. tutorial.setElement("redo", document.getElementById("redo-button"));
  503. tutorial.setElement("stop", stopButton);
  504. tutorial.setElement("node", document.getElementById('canvas'));
  505. helpButton.addEventListener('click', function (event) {
  506. tutorial.start();
  507. });
  508. env.getEnvironment(function(err, environment) {
  509. // Initial tutorial coming from home view
  510. if (environment.help) {
  511. setTimeout(function() {
  512. tutorial.start(tutorial.tourInit);
  513. }, 500);
  514. }
  515. });
  516. // Handle localization
  517. window.addEventListener('localized', function() {
  518. env.getEnvironment(function(err, environment) {
  519. var defaultLanguage = (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) ? chrome.i18n.getUILanguage() : navigator.language;
  520. var language = environment.user ? environment.user.language : defaultLanguage;
  521. if (l10n_s.language.code != language) {
  522. l10n_s.language.code = language;
  523. };
  524. var oldDefaultText = defaultText;
  525. defaultText = l10n_s.get("YourNewIdea");
  526. nodetextButton.title = l10n_s.get("nodetextTitle");
  527. removeButton.title = l10n_s.get("removeButtonTitle");
  528. undoButton.title = l10n_s.get("undoButtonTitle");
  529. redoButton.title = l10n_s.get("redoButtonTitle");
  530. zoomButton.title = l10n_s.get("zoomButtonTitle");
  531. pngButton.title = l10n_s.get("pngButtonTitle");
  532. networkButton.title = l10n_s.get("networkButtonTitle");
  533. helpButton.title = l10n_s.get("helpButtonTitle");
  534. if (cy) {
  535. var nodes = cy.elements("node");
  536. for(var i = 0; i < nodes.length ; i++) {
  537. var node = nodes[i];
  538. if (node.data('content') == oldDefaultText) {
  539. node.data('content', defaultText);
  540. node.style({'content': defaultText});
  541. }
  542. }
  543. if (textValue && textValue.value == oldDefaultText) {
  544. textValue.value = defaultText;
  545. if (lastSelected) {
  546. showEditField(lastSelected);
  547. }
  548. }
  549. }
  550. });
  551. }, false);
  552. // --- Cytoscape handling
  553. // Initialize board
  554. cy = cytoscape({
  555. container: document.getElementById('cy'),
  556. ready: function() {
  557. // Create first node and select id
  558. cy = this;
  559. var firstNode = createNode(newId(), defaultText, getCenter(), defaultColor);
  560. pushState({
  561. redo: {action:"create", id:firstNode.id(), text: firstNode.data("content"), position: {x: firstNode.position().x, y: firstNode.position().y}, color: defaultColor},
  562. undo: {action:"delete", id:firstNode.id()}
  563. });
  564. firstNode.select();
  565. selectNode(firstNode);
  566. showEditField(firstNode);
  567. lastSelected = firstNode;
  568. // Load world
  569. loadGraph();
  570. },
  571. style: [
  572. {
  573. selector: '.standard-node',
  574. css: {
  575. 'width': '200px',
  576. 'height': '200px',
  577. 'text-valign': 'center',
  578. 'text-halign': 'center',
  579. 'border-color': 'darkgray',
  580. 'border-width': '1px',
  581. 'background-color': defaultColor,
  582. 'text-wrap': 'wrap',
  583. 'text-max-width': '200px',
  584. 'shadow-color': 'black',
  585. 'shadow-offset-x': '4px',
  586. 'shadow-offset-y': '4px',
  587. 'shadow-opacity': '0.5',
  588. 'shape': 'rectangle'
  589. }
  590. }
  591. ]
  592. });
  593. // Event: a node is selected
  594. cy.on('tap', 'node', function() {
  595. if (currentMode == 1) {
  596. pushState({
  597. redo: {action:"delete", id:this.id()},
  598. undo: {action:"create", id:this.id(), text: this.data("content"), position: {x: this.position().x, y: this.position().y}, color: defaultColor}
  599. });
  600. deleteNode(this);
  601. if (lastSelected == this) lastSelected = null;
  602. return;
  603. } else {
  604. if (!isSelectedNode(this)) {
  605. if (lastSelected != null) {
  606. updateNodeText(lastSelected, textValue.value);
  607. unselectNode(lastSelected);
  608. }
  609. selectNode(this);
  610. showEditField(this);
  611. }
  612. lastSelected = this;
  613. }
  614. });
  615. // Event: a node is unselected
  616. cy.on('unselect', 'node', function() {
  617. saveAndFinishEdit();
  618. unselectNode(this);
  619. });
  620. // Event: tap on the board
  621. cy.on('tap', function(e){
  622. if (e.cyTarget === cy) {
  623. if (currentMode == 0) {
  624. var newNode = createNode(newId(), defaultText, e.cyPosition, defaultColor);
  625. pushState({
  626. redo: {action:"create", id:newNode.id(), text: newNode.data("content"), position: {x: newNode.position().x, y: newNode.position().y}, color: defaultColor},
  627. undo: {action:"delete", id:newNode.id()}
  628. });
  629. newNode.select();
  630. selectNode(newNode);
  631. showEditField(newNode);
  632. lastSelected = newNode;
  633. }
  634. }
  635. });
  636. // Event: elements moved
  637. cy.on('drag', 'node', function(e) {
  638. saveAndFinishEdit();
  639. if (draggedPosition == null) {
  640. draggedPosition = {x: this.position().x, y: this.position().y};
  641. }
  642. });
  643. cy.on('free', 'node', function(e) {
  644. if (draggedPosition != null && (this.position().x != draggedPosition.x || this.position().y != draggedPosition.y)) {
  645. pushState({
  646. redo: {action:"update", id:this.id(), position: {x: this.position().x, y: this.position().y}},
  647. undo: {action:"update", id:this.id(), position: {x: draggedPosition.x, y: draggedPosition.y}}
  648. });
  649. }
  650. draggedPosition = null;
  651. });
  652. // Event: zoom
  653. cy.on('zoom', function() {
  654. saveAndFinishEdit();
  655. });
  656. // Event: move
  657. cy.on('pan', function() {
  658. saveAndFinishEdit();
  659. });
  660. });
  661. });