From 1c874e3cbe1fb02de91932c3c80d25fa515a2685 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Mon, 22 May 2017 13:48:17 -0400
Subject: [PATCH 01/10] Copy Query Builder to be modified into "Policy Builder"

---
 .../js/dq-security-policy-builder.js          | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 src/apps/dataq/client_src/js/dq-security-policy-builder.js

diff --git a/src/apps/dataq/client_src/js/dq-security-policy-builder.js b/src/apps/dataq/client_src/js/dq-security-policy-builder.js
new file mode 100644
index 00000000..95b80b95
--- /dev/null
+++ b/src/apps/dataq/client_src/js/dq-security-policy-builder.js
@@ -0,0 +1,110 @@
+/**
+ * Logic for constructing a SQL query string from a DataQ.Query object.
+ */
+(function() {
+  // If the global DataQ object does not exist, create it.
+  window.DataQ = window.DataQ || {};
+
+  /**
+   * Take a DataQ.Query object and generate a SQL query string from it.
+   *
+   * @param query - The DataQ.Query object.
+   * @return A String representing the SQL query.
+   */
+  window.DataQ.build_query = function(query) {
+
+    // The list of columns to select.
+    var select_list = [];
+
+    // The list of tables to select from.
+    var from_list = [];
+
+    // The filters to apply.
+    var where_list = [];
+
+    // The grouping to perform.
+    var group_list = [];
+
+    // The sorting to apply.
+    var order_list = [];
+
+    // Get the current repo name - we'll need to prepend this to some of the table/column names.
+    var repo = query.repo();
+
+    // Create the FROM clause. It is simply the list of tables that the user has selected.
+    // Each item in the list is a String of the form: "repo.table".
+    query.get_selected_tables().forEach(function(table) {
+      from_list.push(repo + "." + table);
+    });
+
+    // Create the SELECT clause.
+    // Iterate through every selected column of every selected table and add the column to the 
+    // select list (and write the aggregate if possible).
+    query.get_selected_tables().forEach(function(table) {
+      query.selected_columns(table).forEach(function(column) {
+        if (column.agg === undefined || column.agg === null || column.agg === "none") {
+          select_list.push(repo + "." + table + "." + column.name);
+        } else {
+          // When an aggregate "agg" on column "col" in table "table" and repo "repo" appears, mark
+          // "agg(repo.table.col) as agg_table_col".
+          select_list.push(column.agg + "(" + repo + "." + table + "." + column.name + ")" + 
+            " as " + column.agg + "_" + table + "_"
+            + column.name);
+        }
+      });
+    });
+
+    // Create the WHERE clause.
+    // Simply iterate through each filter and add it to the list.
+    query.get_filters().forEach(function(filter) {
+      where_list.push(filter.filter1 + " " + filter.op + " " + filter.filter2);
+    });
+
+    // Create the  GROUP BY clause.
+    query.grouping().forEach(function(group) {
+      var agg = group.column.agg;
+
+      // We can only add a group by if it's not the aggregate column.
+      if (agg === null || agg === undefined || agg === "none") {
+        group_list.push(repo + "." + group.string);
+      } 
+    });
+
+    // Create the ORDER BY clause.
+    query.sorts().forEach(function(sort) {
+      var agg = sort.column.agg;
+      if (agg === null || agg === undefined || agg === "none") {
+        order_list.push(repo + "." + sort.string);
+      } else {
+        order_list.push(agg + "_" + sort.table + "_" + sort.column.name);
+      }
+    });
+
+    // Set the query string.
+    if (select_list.length === 0) {
+      return "";
+    }
+    var query_string = "SELECT " + select_list.join(", ")
+        + " FROM " + from_list.join(", ");
+
+    // Set the where list.
+    if (where_list.length > 0) {
+      query_string +=  " WHERE " + where_list.join(" AND ");
+    }
+
+    // Set the group list.
+    if (group_list.length > 0) {
+      query_string += " GROUP BY " + group_list.join(", ")
+    }
+
+    // Set the order list.
+    if (order_list.length > 0) {
+      query_string += " ORDER BY " + order_list.join(", ")
+    }
+
+    // Remove leading and trailing spaces and then append semicolon.
+    query_string.trim();
+    query_string += ";";
+    return query_string;
+  };
+})();

From 2659213b8a1b865f5c6f7330c8dad8c114d00b97 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Tue, 23 May 2017 16:36:14 -0400
Subject: [PATCH 02/10] DataQ modal accessible from Security Policies page

---
 src/browser/templates/security-policies.html | 22 ++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/browser/templates/security-policies.html b/src/browser/templates/security-policies.html
index 050d0d3a..5486b298 100644
--- a/src/browser/templates/security-policies.html
+++ b/src/browser/templates/security-policies.html
@@ -23,6 +23,7 @@ <h4 class="inline-block">
     <div class="col-sm-2">
       <button class="btn btn-primary btn-xsm" id="btn-run" type="submit">Execute</button>
       <button class="btn btn-primary btn-xsm modal-upload-dialog" type="button" target-modal="#security-policy-modal" title="New Policy"> New </button>
+      <button class="btn btn-primary btn-xsm" id="btn-dataq" type="button">Query Builder</button>
     </div>
 
     </div>
@@ -228,4 +229,25 @@ <h4 class="modal-title" id="confirm-modal-title">Update Security Policy</h4>
 
 });
 </script>
+
+<script src="//cdnjs.cloudflare.com/ajax/libs/blueimp-md5/1.0.1/js/md5.js"></script>
+<!-- dataq -->
+<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.runtime.js"></script>
+<script src="/static/dataq/templates.js"></script>
+<script src="/static/dataq/js/dataq.min.js"></script>
+<!-- /dataq -->
+
+<script>
+  $(document).on("click", "#btn-dataq", function(e) {
+    e.preventDefault();
+    DataQ.DQ("{{repo}}", function(query) {
+      $("#txt-sql").val(query);
+    });
+    console.log('click');
+  });
+</script>
 {% endblock js %}
+
+{% block stylesheets %}
+<link rel="stylesheet" href="/static/dataq/css/style.css">
+{% endblock stylesheets %}

From 4ea3e182bed04205b93901728edfbbffaf386772 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Wed, 24 May 2017 23:45:29 -0400
Subject: [PATCH 03/10] Build CREATE POLICY string from DataQ.Policy object.

---
 .../js/dq-security-policy-builder.js          | 158 +++++++-----------
 1 file changed, 64 insertions(+), 94 deletions(-)

diff --git a/src/apps/dataq/client_src/js/dq-security-policy-builder.js b/src/apps/dataq/client_src/js/dq-security-policy-builder.js
index 95b80b95..4b136853 100644
--- a/src/apps/dataq/client_src/js/dq-security-policy-builder.js
+++ b/src/apps/dataq/client_src/js/dq-security-policy-builder.js
@@ -1,110 +1,80 @@
 /**
- * Logic for constructing a SQL query string from a DataQ.Query object.
- */
+* Logic for constructing a PostgreSQL CREATE POLICY command from a
+* DataQ.policy object.
+*/
 (function() {
   // If the global DataQ object does not exist, create it.
   window.DataQ = window.DataQ || {};
 
   /**
-   * Take a DataQ.Query object and generate a SQL query string from it.
+   * Take a DataQ.Policy object and generate a CREATE POLICY string from it.
+   * A CREATE POLICY command looks like:
    *
-   * @param query - The DataQ.Query object.
-   * @return A String representing the SQL query.
+   * CREATE POLICY name ON table_name
+   *    [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
+   *    [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]
+   *    [ USING ( using_expression ) ]
+   *    [ WITH CHECK ( check_expression ) ]
+   *
+   * see https://www.postgresql.org/docs/9.5/static/sql-createpolicy.html
+   *
+   * @param policy - The DataQ.policy object.
+   * @return A String representing the CREATE POLICY command.
    */
-  window.DataQ.build_query = function(query) {
-
-    // The list of columns to select.
-    var select_list = [];
-
-    // The list of tables to select from.
-    var from_list = [];
-
-    // The filters to apply.
-    var where_list = [];
-
-    // The grouping to perform.
-    var group_list = [];
-
-    // The sorting to apply.
-    var order_list = [];
-
-    // Get the current repo name - we'll need to prepend this to some of the table/column names.
-    var repo = query.repo();
-
-    // Create the FROM clause. It is simply the list of tables that the user has selected.
-    // Each item in the list is a String of the form: "repo.table".
-    query.get_selected_tables().forEach(function(table) {
-      from_list.push(repo + "." + table);
-    });
-
-    // Create the SELECT clause.
-    // Iterate through every selected column of every selected table and add the column to the 
-    // select list (and write the aggregate if possible).
-    query.get_selected_tables().forEach(function(table) {
-      query.selected_columns(table).forEach(function(column) {
-        if (column.agg === undefined || column.agg === null || column.agg === "none") {
-          select_list.push(repo + "." + table + "." + column.name);
-        } else {
-          // When an aggregate "agg" on column "col" in table "table" and repo "repo" appears, mark
-          // "agg(repo.table.col) as agg_table_col".
-          select_list.push(column.agg + "(" + repo + "." + table + "." + column.name + ")" + 
-            " as " + column.agg + "_" + table + "_"
-            + column.name);
-        }
-      });
-    });
-
-    // Create the WHERE clause.
-    // Simply iterate through each filter and add it to the list.
-    query.get_filters().forEach(function(filter) {
-      where_list.push(filter.filter1 + " " + filter.op + " " + filter.filter2);
-    });
-
-    // Create the  GROUP BY clause.
-    query.grouping().forEach(function(group) {
-      var agg = group.column.agg;
-
-      // We can only add a group by if it's not the aggregate column.
-      if (agg === null || agg === undefined || agg === "none") {
-        group_list.push(repo + "." + group.string);
-      } 
-    });
-
-    // Create the ORDER BY clause.
-    query.sorts().forEach(function(sort) {
-      var agg = sort.column.agg;
-      if (agg === null || agg === undefined || agg === "none") {
-        order_list.push(repo + "." + sort.string);
-      } else {
-        order_list.push(agg + "_" + sort.table + "_" + sort.column.name);
-      }
-    });
-
-    // Set the query string.
-    if (select_list.length === 0) {
-      return "";
-    }
-    var query_string = "SELECT " + select_list.join(", ")
-        + " FROM " + from_list.join(", ");
+  window.DataQ.build_policy = function(policy) {
 
-    // Set the where list.
-    if (where_list.length > 0) {
-      query_string +=  " WHERE " + where_list.join(" AND ");
-    }
+    // Name of policy to be created.
+    var policy_name = policy.name();
 
-    // Set the group list.
-    if (group_list.length > 0) {
-      query_string += " GROUP BY " + group_list.join(", ")
-    }
+    // Name of table to which the policy applies.
+    var table_name = policy.repo() + "." + policy.table_name();
+
+    // Command to which the policy applies.
+    var command = policy.command();
+
+    // List of roles to which the policy applies.
+    var role_list = policy.roles();
+
+    // List of users to which the policy applies.
+    // Each element must be one of { PUBLIC | CURRENT_USER | SESSION_USER }
+    var user_list = policy.users();
+
+    // SQL conditional expression to control row visibility.
+    // Rows for which the expression returns true will be visible.
+    var using_expr = policy.using_expression();
 
-    // Set the order list.
-    if (order_list.length > 0) {
-      query_string += " ORDER BY " + order_list.join(", ")
+    // SQL conditional expression to control INSERT and UPDATE privileges.
+    // Only rows for which the expression evaluates to true will be allowed.
+    var check_expr = policy.check_expression();
+
+    /* Build policy string */
+    var policy_string = "CREATE POLICY " + policy_name;
+
+    // ON clause
+    policy_string += " ON " + table_name;
+
+    // FOR clause
+    policy_string += " FOR " + command;
+
+    // TO clause
+    policy_string += " TO " + role_list.join(", ");
+    policy_string += ", " + user_list.join(", ");
+
+    // USING clause
+    policy_string += " USING " + using_expr;
+
+    // WITH CHECK clause
+    if (command !== "SELECT") {
+      // A SELECT policy cannot have a WITH CHECK expression, as it only applies
+      // in cases where records are being retrieved from the relation.
+      policy_string += " WITH CHECK " + check_expr;
     }
 
     // Remove leading and trailing spaces and then append semicolon.
-    query_string.trim();
-    query_string += ";";
-    return query_string;
+    policy_string.trim();
+    policy_string += ";";
+
+    return policy_string;
+
   };
 })();

From a85aea54462a3ed39078d9882b3fe78be29a6f0b Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Wed, 24 May 2017 23:47:46 -0400
Subject: [PATCH 04/10] Renamed dq-security-policy-builder.js ->
 dq-rls-policy-builder.js

---
 .../{dq-security-policy-builder.js => dq-rls-policy-builder.js}   | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/apps/dataq/client_src/js/{dq-security-policy-builder.js => dq-rls-policy-builder.js} (100%)

diff --git a/src/apps/dataq/client_src/js/dq-security-policy-builder.js b/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
similarity index 100%
rename from src/apps/dataq/client_src/js/dq-security-policy-builder.js
rename to src/apps/dataq/client_src/js/dq-rls-policy-builder.js

From 9e7796cb178a2dfe1898c375200618cee0c84bea Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Thu, 25 May 2017 01:43:30 -0400
Subject: [PATCH 05/10] Remove "user_list"

---
 src/apps/dataq/client_src/js/dq-rls-policy-builder.js | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/apps/dataq/client_src/js/dq-rls-policy-builder.js b/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
index 4b136853..61010c1c 100644
--- a/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
+++ b/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
@@ -27,7 +27,7 @@
     var policy_name = policy.name();
 
     // Name of table to which the policy applies.
-    var table_name = policy.repo() + "." + policy.table_name();
+    var table_name = policy.repo() + "." + policy.name();
 
     // Command to which the policy applies.
     var command = policy.command();
@@ -35,10 +35,6 @@
     // List of roles to which the policy applies.
     var role_list = policy.roles();
 
-    // List of users to which the policy applies.
-    // Each element must be one of { PUBLIC | CURRENT_USER | SESSION_USER }
-    var user_list = policy.users();
-
     // SQL conditional expression to control row visibility.
     // Rows for which the expression returns true will be visible.
     var using_expr = policy.using_expression();
@@ -58,7 +54,6 @@
 
     // TO clause
     policy_string += " TO " + role_list.join(", ");
-    policy_string += ", " + user_list.join(", ");
 
     // USING clause
     policy_string += " USING " + using_expr;

From 0ef4d7dbf12f2a126babf1977f17727638a89978 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Thu, 25 May 2017 01:44:01 -0400
Subject: [PATCH 06/10] Define DataQ.Policy object.

---
 src/apps/dataq/client_src/js/dq-rls-policy.js | 132 ++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100644 src/apps/dataq/client_src/js/dq-rls-policy.js

diff --git a/src/apps/dataq/client_src/js/dq-rls-policy.js b/src/apps/dataq/client_src/js/dq-rls-policy.js
new file mode 100644
index 00000000..249c5615
--- /dev/null
+++ b/src/apps/dataq/client_src/js/dq-rls-policy.js
@@ -0,0 +1,132 @@
+/**
+* The object for building a row-level security policy.
+*/
+(function() {
+  // If the DataQ object doesn't exist, create it.
+  window.DataQ = window.DataQ || {};
+
+  DataQ.Policy = function() {
+
+    // Create the object and initialize its contents.
+    var that = {};
+    that._repo_name = null;
+    that._name = null;
+    that._table = null;
+    that._command = null;
+    that._roles = [];
+    that._using_expr = null;
+    that._check_expr = null;
+
+    /**
+     * Get or set the repo name.
+     *
+     * @param repo_name - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The name of the repo.
+     */
+    that.repo = function(repo_name) {
+      if (repo_name !== undefined) {
+        that._repo_name = repo_name;
+      }
+      return that._repo_name;
+    };
+
+    /**
+     * Get or set the name of the policy.
+     *
+     * @param policy_name - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The name of the policy.
+     */
+    that.name = function(policy_name) {
+      if (policy_name !== undefined) {
+        that._name = policy_name;
+      }
+      return that._name;
+    };
+
+    /**
+     * Get or set the name of the table to which this policy will apply.
+     *
+     * @param table_name - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The name of the table.
+     */
+    that.table = function(table_name) {
+      if (table_name !== undefined) {
+        that._table = table_name;
+      }
+      return that._table;
+    };
+
+    /**
+     * Get or set the command { ALL | SELECT | INSERT| UPDATE | DELETE } to which
+     * this policy will apply.
+     *
+     * @param cmd - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The name of the command.
+     */
+    that.command = function(cmd) {
+      if (cmd !== undefined) {
+        that._command = cmd;
+      }
+      return that._command;
+    };
+
+    /**
+     * Get or set the Roles to which this policy will apply.
+     *
+     * @param role_list - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The list of Roles.
+     */
+    that.roles = function(role_list) {
+      if (role_list !== undefined) {
+        if (!(role_list instanceof Array)) {
+          role_list = [role_list]
+        }
+        that._roles = role_list;
+      }
+      return that._roles;
+    };
+
+    /**
+     * Get or set the policy's using_expression.
+     *
+     * @param expr - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The full using_expression.
+     */
+    that.using_expression = function(expr) {
+      if (expr !== undefined) {
+        that._using_expr = expr;
+      }
+      return that._using_expr;
+    };
+
+    /**
+     * Get or set the policy's check_expression.
+     *
+     * @param expr - If this argument is omitted (or undefined), this function acts as a
+     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *
+     * @return The full check_expression.
+     */
+    that.check_expression = function(expr) {
+      if (expr !== undefined) {
+        that._check_expr = expr;
+      }
+      return that._check_expr;
+    };
+
+    return that;
+
+  };
+})();

From 2dbaf59b0551cdedffcf50d2ea01009478ff2a3d Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Thu, 25 May 2017 20:22:41 -0400
Subject: [PATCH 07/10] Basic CREATE POLICY builder modal accessible on
 security policies page.

---
 src/apps/dataq/client_src/js/dataq.js         | 28 +++++++++-
 .../templates/dataq-container-rls-policy.hbs  | 56 +++++++++++++++++++
 src/browser/templates/security-policies.html  | 11 ++--
 3 files changed, 88 insertions(+), 7 deletions(-)
 create mode 100644 src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs

diff --git a/src/apps/dataq/client_src/js/dataq.js b/src/apps/dataq/client_src/js/dataq.js
index 1a8f640f..65d36d96 100644
--- a/src/apps/dataq/client_src/js/dataq.js
+++ b/src/apps/dataq/client_src/js/dataq.js
@@ -2,7 +2,7 @@
  * Defines the DataQ.DQ object, which is what the user of the library will interact with.
  *
  * Simply call DataQ.DQ(repo_name, callback) and DataQ will launch. After the user builds a query,
- * the callback is executed as callback(query), where query is a String representing the SQL query 
+ * the callback is executed as callback(query), where query is a String representing the SQL query
  * or null if the query was not built successfully.
  */
 (function() {
@@ -40,6 +40,30 @@
     })
   };
 
+  /**
+   * @param repo_name - The name of the repo that DataQ should work on.
+   * @param cb - The callback to trigger when the query is built.
+   */
+  DataQ.DQ_rls_policy = function(repo_name, cb) {
+    // Set the callback.
+    callback = cb;
+
+    // Add the container to the page.
+    var container = $(DataQ.templates["dataq-container-rls-policy"]());
+    $('body').append(container);
+
+    // Create the policy object and set the repo name.
+    policy = DataQ.Policy();
+    policy.repo(repo_name);
+
+    // Handle DataQ close when clicking backdrop.
+    $(".dq-black-background").click(function() {
+      $(".dq-black-background").remove();
+      $(".dataq").remove();
+      callback(null);
+    });
+  };
+
   /**
    * Update the UI to reflect the latest query.
    */
@@ -107,7 +131,7 @@
     query.sorts().forEach(function(sort) {
       sort_strings.push(sort.string);
     });
-    
+
     // Display the sorts.
     if (sort_strings.length > 0) {
       $(".dq-sorting-text").html(sort_strings.join(", "));
diff --git a/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
new file mode 100644
index 00000000..51883934
--- /dev/null
+++ b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
@@ -0,0 +1,56 @@
+<!-- HTML file containing an extension of the overall DataQ interface.
+     This interface is for building DataQ.Policy objects. -->
+
+<!-- Create a translucent black background -->
+<div class="dq-black-background"></div>
+
+<!-- The DataQ container -->
+<div class="dataq container-fluid">
+
+   <h4>
+   DataQ-Policy-Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface.
+   </h4>
+
+<div class="panel-group">
+   <div class="panel panel-default">
+      <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseOne">
+        <h4 class="panel-title">
+          Policy Name
+        </h4>
+   </div>
+   <div id="collapseOne" class="panel-collapse collapse in">
+     <div class="panel-body">
+
+        <input id="policyName" type="text" class="form-control" placeholder="Policy Name" />
+
+     </div>
+   </div>
+
+   <div class="panel panel-default">
+      <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseTwo">
+        <h4 class="panel-title">
+          Allowed commands
+        </h4>
+   </div>
+   <div id="collapseTwo" class="panel-collapse collapse in">
+     <div class="panel-body">
+       <div class="dropdown">
+          <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Choose command
+          <span class="caret"></span></button>
+          <ul class="dropdown-menu">
+           <li><a href="#">SELECT</a></li>
+           <li><a href="#">INSERT</a></li>
+           <li><a href="#">UPDATE</a></li>
+           <li><a href="#">DELETE</a></li>
+           <li><a href="#">ALL</a></li>
+          </ul>
+       </div>
+     </div>
+   </div>
+
+   <!-- Button to run query -->
+   <div class="row">
+   <button class="btn btn-primary col-xs-3 dq-btn-run-query col-xs-offset-3">Query</button>
+   <button class="btn btn-default col-xs-3 dq-btn-cancel-query">Cancel</button>
+   </div>
+</div>
diff --git a/src/browser/templates/security-policies.html b/src/browser/templates/security-policies.html
index 5486b298..732131ee 100644
--- a/src/browser/templates/security-policies.html
+++ b/src/browser/templates/security-policies.html
@@ -22,8 +22,8 @@ <h4 class="inline-block">
 
     <div class="col-sm-2">
       <button class="btn btn-primary btn-xsm" id="btn-run" type="submit">Execute</button>
-      <button class="btn btn-primary btn-xsm modal-upload-dialog" type="button" target-modal="#security-policy-modal" title="New Policy"> New </button>
-      <button class="btn btn-primary btn-xsm" id="btn-dataq" type="button">Query Builder</button>
+      <button class="btn btn-primary btn-xsm modal-upload-dialog" type="button" target-modal="#security-policy-modal" title="New Policy"> New Table-Level Policy </button>
+      <button class="btn btn-primary btn-xsm" id="btn-dataq-rls-policy" type="button">Row-Level Policy Builder</button>
     </div>
 
     </div>
@@ -238,12 +238,13 @@ <h4 class="modal-title" id="confirm-modal-title">Update Security Policy</h4>
 <!-- /dataq -->
 
 <script>
-  $(document).on("click", "#btn-dataq", function(e) {
+  $(document).on("click", "#btn-dataq-rls-policy", function(e) {
     e.preventDefault();
-    DataQ.DQ("{{repo}}", function(query) {
+    console.log(DataQ);
+    // DataQ.DQ("{{repo}}", function(query) {
+    DataQ.DQ_rls_policy("{{repo}}", function(query) {
       $("#txt-sql").val(query);
     });
-    console.log('click');
   });
 </script>
 {% endblock js %}

From 69214b42f35abc4e2c2ca797e397f32190e85572 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Sat, 27 May 2017 13:22:42 -0400
Subject: [PATCH 08/10] Basic front end done

---
 src/apps/dataq/client_src/js/dataq.js         |  23 ++-
 .../templates/dataq-container-rls-policy.hbs  | 154 +++++++++++++++++-
 src/browser/static/dataq/css/style.css        |   2 +-
 src/browser/static/dataq/js/dataq.min.js      |   2 +-
 src/browser/static/dataq/js/dataq.min.js.map  |   2 +-
 src/browser/static/dataq/templates.js         |  59 +++++++
 .../static/docs/html/_static/custom.css       |   1 +
 src/browser/templates/security-policies.html  |   2 -
 8 files changed, 229 insertions(+), 16 deletions(-)
 create mode 100644 src/browser/static/docs/html/_static/custom.css

diff --git a/src/apps/dataq/client_src/js/dataq.js b/src/apps/dataq/client_src/js/dataq.js
index 65d36d96..1465b3f4 100644
--- a/src/apps/dataq/client_src/js/dataq.js
+++ b/src/apps/dataq/client_src/js/dataq.js
@@ -9,11 +9,15 @@
   // Create the global DataQ object if it doesn't exist.
   window.DataQ = window.DataQ || {};
 
-  // The DataQ.Query that is being built.
+  // The DataQ.Query that may be built.
   query = null;
 
-  // The callback to execute after the query is built. It is executed as cb(query) where query
-  // is a String representing the SQL query or null if the query was not built.
+  // The DataQ.Policy object that may be built.
+  policy = null;
+
+  // The callback to execute after the { query | policy } is built. It is executed as
+  // { cb(query) | cb(policy) } where { query | policy } is a String representing the
+  // SQL { query | policy } or null if the { query | policy } was not built.
   var callback;
 
   /**
@@ -197,4 +201,17 @@
     $(".dataq").remove();
     callback(DataQ.build_query(query));
   });
+
+  // Handle DataQ run policy.
+  $(document).on("click", ".dq-btn-run-policy", function() {
+    // Build policy object
+    
+
+    // Close DataQ
+    $(".dq-black-background").remove();
+    $(".dataq").remove();
+
+    // Build policy string
+    callback(DataQ.build_policy(policy));
+  });
 })();
diff --git a/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
index 51883934..352ae66f 100644
--- a/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
+++ b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
@@ -8,7 +8,7 @@
 <div class="dataq container-fluid">
 
    <h4>
-   DataQ-Policy-Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface.
+   DataQ Policy Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface.
    </h4>
 
 <div class="panel-group">
@@ -17,24 +17,24 @@
         <h4 class="panel-title">
           Policy Name
         </h4>
+      </div>
    </div>
    <div id="collapseOne" class="panel-collapse collapse in">
      <div class="panel-body">
-
-        <input id="policyName" type="text" class="form-control" placeholder="Policy Name" />
-
+        <input id="dq-policy-name" type="text" class="form-control" placeholder="Policy Name" />
      </div>
    </div>
 
    <div class="panel panel-default">
       <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseTwo">
         <h4 class="panel-title">
-          Allowed commands
+          Allowed Commands
         </h4>
+      </div>
    </div>
    <div id="collapseTwo" class="panel-collapse collapse in">
      <div class="panel-body">
-       <div class="dropdown">
+       <div class="dropdown" id="dq-policy-commands-dropdown">
           <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Choose command
           <span class="caret"></span></button>
           <ul class="dropdown-menu">
@@ -48,9 +48,147 @@
      </div>
    </div>
 
+   <div class="panel panel-default">
+      <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseThree">
+         <h4 class="panel-title">
+            Select role(s) this policy will apply to:
+         </h4>
+      </div>
+   </div>
+   <div id="collapseThree" class="panel-collapse collapse in">
+     <div class="panel-body">
+       <input id="dq-policy-role-list" type="text" class="form-control" placeholder="Comma separated role names. e.g. CURRENT_USER, admin, myGroupRole" />
+     </div>
+   </div>
+
+   <div class="panel panel-default">
+      <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseFour">
+        <h4 class="panel-title">
+          USING expression: Rows for which this expression returns true will be visible.
+        </h4>
+      </div>
+   </div>
+   <div id="collapseFour" class="panel-collapse collapse in">
+     <div class="panel-body">
+        <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->
+   <div class="input-group col-xs-4">
+      <!-- Text box for entering the filter text -->
+      <input type="text" class="form-control dq-filter-1-text" placeholder="table1.col1 * 3">
+      <!-- Dropdown for selecting a column to add to filter -->
+      <div class="input-group-btn">
+        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+        <ul class="dropdown-menu dropdown-menu-right" role="menu">
+          {{#each columns}}
+          <li><a href="#" class="dq-filter-1-link" data-columnname="{{this.column_name}}" data-reponame="{{../repo}}" data-tablename="{{this.table_name}}">{{this.full_name}}</a></li>
+          {{/each}}
+        </ul>
+      </div>
+   </div> <!-- end filter1 -->
+
+   <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->
+   <div class="input-group col-xs-3">
+      <!-- Text box for entering the operation text -->
+      <input type="text" class="form-control dq-filter-op-text" value="=">
+      <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->
+      <div class="input-group-btn">
+        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+        <ul class="dropdown-menu dropdown-menu-right" role="menu">
+          <li><a href="#" class="dq-filter-op-link">&lt</a></li>
+          <li><a href="#" class="dq-filter-op-link">&lt=</a></li>
+          <li><a href="#" class="dq-filter-op-link">=</a></li>
+          <li><a href="#" class="dq-filter-op-link">&gt</a></li>
+          <li><a href="#" class="dq-filter-op-link">&gt=</a></li>
+          <li><a href="#" class="dq-filter-op-link">LIKE</a></li>
+          <li><a href="#" class="dq-filter-op-link">IN</a></li>
+          <li><a href="#" class="dq-filter-op-link">NOT IN</a></li>
+        </ul>
+      </div>
+   </div> <!-- end filterop -->
+
+   <!-- The second dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->
+   <div class="input-group col-xs-4">
+      <!-- Text box for entering the filter text -->
+      <input type="text" class="form-control dq-filter-2-text" placeholder="table2.col2+3">
+      <!-- Dropdown for selecting a column to add to filter -->
+      <div class="input-group-btn">
+        <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+        <ul class="dropdown-menu dropdown-menu-right" role="menu">
+          {{#each columns}}
+          <li><a href="#" class="dq-filter-2-link" data-columnname="{{this.column_name}}" data-reponame="{{../repo}}" data-tablename="{{this.table_name}}">{{this.full_name}}</a></li>
+          {{/each}}
+        </ul>
+      </div>
+   </div> <!-- end filter2 -->
+</div>
+</div>
+
+   <div class="panel panel-default">
+      <div class="panel-heading dq-panel" data-toggle="collapse" href="#collapseFive">
+        <h4 class="panel-title">
+          WITH CHECK expression: New rows for which this expression returns true will be allowed.
+        </h4>
+   </div>
+   <div id="collapseFive" class="panel-collapse collapse in">
+     <div class="panel-body">
+        <div class="panel-body">
+           <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->
+      <div class="input-group col-xs-4">
+         <!-- Text box for entering the filter text -->
+         <input type="text" class="form-control dq-filter-1-text" placeholder="table1.col1 * 3">
+         <!-- Dropdown for selecting a column to add to filter -->
+         <div class="input-group-btn">
+           <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+           <ul class="dropdown-menu dropdown-menu-right" role="menu">
+             {{#each columns}}
+             <li><a href="#" class="dq-filter-1-link" data-columnname="{{this.column_name}}" data-reponame="{{../repo}}" data-tablename="{{this.table_name}}">{{this.full_name}}</a></li>
+             {{/each}}
+           </ul>
+         </div>
+      </div> <!-- end filter1 -->
+
+      <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->
+      <div class="input-group col-xs-3">
+         <!-- Text box for entering the operation text -->
+         <input type="text" class="form-control dq-filter-op-text" value="=">
+         <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->
+         <div class="input-group-btn">
+           <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+           <ul class="dropdown-menu dropdown-menu-right" role="menu">
+             <li><a href="#" class="dq-filter-op-link">&lt</a></li>
+             <li><a href="#" class="dq-filter-op-link">&lt=</a></li>
+             <li><a href="#" class="dq-filter-op-link">=</a></li>
+             <li><a href="#" class="dq-filter-op-link">&gt</a></li>
+             <li><a href="#" class="dq-filter-op-link">&gt=</a></li>
+             <li><a href="#" class="dq-filter-op-link">LIKE</a></li>
+             <li><a href="#" class="dq-filter-op-link">IN</a></li>
+             <li><a href="#" class="dq-filter-op-link">NOT IN</a></li>
+           </ul>
+         </div>
+      </div> <!-- end filterop -->
+
+      <!-- The first dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->
+      <div class="input-group col-xs-4">
+         <!-- Text box for entering the filter text -->
+         <input type="text" class="form-control dq-filter-2-text" placeholder="table2.col2+3">
+         <!-- Dropdown for selecting a column to add to filter -->
+         <div class="input-group-btn">
+           <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
+           <ul class="dropdown-menu dropdown-menu-right" role="menu">
+             {{#each columns}}
+             <li><a href="#" class="dq-filter-2-link" data-columnname="{{this.column_name}}" data-reponame="{{../repo}}" data-tablename="{{this.table_name}}">{{this.full_name}}</a></li>
+             {{/each}}
+           </ul>
+         </div>
+      </div> <!-- end filter2 -->
+   </div>
+
+     </div>
+   </div>
+</div>
+
    <!-- Button to run query -->
    <div class="row">
-   <button class="btn btn-primary col-xs-3 dq-btn-run-query col-xs-offset-3">Query</button>
-   <button class="btn btn-default col-xs-3 dq-btn-cancel-query">Cancel</button>
+   <button class="btn btn-primary col-xs-3 dq-btn-create-policy col-xs-offset-3">Create Policy</button>
+   <button class="btn btn-default col-xs-3 dq-btn-cancel-create-policy">Cancel</button>
    </div>
 </div>
diff --git a/src/browser/static/dataq/css/style.css b/src/browser/static/dataq/css/style.css
index f22c496b..e0acd6d5 100644
--- a/src/browser/static/dataq/css/style.css
+++ b/src/browser/static/dataq/css/style.css
@@ -122,4 +122,4 @@
 .dq-grouping-modal-list {
   margin-top: 20px;
   list-style: none;
-}
\ No newline at end of file
+}
diff --git a/src/browser/static/dataq/js/dataq.min.js b/src/browser/static/dataq/js/dataq.min.js
index 01ce4c7e..65b33526 100644
--- a/src/browser/static/dataq/js/dataq.min.js
+++ b/src/browser/static/dataq/js/dataq.min.js
@@ -1,2 +1,2 @@
-!function(){window.DataQ=window.DataQ||{},query=null;var t;DataQ.DQ=function(o,e){t=e;var n=$(DataQ.templates["dataq-container"]());$("body").append(n),query=DataQ.Query(),query.repo(o),$(".dq-black-background").click(function(){$(".dq-black-background").remove(),$(".dataq").remove(),t(null)})};var o=function(){$(".dq-selected-tables").html(""),query.get_selected_tables().forEach(function(t){var o=query.selected_columns(t);if(null!==o){var e=[];o.forEach(function(t){"none"===t.agg?e.push(t.name):e.push(t.agg+"("+t.name+")")});var n=DataQ.templates["dq-selected-table"]({table_name:t,column_list:e.join(", ")});$(".dq-selected-tables").append(n)}}),$(".dq-filter-list").html(""),query.get_filters().forEach(function(t){$(".dq-filter-list").append(DataQ.templates["dq-filter-list-item"]({filter:t}))});var t=[];query.grouping().forEach(function(o){t.push(o.string)}),t.length>0?$(".dq-grouping-text").html(t.join(", ")):$(".dq-grouping-text").html("No Grouping...");var o=[];query.sorts().forEach(function(t){o.push(t.string)}),o.length>0?$(".dq-sorting-text").html(o.join(", ")):$(".dq-sorting-text").html("No Sorting...")};$(document).on("click",".dq-btn-add-table",function(){DataQ.TableModal(query,null,o)}),$(document).on("click",".dq-btn-delete-table",function(){var t=$(this).data("tablename");query.operated_column()&&query.operated_column().split(".")[0]===t&&query.operated_column(null),query.selected_columns(t,null),query.update_grouping(),o()}),$(document).on("click",".dq-btn-edit-table",function(){DataQ.TableModal(query,$(this).data("tablename"),o)}),$(document).on("click",".dq-btn-add-filter",function(){DataQ.FilterModal(query,o)}),$(document).on("click",".dq-btn-delete-filter",function(){var t=$(this).parent().data("code");query.delete_filter(t),o()}),$(document).on("click",".dq-btn-edit-grouping",function(){DataQ.GroupingModal(query,o)}),$(document).on("click",".dq-btn-edit-sorting",function(){DataQ.SortModal(query,o)}),$(document).on("click",".dq-btn-cancel-query",function(){$(".dq-black-background").remove(),$(".dataq").remove(),t(null)}),$(document).on("click",".dq-btn-run-query",function(){$(".dq-black-background").remove(),$(".dataq").remove(),t(DataQ.build_query(query))})}(),function(){window.DataQ=window.DataQ||{},DataQ.API={},DataQ.API.get_repos=function(t){$.get("/apps/dataq/api/",t)},DataQ.API.get_tables=function(t,o){$.get("/apps/dataq/api/"+t+"/",o)},DataQ.API.get_schema=function(t,o,e){$.get("/apps/dataq/api/"+t+"/"+o+"/",e)}}(),function(){window.DataQ=window.DataQ||{};var t,o;DataQ.FilterModal=function(e,n){t=n,o=e;var a=$("#dq-filter-modal");if(0===a.length){var r=[];o.get_selected_tables().forEach(function(t){o.schema(t).forEach(function(o){r.push({column_name:o[0],table_name:t,full_name:t+"."+o[0]})})});var l=DataQ.templates["dq-filter-modal"]({columns:r,repo:o.repo()});$("body").append(l)}$("#dq-filter-modal").modal({keyboard:!1}),$(".modal-backdrop").click(function(){t(),$("#dq-filter-modal").remove()})},$(document).on("click",".dq-filter-quit",function(){t(),$("#dq-filter-modal").remove(),$(".modal-backdrop").remove()}),$(document).on("click",".dq-filter-done",function(){var e=$(".dq-filter-1-text").val(),n=$(".dq-filter-2-text").val(),a=$(".dq-filter-op-text").val();e.length>0&&n.length>0&&a.length>0?(o.add_filter(e,a,n),t(),$("#dq-filter-modal").modal("hide"),$("#dq-filter-modal").remove(),$(".modal-backdrop").remove()):alert("You need to fill out the three text boxes")}),$(document).on("click",".dq-filter-1-link",function(){$(".dq-filter-1-text").val($(this).html())}),$(document).on("click",".dq-filter-2-link",function(){$(".dq-filter-2-text").val($(this).html())}),$(document).on("click",".dq-filter-op-link",function(){var t=$(this).html().replace("&gt;",">").replace("&lt;","<");$(".dq-filter-op-text").val(t)})}(),function(){window.DataQ=window.DataQ||{};var t,o;DataQ.GroupingModal=function(e,n){o=e,t=n;var a=$("#dq-grouping-modal");if(0===a.length){var r=DataQ.templates["dq-grouping-modal"]({columns:o.grouping()});$("body").append(r)}$("#dq-grouping-modal").modal({keyboard:!1}),$("#dq-grouping-modal").on("shown.bs.modal",function(){$(".dq-grouping-modal-list").sortable({forcePlaceholderSize:!0})}),$(".modal-backdrop").click(function(){t(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()})},$(document).on("click","#dq-grouping-modal-quit-btn",function(){t(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()}),$(document).on("click","#dq-grouping-modal-done-btn",function(){var e=[];$(".dq-grouping-list-item").each(function(){var t,n=$(this),a=n.data("string");o.grouping();o.grouping().forEach(function(o){o.string===a&&(t=o)}),e.push(t)}),o.grouping(e),t(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()})}(),function(){window.DataQ=window.DataQ||{};for(var t=["bigint","int8","bigserial","serial8","double precision","float8","integer","int","int4","real","float4","smallint","int2","serial","serial4"],o={},e=0;e<t;e++)o[t[e]]=!0;var n={none:"max",max:"min",min:"sum",sum:"count",count:"avg",avg:"none"},a={none:"count",count:"none"};DataQ.next_aggregate=function(t,e){return null===e&&(e="none"),o[t]?n[e]:a[e]}}(),function(){window.DataQ=window.DataQ||{},window.DataQ.build_query=function(t){var o=[],e=[],n=[],a=[],r=[],l=t.repo();if(t.get_selected_tables().forEach(function(t){e.push(l+"."+t)}),t.get_selected_tables().forEach(function(e){t.selected_columns(e).forEach(function(t){void 0===t.agg||null===t.agg||"none"===t.agg?o.push(l+"."+e+"."+t.name):o.push(t.agg+"("+l+"."+e+"."+t.name+") as "+t.agg+"_"+e+"_"+t.name)})}),t.get_filters().forEach(function(t){n.push(t.filter1+" "+t.op+" "+t.filter2)}),t.grouping().forEach(function(t){var o=t.column.agg;null!==o&&void 0!==o&&"none"!==o||a.push(l+"."+t.string)}),t.sorts().forEach(function(t){var o=t.column.agg;null===o||void 0===o||"none"===o?r.push(l+"."+t.string):r.push(o+"_"+t.table+"_"+t.column.name)}),0===o.length)return"";var d="SELECT "+o.join(", ")+" FROM "+e.join(", ");return n.length>0&&(d+=" WHERE "+n.join(" AND ")),a.length>0&&(d+=" GROUP BY "+a.join(", ")),r.length>0&&(d+=" ORDER BY "+r.join(", ")),d.trim(),d+=";"}}(),function(){window.DataQ=window.DataQ||{},DataQ.Query=function(){var t={};return t._schema_for_table_name={},t._repo_name=null,t._operated_column=null,t._selected_columns_for_table={},t._filter_for_code={},t._grouping=[],t._sorts={},t.schema=function(o,e){return void 0!==e&&(t._schema_for_table_name[o]=e),t._schema_for_table_name[o]},t.repo=function(o){return void 0!==o&&(t._repo_name=o),t._repo_name},t.operated_column=function(o){return void 0!==o&&(t._operated_column=o),t._operated_column},t.update_grouping=function(){t._grouping=[];var o=!1;for(var e in t._selected_columns_for_table)t._selected_columns_for_table[e]&&t._selected_columns_for_table[e].forEach(function(n){"none"===n.agg?t._grouping.push({string:e+"."+n.name,table:e,column:n}):o=!0});o||(t._grouping=[])},t.selected_columns=function(o,e){return void 0!==e&&(t._selected_columns_for_table[o]=e),t._selected_columns_for_table[o]},t.get_selected_tables=function(){var o=[];for(var e in t._selected_columns_for_table)o.push(e);return o},t.add_filter=function(o,e,n){var a=o+" "+e+" "+n,r=md5((new Date).getTime()+a);return t._filter_for_code[r]={filter1:o,op:e,filter2:n,filter_string:a},t._filter_for_code[r]},t.delete_filter=function(o){t._filter_for_code[o]=void 0},t.get_filters=function(){var o=[];for(var e in t._filter_for_code){var n=t._filter_for_code[e];n&&o.push({code:e,filter1:n.filter1,op:n.op,filter2:n.filter2,filter_string:n.filter_string})}return o},t.grouping=function(o){return void 0!==o&&(t._grouping=o),t._grouping},t.add_sort=function(o){t._sorts[o.string]=o},t.delete_sort=function(o){t._sorts[o]=void 0},t.sorts=function(){var o=[];for(var e in t._sorts)void 0!==t._sorts[e]&&o.push(t._sorts[e]);return o},t}}(),function(){window.DataQ=window.DataQ||{};var t,o;DataQ.SortModal=function(e,a){o=e,t=a;var r=$("#dq-sort-modal");if(0===r.length){var l=DataQ.templates["dq-sort-modal"]();$("body").append(l)}$("#dq-sort-modal").modal({keyboard:!1}),$("#dq-sort-modal").on("shown.bs.modal",function(){n()}),$(".modal-backdrop").click(function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),t()})},$(document).on("click",".dq-sort-modal-dropdown-btn",function(){var t=$(".dq-sort-modal-dropdown");t.html(""),e().forEach(function(o){t.append(DataQ.templates["dq-sort-dropdown-li"]({item:o}))})});var e=function(){var t={};o.sorts().forEach(function(o){t[o.string]=!0});var e=[];return o.get_selected_tables().forEach(function(n){o.selected_columns(n).forEach(function(o){var a=n+"."+o.name;"none"!==o.agg&&(a=o.agg+"("+n+"."+o.name+")"),t[a]||e.push({string:a,table:n,column:o})})}),e},n=function(){var t=$(".dq-sort-item-list");t.html(""),o.sorts().forEach(function(o){var e=DataQ.templates["dq-sort-list-item"]({item:o});t.append(e)})};$(document).on("click",".dq-sort-modal-quit",function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),t()}),$(document).on("click",".dq-sort-link",function(){var t=$(this),e=t.data("columnname"),a=t.data("columntype"),r=t.data("aggregate"),l=t.data("table"),d=t.data("string");void 0!==r&&null!==r||(r="none");var i={column:{name:e,type:a,agg:r},string:d,table:l};o.add_sort(i),n()}),$(document).on("click",".dq-sort-modal-done-btn",function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),t()}),$(document).on("click",".dq-sort-delete-btn",function(){var t=$(this).parent().parent(),e=t.data("string");o.delete_sort(e),n()})}(),function(){window.DataQ=window.DataQ||{};var t,o,e;DataQ.TableModal=function(a,r,l){o=r,t=l,e=a;var d=$("#dq-table-modal");if(0===d.length){var i=DataQ.templates["dq-table-modal"]({table_name:o});$("body").append(i)}$("#dq-table-modal").modal({keyboard:!1}),$(".dq-modal-done-btn").hide(),$("#dq-table-modal").on("shown.bs.modal",function(){o&&n(o)}),$(".modal-backdrop").click(function(){$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),t()})},$(document).on("click",".dq-modal-quit",function(){$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),t()}),$(document).on("click",".dq-modal-dropdown-btn",function(){var t=$(".dq-modal-dropdown");t.html(""),DataQ.API.get_tables(e.repo(),function(o){o.tables.forEach(function(o){var e=DataQ.templates["dq-modal-dropdown-item"]({item_name:o});t.append(e)})})}),$(document).on("click",".dq-modal-dropdown-link",function(){var t=$(this).data("item_name");o=t,$(".dq-modal-table-dropdown-text").text(o),n()});var n=function(){DataQ.API.get_schema(e.repo(),o,function(t){$(".dq-modal-done-btn").show(),e.schema(o,t.schema).sort(function(t,o){return t[0]>o[0]});var n=DataQ.templates["dq-modal-columns"]({columns:e.schema(o)});$(".dq-column-list").html(n),e.selected_columns(o)&&e.selected_columns(o).forEach(function(t){var o=$('.dq-modal-column[data-columnname="'+t.name+'"]');o.data("columnname",t.name),o.data("columntype",t.type),o.data("currentaggregate",t.agg||"none"),o.find("input[type=checkbox]").prop("checked",!0),"none"!==t.agg&&o.find("button").text(t.agg+"("+t.name+")")})})};$(document).on("click",".dq-modal-column button",function(){var t=$(this).parent(),n=t.data("columnname"),a=t.data("columntype"),r=t.data("currentaggregate"),l=DataQ.next_aggregate(a,r);e.operated_column()!==o+"."+n&&null!==e.operated_column()&&(l="none"),"none"===l?(e.operated_column()===o+"."+n&&e.operated_column(null),$(this).text(n)):($(this).text(l+"("+n+")"),e.operated_column(o+"."+n)),t.data("currentaggregate",l)}),$(document).on("click",".dq-modal-done-btn",function(){var n=[],a=!1;$(".dq-modal-column").each(function(){var t=$(this);if(t.find("input").is(":checked")){var r=t.data("currentaggregate"),l=t.data("columntype"),d=t.data("columnname");o+"."+d===e.operated_column()&&(a=!0),null!==r&&void 0!==r||(r="none"),n.push({name:d,type:l,agg:r})}}),e.operated_column()&&e.operated_column().split(".")[0]===o&&!a&&e.operated_column(null),e.selected_columns(o,n),e.update_grouping(),$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),console.log("help me"),console.log("now"),t()})}();
+!function(){window.DataQ=window.DataQ||{},query=null,policy=null;var n;DataQ.DQ=function(o,t){n=t;var e=$(DataQ.templates["dataq-container"]());$("body").append(e),query=DataQ.Query(),query.repo(o),$(".dq-black-background").click(function(){$(".dq-black-background").remove(),$(".dataq").remove(),n(null)})},DataQ.DQ_rls_policy=function(o,t){n=t;var e=$(DataQ.templates["dataq-container-rls-policy"]());$("body").append(e),policy=DataQ.Policy(),policy.repo(o),$(".dq-black-background").click(function(){$(".dq-black-background").remove(),$(".dataq").remove(),n(null)})};var o=function(){$(".dq-selected-tables").html(""),query.get_selected_tables().forEach(function(n){var o=query.selected_columns(n);if(null!==o){var t=[];o.forEach(function(n){"none"===n.agg?t.push(n.name):t.push(n.agg+"("+n.name+")")});var e=DataQ.templates["dq-selected-table"]({table_name:n,column_list:t.join(", ")});$(".dq-selected-tables").append(e)}}),$(".dq-filter-list").html(""),query.get_filters().forEach(function(n){$(".dq-filter-list").append(DataQ.templates["dq-filter-list-item"]({filter:n}))});var n=[];query.grouping().forEach(function(o){n.push(o.string)}),n.length>0?$(".dq-grouping-text").html(n.join(", ")):$(".dq-grouping-text").html("No Grouping...");var o=[];query.sorts().forEach(function(n){o.push(n.string)}),o.length>0?$(".dq-sorting-text").html(o.join(", ")):$(".dq-sorting-text").html("No Sorting...")};$(document).on("click",".dq-btn-add-table",function(){DataQ.TableModal(query,null,o)}),$(document).on("click",".dq-btn-delete-table",function(){var n=$(this).data("tablename");query.operated_column()&&query.operated_column().split(".")[0]===n&&query.operated_column(null),query.selected_columns(n,null),query.update_grouping(),o()}),$(document).on("click",".dq-btn-edit-table",function(){DataQ.TableModal(query,$(this).data("tablename"),o)}),$(document).on("click",".dq-btn-add-filter",function(){DataQ.FilterModal(query,o)}),$(document).on("click",".dq-btn-delete-filter",function(){var n=$(this).parent().data("code");query.delete_filter(n),o()}),$(document).on("click",".dq-btn-edit-grouping",function(){DataQ.GroupingModal(query,o)}),$(document).on("click",".dq-btn-edit-sorting",function(){DataQ.SortModal(query,o)}),$(document).on("click",".dq-btn-cancel-query",function(){$(".dq-black-background").remove(),$(".dataq").remove(),n(null)}),$(document).on("click",".dq-btn-run-query",function(){$(".dq-black-background").remove(),$(".dataq").remove(),n(DataQ.build_query(query))}),$(document).on("click",".dq-btn-run-policy",function(){$(".dq-black-background").remove(),$(".dataq").remove(),n(DataQ.build_policy(policy))})}(),function(){window.DataQ=window.DataQ||{},DataQ.API={},DataQ.API.get_repos=function(n){$.get("/apps/dataq/api/",n)},DataQ.API.get_tables=function(n,o){$.get("/apps/dataq/api/"+n+"/",o)},DataQ.API.get_schema=function(n,o,t){$.get("/apps/dataq/api/"+n+"/"+o+"/",t)}}(),function(){window.DataQ=window.DataQ||{};var n,o;DataQ.FilterModal=function(t,e){n=e,o=t;var a=$("#dq-filter-modal");if(0===a.length){var r=[];o.get_selected_tables().forEach(function(n){o.schema(n).forEach(function(o){r.push({column_name:o[0],table_name:n,full_name:n+"."+o[0]})})});var l=DataQ.templates["dq-filter-modal"]({columns:r,repo:o.repo()});$("body").append(l)}$("#dq-filter-modal").modal({keyboard:!1}),$(".modal-backdrop").click(function(){n(),$("#dq-filter-modal").remove()})},$(document).on("click",".dq-filter-quit",function(){n(),$("#dq-filter-modal").remove(),$(".modal-backdrop").remove()}),$(document).on("click",".dq-filter-done",function(){var t=$(".dq-filter-1-text").val(),e=$(".dq-filter-2-text").val(),a=$(".dq-filter-op-text").val();t.length>0&&e.length>0&&a.length>0?(o.add_filter(t,a,e),n(),$("#dq-filter-modal").modal("hide"),$("#dq-filter-modal").remove(),$(".modal-backdrop").remove()):alert("You need to fill out the three text boxes")}),$(document).on("click",".dq-filter-1-link",function(){$(".dq-filter-1-text").val($(this).html())}),$(document).on("click",".dq-filter-2-link",function(){$(".dq-filter-2-text").val($(this).html())}),$(document).on("click",".dq-filter-op-link",function(){var n=$(this).html().replace("&gt;",">").replace("&lt;","<");$(".dq-filter-op-text").val(n)})}(),function(){window.DataQ=window.DataQ||{};var n,o;DataQ.GroupingModal=function(t,e){o=t,n=e;var a=$("#dq-grouping-modal");if(0===a.length){var r=DataQ.templates["dq-grouping-modal"]({columns:o.grouping()});$("body").append(r)}$("#dq-grouping-modal").modal({keyboard:!1}),$("#dq-grouping-modal").on("shown.bs.modal",function(){$(".dq-grouping-modal-list").sortable({forcePlaceholderSize:!0})}),$(".modal-backdrop").click(function(){n(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()})},$(document).on("click","#dq-grouping-modal-quit-btn",function(){n(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()}),$(document).on("click","#dq-grouping-modal-done-btn",function(){var t=[];$(".dq-grouping-list-item").each(function(){var n,e=$(this),a=e.data("string");o.grouping();o.grouping().forEach(function(o){o.string===a&&(n=o)}),t.push(n)}),o.grouping(t),n(),$("#dq-grouping-modal").remove(),$(".modal-backdrop").remove()})}(),function(){window.DataQ=window.DataQ||{};for(var n=["bigint","int8","bigserial","serial8","double precision","float8","integer","int","int4","real","float4","smallint","int2","serial","serial4"],o={},t=0;t<n;t++)o[n[t]]=!0;var e={none:"max",max:"min",min:"sum",sum:"count",count:"avg",avg:"none"},a={none:"count",count:"none"};DataQ.next_aggregate=function(n,t){return null===t&&(t="none"),o[n]?e[t]:a[t]}}(),function(){window.DataQ=window.DataQ||{},window.DataQ.build_query=function(n){var o=[],t=[],e=[],a=[],r=[],l=n.repo();if(n.get_selected_tables().forEach(function(n){t.push(l+"."+n)}),n.get_selected_tables().forEach(function(t){n.selected_columns(t).forEach(function(n){void 0===n.agg||null===n.agg||"none"===n.agg?o.push(l+"."+t+"."+n.name):o.push(n.agg+"("+l+"."+t+"."+n.name+") as "+n.agg+"_"+t+"_"+n.name)})}),n.get_filters().forEach(function(n){e.push(n.filter1+" "+n.op+" "+n.filter2)}),n.grouping().forEach(function(n){var o=n.column.agg;null!==o&&void 0!==o&&"none"!==o||a.push(l+"."+n.string)}),n.sorts().forEach(function(n){var o=n.column.agg;null===o||void 0===o||"none"===o?r.push(l+"."+n.string):r.push(o+"_"+n.table+"_"+n.column.name)}),0===o.length)return"";var d="SELECT "+o.join(", ")+" FROM "+t.join(", ");return e.length>0&&(d+=" WHERE "+e.join(" AND ")),a.length>0&&(d+=" GROUP BY "+a.join(", ")),r.length>0&&(d+=" ORDER BY "+r.join(", ")),d.trim(),d+=";"}}(),function(){window.DataQ=window.DataQ||{},DataQ.Query=function(){var n={};return n._schema_for_table_name={},n._repo_name=null,n._operated_column=null,n._selected_columns_for_table={},n._filter_for_code={},n._grouping=[],n._sorts={},n.schema=function(o,t){return void 0!==t&&(n._schema_for_table_name[o]=t),n._schema_for_table_name[o]},n.repo=function(o){return void 0!==o&&(n._repo_name=o),n._repo_name},n.operated_column=function(o){return void 0!==o&&(n._operated_column=o),n._operated_column},n.update_grouping=function(){n._grouping=[];var o=!1;for(var t in n._selected_columns_for_table)n._selected_columns_for_table[t]&&n._selected_columns_for_table[t].forEach(function(e){"none"===e.agg?n._grouping.push({string:t+"."+e.name,table:t,column:e}):o=!0});o||(n._grouping=[])},n.selected_columns=function(o,t){return void 0!==t&&(n._selected_columns_for_table[o]=t),n._selected_columns_for_table[o]},n.get_selected_tables=function(){var o=[];for(var t in n._selected_columns_for_table)o.push(t);return o},n.add_filter=function(o,t,e){var a=o+" "+t+" "+e,r=md5((new Date).getTime()+a);return n._filter_for_code[r]={filter1:o,op:t,filter2:e,filter_string:a},n._filter_for_code[r]},n.delete_filter=function(o){n._filter_for_code[o]=void 0},n.get_filters=function(){var o=[];for(var t in n._filter_for_code){var e=n._filter_for_code[t];e&&o.push({code:t,filter1:e.filter1,op:e.op,filter2:e.filter2,filter_string:e.filter_string})}return o},n.grouping=function(o){return void 0!==o&&(n._grouping=o),n._grouping},n.add_sort=function(o){n._sorts[o.string]=o},n.delete_sort=function(o){n._sorts[o]=void 0},n.sorts=function(){var o=[];for(var t in n._sorts)void 0!==n._sorts[t]&&o.push(n._sorts[t]);return o},n}}(),function(){window.DataQ=window.DataQ||{},window.DataQ.build_policy=function(n){var o=n.name(),t=n.repo()+"."+n.name(),e=n.command(),a=n.roles(),r=n.using_expression(),l=n.check_expression(),d="CREATE POLICY "+o;return d+=" ON "+t,d+=" FOR "+e,d+=" TO "+a.join(", "),d+=" USING "+r,"SELECT"!==e&&(d+=" WITH CHECK "+l),d.trim(),d+=";"}}(),function(){window.DataQ=window.DataQ||{},DataQ.Policy=function(){var n={};return n._repo_name=null,n._name=null,n._table=null,n._command=null,n._roles=[],n._using_expr=null,n._check_expr=null,n.repo=function(o){return void 0!==o&&(n._repo_name=o),n._repo_name},n.name=function(o){return void 0!==o&&(n._name=o),n._name},n.table=function(o){return void 0!==o&&(n._table=o),n._table},n.command=function(o){return void 0!==o&&(n._command=o),n._command},n.roles=function(o){return void 0!==o&&(o instanceof Array||(o=[o]),n._roles=o),n._roles},n.using_expression=function(o){return void 0!==o&&(n._using_expr=o),n._using_expr},n.check_expression=function(o){return void 0!==o&&(n._check_expr=o),n._check_expr},n}}(),function(){window.DataQ=window.DataQ||{};var n,o;DataQ.SortModal=function(t,a){o=t,n=a;var r=$("#dq-sort-modal");if(0===r.length){var l=DataQ.templates["dq-sort-modal"]();$("body").append(l)}$("#dq-sort-modal").modal({keyboard:!1}),$("#dq-sort-modal").on("shown.bs.modal",function(){e()}),$(".modal-backdrop").click(function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),n()})},$(document).on("click",".dq-sort-modal-dropdown-btn",function(){var n=$(".dq-sort-modal-dropdown");n.html(""),t().forEach(function(o){n.append(DataQ.templates["dq-sort-dropdown-li"]({item:o}))})});var t=function(){var n={};o.sorts().forEach(function(o){n[o.string]=!0});var t=[];return o.get_selected_tables().forEach(function(e){o.selected_columns(e).forEach(function(o){var a=e+"."+o.name;"none"!==o.agg&&(a=o.agg+"("+e+"."+o.name+")"),n[a]||t.push({string:a,table:e,column:o})})}),t},e=function(){var n=$(".dq-sort-item-list");n.html(""),o.sorts().forEach(function(o){var t=DataQ.templates["dq-sort-list-item"]({item:o});n.append(t)})};$(document).on("click",".dq-sort-modal-quit",function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),n()}),$(document).on("click",".dq-sort-link",function(){var n=$(this),t=n.data("columnname"),a=n.data("columntype"),r=n.data("aggregate"),l=n.data("table"),d=n.data("string");void 0!==r&&null!==r||(r="none");var i={column:{name:t,type:a,agg:r},string:d,table:l};o.add_sort(i),e()}),$(document).on("click",".dq-sort-modal-done-btn",function(){$("#dq-sort-modal").remove(),$(".modal-backdrop").remove(),n()}),$(document).on("click",".dq-sort-delete-btn",function(){var n=$(this).parent().parent(),t=n.data("string");o.delete_sort(t),e()})}(),function(){window.DataQ=window.DataQ||{};var n,o,t;DataQ.TableModal=function(a,r,l){o=r,n=l,t=a;var d=$("#dq-table-modal");if(0===d.length){var i=DataQ.templates["dq-table-modal"]({table_name:o});$("body").append(i)}$("#dq-table-modal").modal({keyboard:!1}),$(".dq-modal-done-btn").hide(),$("#dq-table-modal").on("shown.bs.modal",function(){o&&e(o)}),$(".modal-backdrop").click(function(){$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),n()})},$(document).on("click",".dq-modal-quit",function(){$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),n()}),$(document).on("click",".dq-modal-dropdown-btn",function(){var n=$(".dq-modal-dropdown");n.html(""),DataQ.API.get_tables(t.repo(),function(o){o.tables.forEach(function(o){var t=DataQ.templates["dq-modal-dropdown-item"]({item_name:o});n.append(t)})})}),$(document).on("click",".dq-modal-dropdown-link",function(){var n=$(this).data("item_name");o=n,$(".dq-modal-table-dropdown-text").text(o),e()});var e=function(){DataQ.API.get_schema(t.repo(),o,function(n){$(".dq-modal-done-btn").show(),t.schema(o,n.schema).sort(function(n,o){return n[0]>o[0]});var e=DataQ.templates["dq-modal-columns"]({columns:t.schema(o)});$(".dq-column-list").html(e),t.selected_columns(o)&&t.selected_columns(o).forEach(function(n){var o=$('.dq-modal-column[data-columnname="'+n.name+'"]');o.data("columnname",n.name),o.data("columntype",n.type),o.data("currentaggregate",n.agg||"none"),o.find("input[type=checkbox]").prop("checked",!0),"none"!==n.agg&&o.find("button").text(n.agg+"("+n.name+")")})})};$(document).on("click",".dq-modal-column button",function(){var n=$(this).parent(),e=n.data("columnname"),a=n.data("columntype"),r=n.data("currentaggregate"),l=DataQ.next_aggregate(a,r);t.operated_column()!==o+"."+e&&null!==t.operated_column()&&(l="none"),"none"===l?(t.operated_column()===o+"."+e&&t.operated_column(null),$(this).text(e)):($(this).text(l+"("+e+")"),t.operated_column(o+"."+e)),n.data("currentaggregate",l)}),$(document).on("click",".dq-modal-done-btn",function(){var e=[],a=!1;$(".dq-modal-column").each(function(){var n=$(this);if(n.find("input").is(":checked")){var r=n.data("currentaggregate"),l=n.data("columntype"),d=n.data("columnname");o+"."+d===t.operated_column()&&(a=!0),null!==r&&void 0!==r||(r="none"),e.push({name:d,type:l,agg:r})}}),t.operated_column()&&t.operated_column().split(".")[0]===o&&!a&&t.operated_column(null),t.selected_columns(o,e),t.update_grouping(),$("#dq-table-modal").remove(),$(".modal-backdrop").remove(),n()})}();
 //# sourceMappingURL=dataq.min.js.map
diff --git a/src/browser/static/dataq/js/dataq.min.js.map b/src/browser/static/dataq/js/dataq.min.js.map
index 9b22dc13..f4c0f536 100644
--- a/src/browser/static/dataq/js/dataq.min.js.map
+++ b/src/browser/static/dataq/js/dataq.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["dataq.js","dq-api.js","dq-filter-modal.js","dq-grouping-modal.js","dq-next-aggregate.js","dq-query-builder.js","dq-query.js","dq-sort-modal.js","dq-table-modal.js"],"names":["window","DataQ","query","callback","DQ","repo_name","cb","container","$","templates","append","Query","repo","click","remove","display_query","html","get_selected_tables","forEach","selected_table","selected_columns","column_list","selected_column","agg","push","name","table_name","join","get_filters","filter","group_strings","grouping","group","string","length","sort_strings","sorts","sort","document","on","TableModal","this","data","operated_column","split","update_grouping","FilterModal","code","parent","delete_filter","GroupingModal","SortModal","build_query","API","get_repos","get","get_tables","get_schema","table","q","modal","columns","schema","column","column_name","full_name","keyboard","filter1","val","filter2","op","add_filter","alert","replace","sortable","forcePlaceholderSize","new_grouping","each","current_group","li","number_types","is_number","i","next_numeric_aggregate","none","max","min","sum","count","avg","next_nonnumeric_aggregate","next_aggregate","columntype","current_aggregate","select_list","from_list","where_list","group_list","order_list","undefined","query_string","trim","that","_schema_for_table_name","_repo_name","_operated_column","_selected_columns_for_table","_filter_for_code","_grouping","_sorts","has_operated_column","tbls","k","filter_string","md5","Date","getTime","result","add_sort","delete_sort","update_list","list","create_items_in_dropdown","item","used_dict","items","type","hide","populate_column_list","dropdown","tables","item_name","text","show","a","b","element","find","prop","parent_li","columnname","currentaggregate","nextaggregate","is_op_col_checked","is","console","log"],"mappings":"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","file":"dataq.min.js","sourcesContent":["/**\n * Defines the DataQ.DQ object, which is what the user of the library will interact with.\n *\n * Simply call DataQ.DQ(repo_name, callback) and DataQ will launch. After the user builds a query,\n * the callback is executed as callback(query), where query is a String representing the SQL query \n * or null if the query was not built successfully.\n */\n(function() {\n  // Create the global DataQ object if it doesn't exist.\n  window.DataQ = window.DataQ || {};\n\n  // The DataQ.Query that is being built.\n  query = null;\n\n  // The callback to execute after the query is built. It is executed as cb(query) where query\n  // is a String representing the SQL query or null if the query was not built.\n  var callback;\n\n  /**\n   * @param repo_name - The name of the repo that DataQ should work on.\n   * @param cb - The callback to trigger when the query is built.\n   */\n  DataQ.DQ = function(repo_name, cb) {\n    // Set the callback.\n    callback = cb;\n\n    // Add the container to the page.\n    var container = $(DataQ.templates[\"dataq-container\"]());\n    $('body').append(container);\n\n    // Create the query object and set the repo name.\n    query = DataQ.Query();\n    query.repo(repo_name);\n\n    // Handle DataQ close when clicking backdrop.\n    $(\".dq-black-background\").click(function() {\n      $(\".dq-black-background\").remove();\n      $(\".dataq\").remove();\n      callback(null);\n    })\n  };\n\n  /**\n   * Update the UI to reflect the latest query.\n   */\n  var display_query = function() {\n\n    /**********************************/\n    /*** 1: Selected Tables/Columns ***/\n    /**********************************/\n    $('.dq-selected-tables').html(\"\");\n    query.get_selected_tables().forEach(function(selected_table) {\n      var selected_columns = query.selected_columns(selected_table);\n      if (selected_columns === null) {\n        return;\n      }\n\n      // Go through each column for the table and add it to the column list.\n      var column_list = [];\n      selected_columns.forEach(function(selected_column) {\n        if (selected_column.agg === \"none\") {\n          column_list.push(selected_column.name);\n        } else {\n          // If col has an aggregate, write it as \"aggregate(col)\".\n          column_list.push(selected_column.agg + \"(\" + selected_column.name + \")\");\n        }\n      });\n\n      // Add the table and column list to the UI.\n      var html = DataQ.templates[\"dq-selected-table\"]({\n        \"table_name\": selected_table,\n        \"column_list\": column_list.join(\", \")\n      });\n      $('.dq-selected-tables').append(html);\n    });\n\n    /***************************/\n    /*** 2: Selected Filters ***/\n    /***************************/\n    $('.dq-filter-list').html(\"\");\n    query.get_filters().forEach(function(filter) {\n      $('.dq-filter-list').append(DataQ.templates['dq-filter-list-item']({\n        \"filter\": filter\n      }));\n    });\n\n    /**************************/\n    /*** 3: Selected Groups ***/\n    /**************************/\n    // Identify which groups are checked.\n    var group_strings = [];\n    query.grouping().forEach(function(group) {\n      group_strings.push(group.string);\n    });\n\n    // Display the groups.\n    if (group_strings.length > 0) {\n      $(\".dq-grouping-text\").html(group_strings.join(\", \"));\n    } else {\n      $(\".dq-grouping-text\").html(\"No Grouping...\");\n    }\n\n    /************************************/\n    /*** 4: Identify the Sort Columns ***/\n    /************************************/\n    var sort_strings = [];\n    query.sorts().forEach(function(sort) {\n      sort_strings.push(sort.string);\n    });\n    \n    // Display the sorts.\n    if (sort_strings.length > 0) {\n      $(\".dq-sorting-text\").html(sort_strings.join(\", \"));\n    } else {\n      $(\".dq-sorting-text\").html(\"No Sorting...\");\n    }\n\n  }; // end display_query\n\n  // Handle table additions.\n  $(document).on(\"click\", \".dq-btn-add-table\", function() {\n    DataQ.TableModal(query, null, display_query);\n  });\n\n  // Handle table deletes.\n  $(document).on(\"click\", \".dq-btn-delete-table\", function() {\n    var table_name = $(this).data(\"tablename\");\n    if (query.operated_column() && query.operated_column().split(\".\")[0] === table_name) {\n      query.operated_column(null);\n    }\n    query.selected_columns(table_name, null);\n    query.update_grouping();\n    display_query();\n  });\n\n  // Handle table edits.\n  $(document).on(\"click\", \".dq-btn-edit-table\", function() {\n    DataQ.TableModal(query, $(this).data(\"tablename\"), display_query);\n  });\n\n  // Handle filter additions.\n  $(document).on(\"click\", \".dq-btn-add-filter\", function() {\n    DataQ.FilterModal(query, display_query);\n  });\n\n  // Handle filter deletion.\n  $(document).on(\"click\", \".dq-btn-delete-filter\", function() {\n    var code = $(this).parent().data(\"code\");\n    query.delete_filter(code);\n    display_query();\n  });\n\n  // Handle grouping edit.\n  $(document).on(\"click\", \".dq-btn-edit-grouping\", function() {\n    DataQ.GroupingModal(query, display_query);\n  });\n\n  // Handle sorting edit.\n  $(document).on(\"click\", \".dq-btn-edit-sorting\", function() {\n    DataQ.SortModal(query, display_query);\n  });\n\n  // Handle DataQ cancel.\n  $(document).on(\"click\", \".dq-btn-cancel-query\", function() {\n    $(\".dq-black-background\").remove();\n    $(\".dataq\").remove();\n    callback(null);\n  });\n\n  // Handle DataQ run query.\n  $(document).on(\"click\", \".dq-btn-run-query\", function() {\n    $(\".dq-black-background\").remove();\n    $(\".dataq\").remove();\n    callback(DataQ.build_query(query));\n  });\n})();\n","/**\n * Helper for accessing DataQ API.\n */\n(function() {\n  // Create the global DataQ object if it doesn't exist.\n  window.DataQ = window.DataQ || {};\n\n  DataQ.API = {};\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_repos = function(callback) {\n    $.get(\"/apps/dataq/api/\", callback);\n  };\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_tables = function(repo, callback) {\n    $.get(\"/apps/dataq/api/\" + repo + \"/\", callback);\n  };\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_schema = function(repo, table, callback) {\n    $.get(\"/apps/dataq/api/\" + repo + \"/\" + table + \"/\", callback);\n  };\n})();\n","/**\n * The modal window that allows the user to create filters (of the form <val1> <operation> <val2>).\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed. This is executed as callback().\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to execute when the filter has been added. It will be executed\n   *                   as cb().\n   */\n  DataQ.FilterModal = function(q, cb) {\n    // Set the instance variables.\n    callback = cb;\n    query = q;\n\n    // If the modal does not exist, create it.\n    var modal = $(\"#dq-filter-modal\");\n    if (modal.length === 0) {\n      var columns = [];\n\n      // Iterate through each column of each selected table and add to the list of columns.\n      query.get_selected_tables().forEach(function(selected_table) {\n        query.schema(selected_table).forEach(function(column) {\n          columns.push({\n            \"column_name\": column[0],\n            \"table_name\": selected_table,\n            \"full_name\": selected_table + \".\" + column[0]\n          });\n        });\n      });\n\n      // Create the HTML for the filter modal.\n      var html = DataQ.templates['dq-filter-modal']({\n        \"columns\": columns,\n        \"repo\": query.repo()\n      });\n\n      // Add the modal to the page.\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc)\n    $('#dq-filter-modal').modal({\n      keyboard: false\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      callback();\n      $(\"#dq-filter-modal\").remove();\n    })\n  } // End FilterModal\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-filter-quit\", function() {\n    callback();\n    $(\"#dq-filter-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n  // Handle modal done.\n  $(document).on(\"click\", \".dq-filter-done\", function() {\n    var filter1 = $('.dq-filter-1-text').val();\n    var filter2 = $('.dq-filter-2-text').val();\n    var op = $('.dq-filter-op-text').val();\n    if (filter1.length > 0 && filter2.length > 0 && op.length > 0) {\n      query.add_filter(filter1, op, filter2);\n      callback();\n      $('#dq-filter-modal').modal('hide');\n      $(\"#dq-filter-modal\").remove();\n      $(\".modal-backdrop\").remove();\n    } else {\n      alert(\"You need to fill out the three text boxes\");\n    }\n  });\n\n  // Handle filter1 dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-1-link\", function() {\n    $('.dq-filter-1-text').val($(this).html());\n  });\n\n  // Handle filter2 dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-2-link\", function() {\n    $('.dq-filter-2-text').val($(this).html());\n  });\n\n  // Handle operation dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-op-link\", function() {\n    // Unescape html for > and <\n    var op = $(this).html().replace(\"&gt;\", \">\").replace(\"&lt;\", \"<\");\n    $('.dq-filter-op-text').val(op);\n  });\n\n})();\n","/***\n * The modal window for editing the groupings.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed. This is executed as callback().\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to execute when the grouping has been modified. It will be executed\n   *              as cb().\n   */\n  DataQ.GroupingModal = function(q, cb) {\n    // Set the instance variables.\n    query = q;\n    callback = cb;\n\n    // Create the modal HTML if it doesn't exist.\n    var modal = $(\"#dq-grouping-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates[\"dq-grouping-modal\"]({\n        columns: query.grouping()\n      });\n      $(\"body\").append(html);\n    }\n\n    // Display the modal. (disable Esc)\n    $(\"#dq-grouping-modal\").modal({\n      keyboard: false\n    });\n\n    // When the modal is displayed, enable HTML5Sortable.\n    $(\"#dq-grouping-modal\").on(\"shown.bs.modal\", function() {\n      $(\".dq-grouping-modal-list\").sortable({\n        forcePlaceholderSize: true\n      });\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      callback();\n      $(\"#dq-grouping-modal\").remove();\n      $(\".modal-backdrop\").remove();\n    })\n  }; // End GroupingModal\n\n  // Handler for close modal.\n  $(document).on(\"click\", \"#dq-grouping-modal-quit-btn\", function() {\n    callback();\n    $(\"#dq-grouping-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n  // Handle for finishing edits.\n  $(document).on(\"click\", \"#dq-grouping-modal-done-btn\", function() {\n    var new_grouping = [];\n\n    // Iterate through each list item (in order).\n    $(\".dq-grouping-list-item\").each(function() {\n      var li = $(this);\n      var string = li.data(\"string\");\n      var grouping = query.grouping();\n      var current_group;\n\n      // Find the group associated with this list item.\n      query.grouping().forEach(function(group) {\n        if (group.string === string) {\n          current_group = group;\n        }\n      });\n\n      new_grouping.push(current_group);\n    });\n\n    query.grouping(new_grouping);\n    callback();\n    $(\"#dq-grouping-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n})();\n","/**\n * Helper function to return the next aggregate for a given column type.\n * ex. max, min, avg, sum, count.\n *\n * This is useful to cycle through the possible aggregates as user clicks a column.\n * col1 --click--> max(col1) --click--> min(col1), etc.\n */\n(function() {\n  window.DataQ = window.DataQ || {};\n\n  // Numeric types supported in PostgreSQL.\n  var number_types = [\"bigint\", \"int8\", \"bigserial\", \"serial8\", \"double precision\", \"float8\", \n    \"integer\", \"int\", \"int4\", \"real\", \"float4\", \"smallint\", \"int2\", \"serial\", \"serial4\"];\n\n  // Turn number_types into a Javascript object for more efficient lookup.\n  // key = PostgreSQL type, val = true iff key is a numeric type.\n  var is_number = {};\n  for (var i = 0; i < number_types; i++) {\n    is_number[number_types[i]] = true;\n  }\n\n  // Helper for cycling through numeric aggregates.\n  // key = aggregate, value = next aggregate\n  // For example, a cycle (beginning at none) would be:\n  // none -> max -> min -> sum -> count -> avg -> none\n  var next_numeric_aggregate = {\n    \"none\": \"max\",\n    \"max\": \"min\",\n    \"min\": \"sum\",\n    \"sum\": \"count\",\n    \"count\": \"avg\",\n    \"avg\": \"none\"\n  };\n\n  // Helper for cycling through non-numeric aggregates.\n  var next_nonnumeric_aggregate = {\n    \"none\": \"count\",\n    \"count\": \"none\"\n  };\n\n\n  /**\n   * Determine the next aggregate in the cycle for the given column type and the current aggregate.\n   *\n   * @param columntype - The PostgreSQL type of the column.\n   * @param current_aggregate - The current aggregate operator applied to the column (ex. max, min)\n   *                            If no aggregate has been applied, this value should be either null\n   *                            or \"none\".\n   * @return  The next aggregate in the cycle.\n   */\n  DataQ.next_aggregate = function(columntype, current_aggregate) {\n    if (current_aggregate === null) {\n      current_aggregate = \"none\";\n    }\n    if (is_number[columntype]) {\n      return next_numeric_aggregate[current_aggregate];\n    } else {\n      return next_nonnumeric_aggregate[current_aggregate];\n    }\n  };\n})();\n","/**\n * Logic for constructing a SQL query string from a DataQ.Query object.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  /**\n   * Take a DataQ.Query object and generate a SQL query string from it.\n   *\n   * @param query - The DataQ.Query object.\n   * @return A String representing the SQL query.\n   */\n  window.DataQ.build_query = function(query) {\n\n    // The list of columns to select.\n    var select_list = [];\n\n    // The list of tables to select from.\n    var from_list = [];\n\n    // The filters to apply.\n    var where_list = [];\n\n    // The grouping to perform.\n    var group_list = [];\n\n    // The sorting to apply.\n    var order_list = [];\n\n    // Get the current repo name - we'll need to prepend this to some of the table/column names.\n    var repo = query.repo();\n\n    // Create the FROM clause. It is simply the list of tables that the user has selected.\n    // Each item in the list is a String of the form: \"repo.table\".\n    query.get_selected_tables().forEach(function(table) {\n      from_list.push(repo + \".\" + table);\n    });\n\n    // Create the SELECT clause.\n    // Iterate through every selected column of every selected table and add the column to the \n    // select list (and write the aggregate if possible).\n    query.get_selected_tables().forEach(function(table) {\n      query.selected_columns(table).forEach(function(column) {\n        if (column.agg === undefined || column.agg === null || column.agg === \"none\") {\n          select_list.push(repo + \".\" + table + \".\" + column.name);\n        } else {\n          // When an aggregate \"agg\" on column \"col\" in table \"table\" and repo \"repo\" appears, mark\n          // \"agg(repo.table.col) as agg_table_col\".\n          select_list.push(column.agg + \"(\" + repo + \".\" + table + \".\" + column.name + \")\" + \n            \" as \" + column.agg + \"_\" + table + \"_\"\n            + column.name);\n        }\n      });\n    });\n\n    // Create the WHERE clause.\n    // Simply iterate through each filter and add it to the list.\n    query.get_filters().forEach(function(filter) {\n      where_list.push(filter.filter1 + \" \" + filter.op + \" \" + filter.filter2);\n    });\n\n    // Create the  GROUP BY clause.\n    query.grouping().forEach(function(group) {\n      var agg = group.column.agg;\n\n      // We can only add a group by if it's not the aggregate column.\n      if (agg === null || agg === undefined || agg === \"none\") {\n        group_list.push(repo + \".\" + group.string);\n      } \n    });\n\n    // Create the ORDER BY clause.\n    query.sorts().forEach(function(sort) {\n      var agg = sort.column.agg;\n      if (agg === null || agg === undefined || agg === \"none\") {\n        order_list.push(repo + \".\" + sort.string);\n      } else {\n        order_list.push(agg + \"_\" + sort.table + \"_\" + sort.column.name);\n      }\n    });\n\n    // Set the query string.\n    if (select_list.length === 0) {\n      return \"\";\n    }\n    var query_string = \"SELECT \" + select_list.join(\", \")\n        + \" FROM \" + from_list.join(\", \");\n\n    // Set the where list.\n    if (where_list.length > 0) {\n      query_string +=  \" WHERE \" + where_list.join(\" AND \");\n    }\n\n    // Set the group list.\n    if (group_list.length > 0) {\n      query_string += \" GROUP BY \" + group_list.join(\", \")\n    }\n\n    // Set the order list.\n    if (order_list.length > 0) {\n      query_string += \" ORDER BY \" + order_list.join(\", \")\n    }\n\n    // Remove leading and trailing spaces and then append semicolon.\n    query_string.trim();\n    query_string += \";\";\n    return query_string;\n  };\n})();\n","/**\n * The object for building a query.\n */\n(function() {\n  // If the DataQ object doesn't exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  DataQ.Query = function() {\n    \n    // Create the object and initialize the objects.\n    var that = {};\n    that._schema_for_table_name = {};\n    that._repo_name = null;\n    that._operated_column = null;\n    that._selected_columns_for_table = {};\n    that._filter_for_code = {};\n    that._grouping = [];\n    that._sorts = {};\n\n    /**\n     * Get or set the schema for a given table.\n     *\n     * @param table_name - The name of the table.\n     * @param schema - If this argument is omitted (or undefined), this function acts as a getter\n     *                returning the schema for the given table. Otherwise, the function acts as a \n     *                setter, setting the schema for table_name.\n     *\n     * @return The schema for the table name.\n     */\n    that.schema = function(table_name, schema) {\n      if (schema !== undefined) {\n        that._schema_for_table_name[table_name] = schema;\n      } \n      return that._schema_for_table_name[table_name];\n    };\n\n    /**\n     * Get or set the repo name.\n     *\n     * @param repo_name - If this argument is omitted (or undefined), this function acts as a \n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the repo.\n     */\n    that.repo = function(repo_name) {\n      if (repo_name !== undefined) {\n        that._repo_name = repo_name;\n      }\n      return that._repo_name;\n    };\n\n    /**\n     * Get or set the operated column.\n     *\n     * @param operated_column - if this argument is omitted (or undefined), this function acts as\n     *                          a getter. Otherwise, it acts a setter, setting the operated column.\n     *\n     * @return The name of the operated column (\"table.col\"). This may be null.\n     */\n    that.operated_column = function(operated_column) {\n      if (operated_column !== undefined) {\n        that._operated_column = operated_column;\n      }\n      return that._operated_column;\n    };\n\n    that.update_grouping = function() {\n      that._grouping = [];\n      var has_operated_column = false;\n      for (var table in that._selected_columns_for_table) {\n        if (!that._selected_columns_for_table[table]) {\n          continue;\n        }\n        that._selected_columns_for_table[table].forEach(function(column) {\n          if (column.agg === \"none\") {\n            that._grouping.push({\n              \"string\": table + \".\" + column.name,\n              \"table\": table,\n              \"column\": column\n            });\n          } else {\n            has_operated_column = true;\n          }\n        });\n      } // end for each table\n\n      if (!has_operated_column) {\n        that._grouping = [];\n      }\n    };\n\n    that.selected_columns = function(table_name, selected_columns) {\n      if (selected_columns !== undefined) {\n        that._selected_columns_for_table[table_name] = selected_columns;\n      }\n      return that._selected_columns_for_table[table_name];\n    };\n\n    that.get_selected_tables = function() {\n      var tbls = [];\n      for (var k in that._selected_columns_for_table) {\n        tbls.push(k);\n      }\n      return tbls;\n    };\n\n    that.add_filter = function(filter1, op, filter2) {\n      var filter_string = filter1 + \" \" + op + \" \" + filter2;\n      var code = md5((new Date()).getTime() + filter_string);\n      that._filter_for_code[code] = {\n        \"filter1\": filter1,\n        \"op\": op,\n        \"filter2\": filter2,\n        \"filter_string\": filter_string\n      };\n      return that._filter_for_code[code];\n    };\n\n    that.delete_filter = function(code) {\n      that._filter_for_code[code] = undefined;\n    };\n\n    that.get_filters = function() {\n      var result = [];\n      for (var k in that._filter_for_code) {\n        var filter = that._filter_for_code[k];\n        if (!filter) {\n          continue;\n        }\n        result.push({\n          \"code\": k,\n          \"filter1\": filter.filter1,\n          \"op\": filter.op,\n          \"filter2\": filter.filter2,\n          \"filter_string\": filter.filter_string\n        });\n      };\n      return result;\n    };\n\n    that.grouping = function(grouping) {\n      if (grouping !== undefined) {\n        that._grouping = grouping;\n      }\n      return that._grouping;\n    };\n\n    that.add_sort = function(sort) {\n      that._sorts[sort.string] = sort;\n    };\n\n    that.delete_sort = function(sort) {\n      that._sorts[sort] = undefined;\n    };\n\n    that.sorts = function() {\n      var result = [];\n      for (var k in that._sorts) {\n        if (that._sorts[k] !== undefined) {\n          result.push(that._sorts[k]);\n        }\n      }\n      return result;\n    };\n\n    return that;\n  };\n})();\n","/**\n * The modal window that allows the user to specify the sorting of the columns.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger whent he modal is closed.\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to trigger when the user finishes specifying sorts. It is executed as\n   *              cb().\n   */\n  DataQ.SortModal = function(q, cb) {\n    // Set the instance variables.\n    query = q;\n    callback = cb;\n\n    // If the modal HTML does not exist, add it to the page.\n    var modal = $(\"#dq-sort-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates['dq-sort-modal']();\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc)\n    $('#dq-sort-modal').modal({\n      keyboard: false\n    });\n\n    // When the modal is shown, populate the columns.\n    $(\"#dq-sort-modal\").on(\"shown.bs.modal\", function() {\n      update_list();\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      $(\"#dq-sort-modal\").remove();\n      $(\".modal-backdrop\").remove();\n      callback();\n    })\n  };\n\n  // Handle dropdown item click.\n  $(document).on(\"click\", \".dq-sort-modal-dropdown-btn\", function() {\n    var list = $('.dq-sort-modal-dropdown');\n    list.html(\"\");\n\n    // When a dropdown link is clicked, add it to the list of selected columns.\n    create_items_in_dropdown().forEach(function(item) {\n      list.append(DataQ.templates[\"dq-sort-dropdown-li\"]({\n        \"item\": item\n      }));\n    });\n\n  });\n\n  // Create the items in the sort dropdown menu.\n  var create_items_in_dropdown = function() {\n    // Identify the sorts that have already been used.\n    var used_dict = {};\n    query.sorts().forEach(function(sort) {\n      used_dict[sort.string] = true;\n    });\n\n    // Iterate through every column of every selected table and, if it isn't used, add it to the\n    // list of items.\n    var items = [];\n    query.get_selected_tables().forEach(function(selected_table) {\n      query.selected_columns(selected_table).forEach(function(column) {\n        var string = selected_table + \".\" + column.name;\n        if (column.agg !== \"none\") {\n          string = column.agg + \"(\" + selected_table + \".\" + column.name + \")\";\n        }\n\n        // Don't add any item to dropdown if it's already used.\n        if (used_dict[string]) {\n          return;\n        }\n\n        items.push({\n          \"string\": string,\n          \"table\": selected_table,\n          \"column\": column\n        });\n\n      });\n    });\n\n    return items;\n  };\n\n  // Add the list items to list of used sorts.\n  var update_list = function() {\n    var list = $('.dq-sort-item-list');\n    list.html(\"\");\n    query.sorts().forEach(function(sort) {\n      var html = DataQ.templates[\"dq-sort-list-item\"]({\n        \"item\": sort\n      });\n      list.append(html);\n    });\n  };\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-sort-modal-quit\", function() {\n    $('#dq-sort-modal').remove();\n    $(\".modal-backdrop\").remove();\n    callback();\n  });\n\n  // Handle sort clicked.\n  $(document).on(\"click\", \".dq-sort-link\", function() {\n    var li = $(this);\n    var name = li.data(\"columnname\");\n    var type = li.data(\"columntype\");\n    var agg = li.data(\"aggregate\");\n    var table = li.data(\"table\");\n    var string = li.data(\"string\");\n\n    if (agg === undefined || agg === null) {\n      agg = \"none\";\n    }\n\n    var item = {\n      \"column\": {\n        \"name\": name,\n        \"type\": type,\n        \"agg\": agg\n      },\n      \"string\": string,\n      \"table\": table\n    };\n\n    query.add_sort(item);\n    update_list();\n\n  });\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-sort-modal-done-btn\", function() {\n    $('#dq-sort-modal').remove();\n    $(\".modal-backdrop\").remove();\n    callback();\n  });\n\n  // Handle delete sort.\n  $(document).on(\"click\", \".dq-sort-delete-btn\", function() {\n    var li = $(this).parent().parent();\n    var string = li.data(\"string\");\n    query.delete_sort(string);\n    update_list();\n  });\n\n})();\n","/**\n * The modal window that allows the user to select the tables (and columns from these tables) that\n * they want to use in their query.\n *\n * This modal can be used to either ADD a new table (and some of its columns) to the query or to\n * EDIT an existing table (and its columns) that is already in the query.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed.\n  var cb;\n\n  // The table we have selected.\n  var table;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   *\n   * @param table_name - The name of the table to modify. This must be either null (in which case\n   * the \"Add Table\" modal is displayed), or a table which the current user is associated with.\n   *\n   * @param callback - The callback that is executed after the user finishes updating selections.\n   * It is executed as callback()\n   */\n  DataQ.TableModal = function(q, table_name, callback) {\n    table = table_name;\n    cb = callback;\n    query = q;\n\n    // If the modal HTML does not exist, add it to the page.\n    var modal = $(\"#dq-table-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates['dq-table-modal']({\n        \"table_name\": table\n      });\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc and clicking the backdrop to exit modal)\n    $('#dq-table-modal').modal({\n      keyboard: false\n    });\n\n    // Don't allow clicking Done until the user selects a table.\n    $(\".dq-modal-done-btn\").hide();\n\n    // When the modal is shown, populate the columns.\n    $(\"#dq-table-modal\").on(\"shown.bs.modal\", function() {\n      if (table) {\n        populate_column_list(table);\n      }\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      $(\"#dq-table-modal\").remove();\n      $(\".modal-backdrop\").remove();\n      cb();\n    })\n  };\n\n  // If the user quits, trigger the callback.\n  $(document).on('click', '.dq-modal-quit', function() {\n    $(\"#dq-table-modal\").remove();\n    $(\".modal-backdrop\").remove();\n    cb();\n  });\n\n\n  // If the user clicks the table dropdown, populate the list with the list\n  // of available tables.\n  $(document).on(\"click\", \".dq-modal-dropdown-btn\", function() {\n    var dropdown = $(\".dq-modal-dropdown\");\n    dropdown.html(\"\");\n    DataQ.API.get_tables(query.repo(), function(data) {\n      data.tables.forEach(function(table) {\n        var html = DataQ.templates[\"dq-modal-dropdown-item\"]({\n          \"item_name\": table\n        });\n        dropdown.append(html);\n      }); // end foreach\n    }) // get_tables\n  }); // document on click\n\n\n  // When a table is selected from the dropdown, create the column list.\n  $(document).on(\"click\", \".dq-modal-dropdown-link\", function() {\n    // Set the content of the dropdown.\n    var item_name = $(this).data(\"item_name\");\n    table = item_name;\n    $('.dq-modal-table-dropdown-text').text(table);\n    populate_column_list();\n  });\n\n  // Populate the list of columns with the schema of the given table.\n  var populate_column_list = function() {\n    // Get the schema for the selected tables.\n    DataQ.API.get_schema(query.repo(), table, function(data) {\n      $(\".dq-modal-done-btn\").show();\n\n      // Sort the columns by name.\n      query.schema(table, data.schema).sort(function(a, b) {return a[0] > b[0]});\n\n      // Create the HTML and add it to the UI.\n      var html = DataQ.templates[\"dq-modal-columns\"]({\n        \"columns\": query.schema(table)\n      });\n      $('.dq-column-list').html(html);\n\n      if (query.selected_columns(table)) {\n        // Iterate through the columns for the selected table.\n        query.selected_columns(table).forEach(function(column) {\n          // Extract the data entries from the element (we find the element by selecting\n          // .dq-modal-column[data-columnname=\"colname\"]\n          var element = $('.dq-modal-column[data-columnname=\"'+column.name+'\"]');\n          element.data(\"columnname\", column.name);\n          element.data(\"columntype\", column.type);\n          element.data(\"currentaggregate\", column.agg || \"none\")\n          element.find(\"input[type=checkbox]\").prop('checked', true);\n          if (column.agg !== \"none\") {\n            element.find(\"button\").text(column.agg + \"(\" + column.name + \")\");\n          }\n        }); // end forEach\n      } // if selected columns\n    });\n  };\n\n  // Handle column aggregate trigger.\n  $(document).on(\"click\", \".dq-modal-column button\", function() {\n    // Extract the data entries.\n    var parent_li = $(this).parent();\n    var columnname = parent_li.data(\"columnname\");\n    var columntype = parent_li.data(\"columntype\");\n    var currentaggregate = parent_li.data(\"currentaggregate\")\n\n    // Compute the next aggregate operator to apply.\n    var nextaggregate = DataQ.next_aggregate(columntype, currentaggregate);\n\n    // If an aggregate has already been applied, don't apply another.\n    if (query.operated_column() !== table + \".\" + columnname && query.operated_column() !== null) {\n      nextaggregate = \"none\";\n    }\n\n    // If the aggregate has been turned off, turn off the operated column.\n    // Else if this is the new operated column, indicate so.\n    if (nextaggregate === \"none\") {\n      if (query.operated_column() === table + \".\" + columnname) {\n        query.operated_column(null);\n      }\n      $(this).text(columnname);\n    } else {\n      $(this).text(nextaggregate + \"(\" + columnname + \")\");\n      query.operated_column(table + \".\" + columnname);\n    }\n    parent_li.data(\"currentaggregate\", nextaggregate);\n  });\n\n  // When this is clicked, return the selected columns.\n  $(document).on(\"click\", \".dq-modal-done-btn\", function() {\n    // Figure out the selected columns.\n    var columns = [];\n    var is_op_col_checked = false;\n\n    // Iterate through each of the columns.\n    $('.dq-modal-column').each(function() {\n      var li = $(this);\n\n      // If the column is checked.\n      if (li.find(\"input\").is(\":checked\")) {\n\n        // Extract the data entries.\n        var agg = li.data(\"currentaggregate\");\n        var type = li.data(\"columntype\");\n        var name = li.data(\"columnname\");\n\n        // If the operated column has been selected, then mark it so.\n        if (table + \".\" + name === query.operated_column()) {\n          is_op_col_checked = true;\n        }\n\n        if (agg === null || agg === undefined) {\n          agg = \"none\";\n        }\n\n        columns.push({\n          \"name\": name,\n          \"type\": type,\n          \"agg\": agg\n        });\n\n      }\n    });\n\n    // If the operated column should be in this table and has not been selected,\n    // set the operated column as null.\n    if (query.operated_column() &&\n        query.operated_column().split(\".\")[0] === table &&\n        !is_op_col_checked) {\n      query.operated_column(null);\n    }\n\n    // Mark the selected columns for this table, and recompute the tables.\n    query.selected_columns(table, columns);\n    query.update_grouping();\n\n    // Remove the modals from the page.\n    $(\"#dq-table-modal\").remove();\n    $(\".modal-backdrop\").remove();\n    console.log('help me');\n    console.log('now');\n\n    cb();\n  });\n\n})();\n"],"sourceRoot":"/source/"}
\ No newline at end of file
+{"version":3,"sources":["dataq.js","dq-api.js","dq-filter-modal.js","dq-grouping-modal.js","dq-next-aggregate.js","dq-query-builder.js","dq-query.js","dq-rls-policy-builder.js","dq-rls-policy.js","dq-sort-modal.js","dq-table-modal.js"],"names":["window","DataQ","query","policy","callback","DQ","repo_name","cb","container","$","templates","append","Query","repo","click","remove","DQ_rls_policy","Policy","display_query","html","get_selected_tables","forEach","selected_table","selected_columns","column_list","selected_column","agg","push","name","table_name","join","get_filters","filter","group_strings","grouping","group","string","length","sort_strings","sorts","sort","document","on","TableModal","this","data","operated_column","split","update_grouping","FilterModal","code","parent","delete_filter","GroupingModal","SortModal","build_query","build_policy","API","get_repos","get","get_tables","get_schema","table","q","modal","columns","schema","column","column_name","full_name","keyboard","filter1","val","filter2","op","add_filter","alert","replace","sortable","forcePlaceholderSize","new_grouping","each","current_group","li","number_types","is_number","i","next_numeric_aggregate","none","max","min","sum","count","avg","next_nonnumeric_aggregate","next_aggregate","columntype","current_aggregate","select_list","from_list","where_list","group_list","order_list","undefined","query_string","trim","that","_schema_for_table_name","_repo_name","_operated_column","_selected_columns_for_table","_filter_for_code","_grouping","_sorts","has_operated_column","tbls","k","filter_string","md5","Date","getTime","result","add_sort","delete_sort","policy_name","command","role_list","roles","using_expr","using_expression","check_expr","check_expression","policy_string","_name","_table","_command","_roles","_using_expr","_check_expr","cmd","Array","expr","update_list","list","create_items_in_dropdown","item","used_dict","items","type","hide","populate_column_list","dropdown","tables","item_name","text","show","a","b","element","find","prop","parent_li","columnname","currentaggregate","nextaggregate","is_op_col_checked","is"],"mappings":"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","file":"dataq.min.js","sourcesContent":["/**\n * Defines the DataQ.DQ object, which is what the user of the library will interact with.\n *\n * Simply call DataQ.DQ(repo_name, callback) and DataQ will launch. After the user builds a query,\n * the callback is executed as callback(query), where query is a String representing the SQL query\n * or null if the query was not built successfully.\n */\n(function() {\n  // Create the global DataQ object if it doesn't exist.\n  window.DataQ = window.DataQ || {};\n\n  // The DataQ.Query that may be built.\n  query = null;\n\n  // The DataQ.Policy object that may be built.\n  policy = null;\n\n  // The callback to execute after the { query | policy } is built. It is executed as\n  // { cb(query) | cb(policy) } where { query | policy } is a String representing the\n  // SQL { query | policy } or null if the { query | policy } was not built.\n  var callback;\n\n  /**\n   * @param repo_name - The name of the repo that DataQ should work on.\n   * @param cb - The callback to trigger when the query is built.\n   */\n  DataQ.DQ = function(repo_name, cb) {\n    // Set the callback.\n    callback = cb;\n\n    // Add the container to the page.\n    var container = $(DataQ.templates[\"dataq-container\"]());\n    $('body').append(container);\n\n    // Create the query object and set the repo name.\n    query = DataQ.Query();\n    query.repo(repo_name);\n\n    // Handle DataQ close when clicking backdrop.\n    $(\".dq-black-background\").click(function() {\n      $(\".dq-black-background\").remove();\n      $(\".dataq\").remove();\n      callback(null);\n    })\n  };\n\n  /**\n   * @param repo_name - The name of the repo that DataQ should work on.\n   * @param cb - The callback to trigger when the query is built.\n   */\n  DataQ.DQ_rls_policy = function(repo_name, cb) {\n    // Set the callback.\n    callback = cb;\n\n    // Add the container to the page.\n    var container = $(DataQ.templates[\"dataq-container-rls-policy\"]());\n    $('body').append(container);\n\n    // Create the policy object and set the repo name.\n    policy = DataQ.Policy();\n    policy.repo(repo_name);\n\n    // Handle DataQ close when clicking backdrop.\n    $(\".dq-black-background\").click(function() {\n      $(\".dq-black-background\").remove();\n      $(\".dataq\").remove();\n      callback(null);\n    });\n  };\n\n  /**\n   * Update the UI to reflect the latest query.\n   */\n  var display_query = function() {\n\n    /**********************************/\n    /*** 1: Selected Tables/Columns ***/\n    /**********************************/\n    $('.dq-selected-tables').html(\"\");\n    query.get_selected_tables().forEach(function(selected_table) {\n      var selected_columns = query.selected_columns(selected_table);\n      if (selected_columns === null) {\n        return;\n      }\n\n      // Go through each column for the table and add it to the column list.\n      var column_list = [];\n      selected_columns.forEach(function(selected_column) {\n        if (selected_column.agg === \"none\") {\n          column_list.push(selected_column.name);\n        } else {\n          // If col has an aggregate, write it as \"aggregate(col)\".\n          column_list.push(selected_column.agg + \"(\" + selected_column.name + \")\");\n        }\n      });\n\n      // Add the table and column list to the UI.\n      var html = DataQ.templates[\"dq-selected-table\"]({\n        \"table_name\": selected_table,\n        \"column_list\": column_list.join(\", \")\n      });\n      $('.dq-selected-tables').append(html);\n    });\n\n    /***************************/\n    /*** 2: Selected Filters ***/\n    /***************************/\n    $('.dq-filter-list').html(\"\");\n    query.get_filters().forEach(function(filter) {\n      $('.dq-filter-list').append(DataQ.templates['dq-filter-list-item']({\n        \"filter\": filter\n      }));\n    });\n\n    /**************************/\n    /*** 3: Selected Groups ***/\n    /**************************/\n    // Identify which groups are checked.\n    var group_strings = [];\n    query.grouping().forEach(function(group) {\n      group_strings.push(group.string);\n    });\n\n    // Display the groups.\n    if (group_strings.length > 0) {\n      $(\".dq-grouping-text\").html(group_strings.join(\", \"));\n    } else {\n      $(\".dq-grouping-text\").html(\"No Grouping...\");\n    }\n\n    /************************************/\n    /*** 4: Identify the Sort Columns ***/\n    /************************************/\n    var sort_strings = [];\n    query.sorts().forEach(function(sort) {\n      sort_strings.push(sort.string);\n    });\n\n    // Display the sorts.\n    if (sort_strings.length > 0) {\n      $(\".dq-sorting-text\").html(sort_strings.join(\", \"));\n    } else {\n      $(\".dq-sorting-text\").html(\"No Sorting...\");\n    }\n\n  }; // end display_query\n\n  // Handle table additions.\n  $(document).on(\"click\", \".dq-btn-add-table\", function() {\n    DataQ.TableModal(query, null, display_query);\n  });\n\n  // Handle table deletes.\n  $(document).on(\"click\", \".dq-btn-delete-table\", function() {\n    var table_name = $(this).data(\"tablename\");\n    if (query.operated_column() && query.operated_column().split(\".\")[0] === table_name) {\n      query.operated_column(null);\n    }\n    query.selected_columns(table_name, null);\n    query.update_grouping();\n    display_query();\n  });\n\n  // Handle table edits.\n  $(document).on(\"click\", \".dq-btn-edit-table\", function() {\n    DataQ.TableModal(query, $(this).data(\"tablename\"), display_query);\n  });\n\n  // Handle filter additions.\n  $(document).on(\"click\", \".dq-btn-add-filter\", function() {\n    DataQ.FilterModal(query, display_query);\n  });\n\n  // Handle filter deletion.\n  $(document).on(\"click\", \".dq-btn-delete-filter\", function() {\n    var code = $(this).parent().data(\"code\");\n    query.delete_filter(code);\n    display_query();\n  });\n\n  // Handle grouping edit.\n  $(document).on(\"click\", \".dq-btn-edit-grouping\", function() {\n    DataQ.GroupingModal(query, display_query);\n  });\n\n  // Handle sorting edit.\n  $(document).on(\"click\", \".dq-btn-edit-sorting\", function() {\n    DataQ.SortModal(query, display_query);\n  });\n\n  // Handle DataQ cancel.\n  $(document).on(\"click\", \".dq-btn-cancel-query\", function() {\n    $(\".dq-black-background\").remove();\n    $(\".dataq\").remove();\n    callback(null);\n  });\n\n  // Handle DataQ run query.\n  $(document).on(\"click\", \".dq-btn-run-query\", function() {\n    $(\".dq-black-background\").remove();\n    $(\".dataq\").remove();\n    callback(DataQ.build_query(query));\n  });\n\n  // Handle DataQ run policy.\n  $(document).on(\"click\", \".dq-btn-run-policy\", function() {\n    // Build policy object\n    \n\n    // Close DataQ\n    $(\".dq-black-background\").remove();\n    $(\".dataq\").remove();\n\n    // Build policy string\n    callback(DataQ.build_policy(policy));\n  });\n})();\n","/**\n * Helper for accessing DataQ API.\n */\n(function() {\n  // Create the global DataQ object if it doesn't exist.\n  window.DataQ = window.DataQ || {};\n\n  DataQ.API = {};\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_repos = function(callback) {\n    $.get(\"/apps/dataq/api/\", callback);\n  };\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_tables = function(repo, callback) {\n    $.get(\"/apps/dataq/api/\" + repo + \"/\", callback);\n  };\n\n  // See dataq/views.py for description of result.\n  DataQ.API.get_schema = function(repo, table, callback) {\n    $.get(\"/apps/dataq/api/\" + repo + \"/\" + table + \"/\", callback);\n  };\n})();\n","/**\n * The modal window that allows the user to create filters (of the form <val1> <operation> <val2>).\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed. This is executed as callback().\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to execute when the filter has been added. It will be executed\n   *                   as cb().\n   */\n  DataQ.FilterModal = function(q, cb) {\n    // Set the instance variables.\n    callback = cb;\n    query = q;\n\n    // If the modal does not exist, create it.\n    var modal = $(\"#dq-filter-modal\");\n    if (modal.length === 0) {\n      var columns = [];\n\n      // Iterate through each column of each selected table and add to the list of columns.\n      query.get_selected_tables().forEach(function(selected_table) {\n        query.schema(selected_table).forEach(function(column) {\n          columns.push({\n            \"column_name\": column[0],\n            \"table_name\": selected_table,\n            \"full_name\": selected_table + \".\" + column[0]\n          });\n        });\n      });\n\n      // Create the HTML for the filter modal.\n      var html = DataQ.templates['dq-filter-modal']({\n        \"columns\": columns,\n        \"repo\": query.repo()\n      });\n\n      // Add the modal to the page.\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc)\n    $('#dq-filter-modal').modal({\n      keyboard: false\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      callback();\n      $(\"#dq-filter-modal\").remove();\n    })\n  } // End FilterModal\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-filter-quit\", function() {\n    callback();\n    $(\"#dq-filter-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n  // Handle modal done.\n  $(document).on(\"click\", \".dq-filter-done\", function() {\n    var filter1 = $('.dq-filter-1-text').val();\n    var filter2 = $('.dq-filter-2-text').val();\n    var op = $('.dq-filter-op-text').val();\n    if (filter1.length > 0 && filter2.length > 0 && op.length > 0) {\n      query.add_filter(filter1, op, filter2);\n      callback();\n      $('#dq-filter-modal').modal('hide');\n      $(\"#dq-filter-modal\").remove();\n      $(\".modal-backdrop\").remove();\n    } else {\n      alert(\"You need to fill out the three text boxes\");\n    }\n  });\n\n  // Handle filter1 dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-1-link\", function() {\n    $('.dq-filter-1-text').val($(this).html());\n  });\n\n  // Handle filter2 dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-2-link\", function() {\n    $('.dq-filter-2-text').val($(this).html());\n  });\n\n  // Handle operation dropdown link click (in <filter1> <operation> <filter2>)\n  $(document).on(\"click\", \".dq-filter-op-link\", function() {\n    // Unescape html for > and <\n    var op = $(this).html().replace(\"&gt;\", \">\").replace(\"&lt;\", \"<\");\n    $('.dq-filter-op-text').val(op);\n  });\n\n})();\n","/***\n * The modal window for editing the groupings.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed. This is executed as callback().\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to execute when the grouping has been modified. It will be executed\n   *              as cb().\n   */\n  DataQ.GroupingModal = function(q, cb) {\n    // Set the instance variables.\n    query = q;\n    callback = cb;\n\n    // Create the modal HTML if it doesn't exist.\n    var modal = $(\"#dq-grouping-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates[\"dq-grouping-modal\"]({\n        columns: query.grouping()\n      });\n      $(\"body\").append(html);\n    }\n\n    // Display the modal. (disable Esc)\n    $(\"#dq-grouping-modal\").modal({\n      keyboard: false\n    });\n\n    // When the modal is displayed, enable HTML5Sortable.\n    $(\"#dq-grouping-modal\").on(\"shown.bs.modal\", function() {\n      $(\".dq-grouping-modal-list\").sortable({\n        forcePlaceholderSize: true\n      });\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      callback();\n      $(\"#dq-grouping-modal\").remove();\n      $(\".modal-backdrop\").remove();\n    })\n  }; // End GroupingModal\n\n  // Handler for close modal.\n  $(document).on(\"click\", \"#dq-grouping-modal-quit-btn\", function() {\n    callback();\n    $(\"#dq-grouping-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n  // Handle for finishing edits.\n  $(document).on(\"click\", \"#dq-grouping-modal-done-btn\", function() {\n    var new_grouping = [];\n\n    // Iterate through each list item (in order).\n    $(\".dq-grouping-list-item\").each(function() {\n      var li = $(this);\n      var string = li.data(\"string\");\n      var grouping = query.grouping();\n      var current_group;\n\n      // Find the group associated with this list item.\n      query.grouping().forEach(function(group) {\n        if (group.string === string) {\n          current_group = group;\n        }\n      });\n\n      new_grouping.push(current_group);\n    });\n\n    query.grouping(new_grouping);\n    callback();\n    $(\"#dq-grouping-modal\").remove();\n    $(\".modal-backdrop\").remove();\n  });\n\n})();\n","/**\n * Helper function to return the next aggregate for a given column type.\n * ex. max, min, avg, sum, count.\n *\n * This is useful to cycle through the possible aggregates as user clicks a column.\n * col1 --click--> max(col1) --click--> min(col1), etc.\n */\n(function() {\n  window.DataQ = window.DataQ || {};\n\n  // Numeric types supported in PostgreSQL.\n  var number_types = [\"bigint\", \"int8\", \"bigserial\", \"serial8\", \"double precision\", \"float8\", \n    \"integer\", \"int\", \"int4\", \"real\", \"float4\", \"smallint\", \"int2\", \"serial\", \"serial4\"];\n\n  // Turn number_types into a Javascript object for more efficient lookup.\n  // key = PostgreSQL type, val = true iff key is a numeric type.\n  var is_number = {};\n  for (var i = 0; i < number_types; i++) {\n    is_number[number_types[i]] = true;\n  }\n\n  // Helper for cycling through numeric aggregates.\n  // key = aggregate, value = next aggregate\n  // For example, a cycle (beginning at none) would be:\n  // none -> max -> min -> sum -> count -> avg -> none\n  var next_numeric_aggregate = {\n    \"none\": \"max\",\n    \"max\": \"min\",\n    \"min\": \"sum\",\n    \"sum\": \"count\",\n    \"count\": \"avg\",\n    \"avg\": \"none\"\n  };\n\n  // Helper for cycling through non-numeric aggregates.\n  var next_nonnumeric_aggregate = {\n    \"none\": \"count\",\n    \"count\": \"none\"\n  };\n\n\n  /**\n   * Determine the next aggregate in the cycle for the given column type and the current aggregate.\n   *\n   * @param columntype - The PostgreSQL type of the column.\n   * @param current_aggregate - The current aggregate operator applied to the column (ex. max, min)\n   *                            If no aggregate has been applied, this value should be either null\n   *                            or \"none\".\n   * @return  The next aggregate in the cycle.\n   */\n  DataQ.next_aggregate = function(columntype, current_aggregate) {\n    if (current_aggregate === null) {\n      current_aggregate = \"none\";\n    }\n    if (is_number[columntype]) {\n      return next_numeric_aggregate[current_aggregate];\n    } else {\n      return next_nonnumeric_aggregate[current_aggregate];\n    }\n  };\n})();\n","/**\n * Logic for constructing a SQL query string from a DataQ.Query object.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  /**\n   * Take a DataQ.Query object and generate a SQL query string from it.\n   *\n   * @param query - The DataQ.Query object.\n   * @return A String representing the SQL query.\n   */\n  window.DataQ.build_query = function(query) {\n\n    // The list of columns to select.\n    var select_list = [];\n\n    // The list of tables to select from.\n    var from_list = [];\n\n    // The filters to apply.\n    var where_list = [];\n\n    // The grouping to perform.\n    var group_list = [];\n\n    // The sorting to apply.\n    var order_list = [];\n\n    // Get the current repo name - we'll need to prepend this to some of the table/column names.\n    var repo = query.repo();\n\n    // Create the FROM clause. It is simply the list of tables that the user has selected.\n    // Each item in the list is a String of the form: \"repo.table\".\n    query.get_selected_tables().forEach(function(table) {\n      from_list.push(repo + \".\" + table);\n    });\n\n    // Create the SELECT clause.\n    // Iterate through every selected column of every selected table and add the column to the \n    // select list (and write the aggregate if possible).\n    query.get_selected_tables().forEach(function(table) {\n      query.selected_columns(table).forEach(function(column) {\n        if (column.agg === undefined || column.agg === null || column.agg === \"none\") {\n          select_list.push(repo + \".\" + table + \".\" + column.name);\n        } else {\n          // When an aggregate \"agg\" on column \"col\" in table \"table\" and repo \"repo\" appears, mark\n          // \"agg(repo.table.col) as agg_table_col\".\n          select_list.push(column.agg + \"(\" + repo + \".\" + table + \".\" + column.name + \")\" + \n            \" as \" + column.agg + \"_\" + table + \"_\"\n            + column.name);\n        }\n      });\n    });\n\n    // Create the WHERE clause.\n    // Simply iterate through each filter and add it to the list.\n    query.get_filters().forEach(function(filter) {\n      where_list.push(filter.filter1 + \" \" + filter.op + \" \" + filter.filter2);\n    });\n\n    // Create the  GROUP BY clause.\n    query.grouping().forEach(function(group) {\n      var agg = group.column.agg;\n\n      // We can only add a group by if it's not the aggregate column.\n      if (agg === null || agg === undefined || agg === \"none\") {\n        group_list.push(repo + \".\" + group.string);\n      } \n    });\n\n    // Create the ORDER BY clause.\n    query.sorts().forEach(function(sort) {\n      var agg = sort.column.agg;\n      if (agg === null || agg === undefined || agg === \"none\") {\n        order_list.push(repo + \".\" + sort.string);\n      } else {\n        order_list.push(agg + \"_\" + sort.table + \"_\" + sort.column.name);\n      }\n    });\n\n    // Set the query string.\n    if (select_list.length === 0) {\n      return \"\";\n    }\n    var query_string = \"SELECT \" + select_list.join(\", \")\n        + \" FROM \" + from_list.join(\", \");\n\n    // Set the where list.\n    if (where_list.length > 0) {\n      query_string +=  \" WHERE \" + where_list.join(\" AND \");\n    }\n\n    // Set the group list.\n    if (group_list.length > 0) {\n      query_string += \" GROUP BY \" + group_list.join(\", \")\n    }\n\n    // Set the order list.\n    if (order_list.length > 0) {\n      query_string += \" ORDER BY \" + order_list.join(\", \")\n    }\n\n    // Remove leading and trailing spaces and then append semicolon.\n    query_string.trim();\n    query_string += \";\";\n    return query_string;\n  };\n})();\n","/**\n * The object for building a query.\n */\n(function() {\n  // If the DataQ object doesn't exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  DataQ.Query = function() {\n    \n    // Create the object and initialize the objects.\n    var that = {};\n    that._schema_for_table_name = {};\n    that._repo_name = null;\n    that._operated_column = null;\n    that._selected_columns_for_table = {};\n    that._filter_for_code = {};\n    that._grouping = [];\n    that._sorts = {};\n\n    /**\n     * Get or set the schema for a given table.\n     *\n     * @param table_name - The name of the table.\n     * @param schema - If this argument is omitted (or undefined), this function acts as a getter\n     *                returning the schema for the given table. Otherwise, the function acts as a \n     *                setter, setting the schema for table_name.\n     *\n     * @return The schema for the table name.\n     */\n    that.schema = function(table_name, schema) {\n      if (schema !== undefined) {\n        that._schema_for_table_name[table_name] = schema;\n      } \n      return that._schema_for_table_name[table_name];\n    };\n\n    /**\n     * Get or set the repo name.\n     *\n     * @param repo_name - If this argument is omitted (or undefined), this function acts as a \n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the repo.\n     */\n    that.repo = function(repo_name) {\n      if (repo_name !== undefined) {\n        that._repo_name = repo_name;\n      }\n      return that._repo_name;\n    };\n\n    /**\n     * Get or set the operated column.\n     *\n     * @param operated_column - if this argument is omitted (or undefined), this function acts as\n     *                          a getter. Otherwise, it acts a setter, setting the operated column.\n     *\n     * @return The name of the operated column (\"table.col\"). This may be null.\n     */\n    that.operated_column = function(operated_column) {\n      if (operated_column !== undefined) {\n        that._operated_column = operated_column;\n      }\n      return that._operated_column;\n    };\n\n    that.update_grouping = function() {\n      that._grouping = [];\n      var has_operated_column = false;\n      for (var table in that._selected_columns_for_table) {\n        if (!that._selected_columns_for_table[table]) {\n          continue;\n        }\n        that._selected_columns_for_table[table].forEach(function(column) {\n          if (column.agg === \"none\") {\n            that._grouping.push({\n              \"string\": table + \".\" + column.name,\n              \"table\": table,\n              \"column\": column\n            });\n          } else {\n            has_operated_column = true;\n          }\n        });\n      } // end for each table\n\n      if (!has_operated_column) {\n        that._grouping = [];\n      }\n    };\n\n    that.selected_columns = function(table_name, selected_columns) {\n      if (selected_columns !== undefined) {\n        that._selected_columns_for_table[table_name] = selected_columns;\n      }\n      return that._selected_columns_for_table[table_name];\n    };\n\n    that.get_selected_tables = function() {\n      var tbls = [];\n      for (var k in that._selected_columns_for_table) {\n        tbls.push(k);\n      }\n      return tbls;\n    };\n\n    that.add_filter = function(filter1, op, filter2) {\n      var filter_string = filter1 + \" \" + op + \" \" + filter2;\n      var code = md5((new Date()).getTime() + filter_string);\n      that._filter_for_code[code] = {\n        \"filter1\": filter1,\n        \"op\": op,\n        \"filter2\": filter2,\n        \"filter_string\": filter_string\n      };\n      return that._filter_for_code[code];\n    };\n\n    that.delete_filter = function(code) {\n      that._filter_for_code[code] = undefined;\n    };\n\n    that.get_filters = function() {\n      var result = [];\n      for (var k in that._filter_for_code) {\n        var filter = that._filter_for_code[k];\n        if (!filter) {\n          continue;\n        }\n        result.push({\n          \"code\": k,\n          \"filter1\": filter.filter1,\n          \"op\": filter.op,\n          \"filter2\": filter.filter2,\n          \"filter_string\": filter.filter_string\n        });\n      };\n      return result;\n    };\n\n    that.grouping = function(grouping) {\n      if (grouping !== undefined) {\n        that._grouping = grouping;\n      }\n      return that._grouping;\n    };\n\n    that.add_sort = function(sort) {\n      that._sorts[sort.string] = sort;\n    };\n\n    that.delete_sort = function(sort) {\n      that._sorts[sort] = undefined;\n    };\n\n    that.sorts = function() {\n      var result = [];\n      for (var k in that._sorts) {\n        if (that._sorts[k] !== undefined) {\n          result.push(that._sorts[k]);\n        }\n      }\n      return result;\n    };\n\n    return that;\n  };\n})();\n","/**\n* Logic for constructing a PostgreSQL CREATE POLICY command from a\n* DataQ.policy object.\n*/\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  /**\n   * Take a DataQ.Policy object and generate a CREATE POLICY string from it.\n   * A CREATE POLICY command looks like:\n   *\n   * CREATE POLICY name ON table_name\n   *    [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]\n   *    [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]\n   *    [ USING ( using_expression ) ]\n   *    [ WITH CHECK ( check_expression ) ]\n   *\n   * see https://www.postgresql.org/docs/9.5/static/sql-createpolicy.html\n   *\n   * @param policy - The DataQ.policy object.\n   * @return A String representing the CREATE POLICY command.\n   */\n  window.DataQ.build_policy = function(policy) {\n\n    // Name of policy to be created.\n    var policy_name = policy.name();\n\n    // Name of table to which the policy applies.\n    var table_name = policy.repo() + \".\" + policy.name();\n\n    // Command to which the policy applies.\n    var command = policy.command();\n\n    // List of roles to which the policy applies.\n    var role_list = policy.roles();\n\n    // SQL conditional expression to control row visibility.\n    // Rows for which the expression returns true will be visible.\n    var using_expr = policy.using_expression();\n\n    // SQL conditional expression to control INSERT and UPDATE privileges.\n    // Only rows for which the expression evaluates to true will be allowed.\n    var check_expr = policy.check_expression();\n\n    /* Build policy string */\n    var policy_string = \"CREATE POLICY \" + policy_name;\n\n    // ON clause\n    policy_string += \" ON \" + table_name;\n\n    // FOR clause\n    policy_string += \" FOR \" + command;\n\n    // TO clause\n    policy_string += \" TO \" + role_list.join(\", \");\n\n    // USING clause\n    policy_string += \" USING \" + using_expr;\n\n    // WITH CHECK clause\n    if (command !== \"SELECT\") {\n      // A SELECT policy cannot have a WITH CHECK expression, as it only applies\n      // in cases where records are being retrieved from the relation.\n      policy_string += \" WITH CHECK \" + check_expr;\n    }\n\n    // Remove leading and trailing spaces and then append semicolon.\n    policy_string.trim();\n    policy_string += \";\";\n\n    return policy_string;\n\n  };\n})();\n","/**\n* The object for building a row-level security policy.\n*/\n(function() {\n  // If the DataQ object doesn't exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  DataQ.Policy = function() {\n\n    // Create the object and initialize its contents.\n    var that = {};\n    that._repo_name = null;\n    that._name = null;\n    that._table = null;\n    that._command = null;\n    that._roles = [];\n    that._using_expr = null;\n    that._check_expr = null;\n\n    /**\n     * Get or set the repo name.\n     *\n     * @param repo_name - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the repo.\n     */\n    that.repo = function(repo_name) {\n      if (repo_name !== undefined) {\n        that._repo_name = repo_name;\n      }\n      return that._repo_name;\n    };\n\n    /**\n     * Get or set the name of the policy.\n     *\n     * @param policy_name - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the policy.\n     */\n    that.name = function(policy_name) {\n      if (policy_name !== undefined) {\n        that._name = policy_name;\n      }\n      return that._name;\n    };\n\n    /**\n     * Get or set the name of the table to which this policy will apply.\n     *\n     * @param table_name - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the table.\n     */\n    that.table = function(table_name) {\n      if (table_name !== undefined) {\n        that._table = table_name;\n      }\n      return that._table;\n    };\n\n    /**\n     * Get or set the command { ALL | SELECT | INSERT| UPDATE | DELETE } to which\n     * this policy will apply.\n     *\n     * @param cmd - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The name of the command.\n     */\n    that.command = function(cmd) {\n      if (cmd !== undefined) {\n        that._command = cmd;\n      }\n      return that._command;\n    };\n\n    /**\n     * Get or set the Roles to which this policy will apply.\n     *\n     * @param role_list - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The list of Roles.\n     */\n    that.roles = function(role_list) {\n      if (role_list !== undefined) {\n        if (!(role_list instanceof Array)) {\n          role_list = [role_list]\n        }\n        that._roles = role_list;\n      }\n      return that._roles;\n    };\n\n    /**\n     * Get or set the policy's using_expression.\n     *\n     * @param expr - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The full using_expression.\n     */\n    that.using_expression = function(expr) {\n      if (expr !== undefined) {\n        that._using_expr = expr;\n      }\n      return that._using_expr;\n    };\n\n    /**\n     * Get or set the policy's check_expression.\n     *\n     * @param expr - If this argument is omitted (or undefined), this function acts as a\n     *                    getter. Otherwise, it acts as a setter, setting the repo name.\n     *\n     * @return The full check_expression.\n     */\n    that.check_expression = function(expr) {\n      if (expr !== undefined) {\n        that._check_expr = expr;\n      }\n      return that._check_expr;\n    };\n\n    return that;\n\n  };\n})();\n","/**\n * The modal window that allows the user to specify the sorting of the columns.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger whent he modal is closed.\n  var callback;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   * @param cb - The callback to trigger when the user finishes specifying sorts. It is executed as\n   *              cb().\n   */\n  DataQ.SortModal = function(q, cb) {\n    // Set the instance variables.\n    query = q;\n    callback = cb;\n\n    // If the modal HTML does not exist, add it to the page.\n    var modal = $(\"#dq-sort-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates['dq-sort-modal']();\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc)\n    $('#dq-sort-modal').modal({\n      keyboard: false\n    });\n\n    // When the modal is shown, populate the columns.\n    $(\"#dq-sort-modal\").on(\"shown.bs.modal\", function() {\n      update_list();\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      $(\"#dq-sort-modal\").remove();\n      $(\".modal-backdrop\").remove();\n      callback();\n    })\n  };\n\n  // Handle dropdown item click.\n  $(document).on(\"click\", \".dq-sort-modal-dropdown-btn\", function() {\n    var list = $('.dq-sort-modal-dropdown');\n    list.html(\"\");\n\n    // When a dropdown link is clicked, add it to the list of selected columns.\n    create_items_in_dropdown().forEach(function(item) {\n      list.append(DataQ.templates[\"dq-sort-dropdown-li\"]({\n        \"item\": item\n      }));\n    });\n\n  });\n\n  // Create the items in the sort dropdown menu.\n  var create_items_in_dropdown = function() {\n    // Identify the sorts that have already been used.\n    var used_dict = {};\n    query.sorts().forEach(function(sort) {\n      used_dict[sort.string] = true;\n    });\n\n    // Iterate through every column of every selected table and, if it isn't used, add it to the\n    // list of items.\n    var items = [];\n    query.get_selected_tables().forEach(function(selected_table) {\n      query.selected_columns(selected_table).forEach(function(column) {\n        var string = selected_table + \".\" + column.name;\n        if (column.agg !== \"none\") {\n          string = column.agg + \"(\" + selected_table + \".\" + column.name + \")\";\n        }\n\n        // Don't add any item to dropdown if it's already used.\n        if (used_dict[string]) {\n          return;\n        }\n\n        items.push({\n          \"string\": string,\n          \"table\": selected_table,\n          \"column\": column\n        });\n\n      });\n    });\n\n    return items;\n  };\n\n  // Add the list items to list of used sorts.\n  var update_list = function() {\n    var list = $('.dq-sort-item-list');\n    list.html(\"\");\n    query.sorts().forEach(function(sort) {\n      var html = DataQ.templates[\"dq-sort-list-item\"]({\n        \"item\": sort\n      });\n      list.append(html);\n    });\n  };\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-sort-modal-quit\", function() {\n    $('#dq-sort-modal').remove();\n    $(\".modal-backdrop\").remove();\n    callback();\n  });\n\n  // Handle sort clicked.\n  $(document).on(\"click\", \".dq-sort-link\", function() {\n    var li = $(this);\n    var name = li.data(\"columnname\");\n    var type = li.data(\"columntype\");\n    var agg = li.data(\"aggregate\");\n    var table = li.data(\"table\");\n    var string = li.data(\"string\");\n\n    if (agg === undefined || agg === null) {\n      agg = \"none\";\n    }\n\n    var item = {\n      \"column\": {\n        \"name\": name,\n        \"type\": type,\n        \"agg\": agg\n      },\n      \"string\": string,\n      \"table\": table\n    };\n\n    query.add_sort(item);\n    update_list();\n\n  });\n\n  // Handle modal close.\n  $(document).on(\"click\", \".dq-sort-modal-done-btn\", function() {\n    $('#dq-sort-modal').remove();\n    $(\".modal-backdrop\").remove();\n    callback();\n  });\n\n  // Handle delete sort.\n  $(document).on(\"click\", \".dq-sort-delete-btn\", function() {\n    var li = $(this).parent().parent();\n    var string = li.data(\"string\");\n    query.delete_sort(string);\n    update_list();\n  });\n\n})();\n","/**\n * The modal window that allows the user to select the tables (and columns from these tables) that\n * they want to use in their query.\n *\n * This modal can be used to either ADD a new table (and some of its columns) to the query or to\n * EDIT an existing table (and its columns) that is already in the query.\n *\n * The modal window is a bootstrap modal.\n */\n(function() {\n  // If the global DataQ object does not exist, create it.\n  window.DataQ = window.DataQ || {};\n\n  // The callback to trigger when the modal is closed.\n  var cb;\n\n  // The table we have selected.\n  var table;\n\n  // The DataQ.Query object being built.\n  var query;\n\n  /**\n   * Launch the modal.\n   *\n   * @param q - The DataQ.Query object being built.\n   *\n   * @param table_name - The name of the table to modify. This must be either null (in which case\n   * the \"Add Table\" modal is displayed), or a table which the current user is associated with.\n   *\n   * @param callback - The callback that is executed after the user finishes updating selections.\n   * It is executed as callback()\n   */\n  DataQ.TableModal = function(q, table_name, callback) {\n    table = table_name;\n    cb = callback;\n    query = q;\n\n    // If the modal HTML does not exist, add it to the page.\n    var modal = $(\"#dq-table-modal\");\n    if (modal.length === 0) {\n      var html = DataQ.templates['dq-table-modal']({\n        \"table_name\": table\n      });\n      $('body').append(html);\n    }\n\n    // Display the modal (disable Esc and clicking the backdrop to exit modal)\n    $('#dq-table-modal').modal({\n      keyboard: false\n    });\n\n    // Don't allow clicking Done until the user selects a table.\n    $(\".dq-modal-done-btn\").hide();\n\n    // When the modal is shown, populate the columns.\n    $(\"#dq-table-modal\").on(\"shown.bs.modal\", function() {\n      if (table) {\n        populate_column_list(table);\n      }\n    });\n\n    // Handle modal close when clicking backdrop.\n    $(\".modal-backdrop\").click(function() {\n      $(\"#dq-table-modal\").remove();\n      $(\".modal-backdrop\").remove();\n      cb();\n    })\n  };\n\n  // If the user quits, trigger the callback.\n  $(document).on('click', '.dq-modal-quit', function() {\n    $(\"#dq-table-modal\").remove();\n    $(\".modal-backdrop\").remove();\n    cb();\n  });\n\n\n  // If the user clicks the table dropdown, populate the list with the list\n  // of available tables.\n  $(document).on(\"click\", \".dq-modal-dropdown-btn\", function() {\n    var dropdown = $(\".dq-modal-dropdown\");\n    dropdown.html(\"\");\n    DataQ.API.get_tables(query.repo(), function(data) {\n      data.tables.forEach(function(table) {\n        var html = DataQ.templates[\"dq-modal-dropdown-item\"]({\n          \"item_name\": table\n        });\n        dropdown.append(html);\n      }); // end foreach\n    }) // get_tables\n  }); // document on click\n\n\n  // When a table is selected from the dropdown, create the column list.\n  $(document).on(\"click\", \".dq-modal-dropdown-link\", function() {\n    // Set the content of the dropdown.\n    var item_name = $(this).data(\"item_name\");\n    table = item_name;\n    $('.dq-modal-table-dropdown-text').text(table);\n    populate_column_list();\n  });\n\n  // Populate the list of columns with the schema of the given table.\n  var populate_column_list = function() {\n    // Get the schema for the selected tables.\n    DataQ.API.get_schema(query.repo(), table, function(data) {\n      $(\".dq-modal-done-btn\").show();\n\n      // Sort the columns by name.\n      query.schema(table, data.schema).sort(function(a, b) {return a[0] > b[0]});\n\n      // Create the HTML and add it to the UI.\n      var html = DataQ.templates[\"dq-modal-columns\"]({\n        \"columns\": query.schema(table)\n      });\n      $('.dq-column-list').html(html);\n\n      if (query.selected_columns(table)) {\n        // Iterate through the columns for the selected table.\n        query.selected_columns(table).forEach(function(column) {\n          // Extract the data entries from the element (we find the element by selecting\n          // .dq-modal-column[data-columnname=\"colname\"]\n          var element = $('.dq-modal-column[data-columnname=\"'+column.name+'\"]');\n          element.data(\"columnname\", column.name);\n          element.data(\"columntype\", column.type);\n          element.data(\"currentaggregate\", column.agg || \"none\")\n          element.find(\"input[type=checkbox]\").prop('checked', true);\n          if (column.agg !== \"none\") {\n            element.find(\"button\").text(column.agg + \"(\" + column.name + \")\");\n          }\n        }); // end forEach\n      } // if selected columns\n    });\n  };\n\n  // Handle column aggregate trigger.\n  $(document).on(\"click\", \".dq-modal-column button\", function() {\n    // Extract the data entries.\n    var parent_li = $(this).parent();\n    var columnname = parent_li.data(\"columnname\");\n    var columntype = parent_li.data(\"columntype\");\n    var currentaggregate = parent_li.data(\"currentaggregate\")\n\n    // Compute the next aggregate operator to apply.\n    var nextaggregate = DataQ.next_aggregate(columntype, currentaggregate);\n\n    // If an aggregate has already been applied, don't apply another.\n    if (query.operated_column() !== table + \".\" + columnname && query.operated_column() !== null) {\n      nextaggregate = \"none\";\n    }\n\n    // If the aggregate has been turned off, turn off the operated column.\n    // Else if this is the new operated column, indicate so.\n    if (nextaggregate === \"none\") {\n      if (query.operated_column() === table + \".\" + columnname) {\n        query.operated_column(null);\n      }\n      $(this).text(columnname);\n    } else {\n      $(this).text(nextaggregate + \"(\" + columnname + \")\");\n      query.operated_column(table + \".\" + columnname);\n    }\n    parent_li.data(\"currentaggregate\", nextaggregate);\n  });\n\n  // When this is clicked, return the selected columns.\n  $(document).on(\"click\", \".dq-modal-done-btn\", function() {\n    // Figure out the selected columns.\n    var columns = [];\n    var is_op_col_checked = false;\n\n    // Iterate through each of the columns.\n    $('.dq-modal-column').each(function() {\n      var li = $(this);\n\n      // If the column is checked.\n      if (li.find(\"input\").is(\":checked\")) {\n\n        // Extract the data entries.\n        var agg = li.data(\"currentaggregate\");\n        var type = li.data(\"columntype\");\n        var name = li.data(\"columnname\");\n\n        // If the operated column has been selected, then mark it so.\n        if (table + \".\" + name === query.operated_column()) {\n          is_op_col_checked = true;\n        }\n\n        if (agg === null || agg === undefined) {\n          agg = \"none\";\n        }\n\n        columns.push({\n          \"name\": name,\n          \"type\": type,\n          \"agg\": agg\n        });\n\n      }\n    });\n\n    // If the operated column should be in this table and has not been selected,\n    // set the operated column as null.\n    if (query.operated_column() &&\n        query.operated_column().split(\".\")[0] === table &&\n        !is_op_col_checked) {\n      query.operated_column(null);\n    }\n\n    // Mark the selected columns for this table, and recompute the tables.\n    query.selected_columns(table, columns);\n    query.update_grouping();\n\n    // Remove the modals from the page.\n    $(\"#dq-table-modal\").remove();\n    $(\".modal-backdrop\").remove();\n    cb();\n  });\n\n})();\n"],"sourceRoot":"/source/"}
\ No newline at end of file
diff --git a/src/browser/static/dataq/templates.js b/src/browser/static/dataq/templates.js
index 2fb62941..23b40789 100644
--- a/src/browser/static/dataq/templates.js
+++ b/src/browser/static/dataq/templates.js
@@ -1,5 +1,64 @@
 this["DataQ"] = this["DataQ"] || {};
 this["DataQ"]["templates"] = this["DataQ"]["templates"] || {};
+this["DataQ"]["templates"]["dataq-container-rls-policy"] = Handlebars.template({"1":function(depth0,helpers,partials,data,depths) {
+  var lambda=this.lambda, escapeExpression=this.escapeExpression;
+  return "          <li><a href=\"#\" class=\"dq-filter-1-link\" data-columnname=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.column_name : depth0), depth0))
+    + "\" data-reponame=\""
+    + escapeExpression(lambda((depths[1] != null ? depths[1].repo : depths[1]), depth0))
+    + "\" data-tablename=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.table_name : depth0), depth0))
+    + "\">"
+    + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0))
+    + "</a></li>\n";
+},"3":function(depth0,helpers,partials,data,depths) {
+  var lambda=this.lambda, escapeExpression=this.escapeExpression;
+  return "          <li><a href=\"#\" class=\"dq-filter-2-link\" data-columnname=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.column_name : depth0), depth0))
+    + "\" data-reponame=\""
+    + escapeExpression(lambda((depths[1] != null ? depths[1].repo : depths[1]), depth0))
+    + "\" data-tablename=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.table_name : depth0), depth0))
+    + "\">"
+    + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0))
+    + "</a></li>\n";
+},"5":function(depth0,helpers,partials,data,depths) {
+  var lambda=this.lambda, escapeExpression=this.escapeExpression;
+  return "             <li><a href=\"#\" class=\"dq-filter-1-link\" data-columnname=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.column_name : depth0), depth0))
+    + "\" data-reponame=\""
+    + escapeExpression(lambda((depths[1] != null ? depths[1].repo : depths[1]), depth0))
+    + "\" data-tablename=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.table_name : depth0), depth0))
+    + "\">"
+    + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0))
+    + "</a></li>\n";
+},"7":function(depth0,helpers,partials,data,depths) {
+  var lambda=this.lambda, escapeExpression=this.escapeExpression;
+  return "             <li><a href=\"#\" class=\"dq-filter-2-link\" data-columnname=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.column_name : depth0), depth0))
+    + "\" data-reponame=\""
+    + escapeExpression(lambda((depths[1] != null ? depths[1].repo : depths[1]), depth0))
+    + "\" data-tablename=\""
+    + escapeExpression(lambda((depth0 != null ? depth0.table_name : depth0), depth0))
+    + "\">"
+    + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0))
+    + "</a></li>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data,depths) {
+  var stack1, buffer = "<!-- HTML file containing an extension of the overall DataQ interface.\n     This interface is for building DataQ.Policy objects. -->\n\n<!-- Create a translucent black background -->\n<div class=\"dq-black-background\"></div>\n\n<!-- The DataQ container -->\n<div class=\"dataq container-fluid\">\n\n   <h4>\n   DataQ Policy Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface.\n   </h4>\n\n<div class=\"panel-group\">\n   <div class=\"panel panel-default\">\n      <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseOne\">\n        <h4 class=\"panel-title\">\n          Policy Name\n        </h4>\n      </div>\n   </div>\n   <div id=\"collapseOne\" class=\"panel-collapse collapse in\">\n     <div class=\"panel-body\">\n        <input id=\"dq-policy-name\" type=\"text\" class=\"form-control\" placeholder=\"Policy Name\" />\n     </div>\n   </div>\n\n   <div class=\"panel panel-default\">\n      <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseTwo\">\n        <h4 class=\"panel-title\">\n          Allowed Commands\n        </h4>\n      </div>\n   </div>\n   <div id=\"collapseTwo\" class=\"panel-collapse collapse in\">\n     <div class=\"panel-body\">\n       <div class=\"dropdown\" id=\"dq-policy-commands-dropdown\">\n          <button class=\"btn btn-primary dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\">Choose command\n          <span class=\"caret\"></span></button>\n          <ul class=\"dropdown-menu\">\n           <li><a href=\"#\">SELECT</a></li>\n           <li><a href=\"#\">INSERT</a></li>\n           <li><a href=\"#\">UPDATE</a></li>\n           <li><a href=\"#\">DELETE</a></li>\n           <li><a href=\"#\">ALL</a></li>\n          </ul>\n       </div>\n     </div>\n   </div>\n\n   <div class=\"panel panel-default\">\n      <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseThree\">\n         <h4 class=\"panel-title\">\n            Select role(s) this policy will apply to:\n         </h4>\n      </div>\n   </div>\n   <div id=\"collapseThree\" class=\"panel-collapse collapse in\">\n     <div class=\"panel-body\">\n       <input id=\"dq-policy-role-list\" type=\"text\" class=\"form-control\" placeholder=\"Comma separated role names. e.g. CURRENT_USER, admin, myGroupRole\" />\n     </div>\n   </div>\n\n   <div class=\"panel panel-default\">\n      <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseFour\">\n        <h4 class=\"panel-title\">\n          USING expression: Rows for which this expression returns true will be visible.\n        </h4>\n      </div>\n   </div>\n   <div id=\"collapseFour\" class=\"panel-collapse collapse in\">\n     <div class=\"panel-body\">\n        <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->\n   <div class=\"input-group col-xs-4\">\n      <!-- Text box for entering the filter text -->\n      <input type=\"text\" class=\"form-control dq-filter-1-text\" placeholder=\"table1.col1 * 3\">\n      <!-- Dropdown for selecting a column to add to filter -->\n      <div class=\"input-group-btn\">\n        <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n        <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n";
+  stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.columns : depth0), {"name":"each","hash":{},"fn":this.program(1, data, depths),"inverse":this.noop,"data":data});
+  if (stack1 != null) { buffer += stack1; }
+  buffer += "        </ul>\n      </div>\n   </div> <!-- end filter1 -->\n\n   <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->\n   <div class=\"input-group col-xs-3\">\n      <!-- Text box for entering the operation text -->\n      <input type=\"text\" class=\"form-control dq-filter-op-text\" value=\"=\">\n      <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->\n      <div class=\"input-group-btn\">\n        <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n        <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n          <li><a href=\"#\" class=\"dq-filter-op-link\">&lt</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">&lt=</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">=</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">&gt</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">&gt=</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">LIKE</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">IN</a></li>\n          <li><a href=\"#\" class=\"dq-filter-op-link\">NOT IN</a></li>\n        </ul>\n      </div>\n   </div> <!-- end filterop -->\n\n   <!-- The second dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->\n   <div class=\"input-group col-xs-4\">\n      <!-- Text box for entering the filter text -->\n      <input type=\"text\" class=\"form-control dq-filter-2-text\" placeholder=\"table2.col2+3\">\n      <!-- Dropdown for selecting a column to add to filter -->\n      <div class=\"input-group-btn\">\n        <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n        <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n";
+  stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.columns : depth0), {"name":"each","hash":{},"fn":this.program(3, data, depths),"inverse":this.noop,"data":data});
+  if (stack1 != null) { buffer += stack1; }
+  buffer += "        </ul>\n      </div>\n   </div> <!-- end filter2 -->\n</div>\n</div>\n\n   <div class=\"panel panel-default\">\n      <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseFive\">\n        <h4 class=\"panel-title\">\n          WITH CHECK expression: New rows for which this expression returns true will be allowed.\n        </h4>\n   </div>\n   <div id=\"collapseFive\" class=\"panel-collapse collapse in\">\n     <div class=\"panel-body\">\n        <div class=\"panel-body\">\n           <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->\n      <div class=\"input-group col-xs-4\">\n         <!-- Text box for entering the filter text -->\n         <input type=\"text\" class=\"form-control dq-filter-1-text\" placeholder=\"table1.col1 * 3\">\n         <!-- Dropdown for selecting a column to add to filter -->\n         <div class=\"input-group-btn\">\n           <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n           <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n";
+  stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.columns : depth0), {"name":"each","hash":{},"fn":this.program(5, data, depths),"inverse":this.noop,"data":data});
+  if (stack1 != null) { buffer += stack1; }
+  buffer += "           </ul>\n         </div>\n      </div> <!-- end filter1 -->\n\n      <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->\n      <div class=\"input-group col-xs-3\">\n         <!-- Text box for entering the operation text -->\n         <input type=\"text\" class=\"form-control dq-filter-op-text\" value=\"=\">\n         <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->\n         <div class=\"input-group-btn\">\n           <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n           <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n             <li><a href=\"#\" class=\"dq-filter-op-link\">&lt</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">&lt=</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">=</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">&gt</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">&gt=</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">LIKE</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">IN</a></li>\n             <li><a href=\"#\" class=\"dq-filter-op-link\">NOT IN</a></li>\n           </ul>\n         </div>\n      </div> <!-- end filterop -->\n\n      <!-- The first dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->\n      <div class=\"input-group col-xs-4\">\n         <!-- Text box for entering the filter text -->\n         <input type=\"text\" class=\"form-control dq-filter-2-text\" placeholder=\"table2.col2+3\">\n         <!-- Dropdown for selecting a column to add to filter -->\n         <div class=\"input-group-btn\">\n           <button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\" aria-expanded=\"false\"><span class=\"caret\"></span></button>\n           <ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\">\n";
+  stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.columns : depth0), {"name":"each","hash":{},"fn":this.program(7, data, depths),"inverse":this.noop,"data":data});
+  if (stack1 != null) { buffer += stack1; }
+  return buffer + "           </ul>\n         </div>\n      </div> <!-- end filter2 -->\n   </div>\n\n     </div>\n   </div>\n</div>\n\n   <!-- Button to run query -->\n   <div class=\"row\">\n   <button class=\"btn btn-primary col-xs-3 dq-btn-create-policy col-xs-offset-3\">Create Policy</button>\n   <button class=\"btn btn-default col-xs-3 dq-btn-cancel-create-policy\">Cancel</button>\n   </div>\n</div>\n";
+},"useData":true,"useDepths":true});
 this["DataQ"]["templates"]["dataq-container"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
   return "<!-- HTML file containing the overall DataQ interface -->\n\n<!-- Create a translucent black background -->\n<div class=\"dq-black-background\"></div>\n\n<!-- The DataQ container -->\n<div class=\"dataq container-fluid\">\n\n  <h4>\n    DataQ allows you to write SQL queries using a simple checklist-like user interface.\n  </h4>\n\n  <div class=\"panel-group\">\n  <div class=\"panel panel-default\">\n    <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseOne\">\n      <h4 class=\"panel-title\">\n        Select Columns\n      </h4>\n    </div>\n    <div id=\"collapseOne\" class=\"panel-collapse collapse in\">\n      <div class=\"panel-body\">\n        \n        <button class=\"btn btn-primary dq-btn-add-table\">Add Table</button>\n        \n        <!-- The table section HTML goes inside this div -->\n        <div class=\"dq-table-section-container\">\n          <div class=\"dq-selected-tables\"></div>\n        </div>\n\n      </div>\n    </div>\n  </div>\n  <div class=\"panel panel-default\">\n    <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseTwo\">\n      <h4 class=\"panel-title\">\n        Filter Rows\n      </h4>\n    </div>\n    <div id=\"collapseTwo\" class=\"panel-collapse collapse in\">\n      <div class=\"panel-body\">\n        \n      <!-- Button to add a new filter -->\n      <button class=\"btn btn-primary dq-btn-add-filter\">Add Filter</button>\n\n      <!-- The filter section HTML goes inside this div -->\n      <div class=\"dq-selected-filters\">\n          <ul class=\"dq-filter-list\"></ul>\n      </div>\n\n      </div>\n    </div>\n  </div>\n  <div class=\"panel panel-default\">\n    <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseThree\">\n      <h4 class=\"panel-title\">\n        Group Columns\n      </h4>\n    </div>\n    <div id=\"collapseThree\" class=\"panel-collapse collapse in\">\n      <div class=\"panel-body\">\n        \n        <!-- Button for adding groupings -->\n        <button class=\"btn btn-primary dq-btn-edit-grouping\">Edit Grouping</button>\n\n        <!-- The grouping is displayed this header -->\n        <div class=\"dq-grouping-section-container\">\n          <h4 class=\"dq-grouping-text\">No Groupings...</h4>\n        </div>\n\n      </div>\n    </div>\n  </div>\n  <div class=\"panel panel-default\">\n    <div class=\"panel-heading dq-panel\" data-toggle=\"collapse\" href=\"#collapseFour\">\n      <h4 class=\"panel-title\">\n        Sort Data\n      </h4>\n    </div>\n    <div id=\"collapseFour\" class=\"panel-collapse collapse in\">\n      <div class=\"panel-body\">\n        \n        <!-- Button to add a sorting -->\n        <button class=\"btn btn-primary dq-btn-edit-sorting\">Edit Sorting</button>\n\n        <!-- The sorting is displayed in this header -->\n        <div class=\"dq-sorting-section-container\">\n          <h4 class=\"dq-sorting-text\">No Sorting...</h4>\n        </div>\n\n      </div>\n    </div>\n  </div>\n\n  <!-- Button to run query -->\n  <div class=\"row\">\n    <button class=\"btn btn-primary col-xs-3 dq-btn-run-query col-xs-offset-3\">Query</button>\n    <button class=\"btn btn-default col-xs-3 dq-btn-cancel-query\">Cancel</button>\n  </div>\n</div>\n";
   },"useData":true});
diff --git a/src/browser/static/docs/html/_static/custom.css b/src/browser/static/docs/html/_static/custom.css
new file mode 100644
index 00000000..2a924f1d
--- /dev/null
+++ b/src/browser/static/docs/html/_static/custom.css
@@ -0,0 +1 @@
+/* This file intentionally left blank. */
diff --git a/src/browser/templates/security-policies.html b/src/browser/templates/security-policies.html
index 732131ee..578c4823 100644
--- a/src/browser/templates/security-policies.html
+++ b/src/browser/templates/security-policies.html
@@ -240,8 +240,6 @@ <h4 class="modal-title" id="confirm-modal-title">Update Security Policy</h4>
 <script>
   $(document).on("click", "#btn-dataq-rls-policy", function(e) {
     e.preventDefault();
-    console.log(DataQ);
-    // DataQ.DQ("{{repo}}", function(query) {
     DataQ.DQ_rls_policy("{{repo}}", function(query) {
       $("#txt-sql").val(query);
     });

From b7f9cbf43c43c79d5c047a2ac0f40c20da2e1bc4 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Tue, 30 May 2017 10:49:06 -0400
Subject: [PATCH 09/10] collecting DataQ.Policy fields, still need to submit
 command to DB

---
 src/apps/dataq/client_src/js/dataq.js        | 14 ++++++++++++--
 src/browser/templates/security-policies.html | 10 +++++++---
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/src/apps/dataq/client_src/js/dataq.js b/src/apps/dataq/client_src/js/dataq.js
index 1465b3f4..35619f48 100644
--- a/src/apps/dataq/client_src/js/dataq.js
+++ b/src/apps/dataq/client_src/js/dataq.js
@@ -48,7 +48,7 @@
    * @param repo_name - The name of the repo that DataQ should work on.
    * @param cb - The callback to trigger when the query is built.
    */
-  DataQ.DQ_rls_policy = function(repo_name, cb) {
+  DataQ.DQ_rls_policy = function(repo_name, table_name, cb) {
     // Set the callback.
     callback = cb;
 
@@ -59,6 +59,7 @@
     // Create the policy object and set the repo name.
     policy = DataQ.Policy();
     policy.repo(repo_name);
+    policy.table(table_name);
 
     // Handle DataQ close when clicking backdrop.
     $(".dq-black-background").click(function() {
@@ -205,7 +206,8 @@
   // Handle DataQ run policy.
   $(document).on("click", ".dq-btn-run-policy", function() {
     // Build policy object
-    
+    policy.name($("#dq-policy-name").val());
+    policy.table
 
     // Close DataQ
     $(".dq-black-background").remove();
@@ -214,4 +216,12 @@
     // Build policy string
     callback(DataQ.build_policy(policy));
   });
+
+  // Handle DataQ close
+  $(document).con("click", ".dq-btn-cancel-create-policy", function() {
+    console.log('click');
+    $(".dq-black-background").remove();
+    $(".dataq").remove();
+    callback(null);
+  });
 })();
diff --git a/src/browser/templates/security-policies.html b/src/browser/templates/security-policies.html
index 578c4823..1d93fae0 100644
--- a/src/browser/templates/security-policies.html
+++ b/src/browser/templates/security-policies.html
@@ -16,14 +16,18 @@ <h4 class="inline-block">
   <form role="form" method="POST" action="{% url 'security-policy_query' repo_base repo table %}">
     {% csrf_token %}
     <div class="row">
-    <div class="form-group col-sm-10">
+
+    <div class="col-sm-2">
+      <button class="btn btn-primary btn-xsm" id="btn-dataq-rls-policy" type="button">Row-Level Policy Builder</button>
+    </div>
+
+    <div class="form-group col-sm-7">
       <input id="txt-sql" type="text" class="form-control" name="q" placeholder="Security Policy e.g.: grant SELECT access to OTHERUSER on REPO.TABLE where ID=1" />
     </div>
 
-    <div class="col-sm-2">
+    <div class="col-sm-3">
       <button class="btn btn-primary btn-xsm" id="btn-run" type="submit">Execute</button>
       <button class="btn btn-primary btn-xsm modal-upload-dialog" type="button" target-modal="#security-policy-modal" title="New Policy"> New Table-Level Policy </button>
-      <button class="btn btn-primary btn-xsm" id="btn-dataq-rls-policy" type="button">Row-Level Policy Builder</button>
     </div>
 
     </div>

From 7d91a4736b6e4524fdf5d3d222b5ee56b627b870 Mon Sep 17 00:00:00 2001
From: Roberto Soto <robsoto@mit.edu>
Date: Thu, 1 Jun 2017 14:13:15 -0400
Subject: [PATCH 10/10] More UI - backend connections

---
 src/apps/dataq/client_src/js/dataq.js         | 37 ++++++++++++++++---
 .../client_src/js/dq-rls-policy-builder.js    | 24 +++++++++---
 src/apps/dataq/client_src/js/dq-rls-policy.js | 26 +++++++------
 .../templates/dataq-container-rls-policy.hbs  | 24 ++++++------
 src/browser/templates/security-policies.html  |  2 +-
 5 files changed, 79 insertions(+), 34 deletions(-)

diff --git a/src/apps/dataq/client_src/js/dataq.js b/src/apps/dataq/client_src/js/dataq.js
index 35619f48..0e685d0e 100644
--- a/src/apps/dataq/client_src/js/dataq.js
+++ b/src/apps/dataq/client_src/js/dataq.js
@@ -49,6 +49,9 @@
    * @param cb - The callback to trigger when the query is built.
    */
   DataQ.DQ_rls_policy = function(repo_name, table_name, cb) {
+    console.log(repo_name);
+    console.log(table_name);
+
     // Set the callback.
     callback = cb;
 
@@ -203,11 +206,31 @@
     callback(DataQ.build_query(query));
   });
 
-  // Handle DataQ run policy.
-  $(document).on("click", ".dq-btn-run-policy", function() {
+  // Handle DataQ create policy.
+  $(document).on("click", ".dq-btn-create-policy", function() {
     // Build policy object
     policy.name($("#dq-policy-name").val());
-    policy.table
+
+    policy.command($("#dq-policy-command-selected").text());
+
+    roles = $("#dq-policy-role-list").val().split(",").map(function(r) {
+      return r.trim();
+    });
+    policy.roles(roles);
+
+    using_expr_obj = {
+      "filter1": $("#dq-policy-using-expr-filter-1").val(),
+      "op"     : $("#dq-policy-using-expr-op").val(),
+      "filter2": $("#dq-policy-using-expr-filter-2").val()
+    };
+    policy.using_expression(using_expr_obj);
+
+    check_expr_obj = {
+      "filter1": $("#dq-policy-check-expr-filter-1").val(),
+      "op"     : $("#dq-policy-check-expr-op").val(),
+      "filter2": $("#dq-policy-check-expr-filter-2").val()
+    };
+    policy.using_expression(check_expr_obj);
 
     // Close DataQ
     $(".dq-black-background").remove();
@@ -217,9 +240,13 @@
     callback(DataQ.build_policy(policy));
   });
 
+  // Handle policy command dropdown selection
+  $(document).on("click", "#dq-policy-dropdown-menu a", function() {
+    $('#dq-policy-command-selected').text($(this).text());
+  });
+
   // Handle DataQ close
-  $(document).con("click", ".dq-btn-cancel-create-policy", function() {
-    console.log('click');
+  $(document).on("click", ".dq-btn-cancel-create-policy", function() {
     $(".dq-black-background").remove();
     $(".dataq").remove();
     callback(null);
diff --git a/src/apps/dataq/client_src/js/dq-rls-policy-builder.js b/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
index 61010c1c..7ed03910 100644
--- a/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
+++ b/src/apps/dataq/client_src/js/dq-rls-policy-builder.js
@@ -27,7 +27,7 @@
     var policy_name = policy.name();
 
     // Name of table to which the policy applies.
-    var table_name = policy.repo() + "." + policy.name();
+    var table_name = policy.repo() + "." + policy.table();
 
     // Command to which the policy applies.
     var command = policy.command();
@@ -56,13 +56,27 @@
     policy_string += " TO " + role_list.join(", ");
 
     // USING clause
-    policy_string += " USING " + using_expr;
+    if (command !== "INSERT") {
+      policy_string += " USING " + using_expr;
+    } else if (using_expr !== null) {
+      var err_msg = "An INSERT policy cannot have a USING expression, as USING is"
+                    + " used for reading existing records, not adding new ones.";
+      alert(err_msg);
+      return null;
+    }
 
     // WITH CHECK clause
-    if (command !== "SELECT") {
-      // A SELECT policy cannot have a WITH CHECK expression, as it only applies
-      // in cases where records are being retrieved from the relation.
+    if (command !== "SELECT" && command !== "DELETE") {
+      // Neither a SELECT or DELETE policy can have a WITH CHECK expression,
+      // as this expression is used for validating new records, not reading or
+      // deleting existing ones.
       policy_string += " WITH CHECK " + check_expr;
+    } else if (check_expr !== null) {
+      var err_msg = "Neither a SELECT or DELETE policy can have a WITH CHECK expression"
+                + ", as this expression is used for validating new records, not"
+                + " reading or deleting existing ones.";
+      alert(err_msg);
+      return null;
     }
 
     // Remove leading and trailing spaces and then append semicolon.
diff --git a/src/apps/dataq/client_src/js/dq-rls-policy.js b/src/apps/dataq/client_src/js/dq-rls-policy.js
index 249c5615..264e36c6 100644
--- a/src/apps/dataq/client_src/js/dq-rls-policy.js
+++ b/src/apps/dataq/client_src/js/dq-rls-policy.js
@@ -63,11 +63,11 @@
     };
 
     /**
-     * Get or set the command { ALL | SELECT | INSERT| UPDATE | DELETE } to which
+     * Get or set the command { ALL | SELECT | INSERT | UPDATE | DELETE } to which
      * this policy will apply.
      *
      * @param cmd - If this argument is omitted (or undefined), this function acts as a
-     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *                    getter. Otherwise, it acts as a setter, setting the allowed command.
      *
      * @return The name of the command.
      */
@@ -82,7 +82,7 @@
      * Get or set the Roles to which this policy will apply.
      *
      * @param role_list - If this argument is omitted (or undefined), this function acts as a
-     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *                    getter. Otherwise, it acts as a setter, setting the list of roles.
      *
      * @return The list of Roles.
      */
@@ -100,13 +100,14 @@
      * Get or set the policy's using_expression.
      *
      * @param expr - If this argument is omitted (or undefined), this function acts as a
-     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *               getter. Otherwise, it acts as a setter, setting the USING expression.
      *
-     * @return The full using_expression.
+     * @return The full USING expression.
      */
-    that.using_expression = function(expr) {
-      if (expr !== undefined) {
-        that._using_expr = expr;
+    that.using_expression = function(expr_obj) {
+      if (expr_obj !== undefined) {
+        using_expression = expr_obj.filter1 + " " + expr_obj.op + " " + expr_obj.filter2;
+        that._using_expr = using_expression;
       }
       return that._using_expr;
     };
@@ -115,12 +116,13 @@
      * Get or set the policy's check_expression.
      *
      * @param expr - If this argument is omitted (or undefined), this function acts as a
-     *                    getter. Otherwise, it acts as a setter, setting the repo name.
+     *               getter. Otherwise, it acts as a setter, setting the WITH CHECK expression.
      *
-     * @return The full check_expression.
+     * @return The full WITH CHECK expression.
      */
-    that.check_expression = function(expr) {
-      if (expr !== undefined) {
+    that.check_expression = function(expr_obj) {
+      if (expr_obj !== undefined) {
+        check_expression = expr_obj.filter1 + " " + expr_obj.op + " " + expr_obj.filter2;
         that._check_expr = expr;
       }
       return that._check_expr;
diff --git a/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
index 352ae66f..0c01cda2 100644
--- a/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
+++ b/src/apps/dataq/client_src/templates/dataq-container-rls-policy.hbs
@@ -21,7 +21,7 @@
    </div>
    <div id="collapseOne" class="panel-collapse collapse in">
      <div class="panel-body">
-        <input id="dq-policy-name" type="text" class="form-control" placeholder="Policy Name" />
+        <input id="dq-policy-name" type="text" placeholder="Policy Name" />
      </div>
    </div>
 
@@ -35,9 +35,11 @@
    <div id="collapseTwo" class="panel-collapse collapse in">
      <div class="panel-body">
        <div class="dropdown" id="dq-policy-commands-dropdown">
-          <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Choose command
-          <span class="caret"></span></button>
-          <ul class="dropdown-menu">
+          <button class="btn btn-primary dropdown-toggle dq-modal-dropdown-btn" type="button" data-toggle="dropdown">
+             <span id="dq-policy-command-selected">Choose command</span>
+             <span class="caret"></span>
+          </button>
+          <ul id="dq-policy-dropdown-menu" class="dropdown-menu">
            <li><a href="#">SELECT</a></li>
            <li><a href="#">INSERT</a></li>
            <li><a href="#">UPDATE</a></li>
@@ -57,7 +59,7 @@
    </div>
    <div id="collapseThree" class="panel-collapse collapse in">
      <div class="panel-body">
-       <input id="dq-policy-role-list" type="text" class="form-control" placeholder="Comma separated role names. e.g. CURRENT_USER, admin, myGroupRole" />
+       <input id="dq-policy-role-list" type="text" placeholder="Comma separated role names. e.g. CURRENT_USER, admin, myGroupRole" />
      </div>
    </div>
 
@@ -73,7 +75,7 @@
         <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->
    <div class="input-group col-xs-4">
       <!-- Text box for entering the filter text -->
-      <input type="text" class="form-control dq-filter-1-text" placeholder="table1.col1 * 3">
+      <input type="text" id="dq-policy-using-expr-filter-1" class="dq-filter-1-text" placeholder="table1.col1 * 3">
       <!-- Dropdown for selecting a column to add to filter -->
       <div class="input-group-btn">
         <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
@@ -88,7 +90,7 @@
    <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->
    <div class="input-group col-xs-3">
       <!-- Text box for entering the operation text -->
-      <input type="text" class="form-control dq-filter-op-text" value="=">
+      <input type="text" id="dq-policy-using-expr-op" class="dq-filter-op-text" value="=">
       <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->
       <div class="input-group-btn">
         <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
@@ -108,7 +110,7 @@
    <!-- The second dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->
    <div class="input-group col-xs-4">
       <!-- Text box for entering the filter text -->
-      <input type="text" class="form-control dq-filter-2-text" placeholder="table2.col2+3">
+      <input type="text" id="dq-policy-using-expr-filter-2" class="dq-filter-2-text" placeholder="table2.col2+3">
       <!-- Dropdown for selecting a column to add to filter -->
       <div class="input-group-btn">
         <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
@@ -134,7 +136,7 @@
            <!-- The first dropdown where the user can specify <val1> in the filter <val1> <operation> <val2> -->
       <div class="input-group col-xs-4">
          <!-- Text box for entering the filter text -->
-         <input type="text" class="form-control dq-filter-1-text" placeholder="table1.col1 * 3">
+         <input type="text" id="dq-policy-check-expr-filter-1" class="dq-filter-1-text" placeholder="table1.col1 * 3">
          <!-- Dropdown for selecting a column to add to filter -->
          <div class="input-group-btn">
            <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
@@ -149,7 +151,7 @@
       <!-- The first dropdown where the user can specify <operation> in the filter <val1> <operation> <val2> -->
       <div class="input-group col-xs-3">
          <!-- Text box for entering the operation text -->
-         <input type="text" class="form-control dq-filter-op-text" value="=">
+         <input type="text" id="dq-policy-check-expr-op" class="dq-filter-op-text" value="=">
          <!-- The possible operations are <, <=, >, >=, =, LIKE, IN, and NOT IN -->
          <div class="input-group-btn">
            <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
@@ -169,7 +171,7 @@
       <!-- The first dropdown where the user can specify <val2> in the filter <val1> <operation> <val2> -->
       <div class="input-group col-xs-4">
          <!-- Text box for entering the filter text -->
-         <input type="text" class="form-control dq-filter-2-text" placeholder="table2.col2+3">
+         <input type="text" id="dq-policy-check-expr-filter-2" class="dq-filter-2-text" placeholder="table2.col2+3">
          <!-- Dropdown for selecting a column to add to filter -->
          <div class="input-group-btn">
            <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
diff --git a/src/browser/templates/security-policies.html b/src/browser/templates/security-policies.html
index 1d93fae0..e71f7a5b 100644
--- a/src/browser/templates/security-policies.html
+++ b/src/browser/templates/security-policies.html
@@ -244,7 +244,7 @@ <h4 class="modal-title" id="confirm-modal-title">Update Security Policy</h4>
 <script>
   $(document).on("click", "#btn-dataq-rls-policy", function(e) {
     e.preventDefault();
-    DataQ.DQ_rls_policy("{{repo}}", function(query) {
+    DataQ.DQ_rls_policy("{{repo}}", "{{table}}", function(query) {
       $("#txt-sql").val(query);
     });
   });