{"version":3,"sources":["IndicatorSelectionButtonsView.js","IndicatorSelectionModel.js","IndicatorSelectionRouter.js","IndicatorSelectionTreeView.js"],"names":["$","_","nspace","IndicatorSelectionButtonsView","Backbone","View","extend","initialize","self","this","bindAll","$container","model","on","render","e","preventDefault","set","currentTab","data","currentTabId","dataLayer","push","event","eventCategory","eventAction","text","trim","eventLabel","window","location","pathname","removeClass","get","addClass","id","hash","options","optOutOfLinkRewrite","each","$link","baseUrl","attr","split","jQuery","IndicatorSelectionModel","Model","defaults","selectedCategories","ns","IndicatorSelectionRouter","Router","routes","",":currentTab",":currentTab/:selectedCategories","initializeViews","silent","catIds","catIdHash","i","length","parseInt","updateUrl","selectorsView","tableView","IndicatorSelectionTreeView","trigger","urlParts","cats","size","keys","join","navigate","replace","newIds","closest","selectedIds","has","omit","$topLevelCategories","ids","suppressEDIndicatorExpansion","elementsToHide","elementsToShow","isHidden","$element","stop","css","hideOrShow","$elements","notHiddenCallback","rawIds","locTypeId","index","inArray","merge","filter","parent","nextParent","slideUp","slideDown","catId","catIsSelected","locationType","categoryName"],"mappings":"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","file":"indicator-selection-generated.js","sourcesContent":["/// \r\n/// \r\n\r\n(function($, _) {\r\n var ns = nspace(\"ccc.indicatorSelection\");\r\n\r\n ns.IndicatorSelectionButtonsView = Backbone.View.extend({\r\n initialize: function(options) {\r\n var self = this;\r\n _.bindAll(this, \"render\");\r\n\r\n this.$container = $(\".location-type-list\");\r\n\r\n // Rerender on model changes\r\n this.model.on(\"change\", this.render);\r\n\r\n // Listen for clicks and update the model accordingly\r\n this.$container.on(\"click\", \".location-type-link\", function(e) {\r\n e.preventDefault();\r\n self.model.set({\r\n currentTab: $(this).data(\"slug\"),\r\n currentTabId: $(this).data(\"id\")\r\n });\r\n\r\n // Tracks clicks on Location Type tabs (Borough, CD, City) from the \"Explore Data\" page\r\n dataLayer.push({\r\n event: \"Location Type Tab Clicks\",\r\n eventCategory: \"Interactive Clicks\",\r\n eventAction:\r\n \"Location Type Tab|\" +\r\n $(this)\r\n .text()\r\n .trim(),\r\n eventLabel: window.location.pathname\r\n });\r\n });\r\n },\r\n\r\n render: function() {\r\n // The button html is rendered on the server side, so just\r\n // assign the active class to the currently selected button\r\n $(\".location-type-link\", this.$container).removeClass(\"active\");\r\n $(\r\n \".location-type-link[data-slug='\" + this.model.get(\"currentTab\") + \"']\",\r\n this.$container\r\n ).addClass(\"active\");\r\n\r\n // If a specific location type was just selected, run through all the links on the page and append a #hash that\r\n // will pre-select the location type on the destination page\r\n var id = this.model.get(\"currentTabId\");\r\n var hash = id > 0 ? \"#a/a/\" + id : \"\";\r\n\r\n // We want to be able to opt out of link rewrites when using the indicator selectors in the indicator windowshade.\r\n if (this.options.optOutOfLinkRewrite) {\r\n return;\r\n }\r\n $(\"a.subtopic-list-link\").each(function() {\r\n // Strip any existing #hash\r\n var $link = $(this);\r\n var baseUrl = $link.attr(\"href\").split(\"#\")[0];\r\n\r\n // Append the new #hash\r\n $link.attr(\"href\", baseUrl + hash);\r\n });\r\n }\r\n });\r\n})(jQuery, _);\r\n","/// \r\n/// \r\n\r\n(function() {\r\n var ns = nspace(\"ccc.indicatorSelection\");\r\n\r\n ns.IndicatorSelectionModel = Backbone.Model.extend({\r\n defaults: {\r\n currentTab: \"any\",\r\n currentTabId: 0,\r\n selectedCategories: {}\r\n }\r\n });\r\n})(jQuery);\r\n","/// \r\n/// \r\n\r\n(function(_) {\r\n var ns = nspace(\"ccc.indicatorSelection\");\r\n\r\n ns.IndicatorSelectionRouter = Backbone.Router.extend({\r\n initialize: function() {\r\n _.bindAll(this, \"updateUrl\");\r\n },\r\n\r\n routes: {\r\n \"\": \"initializeViews\",\r\n \":currentTab\": \"initializeViews\",\r\n \":currentTab/:selectedCategories\": \"initializeViews\"\r\n },\r\n\r\n initializeViews: function(currentTab, selectedCategories) {\r\n // This method will only ever be invoked a single time, so use\r\n // it to initialize the model/view classes and trigger the initial rendering\r\n this.model = new ns.IndicatorSelectionModel();\r\n if (typeof currentTab !== \"undefined\") {\r\n // Find the ID matching the specified tab name\r\n // NOTE: It's not ideal to interact directly with the DOM here in the router, but it IS expedient.\r\n var currentTabId = $(\r\n \".location-type-link[data-slug='\" + currentTab + \"']\"\r\n ).data(\"id\");\r\n\r\n this.model.set({\r\n\t\tcurrentTab: currentTab,\r\n\t\tcurrentTabId: currentTabId,\r\n\t\tsilent: true\r\n\t});\r\n\r\n }\r\n\r\n if (typeof selectedCategories !== \"undefined\") {\r\n var catIds = selectedCategories.split(\",\");\r\n var catIdHash = {};\r\n for (var i = 0; i < catIds.length; i++) {\r\n catIdHash[parseInt(catIds[i], 10)] = true;\r\n }\r\n this.model.set(\"selectedCategories\", catIdHash, {\r\n silent: true\r\n });\r\n }\r\n\r\n // Listen for changes to the model and update our URL\r\n this.model.on(\"change\", this.updateUrl);\r\n\r\n // Apply the model to the views\r\n this.selectorsView = new ns.IndicatorSelectionButtonsView({\r\n model: this.model\r\n });\r\n this.tableView = new ns.IndicatorSelectionTreeView({\r\n model: this.model\r\n });\r\n\r\n // Trigger a change notification on the model now that all of the selectors have finished initializing\r\n // so that other components can update themselves to reflect the current app state\r\n this.model.trigger(\"change\");\r\n },\r\n\r\n updateUrl: function() {\r\n var urlParts = [this.model.get(\"currentTab\")];\r\n\r\n var cats = this.model.get(\"selectedCategories\");\r\n if (cats && _.size(cats) > 0) {\r\n urlParts.push(_.keys(cats).join(\",\"));\r\n }\r\n\r\n this.navigate(urlParts.join(\"/\"), { replace: true });\r\n }\r\n });\r\n})(_);\r\n","/// \r\n/// \r\n\r\n(function($, _) {\r\n var ns = nspace(\"ccc.indicatorSelection\");\r\n\r\n ns.IndicatorSelectionTreeView = Backbone.View.extend({\r\n initialize: function() {\r\n _.bindAll(this, \"render\");\r\n var self = this;\r\n\r\n this.model.on(\"change\", this.render);\r\n\r\n // Flash the indicator tree whenever the tab changes so the user knows\r\n // the it has been updated.\r\n // NOTE: Disabled for now... seemed too \"heavy handed\"\r\n //this.model.on(\"change:currentTab\", function() {\r\n //\t$(\".indicator-lists-container\").stop(true, true).effect(\"highlight\", 700);\r\n //});\r\n\r\n // Listen for clicks on the category nodes and toggle their visibilty\r\n $(\".indicator-lists-container\").on(\r\n \"click\",\r\n \"li[data-top-category] .indicator-list-link\",\r\n function(e) {\r\n var $catNode = $(this).closest(\"li[data-top-category]\");\r\n var id = $catNode.data(\"category-id\");\r\n\r\n var selectedIds = self.model.get(\"selectedCategories\");\r\n var newIds;\r\n if (_.has(selectedIds, id)) {\r\n // update the model\r\n newIds = _.omit(selectedIds, \"\" + id); // _.omit seems to be broken for int keys, so stringify the id\r\n self.model.set(\"selectedCategories\", newIds);\r\n }\n else {\r\n // update the model\r\n newIds = {};\r\n newIds[id] = true;\r\n _.extend(newIds, selectedIds);\r\n self.model.set(\"selectedCategories\", newIds);\r\n }\r\n }\r\n );\r\n\r\n // Listen for a click on the \"expand/collapse\" button and update the model\r\n $(\".indicator-tree-toggle\").on(\"click\", function() {\r\n var $topLevelCategories = $(\"li[data-top-category]\"),\r\n ids = self.model.get(\"selectedCategories\"),\r\n newIds = {};\r\n\r\n self.suppressEDIndicatorExpansion = true;\r\n\r\n // if the number of selectedCategories in the model is equal to the\r\n // number of li's in the view, collapse; otherwise, expand\r\n if ($topLevelCategories.length === _.size(ids)) {\r\n // collapse\r\n self.model.set(\"selectedCategories\", {});\r\n }\n else {\r\n // Tracks when a user clicks the \"Expand All\" button on the \"Explore Data\" page\r\n dataLayer.push({\r\n event: \"ED Expand All\",\r\n eventCategory: \"Interactive Clicks\",\r\n eventAction: \"ED Expand All\",\r\n eventLabel: window.location.pathname\r\n });\r\n\r\n $topLevelCategories.each(function() {\r\n newIds[$(this).data(\"category-id\")] = true;\r\n });\r\n self.model.set(\"selectedCategories\", newIds);\r\n }\r\n\r\n self.suppressEDIndicatorExpansion = false;\r\n });\r\n },\r\n\r\n render: function() {\r\n // The tree html is rendered on the server side, so just\r\n // hide or show the indicator & category nodes that correspond with\r\n // the currently selected location type\r\n var self = this;\r\n var elementsToHide = [];\r\n var elementsToShow = [];\r\n var $topLevelCategories = $(\"li[data-top-category]\");\r\n\r\n function isHidden($element) {\r\n // The :visible/:hidden pseduoselectors are affected by parent visibility, but\r\n // for the purposes of deciding what to show, we need to know if the elements\r\n // would be hidden even if the parent element is not.\r\n // Test this by checking for CSS styles.\r\n return (\r\n $element\r\n .stop(true, true) // Finish any pending animations immediately, or we can't be sure\r\n .css(\"display\") === \"none\"\r\n ); // In this case, we need only look at \"display\"\r\n }\r\n\r\n function hideOrShow($elements, notHiddenCallback) {\r\n $elements.each(function() {\r\n var rawIds, areIdsMultiple, ids;\r\n\r\n // Parse out the list of location type Ids and see if\r\n // the current loc type ID is contained within\r\n var locTypeId = self.model.get(\"currentTabId\");\r\n rawIds = $(this).data(\"loctype-ids\");\r\n // We may get into a situation where we only have one location type defined for a category/indicator.\r\n // In this case, if there's only one location, .data will return an int, which doesn't have a split\r\n // function. So checking for the existing of that function will tells us to either split the location\r\n // type id or wrap it in an array.\r\n areIdsMultiple = !!rawIds.split;\r\n ids = areIdsMultiple ? rawIds.split(\",\") : [rawIds];\r\n\r\n var index = $.inArray(\"\" + locTypeId, ids);\r\n\r\n if (index > -1 && isHidden($(this))) {\r\n elementsToShow.push(this);\r\n }\n else if (index === -1 && !isHidden($(this))) {\r\n elementsToHide.push(this);\r\n\r\n // No need to continue since all children are now hidden\r\n return;\r\n }\r\n\r\n // The element wasn't hidden, so invoke the callback\r\n if (notHiddenCallback) {\r\n notHiddenCallback(this);\r\n }\r\n });\r\n }\r\n\r\n // If the current tab id is \"any\", show everything\r\n if (this.model.get(\"currentTabId\") < 1) {\r\n // Anything with a \"data-loctype-ids\" attribute is potentially hidden\r\n $.merge(\r\n elementsToShow,\r\n $(\"[data-loctype-ids]\").filter(function() {\r\n return isHidden($(this));\r\n })\r\n );\r\n }\n else {\r\n // Examine the top level categories first, then the subtopics, and finally\r\n // the individual indicator nodes.\r\n hideOrShow($(\"[data-top-category]\"), function(parent) {\r\n // Sub Categories\r\n hideOrShow($(\"[data-category]\", parent), function(nextParent) {\r\n // Indicators within sub categories\r\n hideOrShow($(\"[data-indicator]\", nextParent));\r\n });\r\n\r\n // Indicator nodes directly under the top level\r\n hideOrShow($(\"[data-indicator]\", parent));\r\n });\r\n }\r\n\r\n $(elementsToHide).slideUp(700);\r\n $(elementsToShow).slideDown(700);\r\n\r\n // Open / close the top level tree nodes as necessary\r\n var selectedIds = self.model.get(\"selectedCategories\");\r\n $topLevelCategories.each(function() {\r\n var catId = $(this).data(\"category-id\");\r\n var $container = $(\".indicator-list-toggle-container\", this);\r\n var catIsSelected = _.has(selectedIds, catId);\r\n if (catIsSelected && isHidden($container)) {\r\n $container.slideDown();\r\n $(\".indicator-list-link\", this).addClass(\"active\");\r\n\r\n // Tracks clicks on indicator expansion (Demographics, Economic Conditions, etc) on the \"Explore Data\" page\r\n var locationType = $(\".location-type-link.active\")\r\n .text()\r\n .trim();\r\n var categoryName = $(\".indicator-list-link\", this)\r\n .text()\r\n .trim();\r\n if (!self.suppressEDIndicatorExpansion) {\r\n dataLayer.push({\r\n event: \"ED Indicator Expansion\",\r\n eventCategory: \"Interactive Clicks\",\r\n eventAction:\r\n \"Indicator Expansion|\" + locationType + \"|\" + categoryName,\r\n eventLabel: window.location.pathname\r\n });\r\n }\r\n }\n else if (!catIsSelected && !isHidden($container)) {\r\n $container.slideUp();\r\n $(\".indicator-list-link\", this).removeClass(\"active\");\r\n }\r\n });\r\n\r\n $(\".indicator-tree-toggle\").text(\r\n _.size(selectedIds) === $topLevelCategories.length\r\n ? \"Collapse All\"\r\n : \"Expand All\"\r\n );\r\n }\r\n });\r\n})(jQuery, _);\r\n"]}