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);
54 * @brief Plugin destruction
55 * @return : a jQuery collection of objects on which the plugin is
56 * applied, which allows to maintain chainability of calls
58 destroy : function( ) {
60 return this.each(function() {
62 var hazelnut = $this.data('Manifold');
64 // Unbind all events using namespacing
65 $(window).unbind(PLUGIN_NAME);
67 // Remove associated data
69 $this.removeData('Manifold');
71 $this.set_query_handler(options.query_uuid, hazelnut.query_handler);
72 $this.set_record_handler(options.query_uuid, hazelnut.record_handler);
74 /* XXX Subscribe to query updates to maintain current state of query (multiple editors) */
75 jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
76 jQuery.subscribe('/query/' + options.query_uuid + '/diff', {instance: $this}, query_changed_diff);
77 /* Subscribe to results in order to redraw the table when updates arrive */
78 jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_autocomplete);
85 /***************************************************************************
87 ***************************************************************************/
89 function QueryEditor(options)
92 /* member variables */
93 this.options = options;
97 this.initialize_table = function(data)
102 jQuery('.queryeditor-filter').change(function(event) {
103 query = data.current_query;
104 var key=getKeySplitId(event.target.id,"-");
106 var value=event.target.value;
109 query.update_filter(key, op, value);
110 //add_ActiveFilter(event.target.id, '=',event.target.value,data);
112 query.remove_filter(key,op,"");
113 //remove_ActiveFilter(event, data, event.target.id,'=');
115 // Publish the query changed, the other plugins with subscribe will get the changes
116 jQuery.publish('/query/' + query.uuid + '/changed', query);
118 jQuery('.queryeditor-filter-min').change(function(event) {
119 query = data.current_query;
120 var key=getKeySplitId(event.target.id,"-");
122 var value=event.target.value;
125 query.update_filter(key, op, value);
126 //add_ActiveFilter(event.target.id,'>',event.target.value,data);
128 query.remove_filter(key,op,"");
129 //remove_ActiveFilter(event, data, event.target.id,'>');
131 // Publish the query changed, the other plugins with subscribe will get the changes
132 jQuery.publish('/query/' + query.uuid + '/changed', query);
134 jQuery('.queryeditor-filter-max').change(function(event) {
135 query = data.current_query;
136 var key=getKeySplitId(event.target.id,"-");
138 var value=event.target.value;
141 query.update_filter(key, op, value);
142 //add_ActiveFilter(event.target.id,'<',event.target.value,data);
144 query.remove_filter(key,op,"");
145 //remove_ActiveFilter(event, data, event.target.id,'<');
147 // Publish the query changed, the other plugins with subscribe will get the changes
148 jQuery.publish('/query/' + query.uuid + '/changed', query);
151 jQuery('.queryeditor-check').click(function() {
152 manifold.raise_event(object.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, this.value);
154 var column = this.id.substring(6);
155 query = data.current_query;
157 if (jQuery.inArray(column, query.fields) == -1) {
158 query.fields.push(column);
159 jQuery.publish('/query/' + query.uuid + '/changed', query);
162 query.fields = jQuery.grep(query.fields, function(value) {return value != column;});
163 jQuery.publish('/query/' + query.uuid + '/changed', query);
168 //onFunctionAvailable('jQuery.fn.dataTable', function() {
170 var nCloneTh = document.createElement( 'th' );
171 var nCloneTd = document.createElement( 'td' );
172 nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
173 //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
174 nCloneTh.innerHTML = '<b>Info</b>';
175 nCloneTd.className = "center";
176 nCloneTh.className = "center";
178 jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
179 this.insertBefore( nCloneTh, this.childNodes[0] );
182 jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
183 this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
186 var metaTable = jQuery('#'+this.options.plugin_uuid+'-table').dataTable( {
190 sScrollX: '100%', /* Horizontal scrolling */
192 bJQueryUI: true, // Use jQuery UI
193 bProcessing: true, // Loading
194 aaSorting: [[ 1, "asc" ]], // sort by column fields on load
195 aoColumnDefs: [ {"bSortable": false, "aTargets": [ 0 ]},
196 { "sWidth": "8px", "aTargets": [ 0 ] },
197 { "sWidth": "8px", "aTargets": [ 4 ] }
201 jQuery('#'+this.options.plugin_uuid+'_fields tbody td span').live('click', function () {
202 var nTr = this.parentNode.parentNode;
203 // use jQuery UI instead of images to keep a common UI
204 // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
205 //East oriented Triangle class="ui-icon-triangle-1-e"
206 //South oriented Triangle class="ui-icon-triangle-1-s"
208 if(this.className=="ui-icon ui-icon-triangle-1-e"){
209 this.removeClass("ui-icon-triangle-1-e");
210 this.addClass("ui-icon-triangle-1-s");
211 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
213 this.removeClass("ui-icon-triangle-1-s");
214 this.addClass("ui-icon-triangle-1-e");
215 metaTable.fnClose( nTr );
218 if ( this.src.match('details_close') ) {
219 this.src = "/components/com_tophat/images/details_open.png";
220 metaTable.fnClose( nTr );
223 this.src = "/components/com_tophat/images/details_close.png";
224 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
229 jQuery('#'+this.options.plugin_uuid+'_fields_wrapper').css({'padding-top':'0em','padding-bottom':'0em'});
231 //}); // onfunctionAvailable
233 } // initialize_table
235 this.print_field_description = function(field_header, div_id) {
237 //var selected = all_headers[field_header];
238 var selected = getMetadata_field('resource',field_header);
240 field_header = div_id+"_"+field_header;
242 var output = "<div id='desc"+field_header+"'>";
244 output += "<div id='divinfo"+field_header+"'>";
245 output += '<p><span class="column-title">'+selected['title']+'</span></p></span>';
246 output += '<p><span class="column-detail">'+selected['description']+'</span></p></span>';
248 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>";
250 if (selected['value_type'] == 'string') {
252 var values_select = "<p><select id='selectvalues"+field_header+"' MULTIPLE size=3>";
254 output += '<p>Values: ';
256 var values_list = selected['allowed_values'].split(",");
258 for (var value_index = 0; value_index < values_list.length ; value_index++) {
259 var value_desc = values_list[value_index].split("-");
262 output += '<span class="bold">'+value_desc[0]+'</span>';
263 values_select += "<option value ='"+value_desc[0]+"'> "+value_desc[0];
264 if (value_desc[1]!='')
265 output += ' ('+value_desc[1]+')';
267 values_select += " </option>";
269 values_select += "</select>";
272 output+='<p>Unit: '+selected['unit'];
276 output += '<p>Source: <a class="source-url" target="source_window" href="'+selected['platform_url']+'">'+selected['platform']+'</a>';
278 //if (selected['via'] != '')
279 //output += ' via <a class="source-url" target="source_window" href="http://'+selected['via_url']+'">'+selected['via']+'</a>';
285 output += "<div id='divgroup"+field_header+"'>";
286 output += "<p>Group resources with the same value <input type=checkbox></input>";
287 output += "<p>Select aggregator : <select><option>Count</option><option selected=true>Average</option><option>Maximum</option><option>Minimum</option></select>";
289 output += "<div id='divtime"+field_header+"'>";
290 output += "<p>Select timestamp : ";
291 output += period_select;
299 this.update_autocomplete = function(e, rows, current_query)
302 d.current_query = current_query;
303 var availableTags={};
304 jQuery.each (rows, function(index, obj) {
305 jQuery.each(obj,function(key,value){
306 value = get_value(value);
307 if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
308 //availableTags[key].push(value);
309 var currentArray=availableTags[key];
311 if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
315 jQuery.each(availableTags, function(key, value){
317 jQuery("#"+options.plugin_uuid+"-filter-"+key).autocomplete({
320 minLength: 0, // allows to browse items with no value typed in
321 select: function(event, ui) {
322 var key=getKeySplitId(this.id,"-");
324 var val=ui.item.value;
326 query=d.current_query;
327 query.update_filter(key,op,val);
328 // Publish the query changed, the other plugins with subscribe will get the changes
329 jQuery.publish('/query/' + query.uuid + '/changed', query);
330 //add_ActiveFilter(this.id,'=',ui.item.value,d);
334 } // update_autocomplete
337 * This function is used to update autocomplete
339 this.record_handler = function(e, event_type, record)
344 /* NOTE in fact we are doing a join here */
345 if (object.received_all)
346 // update checkbox for record
347 object.set_checkbox(record);
349 // store for later update of checkboxes
350 object.in_set_buffer.push(record);
353 // nothing to do here
356 manifold.spin($(this));
359 if (object.received_all)
360 manifold.spin($(this), false);
361 object.received_set = true;
366 this.query_handler = function(e, event_type, data)
368 // This replaces the complex set_query function
369 // The plugin does not need to remember the query anymore
372 // When Query changed, Then we need to update the filters of
373 // QueryEditor plugin if the filter is active, set the value
374 // (the update can come from another plugin) else set the
375 // filter value to null PB if the filter is composed of MIN/MAX
379 // Set the value of the filter = to query filter value
380 // Necessary if the filter has been modified by another plugin (QuickFilter)
382 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
383 }else if(filter[1]=="<"){
384 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
385 }else if(filter[1]==">"){
386 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
391 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
392 }else if(filter[1]=="<"){
393 //502124d5a5848-filter-asn-max
394 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
395 }else if(filter[1]==">"){
396 //502124d5a5848-filter-asn-min
397 jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
403 /* Hide/unhide columns to match added/removed fields */
405 $('#check_' + data).attr('checked', true);
408 $('#check_' + data).attr('checked', false);
411 alert(PLUGIN_NAME + '::clear_fields() not implemented');
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