<commit_before>'use strict';

/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module('neonDemo.directives')
.directive('sample', ['ConnectionService', 'DatasetService', 'ErrorNotificationService', 'FilterService', 'ExportService',
function(connectionService, datasetService, errorNotificationService, filterService, exportService) {
    return {
        templateUrl: 'partials/directives/sample.html',
        restrict: 'EA',
        // Bindings can be set in the config.json file using the following format (please note the two sets of quotation marks):
        //   "bind-database": "'myDatabase'"
        scope: {
            bindDatabase: '=',
            bindTable: '=',
            bindField: '='
        },
        link: function($scope, $element) {
            // Class used for styling (see sample.less).
            $element.addClass('sampleDirective');

            // Add the directive element to the scope for use by the options menu.
            $scope.element = $element;

            // Use the following two functions to display text next to the options menu in the upper right corner of the visualization (the gear icon).
            $scope.optionsMenuButtonText = function() {
                return ($scope.data.length || "No") + ($scope.data.length === 1 ? " result " : " results");
            };
            $scope.showOptionsMenuButtonText = function() {
                return true;
            };

            // Save the lists of databases, tables, and fields in the active dataset so the user can choose which to use in the options menu.  Each database, table, and field is an object.
            $scope.databases = [];
            $scope.tables = [];
            $scope.fields = [];

            // Filter keys are unique IDs used to set filters through the Neon Messenger.
            $scope.filterKeys = {};

            // The current filter for this visualization.  Some visualizations may not use a filter and others may use multiple (this example only uses one).
            $scope.filter = undefined;

            // The error message from the Neon server currently displayed in this visualization through the Error Notification Service.
            $scope.errorMessage = undefined;

            // Whether this visualization is currently loading data due to a query.
            $scope.loadingData = false;

            // Often the visualization will want to save the data from its most recent query for ongoing interaction through its display.
            $scope.data = [];

            // Options available through the options menu for this visualization (the gear icon).  Must be in an object due to angular transclude scoping.
            $scope.options = {
                database: {},
                table: {},
                field: {}
            };

            /**
             * Initializes the visualization.
             * @method initialize
             */
            var initialize = function() {
                // Create a Neon Messenger for subscribing to and publishing events through the Neon API.
                $scope.messenger = new neon.eventing.Messenger();

                // Mock callback for the example below.
                var customChannelCallback = function(message) {};

                // Subscribe to custom channels.
                $scope.messenger.subscribe("custom_channel", customChannelCallback);

                // Subscribe to the "update data" channel:  If the active dataset is configured to requery periodically, it publishes an "update data" event to force the visualizations to requery.
                $scope.messenger.subscribe(datasetService.UPDATE_DATA_CHANNEL, function() {
                    queryForData();
                });

                // Shortcut function to subscribe to Neon API channels.
                // Subscribe to the "filters changed" channel:  If a visualization adds or removes a filter, it publishes a "filters changed" event to all other visualizations.
                $scope.messenger.events({
                    filtersChanged: onFiltersChanged
                });

                // Register this visualization with the Export Service so its data can be exported to file.
                $scope.exportID = exportService.register($scope.makeExportObject);

                $scope.$on('$destroy', function() {
                    $scope.messenger.removeEvents();
                    // Remove any filters that have been set.
                    if($scope.filter) {
                        filterService.removeFilters($scope.messenger, $scope.filterKeys);
                    }
                    exportService.unregister($scope.exportID);
                });
            };

            /**
             * Event handler for filter changed events issued over Neon's messaging channels.
             * @param {Object} message A Neon filter changed message.
             * @method onFiltersChanged
             * @private
             */
            var onFiltersChanged = function(message) {
                // Check if the event affects the database/table that this visualization is using; if so, requery for the filtered data.
                // Please note that all "filters changed" events (add, replace, or remove) will contain an addedFilter object with databaseName and tableName properties.
                if(message.addedFilter && message.addedFilter.databaseName === $scope.options.database.name && message.addedFilter.tableName === $scope.options.table.name) {
                    queryForData();
                }
            };

            /**
             * Displays data for any currently active datasets.
             * @param {Boolean} Whether this function was called during visualization initialization.
             * @method displayActiveDataset
             * @private
             */
            var displayActiveDataset = function(initializing) {
                if(!datasetService.hasDataset() || $scope.loadingData) {
                    return;
                }

                $scope.databases = datasetService.getDatabases();

                // Set the default database to use in this visualization.  Check if a binding was set in the config.json file.  Each database is an object.
                $scope.options.database = $scope.databases[0];
                if($scope.bindDatabase) {
                    for(var i = 0; i < $scope.databases.length; ++i) {
                        if($scope.bindDatabase === $scope.databases[i].name) {
                            $scope.options.database = $scope.databases[i];
                            break;
                        }
                    }
                }

                // Create a map of filter keys for each database/table pair available to this visualization.
                $scope.filterKeys = filterService.createFilterKeys("sample", datasetService.getDatabaseAndTableNames());

                // If initializing, this function was called inside neon.ready which is already in an angular apply.
                // If not, this function was called by a non-angular event, so wrap the update in an angular apply so the HTML is updated.
                if(initializing) {
                    $scope.updateTables();
                } else {
                    $scope.$apply(function() {
                        $scope.updateTables();
                    });
                }
            };

            /**
             * Updates the list of available tables and the default table to use in this visualization from the tables in the active dataset.
             * @method updateTables
             */
            $scope.updateTables = function() {
                $scope.tables = datasetService.getTables($scope.options.database.name);

                // Set the default table to use in this visualization.  Check if a binding was set in the config.json file.
                $scope.options.table = $scope.tables[0];
                if($scope.bindTable) {
                    for(var i = 0; i < $scope.tables.length; ++i) {
                        if($scope.bindTable === $scope.tables[i].name) {
                            $scope.options.table = $scope.tables[i];
                            break;
                        }
                    }
                }

                $scope.updateFields();
            };

            /**
             * Updates the list of available fields and the default fields to use in this visualization from the fields in the active dataset.
             * @method updateFields
             */
            $scope.updateFields = function() {
                // Prevent extraneous queries from onFieldChanged.
                $scope.loadingData = true;

                $scope.fields = datasetService.getSortedFields($scope.options.database.name, $scope.options.table.name);

                // Set the default table to use in this visualization.  Check if a binding was set in the config.json file.
                var fieldName = $scope.bindField || "";
                // Field objects contain {String} columnName (the name matching the field in the database) and {String} prettyName (the name to display in the dashboard).
                $scope.options.field = _.find($scope.fields, function(field) {
                    return field.columnName === fieldName;
                }) || datasetService.createBlankField();

                if($scope.filter) {
                    $scope.removeFilter();
                }

                queryForData();
            };

            /**
             * Query for data to display in this visualization using the Neon Connection.
             * @method queryForData
             * @private
             */
            var queryForData = function() {
                // Error messages display common errors returned by the server.
                if($scope.errorMessage) {
                    errorNotificationService.hideErrorMessage($scope.errorMessage);
                    $scope.errorMessage = undefined;
                }

                // Get the dashboard's active connection to the Neon server.
                var connection = connectionService.getActiveConnection();

                // If no connection or field is available, reset the visualization.
                if(!connection || !datasetService.isFieldValid($scope.options.field)) {
                    updateData({
                        data: []
                    });
                    $scope.loadingData = false;
                    return;
                }

                // Build the query and execute it by sending it to the server using the connection.
                var query = buildQuery();

                connection.executeQuery(query, function(results) {
                    $scope.$apply(function() {
                        // Update the data for this visualization with the query results.
                        updateData(results);
                        $scope.loadingData = false;
                    });
                }, function(response) {
                    // If the query returned an error, reset the visualization.
                    updateData({
                        data: []
                    });
                    $scope.loadingData = false;
                    // If the error response contains an error message, use the Error Notification Service to display that message.
                    if(response.responseJSON) {
                        $scope.errorMessage = errorNotificationService.showErrorMessage($element, response.responseJSON.error, response.responseJSON.stackTrace);
                    }
                });
            };

            /**
             * Builds the query for this visualization.
             * @method buildQuery
             * @return {neon.query.Query}
             * @private
             */
            var buildQuery = function() {
                var query = new neon.query.Query().selectFrom($scope.options.database.name, $scope.options.table.name);

                // TODO Replace the follwing code with your own query properties.
                query.groupBy($scope.options.field);
                query.aggregate(neon.query.COUNT, '*', 'count');
                query.sortBy('count', neon.query.DESCENDING);
                query.where($scope.options.field.columnName, "!=", null);

                // Some visualizations will ignore their own filters and display unfiltered values differently than filtered values (like changing their color or font).
                query.ignoreFilters([$scope.filterKeys[$scope.options.database.name][$scope.options.table.name]]);

                return query;
            };

            /**
             * Updates this visualization to display the data in the given results.
             * @param {Object} results The results returned from a Neon query containing an {Array} data of {Object} data rows mapping column name to value.
             * @method updateData
             * @private
             */
            var updateData = function(results) {
                // TODO Display the rows returned by the query (results.data) in the visualization.
                $scope.data = results.data;
            };

            /**
             * Sets the given field and value as the filter for this visualization.
             * @param {String} field The filter field
             * @param {String} value The filter value
             * @method setFilter
             */
            $scope.setFilter = function(field, value) {
                var filterExists = $scope.filter ? true : false;

                // Save the filter to display in the visualization and use in createFilterClause.
                $scope.filter = {
                    field: field,
                    value: value
                };

                // Get the dashboard's active connection to the Neon server.
                var connection = connectionService.getActiveConnection();

                if($scope.messenger && connection) {
                    // Get the relations for the filter's database/table/field.  This will include an element for the arguments.  Please see the User Guide for more information on relations.
                    // Please note that the size of the array of fields determines the second argument for the createFilterClause function:
                    //      If the array of fields contains a single field (like this case), the second argument for the createFilterClause function is a String.
                    //      Otherwise, the second argument for the createFilterClause function is an array whose elements are in the same order as the array of fields.
                    var relations = datasetService.getRelations($scope.options.database.name, $scope.options.table.name, [field]);

                    // Create an object containing the name of the filter to display in the filter tray (if enabled).
                    var filterName = {
                        visName: "Sample",
                        text: field + " = " + value
                    };

                    // For this example visualization we only want to set one filter at a time so call replaceFilters to replace the existing filter if one exists or addFilter to add a filter otherwise.
                    // The Filter Service will add/replace filters for all relations using one or more of the filter keys.  Once done, it will query for data.
                    // Filters are created using the createFilterClause function.
                    if(filterExists) {
                        filterService.replaceFilters($scope.messenger, relations, $scope.filterKeys, createFilterClause, filterName, queryForData, queryForData);
                    } else {
                        filterService.addFilters($scope.messenger, relations, $scope.filterKeys, createFilterClause, filterName, queryForData, queryForData);
                    }
                }
            };

            /**
             * Creates and returns a filter on the given field using the value set by this visualization.
             * @param {Object} databaseAndTableName Contains the database and table name
             * @param {String} fieldName The name of the field on which to filter
             * @method createFilterClause
             * @return {Object} A neon.query.Filter object
             * @private
             */
            var createFilterClause = function(databaseAndTableName, fieldName) {
                // The field name may change depending on the related database/table but the value is always the one from the filter.
                return neon.query.where(fieldName, '=', $scope.filter.value);
            };

            /**
             * Removes the filter for this visualization.
             * @method removeFilter
             */
            $scope.removeFilter = function() {
                $scope.filter = undefined;

                if($scope.messenger) {
                    // The Filter Service will remove filters for all filter keys.  Once done, it will query for data.
                    filterService.removeFilters($scope.messenger, $scope.filterKeys, queryForData, queryForData);
                }
            };

            /**
             * Triggered by changing $scope.options.field.
             */
            $scope.onFieldChanged = function() {
                // This function is triggered by updateFields which already start a query so check loadingData to prevent extraneous queries.
                if(!$scope.loadingData) {
                    // Query for data using the new field.
                    queryForData();
                }
            };

            /**
             * Creates and returns an object that contains information needed to export the data in this widget.
             * @return {Object} An object containing all the information needed to export the data in this widget.
             */
            $scope.makeExportObject = function() {
                // Build the normal query and override the limit using the Export Service limit.
                var query = buildQuery();
                query.limitClause = exportService.getLimitClause();

                // Add all fields to be contained in the exported table.
                var fields = [{
                    // The name matching the field in the database.
                    query: $scope.options.field.columnName,
                    // The name to display in the dashboard.
                    pretty: $scope.options.field.prettyName
                }];

                var finalObject = {
                    name: "Sample",
                    data: [{
                        query: query,
                        name: "sample-" + $scope.exportID,
                        fields: fields,
                        ignoreFilters: query.ignoreFilters_,
                        selectionOnly: query.selectionOnly_,
                        ignoredFilterIds: query.ignoredFilterIds_,
                        type: "query"
                    }]
                };

                return finalObject;
            };

            // Initialize the visualization once Neon is ready.  Display the active dataset if any.
            // Visualizations are created in one of two ways:
            // 1) Choosing a dataset replaces all visualizations in the dashboard with new visualizations from the dataset's layout.
            // 2) Adding a new visualization through the Add Visualization navbar item.
            neon.ready(function() {
                initialize();
                displayActiveDataset(true);
            });
        }
    };
}]);
<commit_msg>Minor update to the sample visualization.<commit_after>'use strict';

/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module('neonDemo.directives')
.directive('sample', ['ConnectionService', 'DatasetService', 'ErrorNotificationService', 'FilterService', 'ExportService',
function(connectionService, datasetService, errorNotificationService, filterService, exportService) {
    return {
        templateUrl: 'partials/directives/sample.html',
        restrict: 'EA',
        // Bindings can be set in the config.json file using the following format (please note the two sets of quotation marks):
        //   "bind-database": "'myDatabase'"
        scope: {
            bindDatabase: '=',
            bindTable: '=',
            bindField: '='
        },
        link: function($scope, $element) {
            // Class used for styling (see sample.less).
            $element.addClass('sampleDirective');

            // Add the directive element to the scope for use by the options menu.
            $scope.element = $element;

            // Use the following two functions to display text next to the options menu in the upper right corner of the visualization (the gear icon).
            $scope.optionsMenuButtonText = function() {
                return ($scope.data.length || "No") + ($scope.data.length === 1 ? " result " : " results");
            };
            $scope.showOptionsMenuButtonText = function() {
                return true;
            };

            // Save the lists of databases, tables, and fields in the active dataset so the user can choose which to use in the options menu.  Each database, table, and field is an object.
            $scope.databases = [];
            $scope.tables = [];
            $scope.fields = [];

            // Filter keys are unique IDs used to set filters through the Neon Messenger.
            $scope.filterKeys = {};

            // The current filter for this visualization.  Some visualizations may not use a filter and others may use multiple (this example only uses one).
            $scope.filter = undefined;

            // The error message from the Neon server currently displayed in this visualization through the Error Notification Service.
            $scope.errorMessage = undefined;

            // Whether this visualization is currently initializing to stop extra queries.
            $scope.initializing = false;

            // Often the visualization will want to save the data from its most recent query for ongoing interaction through its display.
            $scope.data = [];

            // Options available through the options menu for this visualization (the gear icon).  Must be in an object due to angular transclude scoping.
            $scope.options = {
                database: {},
                table: {},
                field: {}
            };

            /**
             * Initializes the visualization.
             * @method initialize
             */
            var initialize = function() {
                // Create a Neon Messenger for subscribing to and publishing events through the Neon API.
                $scope.messenger = new neon.eventing.Messenger();

                // Mock callback for the example below.
                var customChannelCallback = function(message) {};

                // Subscribe to custom channels.
                $scope.messenger.subscribe("custom_channel", customChannelCallback);

                // Subscribe to the "update data" channel:  If the active dataset is configured to requery periodically, it publishes an "update data" event to force the visualizations to requery.
                $scope.messenger.subscribe(datasetService.UPDATE_DATA_CHANNEL, function() {
                    queryForData();
                });

                // Shortcut function to subscribe to Neon API channels.
                // Subscribe to the "filters changed" channel:  If a visualization adds or removes a filter, it publishes a "filters changed" event to all other visualizations.
                $scope.messenger.events({
                    filtersChanged: onFiltersChanged
                });

                // Register this visualization with the Export Service so its data can be exported to file.
                $scope.exportID = exportService.register($scope.makeExportObject);

                $scope.$on('$destroy', function() {
                    $scope.messenger.removeEvents();
                    // Remove any filters that have been set.
                    if($scope.filter) {
                        filterService.removeFilters($scope.messenger, $scope.filterKeys);
                    }
                    exportService.unregister($scope.exportID);
                });
            };

            /**
             * Event handler for filter changed events issued over Neon's messaging channels.
             * @param {Object} message A Neon filter changed message.
             * @method onFiltersChanged
             * @private
             */
            var onFiltersChanged = function(message) {
                // Check if the event affects the database/table that this visualization is using; if so, requery for the filtered data.
                // Please note that all "filters changed" events (add, replace, or remove) will contain an addedFilter object with databaseName and tableName properties.
                if(message.addedFilter && message.addedFilter.databaseName === $scope.options.database.name && message.addedFilter.tableName === $scope.options.table.name) {
                    queryForData();
                }
            };

            /**
             * Displays data for any currently active datasets.
             * @method displayActiveDataset
             * @private
             */
            var displayActiveDataset = function() {
                if(!datasetService.hasDataset()) {
                    return;
                }

                $scope.databases = datasetService.getDatabases();

                // Set the default database to use in this visualization.  Check if a binding was set in the config.json file.  Each database is an object.
                $scope.options.database = $scope.databases[0];
                if($scope.bindDatabase) {
                    for(var i = 0; i < $scope.databases.length; ++i) {
                        if($scope.bindDatabase === $scope.databases[i].name) {
                            $scope.options.database = $scope.databases[i];
                            break;
                        }
                    }
                }

                // Create a map of filter keys for each database/table pair available to this visualization.
                $scope.filterKeys = filterService.createFilterKeys("sample", datasetService.getDatabaseAndTableNames());

                $scope.updateTables();
            };

            /**
             * Updates the list of available tables and the default table to use in this visualization from the tables in the active dataset.
             * @method updateTables
             */
            $scope.updateTables = function() {
                $scope.tables = datasetService.getTables($scope.options.database.name);

                // Set the default table to use in this visualization.  Check if a binding was set in the config.json file.
                $scope.options.table = $scope.tables[0];
                if($scope.bindTable) {
                    for(var i = 0; i < $scope.tables.length; ++i) {
                        if($scope.bindTable === $scope.tables[i].name) {
                            $scope.options.table = $scope.tables[i];
                            break;
                        }
                    }
                }

                $scope.updateFields();
            };

            /**
             * Updates the list of available fields and the default fields to use in this visualization from the fields in the active dataset.
             * @method updateFields
             */
            $scope.updateFields = function() {
                // Prevent extraneous queries from onFieldChanged.
                $scope.initializing = true;

                $scope.fields = datasetService.getSortedFields($scope.options.database.name, $scope.options.table.name);

                // Set the default table to use in this visualization.  Check if a binding was set in the config.json file.
                var fieldName = $scope.bindField || "";
                // Field objects contain {String} columnName (the name matching the field in the database) and {String} prettyName (the name to display in the dashboard).
                $scope.options.field = _.find($scope.fields, function(field) {
                    return field.columnName === fieldName;
                }) || datasetService.createBlankField();

                if($scope.filter) {
                    $scope.removeFilter();
                }

                queryForData();
            };

            /**
             * Query for data to display in this visualization using the Neon Connection.
             * @method queryForData
             * @private
             */
            var queryForData = function() {
                // Error messages display common errors returned by the server.
                if($scope.errorMessage) {
                    errorNotificationService.hideErrorMessage($scope.errorMessage);
                    $scope.errorMessage = undefined;
                }

                // Get the dashboard's active connection to the Neon server.
                var connection = connectionService.getActiveConnection();

                // If no connection or field is available, reset the visualization.
                if(!connection || !datasetService.isFieldValid($scope.options.field)) {
                    updateData({
                        data: []
                    });
                    $scope.initializing = false;
                    return;
                }

                // Build the query and execute it by sending it to the server using the connection.
                var query = buildQuery();

                connection.executeQuery(query, function(results) {
                    $scope.$apply(function() {
                        // Update the data for this visualization with the query results.
                        updateData(results);
                        $scope.initializing = false;
                    });
                }, function(response) {
                    // If the query returned an error, reset the visualization.
                    updateData({
                        data: []
                    });
                    $scope.initializing = false;
                    // If the error response contains an error message, use the Error Notification Service to display that message.
                    if(response.responseJSON) {
                        $scope.errorMessage = errorNotificationService.showErrorMessage($element, response.responseJSON.error, response.responseJSON.stackTrace);
                    }
                });
            };

            /**
             * Builds the query for this visualization.
             * @method buildQuery
             * @return {neon.query.Query}
             * @private
             */
            var buildQuery = function() {
                var query = new neon.query.Query().selectFrom($scope.options.database.name, $scope.options.table.name);

                // TODO Replace the follwing code with your own query properties.
                query.groupBy($scope.options.field);
                query.aggregate(neon.query.COUNT, '*', 'count');
                query.sortBy('count', neon.query.DESCENDING);
                query.where($scope.options.field.columnName, "!=", null);

                // Some visualizations will ignore their own filters and display unfiltered values differently than filtered values (like changing their color or font).
                query.ignoreFilters([$scope.filterKeys[$scope.options.database.name][$scope.options.table.name]]);

                return query;
            };

            /**
             * Updates this visualization to display the data in the given results.
             * @param {Object} results The results returned from a Neon query containing an {Array} data of {Object} data rows mapping column name to value.
             * @method updateData
             * @private
             */
            var updateData = function(results) {
                // TODO Display the rows returned by the query (results.data) in the visualization.
                $scope.data = results.data;
            };

            /**
             * Sets the given field and value as the filter for this visualization.
             * @param {String} field The filter field
             * @param {String} value The filter value
             * @method setFilter
             */
            $scope.setFilter = function(field, value) {
                var filterExists = $scope.filter ? true : false;

                // Save the filter to display in the visualization and use in createFilterClause.
                $scope.filter = {
                    field: field,
                    value: value
                };

                // Get the dashboard's active connection to the Neon server.
                var connection = connectionService.getActiveConnection();

                if($scope.messenger && connection) {
                    // Get the relations for the filter's database/table/field.  This will include an element for the arguments.  Please see the User Guide for more information on relations.
                    // Please note that the size of the array of fields determines the second argument for the createFilterClause function:
                    //      If the array of fields contains a single field (like this case), the second argument for the createFilterClause function is a String.
                    //      Otherwise, the second argument for the createFilterClause function is an array whose elements are in the same order as the array of fields.
                    var relations = datasetService.getRelations($scope.options.database.name, $scope.options.table.name, [field]);

                    // Create an object containing the name of the filter to display in the filter tray (if enabled).
                    var filterName = {
                        visName: "Sample",
                        text: field + " = " + value
                    };

                    // For this example visualization we only want to set one filter at a time so call replaceFilters to replace the existing filter if one exists or addFilter to add a filter otherwise.
                    // The Filter Service will add/replace filters for all relations using one or more of the filter keys.  Once done, it will query for data.
                    // Filters are created using the createFilterClause function.
                    if(filterExists) {
                        filterService.replaceFilters($scope.messenger, relations, $scope.filterKeys, createFilterClause, filterName, queryForData, queryForData);
                    } else {
                        filterService.addFilters($scope.messenger, relations, $scope.filterKeys, createFilterClause, filterName, queryForData, queryForData);
                    }
                }
            };

            /**
             * Creates and returns a filter on the given field using the value set by this visualization.
             * @param {Object} databaseAndTableName Contains the database and table name
             * @param {String} fieldName The name of the field on which to filter
             * @method createFilterClause
             * @return {Object} A neon.query.Filter object
             * @private
             */
            var createFilterClause = function(databaseAndTableName, fieldName) {
                // The field name may change depending on the related database/table but the value is always the one from the filter.
                return neon.query.where(fieldName, '=', $scope.filter.value);
            };

            /**
             * Removes the filter for this visualization.
             * @method removeFilter
             */
            $scope.removeFilter = function() {
                $scope.filter = undefined;

                if($scope.messenger) {
                    // The Filter Service will remove filters for all filter keys.  Once done, it will query for data.
                    filterService.removeFilters($scope.messenger, $scope.filterKeys, queryForData, queryForData);
                }
            };

            /**
             * Triggered by changing $scope.options.field.
             */
            $scope.onFieldChanged = function() {
                // This function is triggered by updateFields which already start a query so check initializing to prevent extraneous queries.
                if(!$scope.initializing) {
                    // Query for data using the new field.
                    queryForData();
                }
            };

            /**
             * Creates and returns an object that contains information needed to export the data in this widget.
             * @return {Object} An object containing all the information needed to export the data in this widget.
             */
            $scope.makeExportObject = function() {
                // Build the normal query and override the limit using the Export Service limit.
                var query = buildQuery();
                query.limitClause = exportService.getLimitClause();

                // Add all fields to be contained in the exported table.
                var fields = [{
                    // The name matching the field in the database.
                    query: $scope.options.field.columnName,
                    // The name to display in the dashboard.
                    pretty: $scope.options.field.prettyName
                }];

                var finalObject = {
                    name: "Sample",
                    data: [{
                        query: query,
                        name: "sample-" + $scope.exportID,
                        fields: fields,
                        ignoreFilters: query.ignoreFilters_,
                        selectionOnly: query.selectionOnly_,
                        ignoredFilterIds: query.ignoredFilterIds_,
                        type: "query"
                    }]
                };

                return finalObject;
            };

            // Initialize the visualization once Neon is ready.  Display the active dataset if any.
            // Visualizations are created in one of two ways:
            // 1) Choosing a dataset replaces all visualizations in the dashboard with new visualizations from the dataset's layout.
            // 2) Adding a new visualization through the Add Visualization navbar item.
            neon.ready(function() {
                initialize();
                displayActiveDataset();
            });
        }
    };
}]);
<|endoftext|>


<commit_before>'use strict'

import {Component}      from 'react'
import {Link}           from 'react-router'
import marked           from 'marked'
import Helmet           from 'react-helmet'

import Image            from '../components/Image'
import Header           from '../components/Header'
import Footer           from '../components/Footer'
import CardLink         from '../components/CardLink'
import docs             from '../../docs.json'

marked.setOptions({
  renderer: new marked.Renderer(),
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: false,
  smartLists: true,
  smartypants: false
})

class ArticlePage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      currentPage: null,
      nextPage: null,
      imageLoading: false
    }

    this.initHighlighting = this.initHighlighting.bind(this)
    this.getPageName = this.getPageName.bind(this)
    this.setPage = this.setPage.bind(this)
    this.filterMarkup = this.filterMarkup.bind(this)
    this.onImageLoad = this.onImageLoad.bind(this)
  }

  initHighlighting() {
    const blocks = document.querySelectorAll('pre code')
    Array.prototype.forEach.call(blocks, hljs.highlightBlock)
  }

  componentWillMount() {
    this.setPage(this.props)
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      imageLoading: true
    })
    if (this.props !== nextProps) {
      this.setPage(nextProps)
    }
  }

  componentDidUpdate() {
    this.initHighlighting()
  }

  getPageName(props) {
    let pageName = '404'
    if (props.route.path === '/about') {
      pageName = 'about'
    } else if (props.routeParams.hasOwnProperty('docSection')) {
      if (docs.hasOwnProperty(props.routeParams.docSection)) {
        pageName = props.routeParams.docSection
      }
    }
    return pageName
  }

  filterMarkup(markup) {
    return markup.replace('<a href="', '<a target="_blank" href="')
  }

  setPage(props) {
    let pageName = this.getPageName(props)
    let currentPage = Object.assign({}, docs[pageName], {
      pageName: pageName,
      markup: marked(docs[pageName].markdown)
    })

    let nextPage = null
    if (currentPage.hasOwnProperty('next')) {
      nextPage = Object.assign({}, docs[currentPage.next], {
        pageName: currentPage.next
      })
    }

    this.setState({
      currentPage: currentPage,
      nextPage: nextPage
    })
  }

  onImageLoad() {
    this.setState({
      imageLoading: false
    })
  }

  render() {
    const currentPage = this.state.currentPage,
          nextPage = this.state.nextPage,
          title = currentPage ? currentPage.title : "Docs"
    const headerImageSrc = this.state.currentPage ? this.state.currentPage.image : null
    const pathPrefix = '/' + location.pathname.split('/')[1]
    const githubFileUrlRoot = "https://github.com/blockstack/blockstack-site/blob/master/app/docs/"

    return (

        { currentPage ?
        <Helmet
          title={`Blockstack - ${currentPage.title}`}
          meta={[
            {"name": "description", "content": currentPage.description },

            {"property": "og:type", "content": "article"},
            {"property": "og:url", "content": `https://blockstack.org/docs/${currentPage.pageName}`},
            {"property": "og:title", "content": `Blockstack - ${currentPage.title}` },
            {"property": "og:description", "content": `${currentPage.description}`},
            {"property": "og:image", "content": `${currentPage.image}`},

            {"property": "twitter:card", "content": "summary"},
            {"property": "twitter:site", "content": "@blockstackorg"},
            {"property": "twitter:title", "content": `Blockstack - ${currentPage.title}`},
            {"property": "twitter:description", "content": `${currentPage.description}`},
            {"property": "twitter:image", "content": `${currentPage.image}` }
          ]}
        />
        : null }

          <Image src={headerImageSrc} onLoad={this.onImageLoad} retinaSupport={false} />

          { currentPage !== null && this.state.imageLoading !== true ?
          <Image src={headerImageSrc}
            fallbackSrc="/images/article-photos/road.jpg"
            className="img-fluid docs-header-image"
            retinaSupport={false} />
          : null }

        { currentPage.pageName !== 'about' ?

              <Link className="page-link" to={pathPrefix} aria-label="Back">
                « Back
                Back
              </Link>

        : null }

{currentPage.title}

                <Link to={githubFileUrlRoot + currentPage.pageName + ".md"}
                  role="button" target="_blank"
                  className="btn btn-sm btn-outline-primary m-b-2">
                  Edit this article on GitHub
                </Link>

              {nextPage ?

Next Article

                    <CardLink href={`${pathPrefix}/${nextPage.pageName}`}
                      title={nextPage.title} body={nextPage.description}
                      imageSrc={nextPage.image} />

              : null }

    )
  }
}

export default ArticlePage
<commit_msg>fix links to edit articles<commit_after>'use strict'

import {Component}      from 'react'
import {Link}           from 'react-router'
import marked           from 'marked'
import Helmet           from 'react-helmet'

import Image            from '../components/Image'
import Header           from '../components/Header'
import Footer           from '../components/Footer'
import CardLink         from '../components/CardLink'
import docs             from '../../docs.json'

marked.setOptions({
  renderer: new marked.Renderer(),
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: false,
  smartLists: true,
  smartypants: false
})

class ArticlePage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      currentPage: null,
      nextPage: null,
      imageLoading: false
    }

    this.initHighlighting = this.initHighlighting.bind(this)
    this.getPageName = this.getPageName.bind(this)
    this.setPage = this.setPage.bind(this)
    this.filterMarkup = this.filterMarkup.bind(this)
    this.onImageLoad = this.onImageLoad.bind(this)
  }

  initHighlighting() {
    const blocks = document.querySelectorAll('pre code')
    Array.prototype.forEach.call(blocks, hljs.highlightBlock)
  }

  componentWillMount() {
    this.setPage(this.props)
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      imageLoading: true
    })
    if (this.props !== nextProps) {
      this.setPage(nextProps)
    }
  }

  componentDidUpdate() {
    this.initHighlighting()
  }

  getPageName(props) {
    let pageName = '404'
    if (props.route.path === '/about') {
      pageName = 'about'
    } else if (props.routeParams.hasOwnProperty('docSection')) {
      if (docs.hasOwnProperty(props.routeParams.docSection)) {
        pageName = props.routeParams.docSection
      }
    }
    return pageName
  }

  filterMarkup(markup) {
    return markup.replace('<a href="', '<a target="_blank" href="')
  }

  setPage(props) {
    let pageName = this.getPageName(props)
    let currentPage = Object.assign({}, docs[pageName], {
      pageName: pageName,
      markup: marked(docs[pageName].markdown)
    })

    let nextPage = null
    if (currentPage.hasOwnProperty('next')) {
      nextPage = Object.assign({}, docs[currentPage.next], {
        pageName: currentPage.next
      })
    }

    this.setState({
      currentPage: currentPage,
      nextPage: nextPage
    })
  }

  onImageLoad() {
    this.setState({
      imageLoading: false
    })
  }

  render() {
    const currentPage = this.state.currentPage,
          nextPage = this.state.nextPage,
          title = currentPage ? currentPage.title : "Docs"
    const headerImageSrc = this.state.currentPage ? this.state.currentPage.image : null
    const pathPrefix = '/' + location.pathname.split('/')[1]
    const githubFileUrlRoot = "https://github.com/blockstack/blockstack-site/blob/master/app/docs/"

    return (

        { currentPage ?
        <Helmet
          title={`Blockstack - ${currentPage.title}`}
          meta={[
            {"name": "description", "content": currentPage.description },

            {"property": "og:type", "content": "article"},
            {"property": "og:url", "content": `https://blockstack.org${pathPrefix}/${currentPage.pageName}`},
            {"property": "og:title", "content": `Blockstack - ${currentPage.title}` },
            {"property": "og:description", "content": `${currentPage.description}`},
            {"property": "og:image", "content": `${currentPage.image}`},

            {"property": "twitter:card", "content": "summary"},
            {"property": "twitter:site", "content": "@blockstackorg"},
            {"property": "twitter:title", "content": `Blockstack - ${currentPage.title}`},
            {"property": "twitter:description", "content": `${currentPage.description}`},
            {"property": "twitter:image", "content": `${currentPage.image}` }
          ]}
        />
        : null }

          <Image src={headerImageSrc} onLoad={this.onImageLoad} retinaSupport={false} />

          { currentPage !== null && this.state.imageLoading !== true ?
          <Image src={headerImageSrc}
            fallbackSrc="/images/article-photos/road.jpg"
            className="img-fluid docs-header-image"
            retinaSupport={false} />
          : null }

        { currentPage.pageName !== 'about' ?

              <Link className="page-link" to={pathPrefix} aria-label="Back">
                « Back
                Back
              </Link>

        : null }

{currentPage.title}

                <Link to={githubFileUrlRoot + pathPrefix + '/' + currentPage.pageName + ".md"}
                  role="button" target="_blank"
                  className="btn btn-sm btn-outline-primary m-b-2">
                  Edit this article on GitHub
                </Link>

              {nextPage ?

Next Article

                    <CardLink href={`${pathPrefix}/${nextPage.pageName}`}
                      title={nextPage.title} body={nextPage.description}
                      imageSrc={nextPage.image} />

              : null }

    )
  }
}

export default ArticlePage
<|endoftext|>


<commit_before>/*
Language: TypeScript
Author: Panu Horsmalahti <panu.horsmalahti@iki.fi>
Description: TypeScript is a strict superset of JavaScript
Category: scripting
*/

function(hljs) {
  var KEYWORDS = {
    keyword:
      'in if for while finally var new function|0 do return void else break catch ' +
      'instanceof with throw case default try this switch continue typeof delete ' +
      'let yield const class public private get set super interface extends' +
      'static constructor implements enum export import declare type protected',
    literal:
      'true false null undefined NaN Infinity',
    built_in:
      'eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent ' +
      'encodeURI encodeURIComponent escape unescape Object Function Boolean Error ' +
      'EvalError InternalError RangeError ReferenceError StopIteration SyntaxError ' +
      'TypeError URIError Number Math Date String RegExp Array Float32Array ' +
      'Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array ' +
      'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
      'module console window document any number boolean string void'
  }

  return {
    aliases: ['ts'],
    keywords: KEYWORDS,
    contains: [
      {
        className: 'pi',
        begin: /^\s*['"]use strict['"]/,
        relevance: 0
      },
      hljs.APOS_STRING_MODE,
      hljs.QUOTE_STRING_MODE,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      {
        className: 'number',
        variants: [
          { begin: '\\b(0[bB][01]+)' },
          { begin: '\\b(0[oO][0-7]+)' },
          { begin: hljs.C_NUMBER_RE }
        ],
        relevance: 0
      },
      { // "value" container
        begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
        keywords: 'return throw case',
        contains: [
          hljs.C_LINE_COMMENT_MODE,
          hljs.C_BLOCK_COMMENT_MODE,
          hljs.REGEXP_MODE,
          { // E4X
            begin: /</, end: />;/,
            relevance: 0,
            subLanguage: 'xml'
          }
        ],
        relevance: 0
      },
      {
        className: 'function',
        begin: 'function', end: /[\{;]/, excludeEnd: true,
        keywords: KEYWORDS,
        contains: [
          'self',
          hljs.inherit(hljs.TITLE_MODE, {begin: /[A-Za-z$_][0-9A-Za-z$_]*/}),
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            excludeBegin: true,
            excludeEnd: true,
            keywords: KEYWORDS,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE
            ],
            illegal: /["'\(]/
          }
        ],
        illegal: /\[|%/,
        relevance: 0 // () => {} is more typical in TypeScript
      },
      {
        className: 'constructor',
        begin: 'constructor', end: /\{/, excludeEnd: true,
        keywords: KEYWORDS,
        relevance: 10
      },
      {
        className: 'module',
        beginKeywords: 'module', end: /\{/, excludeEnd: true
      },
      {
        className: 'interface',
        beginKeywords: 'interface', end: /\{/, excludeEnd: true
      },
      {
        begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
      },
      {
        begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots
      }
    ]
  };
}
<commit_msg>Fixed TypeScript keywords.<commit_after>/*
Language: TypeScript
Author: Panu Horsmalahti <panu.horsmalahti@iki.fi>
Description: TypeScript is a strict superset of JavaScript
Category: scripting
*/

function(hljs) {
  var KEYWORDS = {
    keyword:
      'in if for while finally var new function|0 do return void else break catch ' +
      'instanceof with throw case default try this switch continue typeof delete ' +
      'let yield const class public private get set super ' +
      'static implements enum export import declare type protected',
    literal:
      'true false null undefined NaN Infinity',
    built_in:
      'eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent ' +
      'encodeURI encodeURIComponent escape unescape Object Function Boolean Error ' +
      'EvalError InternalError RangeError ReferenceError StopIteration SyntaxError ' +
      'TypeError URIError Number Math Date String RegExp Array Float32Array ' +
      'Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array ' +
      'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
      'module console window document any number boolean string void'
  }

  return {
    aliases: ['ts'],
    keywords: KEYWORDS,
    contains: [
      {
        className: 'pi',
        begin: /^\s*['"]use strict['"]/,
        relevance: 0
      },
      hljs.APOS_STRING_MODE,
      hljs.QUOTE_STRING_MODE,
      hljs.C_LINE_COMMENT_MODE,
      hljs.C_BLOCK_COMMENT_MODE,
      {
        className: 'number',
        variants: [
          { begin: '\\b(0[bB][01]+)' },
          { begin: '\\b(0[oO][0-7]+)' },
          { begin: hljs.C_NUMBER_RE }
        ],
        relevance: 0
      },
      { // "value" container
        begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
        keywords: 'return throw case',
        contains: [
          hljs.C_LINE_COMMENT_MODE,
          hljs.C_BLOCK_COMMENT_MODE,
          hljs.REGEXP_MODE,
          { // E4X
            begin: /</, end: />;/,
            relevance: 0,
            subLanguage: 'xml'
          }
        ],
        relevance: 0
      },
      {
        className: 'function',
        begin: 'function', end: /[\{;]/, excludeEnd: true,
        keywords: KEYWORDS,
        contains: [
          'self',
          hljs.inherit(hljs.TITLE_MODE, {begin: /[A-Za-z$_][0-9A-Za-z$_]*/}),
          {
            className: 'params',
            begin: /\(/, end: /\)/,
            excludeBegin: true,
            excludeEnd: true,
            keywords: KEYWORDS,
            contains: [
              hljs.C_LINE_COMMENT_MODE,
              hljs.C_BLOCK_COMMENT_MODE
            ],
            illegal: /["'\(]/
          }
        ],
        illegal: /\[|%/,
        relevance: 0 // () => {} is more typical in TypeScript
      },
      {
        className: 'constructor',
        beginKeywords: 'constructor', end: /\{/, excludeEnd: true,
        relevance: 10
      },
      {
        className: 'module',
        beginKeywords: 'module', end: /\{/, excludeEnd: true
      },
      {
        className: 'interface',
        beginKeywords: 'interface', end: /\{/, excludeEnd: true,
        keywords: 'interface extends'
      },
      {
        begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
      },
      {
        begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots
      }
    ]
  };
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    return /* Your Expression */;
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    return /* Your Expression */;
}

function getWinner(playerMove,computerMove) {
    var winner;
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    /* YOUR CODE HERE */
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

<commit_msg>lines 24 - 32 : unit 3 homework<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
	return move || ‘getInput ()’;
}

function getComputerMove(move) {
    return move || 'randomPlay()';
}

git commit -m "unit 3 homework is above"

function getWinner(playerMove,computerMove) {
    var winner;
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    /* YOUR CODE HERE */
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    return move || getInput();
/* Your Expression */
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    return move || randomPlay();
/* Your Expression */
}

function getWinner(playerMove,computerMove) {
	// either have var playerMove and var computerMove and take these out
	// of parentheses (parameters) or keep in paren and take out var.
	while (playerMove === “rock”) {
	if (computerMove === “rock”) {
		return “tie”;
	// Not sure this makes sense—maybe return “tie” instead/ winner not def
	} else if (computerMove === “paper”) {
		return “computer”;
	} else if (computerMove === “scissors”) {
		return “player”;
		}
	}
	while (playerMove === “paper”) {
	if (computerMove === “rock”) {
		return “player”;
	} else if (computerMove === “paper”) {
		return “tie”;
	} else if (computerMove === “scissors”) {
		return “computer”;
		}
	}
	while (playerMove === “scissors”) {
	if (computerMove === “rock”) {
		return “computer”;
	} else if (computerMove === “paper”) {
		return “player”;
	} else if (computerMove === “scissors”) {
		return “tie”;
		}
	}

}

function playToFive() {
  console.log('Let\'s play Rock Paper Scissors');
  var playerWins = 0;
  var computerWins = 0;
  while (playerWins < 5 && computerWins < 5) {
    if (winnerIsPlayer()){
	   playerWins += 1;
    }
    if (winnerIsComputer()){
	computerWins += 1;
    }
 	console.log('Player chose ' + playerMove + ' while Computer chose ' + computerMove);
   console.log('The score is currently ' + playerWins + ' to ' + computerWins);
  return [playerWins, computerWins];
  }
}

function winnerIsPlayer() {
	return playerWinsRock() || playerWinsPaper() || playerWinsScissors();
	}

function winnerIsComputer() {
	return computerWinsRock() || computerWinsPaper()|| computerWinsScissors();
	}

function playerWinsRock() {
	return (playerMove === ‘rock’) && (computerMove === ‘scissors’);
	}

function playerWinsPaper() {
	return (playerMove === ‘paper’) && (computerMove === ‘rock’);
	}

function playerWinsScissors() {
	return (playerMove === ‘scissors’) && (computerMove === ‘paper’);
	}

function computerWinsRock() {
	return (computerMove === ‘rock’) && (playerMove === ‘scissors’);
	}

function computerWinsPaper() {
	return (computerMove === ‘paper’) && (playerMove === ‘rock’);
	}

function computerWinsScissors() {
	return (computerMove === ‘scissors’) && (playerMove === ‘paper’);
	}

/* I think this version of playToFive could operate independently of the getWinner function above, but it makes logical sense to me, whereas I don’t think my attempt at incorporating getWinner function (below) would work. */

/*
// here is a version of playToFive that accounts for getWinner function
function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    while (playerWins < 5 && computerWins < 5) {
    //not sure while is allowed within a function.
    if (winnerIsPlayer()){
	playerWins += 1;
    }
    if (winnerIsComputer()){
	computerWins += 1;
    }
   console.log('Player chose ' + playerMove + ' while Computer chose ' + computerMove);
   console.log('The score is currently ' + playerWins + ' to ' + computerWins + ‘\n’);
	return [playerWins, computerWins];
  }
}

function winnerIsPlayer() {
	return ‘player’;
	// if getWinner = “player” (not sure of syntax)
	}

function winnerIsComputer() {
	return ‘computer’;
	// if getWinner = “computer” (not sure of syntax)
}
*/

<commit_msg>changed 'while' to 'for' in playToFive<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    return move || getInput();
/* Your Expression */
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    return move || randomPlay();
/* Your Expression */
}

function getWinner(playerMove,computerMove) {
	// either have var playerMove and var computerMove and take these out
	// of parentheses (parameters) or keep in paren and take out var.
	while (playerMove === “rock”) {
	if (computerMove === “rock”) {
		return “tie”;
	// Not sure this makes sense—maybe return “tie” instead/ winner not def
	} else if (computerMove === “paper”) {
		return “computer”;
	} else if (computerMove === “scissors”) {
		return “player”;
		}
	}
	while (playerMove === “paper”) {
	if (computerMove === “rock”) {
		return “player”;
	} else if (computerMove === “paper”) {
		return “tie”;
	} else if (computerMove === “scissors”) {
		return “computer”;
		}
	}
	while (playerMove === “scissors”) {
	if (computerMove === “rock”) {
		return “computer”;
	} else if (computerMove === “paper”) {
		return “player”;
	} else if (computerMove === “scissors”) {
		return “tie”;
		}
	}

}

function playToFive() {
  console.log('Let\'s play Rock Paper Scissors');
  var playerWins = 0;
  var computerWins = 0;
  for ((playerWins < 5) && (computerWins < 5)) {
    if (winnerIsPlayer()){
	   playerWins += 1;
    }
    if (winnerIsComputer()){
	computerWins += 1;
    }
 	console.log('Player chose ' + playerMove + ' while Computer chose ' + computerMove);
   console.log('The score is currently ' + playerWins + ' to ' + computerWins);
  return [playerWins, computerWins];
  }
}

function winnerIsPlayer() {
	return playerWinsRock() || playerWinsPaper() || playerWinsScissors();
	}

function winnerIsComputer() {
	return computerWinsRock() || computerWinsPaper()|| computerWinsScissors();
	}

function playerWinsRock() {
	return (playerMove === ‘rock’) && (computerMove === ‘scissors’);
	}

function playerWinsPaper() {
	return (playerMove === ‘paper’) && (computerMove === ‘rock’);
	}

function playerWinsScissors() {
	return (playerMove === ‘scissors’) && (computerMove === ‘paper’);
	}

function computerWinsRock() {
	return (computerMove === ‘rock’) && (playerMove === ‘scissors’);
	}

function computerWinsPaper() {
	return (computerMove === ‘paper’) && (playerMove === ‘rock’);
	}

function computerWinsScissors() {
	return (computerMove === ‘scissors’) && (playerMove === ‘paper’);
	}

/* I think this version of playToFive could operate independently of the getWinner function above, but it makes logical sense to me, whereas I don’t think my attempt at incorporating getWinner function (below) would work. */

/*
// here is a version of playToFive that accounts for getWinner function
function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    for ((playerWins < 5) && (computerWins < 5)) {
    //not sure while is allowed within a function.
    if (winnerIsPlayer()){
	playerWins += 1;
    }
    if (winnerIsComputer()){
	computerWins += 1;
    }
   console.log('Player chose ' + playerMove + ' while Computer chose ' + computerMove);
   console.log('The score is currently ' + playerWins + ' to ' + computerWins + ‘\n’);
	return [playerWins, computerWins];
  }
}

function winnerIsPlayer() {
	return ‘player’;
	// if getWinner = “player” (not sure of syntax)
	}

function winnerIsComputer() {
	return ‘computer’;
	// if getWinner = “computer” (not sure of syntax)
}
*/

<|endoftext|>


<commit_before>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.")
    return prompt();
}
function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    var move;
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    move = move || getInput();
    console.log ("Player move is: " + move);
    return move;
}

function getComputerMove(move) {
    var move;
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    move = move || randomPlay();
    console.log ("Computer move is: " + move);
    return move;

}

function getWinner(playerMove,computerMove) {
    var winner;
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    /* YOUR CODE HERE */
    if (computerMove === playerMove) {
        winner = 'tie';
    }
    else if (computerMove === 'rock' && playerMove === 'paper') {
        winner = 'player';
    }
    else if (computerMove === 'rock' && playerMove === 'scissors') {
        winner = 'computer';
    }
    else if (computerMove === 'scissors' && playerMove === 'rock') {
        winner = 'player';
    }
    else if (computerMove === 'scissors' && playerMove === 'paper') {
        winner = 'computer';
    }
    else if (computerMove === 'paper' && playerMove === 'scissors') {
        winner = 'player';
    }
    else if (computerMove === 'paper' && playerMove === 'rock') {
        winner = 'computer';
    }

    console.log("hit")
    return winner;

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;
    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

<commit_msg>FINAL<commit_after>////////////////////////////////////////////////
/*   Provided Code - Please Don't Edit   */
////////////////////////////////////////////////
'use strict';

function getInput() {
    console.log("Please choose either 'rock', 'paper', or 'scissors'.");
    return prompt();
}

function randomPlay() {
    var randomNumber = Math.random();
    if (randomNumber < 0.33) {
        return "rock";
    } else if (randomNumber < 0.66) {
        return "paper";
    } else {
        return "scissors";
    }
}
////////////////////////////////////////////////
/*           Write Your Code Below            */
////////////////////////////////////////////////

function getPlayerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `getInput()`.
    //move = move || getInput();
    console.log ("Player move is: " + move);
    return move;
}

function getComputerMove(move) {
    // Write an expression that operates on a variable called `move`
    // If a `move` has a value, your expression should evaluate to that value.
    // However, if `move` is not specified / is null, your expression should equal `randomPlay()`.
    //move = move || randomPlay();
    console.log ("Computer move is: " + move);
    return move;

}

function getWinner(playerMove,computerMove) {
    var winner;
    // Write code that will set winner to either 'player', 'computer', or 'tie' based on the values of playerMove and computerMove.
    // Assume that the only values playerMove and computerMove can have are 'rock', 'paper', and 'scissors'.
    // The rules of the game are that 'rock' beats 'scissors', 'scissors' beats 'paper', and 'paper' beats 'rock'.
    /* YOUR CODE HERE */
    if (computerMove === playerMove) {
        winner = 'tie';
    }
    else if (computerMove === 'rock' && playerMove === 'paper') {
        winner = 'player';
    }
    else if (computerMove === 'rock' && playerMove === 'scissors') {
        winner = 'computer';
    }
    else if (computerMove === 'scissors' && playerMove === 'rock') {
        winner = 'player';
    }
    else if (computerMove === 'scissors' && playerMove === 'paper') {
        winner = 'computer';
    }
    else if (computerMove === 'paper' && playerMove === 'scissors') {
        winner = 'player';
    }
    else if (computerMove === 'paper' && playerMove === 'rock') {
        winner = 'computer';
    }
    return winner;
}

function playToFive() {
    console.log("Let's play Rock, Paper, Scissors");
    var playerWins = 0;
    var computerWins = 0;

    while (playerWins < 5 && computerWins < 5) {

        var playerMove = getPlayerMove(getInput());
        var computerMove = getComputerMove(randomPlay());
        var winner = getWinner(playerMove, computerMove);

        if (winner == 'player') {
            playerWins = playerWins + 1;
            console.log("Player wins!");
        }

        else if (winner == 'computer') {
            computerWins = computerWins + 1;
            console.log("Computer wins!");
        }

        else {
            console.log("Tie, play again!");
        }
    console.log('Player chose ' + playerMove + ' while Computer chose ' + computerMove);
    console.log('The score is currently ' + playerWins + ' to ' + computerWins + '\n');
    }

    // Write code that plays 'Rock, Paper, Scissors' until either the player or the computer has won five times.
    /* YOUR CODE HERE */
    return [playerWins, computerWins];
}

playToFive();

<|endoftext|>


<commit_before>angular.module('kinghunt.services').
    factory('readySvc', ['$window', '$q', function($window, $q) {

      var request = $window.indexedDB.open("KingHunt");
      var db;
      var book;
      var solved;
      var dbDfd = $q.defer();
      var bookDfd = $q.defer();
      var solvedDfd = $q.defer();
      var readyPromise = $q.all([bookDfd.promise, solvedDfd.promise]).then(
          function() { return true; },
          function() { return false; }
      );

      // initialization
      request.onerror = function(event) {
        dbDfd.reject();
      };
      request.onsuccess = function(event) {
        db = request.result;
        dbDfd.resolve();
      };
      // run this the first time through, to setup stores
      request.onupgradeneeded = function(evt) {
        // TODO: implement me
      };

      dbDfd.promise.then(function(e) {

        // open store, initialize if necessary, fetch contents, resolve promise with contents
        var bookStore = db.transaction(["book"], "readonly").objectStore("book");
        var bookRequest = bookStore.getAll();

        var solvedStore = db.transaction(["solved"], "readonly").objectStore("solved");
        // FIXME: get just the solved ones for this store please:
        var solvedRequest = solvedStore.getAll();

        bookRequest.onsuccess = function(e) {
          book = e.result;
          bookDfd.resolve(book);
        };
        bookRequest.onerror = function(e) {
          // TODO: if there is no book defined, load default book and return it
          bookDfd.reject();
        };

        solvedRequest.onsuccess = function(e) {
          solved = e.result;
          solvedDfd.resolve(solved);
        };
        solvedRequest.onerror = function(e) {
          solvedDfd.reject();
        };
      });

      return {
        promise: $q.all([dbDfd.promise, bookDfd.promise, solvedDfd.promise]),
        db: db,
        book: book,
        solved: solved
      };
    }]);<commit_msg>software is hard<commit_after>angular.module('kinghunt.services').
    factory('readySvc', ['$window', '$q', function($window, $q) {

      var request = $window.indexedDB.open("KingHunt", 1);
      var db;
      var book;
      var solved;
      var dbDfd = $q.defer();
      var bookDfd = $q.defer();
      var solvedDfd = $q.defer();

      // initialization
      request.onerror = function(event) {
        dbDfd.reject();
      };
      request.onsuccess = function(event) {
        db = event.target.result;
        dbDfd.resolve();
      };
      // run this the first time through, to setup stores
      request.onupgradeneeded = function(event) {
        var idb = event.target.result;
        idb.createObjectStore("book", {keyPath: "id"});
        idb.createObjectStore("solved", {keyPath: "bookId"});
      };

      dbDfd.promise.then(function(e) {

        // open store, initialize if necessary, fetch contents, resolve promise with contents
        var bookStore = db.transaction(["book"], "readonly").objectStore("book");
        var bookRequest = bookStore.getAll();

        var solvedStore = db.transaction(["solved"], "readonly").objectStore("solved");
        // FIXME: get just the solved ones for this store please:
        var solvedRequest = solvedStore.getAll();

        bookRequest.onsuccess = function(e) {
          book = e.result;
          bookDfd.resolve(book);
        };
        bookRequest.onerror = function(e) {
          // TODO: if there is no book defined, load default book and return it
          bookDfd.reject();
        };

        solvedRequest.onsuccess = function(e) {
          solved = e.result;
          solvedDfd.resolve(solved);
        };
        solvedRequest.onerror = function(e) {
          solvedDfd.reject();
        };
      });

      return {
        promise: $q.all([dbDfd.promise, bookDfd.promise, solvedDfd.promise]),
        db: db,
        book: book,
        solved: solved
      };
    }]);<|endoftext|>


<commit_before>var Immutable = require("immutable");
var R         = require("ramda");
var titleCase = require("title-case");

exports.siti = {
    filter: function (item, search) {
        var searchRegExp = new RegExp(search, "i");
        return !R.isNil(item) ? (
            searchRegExp.test(item.get("societa")) ||
            searchRegExp.test(item.get("idCoin")) ||
            searchRegExp.test(item.get("pod"))
        ) : null;
    },
    getLabel: function (sito) {
        return R.is(Immutable.Map, sito) ? (
            [
                titleCase(sito.get("societa")),
                titleCase(sito.get("idCoin"))
            ].join(" - ")
        ) : "";
    },
    getKey: function (sito) {
        return R.is(Immutable.Map, sito) ? sito.get("_id") : "";
    }
};

exports.labelGraph = {
    getYLabel: function (tipologia) {
        if (tipologia.key === "energia attiva") {
            return "kWh";
        } else if (tipologia.key === "potenza massima") {
            return "kW";
        } else if (tipologia.key === "energia reattiva") {
            return "kVARh";
        } else {
            return "";
        }
    },
    getY2Label: function (consumption) {
        if (consumption.key === "temperature") {
            return "°C";
        } else if (consumption.key === "humidity") {
            return "%";
        } else if (consumption.key === "illuminance") {
            return "Lux";
        } else if (consumption.key === "co2") {
            return "ppm";
        } else {
            return "";
        }
    }
};

/*
    objFromDB: {
        _id: uuid,
        sitoId: innowatio pod string,
        podId: uuid,
        month: "YYYY-MM",
        readings: {
            "energia attiva": JSON string of array,
            "energia reattiva": JSON string of array,
            "potenza massima": JSON string of array,
            ...
        }
    }
*/
exports.measures = {
    convertByVariables: R.memoize(function (measures, variables, startOfTime) {
        var mLength;
        const fiveMinutesInMS = 5 * 60 * 1000;
        const startOfMonthInMS = !R.isNil(startOfTime) ? startOfTime.getTime() : new Date(measures.get("month")).getTime();
        const measuresArray = R.map(variable => {
            const m = (measures.getIn(["readings", variable]) || "")
                .split(",")
                .map(v => parseFloat(v));
            mLength = m.length;
            return R.range(0, mLength).map(idx => m[idx]);
        }, variables);
        const toDateTime = (index) => (
            startOfMonthInMS + (index * fiveMinutesInMS)
        );
        const isInRange = (val1, val2) => (
            val1 * 1.10 > val2 &&
            val1 * 0.90 < val2
        );
        const getValue = (idx, ...vals) => {
            return [
                new Date(toDateTime(idx)),
                ...vals.map(val => [val])
            ];
        };
        const needsToAdd = (val, prevVal, idx) => {
            return (
                // Add the first
                idx === 0 ||
                // Add the last
                idx === mLength - 1 ||
                // Add out of range values
                !isInRange(prevVal, val) ||
                // Add every even hour
                idx % 24 === 12
            );
        };
        return R.reduce((acc, num) => {
            const needsTo = measuresArray.reduce((need, m, midx) => {
                return need || needsToAdd(
                    m[num],
                    R.path([midx + 1, 0], R.last(acc)),
                    num
                );
            }, false);
            return needsTo ? (
                acc.concat([getValue(
                    num,
                    ...measuresArray.map(m => m[num])
                )])
            ) : acc;
        }, [], R.range(0, mLength));
    }),
    convertBySitesAndVariable: function (measures, pods, variable) {
        var self = this;
        var measuresBySito = [];
        pods.forEach(pod => {
            measures.filter(function (misura) {
                return misura.get("podId") === pod;
            })
            .forEach(function (values) {
                measuresBySito.push(self.convertByVariables(values, [variable]));
            });
        });
        return this.mergeCoordinates(measuresBySito[0] || [], measuresBySito[1] || []);
    },
    convertByDatesAndVariable: function (measures, pod, variable, dates) {
        // console.log(measures);
        // console.log(pod);
        // console.log(variable);
        // console.log(dates);
        var self = this;
        var measuresByDates = [];
        dates.forEach(date => {
            measures.filter(function (misura) {
                return misura.get("podId") === pod;
            })
            .filter(function (misura) {
                return misura.get("month") === date;
            })
            .forEach(function (values) {
                measuresByDates.push(self.convertByVariables(values, [variable], new Date(0)));
            });
        });
        return this.mergeCoordinates(measuresByDates[0] || [], measuresByDates[1] || []);
    },
    mergeCoordinates: function (coordinate1, coordinate2) {
        /*
            f(a,b) => c

            a = [[[date1, [n01, dev01]], [date02, [n02, dev02]], ... ]
            b = [[[date1, [n11, dev11]], [date12, [n12, dev12]], ... ]
            c = [[[date1, [n01, dev01], [n11, dev11]], [date02, [n02, dev02], [n12, dev12]], ...]
        */
        var maxCriteria = function (a) {
            return !R.isNil(a) ? a.length : null;
        };
        var maxCoordinate = R.maxBy(maxCriteria, coordinate1, coordinate2);
        var minCoordinate = maxCoordinate === coordinate1 ? coordinate2 : coordinate1;

        return maxCoordinate.map(function (value, index) {
            var toConcat = [NaN];
            if (minCoordinate.length > index) {
                toConcat = minCoordinate[index][1];
            }
            if (coordinate1 === maxCoordinate) {
                return value.concat([toConcat]);
            } else {
                return [value[0], toConcat].concat([value[1]]);
            }
        });
    }
};
<commit_msg>IWWA-103 added a value of 0.01 to measures null to improve performance<commit_after>var Immutable = require("immutable");
var R         = require("ramda");
var titleCase = require("title-case");

exports.siti = {
    filter: function (item, search) {
        var searchRegExp = new RegExp(search, "i");
        return !R.isNil(item) ? (
            searchRegExp.test(item.get("societa")) ||
            searchRegExp.test(item.get("idCoin")) ||
            searchRegExp.test(item.get("pod"))
        ) : null;
    },
    getLabel: function (sito) {
        return R.is(Immutable.Map, sito) ? (
            [
                titleCase(sito.get("societa")),
                titleCase(sito.get("idCoin"))
            ].join(" - ")
        ) : "";
    },
    getKey: function (sito) {
        return R.is(Immutable.Map, sito) ? sito.get("_id") : "";
    }
};

exports.labelGraph = {
    getYLabel: function (tipologia) {
        if (tipologia.key === "energia attiva") {
            return "kWh";
        } else if (tipologia.key === "potenza massima") {
            return "kW";
        } else if (tipologia.key === "energia reattiva") {
            return "kVARh";
        } else {
            return "";
        }
    },
    getY2Label: function (consumption) {
        if (consumption.key === "temperature") {
            return "°C";
        } else if (consumption.key === "humidity") {
            return "%";
        } else if (consumption.key === "illuminance") {
            return "Lux";
        } else if (consumption.key === "co2") {
            return "ppm";
        } else {
            return "";
        }
    }
};

/*
    objFromDB: {
        _id: uuid,
        sitoId: innowatio pod string,
        podId: uuid,
        month: "YYYY-MM",
        readings: {
            "energia attiva": JSON string of array,
            "energia reattiva": JSON string of array,
            "potenza massima": JSON string of array,
            ...
        }
    }
*/
exports.measures = {
    convertByVariables: R.memoize(function (measures, variables, startOfTime) {
        var mLength;
        const fiveMinutesInMS = 5 * 60 * 1000;
        const startOfMonthInMS = !R.isNil(startOfTime) ? startOfTime.getTime() : new Date(measures.get("month")).getTime();
        const measuresArray = R.map(variable => {
            const m = (measures.getIn(["readings", variable]) || "")
                .split(",")
                .map(v => parseFloat(v));
            mLength = m.length;
            return R.range(0, mLength).map(idx => m[idx] || 0.006);
        }, variables);
        const toDateTime = (index) => (
            startOfMonthInMS + (index * fiveMinutesInMS)
        );
        const isInRange = (val1, val2) => (
            val1 * 1.10 > val2 &&
            val1 * 0.90 < val2
        );
        const getValue = (idx, ...vals) => {
            return [
                new Date(toDateTime(idx)),
                ...vals.map(val => [val])
            ];
        };
        const needsToAdd = (val, prevVal, idx) => {
            return (
                // Add the first
                idx === 0 ||
                // Add the last
                idx === mLength - 1 ||
                // Add out of range values
                !isInRange(prevVal, val) ||
                // Add every even hour
                idx % 24 === 12
            );
        };
        return R.reduce((acc, num) => {
            const needsTo = measuresArray.reduce((need, m, midx) => {
                return need || needsToAdd(
                    m[num],
                    R.path([midx + 1, 0], R.last(acc)),
                    num
                );
            }, false);
            return needsTo ? (
                acc.concat([getValue(
                    num,
                    ...measuresArray.map(m => m[num])
                )])
            ) : acc;
        }, [], R.range(0, mLength));
    }),
    convertBySitesAndVariable: function (measures, pods, variable) {
        var self = this;
        var measuresBySito = [];
        pods.forEach(pod => {
            measures.filter(function (misura) {
                return misura.get("podId") === pod;
            })
            .forEach(function (values) {
                measuresBySito.push(self.convertByVariables(values, [variable]));
            });
        });
        return this.mergeCoordinates(measuresBySito[0] || [], measuresBySito[1] || []);
    },
    convertByDatesAndVariable: function (measures, pod, variable, dates) {
        // console.log(measures);
        // console.log(pod);
        // console.log(variable);
        // console.log(dates);
        var self = this;
        var measuresByDates = [];
        dates.forEach(date => {
            measures.filter(function (misura) {
                return misura.get("podId") === pod;
            })
            .filter(function (misura) {
                return misura.get("month") === date;
            })
            .forEach(function (values) {
                measuresByDates.push(self.convertByVariables(values, [variable], new Date(0)));
            });
        });
        return this.mergeCoordinates(measuresByDates[0] || [], measuresByDates[1] || []);
    },
    mergeCoordinates: function (coordinate1, coordinate2) {
        /*
            f(a,b) => c

            a = [[[date1, [n01, dev01]], [date02, [n02, dev02]], ... ]
            b = [[[date1, [n11, dev11]], [date12, [n12, dev12]], ... ]
            c = [[[date1, [n01, dev01], [n11, dev11]], [date02, [n02, dev02], [n12, dev12]], ...]
        */
        var maxCriteria = function (a) {
            return !R.isNil(a) ? a.length : null;
        };
        var maxCoordinate = R.maxBy(maxCriteria, coordinate1, coordinate2);
        var minCoordinate = maxCoordinate === coordinate1 ? coordinate2 : coordinate1;

        return maxCoordinate.map(function (value, index) {
            var toConcat = [NaN];
            if (minCoordinate.length > index) {
                toConcat = minCoordinate[index][1];
            }
            if (coordinate1 === maxCoordinate) {
                return value.concat([toConcat]);
            } else {
                return [value[0], toConcat].concat([value[1]]);
            }
        });
    }
};
<|endoftext|>