summaryrefslogtreecommitdiff
path: root/addons/web_graph/static/src/js
diff options
context:
space:
mode:
authorFabien Pinckaers <fp@tinyerp.com>2012-05-07 10:19:08 +0200
committerFabien Pinckaers <fp@tinyerp.com>2012-05-07 10:19:08 +0200
commitdd6de9b2006cfe5bc7be28d70addc321691d2746 (patch)
tree677a360fdc3b660add0e9589b74c10da3b562766 /addons/web_graph/static/src/js
parent16a2d354fc333d8fd3c0c638c5c2d74ffee8c580 (diff)
[IMP] replacing by flotr2, initial commit
bzr revid: fp@tinyerp.com-20120507081908-j0sfm5f84f0t32yg
Diffstat (limited to 'addons/web_graph/static/src/js')
-rw-r--r--addons/web_graph/static/src/js/graph.js354
-rw-r--r--addons/web_graph/static/src/js/index.html358
2 files changed, 374 insertions, 338 deletions
diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js
index 7016b9d8d85..27ae5ee3842 100644
--- a/addons/web_graph/static/src/js/graph.js
+++ b/addons/web_graph/static/src/js/graph.js
@@ -3,17 +3,10 @@
*---------------------------------------------------------*/
openerp.web_graph = function (instance) {
-var COLOR_PALETTE = [
- '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b',
- '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00',
- '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#9abe00',
- '#ffc900', '#510090', '#0000c9', '#009b00', '#75507b', '#3465a4',
- '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9',
- '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900',
- '#cc0000', '#d400a8'];
var QWeb = instance.web.qweb,
_lt = instance.web._lt;
+
instance.web.views.add('graph', 'instance.web_graph.GraphView');
instance.web_graph.GraphView = instance.web.View.extend({
display_name: _lt('Graph'),
@@ -25,13 +18,16 @@ instance.web_graph.GraphView = instance.web.View.extend({
this.dataset = dataset;
this.view_id = view_id;
- this.first_field = null;
- this.abscissa = null;
- this.ordinate = null;
- this.columns = [];
- this.group_field = null;
- this.is_loaded = $.Deferred();
+ this.mode="pie"; // line, bar, area, pie, radar
+ this.orientation=true; // true: horizontal, false: vertical
+ this.stacked=true;
+ this.spreadsheet=false; // Display data gris, allows copy to CSV
+ this.forcehtml=false;
+ this.legend_container;
+ this.legend="top"; // top, inside, no
+
+ this.is_loaded = $.Deferred();
this.renderer = null;
},
destroy: function () {
@@ -40,6 +36,9 @@ instance.web_graph.GraphView = instance.web.View.extend({
}
this._super();
},
+
+
+
on_loaded: function(fields_view_get) {
var self = this;
self.fields_view = fields_view_get;
@@ -80,6 +79,7 @@ instance.web_graph.GraphView = instance.web.View.extend({
this.is_loaded.resolve();
return $.when();
},
+
schedule_chart: function(results) {
var self = this;
this.$element.html(QWeb.render("GraphView", {
@@ -90,330 +90,8 @@ instance.web_graph.GraphView = instance.web.View.extend({
var fields = _(this.columns).pluck('name').concat([this.abscissa]);
if (this.group_field) { fields.push(this.group_field); }
- // transform search result into usable records (convert from OpenERP
- // value shapes to usable atomic types
- var records = _(results).map(function (result) {
- var point = {};
- _(result).each(function (value, field) {
- if (!_(fields).contains(field)) { return; }
- if (value === false) { point[field] = false; return; }
- switch (self.fields[field].type) {
- case 'selection':
- point[field] = _(self.fields[field].selection).detect(function (choice) {
- return choice[0] === value;
- })[1];
- break;
- case 'many2one':
- point[field] = value[1];
- break;
- case 'integer': case 'float': case 'char':
- case 'date': case 'datetime':
- point[field] = value;
- break;
- default:
- throw new Error(
- "Unknown field type " + self.fields[field].type
- + "for field " + field + " (" + value + ")");
- }
- });
- return point;
- });
- // aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
- // leave split on group field => max m*n records where m is the # of
- // values for the abscissa and n is the # of values for the group field
- var graph_data = [];
- _(records).each(function (record) {
- var abscissa = record[self.abscissa],
- group = record[self.group_field];
- var r = _(graph_data).detect(function (potential) {
- return potential[self.abscissa] === abscissa
- && (!self.group_field
- || potential[self.group_field] === group);
- });
- var datapoint = r || {};
- datapoint[self.abscissa] = abscissa;
- if (self.group_field) { datapoint[self.group_field] = group; }
- _(self.columns).each(function (column) {
- var val = record[column.name],
- aggregate = datapoint[column.name];
- switch(column.operator) {
- case '+':
- datapoint[column.name] = (aggregate || 0) + val;
- return;
- case '*':
- datapoint[column.name] = (aggregate || 1) * val;
- return;
- case 'min':
- datapoint[column.name] = (aggregate || Infinity) > val
- ? val
- : aggregate;
- return;
- case 'max':
- datapoint[column.name] = (aggregate || -Infinity) < val
- ? val
- : aggregate;
- }
- });
-
- if (!r) { graph_data.push(datapoint); }
- });
- graph_data = _(graph_data).sortBy(function (point) {
- return point[self.abscissa] + '[[--]]' + point[self.group_field];
- });
- if (_.include(['bar','line','area'],this.chart)) {
- return this.schedule_bar_line_area(graph_data);
- } else if (this.chart == "pie") {
- return this.schedule_pie(graph_data);
- }
},
- schedule_bar_line_area: function(results) {
- var self = this;
- var group_list,
- view_chart = (self.chart == 'line')?'line':(self.chart == 'area')?'area':'';
- if (!this.group_field || !results.length) {
- if (self.chart == 'bar'){
- view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
- }
- group_list = _(this.columns).map(function (column, index) {
- return {
- group: column.name,
- text: self.fields[column.name].string,
- color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
- }
- });
- } else {
- // dhtmlx handles clustered bar charts (> 1 column per abscissa
- // value) and stacked bar charts (basically the same but with the
- // columns on top of one another instead of side by side), but it
- // does not handle clustered stacked bar charts
- if (self.chart == 'bar' && (this.columns.length > 1)) {
- this.$element.text(
- 'OpenERP Web does not support combining grouping and '
- + 'multiple columns in graph at this time.');
- throw new Error(
- 'dhtmlx can not handle columns counts of that magnitude');
- }
- // transform series for clustered charts into series for stacked
- // charts
- if (self.chart == 'bar'){
- view_chart = (this.orientation === 'horizontal')
- ? 'stackedBarH' : 'stackedBar';
- }
- group_list = _(results).chain()
- .pluck(this.group_field)
- .uniq()
- .map(function (value, index) {
- var groupval = '';
- if(value) {
- groupval = value.toLowerCase().replace(/[\s\/]+/g,'_');
- }
- return {
- group: _.str.sprintf('%s_%s', self.ordinate, groupval),
- text: value,
- color: COLOR_PALETTE[index % COLOR_PALETTE.length]
- };
- }).value();
-
- results = _(results).chain()
- .groupBy(function (record) { return record[self.abscissa]; })
- .map(function (records) {
- var r = {};
- // second argument is coerced to a str, no good for boolean
- r[self.abscissa] = records[0][self.abscissa];
- _(records).each(function (record) {
- var value = record[self.group_field];
- if(value) {
- value = value.toLowerCase().replace(/[\s\/]+/g,'_');
- }
- var key = _.str.sprintf('%s_%s', self.ordinate, value);
- r[key] = record[self.ordinate];
- });
- return r;
- })
- .value();
- }
- var abscissa_description = {
- title: "<b>" + this.fields[this.abscissa].string + "</b>",
- template: function (obj) {
- return obj[self.abscissa] || 'Undefined';
- }
- };
- var ordinate_description = {
- lines: true,
- title: "<b>" + this.fields[this.ordinate].string + "</b>"
- };
-
- var x_axis, y_axis;
- if (self.chart == 'bar' && self.orientation == 'horizontal') {
- x_axis = ordinate_description;
- y_axis = abscissa_description;
- } else {
- x_axis = abscissa_description;
- y_axis = ordinate_description;
- }
- var renderer = function () {
- if (self.$element.is(':hidden')) {
- self.renderer = setTimeout(renderer, 100);
- return;
- }
- self.renderer = null;
- var charts = new dhtmlXChart({
- view: view_chart,
- container: self.getParent().element_id+"-"+self.chart+"chart",
- value:"#"+group_list[0].group+"#",
- gradient: (self.chart == "bar") ? "3d" : "light",
- alpha: (self.chart == "area") ? 0.6 : 1,
- border: false,
- width: 1024,
- tooltip:{
- template: _.str.sprintf("#%s#, %s=#%s#",
- self.abscissa, group_list[0].text, group_list[0].group)
- },
- radius: 0,
- color: (self.chart != "line") ? group_list[0].color : "",
- item: (self.chart == "line") ? {
- borderColor: group_list[0].color,
- color: "#000000"
- } : "",
- line: (self.chart == "line") ? {
- color: group_list[0].color,
- width: 3
- } : "",
- origin:0,
- xAxis: x_axis,
- yAxis: y_axis,
- padding: {
- left: 75
- },
- legend: {
- values: group_list,
- align:"left",
- valign:"top",
- layout: "x",
- marker: {
- type:"round",
- width:12
- }
- }
- });
- self.$element.find("#"+self.getParent().element_id+"-"+self.chart+"chart").width(
- self.$element.find("#"+self.getParent().element_id+"-"+self.chart+"chart").width()+120);
-
- for (var m = 1; m<group_list.length;m++){
- var column = group_list[m];
- if (column.group === self.group_field) { continue; }
- charts.addSeries({
- value: "#"+column.group+"#",
- tooltip:{
- template: _.str.sprintf("#%s#, %s=#%s#",
- self.abscissa, column.text, column.group)
- },
- color: (self.chart != "line") ? column.color : "",
- item: (self.chart == "line") ? {
- borderColor: column.color,
- color: "#000000"
- } : "",
- line: (self.chart == "line") ? {
- color: column.color,
- width: 3
- } : ""
- });
- }
- charts.parse(results, "json");
- self.$element.find("#"+self.getParent().element_id+"-"+self.chart+"chart").height(
- self.$element.find("#"+self.getParent().element_id+"-"+self.chart+"chart").height()+50);
- charts.attachEvent("onItemClick", function(id) {
- self.open_list_view(charts.get(id));
- });
- };
- if (this.renderer) {
- clearTimeout(this.renderer);
- }
- this.renderer = setTimeout(renderer, 0);
- },
- schedule_pie: function(result) {
- var self = this;
- var renderer = function () {
- if (self.$element.is(':hidden')) {
- self.renderer = setTimeout(renderer, 100);
- return;
- }
- self.renderer = null;
- var chart = new dhtmlXChart({
- view:"pie3D",
- container:self.getParent().element_id+"-piechart",
- value:"#"+self.ordinate+"#",
- pieInnerText:function(obj) {
- var sum = chart.sum("#"+self.ordinate+"#");
- var val = obj[self.ordinate] / sum * 100 ;
- return val.toFixed(1) + "%";
- },
- tooltip:{
- template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
- },
- gradient:"3d",
- height: 20,
- radius: 200,
- legend: {
- width: 300,
- align:"left",
- valign:"top",
- layout: "x",
- marker:{
- type:"round",
- width:12
- },
- template:function(obj){
- return obj[self.abscissa] || 'Undefined';
- }
- }
- });
- chart.parse(result,"json");
- chart.attachEvent("onItemClick", function(id) {
- self.open_list_view(chart.get(id));
- });
- };
- if (this.renderer) {
- clearTimeout(this.renderer);
- }
- this.renderer = setTimeout(renderer, 0);
- },
- open_list_view : function (id){
- var self = this;
- // unconditionally nuke tooltips before switching view
- $(".dhx_tooltip").remove('div');
- id = id[this.abscissa];
- if(this.fields[this.abscissa].type == "selection"){
- id = _.detect(this.fields[this.abscissa].selection,function(select_value){
- return _.include(select_value, id);
- });
- }
- if (typeof id == 'object'){
- id = id[0];
- }
-
- var views;
- if (this.getParent().action) {
- views = this.getParent().action.views;
- if (!_(views).detect(function (view) {
- return view[1] === 'list' })) {
- views = [[false, 'list']].concat(views);
- }
- } else {
- views = _(["list", "form", "graph"]).map(function(mode) {
- return [false, mode];
- });
- }
- this.do_action({
- res_model : this.dataset.model,
- domain: [[this.abscissa, '=', id], ['id','in',this.dataset.ids]],
- views: views,
- type: "ir.actions.act_window",
- flags: {default_view: 'list'}
- });
- },
-
do_search: function(domain, context, group_by) {
var self = this;
return $.when(this.is_loaded).pipe(function() {
@@ -430,7 +108,7 @@ instance.web_graph.GraphView = instance.web.View.extend({
do_show: function() {
this.do_push_state({});
return this._super();
- }
+ },
+
});
};
-// vim:et fdc=0 fdl=0:
diff --git a/addons/web_graph/static/src/js/index.html b/addons/web_graph/static/src/js/index.html
new file mode 100644
index 00000000000..4c02a86376f
--- /dev/null
+++ b/addons/web_graph/static/src/js/index.html
@@ -0,0 +1,358 @@
+<html>
+ <head>
+ <script src="js/jquery-1.7.1.min.js"></script>
+ <script src="js/dropdown.js"></script>
+ <script type="text/javascript" src="flotr2/flotr2.js"></script>
+ <style type="text/css">
+ .dropdown-menu-icon {
+ z-index: 5000;
+ position: absolute;
+ right: 0px;
+ }
+
+ .editor-render {
+ width: 650px;
+ position: relative;
+ }
+ #editor-render-body {
+ position: relative;
+ width: 650px;
+ height: 440px;
+ }
+ .graph-dropdown {
+ padding: 5px;
+ width: 200px;
+ border: 1px #333 solid;
+ display: none;
+ position: absolute;
+ right: 0px;
+ background: #fafaf5;
+ z-index: 100;
+ }
+ .open .graph-dropdown {
+ display: block;
+ }
+ .graph-menu {
+ padding: 3px;
+ }
+ .graph-menu .active {
+ background: #aaaaff;
+ }
+ li {
+ padding: 2px;
+ margin-left: 10px;
+ }
+ a .graph-menu-options {
+ color: white;
+ }
+ div .graph-menu-options {
+ float: right;
+ top: 0px;
+ }
+ </style>
+ <link rel="stylesheet" href="css/openerp.css" type="text/css">
+ <link rel="stylesheet" href="css/flotr2.css" type="text/css">
+
+ </head>
+
+<body style="padding:20px;">
+ <h1 id="title">
+ Hello World!
+ </h1>
+
+ <div class="editor-render">
+ <div class="graph_header_legend">
+ </div>
+ <a href="#" class="i dropdown-menu-icon" data-toggle="dropdown">B</a>
+ <div class="graph-dropdown">
+ <div class="menu-section">
+ Graph Mode:
+ </div>
+ <ul class="graph-menu">
+ <li><a href="#" id="graph_pie">Pie</a>
+ </li>
+ <li>
+ <a href="#" id="graph_bar">Bars</a>
+ <div class="i graph-menu-options">
+ <a href="#" id="graph_bar_stacked">w</a>
+ <a href="#" id="graph_bar_not_stacked">O</a>
+ </div>
+ </li>
+ <li><a href="#" id="graph_line">Lines</a>
+ <div class="i graph-menu-options">
+
+ </div>
+ </li>
+ <li><a href="#" id="graph_area">Areas</a>
+ <div class="i graph-menu-options">
+ <a href="#" id="graph_area_stacked">w</a>
+ <a href="#" id="graph_area_not_stacked">O</a>
+ </div>
+
+ </li>
+ <li><a href="#" id="graph_radar">Radar</a></li>
+ </ul>
+ <div class="menu-section">
+ Legend:
+ </div>
+ <ul class="graph-menu">
+ <li><a href="#" id="graph_legend_no">Hide</a></li>
+ <li><a href="#" id="graph_legend_inside">Inside</a></li>
+ <li><a href="#" id="graph_legend_top">Top</a></li>
+ </ul>
+ <div class="menu-section">
+ Actions:
+ </div>
+ <ul class="graph-menu">
+ <li><a href="#" id="graph_switch">Switch Axis</a></li>
+ <li><a href="#" id="graph_show_data">Show Data</a></li>
+ <li><a href="#" id="graph_download">Download as PNG</a></li>
+ </ul>
+ </div>
+
+ <div id="editor-render-body">
+ </div>
+ </div>
+
+
+ <script type="text/javascript">
+ var
+ mode="pie", // line, bar, area, pie, radar
+ orientation=true, // true: horizontal, false: vertical
+ stacked=true,
+ spreadsheet=false,
+ forcehtml=false,
+ legend_container,
+ legend="top"; // top, inside, no
+
+ function get_format(options) {
+ result = {
+ show: legend!='no',
+ }
+ if (legend=="top") {
+ result.noColumns = 4;
+ result.legend_container = $("div .graph_header_legend")[0];
+ } else if (legend=="inside") {
+ result.position = 'nw';
+ result.backgroundColor = '#D2E8FF';
+ }
+ return $.extend({
+ legend: result,
+ mouse: {
+ track: true,
+ relative: true
+ },
+ spreadsheet : {
+ show: spreadsheet,
+ initialTab: "data"
+ },
+ HtmlText : (options && options.labelsAngle)?false:!forcehtml,
+ }, options)
+ }
+
+ /*
+ * Fetch data in Ajax. Data is preprocessed by the server, in python
+ */
+ function get_data(options) {
+ var i,
+ d1 = [],
+ d2 = [],
+ d3 = [];
+
+ for (i = -3; i < 3; i++) {
+ if (orientation % 2) {
+ d1.push([Math.random(), i]);
+ d2.push([Math.random(), i]);
+ d3.push([Math.random(), i]);
+ } else {
+ d1.push([i, Math.random()]);
+ d2.push([i, Math.random()]);
+ d3.push([i, Math.random()]);
+ }
+ };
+ return [
+ $.extend({ data : d2, label : 'Serie 2 '+orientation%2 }, options),
+ $.extend({ data : d3, label : 'Serie 3 '+orientation%2 }, options),
+ $.extend({ data : d1, label : 'Serie 1 '+orientation%2 }, options),
+ ];
+ }
+
+ function graph_bar(container, data) {
+ return Flotr.draw(container, data, get_format({
+ bars : {
+ show : true,
+ stacked : stacked,
+ horizontal : orientation,
+ barWidth : 0.7,
+ lineWidth : 1
+ },
+ grid : {
+ verticalLines : orientation,
+ horizontalLines : !orientation,
+ outline : "sw",
+ },
+ labelsAngle: 45
+ })
+ )
+ }
+
+ function graph_pie(container, data) {
+ return Flotr.draw(container, data, get_format({
+ pie : {
+ show: true
+ },
+ grid : {
+ verticalLines : false,
+ horizontalLines : false,
+ outline : "",
+ },
+ xaxis : {showLabels: false},
+ yaxis : {showLabels: false},
+ })
+ )
+ }
+
+
+ function graph_radar(container, data) {
+ return Flotr.draw(container, data, get_format({
+ radar : {
+ show : true,
+ stacked : stacked
+ },
+ grid : {
+ circular : true,
+ minorHorizontalLines : true
+ }
+ })
+ )
+ }
+
+
+ function graph_line(container, data) {
+ return Flotr.draw(container, data, get_format({
+ lines : {
+ show : true,
+ stacked : stacked
+ },
+ grid : {
+ verticalLines : orientation,
+ horizontalLines : !orientation,
+ outline : "sw",
+ },
+ labelsAngle : 45
+ })
+ )
+ }
+
+ // Render the graph and update menu styles
+
+ function graph_render(options) {
+ var graph, data, mode_options, i;
+
+ if (options)
+ for (i in options.data)
+ window[i] = options.data[i];
+
+ mode_options = (mode=='area')?{lines: {fill: true}}:{}
+
+ // Render the graph
+ $(".graph_header_legend").children().remove()
+ data = get_data(mode_options);
+ graph = {
+ radar: graph_radar,
+ pie: graph_pie,
+ bar: graph_bar,
+ area: graph_line,
+ line: graph_line
+ }[mode](container, data)
+
+ // Update styles of menus
+
+ $("a[id^='graph_']").removeClass("active");
+ $("a[id='graph_"+mode+"']").addClass("active");
+ $("a[id='graph_"+mode+(stacked?"_stacked":"_not_stacked")+"']").addClass("active");
+
+ if (legend=='inside')
+ $("a[id='graph_legend_inside']").addClass("active");
+ else if (legend=='top')
+ $("a[id='graph_legend_top']").addClass("active");
+ else
+ $("a[id='graph_legend_no']").addClass("active");
+
+ if (spreadsheet)
+ $("a[id='graph_show_data']").addClass("active");
+ return graph;
+ }
+
+
+ $(document).ready(
+ function() {
+ container = document.getElementById("editor-render-body");
+ graph_render();
+
+ $("#graph_bar,#graph_bar_stacked").click(
+ {mode: 'bar', stacked: true, legend: 'top'}, graph_render)
+
+ $("#graph_bar_not_stacked").click(
+ {mode: 'bar', stacked: false, legend: 'top'}, graph_render)
+
+ $("#graph_area,#graph_area_stacked").click(
+ {mode: "area", stacked: true, legend: "top"}, graph_render);
+
+ $("#graph_area_not_stacked").click(
+ {mode: "area", stacked: false, legend: "top"}, graph_render);
+
+ $("#graph_radar").click(
+ {orientation: 0, mode: "radar", legend: "inside"}, graph_render);
+
+ $("#graph_pie").click(
+ {mode: "pie", legend: "inside"}, graph_render);
+
+ $("#graph_legend_top").click(
+ {legend: "top"}, graph_render);
+
+ $("#graph_legend_inside").click(
+ {legend: "inside"}, graph_render);
+
+ $("#graph_legend_no").click(
+ {legend: "no"}, graph_render);
+
+ $("#graph_line").click(
+ {mode: "line"}, graph_render);
+
+ $("#graph_show_data").click(
+ function() {
+ spreadsheet = ! spreadsheet;
+ graph_render();
+ }
+ );
+ $("#graph_switch").click(
+ function() {
+ orientation = ! orientation;
+ graph_render();
+ }
+ );
+
+ $("#graph_download").click(
+ function() {
+ var graph;
+ if (Flotr.isIE && Flotr.isIE < 9) {
+ alert(
+ "Your browser doesn't allow you to get a bitmap image from the plot, " +
+ "you can only get a VML image that you can use in Microsoft Office."
+ );
+ }
+ if (legend=="top") legend="inside";
+ forcehtml = true;
+ graph = graph_render();
+ graph.download.saveImage('png');
+ forcehtml = false;
+ }
+ );
+ }
+ );
+ </script>
+
+</body>
+
+</html>