From 0feaf648c801631483b4889b7b00c1d6b01fb253 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jordan=20Aug=C3=A9?= Date: Tue, 3 Dec 2013 17:35:05 +0100 Subject: [PATCH] added draft maddash plugin --- plugins/maddash/__init__.py | 40 ++ plugins/maddash/static/css/maddash.css | 37 ++ plugins/maddash/static/css/tipsy.css | 12 + plugins/maddash/static/img/tipsy.gif | Bin 0 -> 58 bytes plugins/maddash/static/js/.maddash.js.swp | Bin 0 -> 40960 bytes plugins/maddash/static/js/jquery.tipsy.js | 198 ++++++++ plugins/maddash/static/js/maddash.js | 583 ++++++++++++++++++++++ plugins/maddash/templates/maddash.html | 5 + 8 files changed, 875 insertions(+) create mode 100644 plugins/maddash/__init__.py create mode 100644 plugins/maddash/static/css/maddash.css create mode 100644 plugins/maddash/static/css/tipsy.css create mode 100644 plugins/maddash/static/img/tipsy.gif create mode 100644 plugins/maddash/static/js/.maddash.js.swp create mode 100644 plugins/maddash/static/js/jquery.tipsy.js create mode 100644 plugins/maddash/static/js/maddash.js create mode 100644 plugins/maddash/templates/maddash.html diff --git a/plugins/maddash/__init__.py b/plugins/maddash/__init__.py new file mode 100644 index 00000000..0447ff4d --- /dev/null +++ b/plugins/maddash/__init__.py @@ -0,0 +1,40 @@ +from unfold.plugin import Plugin + +class MadDash (Plugin): + + # set checkboxes if a final column with checkboxes is desired + # pass columns as the initial set of columns + # if None then this is taken from the query's fields + # latitude,longitude, zoom : the starting point + def __init__ (self, query = None, query_all = None, **settings): + Plugin.__init__ (self, **settings) + self.query=query + self.query_all = query_all + self.query_all_uuid = query_all.query_uuid if query_all else None + + def template_file (self): + return "maddash.html" + + def template_env (self, request): + env={} + return env + + def requirements (self): + reqs = { + 'js_files' : [ + 'http://d3js.org/d3.v3.min.js', + 'js/jquery.tipsy.js', + 'js/buffer.js', 'js/maddash.js', + 'js/manifold.js', 'js/manifold-query.js', + 'js/spin.presets.js', 'js/spin.min.js', 'js/jquery.spin.js', + 'js/unfold-helper.js', + ], + 'css_files' : [ + 'css/maddash.css', + 'css/tipsy.css', + ], + } + return reqs + + # the list of things passed to the js plugin + def json_settings_list (self): return ['plugin_uuid','query_uuid', 'query_all_uuid'] diff --git a/plugins/maddash/static/css/maddash.css b/plugins/maddash/static/css/maddash.css new file mode 100644 index 00000000..93ece291 --- /dev/null +++ b/plugins/maddash/static/css/maddash.css @@ -0,0 +1,37 @@ +#maddash__maddash { + height: 600px; +} + +.grid-container{float:left;overflow:hidden;margin-right:10px;} +.gcol{position:absolute;top:0px;} +.gactive{background:#3c3c3c; fill:#3c3c3c} +.grow{overflow:hidden;clear:both;z-index:1;position:relative;} +.grow-heading{float:left;margin:1px 0px;} +.gtext{font-family:verdana;font-size:10px;fill:#242424} +.gactive .gtext {fill:#fff;font-size:11px;} +.gcell{float:left;cursor:pointer;background:none;} +.shadow { +/* -moz-box-shadow: 0 0 30px 5px #000; + -webkit-box-shadow: 0 0 30px 5px #000; +*/ border:1px solid #fff; +} +.ggrid{float:left;position:relative;} +.gleft{float:left;clear:both} +.gtop{} +.gsubcell{float:left;} +rect{fill:none;} +.tooltip{text-align:left;font-size:12px;} +.tooltip .top-tip{padding-bottom:2px;border-bottom:1px solid #fff} +.tooltip .bottom-tip{padding-top:2px;} +.legends{ + background-color:#fff7fb; + border:1px solid #d0d1e6; + margin: 5px 5px 5px 5px; + font-size:11px; + float: left; + min-width: 97%; + +} +.legend{float:left;margin: 4px 10px 4px 10px;} +.lsymbol{float:left;width:13px;height:13px;margin-right:3px;} +.ltext{float:left;line-height:13px;font-style:normal;color:#252525} diff --git a/plugins/maddash/static/css/tipsy.css b/plugins/maddash/static/css/tipsy.css new file mode 100644 index 00000000..ae04cd9c --- /dev/null +++ b/plugins/maddash/static/css/tipsy.css @@ -0,0 +1,12 @@ +.tipsy { padding: 5px; font-size: 10px; position: absolute; z-index: 100000; } + .tipsy-inner { padding: 5px 8px 4px 8px; background-color: #3c3c3c; color: white; max-width: 200px; text-align: center;} + .tipsy-inner { border-radius: 3px; -moz-border-radius:3px; -webkit-border-radius:3px; } + .tipsy-arrow { position: absolute; background: url('tipsy.gif') no-repeat top left; width: 9px; height: 5px; } + .tipsy-n .tipsy-arrow { top: 0; left: 50%; margin-left: -4px; } + .tipsy-nw .tipsy-arrow { top: 0; left: 10px; } + .tipsy-ne .tipsy-arrow { top: 0; right: 10px; } + .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -4px; background-position: bottom left; } + .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; background-position: bottom left; } + .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; background-position: bottom left; } + .tipsy-e .tipsy-arrow { top: 50%; margin-top: -4px; right: 0; width: 5px; height: 9px; background-position: top right; } + .tipsy-w .tipsy-arrow { top: 50%; margin-top: -4px; left: 0; width: 5px; height: 9px; } \ No newline at end of file diff --git a/plugins/maddash/static/img/tipsy.gif b/plugins/maddash/static/img/tipsy.gif new file mode 100644 index 0000000000000000000000000000000000000000..74eebae2d556220c31f8a255a93b0a8f091a0b70 GIT binary patch literal 58 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').html('
'); + } + return this.$tip; + }, + + validate: function() { + if (!this.$element[0].parentNode) { + this.hide(); + this.$element = null; + this.options = null; + } + }, + + enable: function() { this.enabled = true; }, + disable: function() { this.enabled = false; }, + toggleEnabled: function() { this.enabled = !this.enabled; } + }; + + $.fn.tipsy = function(options) { + + if (options === true) { + return this.data('tipsy'); + } else if (typeof options == 'string') { + return this.data('tipsy')[options](); + } + + options = $.extend({}, $.fn.tipsy.defaults, options); + + function get(ele) { + var tipsy = $.data(ele, 'tipsy'); + if (!tipsy) { + tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); + $.data(ele, 'tipsy', tipsy); + } + return tipsy; + } + + function enter() { + var tipsy = get(this); + tipsy.hoverState = 'in'; + if (options.delayIn == 0) { + tipsy.show(); + } else { + setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); + } + }; + + function leave() { + var tipsy = get(this); + tipsy.hoverState = 'out'; + if (options.delayOut == 0) { + tipsy.hide(); + } else { + setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut); + } + }; + + if (!options.live) this.each(function() { get(this); }); + + if (options.trigger != 'manual') { + var binder = options.live ? 'live' : 'bind', + eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', + eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; + this[binder](eventIn, enter)[binder](eventOut, leave); + } + + return this; + + }; + + $.fn.tipsy.defaults = { + delayIn: 0, + delayOut: 0, + fade: false, + fallback: '', + gravity: 'n', + html: false, + live: false, + offset: 0, + opacity: 0.8, + title: 'title', + trigger: 'hover' + }; + + // Overwrite this method to provide options on a per-element basis. + // For example, you could store the gravity in a 'tipsy-gravity' attribute: + // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); + // (remember - do not modify 'options' in place!) + $.fn.tipsy.elementOptions = function(ele, options) { + return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; + }; + + $.fn.tipsy.autoNS = function() { + return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; + }; + + $.fn.tipsy.autoWE = function() { + return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; + }; + +})(jQuery); \ No newline at end of file diff --git a/plugins/maddash/static/js/maddash.js b/plugins/maddash/static/js/maddash.js new file mode 100644 index 00000000..110eec13 --- /dev/null +++ b/plugins/maddash/static/js/maddash.js @@ -0,0 +1,583 @@ +/** + * MyPlugin: MadDash + * Version: 0.1 + * Description: Template for writing new plugins and illustrating the different + * possibilities of the plugin API. + * This file is part of the Manifold project + * Requires: js/plugin.js + * URL: http://www.myslice.info + * Author: Jordan Augé + * Copyright: Copyright 2012-2013 UPMC Sorbonne Universités + * License: GPLv3 + */ +var SVG='http://www.w3.org/2000/svg'; +var instance = this; +var colorscale = d3.scale.category10().range(["green", "yellow", "red", "orange", "gray"]); + +/* XXX */ +/** + * Class: MaDDashGrid + * Description: Widget that displays grid of checks. Uses + * d3 and jQuery to draw the grid. + * Parameters: + * parentId: id string of the container element + * legendId: id string of the legend element + */ + +(function($){ + + var MadDash = Plugin.extend({ + + /** XXX to check + * @brief Plugin constructor + * @param options : an associative array of setting values + * @param element : + * @return : a jQuery collection of objects on which the plugin is + * applied, which allows to maintain chainability of calls + */ + init: function(options, element) { + // Call the parent constructor, see FAQ when forgotten + this._super(options, element); + + + /* Member variables */ + //this.canvas = this.id('canvas'); + this._legend = this.id('legend'); //legendId; + this._labels = Array( + 'un', 'deux', 'trois', 'quatre' + ) + + this._cellsize = 13; + this._cellpadding = 2; + this._text_block_size = 130; + + this._map_elements = {}; + this._num_elements = 0; + + this._max_width_elements = 50; + this._max_height_elements = 30; + + this._buffer_key_list = new Buffer(this._process_key_list, this); + this._buffer_records = new Buffer(this._process_records, this); + + /* Pointers */ + this._canvas = d3.select("#" + options.plugin_uuid + '__canvas') + .style("width", this._max_width_elements * (this._cellsize + 2*this._cellpadding) + 110 + this._text_block_size) + .style("height", this._max_height_elements * (this._cellsize + 2*this._cellpadding) + 110 + this._text_block_size) + this._left_element = null; + this._top_element = null; + this._row_element = Array(); + this._grid_element = null; + + + + /* Buffered input */ +// this._buffer = Array(); +// this._update_interval = 1000; /* ms to wait, 1000 = 1 second */ +// setInterval(function(){ +// var tmp = buffer; /* Switch the buffer out quickly so we aren't scrambled +// if you addToBuffer in the middle of this */ +// buffer = Array(); +// $thisdocument.getElementById(htmlId).innerHTML = tmp.join(""); +// //document.getElementById(htmlId).innerHTML = tmp.join(""); +// }, wait); +// +//addToBuffer = function(html){ +// buffer.push(html); +//}; + + /* Plugin events */ + + /* Setup query and record handlers */ + + // Explain this will allow query events to be handled + // What happens when we don't define some events ? + // Some can be less efficient + this.listen_query(options.query_uuid); + this.listen_query(options.query_all_uuid, 'all'); + + /* GUI setup and event binding */ + // call function + this._display_legends(); + this._init_top(); + this._init_left(); + this._init_grid(); + //this._test(); + + }, + + _test: function() + { + data = { + "name":"OWAMP", + "statusLabels":[ + "Loss is 0",null,"Loss is greater than 0","Unable to retrieve data","Check has not yet run" + ], + "lastUpdateTime":1385376538, + "rows":[ + {"name":"200.128.79.100","uri":"/maddash/grids/OWAMP/200.128.79.100"}, + {"name":"ata.larc.usp.br","uri":"/maddash/grids/OWAMP/ata.larc.usp.br"}, + {"name":"mon-lt.fibre.cin.ufpe.br","uri":"/maddash/grids/OWAMP/mon-lt.fibre.cin.ufpe.br"} + ], + "columnNames":[ + "200.128.79.100","ata.larc.usp.br","mon-lt.fibre.cin.ufpe.br" + ], + "checkNames": ["Loss","Loss Reverse"], + "grid": [ + [ + /* First line */ + null, + [{ + "message":" No one-way delay data returned for direction where src=200.128.79.100 dst=ata.larc.usp.br", + "status":3, + "prevCheckTime":1385376238, + "uri":"/maddash/grids/OWAMP/200.128.79.100/ata.larc.usp.br/Loss" + },{ + "message":" No one-way delay data returned for direction where src=ata.larc.usp.br dst=200.128.79.100", + "status":2,"prevCheckTime":1385376178, + "uri":"/maddash/grids/OWAMP/200.128.79.100/ata.larc.usp.br/Loss+Reverse" + }],[{ + "message":" Loss is 100.000% ", + "status":2, + "prevCheckTime":1385374877, + "uri":"/maddash/grids/OWAMP/200.128.79.100/mon-lt.fibre.cin.ufpe.br/Loss" + },{ + "message":" Loss is 100.000% ", + "status":2, + "prevCheckTime":1385375498, + "uri":"/maddash/grids/OWAMP/200.128.79.100/mon-lt.fibre.cin.ufpe.br/Loss+Reverse" + }] + ],[ + /* Second line */ + [{ + "message":" Unable to contact MA. Please check that the MA is running and the URL is correct.", + "status":3, + "prevCheckTime":1385376037, + "uri":"/maddash/grids/OWAMP/ata.larc.usp.br/200.128.79.100/Loss" + }, { + "message":" Unable to contact MA. Please check that the MA is running and the URL is correct.", + "status":3, + "prevCheckTime":1385376117, + "uri":"/maddash/grids/OWAMP/ata.larc.usp.br/200.128.79.100/Loss+Reverse" + }], + null, + [{ + "message":" Unable to contact MA. Please check that the MA is running and the URL is correct.", + "status":3, + "prevCheckTime":1385376117, + "uri":"/maddash/grids/OWAMP/ata.larc.usp.br/mon-lt.fibre.cin.ufpe.br/Loss" + }, { + "message":" Unable to contact MA. Please check that the MA is running and the URL is correct.", + "status":3, + "prevCheckTime":1385376017, + "uri":"/maddash/grids/OWAMP/ata.larc.usp.br/mon-lt.fibre.cin.ufpe.br/Loss+Reverse" + }] + ],[ + /* Third line */ + [{ + "message":" Loss is 100.000% ", + "status":2, + "prevCheckTime":1385376478, + "uri":"/maddash/grids/OWAMP/mon-lt.fibre.cin.ufpe.br/200.128.79.100/Loss" + },{ + "message":" Loss is 100.000% ", + "status":2, + "prevCheckTime":1385375958, + "uri":"/maddash/grids/OWAMP/mon-lt.fibre.cin.ufpe.br/200.128.79.100/Loss+Reverse" + }], [{ + "message":" No one-way delay data returned for direction where src=mon-lt.fibre.cin.ufpe.br dst=ata.larc.usp.br", + "status":3, + "prevCheckTime":1385376538, + "uri":"/maddash/grids/OWAMP/mon-lt.fibre.cin.ufpe.br/ata.larc.usp.br/Loss" + },{ + "message":" No one-way delay data returned for direction where src=ata.larc.usp.br dst=mon-lt.fibre.cin.ufpe.br", + "status":3, + "prevCheckTime":1385376358, + "uri":"/maddash/grids/OWAMP/mon-lt.fibre.cin.ufpe.br/ata.larc.usp.br/Loss+Reverse" + }], + null + ] + ] + } + this._render(data); + }, + + /* ------------------------------------------------------------------ */ + /* Accessors */ + /* ------------------------------------------------------------------ */ + + setClickHandler: function(f) + { + this._handleClick = f; + }, + + setCellSize: function(value) + { + this._cellSize = value; + }, + + setCellPadding: function(value) + { + this._cellPadding = value; + }, + + setTextBlockSize: function(value) + { + this._textBlockSize = value; + }, + + + + + /* PLUGIN EVENTS */ + // on_show like in querytable + + + /* GUI EVENTS */ + + // a function to bind events here: click change + // how to raise manifold events + + + /* GUI MANIPULATION */ + + /* XXX */ + + + /* TEMPLATES */ + + // see in the html template + // How to load a template, use of mustache + + /* QUERY HANDLERS */ + + // How to make sure the plugin is not desynchronized + // He should manifest its interest in filters, fields or records + // functions triggered only if the proper listen is done + + // no prefix + + on_filter_added: function(filter) + { + + }, + + // ... be sure to list all events here + + /* RECORD HANDLERS */ + on_all_new_record: function(record) + { + var key_value = record['hrn']; + if (!(this._map_elements.hasOwnProperty(key_value))) { + /* Add the key_value to the buffer to be drawn */ + this._buffer_key_list.add(key_value); + /* Assign coordinates */ + this._map_elements[key_value] = this._num_elements++; + } + /* Add the record to the buffer to be drawn */ + this._buffer_records.add(record); + }, + + /* INTERNAL FUNCTIONS */ + + _render: function(data) + { + //TODO: Set title + //d3.select("#dashboard_name").html(dashboard.name + " Dashboard"); + // XXX OLD XXX d3.select("#" + this.parent).html(""); + //this.elmt().html('') + + this.display_component(data); + }, + + _init_left: function() + { + var self = this; + this._left_element = this._canvas.append("div") + .attr("class", "gleft") + .style("overflow-y", "scroll") + .style("height", "400px") + .append("svg:svg") + .attr("width", self._text_block_size) + .attr("height", self._max_height_elements * (self._cellsize + 2*self._cellpadding) + 1000) + + }, + + _process_left: function(key_list) + { + var self = this; + + this._left_element = this._left_element + .selectAll(".rname") + .data(key_list) + .enter() + .append("g") + .attr("class", function(d,i){return "grow" + i}) + .attr("transform", function(d,i){return "translate(0,"+(i*(self._cellsize+2*self._cellpadding))+")"}) + + this._left_element.append("svg:rect") + .attr("class", function(d,i){return "grow" + i}) + .attr("x",0).attr("y",0) + .attr("width",this._text_block_size).attr("height",(this._cellsize+2*this._cellpadding)) + + this._left_element.append("svg:text") + .attr("class", "gtext") + .attr("transform", "translate("+ (this._text_block_size-5) +",0)") + .text(function(d,i){return d}) //strdata.rows[i].name}) + .attr("text-anchor", "end") + .attr("dy", "1.1em") + + // XXX Let's generate fake records to create all rows + var records = Array(); + for(var i = 0; i < key_list.length; i++) { + for(var j = 0; j < key_list.length; j++) { + records.push({ + 'source': key_list[i], + 'destination': key_list[j], + 'value': Math.floor(Math.random()*4) /* 0 1 2 3 */ + }); + } + } + + // Create the rows + this._row_element = this._grid_element.selectAll(".grow") + .data(key_list) + .enter() + .append("div") + .attr("class", function(d,i){return "grow grow" + i}) + .style("width", "100%") + .style("z-index", 1000) + // jordan + .style('position', 'absolute') +/* + var cells = this._row_element.selectAll('.gcell') + .data(records) //function(d,r){ return d.map(function(d,i){ return {celldata:d, row:r}}) }, function(d) { return d['source'] + '--' + d['destination']; }) + .enter() + .append("div") + .attr("class", "gcell") + .style("height", self._cellsize +"px") + .style("width", self._cellsize +"px") + .style("margin", (self._cellpadding) +"px") + + .on("mouseover", function(d,i){ + selected_row = d.row; // We could find the row from the _map_elements + selected_col = i; + if(d.celldata){ + d3.select(this).style("margin", (self._cellpadding-1) +"px"); + d3.select(this).classed("shadow", true); + } + this._canvas.selectAll(".gcol"+ i).classed("gactive", true); + this._canvas.selectAll(".grow"+ d.row).classed("gactive", true); + }) + .on("mouseout", function(d,i){ + // d3.select(this).classed("shadow", false); + // d3.select(this).style("background-color", color.brighter()); + d3.select(this).style("margin", (self._cellpadding) +"px"); + d3.select(this).classed("shadow", false); + this._canvas.selectAll(".gcol"+ i).classed("gactive", false); + this._canvas.selectAll(".grow"+ d.row).classed("gactive", false); + }) +*/ + }, + + _init_top: function() + { + var self = this; + this._top_element = this._canvas.append("div") + .attr("class", "gtop") + .style("margin-left", self._text_block_size + "px") + .style("float", "left") + .style("overflow-x", "scroll") + .style("width", "960px") + .append("svg:svg") + .attr("height", self._text_block_size) + .attr("width", self._max_width_elements * (self._cellsize + 2*self._cellpadding) + 90 + 1000) + + }, + + + _process_top: function(key_list) + { + var self = this; + + this._top_element = this._top_element + .selectAll(".rname") + .data(key_list) + .enter() + .append("g") + .attr("class", function(d,i){return "gcol" + i}) + .attr("transform", function(d,i){ return "translate("+(i*(self._cellsize+2*self._cellpadding))+",0)"}) + + this._top_element.append("svg:rect") + .attr("class", function(d,i){return "gcol" + i}) + .attr("x",0).attr("y",0) + .attr("transform", "rotate(45,0,"+ self._text_block_size +") translate (-0.5,3)") + .attr("height",self._text_block_size).attr("width",(self._cellsize+self._cellpadding)) + //.attr("transform", "rotate(35,"+ (this._cellsize+this._cellpadding)/2 + "," + this._text_block_size/2 + ")") + + + this._top_element.append("svg:text") + .attr("class", "gtext") + .attr("text-anchor", "start") + .attr("dy", "1.5em") + .attr("dx", "1em") + .attr("transform", "rotate(-45,0,"+ self._text_block_size +") translate(0,"+ (self._text_block_size-5) + ")") + .text(function(d,i){return d}) + + // Create the columns... + var cols = this._grid_element.selectAll(".gcol") + .data(key_list) + .enter() + .append("div") + .attr("class", function(d,i){return "gcol gcol" + i}) + .style("width", (self._cellsize+2*self._cellpadding) + "px") + .style("height", "100%") + .style("left", function(d,i){return (i*(self._cellsize+2*self._cellpadding)) + "px"}) + }, + + _process_key_list: function() + { + console.log("process key list"); + var key_list = this._buffer_key_list.get(); + this._process_top(key_list); + this._process_left(key_list); + console.log("process key list done"); + }, + + _display_legends: function() + { + // Color scale = the number of different statuses + colorscale.domain(d3.range(0, this._labels.length)); + + // Labels + var legendsdata = this._labels + .map(function(d,i){ return {label:d, color:colorscale(i)} }) + //.filter(function(d,i){return d.label === null ? false : true}) + + // Clear and redraw legend + d3.select("#"+this._legend).html("") + + var legends = d3.select("#"+this._legend) + .selectAll(".legend") + .data(legendsdata) + .enter() + .append("div").attr("class", "legend"); + legends.append("div") + .attr("class", "lsymbol") + .style("background", function(d,i){return d.color}) + .style("display", function(d,i){return d.label === null ? "none" : "block"}) + legends.append("div") + .attr("class", "ltext") + .text(function(d,i){return d.label}) + .style("display", function(d,i){return d.label === null ? "none" : "block"}) + }, + + _init_grid: function() + { + this._grid_element = this._canvas + .style("width", this._ncols * (this._cellsize + 2*this._cellpadding) + 110 + this._text_block_size) + .append("div") + .attr("class", "ggrid") + // jordan + .style('top', '165px') + .style('left', '145px') + .style('float', 'none') + .style('width', '2000px') + .style('height', '1000px') + + }, + + _process_records: function() + { + var self = this; + var records = this._buffer_records.get(); + console.log("processing records"); + + // XXX Let's generate fake records instead... NxN + var _records = Array(); + for(var i = 0; i < records.length; i++) { + for(var j = 0; j < records.length; j++) { + if (Math.random() < 0.2) { /* one out of 5 */ + _records.push({ + 'source': records[i]['hrn'], + 'destination': records[j]['hrn'], + 'value': Math.floor(Math.random()*4) /* 0 1 2 3 */ + }) + } + } + } + + // + + // Rows and columns have been created + + var color = ""; + var selected_row = 0; + var selected_col = 0; + var cells = this._row_element.selectAll(".gcell") + /* Not sure to understand this part of the code */ + //uses data.grid initially... this is not good anymore + .data(_records, function(d) { return d['source'] + '--' + d['destination']; }) + .style("background", function(d,i){ + return colorscale(parseInt(d.value)); + }) + + // Associate a tooltip to each cell thanks to tipsy + /* + this.elmt().find(".gcell").each(function(i,d){ + var data = d3.select(this).data()[0]; + if(data.celldata!=null){ + var html = "
" + (data.celldata[0]? data.celldata[0].message : "") + "
" + (data.celldata[1]? data.celldata[1].message : "") + "
"; + $(this).tipsy({ + html :true, + opacity: 0.9, + title : function(){ + return html + }}) + } + }) + */ + + // This seems to create the coloured subcells + var temp = cells.selectAll(".gsubcell") + .data(function(d,i){return d.celldata===null? [] : d.celldata }) + .enter() + .append("div"); + temp + .style("height", this._cellsize/2 +"px") + .style("background", function(d,i){ + return colorscale(parseInt(d.status)); + }) + .on("click", function(d,i){ //CHANGE FROM PORTAL + var that = this; + if(d!=null && d.uri!=null && self.handleClick != null){ + self.handleClick(d); + } + }) + console.log("processing records done"); + }, + + display_component: function(data) + { + this._display_grid_container(data); + }, + + _handleClick: function(d) + { + var uri = d.uri; + $.getJSON(uri, function(data) { + var href = data['history'][0].returnParams.graphUrl.replace("https", "http"); + window.open( href, "Graph", "menubar=0,location=0,height=700,width=700" ); + }) + + } + + + }); + + /* Plugin registration */ + $.plugin('MadDash', MadDash); + + // TODO Here use cases for instanciating plugins in different ways like in the pastie. + +})(jQuery); diff --git a/plugins/maddash/templates/maddash.html b/plugins/maddash/templates/maddash.html new file mode 100644 index 00000000..8e678e91 --- /dev/null +++ b/plugins/maddash/templates/maddash.html @@ -0,0 +1,5 @@ +
+
+
+
+ -- 2.47.0