diff options
| author | Fabien Pinckaers <fp@tinyerp.com> | 2012-05-07 10:19:08 +0200 |
|---|---|---|
| committer | Fabien Pinckaers <fp@tinyerp.com> | 2012-05-07 10:19:08 +0200 |
| commit | dd6de9b2006cfe5bc7be28d70addc321691d2746 (patch) | |
| tree | 677a360fdc3b660add0e9589b74c10da3b562766 /addons/web_graph/static/src/js | |
| parent | 16a2d354fc333d8fd3c0c638c5c2d74ffee8c580 (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.js | 354 | ||||
| -rw-r--r-- | addons/web_graph/static/src/js/index.html | 358 |
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> |
