From 1c874e3cbe1fb02de91932c3c80d25fa515a2685 Mon Sep 17 00:00:00 2001 From: Roberto Soto 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 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 @@

+
@@ -228,4 +229,25 @@

}); + + + + + + + + + {% endblock js %} + +{% block stylesheets %} + +{% endblock stylesheets %} From 4ea3e182bed04205b93901728edfbbffaf386772 Mon Sep 17 00:00:00 2001 From: Roberto Soto 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 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 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 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 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 @@ + + + +
+ + +
+ +

+ DataQ-Policy-Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface. +

+ +
+
+
+

+ Policy Name +

+
+
+
+ + + +
+
+ +
+
+

+ Allowed commands +

+
+
+
+ +
+
+ + +
+ + +
+
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 @@

- - + +

@@ -238,12 +238,13 @@ {% endblock js %} From 69214b42f35abc4e2c2ca797e397f32190e85572 Mon Sep 17 00:00:00 2001 From: Roberto Soto 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 @@

- 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.

@@ -17,24 +17,24 @@

Policy Name

+
- - - +

- Allowed commands + Allowed Commands

+
- +
+
+

+ Select role(s) this policy will apply to: +

+
+
+
+
+ +
+
+ +
+
+

+ USING expression: Rows for which this expression returns true will be visible. +

+
+
+
+
+ +
+ + + +
+ + +
+
+ + +
+ + + +
+ + +
+
+ + +
+ + + +
+ + +
+
+
+
+ +
+
+

+ WITH CHECK expression: New rows for which this expression returns true will be allowed. +

+
+
+
+
+ +
+ + + +
+ + +
+
+ + +
+ + + +
+ + +
+
+ + +
+ + + +
+ + +
+
+
+ +
+
+
+
- - + +
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(">",">").replace("<","<");$(".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;e0&&(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(">",">").replace("<","<");$(".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;t0&&(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 ).\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 )\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 )\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 )\n $(document).on(\"click\", \".dq-filter-op-link\", function() {\n // Unescape html for > and <\n var op = $(this).html().replace(\">\", \">\").replace(\"<\", \"<\");\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 ).\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 )\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 )\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 )\n $(document).on(\"click\", \".dq-filter-op-link\", function() {\n // Unescape html for > and <\n var op = $(this).html().replace(\">\", \">\").replace(\"<\", \"<\");\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 "
  • " + + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0)) + + "
  • \n"; +},"3":function(depth0,helpers,partials,data,depths) { + var lambda=this.lambda, escapeExpression=this.escapeExpression; + return "
  • " + + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0)) + + "
  • \n"; +},"5":function(depth0,helpers,partials,data,depths) { + var lambda=this.lambda, escapeExpression=this.escapeExpression; + return "
  • " + + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0)) + + "
  • \n"; +},"7":function(depth0,helpers,partials,data,depths) { + var lambda=this.lambda, escapeExpression=this.escapeExpression; + return "
  • " + + escapeExpression(lambda((depth0 != null ? depth0.full_name : depth0), depth0)) + + "
  • \n"; +},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data,depths) { + var stack1, buffer = "\n\n\n
    \n\n\n
    \n\n

    \n DataQ Policy Builder allows you to write PostgreSQL CREATE POLICY commands using a simple checklist-like user interface.\n

    \n\n
    \n
    \n
    \n

    \n Policy Name\n

    \n
    \n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n

    \n Allowed Commands\n

    \n
    \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n\n
    \n
    \n

    \n Select role(s) this policy will apply to:\n

    \n
    \n
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n

    \n USING expression: Rows for which this expression returns true will be visible.\n

    \n
    \n
    \n
    \n
    \n \n
    \n \n \n \n
    \n \n
      \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 += "
    \n
    \n
    \n\n \n
    \n \n \n \n
    \n \n \n
    \n
    \n\n \n
    \n \n \n \n
    \n \n
      \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 += "
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n

    \n WITH CHECK expression: New rows for which this expression returns true will be allowed.\n

    \n
    \n
    \n
    \n
    \n \n
    \n \n \n \n
    \n \n
      \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 += "
    \n
    \n
    \n\n \n
    \n \n \n \n
    \n \n \n
    \n
    \n\n \n
    \n \n \n \n
    \n \n
      \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 + "
    \n
    \n
    \n
    \n\n
    \n
    \n
    \n\n \n
    \n \n \n
    \n
    \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 "\n\n\n
    \n\n\n
    \n\n

    \n DataQ allows you to write SQL queries using a simple checklist-like user interface.\n

    \n\n
    \n
    \n
    \n

    \n Select Columns\n

    \n
    \n
    \n
    \n \n \n \n \n
    \n
    \n
    \n\n
    \n
    \n
    \n
    \n
    \n

    \n Filter Rows\n

    \n
    \n
    \n
    \n \n \n \n\n \n
    \n
      \n
      \n\n
      \n
      \n
      \n
      \n
      \n

      \n Group Columns\n

      \n
      \n
      \n
      \n \n \n \n\n \n
      \n

      No Groupings...

      \n
      \n\n
      \n
      \n
      \n
      \n
      \n

      \n Sort Data\n

      \n
      \n
      \n
      \n \n \n \n\n \n
      \n

      No Sorting...

      \n
      \n\n
      \n
      \n
      \n\n \n
      \n \n \n
      \n
      \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 @@