{"version":3,"sources":["TableVizRouter.js","DataResponseModel.js","DataTableView.js","NoTableDataView.js","SelectorsView.js"],"names":["ns","sharedNs","window","nspace","TableVizRouter","routers","VizRouterBase","extend","routes","",":indicatorId",":indicatorId/:dataFormatId",":indicatorId/:dataFormatId/:timeFrameId",":indicatorId/:dataFormatId/:timeFrameId/:sortColumn",":indicatorId/:dataFormatId/:timeFrameId/:sortColumn/:sortDirection","initializeViews","indicatorId","dataFormatId","timeFrameId","sortColumn","sortDirection","parseInt","this","selectorsModel","models","SelectorsModel","set","silent","dataModel","DataResponseModel","dispatcher","dataUrl","views","loadingSpinner","LoadingSpinnerView","sourceDefinitionNotesView","SourceDefinitionNotesView","model","dataTable","DataTableView","dataResponseModel","selectorsView","SelectorsView","indicatorGroupId","noTableDataView","NoTableDataView","completeRouterInitialization","routeUrlParts","get","_","getDataResponseOnChange","getDataResponse","getFilterString","params","filterStringNvc","ccc","viz","util","UrlParams","velir","collections","NameValueCollection","add","indicator","timeFrame","dataFormat","toQueryString","getDataParams","filterString","hasData","$","Backbone","View","el","templateSelector","name","initialize","options","self","initialRenderPromise","Deferred","bindAll","on","render","onBeforeChange","$el","props","data","is","getInitialRenderPromise","newProps","loadTemplate","template","$tmpl","length","html","sortData","response","nycRow","outputRows","locationTypeHeaders","locationRowsByType","locTypeId","each","rows","row","rowType","locationTypeId","hasOwnProperty","push","clone","sorted","sortBy","cells","displayValue","value","numWithoutData","Number","MIN_VALUE","MAX_VALUE","n","cell","str","isBlank","abbreviation","isNaN","parseFloat","isFinite","reverse","locTypeHeader","locationRowsForThisType","concat","sortedResponse","sortChangedCount","filter","keys","changed","key","onlySortChanged","size","css","when","done","getSummaryFunc","table","instances","getDataSummaryByPropertyNames","summary1","indicatorGroupName","currentDistributionName","summary2","dataSummary","text","templateData","hide","fadeIn","resolve","jQuery","toggle","selectorNs","initializeView","initializeSelectors","allSelectors","breakdownSelectorView","BreakdownSelectorView","hideCharacteristics","dataTypeSelectorView","DataTypeSelectorView","getSelectorInfoFunc","getSelectorInfoOrThrow","timeFrameSelectorView","TimeFrameSelectorView","getIndicatorSpecificSelectors"],"mappings":"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","file":"table-generated.js","sourcesContent":["/// \r\n/// \r\n/// \r\n/// \r\n/// \r\n\r\n(function(_) {\r\n var ns, sharedNs;\r\n\r\n ns = window.nspace(\"ccc.viz.table\");\r\n sharedNs = window.nspace(\"ccc.viz.shared\");\r\n\r\n ns.TableVizRouter = sharedNs.routers.VizRouterBase.extend({\r\n routes: {\r\n \"\": \"initializeViews\",\r\n \":indicatorId\": \"initializeViews\",\r\n \":indicatorId/:dataFormatId\": \"initializeViews\",\r\n \":indicatorId/:dataFormatId/:timeFrameId\": \"initializeViews\",\r\n \":indicatorId/:dataFormatId/:timeFrameId/:sortColumn\": \"initializeViews\",\r\n \":indicatorId/:dataFormatId/:timeFrameId/:sortColumn/:sortDirection\":\r\n \"initializeViews\"\r\n },\r\n\r\n /**\r\n * This method will only ever be invoked a single time, so use it to initialize\r\n * the model/view classes and trigger the initial rendering\r\n */\r\n initializeViews: function(\r\n indicatorId,\r\n dataFormatId,\r\n timeFrameId,\r\n sortColumn,\r\n sortDirection\r\n ) {\r\n //// MODELS ////\r\n\r\n if (sortColumn && sortColumn !== \"a\" && sortColumn !== \"abbr\") {\r\n sortColumn = parseInt(sortColumn, 10);\r\n }\r\n\r\n // Create the selectors model, which represents anything that is selectable by the user and tracked in the URL\r\n this.selectorsModel = new sharedNs.models.SelectorsModel();\r\n this.selectorsModel.set(\r\n {\r\n indicatorId: indicatorId,\r\n dataFormatId: dataFormatId,\r\n timeFrameId: timeFrameId,\r\n sortColumn: sortColumn,\r\n sortDirection: sortDirection\r\n },\r\n {\r\n silent: true\r\n }\r\n );\r\n\r\n // Create the data response model, the model that is responsible for querying the server-side for data,\r\n // storing the response, and triggering updates to other views after the response has been retrieved\r\n this.dataModel = new ns.models.DataResponseModel({\r\n selectorsModel: this.selectorsModel,\r\n dispatcher: this.dispatcher,\r\n dataUrl: \"/api/TableData/GetTableData\"\r\n });\r\n\r\n //// VIEWS ////\r\n\r\n this.views = {\r\n // Loading spinner (no model required)\r\n loadingSpinner: new sharedNs.views.LoadingSpinnerView({\r\n dispatcher: this.dispatcher\r\n }),\r\n\r\n // Source/Definition/Notes\r\n sourceDefinitionNotesView: new sharedNs.views.SourceDefinitionNotesView(\r\n {\r\n selectorsModel: this.selectorsModel,\r\n model: this.dataModel\r\n }\r\n ),\r\n\r\n // The main data table\r\n dataTable: new ns.views.DataTableView({\r\n model: this.selectorsModel,\r\n dataResponseModel: this.dataModel\r\n }),\r\n\r\n // Selectors. These must be instantiated late, since these views are used to bootstrap\r\n // the selectors model values, and certain other views need to hear when the selectors model changes.\r\n selectorsView: new ns.views.SelectorsView({\r\n model: this.selectorsModel,\r\n indicatorGroupId: this.indicatorGroupId,\r\n dispatcher: this.dispatcher\r\n }),\r\n\r\n noTableDataView: new ns.views.NoTableDataView({\r\n selectorsModel: this.selectorsModel,\r\n model: this.dataModel\r\n })\r\n };\r\n\r\n this.completeRouterInitialization();\r\n },\r\n\r\n routeUrlParts: function() {\r\n return [\r\n this.selectorsModel.get(\"indicatorId\"),\r\n this.selectorsModel.get(\"dataFormatId\"),\r\n this.selectorsModel.get(\"timeFrameId\"),\r\n this.selectorsModel.get(\"sortColumn\"),\r\n this.selectorsModel.get(\"sortDirection\")\r\n ];\r\n }\r\n });\r\n})(_);\r\n","/// \r\n/// \r\n/// \r\n/// \r\n\r\n(function() {\r\n var ns, sharedNs;\r\n\r\n ns = window.nspace(\"ccc.viz.table.models\");\r\n sharedNs = window.nspace(\"ccc.viz.shared.models\");\r\n\r\n /**\r\n * Data response model is responsible for querying the server-side for data,\r\n * storing the response, and other views listen for the response to change\r\n * in order to be updated when new data is retrieved\r\n */\r\n ns.DataResponseModel = sharedNs.DataResponseModel.extend({\r\n getDataResponseOnChange: function() {\r\n this.getDataResponse();\r\n },\r\n\r\n getFilterString: function() {\r\n var params, filterStringNvc, filterString;\r\n\r\n params = window.ccc.viz.util.UrlParams;\r\n\r\n filterStringNvc = new window.velir.collections.NameValueCollection();\r\n filterStringNvc.add(\r\n params.indicator,\r\n this.selectorsModel.get(\"indicatorId\")\r\n );\r\n filterStringNvc.add(\r\n params.timeFrame,\r\n this.selectorsModel.get(\"timeFrameId\")\r\n );\r\n filterStringNvc.add(\r\n params.dataFormat,\r\n this.selectorsModel.get(\"dataFormatId\")\r\n );\r\n\r\n filterString = filterStringNvc.toQueryString();\r\n\r\n return filterString;\r\n },\r\n\r\n getDataParams: function(filterString) {\r\n return {\r\n filterString: filterString\r\n };\r\n },\r\n\r\n hasData: function() {\r\n return this.get(\"response\").hasData;\r\n }\r\n });\r\n})();\r\n","(function($, _) {\r\n var ns = window.nspace(\"ccc.viz.table.views\");\r\n\r\n // Clever way to tell if we are sorting by a number.\r\n // From here: http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844\r\n function isNumber(n) {\r\n return !isNaN(parseFloat(n)) && isFinite(n);\r\n }\r\n\r\n /**\r\n * The main data table\r\n */\r\n ns.DataTableView = Backbone.View.extend({\r\n el: \"#data-table-container\",\r\n templateSelector: \"#data-table-template\",\r\n name: \"DataTableView\",\r\n\r\n initialize: function(options) {\r\n var self = this;\r\n this.initialRenderPromise = $.Deferred();\r\n\r\n _.bindAll(\r\n this,\r\n \"render\",\r\n \"loadTemplate\",\r\n \"sortData\",\r\n \"onBeforeChange\",\r\n \"getInitialRenderPromise\"\r\n );\r\n\r\n this.dataResponseModel = options.dataResponseModel;\r\n\r\n this.model.on(\"change\", this.render);\r\n\r\n // Handle the before change event so that we have an opportunity to update the\r\n // Sort Column when the indicator ID changes. This will prevent multiple \"change\"\r\n // events from being emitted in response to changing the indicator.\r\n this.model.on(\"before-change\", this.onBeforeChange);\r\n\r\n // Listen for clicks on the sort buttons and update the selectors model\r\n this.$el.on(\r\n \"click\",\r\n \"button[data-sort-up], button[data-sort-down]\",\r\n function() {\r\n var props = {\r\n sortColumn: $(this).data(\"column\")\r\n };\r\n\r\n if ($(this).is(\"[data-sort-up]\")) {\r\n props.sortDirection = \"u\";\r\n }\r\n else {\r\n props.sortDirection = \"d\";\r\n }\r\n\r\n self.model.set(props);\r\n }\r\n );\r\n },\r\n\r\n getInitialRenderPromise: function() {\r\n return this.initialRenderPromise;\r\n },\r\n\r\n onBeforeChange: function(newProps) {\r\n // If the indicator ID is changing, then remove the selected sort column\r\n if (this.model.get(\"indicatorId\") !== newProps.indicatorId) {\r\n newProps[\"sortColumn\"] = null;\r\n newProps[\"sortDirection\"] = null;\r\n }\r\n },\r\n\r\n loadTemplate: function() {\r\n // Get the underscore template\r\n if (!this.template) {\r\n var $tmpl = $(this.templateSelector);\r\n if ($tmpl.length === 0) {\r\n throw \"Could not find the \" +\r\n this.name +\r\n \" template: \" +\r\n this.templateSelector;\r\n }\r\n\r\n // Pre-compile and cache the template\r\n this.template = _.template($tmpl.html());\r\n }\r\n },\r\n\r\n sortData: function(response) {\r\n var self = this;\r\n\r\n var sortColumn = this.model.get(\"sortColumn\");\r\n if (sortColumn == null || sortColumn === \"a\") {\r\n return response;\r\n }\r\n\r\n // Iterate over rows in the table and gather the items to be sorted\r\n var outputRows = [];\r\n var locationTypeHeaders = [];\r\n var locationRowsByType = {};\r\n var nycRow;\r\n _.each(response.rows, function(row) {\r\n // If it's a header row, just copy it straight to the output\r\n if (row.rowType === \"Header\") {\r\n outputRows.push(row);\r\n return;\r\n }\r\n\r\n // NYC (location type ID 1) always stays alone at the top, so save it off separately\r\n if (row.locationTypeId === 1) {\r\n nycRow = row;\r\n return;\r\n }\r\n\r\n // Save off the location type rows\r\n if (row.rowType === \"LocationType\") {\r\n locationTypeHeaders.push(row);\r\n return;\r\n }\r\n\r\n // Any remaining rows must be a data row. Index them by location type\r\n if (!locationRowsByType.hasOwnProperty(row.locationTypeId)) {\r\n locationRowsByType[row.locationTypeId] = [];\r\n }\r\n locationRowsByType[row.locationTypeId].push(row);\r\n });\r\n\r\n // Sort each group of location rows by the specified column\r\n for (var locTypeId in locationRowsByType) {\r\n var rows = locationRowsByType[locTypeId];\r\n\r\n // Clone the rows so that we're not modifying to original instance\r\n var clone = [];\r\n _.each(rows, function(row) {\r\n clone.push(_.clone(row));\r\n });\r\n rows = clone;\r\n\r\n var numWithoutData = 0;\r\n var sorted = _.sortBy(rows, function(row) {\r\n // If we're sorting by the first column, it's location names, so sort by alpha\r\n if (sortColumn === 0) {\r\n return row.cells[0].displayValue;\r\n }\r\n\r\n // Are we sorting by abbreviation?\r\n if (sortColumn === \"abbr\") {\r\n var cell = row.cells[0];\r\n return _.str.isBlank(cell.abbreviation)\r\n ? cell.displayValue\r\n : // Sometimes the abbreviations are numbers that vary in # of decimal places.\r\n // Detect numbers and parse them out.\r\n isNumber(cell.abbreviation)\r\n ? parseFloat(cell.abbreviation)\r\n : cell.abbreviation;\r\n }\r\n\r\n // Otherwise, sort by the data value\r\n if (row.cells[sortColumn].hasOwnProperty(\"value\")) {\r\n return row.cells[sortColumn].value;\r\n }\r\n\r\n // No value? Sort to bottom\r\n numWithoutData++;\r\n return self.model.get(\"sortDirection\") === \"d\"\r\n ? Number.MIN_VALUE\r\n : Number.MAX_VALUE;\r\n });\r\n\r\n // Reverse the order if we're sorting highest first\r\n if (this.model.get(\"sortDirection\") === \"d\") {\r\n sorted.reverse();\r\n }\r\n\r\n // Save the results\r\n locationRowsByType[locTypeId] = sorted;\r\n }\r\n\r\n // Reassemble the output\r\n outputRows.push(nycRow);\r\n _.each(locationTypeHeaders, function(locTypeHeader) {\r\n outputRows.push(locTypeHeader);\r\n\r\n var locationRowsForThisType = locationRowsByType[locTypeHeader.locationTypeId];\r\n if (locationRowsForThisType) {\r\n outputRows = outputRows.concat(locationRowsForThisType);\r\n }\r\n });\r\n\r\n var sortedResponse = _.extend({}, response);\r\n sortedResponse.rows = outputRows;\r\n\r\n return sortedResponse;\r\n },\r\n\r\n /**\r\n * Renders the data table\r\n */\r\n render: function() {\r\n var self = this;\r\n\r\n this.loadTemplate();\r\n\r\n // If the only thing that changed was sorting, we want to show the transition differently\r\n var sortChangedCount = _.filter(_.keys(self.model.changed), function(\r\n key\r\n ) {\r\n return key === \"sortColumn\" || key === \"sortDirection\";\r\n }).length;\r\n\r\n var onlySortChanged = _.size(self.model.changed) === sortChangedCount;\r\n\r\n if (!onlySortChanged) {\r\n this.$el.css(\"visibility\", \"hidden\");\r\n }\r\n\r\n // Get the data to display\r\n $.when(this.dataResponseModel.getDataResponse()).done(function() {\r\n // Gather the properties necessary to render the template\r\n var response = self.dataResponseModel.get(\"response\");\r\n\r\n // Apply sorting, if any\r\n response = self.sortData(response);\r\n\r\n // Data summary -- Pull it from the global selectors instance.\r\n var getSummaryFunc =\r\n ccc.viz.table.views.instances.SelectorsView\r\n .getDataSummaryByPropertyNames;\r\n \r\n\tvar summary1 = response.indicatorGroupName;\r\n\r\n if (!_.str.isBlank(response.currentDistributionName)) {\r\n summary1 += \": \" + response.currentDistributionName;\r\n }\r\n var summary2 = getSummaryFunc([\"timeFrameId\", \"dataFormatId\"]);\r\n\r\n // Update the data summary up in the selector area (Maybe this should be in a different view? Meh.)\r\n var distro = _.str.isBlank(response.currentDistributionName)\r\n ? \"\"\r\n : response.currentDistributionName + \"; \";\r\n var dataSummary = distro + summary2;\r\n $(\"#data-summary\").text(dataSummary);\r\n\r\n // Assemble the data necessary to render the template\r\n var templateData = _.extend(\r\n {\r\n sortColumn: self.model.get(\"sortColumn\"),\r\n sortDirection: self.model.get(\"sortDirection\"),\r\n summary1: summary1,\r\n summary2: summary2\r\n },\r\n response\r\n );\r\n\r\n // Render the contents\r\n self.$el.html(self.template(templateData));\r\n\r\n // Ensure we're visible / animate the transition\r\n self.$el.css(\"visibility\", \"visible\");\r\n\r\n // If the only thing that changed was sorting, only animate the rows after the headers\r\n if (onlySortChanged) {\r\n $(\"tr:not(.table-sort-row):not(.column-title-row)\", self.el)\r\n .hide()\r\n .fadeIn(function() {\r\n self.initialRenderPromise.resolve();\r\n });\r\n }\r\n // Otherwise, animate the whole thing\r\n else {\r\n self.$el.hide().fadeIn(function() {\r\n self.initialRenderPromise.resolve();\r\n });\r\n }\r\n });\r\n }\r\n });\r\n})(jQuery, _);\r\n","(function($, _) {\r\n var ns = window.nspace(\"ccc.viz.table.views\");\r\n\r\n ns.NoTableDataView = Backbone.View.extend({\r\n el: \"#overlay\",\r\n name: \"No Data Overlay\",\r\n\r\n initialize: function() {\r\n var self = this;\r\n _.bindAll(this, \"render\");\r\n\r\n this.model.on(\"change\", this.render);\r\n\r\n // Hide the legend text when a data update begins\r\n this.model.on(\"busy\", function() {\r\n self.$el.hide();\r\n });\r\n },\r\n\r\n render: function() {\r\n this.$el.toggle(!this.model.hasData());\r\n }\r\n });\r\n})(jQuery, _);\r\n","/// \r\n/// \r\n/// \r\n\r\n(function($, _) {\r\n var ns, selectorNs, sharedNs;\r\n\r\n ns = window.nspace(\"ccc.viz.table.views\");\r\n selectorNs = window.nspace(\"ccc.viz.selectors\");\r\n sharedNs = window.nspace(\"ccc.viz.shared\");\r\n\r\n ns.SelectorsView = sharedNs.views.SelectorsView.extend({\r\n initializeView: function() {\r\n // Expose this (presumably one-and-only) instance globally so that other components\r\n // can call \"public\" methods and get selector information, such as for building data summaries.\r\n // TODO: What's a better way to architect this?\r\n ns.instances = { SelectorsView: this };\r\n },\r\n\r\n initializeSelectors: function() {\r\n this.allSelectors = [\r\n // Breakdown\r\n (this.breakdownSelectorView = new selectorNs.BreakdownSelectorView({\r\n model: this.model,\r\n hideCharacteristics: true\r\n })),\r\n // Data Type\r\n (this.dataTypeSelectorView = new selectorNs.DataTypeSelectorView({\r\n model: this.model,\r\n getSelectorInfoFunc: this.getSelectorInfoOrThrow\r\n })),\r\n // TimeFrame\r\n (this.timeFrameSelectorView = new selectorNs.TimeFrameSelectorView({\r\n model: this.model,\r\n getSelectorInfoFunc: this.getSelectorInfoOrThrow\r\n }))\r\n ];\r\n },\r\n\r\n getIndicatorSpecificSelectors: function() {\r\n return [this.dataTypeSelectorView, this.timeFrameSelectorView];\r\n }\r\n });\r\n})(jQuery, _);\r\n"]}