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 is used for autocomplete
52 $this.set_record_handler(options.query_uuid, plugin.record_handler);
58 * @brief Plugin destruction
59 * @return : a jQuery collection of objects on which the plugin is
60 * applied, which allows to maintain chainability of calls
62 destroy : function( ) {
64 return this.each(function() {
66 var plugin = $this.data('Manifold');
68 // Unbind all events using namespacing
69 $(window).unbind(PLUGIN_NAME);
71 // Remove associated data
73 $this.removeData('Manifold');
80 /***************************************************************************
82 ***************************************************************************/
84 function QueryEditor(options)
87 /* member variables */
88 this.options = options;
92 this.initialize_table = function(data)
97 jQuery('.queryeditor-auto-filter').change(function(event) {
98 var key = event.target.id.split('-')[4]; // Should be consistent with the naming of fields
100 var value = event.target.value;
102 manifold.raise_event(object.options.query_uuid, FILTER_ADDED, [key, op, value]);
105 jQuery('.queryeditor-filter').change(function(event) {
106 var key = event.target.id.split('-')[4];
108 var value = event.target.value;
110 manifold.raise_event(object.options.query_uuid, FILTER_ADDED, [key, op, value]);
112 jQuery('.queryeditor-filter-min').change(function(event) {
113 query = data.current_query;
114 var key=getKeySplitId(event.target.id,"-");
116 var value=event.target.value;
119 query.update_filter(key, op, value);
120 //add_ActiveFilter(event.target.id,'>',event.target.value,data);
122 query.remove_filter(key,op,"");
123 //remove_ActiveFilter(event, data, event.target.id,'>');
125 // Publish the query changed, the other plugins with subscribe will get the changes
126 jQuery.publish('/query/' + query.uuid + '/changed', query);
128 jQuery('.queryeditor-filter-max').change(function(event) {
129 query = data.current_query;
130 var key=getKeySplitId(event.target.id,"-");
132 var value=event.target.value;
135 query.update_filter(key, op, value);
136 //add_ActiveFilter(event.target.id,'<',event.target.value,data);
138 query.remove_filter(key,op,"");
139 //remove_ActiveFilter(event, data, event.target.id,'<');
141 // Publish the query changed, the other plugins with subscribe will get the changes
142 jQuery.publish('/query/' + query.uuid + '/changed', query);
145 jQuery('.queryeditor-check').click(function() {
146 manifold.raise_event(object.options.query_uuid, this.checked?FIELD_ADDED:FIELD_REMOVED, this.value);
148 var column = this.id.substring(6);
149 query = data.current_query;
151 if (jQuery.inArray(column, query.fields) == -1) {
152 query.fields.push(column);
153 jQuery.publish('/query/' + query.uuid + '/changed', query);
156 query.fields = jQuery.grep(query.fields, function(value) {return value != column;});
157 jQuery.publish('/query/' + query.uuid + '/changed', query);
162 //onFunctionAvailable('jQuery.fn.dataTable', function() {
164 var nCloneTh = document.createElement( 'th' );
165 var nCloneTd = document.createElement( 'td' );
166 nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
167 //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
168 nCloneTh.innerHTML = '<b>Info</b>';
169 nCloneTd.className = "center";
170 nCloneTh.className = "center";
172 jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
173 this.insertBefore( nCloneTh, this.childNodes[0] );
176 jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
177 this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
180 var metaTable = jQuery('#'+this.options.plugin_uuid+'-table').dataTable( {
184 sScrollX: '100%', /* Horizontal scrolling */
186 bJQueryUI: true, // Use jQuery UI
187 bProcessing: true, // Loading
188 aaSorting: [[ 1, "asc" ]], // sort by column fields on load
189 aoColumnDefs: [ {"bSortable": false, "aTargets": [ 0 ]},
190 { "sWidth": "8px", "aTargets": [ 0 ] },
191 { "sWidth": "8px", "aTargets": [ 4 ] }
195 jQuery('#'+this.options.plugin_uuid+'_fields tbody td span').live('click', function () {
196 var nTr = this.parentNode.parentNode;
197 // use jQuery UI instead of images to keep a common UI
198 // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
199 //East oriented Triangle class="ui-icon-triangle-1-e"
200 //South oriented Triangle class="ui-icon-triangle-1-s"
202 if(this.className=="ui-icon ui-icon-triangle-1-e"){
203 this.removeClass("ui-icon-triangle-1-e");
204 this.addClass("ui-icon-triangle-1-s");
205 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
207 this.removeClass("ui-icon-triangle-1-s");
208 this.addClass("ui-icon-triangle-1-e");
209 metaTable.fnClose( nTr );
212 if ( this.src.match('details_close') ) {
213 this.src = "/components/com_tophat/images/details_open.png";
214 metaTable.fnClose( nTr );
217 this.src = "/components/com_tophat/images/details_close.png";
218 metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
223 jQuery('#'+this.options.plugin_uuid+'_fields_wrapper').css({'padding-top':'0em','padding-bottom':'0em'});
225 //}); // onfunctionAvailable
227 } // initialize_table
229 this.print_field_description = function(field_header, div_id) {
231 //var selected = all_headers[field_header];
232 var selected = getMetadata_field('resource',field_header);
234 field_header = div_id+"_"+field_header;
236 var output = "<div id='desc"+field_header+"'>";
238 output += "<div id='divinfo"+field_header+"'>";
239 output += '<p><span class="column-title">'+selected['title']+'</span></p></span>';
240 output += '<p><span class="column-detail">'+selected['description']+'</span></p></span>';
242 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>";
244 if (selected['value_type'] == 'string') {
246 var values_select = "<p><select id='selectvalues"+field_header+"' MULTIPLE size=3>";
248 output += '<p>Values: ';
250 var values_list = selected['allowed_values'].split(",");
252 for (var value_index = 0; value_index < values_list.length ; value_index++) {
253 var value_desc = values_list[value_index].split("-");
256 output += '<span class="bold">'+value_desc[0]+'</span>';
257 values_select += "<option value ='"+value_desc[0]+"'> "+value_desc[0];
258 if (value_desc[1]!='')
259 output += ' ('+value_desc[1]+')';
261 values_select += " </option>";
263 values_select += "</select>";
266 output+='<p>Unit: '+selected['unit'];
270 output += '<p>Source: <a class="source-url" target="source_window" href="'+selected['platform_url']+'">'+selected['platform']+'</a>';
272 //if (selected['via'] != '')
273 //output += ' via <a class="source-url" target="source_window" href="http://'+selected['via_url']+'">'+selected['via']+'</a>';
279 output += "<div id='divgroup"+field_header+"'>";
280 output += "<p>Group resources with the same value <input type=checkbox></input>";
281 output += "<p>Select aggregator : <select><option>Count</option><option selected=true>Average</option><option>Maximum</option><option>Minimum</option></select>";
283 output += "<div id='divtime"+field_header+"'>";
284 output += "<p>Select timestamp : ";
285 output += period_select;
293 this.update_autocomplete = function(e, rows, current_query)
296 d.current_query = current_query;
297 var availableTags={};
298 jQuery.each (rows, function(index, obj) {
299 jQuery.each(obj,function(key,value){
300 value = get_value(value);
301 if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
302 //availableTags[key].push(value);
303 var currentArray=availableTags[key];
305 if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
309 jQuery.each(availableTags, function(key, value){
311 jQuery("#"+options.plugin_uuid+"-filter-"+key).autocomplete({
314 minLength: 0, // allows to browse items with no value typed in
315 select: function(event, ui) {
316 var key=getKeySplitId(this.id,"-");
318 var val=ui.item.value;
320 query=d.current_query;
321 query.update_filter(key,op,val);
322 // Publish the query changed, the other plugins with subscribe will get the changes
323 jQuery.publish('/query/' + query.uuid + '/changed', query);
324 //add_ActiveFilter(this.id,'=',ui.item.value,d);
328 } // update_autocomplete
331 * This function is used to update autocomplete
333 this.record_handler = function(e, event_type, record)
348 this.query_handler = function(e, event_type, data)
350 // This replaces the complex set_query function
351 // The plugin does not need to remember the query anymore
352 // switch(event_type) {
354 // // When Query changed, Then we need to update the filters of
355 // // QueryEditor plugin if the filter is active, set the value
356 // // (the update can come from another plugin) else set the
357 // // filter value to null PB if the filter is composed of MIN/MAX
359 // case FILTER_ADDED:
361 // // Set the value of the filter = to query filter value
362 // // Necessary if the filter has been modified by another plugin (QuickFilter)
363 // if(filter[1]=="="){
364 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
365 // }else if(filter[1]=="<"){
366 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
367 // }else if(filter[1]==">"){
368 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
370 // case FILTER_REMOVED:
372 // if(filter[1]=="="){
373 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
374 // }else if(filter[1]=="<"){
375 // //502124d5a5848-filter-asn-max
376 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
377 // }else if(filter[1]==">"){
378 // //502124d5a5848-filter-asn-min
379 // jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
381 // case CLEAR_FILTERS:
385 // /* Hide/unhide columns to match added/removed fields */
386 // // XXX WRONG IDENTIFIERS
388 // object.check(data);
390 // case FIELD_REMOVED:
391 // object.uncheck(data);
393 // case CLEAR_FIELDS:
394 // alert(PLUGIN_NAME + '::clear_fields() not implemented');
400 this.check = function(field)
402 $('#check_' + field).attr('checked', true);
404 this.uncheck = function(field)
406 $('#check_' + field).attr('checked', false);
408 this.fnFormatDetails = function( metaTable, nTr, div_id ) {
409 var aData = metaTable.fnGetData( nTr );
410 var sOut = '<blockquote>';
411 //sOut += prepare_tab_description(aData[1].substr(21, aData[1].length-21-7), div_id);
412 sOut += this.print_field_description(aData[1].substring(3, aData[1].length-4), div_id);
413 sOut += '</blockquote>';
422 this.initialize = function() {
424 this.initialize_table(jQuery(this).data());
430 } // function PresView