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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 literal 0 HcmV?d00001 diff --git a/plugins/maddash/static/js/jquery.tipsy.js b/plugins/maddash/static/js/jquery.tipsy.js new file mode 100644 index 00000000..2eb567a3 --- /dev/null +++ b/plugins/maddash/static/js/jquery.tipsy.js @@ -0,0 +1,198 @@ +// tipsy, facebook style tooltips for jquery +// version 1.0.0a +// (c) 2008-2010 jason frame [jason@onehackoranother.com] +// releated under the MIT license + +(function($) { + + function fixTitle($ele) { + if ($ele.attr('title') || typeof($ele.attr('original-title')) != 'string') { + $ele.attr('original-title', $ele.attr('title') || '').removeAttr('title'); + } + } + + function Tipsy(element, options) { + this.$element = $(element); + this.options = options; + this.enabled = true; + fixTitle(this.$element); + } + + Tipsy.prototype = { + show: function() { + var title = this.getTitle(); + if (title && this.enabled) { + var $tip = this.tip(); + + $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); + $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity + $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body); + + var pos = $.extend({}, this.$element.offset(), { + width: this.$element[0].offsetWidth, + height: this.$element[0].offsetHeight + }); + + var actualWidth = $tip[0].offsetWidth, actualHeight = $tip[0].offsetHeight; + var gravity = (typeof this.options.gravity == 'function') + ? this.options.gravity.call(this.$element[0]) + : this.options.gravity; + + var tp; + switch (gravity.charAt(0)) { + case 'n': + tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; + break; + case 's': + tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; + break; + case 'e': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; + break; + case 'w': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; + break; + } + + if (gravity.length == 2) { + if (gravity.charAt(1) == 'w') { + tp.left = pos.left + pos.width / 2 - 15; + } else { + tp.left = pos.left + pos.width / 2 - actualWidth + 15; + } + } + + $tip.css(tp).addClass('tipsy-' + gravity); + + if (this.options.fade) { + $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); + } else { + $tip.css({visibility: 'visible', opacity: this.options.opacity}); + } + } + }, + + hide: function() { + if (this.options.fade) { + this.tip().stop().fadeOut(function() { $(this).remove(); }); + } else { + this.tip().remove(); + } + }, + + getTitle: function() { + var title, $e = this.$element, o = this.options; + fixTitle($e); + var title, o = this.options; + if (typeof o.title == 'string') { + title = $e.attr(o.title == 'title' ? 'original-title' : o.title); + } else if (typeof o.title == 'function') { + title = o.title.call($e[0]); + } + title = ('' + title).replace(/(^\s*|\s*$)/, ""); + return title || o.fallback; + }, + + tip: function() { + if (!this.$tip) { + this.$tip = $('
').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.51.1