From 8c344999d6ee3b3a9172a1068a77c83af77a7aee Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jordan=20Aug=C3=A9?= Date: Fri, 9 Aug 2013 14:14:37 +0200 Subject: [PATCH] plugins: migrated hazelnut and googlemaps to the new plugin class, updated plugin template --- auth/css/login.css | 2 +- devel/server-loop.sh | 1 + manifold/js/manifold.js | 40 +- manifold/js/plugin.js | 62 +- myslice/settings.py | 6 +- plugins/debug_platform/__init__.py | 2 +- plugins/googlemap/__init__.py | 0 plugins/googlemap/static/js/googlemap.js | 341 ------- .../googlemap.py => googlemaps/__init__.py} | 8 +- .../static/css/googlemaps.css} | 0 .../static/googlemaps.html} | 0 plugins/googlemaps/static/js/googlemaps.js | 248 +++++ .../static/js/markerclusterer.js | 0 .../static/js/markerclusterer_compiled.js | 0 .../static/js/markerclusterer_packed.js | 0 plugins/hazelnut/DataTables.php | 116 --- plugins/hazelnut/static/hazelnut.html | 2 +- plugins/hazelnut/static/js/hazelnut.js | 946 +++++++----------- plugins/myplugin/static/js/myplugin.js | 31 +- trash/pluginview.py | 37 +- trash/sliceview.py | 6 +- 21 files changed, 763 insertions(+), 1085 deletions(-) delete mode 100644 plugins/googlemap/__init__.py delete mode 100644 plugins/googlemap/static/js/googlemap.js rename plugins/{googlemap/googlemap.py => googlemaps/__init__.py} (89%) rename plugins/{googlemap/static/css/googlemap.css => googlemaps/static/css/googlemaps.css} (100%) rename plugins/{googlemap/static/googlemap.html => googlemaps/static/googlemaps.html} (100%) create mode 100644 plugins/googlemaps/static/js/googlemaps.js rename plugins/{googlemap => googlemaps}/static/js/markerclusterer.js (100%) rename plugins/{googlemap => googlemaps}/static/js/markerclusterer_compiled.js (100%) rename plugins/{googlemap => googlemaps}/static/js/markerclusterer_packed.js (100%) delete mode 100644 plugins/hazelnut/DataTables.php diff --git a/auth/css/login.css b/auth/css/login.css index 06528eee..11444f7f 100644 --- a/auth/css/login.css +++ b/auth/css/login.css @@ -6,7 +6,7 @@ input.login { } input[type="password"] { border: solid 1px #444; - background-image: url( '/all-static/img/form_input_password.png' ); + background-image: url( '../img/form_input_password.png' ); background-repeat: repeat-x; background-position: top; } diff --git a/devel/server-loop.sh b/devel/server-loop.sh index 94215e88..4ff84bf1 100755 --- a/devel/server-loop.sh +++ b/devel/server-loop.sh @@ -9,4 +9,5 @@ hostname | grep -q '^z' && port=8080 || port=80 while true; do ./manage.py collectstatic --noinput ./manage.py runserver 0.0.0.0:$port + sleep 1 done diff --git a/manifold/js/manifold.js b/manifold/js/manifold.js index 667ad34b..f7c098a4 100644 --- a/manifold/js/manifold.js +++ b/manifold/js/manifold.js @@ -370,14 +370,11 @@ var manifold = { raise_event_handler: function(type, query_uuid, event_type, value) { - if (type == 'query') { - var channels = [ manifold.get_query_channel(query_uuid), manifold.get_query_channel('*') ]; - } else if (type == 'record') { - var channels = [ manifold.get_record_channel(query_uuid), manifold.get_record_channel('*') ]; - - } else { + if ((type != 'query') && (type != 'record')) throw 'Incorrect type for manifold.raise_event()'; - } + + var channels = [ manifold.get_channel(type, query_uuid), manifold.get_channel(type, '*') ]; + $.each(channels, function(i, channel) { if (value === undefined) $('.plugin').trigger(channel, [event_type]); @@ -468,35 +465,18 @@ var manifold = { }, /* Publish/subscribe channels for internal use */ - get_query_channel: function(uuid) { return '/query/' + uuid }, - get_record_channel: function(uuid) { return '/record/' + uuid }, + get_channel: function(type, query_uuid) + { + if ((type !== 'query') && (type != 'record')) + return null; + return '/' + type + '/' + query_uuid; + }, }; // manifold object /* ------------------------------------------------------------ */ (function($) { - /* NEW PLUGIN API - * - * NOTE: Since we don't have a plugin class, we are extending all jQuery - * plugins... - */ - - /*! - * \brief Associates a query handler to the current plugin - * \param uuid (string) query uuid - * \param handler (function) handler callback - */ - $.fn.set_query_handler = function(uuid, handler) - { - this.on(manifold.get_query_channel(uuid), handler); - } - - $.fn.set_record_handler = function(uuid, handler) - { - this.on(manifold.get_record_channel(uuid), handler); - } - // OLD PLUGIN API: extend jQuery/$ with pubsub capabilities // https://gist.github.com/661855 var o = $({}); diff --git a/manifold/js/plugin.js b/manifold/js/plugin.js index 84ebbc4f..b72cc30c 100644 --- a/manifold/js/plugin.js +++ b/manifold/js/plugin.js @@ -40,7 +40,7 @@ var Plugin = Class.extend({ return (typeof this.on_filter_added === 'function'); }, - _query_handler: function(e, event_type, data) + _query_handler: function(prefix, event_type, data) { // We suppose this.query_handler_prefix has been defined if this // callback is triggered @@ -68,7 +68,7 @@ var Plugin = Class.extend({ return; } // switch - fn = 'on_' + this.query_handler_prefix + fn; + fn = 'on_' + prefix + fn; if (typeof this[fn] === 'function') { // call with data as parameter // XXX implement anti loop @@ -76,9 +76,51 @@ var Plugin = Class.extend({ } }, - listen_query: function(query_uuid, prefix) { - this.query_handler_prefix = (typeof prefix === 'undefined') ? '' : (prefix + manifold.separator); - this.$element.on(manifold.get_query_channel(query_uuid), $.proxy(this._query_handler, this)); + _record_handler: function(prefix, event_type, record) + { + // We suppose this.query_handler_prefix has been defined if this + // callback is triggered + var fn; + switch(event_type) { + case NEW_RECORD: + fn = 'new_record'; + break; + case CLEAR_RECORDS: + fn = 'clear_records'; + break; + case IN_PROGRESS: + fn = 'query_in_progress'; + break; + case DONE: + fn = 'query_done'; + break; + default: + return; + } // switch + + fn = 'on_' + prefix + fn; + if (typeof this[fn] === 'function') { + // call with data as parameter + // XXX implement anti loop + this[fn](record); + } + }, + + get_handler_function: function(type, prefix) + { + + return $.proxy(function(e, event_type, record) { + return this['_' + type + '_handler'](prefix, event_type, record); + }, this); + }, + + listen_query: function(query_uuid, prefix) + { + // default: prefix = '' + prefix = (typeof prefix === 'undefined') ? '' : (prefix + '_'); + + this.$element.on(manifold.get_channel('query', query_uuid), this.get_handler_function('query', prefix)); + this.$element.on(manifold.get_channel('record', query_uuid), this.get_handler_function('record', prefix)); }, default_options: {}, @@ -155,4 +197,14 @@ var Plugin = Class.extend({ return array[1]; }, + spin: function() + { + manifold.spin(this.element); + }, + + unspin: function() + { + manifold.spin(this.element, false); + }, + }); diff --git a/myslice/settings.py b/myslice/settings.py index 269bde33..06e4eae7 100644 --- a/myslice/settings.py +++ b/myslice/settings.py @@ -98,10 +98,12 @@ STATICFILES_DIRS = ( # Don't forget to use absolute paths, not relative paths. # thierry os.path.join(ROOT,'all-static'), ('js', os.path.join(ROOT,'manifold/js')), - ('js', os.path.join(ROOT,'unfold/js')), - ('js', os.path.join(ROOT,'auth/js')), ('css', os.path.join(ROOT,'manifold/css')), + ('js', os.path.join(ROOT,'unfold/js')), ('css', os.path.join(ROOT,'unfold/css')), + ('js', os.path.join(ROOT,'auth/js')), + ('css', os.path.join(ROOT,'auth/css')), + ('img', os.path.join(ROOT,'auth/img')), ('css', os.path.join(ROOT,'views/css')), ('img', os.path.join(ROOT,'views/img')), ) diff --git a/plugins/debug_platform/__init__.py b/plugins/debug_platform/__init__.py index 5dbb2b63..543fa5c0 100644 --- a/plugins/debug_platform/__init__.py +++ b/plugins/debug_platform/__init__.py @@ -1,7 +1,7 @@ from unfold.plugin import Plugin from unfold.page import Page from plugins.code_editor import CodeEditor -from plugins.hazelnut.hazelnut import Hazelnut +from plugins.hazelnut import Hazelnut class DebugPlatform(Plugin): diff --git a/plugins/googlemap/__init__.py b/plugins/googlemap/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/plugins/googlemap/static/js/googlemap.js b/plugins/googlemap/static/js/googlemap.js deleted file mode 100644 index c9d39232..00000000 --- a/plugins/googlemap/static/js/googlemap.js +++ /dev/null @@ -1,341 +0,0 @@ -/** - * Description: display a query result in a googlemap - * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA - * License: GPLv3 - */ - -/* - * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function - * Expression) that maps it to the dollar sign so it can't be overwritten by - * another library in the scope of its execution. - */ - -(function($){ - - var PLUGIN_NAME = 'GoogleMap'; - - // routing calls - jQuery.fn.GoogleMap = function( method ) { - if ( methods[method] ) { - return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); - } else if ( typeof method === 'object' || ! method ) { - return methods.init.apply( this, arguments ); - } else { - jQuery.error( 'Method ' + method + ' does not exist on jQuery.' + PLUGIN_NAME ); - } - }; - - /*************************************************************************** - * Public methods - ***************************************************************************/ - - var methods = { - - /** - * @brief Plugin initialization - * @param options : an associative array of setting values - * @return : a jQuery collection of objects on which the plugin is - * applied, which allows to maintain chainability of calls - */ - init : function( options ) { - - return this.each(function(){ - - var $this = $(this); - - /* An object that will hold private variables and methods */ - var plugin = new GoogleMaps(options); - $this.data('Manifold', plugin); - - plugin.initialize(); - - /* Events */ - $this.on('show.' + PLUGIN_NAME, methods.show); - - $this.set_query_handler(options.query_uuid, plugin.query_handler); - $this.set_record_handler(options.query_uuid, plugin.record_handler); - $this.set_record_handler(options.query_all_uuid, plugin.record_handler_all); - - }); // this.each - }, // init - - /** - * @brief Plugin destruction - * @return : a jQuery collection of objects on which the plugin is - * applied, which allows to maintain chainability of calls - */ - destroy : function( ) { - - return this.each(function() { - var $this = $(this); - var hazelnut = $this.data('Manifold'); - - // Unbind all events using namespacing - $(window).unbind(PLUGIN_NAME); - - // Remove associated data - hazelnut.remove(); - $this.removeData('Manifold'); - }); - - }, // destroy - - show : function( ) { - google.maps.event.trigger(map, 'resize'); - } // show - - }; // var methods; - - /*************************************************************************** - * Plugin object - ***************************************************************************/ - - function GoogleMaps(options) - { - /* member variables */ - this.options = options; - - // query status - this.received_all = false; - this.received_set = false; - this.in_set_buffer = Array(); - - var object = this; - - /** - */ - this.initialize = function() { - this.map = null; - this.markerCluster = null; - this.markers = []; - this.coords = new Array(); - - var myLatlng = new google.maps.LatLng(options.latitude, options.longitude); - var myOptions = { - zoom: options.zoom, - center: myLatlng, - mapTypeId: google.maps.MapTypeId.ROADMAP - } - - this.map = new google.maps.Map(document.getElementById("map"), myOptions); - this.infowindow = new google.maps.InfoWindow(); - } - - /** - */ - this.new_record = function(record) - { - - // get the coordinates - var latitude=get_value(record['latitude']); - var longitude=get_value(record['longitude']); - var hash = latitude + longitude; - - // check to see if we've seen this hash before - if(this.coords[hash] == null) { - // get coordinate object - var myLatlng = new google.maps.LatLng(latitude, longitude); - // store an indicator that we've seen this point before - this.coords[hash] = 1; - } else { - // add some randomness to this point 1500 = 100 meters, 15000 = 10 meters - var lat = latitude + (Math.random() -.5) / 1500; - var lng = longitude + (Math.random() -.5) / 1500; - - // get the coordinate object - var myLatlng = new google.maps.LatLng(lat, lng); - } - // If the node is attached to the slice, action will be Remove; else action will be add to slice - if (typeof(record['sliver']) != 'undefined') { - data.current_resources.push(record['urn']); - action="del"; - action_class="ui-icon-circle-minus"; - action_message="Remove from slice"; - }else{ - action="add"; - action_class="ui-icon-circle-plus"; - action_message="Add to slice"; - } - // XXX not working - if (!(record['latitude'])) { - return true; - } - - //jQuery(".map-button").click(button_click); - //if(jQuery.inArray(record, rows)>-1){ - var marker = new google.maps.Marker({ - position: myLatlng, - title: get_value(record['hostname']), - // This should be done by the rendering - content: '

Agent: ' + get_value(record['ip']) + ' (' + get_value(record['resource_hrn']) + ')
Platform: ' + get_value(record['platform'])+'

' + - '
'+ - ''+action_message+ - '
' - }); - - this.addInfoWindow(marker, object.map); - object.markers.push(marker); - //} - - }; - - this.addInfoWindow = function(marker, map) { - google.maps.event.addListener(marker, 'click', function () { - if(object.infowindow){ - object.infowindow.close(); - } - object.infowindow.setContent(marker.content);// = new google.maps.InfoWindow({ content: marker.content }); - object.infowindow.open(map, marker); - // onload of the infowindow on the map, bind a click on a button - google.maps.event.addListener(object.infowindow, 'domready', function() { - jQuery('.map-button').unbind('click'); -// jQuery(".map-button").click({instance: instance_, infoWindow: object.infowindow}, button_click); - }); - }); - } - - - this.set_checkbox = function(record) - { - // XXX urn should be replaced by the key - // XXX we should enforce that both queries have the same key !! - //checkbox_id = "#hazelnut-checkbox-" + object.options.plugin_uuid + "-" + unfold.escape_id(record[ELEMENT_KEY].replace(/\\/g, '')) - //$(checkbox_id, object.table.fnGetNodes()).attr('checked', true); - } - - this.record_handler = function(e, event_type, record) - { - // elements in set - switch(event_type) { - case NEW_RECORD: - /* NOTE in fact we are doing a join here */ - if (object.received_all) - // update checkbox for record - object.set_checkbox(record); - else - // store for later update of checkboxes - object.in_set_buffer.push(record); - break; - case CLEAR_RECORDS: - // nothing to do here - break; - case IN_PROGRESS: - manifold.spin($(this)); - break; - case DONE: - if (object.received_all) - manifold.spin($(this), false); - object.received_set = true; - break; - } - }; - - this.record_handler_all = function(e, event_type, record) - { - // all elements - switch(event_type) { - case NEW_RECORD: - // Add the record to the table - object.new_record(record); - break; - case CLEAR_RECORDS: - // object.table.fnClearTable(); - break; - case IN_PROGRESS: - manifold.spin($(this)); - break; - case DONE: - - // MarkerClusterer - object.markerCluster = new MarkerClusterer(object.map, object.markers, {zoomOnClick: false}); - google.maps.event.addListener(object.markerCluster, "clusterclick", function (cluster) { - var markers = cluster.getMarkers(); - var bounds = new google.maps.LatLngBounds(); - /* - * date: 24/05/2012 - * author: lbaron - * Firefox JS Error - replaced $.each by JQuery.each - */ - jQuery.each(markers, function(i, marker){ - bounds.extend(marker.getPosition()); - }); - - //map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds)); - object.map.fitBounds(bounds); - }); - - if (object.received_set) { - /* XXX needed ? XXX We uncheck all checkboxes ... */ - $("[id^='datatables-checkbox-" + object.options.plugin_uuid +"']").attr('checked', false); - - /* ... and check the ones specified in the resource list */ - $.each(object.in_set_buffer, function(i, record) { - object.set_checkbox(record); - }); - - manifold.spin($(this), false); - } - object.received_all = true; - break; - } - }; - - - this.query_handler = function(e, event_type, query) - { - // This replaces the complex set_query function - // The plugin does not need to remember the query anymore - switch(event_type) { - // Filters - case FILTER_ADDED: - case FILTER_REMOVED: - case CLEAR_FILTERS: - break; - - // Fields - /* Hide/unhide columns to match added/removed fields */ - case FIELD_ADDED: - break; - case FIELD_REMOVED: - break; - case CLEAR_FIELDS: - alert('GoogleMaps::clear_fields() not implemented'); - break; - } // switch - - - } - - function button_click(e){ - var op_value=this.id.split("/"); - if(op_value.length>0){ - var value = op_value[1]; - manifold.raise_event(object.options.query_uuid, (op_value[0] == 'add')?SET_ADD:SET_REMOVED, value); - } - } // function button_click() - } - - // clear and replace -// jQuery.each(data.results, function(i, row){ -// jQuery.each(query.filter, function (idx, filter){ -// if(get_value(row[filter[0]])==filter[2]){ -// rows.push(row); -// } -// }); -// }); -// data.markerCluster=[]; -// data.markers=[]; -// var myLatlng = new google.maps.LatLng(34.397, 150.644); -// var myOptions = { -// zoom: 2, -// center: myLatlng, -// mapTypeId: google.maps.MapTypeId.ROADMAP -// } -// map = new google.maps.Map(jQuery('#map')[0],myOptions); -// data.map=map; -// //map.clearMarkers(); -// update_map(e, rows); -// } -// } - -})( jQuery ); diff --git a/plugins/googlemap/googlemap.py b/plugins/googlemaps/__init__.py similarity index 89% rename from plugins/googlemap/googlemap.py rename to plugins/googlemaps/__init__.py index bce84f54..38c6564c 100644 --- a/plugins/googlemap/googlemap.py +++ b/plugins/googlemaps/__init__.py @@ -1,6 +1,6 @@ from unfold.plugin import Plugin -class GoogleMap (Plugin): +class GoogleMaps (Plugin): # set checkboxes if a final column with checkboxes is desired # pass columns as the initial set of columns @@ -16,7 +16,7 @@ class GoogleMap (Plugin): self.zoom=zoom def template_file (self): - return "googlemap.html" + return "googlemaps.html" def template_env (self, request): env={} @@ -25,13 +25,13 @@ class GoogleMap (Plugin): def requirements (self): reqs = { 'js_files' : [ "https://maps.googleapis.com/maps/api/js?sensor=false", - "/js/googlemap.js", + "/js/googlemaps.js", "/js/markerclusterer.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/googlemap.css", + 'css_files' : [ "css/googlemaps.css", ], } return reqs diff --git a/plugins/googlemap/static/css/googlemap.css b/plugins/googlemaps/static/css/googlemaps.css similarity index 100% rename from plugins/googlemap/static/css/googlemap.css rename to plugins/googlemaps/static/css/googlemaps.css diff --git a/plugins/googlemap/static/googlemap.html b/plugins/googlemaps/static/googlemaps.html similarity index 100% rename from plugins/googlemap/static/googlemap.html rename to plugins/googlemaps/static/googlemaps.html diff --git a/plugins/googlemaps/static/js/googlemaps.js b/plugins/googlemaps/static/js/googlemaps.js new file mode 100644 index 00000000..e97d1f5f --- /dev/null +++ b/plugins/googlemaps/static/js/googlemaps.js @@ -0,0 +1,248 @@ +/** + * Description: display a query result in a Google map + * Copyright (c) 2012-2013 UPMC Sorbonne Universite - INRIA + * License: GPLv3 + */ + +(function($){ + + var GoogleMaps = Plugin.extend({ + + init: function(options, element) + { + this._super(options, element); + + /* Member variables */ + // query status + this.received_all = false; + this.received_set = false; + this.in_set_buffer = Array(); + + /* XXX Events XXX */ + this.$element.on('show.Datatables', this.on_show); + // TODO in destructor + // $(window).unbind('Hazelnut'); + + /* Setup query and record handlers */ + this.listen_query(options.query_uuid); + this.listen_query(options.query_all_uuid, 'all'); + + /* GUI setup and event binding */ + this.initialize_map(); + }, // init + + /* PLUGIN EVENTS */ + + on_show: function() + { + google.maps.event.trigger(map, 'resize'); + }, // on_show + + /* GUI EVENTS */ + + /* GUI MANIPULATION */ + + /** + */ + initialize_map: function() + { + this.map = null; + this.markerCluster = null; + this.markers = []; + this.coords = new Array(); + + var myLatlng = new google.maps.LatLng(this.options.latitude, this.options.longitude); + var myOptions = { + zoom: this.options.zoom, + center: myLatlng, + mapTypeId: google.maps.MapTypeId.ROADMAP + } + + this.map = new google.maps.Map(document.getElementById("map"), myOptions); + this.infowindow = new google.maps.InfoWindow(); + }, // initialize_map + + /** + */ + new_record: function(record) + { + // get the coordinates + var latitude=get_value(record['latitude']); + var longitude=get_value(record['longitude']); + var hash = latitude + longitude; + + // check to see if we've seen this hash before + if(this.coords[hash] == null) { + // get coordinate object + var myLatlng = new google.maps.LatLng(latitude, longitude); + // store an indicator that we've seen this point before + this.coords[hash] = 1; + } else { + // add some randomness to this point 1500 = 100 meters, 15000 = 10 meters + var lat = latitude + (Math.random() -.5) / 1500; + var lng = longitude + (Math.random() -.5) / 1500; + + // get the coordinate object + var myLatlng = new google.maps.LatLng(lat, lng); + } + // If the node is attached to the slice, action will be Remove; else action will be add to slice + if (typeof(record['sliver']) != 'undefined') { + data.current_resources.push(record['urn']); + action="del"; + action_class="ui-icon-circle-minus"; + action_message="Remove from slice"; + }else{ + action="add"; + action_class="ui-icon-circle-plus"; + action_message="Add to slice"; + } + // XXX not working + if (!(record['latitude'])) { + return true; + } + + //jQuery(".map-button").click(button_click); + //if(jQuery.inArray(record, rows)>-1){ + var marker = new google.maps.Marker({ + position: myLatlng, + title: get_value(record['hostname']), + // This should be done by the rendering + content: '

Agent: ' + get_value(record['ip']) + ' (' + get_value(record['resource_hrn']) + ')
Platform: ' + get_value(record['platform'])+'

' + + '
'+ + ''+action_message+ + '
' + }); + + this.addInfoWindow(marker, this.map); + this.markers.push(marker); + //} + + }, // new_record + + addInfoWindow: function(marker, map) + { + google.maps.event.addListener(marker, 'click', function () { + if(object.infowindow){ + object.infowindow.close(); + } + object.infowindow.setContent(marker.content);// = new google.maps.InfoWindow({ content: marker.content }); + object.infowindow.open(map, marker); + // onload of the infowindow on the map, bind a click on a button + google.maps.event.addListener(object.infowindow, 'domready', function() { + jQuery('.map-button').unbind('click'); +// jQuery(".map-button").click({instance: instance_, infoWindow: object.infowindow}, button_click); + }); + }); + }, // addInfoWindow + + set_checkbox: function(record) + { + // XXX urn should be replaced by the key + // XXX we should enforce that both queries have the same key !! + //checkbox_id = "#hazelnut-checkbox-" + object.options.plugin_uuid + "-" + unfold.escape_id(record[ELEMENT_KEY].replace(/\\/g, '')) + //$(checkbox_id, object.table.fnGetNodes()).attr('checked', true); + }, // set_checkbox + + + /*************************** QUERY HANDLER ****************************/ + + /*************************** RECORD HANDLER ***************************/ + + on_new_record: function(record) + { + if (this.received_all) + // update checkbox for record + this.set_checkbox(record); + else + // store for later update of checkboxes + this.in_set_buffer.push(record); + }, + + on_clear_records: function(record) + { + + }, + + // Could be the default in parent + on_query_in_progress: function() + { + this.spin(); + }, + + on_query_done: function() + { + if (this.received_all) + this.unspin(); + this.received_set = true; + }, + + // all + + on_all_new_record: function(record) + { + this.new_record(record); + }, + + on_all_clear_records: function() + { + }, + + on_all_query_in_progress: function() + { + // XXX parent + this.spin(); + }, + + on_all_query_done: function() + { + + // MarkerClusterer + this.markerCluster = new MarkerClusterer(this.map, this.markers, {zoomOnClick: false}); + google.maps.event.addListener(this.markerCluster, "clusterclick", function (cluster) { + var markers = cluster.getMarkers(); + var bounds = new google.maps.LatLngBounds(); + /* + * date: 24/05/2012 + * author: lbaron + * Firefox JS Error - replaced $.each by JQuery.each + */ + jQuery.each(markers, function(i, marker){ + bounds.extend(marker.getPosition()); + }); + + //map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds)); + this.map.fitBounds(bounds); + }); + + var self = this; + if (this.received_set) { + /* XXX needed ? XXX We uncheck all checkboxes ... */ + $("[id^='datatables-checkbox-" + this.options.plugin_uuid +"']").attr('checked', false); + + /* ... and check the ones specified in the resource list */ + $.each(this.in_set_buffer, function(i, record) { + self.set_checkbox(record); + }); + + this.unspin(); + } + this.received_all = true; + + }, // on_all_query_done + + /************************** PRIVATE METHODS ***************************/ + + _button_click: function(e) + { + var op_value=this.id.split("/"); + if(op_value.length>0) { + var value = op_value[1]; + manifold.raise_event(this.options.query_uuid, (op_value[0] == 'add')?SET_ADD:SET_REMOVED, value); + } + } // button_click + + }); + + $.plugin('GoogleMaps', GoogleMaps); + +})(jQuery); diff --git a/plugins/googlemap/static/js/markerclusterer.js b/plugins/googlemaps/static/js/markerclusterer.js similarity index 100% rename from plugins/googlemap/static/js/markerclusterer.js rename to plugins/googlemaps/static/js/markerclusterer.js diff --git a/plugins/googlemap/static/js/markerclusterer_compiled.js b/plugins/googlemaps/static/js/markerclusterer_compiled.js similarity index 100% rename from plugins/googlemap/static/js/markerclusterer_compiled.js rename to plugins/googlemaps/static/js/markerclusterer_compiled.js diff --git a/plugins/googlemap/static/js/markerclusterer_packed.js b/plugins/googlemaps/static/js/markerclusterer_packed.js similarity index 100% rename from plugins/googlemap/static/js/markerclusterer_packed.js rename to plugins/googlemaps/static/js/markerclusterer_packed.js diff --git a/plugins/hazelnut/DataTables.php b/plugins/hazelnut/DataTables.php deleted file mode 100644 index 67a8985c..00000000 --- a/plugins/hazelnut/DataTables.php +++ /dev/null @@ -1,116 +0,0 @@ -uuid; - - $results = Array(); - $async = 1; - - - /* XXX required field in options : query_uuid */ - - - $query = Plugins::get_query_by_uuid($this->params['query_uuid']); - $is_unique = Plugins::get_key_filter($query); - $method_keys = Plugins::get_default_fields($query->method, $is_unique); - //$method_keys = Plugins::query_get_default_keys($options['query_uuid']); - //$fields = Plugins::query_get_fields($options['query_uuid']); - //$all_headers = $_SESSION['metadata']['nodes']['columns']; - //$fields = $all_headers; - - $fields = Plugins::metadata_get_fields($query->method); - - /* - * @author: lbaron - * date: 2012-05-29 - * debug columns QueryEditor to DataTables - * - */ - // Problem: field names are differents between - // $_SESSION['metadata']['nodes']['columns'] - // JSON values - //$fields[]="arch"; // architecture in Session metadata - //$fields[]="astype"; // as_type in Session metadata - $fields['platform'] = Array('column' => 'platform'); - $fields['platform_longname'] = Array('column' => 'platform_longname'); - - //---------------------------------- - - $out = Array(); - $out[] = ""; - $out[] = ""; - - foreach ($method_keys as $f) { - $out[] = ""; - } - - /* We put defaults fields (should be keys) at the beginning, and don't repeat them afterwards */ - foreach ($fields as $key=>$f) { - if((array_search($f['column'], $method_keys)) === false) - $out[] = ""; - } - - if (array_key_exists('checkboxes', $this->params) && ($this->params['checkboxes'])) { - $out[] = ""; - } - $out[] = ""; - $out[] = ""; - - /* This might be done asynchronously */ - if (!$async) { - $query = Plugins::get_query_by_uuid($this->params['query_uuid']); - foreach ($results as $r) { - $out[] = ""; - foreach ($fields as $f) { - $out[] = ""; - } - if (array_key_exists('checkboxes', $this->params) && ($this->params['checkboxes'])) { - $out[] = ""; - } - $out[] = ""; - } - } - - - if ($async) { - /* setup datatables */ - /* TODO: - * - fixed row for key columns - * - uniform row height - * - how to make some columns disappear - * - how to udpate some columns based on keys - */ - } - $out[] = ""; - $out[] = "
$f".$f['column']."+/-
"; - $out[] = Plugins::render_element($query, $r[$f], $f); // XXX was query->method - $out[] = "[X]
"; - return implode($out); - } -} - -Plugins::register_plugin( - 'DataTables', /* plugin name */ - 'DataTables', /* class name */ - Array( - 'method' => '*', - 'fields' => Array() - ) - /* XXX dependencies */ -); diff --git a/plugins/hazelnut/static/hazelnut.html b/plugins/hazelnut/static/hazelnut.html index aae78e44..b49161b6 100644 --- a/plugins/hazelnut/static/hazelnut.html +++ b/plugins/hazelnut/static/hazelnut.html @@ -1,5 +1,5 @@
- +
{% for column in columns %} diff --git a/plugins/hazelnut/static/js/hazelnut.js b/plugins/hazelnut/static/js/hazelnut.js index 8cbc5370..d26f0ea9 100644 --- a/plugins/hazelnut/static/js/hazelnut.js +++ b/plugins/hazelnut/static/js/hazelnut.js @@ -1,107 +1,51 @@ /** * Description: display a query result in a datatables-powered
- * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA + * Copyright (c) 2012-2013 UPMC Sorbonne Universite - INRIA * License: GPLv3 */ -/* - * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function - * Expression) that maps it to the dollar sign so it can't be overwritten by - * another library in the scope of its execution. - */ - -// TEMP -var ELEMENT_KEY = 'resource_hrn'; - (function($){ + // TEMP + var ELEMENT_KEY = 'resource_hrn'; var debug=false; debug=true - // routing calls - $.fn.Hazelnut = function( method ) { - if ( methods[method] ) { - return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); - } else if ( typeof method === 'object' || ! method ) { - return methods.init.apply( this, arguments ); - } else { - $.error( 'Method ' + method + ' does not exist on jQuery.Hazelnut' ); - } - }; + var Hazelnut = Plugin.extend({ - /*************************************************************************** - * Public methods - ***************************************************************************/ + init: function(options, element) + { + this._super(options, element); - var methods = { + /* Member variables */ + // query status + this.received_all = false; + this.received_set = false; + this.in_set_buffer = Array(); - /** - * @brief Plugin initialization - * @param options : an associative array of setting values - * @return : a jQuery collection of objects on which the plugin is - * applied, which allows to maintain chainability of calls - */ - init : function ( options ) { - - /* Default settings */ - var options = $.extend( { - 'checkboxes': false - }, options); - - return this.each(function() { - - var $this = $(this); - - /* An object that will hold private variables and methods */ - var plugin = new Hazelnut (options); - $this.data('Hazelnut', plugin); - - /* Events */ - $this.on('show.Datatables', methods.show); - - // This is the new plugin API meant to replace the weird publish_subscribe mechanism - $this.set_query_handler(options.query_uuid, plugin.query_handler); - $this.set_record_handler(options.query_uuid, plugin.record_handler); - $this.set_record_handler(options.query_all_uuid, plugin.record_handler_all); - -// /* Subscriptions */ -// var query_channel = '/query/' + options.query_uuid + '/changed'; -// var update_channel = '/update-set/' + options.query_uuid; -// var results_channel = '/results/' + options.query_uuid + '/changed'; -// -// // xxx not tested yet -// $.subscribe(query_channel, function(e, query) { hazelnut.set_query(query); }); -// // xxx not tested yet -// $.subscribe(update_channel, function(e, resources, instance) { hazelnut.set_resources(resources, instance); }); -// // expected to work -// $.subscribe(results_channel, $this, function(e, rows) { hazelnut.update_plugin(e,rows); }); -// if (debug) -// messages.debug("hazelnut '" + this.id + "' subscribed to e.g." + results_channel); - - }); // this.each - }, // init + /* XXX Events XXX */ + this.$element.on('show.Datatables', this.on_show); + // Unbind all events using namespacing + // TODO in destructor + // $(window).unbind('Hazelnut'); - /** - * @brief Plugin destruction - * @return : a jQuery collection of objects on which the plugin is - * applied, which allows to maintain chainability of calls - */ - destroy : function( ) { + /* Setup query and record handlers */ + this.listen_query(options.query_uuid); + this.listen_query(options.query_all_uuid, 'all'); - return this.each(function() { - var $this = $(this); - var hazelnut = $this.data('Hazelnut'); + /* GUI setup and event binding */ + this.initialize_table(); + }, - // Unbind all events using namespacing - $(window).unbind('Hazelnut'); + default_options: { + 'checkboxes': false + }, - // Remove associated data - hazelnut.remove(); - $this.removeData('Hazelnut'); - }); - }, // destroy + /* PLUGIN EVENTS */ - show : function( ) { + on_show: function() + { + // XXX var $this=$(this); // xxx wtf. why [1] ? would expect 0... if (debug) @@ -119,35 +63,15 @@ var ELEMENT_KEY = 'resource_hrn'; } } }); - } // show - - }; // var methods; - - /*************************************************************************** - * Plugin object - ***************************************************************************/ + }, // on_show - function Hazelnut(options) - { - /* member variables */ - this.options = options; + /* GUI EVENTS */ - // xxx thierry : initialize this here - it was not, I expect this relied on set_query somehow.. - //this.current_query = null; - this.current_query=manifold.find_query(this.options.query_uuid); - // if (debug) messages.debug("Hazelnut constructor: have set current_query -> " + this.current_query.__repr()); - this.query_update = null; - this.current_resources = Array(); + /* GUI MANIPULATION */ - // query status - this.received_all = false; - this.received_set = false; - this.in_set_buffer = Array(); - - var object = this; - - this.initialize = function() { + initialize_table: function() { /* Transforms the table into DataTable, and keep a pointer to it */ + var self = this; actual_options = { // Customize the position of Datatables elements (length,filter,button,...) // we use a fluid row on top and another on the bottom, making sure we take 12 grid elt's each time @@ -159,15 +83,16 @@ var ELEMENT_KEY = 'resource_hrn'; // WARNING: this one causes tables in a 'tabs' that are not exposed at the time this is run to show up empty // sScrollX: '100%', /* Horizontal scrolling */ bProcessing: true, /* Loading */ - fnDrawCallback: function() { hazelnut_draw_callback.call(object, options); } + fnDrawCallback: function() { self._hazelnut_draw_callback.call(self); } + // XXX use $.proxy here ! }; // the intention here is that options.datatables_options as coming from the python object take precedence // XXX DISABLED by jordan: was causing errors in datatables.js $.extend(actual_options, options.datatables_options ); - this.table = $('#hazelnut-' + options.plugin_uuid).dataTable(actual_options); + this.table = this.el('table').dataTable(actual_options); /* Setup the SelectAll button in the dataTable header */ /* xxx not sure this is still working */ - var oSelectAll = $('#datatableSelectAll-'+ options.plugin_uuid); + var oSelectAll = $('#datatableSelectAll-'+ this.options.plugin_uuid); oSelectAll.html("Select All"); oSelectAll.button(); oSelectAll.css('font-size','11px'); @@ -175,228 +100,128 @@ var ELEMENT_KEY = 'resource_hrn'; oSelectAll.css('margin-right','15px'); oSelectAll.css('margin-bottom','5px'); oSelectAll.unbind('click'); - oSelectAll.click(selectAll); + oSelectAll.click(this._selectAll); /* Add a filtering function to the current table * Note: we use closure to get access to the 'options' */ $.fn.dataTableExt.afnFiltering.push(function( oSettings, aData, iDataIndex ) { /* No filtering if the table does not match */ - if (oSettings.nTable.id != "hazelnut-" + options.plugin_uuid) + if (oSettings.nTable.id != "hazelnut-" + self.options.plugin_uuid) return true; - return hazelnut_filter.call(object, oSettings, aData, iDataIndex); + return this._hazelnut_filter.call(self, oSettings, aData, iDataIndex); }); /* Processing hidden_columns */ - $.each(options.hidden_columns, function(i, field) { - object.hide_column(field); + $.each(this.options.hidden_columns, function(i, field) { + self.hide_column(field); }); - } - - /* methods */ - -// DEPRECATED // this.set_query = function(query) { -// DEPRECATED // messages.info('hazelnut.set_query'); -// DEPRECATED // var options = this.options; -// DEPRECATED // /* Compare current and advertised query to get added and removed fields */ -// DEPRECATED // previous_query = this.current_query; -// DEPRECATED // /* Save the query as the current query */ -// DEPRECATED // this.current_query = query; -// DEPRECATED // if (debug) -// DEPRECATED // messages.debug("hazelnut.set_query, current_query is now -> " + this.current_query); -// DEPRECATED // -// DEPRECATED // /* We check all necessary fields : in column editor I presume XXX */ -// DEPRECATED // // XXX ID naming has no plugin_uuid -// DEPRECATED // if (typeof(query.fields) != 'undefined') { -// DEPRECATED // $.each (query.fields, function(index, value) { -// DEPRECATED // if (!$('#hazelnut-checkbox-' + options.plugin_uuid + "-" + value).attr('checked')) -// DEPRECATED // $('#hazelnut-checkbox-' + options.plugin_uuid + "-" + value).attr('checked', true); -// DEPRECATED // }); -// DEPRECATED // } -// DEPRECATED // -// DEPRECATED // /* Process updates in filters / current_query must be updated before this call for filtering ! */ -// DEPRECATED // this.table.fnDraw(); -// DEPRECATED // -// DEPRECATED // /* -// DEPRECATED // * Process updates in fields -// DEPRECATED // */ -// DEPRECATED // if (typeof(query.fields) != 'undefined') { -// DEPRECATED // /* DataTable Settings */ -// DEPRECATED // var oSettings = this.table.dataTable().fnSettings(); -// DEPRECATED // var cols = oSettings.aoColumns; -// DEPRECATED // var colnames = cols.map(function(x) {return x.sTitle}); -// DEPRECATED // colnames = $.grep(colnames, function(value) {return value != "+/-";}); -// DEPRECATED // -// DEPRECATED // if (previous_query == null) { -// DEPRECATED // var added_fields = query.fields; -// DEPRECATED // var removed_fields = colnames; -// DEPRECATED // removed_fields = $.grep(colnames, function(x) { return $.inArray(x, added_fields) == -1}); -// DEPRECATED // } else { -// DEPRECATED // var tmp = previous_query.diff_fields(query); -// DEPRECATED // var added_fields = tmp.added; -// DEPRECATED // var removed_fields = tmp.removed; -// DEPRECATED // } -// DEPRECATED // -// DEPRECATED // /* Hide/unhide columns to match added/removed fields */ -// DEPRECATED // var object = this; -// DEPRECATED // $.each(added_fields, function (index, field) { -// DEPRECATED // var index = object.getColIndex(field,cols); -// DEPRECATED // if(index != -1) -// DEPRECATED // object.table.fnSetColumnVis(index, true); -// DEPRECATED // }); -// DEPRECATED // $.each(removed_fields, function (index, field) { -// DEPRECATED // var index = object.getColIndex(field,cols); -// DEPRECATED // if(index != -1) -// DEPRECATED // object.table.fnSetColumnVis(index, false); -// DEPRECATED // }); -// DEPRECATED // } -// DEPRECATED // } - -// DEPRECATED // this.set_resources = function(resources, instance) { -// DEPRECATED // if (debug) -// DEPRECATED // messages.debug("entering hazelnut.set_resources"); -// DEPRECATED // var options = this.options; -// DEPRECATED // var previous_resources = this.current_resources; -// DEPRECATED // this.current_resources = resources; -// DEPRECATED // -// DEPRECATED // /* We uncheck all checkboxes ... */ -// DEPRECATED // $('hazelnut-checkbox-' + options.plugin_uuid).attr('checked', false); -// DEPRECATED // /* ... and check the ones specified in the resource list */ -// DEPRECATED // $.each(this.current_resources, function(index, urn) { -// DEPRECATED // $('#hazelnut-checkbox-' + options.plugin_uuid + "-" + urn).attr('checked', true) -// DEPRECATED // }); -// DEPRECATED // -// DEPRECATED // } + }, // initialize_table /** * @brief Determine index of key in the table columns * @param key * @param cols */ - this.getColIndex = function(key, cols) { + getColIndex: function(key, cols) { var tabIndex = $.map(cols, function(x, i) { if (x.sTitle == key) return i; }); return (tabIndex.length > 0) ? tabIndex[0] : -1; - }; - -// DEPRECATED // /** -// DEPRECATED // * @brief -// DEPRECATED // * XXX will be removed/replaced -// DEPRECATED // */ -// DEPRECATED // this.selected_changed = function(e, change) { -// DEPRECATED // if (debug) messages.debug("entering hazelnut.selected_changed"); -// DEPRECATED // var actions = change.split("/"); -// DEPRECATED // if (actions.length > 1) { -// DEPRECATED // var oNodes = this.table.fnGetNodes(); -// DEPRECATED // var myNode = $.grep(oNodes, function(value) { -// DEPRECATED // if (value.id == actions[1]) { return value; } -// DEPRECATED // }); -// DEPRECATED // if( myNode.length>0 ) { -// DEPRECATED // if ((actions[2]=="add" && actions[0]=="cancel") || actions[0]=="del") -// DEPRECATED // checked=''; -// DEPRECATED // else -// DEPRECATED // checked="checked='checked' "; -// DEPRECATED // var newValue = this.checkbox(actions[1], 'node', checked, false); -// DEPRECATED // var columnPos = this.table.fnSettings().aoColumns.length - 1; -// DEPRECATED // this.table.fnUpdate(newValue, myNode[0], columnPos); -// DEPRECATED // this.table.fnDraw(); -// DEPRECATED // } -// DEPRECATED // } -// DEPRECATED // } - - this.update_plugin = function(e, rows) { - // e.data is what we passed in second argument to subscribe - // so here it is the jquery object attached to the plugin
- var $plugindiv=e.data; - if (debug) - messages.debug("entering hazelnut.update_plugin on id '" + $plugindiv.attr('id') + "'"); - // clear the spinning wheel: look up an ancestor that has the need-spin class - // do this before we might return - $plugindiv.closest('.need-spin').spin(false); - - var options = this.options; - var hazelnut = this; - - /* if we get no result, or an error, try to make that clear, and exit */ - if (rows.length==0) { - if (debug) - messages.debug("Empty result on hazelnut " + this.options.domid); - var placeholder=$(this.table).find("td.dataTables_empty"); - console.log("placeholder "+placeholder); - if (placeholder.length==1) - placeholder.html(unfold.warning("Empty result")); - else - this.table.html(unfold.warning("Empty result")); - return; - } else if (typeof(rows[0].error) != 'undefined') { - // we now should have another means to report errors that this inline/embedded hack - if (debug) - messages.error ("undefined result on " + this.options.domid + " - should not happen anymore"); - this.table.html(unfold.error(rows[0].error)); - return; - } - - /* - * fill the dataTables object - * we cannot set html content directly here, need to use fnAddData - */ - var lines = new Array(); - - this.current_resources = Array(); - - $.each(rows, function(index, row) { - // this models a line in dataTables, each element in the line describes a cell - line = new Array(); - - // go through table headers to get column names we want - // in order (we have temporarily hack some adjustments in names) - var cols = object.table.fnSettings().aoColumns; - var colnames = cols.map(function(x) {return x.sTitle}) - var nb_col = cols.length; - /* if we've requested checkboxes, then forget about the checkbox column for now */ - if (options.checkboxes) nb_col -= 1; - - /* fill in stuff depending on the column name */ - for (var j = 0; j < nb_col; j++) { - if (typeof colnames[j] == 'undefined') { - line.push('...'); - } else if (colnames[j] == 'hostname') { - if (row['type'] == 'resource,link') - //TODO: we need to add source/destination for links - line.push(''); - else - line.push(row['hostname']); - } else { - if (row[colnames[j]]) - line.push(row[colnames[j]]); - else - line.push(''); - } - } - - /* catch up with the last column if checkboxes were requested */ - if (options.checkboxes) { - var checked = ''; - // xxx problem is, we don't get this 'sliver' thing set apparently - if (typeof(row['sliver']) != 'undefined') { /* It is equal to null when is present */ - checked = 'checked '; - hazelnut.current_resources.push(row[ELEMENT_KEY]); - } - // Use a key instead of hostname (hard coded...) - line.push(hazelnut.checkbox(options.plugin_uuid, row[ELEMENT_KEY], row['type'], checked, false)); - } - - lines.push(line); - - }); - - this.table.fnClearTable(); - if (debug) - messages.debug("hazelnut.update_plugin: total of " + lines.length + " rows"); - this.table.fnAddData(lines); - - }; - - this.checkbox = function (plugin_uuid, header, field, selected_str, disabled_str) + }, // getColIndex + + // UNUSED ? // this.update_plugin = function(e, rows) { + // UNUSED ? // // e.data is what we passed in second argument to subscribe + // UNUSED ? // // so here it is the jquery object attached to the plugin
+ // UNUSED ? // var $plugindiv=e.data; + // UNUSED ? // if (debug) + // UNUSED ? // messages.debug("entering hazelnut.update_plugin on id '" + $plugindiv.attr('id') + "'"); + // UNUSED ? // // clear the spinning wheel: look up an ancestor that has the need-spin class + // UNUSED ? // // do this before we might return + // UNUSED ? // $plugindiv.closest('.need-spin').spin(false); + // UNUSED ? // + // UNUSED ? // var options = this.options; + // UNUSED ? // var hazelnut = this; + // UNUSED ? // + // UNUSED ? // /* if we get no result, or an error, try to make that clear, and exit */ + // UNUSED ? // if (rows.length==0) { + // UNUSED ? // if (debug) + // UNUSED ? // messages.debug("Empty result on hazelnut " + this.options.domid); + // UNUSED ? // var placeholder=$(this.table).find("td.dataTables_empty"); + // UNUSED ? // console.log("placeholder "+placeholder); + // UNUSED ? // if (placeholder.length==1) + // UNUSED ? // placeholder.html(unfold.warning("Empty result")); + // UNUSED ? // else + // UNUSED ? // this.table.html(unfold.warning("Empty result")); + // UNUSED ? // return; + // UNUSED ? // } else if (typeof(rows[0].error) != 'undefined') { + // UNUSED ? // // we now should have another means to report errors that this inline/embedded hack + // UNUSED ? // if (debug) + // UNUSED ? // messages.error ("undefined result on " + this.options.domid + " - should not happen anymore"); + // UNUSED ? // this.table.html(unfold.error(rows[0].error)); + // UNUSED ? // return; + // UNUSED ? // } + // UNUSED ? // + // UNUSED ? // /* + // UNUSED ? // * fill the dataTables object + // UNUSED ? // * we cannot set html content directly here, need to use fnAddData + // UNUSED ? // */ + // UNUSED ? // var lines = new Array(); + // UNUSED ? // + // UNUSED ? // this.current_resources = Array(); + // UNUSED ? // + // UNUSED ? // $.each(rows, function(index, row) { + // UNUSED ? // // this models a line in dataTables, each element in the line describes a cell + // UNUSED ? // line = new Array(); + // UNUSED ? // + // UNUSED ? // // go through table headers to get column names we want + // UNUSED ? // // in order (we have temporarily hack some adjustments in names) + // UNUSED ? // var cols = object.table.fnSettings().aoColumns; + // UNUSED ? // var colnames = cols.map(function(x) {return x.sTitle}) + // UNUSED ? // var nb_col = cols.length; + // UNUSED ? // /* if we've requested checkboxes, then forget about the checkbox column for now */ + // UNUSED ? // if (options.checkboxes) nb_col -= 1; + // UNUSED ? // + // UNUSED ? // /* fill in stuff depending on the column name */ + // UNUSED ? // for (var j = 0; j < nb_col; j++) { + // UNUSED ? // if (typeof colnames[j] == 'undefined') { + // UNUSED ? // line.push('...'); + // UNUSED ? // } else if (colnames[j] == 'hostname') { + // UNUSED ? // if (row['type'] == 'resource,link') + // UNUSED ? // //TODO: we need to add source/destination for links + // UNUSED ? // line.push(''); + // UNUSED ? // else + // UNUSED ? // line.push(row['hostname']); + // UNUSED ? // } else { + // UNUSED ? // if (row[colnames[j]]) + // UNUSED ? // line.push(row[colnames[j]]); + // UNUSED ? // else + // UNUSED ? // line.push(''); + // UNUSED ? // } + // UNUSED ? // } + // UNUSED ? // + // UNUSED ? // /* catch up with the last column if checkboxes were requested */ + // UNUSED ? // if (options.checkboxes) { + // UNUSED ? // var checked = ''; + // UNUSED ? // // xxx problem is, we don't get this 'sliver' thing set apparently + // UNUSED ? // if (typeof(row['sliver']) != 'undefined') { /* It is equal to null when is present */ + // UNUSED ? // checked = 'checked '; + // UNUSED ? // hazelnut.current_resources.push(row[ELEMENT_KEY]); + // UNUSED ? // } + // UNUSED ? // // Use a key instead of hostname (hard coded...) + // UNUSED ? // line.push(hazelnut.checkbox(options.plugin_uuid, row[ELEMENT_KEY], row['type'], checked, false)); + // UNUSED ? // } + // UNUSED ? // + // UNUSED ? // lines.push(line); + // UNUSED ? // + // UNUSED ? // }); + // UNUSED ? // + // UNUSED ? // this.table.fnClearTable(); + // UNUSED ? // if (debug) + // UNUSED ? // messages.debug("hazelnut.update_plugin: total of " + lines.length + " rows"); + // UNUSED ? // this.table.fnAddData(lines); + // UNUSED ? // + // UNUSED ? // }, // update_plugin + + checkbox: function (plugin_uuid, header, field, selected_str, disabled_str) { var result=""; if (header === null) @@ -413,25 +238,21 @@ var ELEMENT_KEY = 'resource_hrn'; result += " value='" + unfold.get_value(header) + "'"; result += ">"; return result; - }; - - //////////////////////////////////////////////////////////////////////// - // New plugin API (in tests) + }, // checkbox - // TODO : signal empty/non empty table - this.new_record = function(record) + new_record: function(record) { // this models a line in dataTables, each element in the line describes a cell line = new Array(); // go through table headers to get column names we want // in order (we have temporarily hack some adjustments in names) - var cols = object.table.fnSettings().aoColumns; + var cols = this.table.fnSettings().aoColumns; var colnames = cols.map(function(x) {return x.sTitle}) var nb_col = cols.length; /* if we've requested checkboxes, then forget about the checkbox column for now */ - if (options.checkboxes) nb_col -= 1; + if (this.options.checkboxes) nb_col -= 1; /* fill in stuff depending on the column name */ for (var j = 0; j < nb_col; j++) { @@ -452,7 +273,7 @@ var ELEMENT_KEY = 'resource_hrn'; } /* catch up with the last column if checkboxes were requested */ - if (options.checkboxes) { + if (this.options.checkboxes) { var checked = ''; // xxx problem is, we don't get this 'sliver' thing set apparently if (typeof(record['sliver']) != 'undefined') { /* It is equal to null when is present */ @@ -460,270 +281,277 @@ var ELEMENT_KEY = 'resource_hrn'; hazelnut.current_resources.push(record[ELEMENT_KEY]); } // Use a key instead of hostname (hard coded...) - line.push(object.checkbox(options.plugin_uuid, record[ELEMENT_KEY], record['type'], checked, false)); + line.push(this.checkbox(this.options.plugin_uuid, record[ELEMENT_KEY], record['type'], checked, false)); } // XXX Is adding an array of lines more efficient ? this.table.fnAddData(line); - }; - - this.set_checkbox = function(record) - { - // XXX urn should be replaced by the key - // XXX we should enforce that both queries have the same key !! - checkbox_id = "#hazelnut-checkbox-" + object.options.plugin_uuid + "-" + unfold.escape_id(record[ELEMENT_KEY].replace(/\\/g, '')) - $(checkbox_id, object.table.fnGetNodes()).attr('checked', true); - } + }, - this.record_handler = function(e, event_type, record) + clear_table: function() { - // elements in set - switch(event_type) { - case NEW_RECORD: - /* NOTE in fact we are doing a join here */ - if (object.received_all) - // update checkbox for record - object.set_checkbox(record); - else - // store for later update of checkboxes - object.in_set_buffer.push(record); - break; - case CLEAR_RECORDS: - // nothing to do here - break; - case IN_PROGRESS: - manifold.spin($(this)); - break; - case DONE: - if (object.received_all) - manifold.spin($(this), false); - object.received_set = true; - break; - } - }; + this.table.fnClearTable(); + }, - this.record_handler_all = function(e, event_type, record) + redraw_table: function() { - // all elements - switch(event_type) { - case NEW_RECORD: - // Add the record to the table - object.new_record(record); - break; - case CLEAR_RECORDS: - object.table.fnClearTable(); - break; - case IN_PROGRESS: - manifold.spin($(this)); - break; - case DONE: - if (object.received_set) { - /* XXX needed ? XXX We uncheck all checkboxes ... */ - $("[id^='datatables-checkbox-" + object.options.plugin_uuid +"']").attr('checked', false); - - /* ... and check the ones specified in the resource list */ - $.each(object.in_set_buffer, function(i, record) { - object.set_checkbox(record); - }); - - manifold.spin($(this), false); - } - object.received_all = true; - break; - } - }; + this.table.fnDraw(); + }, - this.show_column = function(field) + show_column: function(field) { - var oSettings = object.table.fnSettings(); + var oSettings = this.table.fnSettings(); var cols = oSettings.aoColumns; - var index = object.getColIndex(field,cols); + var index = this.getColIndex(field,cols); if (index != -1) - object.table.fnSetColumnVis(index, true); - } + this.table.fnSetColumnVis(index, true); + }, - this.hide_column = function(field) + hide_column: function(field) { - var oSettings = object.table.fnSettings(); + var oSettings = this.table.fnSettings(); var cols = oSettings.aoColumns; - var index = object.getColIndex(field,cols); + var index = this.getColIndex(field,cols); if (index != -1) - object.table.fnSetColumnVis(index, false); - } + this.table.fnSetColumnVis(index, false); + }, - this.query_handler = function(e, event_type, data) + set_checkbox: function(record) { - // This replaces the complex set_query function - // The plugin does not need to remember the query anymore - switch(event_type) { - // Filters - case FILTER_ADDED: - case FILTER_REMOVED: - case CLEAR_FILTERS: - // XXX Here we might need to maintain the list of filters ! - /* Process updates in filters / current_query must be updated before this call for filtering ! */ - object.table.fnDraw(); - break; - - // Fields - /* Hide/unhide columns to match added/removed fields */ - case FIELD_ADDED: - object.show_column(data); - break; - case FIELD_REMOVED: - object.hide_column(data); - break; - case CLEAR_FIELDS: - alert('Hazelnut::clear_fields() not implemented'); - break; - } // switch - - - } - - // Constructor - object.initialize(); - - } // constructor - - /*************************************************************************** - * Private methods - * xxx I'm not sure why this should not be methods in the Hazelnut class above - ***************************************************************************/ - - /** - * @brief Hazelnut filtering function - */ - function hazelnut_filter (oSettings, aData, iDataIndex) { - var cur_query = this.current_query; - if (!cur_query) return true; - var ret = true; - - /* We have an array of filters : a filter is an array (key op val) - * field names (unless shortcut) : oSettings.aoColumns = [ sTitle ] - * can we exploit the data property somewhere ? - * field values (unless formatting) : aData - * formatting should leave original data available in a hidden field - * - * The current line should validate all filters - */ - $.each (cur_query.filters, function(index, filter) { - /* XXX How to manage checkbox ? */ - var key = filter[0]; - var op = filter[1]; - var value = filter[2]; + // XXX urn should be replaced by the key + // XXX we should enforce that both queries have the same key !! + checkbox_id = "#hazelnut-checkbox-" + this.options.plugin_uuid + "-" + unfold.escape_id(record[ELEMENT_KEY].replace(/\\/g, '')) + $(checkbox_id, this.table.fnGetNodes()).attr('checked', true); + }, - /* Determine index of key in the table columns */ - var col = $.map(oSettings.aoColumns, function(x, i) {if (x.sTitle == key) return i;})[0]; + /*************************** QUERY HANDLER ****************************/ - /* Unknown key: no filtering */ - if (typeof(col) == 'undefined') - return; + on_filter_added: function(filter) + { + // XXX + this.redraw_table(); + }, - col_value=unfold.get_value(aData[col]); - /* Test whether current filter is compatible with the column */ - if (op == '=' || op == '==') { - if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - }else if (op == '!=') { - if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - } else if(op=='<') { - if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - } else if(op=='>') { - if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - } else if(op=='<=' || op=='≤') { - if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - } else if(op=='>=' || op=='≥') { - if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a") - ret = false; - }else{ - // How to break out of a loop ? - alert("filter not supported"); - return false; - } + on_filter_removed: function(filter) + { + // XXX + this.redraw_table(); + }, + + on_filter_clear: function() + { + // XXX + this.redraw_table(); + }, - }); - return ret; - } + on_field_added: function(field) + { + this.show_column(field); + }, - function hazelnut_draw_callback() { - var options = this.options; - /* - * Handle clicks on checkboxes: reassociate checkbox click every time - * the table is redrawn - */ - $('.hazelnut-checkbox-' + options.plugin_uuid).unbind('click'); - $('.hazelnut-checkbox-' + options.plugin_uuid).click({instance: this}, check_click); + on_field_removed: function(field) + { + this.hide_column(field); + }, - if (!this.table) - return; + on_field_clear: function() + { + alert('Hazelnut::clear_fields() not implemented'); + }, - /* Remove pagination if we show only a few results */ - var wrapper = this.table; //.parent().parent().parent(); - var rowsPerPage = this.table.fnSettings()._iDisplayLength; - var rowsToShow = this.table.fnSettings().fnRecordsDisplay(); - var minRowsPerPage = this.table.fnSettings().aLengthMenu[0]; + /* RECORD HANDLERS */ - if ( rowsToShow <= rowsPerPage || rowsPerPage == -1 ) { - $('.hazelnut_paginate', wrapper).css('visibility', 'hidden'); - } else { - $('.hazelnut_paginate', wrapper).css('visibility', 'visible'); - } + on_new_record: function(record) + { + /* NOTE in fact we are doing a join here */ + if (this.received_all) + // update checkbox for record + this.set_checkbox(record); + else + // store for later update of checkboxes + this.in_set_buffer.push(record); + }, + + on_clear_records: function() + { + }, - if ( rowsToShow <= minRowsPerPage ) { - $('.hazelnut_length', wrapper).css('visibility', 'hidden'); - } else { - $('.hazelnut_length', wrapper).css('visibility', 'visible'); - } - } + // Could be the default in parent + on_query_in_progress: function() + { + this.spin(); + }, - function check_click (e) { + on_query_done: function() + { + if (this.received_all) + this.unspin(); + this.received_set = true; + }, - var object = e.data.instance; + // all - /* The key of the object to be added */ - // XXX What about multiple keys ? - var value = this.value; + on_all_new_record: function(record) + { + this.new_record(record); + }, - // NEW PLUGIN API - manifold.raise_event(object.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, value); - - // OLD PLUGIN API BELOW - - if (this.checked) { - object.current_resources.push(value); - } else { - tmp = $.grep(object.current_resources, function(x) { return x != value; }); - object.current_resources = tmp; - } - - /* inform slice that our selected resources have changed */ - $.publish('/update-set/' + object.options.query_uuid, [object.current_resources, true]); - - } - - function selectAll() { - // requires jQuery id - var uuid=this.id.split("-"); - var oTable=$("#hazelnut-"+uuid[1]).dataTable(); - // Function available in Hazelnut 1.9.x - // Filter : displayed data only - var filterData = oTable._('tr', {"filter":"applied"}); - /* TODO: WARNING if too many nodes selected, use filters to reduce nuber of nodes */ - if(filterData.length<=100){ - $.each(filterData, function(index, obj) { - var last=$(obj).last(); - var key_value=unfold.get_value(last[0]); - if(typeof($(last[0]).attr('checked'))=="undefined"){ - $.publish('selected', 'add/'+key_value); + on_all_clear_records: function() + { + this.clear_table(); + + }, + + on_all_query_in_progress: function() + { + // XXX parent + this.spin(); + }, // on_all_query_in_progress + + on_all_query_done: function() + { + var self = this; + if (this.received_set) { + /* XXX needed ? XXX We uncheck all checkboxes ... */ + $("[id^='datatables-checkbox-" + this.options.plugin_uuid +"']").attr('checked', false); + + /* ... and check the ones specified in the resource list */ + $.each(this.in_set_buffer, function(i, record) { + self.set_checkbox(record); + }); + + this.unspin(); + } + this.received_all = true; + + }, // on_all_query_done + + /************************** PRIVATE METHODS ***************************/ + + /** + * @brief Hazelnut filtering function + */ + _hazelnut_filter: function(oSettings, aData, iDataIndex) + { + var cur_query = this.current_query; + if (!cur_query) return true; + var ret = true; + + /* We have an array of filters : a filter is an array (key op val) + * field names (unless shortcut) : oSettings.aoColumns = [ sTitle ] + * can we exploit the data property somewhere ? + * field values (unless formatting) : aData + * formatting should leave original data available in a hidden field + * + * The current line should validate all filters + */ + $.each (cur_query.filters, function(index, filter) { + /* XXX How to manage checkbox ? */ + var key = filter[0]; + var op = filter[1]; + var value = filter[2]; + + /* Determine index of key in the table columns */ + var col = $.map(oSettings.aoColumns, function(x, i) {if (x.sTitle == key) return i;})[0]; + + /* Unknown key: no filtering */ + if (typeof(col) == 'undefined') + return; + + col_value=unfold.get_value(aData[col]); + /* Test whether current filter is compatible with the column */ + if (op == '=' || op == '==') { + if ( col_value != value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + }else if (op == '!=') { + if ( col_value == value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + } else if(op=='<') { + if ( parseFloat(col_value) >= value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + } else if(op=='>') { + if ( parseFloat(col_value) <= value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + } else if(op=='<=' || op=='≤') { + if ( parseFloat(col_value) > value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + } else if(op=='>=' || op=='≥') { + if ( parseFloat(col_value) < value || col_value==null || col_value=="" || col_value=="n/a") + ret = false; + }else{ + // How to break out of a loop ? + alert("filter not supported"); + return false; } + }); - } - } - -})( jQuery ); + return ret; + }, + + _hazelnut_draw_callback: function() + { + /* + * Handle clicks on checkboxes: reassociate checkbox click every time + * the table is redrawn + */ + $('.hazelnut-checkbox-' + this.options.plugin_uuid).unbind('click'); + $('.hazelnut-checkbox-' + this.options.plugin_uuid).click({instance: this}, this._check_click); + + if (!this.table) + return; + + /* Remove pagination if we show only a few results */ + var wrapper = this.table; //.parent().parent().parent(); + var rowsPerPage = this.table.fnSettings()._iDisplayLength; + var rowsToShow = this.table.fnSettings().fnRecordsDisplay(); + var minRowsPerPage = this.table.fnSettings().aLengthMenu[0]; + + if ( rowsToShow <= rowsPerPage || rowsPerPage == -1 ) { + $('.hazelnut_paginate', wrapper).css('visibility', 'hidden'); + } else { + $('.hazelnut_paginate', wrapper).css('visibility', 'visible'); + } + + if ( rowsToShow <= minRowsPerPage ) { + $('.hazelnut_length', wrapper).css('visibility', 'hidden'); + } else { + $('.hazelnut_length', wrapper).css('visibility', 'visible'); + } + }, + + _check_click: function(e) + { + + var self = e.data.instance; + + // XXX this.value = key of object to be added... what about multiple keys ? + manifold.raise_event(self.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, this.value); + + }, + + _selectAll: function() + { + // requires jQuery id + var uuid=this.id.split("-"); + var oTable=$("#hazelnut-"+uuid[1]).dataTable(); + // Function available in Hazelnut 1.9.x + // Filter : displayed data only + var filterData = oTable._('tr', {"filter":"applied"}); + /* TODO: WARNING if too many nodes selected, use filters to reduce nuber of nodes */ + if(filterData.length<=100){ + $.each(filterData, function(index, obj) { + var last=$(obj).last(); + var key_value=unfold.get_value(last[0]); + if(typeof($(last[0]).attr('checked'))=="undefined"){ + $.publish('selected', 'add/'+key_value); + } + }); + } + }, + + }); + + $.plugin('Hazelnut', Hazelnut); + +})(jQuery); diff --git a/plugins/myplugin/static/js/myplugin.js b/plugins/myplugin/static/js/myplugin.js index fee1e4bd..18011150 100644 --- a/plugins/myplugin/static/js/myplugin.js +++ b/plugins/myplugin/static/js/myplugin.js @@ -15,27 +15,44 @@ var MyPlugin = Plugin.extend({ - // Constructor + /** 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 */ + + /* 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_uuid, 'all'); - // GUI Event binding + /* GUI setup and event binding */ // call function }, + /* PLUGIN EVENTS */ + // on_show like in hazelnut + + /* GUI EVENTS */ - // a function to bind events here + // a function to bind events here: click change // how to raise manifold events + /* GUI MANIPULATION */ // We advise you to write function to change behaviour of the GUI @@ -75,8 +92,16 @@ // }, + /* INTERNAL FUNCTIONS */ + _dummy: function() { + // only convention, not strictly enforced at the moment + }, + }); + /* Plugin registration */ + $.plugin('MyPlugin', MyPlugin); + // TODO Here use cases for instanciating plugins in different ways like in the pastie. })(jQuery); diff --git a/trash/pluginview.py b/trash/pluginview.py index 6d602204..b165d6a1 100644 --- a/trash/pluginview.py +++ b/trash/pluginview.py @@ -1,28 +1,27 @@ # Create your views here. -from django.core.context_processors import csrf -from django.template import RequestContext -from django.template.loader import render_to_string -from django.shortcuts import render_to_response +from django.core.context_processors import csrf +from django.template import RequestContext +from django.template.loader import render_to_string +from django.shortcuts import render_to_response -from django.contrib.auth.decorators import login_required +from django.contrib.auth.decorators import login_required -from unfold.page import Page -#from manifold.manifoldquery import ManifoldQuery -from manifold.core.query import Query +from unfold.page import Page +from manifold.core.query import Query -from plugins.stack.stack import Stack -from plugins.tabs.tabs import Tabs -from plugins.lists.staticlist import StaticList -from plugins.quickfilter.quickfilter import QuickFilter -from plugins.querycode.querycode import QueryCode -from plugins.raw.raw import Raw -from plugins.messages.messages import Messages -from plugins.hazelnut.hazelnut import Hazelnut -from plugins.updater.updater import Updater +from plugins.stack.stack import Stack +from plugins.tabs.tabs import Tabs +from plugins.lists.staticlist import StaticList +from plugins.quickfilter.quickfilter import QuickFilter +from plugins.querycode.querycode import QueryCode +from plugins.raw.raw import Raw +from plugins.messages.messages import Messages +from plugins.hazelnut import Hazelnut +from plugins.updater.updater import Updater -from myslice.viewutils import topmenu_items, the_user -from myslice.viewutils import hard_wired_slice_names, hard_wired_list, lorem_p, lorem, quickfilter_criterias +from myslice.viewutils import topmenu_items, the_user +from myslice.viewutils import hard_wired_slice_names, hard_wired_list, lorem_p, lorem, quickfilter_criterias @login_required def test_plugin_view (request): diff --git a/trash/sliceview.py b/trash/sliceview.py index a0790c0e..f1591c81 100644 --- a/trash/sliceview.py +++ b/trash/sliceview.py @@ -15,9 +15,9 @@ from plugins.raw.raw import Raw from plugins.stack.stack import Stack from plugins.tabs.tabs import Tabs from plugins.lists.slicelist import SliceList -from plugins.hazelnut.hazelnut import Hazelnut +from plugins.hazelnut import Hazelnut from plugins.resources_selected import ResourcesSelected -from plugins.googlemap.googlemap import GoogleMap +from plugins.googlemaps import GoogleMaps from plugins.senslabmap.senslabmap import SensLabMap from plugins.querycode.querycode import QueryCode from plugins.query_editor import QueryEditor @@ -163,7 +163,7 @@ def _slice_view (request, slicename): }, )) - tab_resource_plugins.insert(GoogleMap( + tab_resource_plugins.insert(GoogleMaps( page = page, title = 'Geographic view', domid = 'gmap', -- 2.43.0