odoo/addons/web_diagram/static/src/js/diagram.js

390 lines
14 KiB
JavaScript

/*---------------------------------------------------------
* OpenERP diagram library
*---------------------------------------------------------*/
openerp.web_diagram = function (instance) {
var QWeb = instance.web.qweb,
_t = instance.web._t,
_lt = instance.web._lt;
instance.web.views.add('diagram', 'instance.web.DiagramView');
instance.web.DiagramView = instance.web.View.extend({
display_name: _lt('Diagram'),
searchable: false,
init: function(parent, dataset, view_id, options) {
this._super(parent);
this.set_default_options(options);
this.view_manager = parent;
this.dataset = dataset;
this.model = this.dataset.model;
this.view_id = view_id;
this.domain = this.dataset._domain || [];
this.context = {};
this.ids = this.dataset.ids;
},
start: function() {
return this.rpc("/web_diagram/diagram/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
},
toTitleCase: function(str) {
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
},
on_loaded: function(result) {
var self = this;
if(this.ids && this.ids.length) {
this.id = this.ids[self.dataset.index || 0];
}
this.fields_view = result.fields_view,
this.view_id = this.fields_view.view_id,
this.fields = this.fields_view.fields,
this.nodes = this.fields_view.arch.children[0],
this.connectors = this.fields_view.arch.children[1],
this.node = this.nodes.attrs.object,
this.connector = this.connectors.attrs.object;
this.labels = _.filter(this.fields_view.arch.children, function(label) {
return label.tag == "label";
});
this.$element.html(QWeb.render("DiagramView", this));
this.$element.addClass(this.fields_view.arch.attrs['class']);
_.each(self.labels,function(label){
html_label = '<p style="padding: 4px;">' + label.attrs.string + "</p>";
self.$element.find('.oe_diagram_header').append(html_label);
})
this.$element.find('div.oe_diagram_pager button[data-pager-action]').click(function() {
var action = $(this).data('pager-action');
self.on_pager_action(action);
});
this.do_update_pager();
// New Node,Edge
this.$element.find('#new_node.oe_diagram_button_new').click(function(){self.add_node();});
if(this.id) {
self.get_diagram_info();
}
},
get_diagram_info: function() {
var self = this;
var params = {
'id': this.id,
'model': this.model,
'node': this.node,
'connector': this.connector,
'bgcolor': this.nodes.attrs.bgcolor,
'shape': this.nodes.attrs.shape,
'src_node': this.connectors.attrs.source,
'des_node': this.connectors.attrs.destination,
'label': this.connectors.attrs.label || false,
'visible_nodes': [],
'invisible_nodes': [],
'node_fields': [],
'connectors': [],
'connectors_fields': []
};
_.each(this.nodes.children, function(child) {
if(child.attrs.invisible == '1')
params['invisible_nodes'].push(child.attrs.name);
else {
params['visible_nodes'].push(child.attrs.name);
params['node_fields'].push(self.fields[child.attrs.name]['string']|| this.toTitleCase(child.attrs.name));
}
});
_.each(this.connectors.children, function(conn) {
params['connectors_fields'].push(self.fields[conn.attrs.name]['string']|| this.toTitleCase(conn.attrs.name));
params['connectors'].push(conn.attrs.name);
});
this.rpc(
'/web_diagram/diagram/get_diagram_info',params,
function(result) {
self.draw_diagram(result);
}
);
},
on_diagram_loaded: function(record) {
var id_record = record['id'];
if(id_record) {
this.id = id_record;
this.get_diagram_info();
}
},
// Set-up the drawing elements of the diagram
draw_diagram: function(result) {
var self = this;
var res_nodes = result['nodes'];
var res_edges = result['conn'];
this.parent_field = result.parent_field;
this.$element.find('h3.oe_diagram_title').text(result.name);
var id_to_node = {};
var style = {
edge_color: "#A0A0A0",
edge_label_color: "#555",
edge_label_font_size: 10,
edge_width: 2,
edge_spacing: 100,
edge_loop_radius: 100,
node_label_color: "#333",
node_label_font_size: 12,
node_outline_color: "#333",
node_outline_width: 1,
node_selected_color: "#0097BE",
node_selected_width: 2,
node_size_x: 110,
node_size_y: 80,
connector_active_color: "#FFF",
connector_radius: 4,
close_button_radius: 8,
close_button_color: "#333",
close_button_x_color: "#FFF",
gray: "#DCDCDC",
white: "#FFF",
viewport_margin: 50
};
// remove previous diagram
var canvas = self.$element.find('div.oe_diagram_diagram')
.empty().get(0);
var r = new Raphael(canvas, '100%','100%');
var graph = new CuteGraph(r,style,canvas.parentNode);
var confirm_dialog = $('#dialog').dialog({
autoOpen: false,
title: _t("Are you sure?") });
_.each(res_nodes, function(node) {
var n = new CuteNode(
graph,
node.x + 50, //FIXME the +50 should be in the layout algorithm
node.y + 50,
CuteGraph.wordwrap(node.name, 14),
node.shape === 'rectangle' ? 'rect' : 'circle',
node.color === 'white' ? style.white : style.gray);
n.id = node.id;
id_to_node[node.id] = n;
});
_.each(res_edges, function(edge) {
var e = new CuteEdge(
graph,
CuteGraph.wordwrap(edge.signal, 32),
id_to_node[edge.s_id],
id_to_node[edge.d_id] || id_to_node[edge.s_id] ); //WORKAROUND
e.id = edge.id;
});
CuteNode.double_click_callback = function(cutenode){
self.edit_node(cutenode.id);
};
var i = 0;
CuteNode.destruction_callback = function(cutenode){
if(!confirm(_t("Deleting this node cannot be undone.\nIt will also delete all connected transitions.\n\nAre you sure ?"))){
return $.Deferred().reject().promise();
}
return new instance.web.DataSet(self,self.node).unlink([cutenode.id]);
};
CuteEdge.double_click_callback = function(cuteedge){
self.edit_connector(cuteedge.id);
};
CuteEdge.creation_callback = function(node_start, node_end){
return {label:_t("")};
};
CuteEdge.new_edge_callback = function(cuteedge){
self.add_connector(cuteedge.get_start().id,
cuteedge.get_end().id,
cuteedge);
};
CuteEdge.destruction_callback = function(cuteedge){
if(!confirm(_t("Deleting this transition cannot be undone.\n\nAre you sure ?"))){
return $.Deferred().reject().promise();
}
return new instance.web.DataSet(self,self.connector).unlink([cuteedge.id]);
};
},
// Creates a popup to edit the content of the node with id node_id
edit_node: function(node_id){
var self = this;
var title = _t('Activity');
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.node,
node_id,
self.context || self.dataset.context,
{
title: _t("Open: ") + title
}
);
pop.on_write.add(function() {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
var form_fields = [self.parent_field];
var form_controller = pop.view_form;
form_controller.on_record_loaded.add_first(function() {
_.each(form_fields, function(fld) {
if (!(fld in form_controller.fields)) { return; }
var field = form_controller.fields[fld];
field.$input.prop('disabled', true);
field.$drop_down.unbind();
field.$menu_btn.unbind();
});
});
},
// Creates a popup to add a node to the diagram
add_node: function(){
var self = this;
var title = _t('Activity');
var pop = new instance.web.form.SelectCreatePopup(self);
pop.select_element(
self.node,
{
title: _t("Create:") + title,
initial_view: 'form',
disable_multiple_selection: true
},
self.dataset.domain,
self.context || self.dataset.context
);
pop.on_select_elements.add_last(function(element_ids) {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
var form_controller = pop.view_form;
var form_fields = [this.parent_field];
form_controller.on_record_loaded.add_last(function() {
_.each(form_fields, function(fld) {
if (!(fld in form_controller.fields)) { return; }
var field = form_controller.fields[fld];
field.set_value(self.id);
field.dirty = true;
});
});
},
// Creates a popup to edit the connector of id connector_id
edit_connector: function(connector_id){
var self = this;
var title = _t('Transition');
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.connector,
parseInt(connector_id,10), //FIXME Isn't connector_id supposed to be an int ?
self.context || self.dataset.context,
{
title: _t("Open: ") + title
}
);
pop.on_write.add(function() {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
},
// Creates a popup to add a connector from node_source_id to node_dest_id.
// dummy_cuteedge if not null, will be removed form the graph after the popup is closed.
add_connector: function(node_source_id, node_dest_id, dummy_cuteedge){
var self = this;
var title = _t('Transition');
var pop = new instance.web.form.SelectCreatePopup(self);
pop.select_element(
self.connector,
{
title: _t("Create:") + title,
initial_view: 'form',
disable_multiple_selection: true
},
this.dataset.domain,
this.context || this.dataset.context
);
pop.on_select_elements.add_last(function(element_ids) {
self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded);
});
// We want to destroy the dummy edge after a creation cancel. This destroys it even if we save the changes.
// This is not a problem since the diagram is completely redrawn on saved changes.
pop.$element.bind("dialogbeforeclose",function(){
if(dummy_cuteedge){
dummy_cuteedge.remove();
}
});
var form_controller = pop.view_form;
form_controller.on_record_loaded.add_last(function () {
form_controller.fields[self.connectors.attrs.source].set_value(node_source_id);
form_controller.fields[self.connectors.attrs.source].dirty = true;
form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id);
form_controller.fields[self.connectors.attrs.destination].dirty = true;
});
},
on_pager_action: function(action) {
switch (action) {
case 'first':
this.dataset.index = 0;
break;
case 'previous':
this.dataset.previous();
break;
case 'next':
this.dataset.next();
break;
case 'last':
this.dataset.index = this.dataset.ids.length - 1;
break;
}
var loaded = this.dataset.read_index(_.keys(this.fields_view.fields))
.pipe(this.on_diagram_loaded);
this.do_update_pager();
return loaded;
},
do_update_pager: function(hide_index) {
var $pager = this.$element.find('div.oe_diagram_pager');
var index = hide_index ? '-' : this.dataset.index + 1;
if(!this.dataset.count) {
this.dataset.count = this.dataset.ids.length;
}
$pager.find('span.oe_pager_index').html(index);
$pager.find('span.oe_pager_count').html(this.dataset.count);
},
do_show: function() {
this.do_push_state({});
return $.when(this._super(), this.on_pager_action('reload'));
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: