X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=plugins%2Fscheduler%2Fstatic%2Fjs%2Fscheduler.js;fp=plugins%2Fscheduler%2Fstatic%2Fjs%2Fscheduler.js;h=709380f131f2c851e346c365248d7877362fc55c;hb=6e13c225bb6e91885b5eb4cb668d59d0ed707830;hp=0000000000000000000000000000000000000000;hpb=d1197c134b70bda643a752bdaf891190cb5a15e7;p=myslice.git diff --git a/plugins/scheduler/static/js/scheduler.js b/plugins/scheduler/static/js/scheduler.js new file mode 100644 index 00000000..709380f1 --- /dev/null +++ b/plugins/scheduler/static/js/scheduler.js @@ -0,0 +1,648 @@ +/** + * Description: display a query result in a Google map + * Copyright (c) 2012-2013 UPMC Sorbonne Universite - INRIA + * License: GPLv3 + */ + +/* based on the leases.js code in PLE WWW interface for MyPLC by Thierry Parmentelat -- INRIA */ + +/* XXX Those declarations should not be part of the js file... */ + +/* decorations / headers */ +/* note: looks like the 'font' attr is not effective... */ + +// vertical rules +var attr_rules={'fill':"#888", 'stroke-dasharray':'- ', 'stroke-width':0.5}; +// set font-size separately in here rather than depend on the height +var txt_timelabel = {"font": 'Times, "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', + stroke: "none", fill: "#008", 'font-size': 9}; +var txt_allnodes = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', stroke: "none", fill: "#404"}; +var txt_nodelabel = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', stroke: "none", fill: "#008"}; + +var attr_timebutton = {'fill':'#bbf', 'stroke': '#338','stroke-width':1, + 'stroke-linecap':'round', 'stroke-linejoin':'miter', 'stroke-miterlimit':3}; +var attr_daymarker = {'stroke':'#000','stroke-width':2}; +var attr_half_daymarker = {'stroke':'#444','stroke-width':2}; + +/* lease dimensions and colors */ +/* refrain from using gradient color, seems to not be animated properly */ +/* lease was originally free and is still free */ +var attr_lease_free_free={'fill':"#def", 'stroke-width':0.5, 'stroke-dasharray':''}; +/* lease was originally free and is now set for our usage */ +var attr_lease_free_mine={'fill':"green", 'stroke-width':1, 'stroke-dasharray':'-..'}; +/* was mine and is still mine */ +var attr_lease_mine_mine={'fill':"#beb", 'stroke-width':0.5, 'stroke-dasharray':''}; +/* was mine and is about to be released */ +var attr_lease_mine_free={'fill':"white", 'stroke-width':1, 'stroke-dasharray':'-..'}; +var attr_lease_other={'fill':"#f88"}; + +/* other slices name */ +var txt_otherslice = {"font": '"Trebuchet MS", Verdana, Arial, Helvetica, sans-serif', stroke: "none", fill: "#444", "font-size": "12px"}; + +(function($){ + + var Scheduler = Plugin.extend({ + + init: function(options, element) + { + this._super(options, element); + + /* Member variables */ + this.options = options; + this.canvas_id = 'leases_area-' + options.plugin_uuid; + + this.query_uuid = options.query_uuid; + this.rows = null; + // XXX TODEL + //this.current_resources = Array(); + //this.current_leases = Array(); + + this.myLeases = Array(); + this.allLeases = Array(); + + this.listLeases = Array(); + + this.axisx = Array(); + this.axisy = Array(); + this.data = Array(); + this.default_granularity = 1800; /* 30 min */ + this.initial_timestamp = null; + + /* This should be updated to be the ppcm of all granularities */ + this.min_granularity = this.default_granularity; + + // the data contains slice names, and lease_id, we need this to find our own leases (mine) + this.paper=null; + + /* XXX Events */ + + /* XXX Keys */ + + /* Listening to queries */ + this.listen_query(options.resource_query_uuid); + this.listen_query(options.lease_query_uuid); + + /* XXX GUI setup and event binding */ + jQuery("#datepicker").datetimepicker({ + //Timezone wise selection option + //timeFormat: 'hh:mm tt z', + //showTimezone: true, + + onSelect: function(value) { + //console.log(value); + //converting time to timestamp + currentDate= Math.round(Date.parse(value)/ 1000); + // TODO -- DONE + // convert value to timestamp --done + // Get jQuery("#timepicker").val(); - no need anymore + // convert timepicker into seconds - no need + // Add time to date - done + // pass the result to init_axisx - done + //console.log(currentDate); + s.clear(); + s.init_axisx(currentDate); + s.draw(); + // Do we need to populate the timeslots with existing leases? + // Look how to populate with initial_leases [we have to show the leases] + + } + }); + //console.log(s.nodelabels[1]); + // Do we need to populate the timeslots with existing leases? + // Look how to populate with initial_leases + + // TODO -- DONE Note: autocomplete is not search box + // Implement a filtering functionality, on the name of the node + // http://jqueryui.com/autocomplete/ + // pass s.nodelabels to autocomplete function + //jQuery("#search").autocomplete('s.nodelabel'); + //console.log(s.nodelabel); + + /* + * TODO + * During init, there are no resources + * So the list of nodes is empty + * The function triggered by the subscription to the resources query + * var RESULTS_RESOURCES = '/results/' + options.resource_query_uuid + '/changed'; + * $.subscribe(RESULTS_RESOURCES, function(e, resources) { s.set_resources(resources); }); + * Will have to update the list of nodes available through autocomplete (availableTags) + * + * Be inspired by QueryEditor plugin + * + * Resources informations in s.axisy ??? + * + * Filter what has been selected in other plugins: + * QueryEditor + * QuickFilter + * AdvancedFilter + * + * Implement an action of filtering while typing + * filter what correspond to the user choice + * + */ + $(function() { + var availableTags = ['omf','nitos','ple', s.nodelabel]; + $( "#search" ).autocomplete({ + source: availableTags + }); + }); + + this.init_axisx(''); + this.draw(); + + }, /* init */ + + /* Default settings */ + default_options: { + leases_offset: 0, + leases_slots: 36, + leases_w: 180, + leases_granularity: 3600, + + x_nodelabel: 200, /* space for the nodenames */ + x_sep: 20, /* right space after the nodename - removed from the above */ + y_header: 12, /* height for the (two) rows of timelabels */ + y_sep: 10, /* space between nodes */ + leases_w: 20, /* 1-grain leases attributes */ + y_node: 15, + radius: 6, + anim_delay: 350, + checkboxes: false, + resource_query_uuid: null, /* resources */ + lease_query_uuid: null, /* leases */ + }, + + /* PLUGIN EVENTS */ + + /* GUI EVENTS */ + + /* GUI MANIPULATION */ + + /* TEMPLATES */ + + /* QUERY HANDLERS */ + + set_resources: function(resources) + { + //console.log(resources); + var scheduler = this; + jQuery.each(resources, function(i, resource) { + // ... add reservable ones to the x axis + if ((typeof resource.exclusive != 'undefined') && (resource.exclusive)) { + scheduler.axisy.push(Array(resource.urn, resource.resource_hrn, resource.type)); + } + // ... if we do not have information about slivers (first update), update it + if (typeof resource.sliver != 'undefined') { + // XXX + } + }); + + this.draw(this.canvas_id); + } + + this.set_leases = function(leases) { + this.initial_leases=leases; + this.draw(this.canvas_id); + } + + this.update_resources = function(resources) { + // + } + + this.update_leases = function(leases) { + // + } + + /* RECORD HANDLERS */ + + /* INTERNAL FUNCTIONS */ + + /** + * @brief Return the number of time slots + */ + nb_grains: function () + { + return this.axisx.length; + }, + + /** + * @brief Returns whether there is a pending lease at this timestamp + */ + find_lease: function(urn, timestamp) + { + var scheduler = this; + var result = null; + + $.each(Array(scheduler.myLeases, scheduler.allLeases), function(i, array) { + $.each(array, function(i, lease) { + if (lease[0] == urn) { + if ((timestamp >= lease[1]) && (timestamp < (lease[1] + lease[2] * 1800))) { + result = lease; + + return false; + } + } + }); + if (result) + return false; + }); + return result; + }, + + /** + * @brief Draw + */ + draw: function() + { + var canvas_id = this.canvas_id; + var o = this.options; + var total_width = o.x_nodelabel + this.nb_grains() * this.options.leases_w; + var total_height = 2 * o.y_header /* the timelabels */ + + 2 * o.y_sep /* extra space */ + + o.y_node /* all-nodes & timebuttons row */ + + (this.axisy.length)*(o.y_node+o.y_sep); /* the regular nodes and preceding space */ + + /* reuse for paper if exists with same size, or (re-)create otherwise */ + var paper; + if (this.paper == null) { + paper = Raphael (canvas_id, total_width+o.x_sep, total_height); + } else if (this.paper.width==total_width && this.paper.height==total_height) { + paper=this.paper; + paper.clear(); + } else { + $$("#"+canvas_id)[0].innerHTML=""; + paper = Raphael (canvas_id, total_width+o.x_sep, total_height); + } + this.paper=paper; + + /* the path for the triangle-shaped buttons */ + this.timebutton_path = "M1,0L"+(this.options.leases_w-1)+",0L"+(this.options.leases_w/2)+","+o.y_header+"L1,0"; + + var axisx = this.axisx; + var axisy = this.axisy; + + /* maintain the list of nodelabels for the 'all nodes' button */ + this.nodelabels=[]; + + + /* create the time slots legend */ + var top = 0; + var left = o.x_nodelabel; + + var daymarker_height= 2*o.y_header + 2*o.y_sep + (axisy.length+1)*(o.y_node+o.y_sep); + var daymarker_path="M0,0L0," + daymarker_height; + + var half_daymarker_off= 2*o.y_header + o.y_sep; + var half_daymarker_path="M0," + half_daymarker_off + "L0," + daymarker_height; + + var col=0; + for (var i=0, len=axisx.length; i < len; ++i) { + /* pick the printable part */ + var timelabel=axisx[i][1]; + var y = top + o.y_header; + if (col%2 == 0) y += o.y_header; + col +=1; + /* display time label */ + var timelabel=paper.text(left,y,timelabel).attr(txt_timelabel).attr({"text-anchor":"middle"}); + /* draw vertical line */ + var path_spec="M"+left+" "+(y+o.y_header/2)+"L"+left+" "+this.total_height; + var rule=paper.path(path_spec).attr(attr_rules); + /* show a day marker when relevant */ + var timestamp=parseInt(axisx[i][0]); + if ( (timestamp%(24*3600))==0) { + paper.path(daymarker_path).attr({'translation':left+','+top}).attr(attr_daymarker); + } else if ( (timestamp%(12*3600))==0) { + paper.path(half_daymarker_path).attr({'translation':left+','+top}).attr(attr_daymarker); + } + left += this.options.leases_w; + }, + + ////////// the row with the timeslot buttons (the one labeled 'All nodes') + this.granularity= this.min_granularity; // XXX axisx[1][0]-axisx[0][0]; + + // move two lines down + top += 2*o.y_header + 2*o.y_sep; + left=o.x_nodelabel; + // all nodes buttons + var allnodes = paper.text (o.x_nodelabel-o.x_sep,top+o.y_node/2,"All nodes").attr(txt_allnodes) + .attr ({"font-size":o.y_node, "text-anchor":"end","baseline":"bottom"}); + allnodes.scheduler=this; + allnodes.click(allnodes_methods.click); + + // timeslot buttons [it's the triangles above the slots] + for (var i=0, len=axisx.length; i < len; ++i) { + var timebutton=paper.path(this.timebutton_path).attr({'translation':left+','+top}).attr(attr_timebutton); + timebutton.from_time=axisx[i][0]; + timebutton.scheduler=this; + timebutton.click(timebutton_methods.click); + left+=(this.options.leases_w); + } + + //////// the body of the scheduler : loop on nodes + top += o.y_node + o.y_sep; + var data_index=0; + this.leases=[]; + for (var i=0, len=axisy.length; i0) ? 1 : 0; + for (var i=0, len=scheduler.nodelabels.length; i=until_time) { + if (scan.current == "free") relevant_free.push(scan); + else if (scan.current == "mine") relevant_mine.push(scan); + } + } + // window.console.log("Found " + relevant_free.length + " free and " + relevant_mine.length + " mine"); + /* decide what to do, whether book or release */ + if (relevant_mine.length==0 && relevant_free.length==0) { + alert ("Nothing to do in this timeslot on the selected nodes"); + return; + } + // if at least one is free, let's book + if (relevant_free.length > 0) { + for (var i=0, len=relevant_free.length; i