plugins: added query_editor
authorJordan Augé <jordan.auge@lip6.fr>
Fri, 2 Aug 2013 14:50:19 +0000 (16:50 +0200)
committerJordan Augé <jordan.auge@lip6.fr>
Fri, 2 Aug 2013 14:50:19 +0000 (16:50 +0200)
13 files changed:
manifold/js/manifold.js
plugins/query_editor/__init__.py [new file with mode: 0644]
plugins/query_editor/demo_page.css [new file with mode: 0644]
plugins/query_editor/demo_table.css [new file with mode: 0644]
plugins/query_editor/filter_input_integer.html [new file with mode: 0644]
plugins/query_editor/filter_input_others.html [new file with mode: 0644]
plugins/query_editor/filter_input_string.html [new file with mode: 0644]
plugins/query_editor/filter_input_string_values.html [new file with mode: 0644]
plugins/query_editor/images/myslice-icon-filter.png [new file with mode: 0644]
plugins/query_editor/query_editor.css [new file with mode: 0644]
plugins/query_editor/query_editor.html [new file with mode: 0644]
plugins/query_editor/query_editor.js [new file with mode: 0644]
trash/sliceview.py

index e7f6c1d..89c83d1 100644 (file)
@@ -87,7 +87,7 @@ function QueryStore() {
                 parent_query_ext = manifold.query_store.find_analyzed_query_ext(parent_query.query_uuid);
             else
                 parent_query_ext = null;
-            sq_ext = QueryExt(sq, parent_query_ext, query_ext)
+            sq_ext = new QueryExt(sq, parent_query_ext, query_ext)
             manifold.query_store.analyzed_queries[sq.query_uuid] = sq_ext;
         });
     }
@@ -397,7 +397,7 @@ var manifold = {
         switch(event_type) {
             case SET_ADD:
                 // Query uuid has been updated with the key of a new element
-                query_ext    = manifold.find_analyzed_query(query_uuid);
+                query_ext    = manifold.query_store.find_analyzed_query(query_uuid);
 
                 // update is only possible is the query is not pending, etc
                 // CHECK status !
diff --git a/plugins/query_editor/__init__.py b/plugins/query_editor/__init__.py
new file mode 100644 (file)
index 0000000..f5a334e
--- /dev/null
@@ -0,0 +1,68 @@
+from unfold.plugin import Plugin
+
+from django.template.loader import render_to_string
+
+class QueryEditor(Plugin):
+
+    def template_file(self):
+        return "query_editor.html"
+
+    def requirements (self):
+        reqs = {
+            'js_files' : [
+                'js/query_editor.js',
+            ] ,
+            'css_files': [
+                'css/query_editor.css',
+                'css/demo_page.css',
+                'css/demo_table.css',
+            ]
+        }
+        return reqs
+
+    def json_settings_list (self):
+        return ['plugin_uuid', 'domid', 'query_uuid']
+
+    def export_json_settings (self):
+        return True
+
+    def template_env(self, request):
+        fields = []
+        metadata = self.page.get_metadata()
+        md_fields = metadata.details_by_object('resource')
+        print "METADATA FIELDS", md_fields
+
+        # XXX use django templating system here
+        for md_field in md_fields['column']:
+
+            if md_field['type'] == 'string':
+                if 'allowed_values' in md_field:
+                    allowed_values = md_field['allowed_values'].split(',')
+
+                    options = []
+                    for v in allowed_values:
+                        v_desc = v.split('-')
+                        options.append(v_desc[0])
+
+                    env = {'options': options}
+                    filter_input = render_to_string('filter_input_string_values.html', env)
+                else:
+                    env = {'filter_id': "%s-filter-%s" % (self.domid, md_field['name'])}
+                    filter_input = render_to_string('filter_input_string.html', env)
+                    
+            elif md_field['type'] == 'int':
+                allowed_values = md_field.get('allowed_values', '0,0').split(',')
+                env = {'min': allowed_values[0], 'max': allowed_values[1]}
+                filter_input = render_to_string('filter_input_integer.html', env)
+            else:
+                env = {'filter_id': "%s-filter-%s" % (self.domid, md_field['name'])}
+                filter_input = render_to_string('filter_input_others.html', env)
+
+            fields.append({
+                'name':          md_field['name'],
+                'type':          md_field['type'],
+                'resource_type': 'N/A',
+                'filter_input':  filter_input,
+                'header':        None,
+            })
+        return { 'fields': fields }
diff --git a/plugins/query_editor/demo_page.css b/plugins/query_editor/demo_page.css
new file mode 100644 (file)
index 0000000..bee7b0d
--- /dev/null
@@ -0,0 +1,93 @@
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * General page setup
+ */
+#dt_example {
+       font: 80%/1.45em "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+       margin: 0;
+       padding: 0;
+       color: #333;
+       background-color: #fff;
+}
+
+
+#dt_example #container {
+       width: 800px;
+       margin: 30px auto;
+       padding: 0;
+}
+
+
+#dt_example #footer {
+       margin: 50px auto 0 auto;
+       padding: 0;
+}
+
+#dt_example #demo {
+       margin: 30px auto 0 auto;
+}
+
+#dt_example .demo_jui {
+       margin: 30px auto 0 auto;
+}
+
+#dt_example .big {
+       font-size: 1.3em;
+       font-weight: bold;
+       line-height: 1.6em;
+       color: #4E6CA3;
+}
+
+#dt_example .spacer {
+       height: 20px;
+       clear: both;
+}
+
+#dt_example .clear {
+       clear: both;
+}
+
+#dt_example pre {
+       padding: 15px;
+       background-color: #F5F5F5;
+       border: 1px solid #CCCCCC;
+}
+
+#dt_example h1 {
+       margin-top: 2em;
+       font-size: 1.3em;
+       font-weight: normal;
+       line-height: 1.6em;
+       color: #4E6CA3;
+       border-bottom: 1px solid #B0BED9;
+       clear: both;
+}
+
+#dt_example h2 {
+       font-size: 1.2em;
+       font-weight: normal;
+       line-height: 1.6em;
+       color: #4E6CA3;
+       clear: both;
+}
+
+#dt_example a {
+       color: #0063DC;
+       text-decoration: none;
+}
+
+#dt_example a:hover {
+       text-decoration: underline;
+}
+
+#dt_example ul {
+       color: #4E6CA3;
+}
+
+.css_right {
+       float: right;
+}
+
+.css_left {
+       float: left;
+}
\ No newline at end of file
diff --git a/plugins/query_editor/demo_table.css b/plugins/query_editor/demo_table.css
new file mode 100644 (file)
index 0000000..6b6fb34
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     ../images/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+/*
+.dataTables_wrapper {
+    padding-bottom: 0em;
+    padding-top: 0em;
+       position: relative;
+       min-height: 302px;
+       clear: both;
+       _height: 302px;
+       zoom: 1; / Feeling sorry for IE /
+}
+*/
+.dataTables_processing {
+       position: absolute;
+       top: 50%;
+       left: 50%;
+       width: 250px;
+       height: 30px;
+       margin-left: -125px;
+       margin-top: -15px;
+       padding: 14px 0 2px 0;
+       border: 1px solid #ddd;
+       text-align: center;
+       color: #999;
+       font-size: 14px;
+       background-color: white;
+}
+
+.dataTables_length {
+       width: 40%;
+       float: left;
+}
+
+.dataTables_filter {
+       width: 50%;
+       float: right;
+       text-align: right;
+}
+
+.dataTables_info {
+/*     width: 45%;
+       float: left;
+    padding-top: 1.2em;
+    padding-left: 1em;
+}
+
+.dataTables_paginate {
+       /* width: 44px;
+        width: 50px;
+       float: right;
+       text-align: right;
+}
+*/
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+       height: 19px;
+       width: 19px;
+       margin-left: 3px;
+       /*float: left;*/
+}
+
+.paginate_disabled_previous {
+       background-image: url('images/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+       background-image: url('images/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+       background-image: url('images/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+       background-image: url('images/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+       margin: 0 auto;
+       clear: both;
+       width: 100%;
+       
+       /* Note Firefox 3.5 and before have a bug with border-collapse
+        * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+        * border-spacing: 0; is one possible option. Conditional-css.com is
+        * useful for this kind of thing
+        *
+        * Further note IE 6/7 has problems when calculating widths with border width.
+        * It subtracts one px relative to the other browsers from the first column, and
+        * adds one to the end...
+        *
+        * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+        * then filling in the gaps with other borders.
+        */
+}
+
+table.display thead th {
+       padding: 3px 18px 3px 10px;
+       border-bottom: 1px solid black;
+       font-weight: bold;
+       cursor: pointer;
+       * cursor: hand;
+}
+
+table.display tfoot th {
+       padding: 3px 18px 3px 10px;
+       border-top: 1px solid black;
+       font-weight: bold;
+}
+
+table.display tr.heading2 td {
+       border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+       padding: 3px 10px;
+}
+
+table.display td.center {
+       text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+       background: url('images/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+       background: url('images/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+       background: url('images/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+       background: url('images/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+       background: url('images/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+       background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+       background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+       background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+       background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+       background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+       background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+       background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+       background-color: #eee;
+}
+
+
+tr.odd {
+       background-color: #E2E4FF;
+}
+
+tr.even {
+       background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+/*     clear: both; JORDAN */
+}
+
+.dataTables_scrollBody {
+       *margin-top: -1px;
+}
+/*
+.top, .bottom {
+       padding: 15px;
+       background-color: #F5F5F5;
+       border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+       float: none;
+}
+*/
+.clear {
+       clear: both;
+}
+
+.dataTables_empty {
+       text-align: center;
+}
+
+tfoot input {
+       margin: 0.5em 0;
+       width: 100%;
+       color: #444;
+}
+
+tfoot input.search_init {
+       color: #999;
+}
+
+td.group {
+       background-color: #d1cfd0;
+       border-bottom: 2px solid #A19B9E;
+       border-top: 2px solid #A19B9E;
+}
+
+td.details {
+       background-color: #d1cfd0;
+       border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+       width: 40%;
+}
+
+.paging_full_numbers {
+       width: 45%;
+       height: 22px;
+       line-height: 22px;
+    padding-top: 1em;    
+    padding-right: 1em;    
+}
+
+.paging_full_numbers a.paginate_button,
+       .paging_full_numbers a.paginate_active {
+       border: 1px solid #aaa;
+       -webkit-border-radius: 5px;
+       -moz-border-radius: 5px;
+       padding: 2px 5px;
+       margin: 0 3px;
+       cursor: pointer;
+       *cursor: hand;
+}
+
+.paging_full_numbers a.paginate_button {
+       background-color: #ddd;
+}
+
+.paging_full_numbers a.paginate_button:hover {
+       background-color: #ccc;
+}
+
+.paging_full_numbers a.paginate_active {
+       background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+       background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+       background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+       background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+       background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+       background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+       background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+       background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+       background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+       Colour calculations (based off the main row colours)
+  Level 1:
+               dd > c4
+               ee > d5
+       Level 2:
+         dd > d1
+         ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+       background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+       background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+       background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+       background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+       background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+       background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+       background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+       background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+       background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+       background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+       background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+       background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+       background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+       background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+       background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+       background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+       background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+       background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+       background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+       background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+       background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+       background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+       background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+       background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+       background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+       background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+       background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+       background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+       background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+       background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+       background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+       background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+       background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+       background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+       border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+       border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+       background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+       background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+       background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+       background-color: #ddd;
+}
+
+div.box {
+       height: 100px;
+       padding: 10px;
+       overflow: auto;
+       border: 1px solid #8080FF;
+       background-color: #E5E5FF;
+}
diff --git a/plugins/query_editor/filter_input_integer.html b/plugins/query_editor/filter_input_integer.html
new file mode 100644 (file)
index 0000000..ab80a64
--- /dev/null
@@ -0,0 +1,2 @@
+<input class='queryeditor-filter-min' id='$filter_id-min' type=text size=3 value='{{ min }}'></input>&nbsp;-&nbsp;
+<input class='queryeditor-filter-max' id='$filter_id-max' type=text size=3 value='{{ max }}'></input>
diff --git a/plugins/query_editor/filter_input_others.html b/plugins/query_editor/filter_input_others.html
new file mode 100644 (file)
index 0000000..3ccaae9
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='ui-widget content' style='margin-top:0px;'>
+    <label for='{{ filter_id }}'></label>
+    <input class='queryeditor-auto-filter' id='{{ filter_id }}' type=text></input>
+</div>
diff --git a/plugins/query_editor/filter_input_string.html b/plugins/query_editor/filter_input_string.html
new file mode 100644 (file)
index 0000000..12971d9
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='ui-widget content' style='margin-top:0px;'>
+  <label for='{{ filter_id }}'></label>
+  <input class='queryeditor-auto-filter' id='{{ filter_id }}' type=text></input>
+</div>
diff --git a/plugins/query_editor/filter_input_string_values.html b/plugins/query_editor/filter_input_string_values.html
new file mode 100644 (file)
index 0000000..f0a10df
--- /dev/null
@@ -0,0 +1,5 @@
+<select class='queryeditor-filter' id='$filter_id'>
+    <option value=''>Show all</option>
+    _filter_input.append("<option>%s</option>" % v_desc[0])
+_filter_input.append('</select>')
+filter_input = "\n".join(_filter_input)
diff --git a/plugins/query_editor/images/myslice-icon-filter.png b/plugins/query_editor/images/myslice-icon-filter.png
new file mode 100644 (file)
index 0000000..b2f4ec7
Binary files /dev/null and b/plugins/query_editor/images/myslice-icon-filter.png differ
diff --git a/plugins/query_editor/query_editor.css b/plugins/query_editor/query_editor.css
new file mode 100644 (file)
index 0000000..5ef2875
--- /dev/null
@@ -0,0 +1,341 @@
+/* Add a scrollbar to autocomplete fields */
+.ui-autocomplete {
+        max-height: 100px;
+        overflow-y: auto;
+        /* prevent horizontal scrollbar */
+        overflow-x: hidden;
+        /* add padding to account for vertical scrollbar */
+        padding-right: 20px;
+        
+        /* NEED TO BE IMPROVED LATER... */
+        /* How to use properties from content class in /templates/myslice/css/myslice.css ? */
+        /* How to factorize this ? Maybe applied differently in other plugins ? */        
+        font-size: 11px;
+}
+.queryeditor-auto-filter{
+    width:200px;
+}
+/* IE 6 doesn't support max-height
+    * we use height instead, but this forces the menu to always be this tall
+    */
+* html .ui-autocomplete {
+        height: 100px;
+}
+
+table.columns {
+       margin: 0 auto;
+       clear: both;
+       /* width: 80%;*/
+       width: 300px;
+}
+
+table.columns thead th {
+       padding: 3px 18px 3px 3px; 
+       border-bottom: 1px solid black;
+       font-weight: bold;
+       cursor: pointer;
+       * cursor: hand;
+}
+
+table.columns tfoot th {
+       padding: 3px 18px 3px 10px;
+       border-top: 1px solid black;
+       font-weight: bold;
+}
+
+table.columns td {
+       padding: 3px 5px;
+}
+
+table.columns td.center {
+       text-align: center;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.columns tr.odd.gradeA {
+       background-color: #ddffdd;
+}
+
+table.columns tr.even.gradeA {
+       background-color: #eeffee;
+}
+
+table.columns tr.odd.gradeC {
+       background-color: #ddddff;
+}
+
+table.columns tr.even.gradeC {
+       background-color: #eeeeff;
+}
+
+table.columns tr.odd.gradeX {
+       background-color: #ffdddd;
+}
+
+table.columns tr.even.gradeX {
+       background-color: #ffeeee;
+}
+
+table.columns tr.odd.gradeU {
+       background-color: #ddd;
+}
+
+table.columns tr.even.gradeU {
+       background-color: #eee;
+}
+
+/* change color: T / even -> odd +1 +5 -3*/
+table.columns tr.odd.row_sliver td {
+       background-color: #9FAFD1;
+}
+
+table.columns tr.even.row_added td {
+       background-color: #b1d19f;
+}
+table.columns tr.odd.row_added td {
+       background-color: #a3c98f;
+}
+
+table.columns tr.even.row_removed td {
+       background-color: #d9b0b0;
+}
+
+table.columns tr.odd.row_removed td {
+       background-color: #d1a09f;
+}
+
+table.columns tr.gradeA {
+       background-color: #eeffee;
+}
+
+table.columns tr.gradeC {
+       background-color: #ddddff;
+}
+
+table.columns tr.gradeX {
+       background-color: #ffdddd;
+}
+
+table.columns tr.gradeU {
+       background-color: #ddd;
+}
+
+
+
+
+div.selected{background-color:gray; color:black}
+
+/* icons */
+.myslice-icon-timestamp {
+    background-image: url('images/myslice-icon-timestamp.png') !important;
+}
+.myslice-icon-filter {
+    background-image: url('images/myslice-icon-filter.png') !important;
+}
+.myslice-icon-fields {
+    background-image: url('images/myslice-icon-fields.png') !important;
+}
+.myslice-icon-groups {
+    background-image: url('images/myslice-icon-groups.png') !important;
+}
+.myslice-icon-summary {
+    background-image: url('images/myslice-icon-summary.png') !important;
+}
+.myslice-icon-resources {
+    background-image: url('images/myslice-icon-resources.png') !important;
+}
+.myslice-icon-users {
+    background-image: url('images/myslice-icon-users.png') !important;
+}
+
+a.source-url{
+    font-weight: bold;
+}
+
+span.bold {
+    font-weight: bold;
+}
+
+div#selectdescr {
+    padding-top:2em;
+    color: #555555;
+}
+
+span.short {
+height:10px;
+}
+
+span.column-title {
+    font-size: 15px;
+    font-weight: bold;
+}
+
+span.column-detail {
+    font-size: 11px; 
+    font-style: italic;
+}
+
+span.group_info {
+    font-size: 11px; 
+    color: green;
+    font-weight: bold;
+}
+
+span.filter_info {
+    color: red;
+    font-weight: bold;
+}
+
+
+/* column configuation style */
+
+OPTION.out{background-color:white; color:black}
+OPTION.in{background-color:#CAE8EA; color:#4f6b72}
+
+/* jordan disabled
+div.out{background-color:white; color:black}
+div.in{background-color:#CAE8EA; color:#4f6b72}
+div.selected{background-color:gray; color:black}
+div.invisible{display:none}
+*/
+
+div.note-div {
+       padding: 4px;
+       background-color: #cae8ea;
+       width: 800px;
+       margin-left:auto;
+       margin-right:auto;
+
+}
+
+div#scrolldiv_old {
+       border : solid 2px grey; 
+       padding:4px; 
+       width:300px; 
+       height:180px; 
+       overflow:auto;
+}
+
+th,td.top {
+       vertical-align: top;
+       text-align: left;
+       padding:10px;
+}
+
+tr.hidden {
+    display:none;
+}
+
+td.smallright {
+       text-align: right;
+       width:20px;
+}
+
+table.center {
+       margin-left:auto; 
+       margin-right:auto;
+}
+
+table.columnlist {
+       width:270px;
+}
+
+table.columns td.header {
+       background-color: #CAE8EA;
+       text-align: center;
+       width:30px;
+}
+
+span.header {
+       font-weight: bold;
+       color: #3399CC;
+}
+
+a.source-url{
+        font-weight: bold;
+}
+
+span.menubig {
+        font-size: 16px;
+        font-weight: bold;
+}
+
+span.menusmall {
+        font-size: 14px;
+        font-weight: bold;
+}
+
+span.menuright {
+        font-weight: bold;
+       float: right;
+}
+
+span.simpleright {
+       float: right;
+}
+
+span.gray{
+        color: #555555;
+}
+
+span.short {
+       height:10px;
+}
+
+span.column-title {
+        font-size: 13px;
+        font-weight: bold;
+}
+
+span.column-detail {
+        font-size: 11px; 
+        font-style: italic;
+}
+
+span.myslice_small {
+        font-size: 11px; 
+}
+
+span#username {
+    font-weight: bold;
+    font-size: 1.3em;
+}
+
+.filter_popup{
+    position:relative; /*this is the key*/
+    float: right;
+    z-index:24;
+    background: url('images/myslice-icon-filter.png') no-repeat;
+    //background-color:#ccc;
+    width: 200px;
+    height: 300px;
+    color:#000;
+    text-decoration:none;
+    clear: both;
+}
+
+.filter_popup:hover{
+    z-index:25;
+    //background-color:#ff0
+}
+
+.filter_popup span{display: none}
+
+.filter_popup:hover span{ /*the span will display just on :hover state*/
+    display:block;
+    position:absolute;
+    //top:1em;
+    left:-19em;
+    width: 20em;
+    font-size: 8pt;
+    border:1px solid #ccdddd;
+    background-color:#ddeeee;
+    color:#000;
+    text-align: left;
+    padding: 0em 0em 0em 1em;
+}
+
+
diff --git a/plugins/query_editor/query_editor.html b/plugins/query_editor/query_editor.html
new file mode 100644 (file)
index 0000000..b822959
--- /dev/null
@@ -0,0 +1,41 @@
+<!--
+       $infopopup = <<<EOF
+<span class='info'><span> This tab allows you to customize the visible columns in the resource list, below. Note that information on the nodes comes from a variety of monitoring sources.  If you, as either a user or a provider of monitoring data, would like to see additional columns made available, please send us your request in mail to <a href='mailto:support@myslice.info'>support@myslice.info</a>. You can find more information about the MySlice project at <a href='http://trac.myslice.info'>http://trac.myslice.info</a>.  </span></span>
+EOF;
+
+           $filter_input = "<input id='filter_value' type=text size=5></input>";
+
+EOF; -->
+
+<div>
+  <table id='{{domid}}-table' class='display'>
+    <thead>
+      <tr>
+        <th class='center'>Field</th>
+        <th class='center'>Resource</th>
+        <th class='center'>Type</th>
+        <th class='center'>Filter</th>
+        <th class='center'>+/-</th>
+      </tr>
+    </thead>
+    <tbody>
+
+    {# Loop through metadata and display related information #}
+    {% for field in fields %}
+
+      <tr>
+        <td class='center'><b> {{ field.name }}</b></td>
+        <td class='center'> {{ field.resource_type }}</td>
+        <td class='center'> {{ field.type }}</td>
+        <td class='center'> {{ field.filter_input }}</td>
+        <td class='center'>
+          <input class='queryeditor-check' id='check_{{ field.name }}' name='{{ field.header }}' type='checkbox' autocomplete='off' value='{{ field.header}}'></input>
+        </td>
+      </tr>
+               
+       {% endfor %}
+
+    </tbody>
+  </table>
+</div>
+
diff --git a/plugins/query_editor/query_editor.js b/plugins/query_editor/query_editor.js
new file mode 100644 (file)
index 0000000..3bc3d8b
--- /dev/null
@@ -0,0 +1,441 @@
+/**
+ * Description: QueryEditor plugin
+ * Copyright (c) 2012-2013 UPMC Sorbonne Universite
+ * 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 = 'QueryEditor';
+
+    // routing calls
+    jQuery.fn.QueryEditor = 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 QueryEditor(options);
+                $this.data('Manifold', plugin);
+
+            }); // 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');
+
+                $this.set_query_handler(options.query_uuid, hazelnut.query_handler);
+                $this.set_record_handler(options.query_uuid, hazelnut.record_handler); 
+
+                /* XXX Subscribe to query updates to maintain current state of query (multiple editors) */
+                jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
+                jQuery.subscribe('/query/' + options.query_uuid + '/diff', {instance: $this}, query_changed_diff);
+                /* Subscribe to results in order to redraw the table when updates arrive */
+                jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_autocomplete);
+
+            });
+        }, // destroy
+
+    }; // var methods;
+
+    /***************************************************************************
+     * Plugin object
+     ***************************************************************************/
+
+    function QueryEditor(options)
+    {
+
+        /* member variables */
+        this.options = options;
+
+        var object = this;
+
+        this.initialize_table = function(data) 
+        {
+
+            var d = data;
+            
+            jQuery('.queryeditor-filter').change(function(event) { 
+                query = data.current_query;
+                var key=getKeySplitId(event.target.id,"-");
+                var op='=';
+                var value=event.target.value;
+                            
+                if(value){                
+                    query.update_filter(key, op, value);
+                    //add_ActiveFilter(event.target.id, '=',event.target.value,data);
+                }else{
+                    query.remove_filter(key,op,"");
+                    //remove_ActiveFilter(event, data, event.target.id,'=');
+                }
+                // Publish the query changed, the other plugins with subscribe will get the changes
+                jQuery.publish('/query/' + query.uuid + '/changed', query);
+            });
+            jQuery('.queryeditor-filter-min').change(function(event) {
+                query = data.current_query;
+                var key=getKeySplitId(event.target.id,"-");
+                var op='>';
+                var value=event.target.value;
+                
+                if(value){
+                    query.update_filter(key, op, value);
+                    //add_ActiveFilter(event.target.id,'>',event.target.value,data);
+                }else{
+                    query.remove_filter(key,op,"");
+                    //remove_ActiveFilter(event, data, event.target.id,'>');
+                }
+                // Publish the query changed, the other plugins with subscribe will get the changes
+                jQuery.publish('/query/' + query.uuid + '/changed', query);
+            });
+            jQuery('.queryeditor-filter-max').change(function(event) {
+                query = data.current_query;
+                var key=getKeySplitId(event.target.id,"-");
+                var op='<';
+                var value=event.target.value;
+                
+                if(value){
+                    query.update_filter(key, op, value);
+                    //add_ActiveFilter(event.target.id,'<',event.target.value,data);
+                }else{
+                    query.remove_filter(key,op,"");
+                    //remove_ActiveFilter(event, data, event.target.id,'<');
+                }
+                // Publish the query changed, the other plugins with subscribe will get the changes
+                jQuery.publish('/query/' + query.uuid + '/changed', query);
+            });
+
+            jQuery('.queryeditor-check').click(function() { 
+                manifold.raise_event(object.options.query_uuid, this.checked?SET_ADD:SET_REMOVED, this.value);
+                /*
+                    var column = this.id.substring(6);
+                    query = data.current_query;
+                    if (this.checked) {
+                        if (jQuery.inArray(column, query.fields) == -1) {
+                            query.fields.push(column);
+                            jQuery.publish('/query/' + query.uuid + '/changed', query);
+                        }
+                    } else {
+                        query.fields = jQuery.grep(query.fields, function(value) {return value != column;});
+                        jQuery.publish('/query/' + query.uuid + '/changed', query);
+                    }
+                */
+                });
+
+            //onFunctionAvailable('jQuery.fn.dataTable', function() {
+
+                var nCloneTh = document.createElement( 'th' );
+                var nCloneTd = document.createElement( 'td' );
+                nCloneTd.innerHTML = "<span class='ui-icon ui-icon-triangle-1-e' style='cursor:pointer'></span>";
+                //nCloneTd.innerHTML = '<img src="/components/com_tophat/images/details_open.png">';
+                nCloneTh.innerHTML = '<b>Info</b>';
+                nCloneTd.className = "center";
+                nCloneTh.className = "center";
+         
+                jQuery('#'+this.options.plugin_uuid+'_fields thead tr').each( function () {
+                    this.insertBefore( nCloneTh, this.childNodes[0] );
+                });
+         
+                jQuery('#'+this.options.plugin_uuid+'_fields tbody tr').each( function () {
+                    this.insertBefore(  nCloneTd.cloneNode( true ), this.childNodes[0] );
+                });
+         
+                var  metaTable = jQuery('#'+this.options.plugin_uuid+'-table').dataTable( {
+                    bFilter: false,
+                    bPaginate: false,
+                    bInfo: false,
+                    sScrollX: '100%',       /* Horizontal scrolling */
+                    sScrollY: "200px",
+                    bJQueryUI: true, // Use jQuery UI
+                    bProcessing: true, // Loading
+                    aaSorting: [[ 1, "asc" ]], // sort by column fields on load
+                    aoColumnDefs: [ {"bSortable": false, "aTargets": [ 0 ]},
+                                      { "sWidth": "8px", "aTargets": [ 0 ] },
+                                      { "sWidth": "8px", "aTargets": [ 4 ] }
+                    ]
+                });
+
+                jQuery('#'+this.options.plugin_uuid+'_fields tbody td span').live('click', function () {
+                    var nTr = this.parentNode.parentNode;
+                    // use jQuery UI instead of images to keep a common UI
+                    // class="ui-icon treeclick ui-icon-triangle-1-s tree-minus"
+                    //East oriented Triangle class="ui-icon-triangle-1-e"
+                    //South oriented Triangle class="ui-icon-triangle-1-s"
+                    
+                    if(this.className=="ui-icon ui-icon-triangle-1-e"){
+                        this.removeClass("ui-icon-triangle-1-e");
+                        this.addClass("ui-icon-triangle-1-s");
+                        metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
+                    }else{
+                        this.removeClass("ui-icon-triangle-1-s");
+                        this.addClass("ui-icon-triangle-1-e");
+                        metaTable.fnClose( nTr );
+                    }
+                    /*
+                    if ( this.src.match('details_close') ) {
+                        this.src = "/components/com_tophat/images/details_open.png";
+                        metaTable.fnClose( nTr );
+                    }
+                    else {
+                        this.src = "/components/com_tophat/images/details_close.png";
+                        metaTable.fnOpen( nTr, this.fnFormatDetails(metaTable, nTr, this.options.plugin_uuid+'_div'), 'details' );
+                    }
+                    */
+                });
+
+                jQuery('#'+this.options.plugin_uuid+'_fields_wrapper').css({'padding-top':'0em','padding-bottom':'0em'});
+
+            //}); // onfunctionAvailable
+
+        } // initialize_table
+
+        this.print_field_description = function(field_header, div_id) { 
+            
+            //var selected = all_headers[field_header];
+            var selected = getMetadata_field('resource',field_header);
+
+            field_header = div_id+"_"+field_header;
+
+            var output = "<div id='desc"+field_header+"'>";
+
+            output += "<div id='divinfo"+field_header+"'>";
+            output += '<p><span class="column-title">'+selected['title']+'</span></p></span>'; 
+            output += '<p><span class="column-detail">'+selected['description']+'</span></p></span>'; 
+
+            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>";
+
+            if (selected['value_type'] == 'string') {
+
+                var values_select = "<p><select id='selectvalues"+field_header+"' MULTIPLE size=3>";
+
+                output += '<p>Values: ';
+
+                var values_list = selected['allowed_values'].split(",");
+
+                for (var value_index = 0; value_index < values_list.length ; value_index++) {
+                    var value_desc = values_list[value_index].split("-");
+                    if (value_index > 0)
+                        output += ', ';
+                    output += '<span class="bold">'+value_desc[0]+'</span>';
+                    values_select += "<option value ='"+value_desc[0]+"'>&nbsp;"+value_desc[0];
+                    if (value_desc[1]!='') 
+                        output += ' ('+value_desc[1]+')';
+
+                    values_select += "&nbsp;</option>";
+                }
+                values_select += "</select>";
+            }
+            else
+                output+='<p>Unit: '+selected['unit'];
+
+            output+= '</p>';
+
+            output += '<p>Source: <a class="source-url" target="source_window" href="'+selected['platform_url']+'">'+selected['platform']+'</a>';
+
+            //if (selected['via'] != '') 
+                //output += ' via <a class="source-url" target="source_window" href="http://'+selected['via_url']+'">'+selected['via']+'</a>';
+
+            output += '</p>';
+            output += "</div>";
+
+    /*
+            output += "<div id='divgroup"+field_header+"'>";
+            output += "<p>Group resources with the same value <input type=checkbox></input>";
+            output += "<p>Select aggregator : <select><option>Count</option><option selected=true>Average</option><option>Maximum</option><option>Minimum</option></select>";
+            output += "</div>";
+            output += "<div id='divtime"+field_header+"'>";
+            output += "<p>Select timestamp : ";
+            output += period_select;
+            output += "</div>";
+    */
+            output += "</div>";
+
+            return output;
+        }
+
+        this.update_autocomplete = function(e, rows, current_query)
+        {
+            var d = data;
+            d.current_query = current_query;
+            var availableTags={};
+            jQuery.each (rows, function(index, obj) {                    
+                jQuery.each(obj,function(key,value){                       
+                    value = get_value(value); 
+                    if(!availableTags.hasOwnProperty(key)){availableTags[key]=new Array();}
+                    //availableTags[key].push(value);
+                    var currentArray=availableTags[key];
+                    if(value!=null){
+                        if(jQuery.inArray(value,currentArray)==-1){availableTags[key].push(value);}
+                    }
+                });                    
+            });
+            jQuery.each(availableTags, function(key, value){
+                value.sort();
+                jQuery("#"+options.plugin_uuid+"-filter-"+key).autocomplete({
+                            source: value,
+                            selectFirst: true,
+                            minLength: 0, // allows to browse items with no value typed in
+                            select: function(event, ui) {
+                                var key=getKeySplitId(this.id,"-");
+                                var op='=';
+                                var val=ui.item.value;
+                                
+                                query=d.current_query;
+                                query.update_filter(key,op,val);
+                                // Publish the query changed, the other plugins with subscribe will get the changes
+                                jQuery.publish('/query/' + query.uuid + '/changed', query);
+                                //add_ActiveFilter(this.id,'=',ui.item.value,d);
+                            }
+                });
+            });                
+        } // update_autocomplete     
+
+        /**
+         * This function is used to update autocomplete
+         */
+        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.query_handler = function(e, event_type, data)
+        {
+            // This replaces the complex set_query function
+            // The plugin does not need to remember the query anymore
+            switch(event_type) {
+                // Filters
+                // When Query changed, Then we need to update the filters of
+                // QueryEditor plugin if the filter is active, set the value
+                // (the update can come from another plugin) else set the
+                // filter value to null PB if the filter is composed of MIN/MAX
+                // values
+                case FILTER_ADDED:
+                    filter = data;
+                    // Set the value of the filter = to query filter value
+                    // Necessary if the filter has been modified by another plugin (QuickFilter)
+                    if(filter[1]=="="){
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(filter[2]);
+                    }else if(filter[1]=="<"){
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(filter[2]);
+                    }else if(filter[1]==">"){
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(filter[2]);
+                    }
+                case FILTER_REMOVED:
+                    filter = data;
+                    if(filter[1]=="="){
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]).val(null);
+                    }else if(filter[1]=="<"){
+                        //502124d5a5848-filter-asn-max
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-max').val(null);
+                    }else if(filter[1]==">"){
+                        //502124d5a5848-filter-asn-min
+                        jQuery('#'+this.options.plugin_uuid+'-filter-'+filter[0]+'-min').val(null);
+                    }
+                case CLEAR_FILTERS:
+                    break;
+
+                // Fields
+                /* Hide/unhide columns to match added/removed fields */
+                case FIELD_ADDED:
+                    $('#check_' + data).attr('checked', true);
+                    break;
+                case FIELD_REMOVED:
+                    $('#check_' + data).attr('checked', false);
+                    break;
+                case CLEAR_FIELDS:
+                    alert(PLUGIN_NAME + '::clear_fields() not implemented');
+                    break;
+            } // switch
+
+
+        }
+        this.fnFormatDetails  = function( metaTable, nTr, div_id ) {
+            var aData = metaTable.fnGetData( nTr );
+            var sOut = '<blockquote>';
+            //sOut += prepare_tab_description(aData[1].substr(21, aData[1].length-21-7), div_id);
+            sOut += this.print_field_description(aData[1].substring(3, aData[1].length-4), div_id);
+            sOut += '</blockquote>';
+         
+            return sOut;
+        }
+       
+            
+        /**
+         *
+         */
+        this.initialize = function() {
+            //XXX
+            this.initialize_table(jQuery(this).data());
+        }
+        /* Constructor */
+
+        this.initialize();
+
+    } // function PresView
+
+})( jQuery );
index 759d20b..94b539c 100644 (file)
@@ -20,6 +20,7 @@ from plugins.resources_selected      import ResourcesSelected
 from plugins.googlemap.googlemap     import GoogleMap 
 from plugins.senslabmap.senslabmap   import SensLabMap
 from plugins.querycode.querycode     import QueryCode
+from plugins.query_editor            import QueryEditor
 from plugins.quickfilter.quickfilter import QuickFilter
 from plugins.messages.messages       import Messages
 from plugins.updater.updater         import Updater
@@ -127,6 +128,12 @@ def _slice_view (request, slicename):
         sons=[],
     )
 
+    resource_query_editor = QueryEditor(
+        page  = page,
+        query = sq_resource,
+    )
+    stack_resources.insert(resource_query_editor)
+
     # --------------------------------------------------------------------------
     # Different displays = DataTables + GoogleMaps
     #