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 zcmZ?wbhEHbkiMH{6k);cGnd{85pbq D1PBXP literal 0 HcmV?d00001 diff --git a/plugins/maddash/static/js/.maddash.js.swp b/plugins/maddash/static/js/.maddash.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..e8559a97ca12e45a66ed545b0673e8d87c1445ac GIT binary patch literal 40960 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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.43.0