2 * Description: QueryEditor plugin
3 * Copyright (c) 2012-2013 UPMC Sorbonne Universite
8 * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function
9 * Expression) that maps it to the dollar sign so it can't be overwritten by
10 * another library in the scope of its execution.
15 var PLUGIN_NAME = 'QueryEditor';
18 jQuery.fn.QueryEditor = function( method ) {
19 if ( methods[method] ) {
20 return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
21 } else if ( typeof method === 'object' || ! method ) {
22 return methods.init.apply( this, arguments );
24 jQuery.error( 'Method ' + method + ' does not exist on jQuery.' + PLUGIN_NAME );
28 /***************************************************************************
30 ***************************************************************************/
35 * @brief Plugin initialization
36 * @param options : an associative array of setting values
37 * @return : a jQuery collection of objects on which the plugin is
38 * applied, which allows to maintain chainability of calls
40 init : function ( options ) {
42 return this.each(function() {
46 /* An object that will hold private variables and methods */
47 var plugin = new QueryEditor(options);
48 $this.data('Manifold', plugin);
50 $this.set_query_handler(options.query_uuid, plugin.query_handler);
51 $this.set_record_handler(options.query_uuid, plugin.record_handler);
57 * @brief Plugin destruction
58 * @return : a jQuery collection of objects on which the plugin is
59 * applied, which allows to maintain chainability of calls
61 destroy : function( ) {
63 return this.each(function() {
65 var hazelnut = $this.data('Manifold');
67 // Unbind all events using namespacing
68 $(window).unbind(PLUGIN_NAME);
70 // Remove associated data
72 $this.removeData('Manifold');
74 $this.set_query_handler(options.query_uuid, hazelnut.query_handler);
75 $this.set_record_handler(options.query_uuid, hazelnut.record_handler);
77 /* XXX Subscribe to query updates to maintain current state of query (multiple editors) */
78 jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
79 jQuery.subscribe('/query/' + options.query_uuid + '/diff', {instance: $this}, query_changed_diff);
80 /* Subscribe to results in order to redraw the table when updates arrive */
81 jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_autocomplete);
88 /***************************************************************************
90 ***************************************************************************/
92 function QueryEditor(options)
95 /* member variables */
96 this.options = options;
100 this.initialize_table = function(data)
105 jQuery('.queryeditor-filter').change(function(event) {
106 query = data.current_query;
107 var key=getKeySplitId(event.target.id,"-");
109 var value=event.target.value;
112 query.update_filter(key, op, value);
113 //add_ActiveFilter(event.target.id, '=',event.target.value,data);
115 query.remove_filter(key,op,"");
116 //remove_ActiveFilter(event, data, event.target.id,'=');
118 // Publish the query changed, the other plugins with subscribe will get the changes
119 jQuery.publish('/query/' + query.uuid + '/changed', query);
121 jQuery('.queryeditor-filter-min').change(function(event) {
122 query = data.current_query;
123 var key=getKeySplitId(event.target.id,"-");
125 var value=event.target.value;
128 query.update_filter(key, op, value);
129 //add_ActiveFilter(event.target.id,'>',event.target.value,data);
131 query.remove_filter(key,op,"");
132 //remove_ActiveFilter(event, data, event.target.id,'>');
134 // Publish the query changed, the other plugins with subscribe will get the changes
135 jQuery.publish('/query/' + query.uuid + '/changed', query);
137 jQuery('.queryeditor-filter-max').change(function(event) {
138 query = data.current_query;
139 var key=getKeySplitId(event.target.id,"-");
141 var value=event.target.value;
144 query.update_filter(key, op, value);
145 //add_ActiveFilter(event.target.id,'<',event.target.value,data);
147 query.remove_filter(key,op,"");
148 //remove_ActiveFilter(event, data, event.target.id,'<');
150 // Publish the query changed, the other plugins with subscribe will get the changes
151 jQuery.publish('/query/' + query.uuid + '/changed', query);
154 jQuery('.queryeditor-check').click(function() {
155 manifold.raise_event(object.options.query_uuid, this.checked?FIELD_ADDED:FIELD_REMOVED, this.value);
157 var column = this.id.substring(6);
158 query = data.current_query;
160 if (jQuery.inArray(column, query.fields) == -1) {
161 query.fields.push(column);
162 jQuery.publish('/query/' + query.uuid + '/changed', query);
165 query.fields = jQuery.grep(query.fields, function(value) {return value != column;});
166 jQuery.publish('/query/' + query.uuid + '/changed', query);
171 //onFunctionAvailable('jQuery.fn.dataTable', function() {
173 var nCloneTh = document.createElement( 'th' );
174 var nCloneTd = document.createElement( 'td' );
175 nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
176 //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
177 nCloneTh.innerHTML = '<b>Info</b>';
178 nCloneTd.className = "center";
179 nCloneTh.className = "center";
181 jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
182 this.insertBefore( nCloneTh, this.childNodes[0] );
185 jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
186 this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
189 var metaTable = jQuery('#'+this.options.plugin_uuid+'-table').dataTable( {
193 sScrollX: '100%', /* Horizontal scrolling */
195 bJQueryUI: true, // Use jQuery UI
196 bProcessing: true, // Loading
197 aaSorting: [[ 1, "asc" ]], // sort by column fields on load
198 aoColumnDefs: [ {"bSortable": false, "aTargets": [ 0 ]},
199 { "sWidth": "8px", "aTargets": [ 0 ] },
200 { "sWidth": "8px", "aTargets": [ 4 ] }
204 jQuery('#'+this.options.plugin_uuid+'_fields tbody td span').live('click', function () {
205 var nTr = this.parentNode.parentNode;
206 // use jQuery UI instead of images to keep a common UI
207 // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
208 //East oriented Triangle class="ui-icon-triangle-1-e"
209 //South oriented Triangle class="ui-icon-triangle-1-s"
211 if(this.className=="ui-icon ui-icon-triangle-1-e"){
212 this.removeClass("ui-icon-triangle-1-e");
213 this.addClass("ui-icon-triangle-1-s");
214 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
216 this.removeClass("ui-icon-triangle-1-s");
217 this.addClass("ui-icon-triangle-1-e");
218 metaTable.fnClose( nTr );
221 if ( this.src.match('details_close') ) {
222 this.src = "/components/com_tophat/images/details_open.png";
223 metaTable.fnClose( nTr );
226 this.src = "/components/com_tophat/images/details_close.png";
227 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
232 jQuery('#'+this.options.plugin_uuid+'_fields_wrapper').css({'padding-top':'0em','padding-bottom':'0em'});
234 //}); // onfunctionAvailable
236 } // initialize_table
238 this.print_field_description = function(field_header, div_id) {
240 //var selected = all_headers[field_header];
241 var selected = getMetadata_field('resource',field_header);
243 field_header = div_id+"_"+field_header;
245 var output = "<div id='desc"+field_header+"'>";
247 output += "<div id='divinfo"+field_header+"'>";
248 output += '<p><span class="column-title">'+selected['title']+'</span></p></span>';
249 output += '<p><span class="column-detail">'+selected['description']+'</span></p></span>';
251 var period_select = "<select id='selectperiod"+field_header+"'><option value='Now'> Now </option><option value='latest'> Latest </option><option value=w> Week </option><option value=m> Month </option><option value=y> Year </option></select>";
253 if (selected['value_type'] == 'string') {
255 var values_select = "<p><select id='selectvalues"+field_header+"' MULTIPLE size=3>";
257 output += '<p>Values: ';
259 var values_list = selected['allowed_values'].split(",");
261 for (var value_index = 0; value_index < values_list.length ; value_index++) {
262 var value_desc = values_list[value_index].split("-");
265 output += '<span class="bold">'+value_desc[0]+'</span>';
266 values_select += "<option value ='"+value_desc[0]+"'> "+value_desc[0];
267 if (value_desc[1]!='')
268 output += ' ('+value_desc[1]+')';
270 values_select += " </option>";
272 values_select += "</select>";
275 output+='<p>Unit: '+selected['unit'];
279 output += '<p>Source: <a class="source-url" target="source_window" href="'+selected['platform_url']+'">'+selected['platform']+'</a>';
281 //if (selected['via'] != '')
282 //output += ' via <a class="source-url" target="source_window" href="http://'+selected['via_url']+'">'+selected['via']+'</a>';
288 output += "<div id='divgroup"+field_header+"'>";
289 output += "<p>Group resources with the same value <input type=checkbox></input>";
290 output += "<p>Select aggregator : <select><option>Count</option><option selected=true>Average</option><option>Maximum</option><option>Minimum</option></select>";
292 output += "<div id='divtime"+field_header+"'>";
293 output += "<p>Select timestamp : ";
294 output += period_select;
302 this.update_autocomplete = function(e, rows, current_query)
305 d.current_query = current_query;
306 var availableTags={};
307 jQuery.each (rows, function(index, obj) {
308 jQuery.each(obj,function(key,value){
309 value = get_value(value);
310 if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
311 //availableTags[key].push(value);
312 var currentArray=availableTags[key];
314 if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
318 jQuery.each(availableTags, function(key, value){
320 jQuery("#"+options.plugin_uuid+"-filter-"+key).autocomplete({
323 minLength: 0, // allows to browse items with no value typed in
324 select: function(event, ui) {
325 var key=getKeySplitId(this.id,"-");
327 var val=ui.item.value;
329 query=d.current_query;
330 query.update_filter(key,op,val);
331 // Publish the query changed, the other plugins with subscribe will get the changes
332 jQuery.publish('/query/' + query.uuid + '/changed', query);
333 //add_ActiveFilter(this.id,'=',ui.item.value,d);
337 } // update_autocomplete
340 * This function is used to update autocomplete
342 this.record_handler = function(e, event_type, record)
357 this.query_handler = function(e, event_type, data)
359 // This replaces the complex set_query function
360 // The plugin does not need to remember the query anymore
363 // When Query changed, Then we need to update the filters of
364 // QueryEditor plugin if the filter is active, set the value
365 // (the update can come from another plugin) else set the
366 // filter value to null PB if the filter is composed of MIN/MAX
370 // Set the value of the filter = to query filter value
371 // Necessary if the filter has been modified by another plugin (QuickFilter)
373 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
374 }else if(filter[1]=="<"){
375 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
376 }else if(filter[1]==">"){
377 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
382 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
383 }else if(filter[1]=="<"){
384 //502124d5a5848-filter-asn-max
385 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
386 }else if(filter[1]==">"){
387 //502124d5a5848-filter-asn-min
388 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
394 /* Hide/unhide columns to match added/removed fields */
395 // XXX WRONG IDENTIFIERS
400 object.uncheck(data);
403 alert(PLUGIN_NAME + '::clear_fields() not implemented');
409 this.check = function(field)
411 $('#check_' + field).attr('checked', true);
413 this.uncheck = function(field)
415 $('#check_' + field).attr('checked', false);
417 this.fnFormatDetails = function( metaTable, nTr, div_id ) {
418 var aData = metaTable.fnGetData( nTr );
419 var sOut = '<blockquote>';
420 //sOut += prepare_tab_description(aData[1].substr(21, aData[1].length-21-7), div_id);
421 sOut += this.print_field_description(aData[1].substring(3, aData[1].length-4), div_id);
422 sOut += '</blockquote>';
431 this.initialize = function() {
433 this.initialize_table(jQuery(this).data());
439 } // function PresView