Commit 564116c2 authored by Jérome Perrin's avatar Jérome Perrin

update to new jsplumb gadget from DREAM (without patches)

parent 5f06ae20
......@@ -8,7 +8,7 @@
<dictionary>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts17764628.88</string> </value>
<value> <string>ts18282338.36</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
......@@ -41,28 +41,25 @@
* along with DREAM. If not, see <http://www.gnu.org/licenses/>.\n
* ==========================================================================*/\n
/*global RSVP, rJS, $, jsPlumb, Handlebars,\n
loopEventListener, promiseEventListener, DOMParser, confirm */\n
loopEventListener, promiseEventListener, DOMParser */\n
/*jslint unparam: true todo: true */\n
(function(RSVP, rJS, $, jsPlumb, Handlebars, loopEventListener, promiseEventListener, DOMParser) {\n
"use strict";\n
/* TODO:\n
* use services\n
* drop jquery ui dependency\n
* less dependancies ( promise event listner ? )\n
* document exposed css / jsplumb config\n
* no more handlebars\n
* accept ERP5 format\n
* auto springy layout\n
* drop zoom level\n
* edge edit popup on click\n
* rename draggable()\n
* somehow choose edge class on connect\n
* allow changing node and edge class\n
* factorize node & edge popup edition\n
*/\n
/*jslint nomen: true */\n
var gadget_klass = rJS(window),\n
domParser = new DOMParser(),\n
node_template_source = gadget_klass.__template_element.getElementById("node-template").innerHTML,\n
node_template = Handlebars.compile(node_template_source),\n
popup_edit_template = gadget_klass.__template_element.getElementById("popup-edit-template");\n
\n
var gadget_klass = rJS(window), node_template_source = gadget_klass.__template_element.getElementById("node-template").innerHTML, node_template = Handlebars.compile(node_template_source), popup_edit_template = gadget_klass.__template_element.getElementById("popup-edit-template"), domParser = new DOMParser();\n
function loopJsplumbBind(gadget, type, callback) {\n
//////////////////////////\n
// Infinite event listener (promise is never resolved)\n
......@@ -92,6 +89,7 @@
reject(error);\n
}\n
});\n
return callback_promise;\n
};\n
jsplumb_instance.bind(type, handle_event_callback);\n
}\n
......@@ -124,12 +122,21 @@
}\n
return "DreamNode_" + n;\n
}\n
function updateConnectionData(gadget, connection, remove, edge_data) {\n
function getDefaultEdgeClass(gadget) {\n
// TODO: use first edge class available in class_definition\n
return "Dream.Edge";\n
}\n
function updateConnectionData(gadget, connection, remove) {\n
if (connection.ignoreEvent) {\n
// this hack is for edge edition. Maybe there I missed one thing and\n
// there is a better way.\n
return;\n
}\n
if (remove) {\n
delete gadget.props.data.graph.edge[connection.id];\n
} else {\n
edge_data = edge_data || gadget.props.data.graph.edge[connection.id] || {\n
_class: "Dream.Edge"\n
var edge_data = gadget.props.data.graph.edge[connection.id] || {\n
_class: getDefaultEdgeClass(gadget)\n
};\n
edge_data.source = getNodeId(gadget, connection.sourceId);\n
edge_data.destination = getNodeId(gadget, connection.targetId);\n
......@@ -137,23 +144,6 @@
}\n
gadget.notifyDataChanged();\n
}\n
function waitForConnection(gadget) {\n
loopJsplumbBind(gadget, "connection", function(info, originalEvent) {\n
updateConnectionData(gadget, info.connection);\n
});\n
}\n
function waitForConnectionDetached(gadget) {\n
loopJsplumbBind(gadget, "connectionDetached", function(info, originalEvent) {\n
updateConnectionData(gadget, info.connection, true);\n
});\n
}\n
function waitForConnectionClick(gadget) {\n
// TODO: dialog to edit connection properties\n
// XXX error in this function are silently ignored !!!\n
loopJsplumbBind(gadget, "dblclick", function(connection) {\n
return window.location.replace(gadget.props.data.graph.edge[connection.id].business_link_url);\n
});\n
}\n
function convertToAbsolutePosition(gadget, x, y) {\n
var zoom_level = gadget.props.zoom_level * 1.1111, canvas_size_x = $(gadget.props.main).width(), canvas_size_y = $(gadget.props.main).height(), size_x = $(gadget.props.element).find(".dummy_window").width() * zoom_level, size_y = $(gadget.props.element).find(".dummy_window").height() * zoom_level, top = Math.floor(y * (canvas_size_y - size_y)) + "px", left = Math.floor(x * (canvas_size_x - size_x)) + "px";\n
return [ left, top ];\n
......@@ -215,24 +205,6 @@
element.css(j, new_value);\n
});\n
}\n
// function redraw(gadget) {\n
// var coordinates = gadget.props.preference_container.coordinates || {},\n
// absolute_position,\n
// element;\n
// $.each(coordinates, function (node_id, v) {\n
// absolute_position = convertToAbsolutePosition(\n
// gadget,\n
// v.left,\n
// v.top\n
// );\n
// element = $(gadget.props.element).find(\n
// \'#\' + gadget.props.node_id_to_dom_element_id[node_id];\n
// );\n
// element.css(\'top\', absolute_position[1]);\n
// element.css(\'left\', absolute_position[0]);\n
// gadget.props.jsplumb_instance.repaint(element);\n
// });\n
// }\n
// function positionGraph(gadget) {\n
// $.ajax(\n
// \'/positionGraph\',\n
......@@ -262,6 +234,7 @@
gadget.props.jsplumb_instance.removeAllEndpoints($(gadget.props.element).find("#" + element_id));\n
$(gadget.props.element).find("#" + element_id).remove();\n
delete gadget.props.data.graph.node[node_id];\n
delete gadget.props.node_id_to_dom_element_id[node_id];\n
$.each(gadget.props.data.graph.edge, function(k, v) {\n
if (node_id === v.source || node_id === v.destination) {\n
delete gadget.props.data.graph.edge[k];\n
......@@ -269,6 +242,7 @@
});\n
gadget.notifyDataChanged();\n
}\n
// TODO: rename updateNodeData\n
function updateElementData(gadget, node_id, data) {\n
var element_id = gadget.props.node_id_to_dom_element_id[node_id], new_id = data.id;\n
if (data.data.name) {\n
......@@ -334,6 +308,8 @@
overlays: overlays\n
});\n
}\n
// set data for \'connection\' event that will be called "later"\n
gadget.props.data.graph.edge[edge_id] = edge_data;\n
// jsplumb assigned an id, but we are controlling ids ourselves.\n
connection.id = edge_id;\n
}\n
......@@ -348,7 +324,8 @@
for (i = 0; i < class_definition.allOf.length; i += 1) {\n
referenced = class_definition.allOf[i];\n
if (referenced.$ref) {\n
referenced = expandSchema(full_schema.class_definition[referenced.$ref.substr(1, referenced.$ref.length)], full_schema);\n
referenced = expandSchema(full_schema.class_definition[// 2 here is for #/\n
referenced.$ref.substr(2, referenced.$ref.length)], full_schema);\n
}\n
if (referenced.properties) {\n
for (property in referenced.properties) {\n
......@@ -363,9 +340,73 @@
}\n
return expanded_class_definition;\n
}\n
function openNodeDialog(gadget, element) {\n
function openEdgeEditionDialog(gadget, connection) {\n
var edge_id = connection.id, edge_data = gadget.props.data.graph.edge[edge_id], edit_popup = $(gadget.props.element).find("#popup-edit-template"), schema, fieldset_element, delete_promise;\n
schema = expandSchema(gadget.props.data.class_definition[edge_data._class], gadget.props.data);\n
// We do not edit source & destination on edge this way.\n
delete schema.properties.source;\n
delete schema.properties.destination;\n
gadget.props.element.appendChild(document.importNode(popup_edit_template.content, true).children[0]);\n
edit_popup = $(gadget.props.element).find("#edit-popup");\n
edit_popup.find(".node_class").text(connection._class);\n
fieldset_element = edit_popup.find("fieldset")[0];\n
edit_popup.popup();\n
edit_popup.show();\n
function save_promise(fieldset_gadget, edge_id) {\n
return RSVP.Queue().push(function() {\n
return promiseEventListener(edit_popup.find("form")[0], "submit", false);\n
}).push(function(evt) {\n
var data = {\n
id: $(evt.target[1]).val(),\n
data: {}\n
};\n
return fieldset_gadget.getContent().then(function(r) {\n
$.extend(data.data, gadget.props.data.graph.edge[connection.id]);\n
$.extend(data.data, r);\n
// to redraw, we remove the edge and add again.\n
// but we want to disable events on connection, since event\n
// handling promise are executed asynchronously in undefined order,\n
// we cannot just remove and /then/ add, because the new edge is\n
// added before the old is removed.\n
connection.ignoreEvent = true;\n
gadget.props.jsplumb_instance.detach(connection);\n
addEdge(gadget, r.id, data.data);\n
});\n
});\n
}\n
delete_promise = new RSVP.Queue().push(function() {\n
return promiseEventListener(edit_popup.find("form [type=\'button\']")[0], "click", false);\n
}).push(function() {\n
// connectionDetached event will remove the edge from data\n
gadget.props.jsplumb_instance.detach(connection);\n
});\n
return gadget.declareGadget("../fieldset/index.html", {\n
element: fieldset_element,\n
scope: "fieldset"\n
}).push(function(fieldset_gadget) {\n
return RSVP.all([ fieldset_gadget, fieldset_gadget.render({\n
value: edge_data,\n
property_definition: schema\n
}, edge_id) ]);\n
}).push(function(fieldset_gadget) {\n
edit_popup.enhanceWithin();\n
edit_popup.popup("open");\n
return fieldset_gadget[0];\n
}).push(function(fieldset_gadget) {\n
// Expose the dialog handling promise so that we can wait for it in\n
// test.\n
gadget.props.dialog_promise = RSVP.any([ save_promise(fieldset_gadget, edge_id), delete_promise ]);\n
return gadget.props.dialog_promise;\n
}).push(function() {\n
edit_popup.popup("close");\n
edit_popup.remove();\n
delete gadget.props.dialog_promise;\n
});\n
}\n
function openNodeEditionDialog(gadget, element) {\n
var node_id = getNodeId(gadget, element.id), node_data = gadget.props.data.graph.node[node_id], node_edit_popup = $(gadget.props.element).find("#popup-edit-template"), schema, fieldset_element, delete_promise;\n
// If we have no definition for this, we do not allow edition.\n
// XXX wrong we need to be able to delete\n
if (gadget.props.data.class_definition[node_data._class] === undefined) {\n
return;\n
}\n
......@@ -374,7 +415,7 @@
node_edit_popup.remove();\n
}\n
gadget.props.element.appendChild(document.importNode(popup_edit_template.content, true).children[0]);\n
node_edit_popup = $(gadget.props.element).find("#node-edit-popup");\n
node_edit_popup = $(gadget.props.element).find("#edit-popup");\n
// Set the name of the popup to the node class\n
node_edit_popup.find(".node_class").text(node_data._class);\n
fieldset_element = node_edit_popup.find("fieldset")[0];\n
......@@ -386,7 +427,6 @@
return promiseEventListener(node_edit_popup.find("form")[0], "submit", false);\n
}).push(function(evt) {\n
var data = {\n
// XXX id should not be handled differently ...\n
id: $(evt.target[1]).val(),\n
data: {}\n
};\n
......@@ -425,7 +465,22 @@
});\n
}\n
function waitForNodeClick(gadget, node) {\n
gadget.props.nodes_click_monitor.monitor(loopEventListener(node, "dblclick", false, openNodeDialog.bind(null, gadget, node)));\n
gadget.props.nodes_click_monitor.monitor(loopEventListener(node, "dblclick", false, openNodeEditionDialog.bind(null, gadget, node)));\n
}\n
function waitForConnection(gadget) {\n
return loopJsplumbBind(gadget, "connection", function(info, originalEvent) {\n
updateConnectionData(gadget, info.connection, false);\n
});\n
}\n
function waitForConnectionDetached(gadget) {\n
return loopJsplumbBind(gadget, "connectionDetached", function(info, originalEvent) {\n
updateConnectionData(gadget, info.connection, true);\n
});\n
}\n
function waitForConnectionClick(gadget) {\n
return loopJsplumbBind(gadget, "click", function(connection) {\n
return openEdgeEditionDialog(gadget, connection);\n
});\n
}\n
function addNode(gadget, node_id, node_data) {\n
var render_element = $(gadget.props.main), class_definition = gadget.props.data.class_definition[node_data._class], coordinate = node_data.coordinate, dom_element_id, box, absolute_position, domElement;\n
......@@ -523,39 +578,13 @@
g.props.element = element;\n
});\n
}).declareAcquiredMethod("notifyDataChanged", "notifyDataChanged").declareMethod("render", function(data) {\n
var gadget = this;\n
this.props.data = {};\n
if (data.value) {\n
// Gadget embedded in ERP5\n
this.props.erp5_key = data.key;\n
data = data.value;\n
}\n
\n
if (data) {\n
// var gadget = this;\n
this.props.data = JSON.parse(data);\n
// load the data\n
$.each(this.props.data.graph.node, function(key, value) {\n
addNode(gadget, key, value);\n
});\n
$.each(this.props.data.graph.edge, function(key, value) {\n
addEdge(gadget, key, value);\n
});\n
}\n
this.props.jsplumb_instance = jsPlumb.getInstance();\n
}).declareMethod("getContent", function() {\n
var ret = {};\n
if (this.props.erp5_key) {\n
// ERP5\n
ret[this.props.erp5_key] = JSON.stringify(this.props.data);\n
return ret;\n
}\n
return JSON.stringify(this.props.data);\n
}).declareMethod("startService", function() {\n
// no more needed, see below\n
}).declareService(function() {\n
var gadget = this, jsplumb_instance;\n
this.props.jsplumb_instance = jsPlumb.getInstance();\n
jsplumb_instance= gadget.props.jsplumb_instance;\n
var gadget = this, jsplumb_instance = gadget.props.jsplumb_instance;\n
this.props.main = this.props.element.querySelector("#main");\n
jsplumb_instance.setRenderMode(jsplumb_instance.SVG);\n
jsplumb_instance.importDefaults({\n
......@@ -576,7 +605,6 @@
});\n
draggable(gadget);\n
this.props.nodes_click_monitor = RSVP.Monitor();\n
if (this.props.data) {\n
// load the data\n
$.each(this.props.data.graph.node, function(key, value) {\n
addNode(gadget, key, value);\n
......@@ -584,7 +612,6 @@
$.each(this.props.data.graph.edge, function(key, value) {\n
addEdge(gadget, key, value);\n
});\n
}\n
return RSVP.all([ waitForDrop(gadget), waitForConnection(gadget), waitForConnectionDetached(gadget), waitForConnectionClick(gadget), gadget.props.nodes_click_monitor ]);\n
});\n
})(RSVP, rJS, $, jsPlumb, Handlebars, loopEventListener, promiseEventListener, DOMParser);
......@@ -597,7 +624,7 @@
</item>
<item>
<key> <string>size</string> </key>
<value> <int>25483</int> </value>
<value> <int>27792</int> </value>
</item>
<item>
<key> <string>title</string> </key>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment