{"action":"created","comment":{"url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/comments/165799679","pull_request_review_id":93801772,"id":165799679,"diff_hunk":"@@ -57,16 +64,21 @@ var App = React.createClass({\n         <Row>\n           <Col lg={6}>\n             <div>\n-              <MapController\n+              <TheMapController\n                 meta = {this.getfilteredMeta()}\n+                area={this.state.area}\n                 onSetArea={this.handleSetArea}\n               />\n             </div>\n           </Col>\n           <Col lg={6}>\n             <DataController\n-              // ensemble_name, model_id, variable_id, experiment, area\n-              {...this.state}\n+              ensemble_name={this.state.ensemble_name}","path":"src/components/AppController/AppController.js","position":32,"original_position":46,"commit_id":"786900ccaf6fcc169882779b2431f3f81eeaf981","original_commit_id":"1fca0e651406e04ed60855f83ffc82a8a7795dbb","user":{"login":"rod-glover","id":4139706,"avatar_url":"https://avatars3.githubusercontent.com/u/4139706?v=4","gravatar_id":"","url":"https://api.github.com/users/rod-glover","html_url":"https://github.com/rod-glover","followers_url":"https://api.github.com/users/rod-glover/followers","following_url":"https://api.github.com/users/rod-glover/following{/other_user}","gists_url":"https://api.github.com/users/rod-glover/gists{/gist_id}","starred_url":"https://api.github.com/users/rod-glover/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rod-glover/subscriptions","organizations_url":"https://api.github.com/users/rod-glover/orgs","repos_url":"https://api.github.com/users/rod-glover/repos","events_url":"https://api.github.com/users/rod-glover/events{/privacy}","received_events_url":"https://api.github.com/users/rod-glover/received_events","type":"User","site_admin":false},"body":"Yeah, that's a problem throughout. I considered, as a general practice, pulling the required properties into a few separate objects, but that didn't seem to enhance readability much. \r\n\r\nIt might just be worth it in MapController where there is so much passing of values into components as props, often the same or very similar groups of values to several different components.","created_at":"2018-02-03T01:46:45Z","updated_at":"2018-02-03T01:46:46Z","html_url":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113#discussion_r165799679","pull_request_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/comments/165799679"},"html":{"href":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113#discussion_r165799679"},"pull_request":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113"}},"in_reply_to_id":165772455},"pull_request":{"url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113","id":165492182,"html_url":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113","diff_url":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113.diff","patch_url":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113.patch","issue_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/113","number":113,"state":"open","locked":false,"title":"Refactor using React Leaflet and React best practices","user":{"login":"rod-glover","id":4139706,"avatar_url":"https://avatars3.githubusercontent.com/u/4139706?v=4","gravatar_id":"","url":"https://api.github.com/users/rod-glover","html_url":"https://github.com/rod-glover","followers_url":"https://api.github.com/users/rod-glover/followers","following_url":"https://api.github.com/users/rod-glover/following{/other_user}","gists_url":"https://api.github.com/users/rod-glover/gists{/gist_id}","starred_url":"https://api.github.com/users/rod-glover/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rod-glover/subscriptions","organizations_url":"https://api.github.com/users/rod-glover/orgs","repos_url":"https://api.github.com/users/rod-glover/repos","events_url":"https://api.github.com/users/rod-glover/events{/privacy}","received_events_url":"https://api.github.com/users/rod-glover/received_events","type":"User","site_admin":false},"body":"## Overview\r\n\r\nResolves #112 \r\n\r\nThis is a big one! Apologies; I intended to break this down into several incremental PRs. But as noted in the associated issue, the old code and React Leaflet-based solutions turned out not to play well together, i.e., not at all. This forced a fully componentized refactoring, rather than a hybrid approach.\r\n\r\n## Isolation of changes\r\n\r\nThis branch replaces `MapController` and its descendants with refactored code.\r\n\r\n- All significant changes are contained within `MapContoller` and its child components and supporting code.\r\n- A few minor changes to parent components have been make (e.g., redefinition of `area`).\r\n- The original `MapController` and its co-conspirator `CanadaMap` have been removed and replaced by the new `MapController` and its cast of new supporting components.\r\n\r\n## Questions and suggestions for reviewers\r\n\r\nI think it might make sense to review more or less top-down, starting with `AppController` / `DualController` / `MotiController`, which render `MapController`, which contains the big changes.\r\n\r\n### `AppController` / `DualController` / `MotiController`\r\n1. Note refactoring of `area` to be \r\n   1. state _only at_ this level (via `AppMixin`), prop everywhere else\r\n   1. represented as geojson and converted to WKT only when passed to `DataController`\r\n\r\n### `AltMapController`\r\n1. Mostly refactors the content of original `MapController` and `CanadaMap` into React-Leaflet and React components that are composed here (user-selection components rendered as children of `DataMap`).\r\n1. Most of it is devoted to \"wiring up\" the rendered components. Except, unfortunately, for ...\r\n1. Preserves some less desirable coding from original code in `loadMap` and `componentWillReceiveProps`. I'm preparing some questions and suggestions on refactoring this properly but I think there are enough changes already that this should be left for a follow-up PR. Essence of the questions/suggestions: \r\n   1. Why is variable_id (and comparand_id) extracted from metadata instead of passed in as props? \r\n   1. Don't store values that are immediately derived on state. Just compute them and use them in place.\r\n1. I think the business of representing the choice of a dataset as an encoding of it can now be simply replaced by creating the list `dataset` and having the selector return the index into it. But this is part of the cleanup of `loadMap` and `componentWillReceiveProps`. \r\n\r\n### `DataMap`\r\n1. Mostly just wires up the React-Leaflet components needed to render the \"data map\", which is the map with the raster and isoline layers. This is its primary purpose.\r\n1. Renders its children to enable composing at higher level.\r\n1. `NcWMSColorbarControl`s and `NcWMSAutosetColorscaleControl` controls are dependent on their sibling `DataLayer`s. Since they are rendered as sibling components (and can't be otherwise, without rewriting the React Leaflet `WMSTileLayer` component), their existence relative to each other is asynchronous, and that asynchrony has to be (or at least is most simply) coordinated by storing refs to the DataLayers on state. \r\n1. Suggest promoting the `area` controls and layer up to `AltMapController`? It doesn't do anything at this level, and isn't directly concerned with DataMap's primary purpose, which is the data map. That would make this a really crisp component.\r\n\r\n### `CanadaBaseMap`\r\n1. Provides the base layer and renders all children (React Leaflet components) within itself.\r\n1. Simple, once you understand how to invoke `L.Proj.CRS` for our kind of tile layers. See the comments to that.\r\n1. Suggest `crs`, `version`, `srs`, `origin` not be props, just constants. I can't see how they can be useful as props. Am I missing something?\r\n\r\n### `DataLayer`\r\n1. Simple, but might benefit from some minor refactoring (TODO).\r\n\r\n### `NcWMSColorbarControl`\r\n1. Demonstrates how to create a new React Leaflet control component. Agreeably simple.\r\n\r\n### `LeafletNcWMSColorbarControl`\r\n1. Custom pure Leaflet control that does the heavy lifting. \r\n1. Could probably be optimized a bit so it doesn't re-render the control every time. But it has to render and empty control when its `layer` argument is null.\r\n1. Would be cleaner to render React components instead of building content with `L.DomUtil`.\r\n\r\n### `NcWMSAutosetColorscaleControl`\r\n1. Ditto.\r\n\r\n### `LeafletNcWMSAutosetColorscaleControl`\r\n1. Ditto.\r\n\r\n### `src/HOCs/compAcontrolsB`\r\n1. A HOC that encapsulates and DRYs up the state and callback wiring necessary to have one component \"control\" another, e.g., a Button open a Modal.\r\n1. Used to create the button-triggered map control dialogs.\r\n\r\n### `MapSettings`\r\n1. Created by HOC `compAcontrolsB`. Button opens dialog.\r\n\r\n### `MapSettingsDialog`\r\n1. Mostly wiring of the components that render the required selectors etc. inside the dialog.\r\n\r\n### `DataDisplayControls`\r\n1. DRYs up the creation of selectors in MapSettings for both raster and isoline layers.\r\n\r\n### `GeoLoader`\r\n1. Created by HOC `compAcontrolsB`. Button opens main dialog.\r\n\r\n### ``\r\n1. \r\n\r\n### ``\r\n1. \r\n\r\n\r\n## Known issues\r\n\r\n- [ ] Autoscaling and colorbars interact in an undesirable way. After clicking AS, the colorbars don't update properly when model, scenario, or variable is changed. This seems to be mediated by the use of the layer parameter `colorscalerange`. What's really needed is a clear spec of the desired behaviour.\r\n- [ ] A whack of tests don't pass. This is just laziness on my part.\r\n\r\n## Principles and best practices followed\r\n\r\n1. Favor declarative code (i.e. results rendered in JSX) over procedural code. \r\n   1. Rendering components is declarative. Almost everything can be done with components. \r\n   1. Conversely, code that procedurally builds or modifies components indicates that the breakdown into components is incomplete.\r\n1. Break down large components into many smaller, single-purpose components that are composed by a parent component.\r\n   1. Limit the purpose and complexity of each component. One component, one responsibility. (The responsibility of components higher in the hierarchy is mainly to compose other components.)\r\n   1. Encapsulate details. Don't let information spread beyond exactly where its needed.\r\n   1. Maximize code locality. Supporting code (e.g., a callback) and its user (e.g., a component) should very close to each other, uncluttered by code that supports other things.\r\n   1. Let React do the work of coordinating actions. Small, properly composed components need little in the way of lifecycle methods. The few cases where lifecycle methods need to be defined are simple and focused.\r\n1. Use state and props correctly.\r\n   1. A value should never be both state and prop. If you are tempted to do so, promote the state upward,  pass it in as a prop, and add a callback to cause state modifications.\r\n   1. A value shared by components should be the state of exactly one component (the lowest common ancestor of all components that share it).\r\n   1. All other components receive this value as a prop.\r\n1. Extract things like data services into separate modules. \r\n   1. This isolates the complexity of building requests and their dependence on specific tools (e.g., axios) and APIs, and promotes reuse and DRYness when building other components that need the same or similar requests. \r\n   1. This is a far superior substitute for mixins supplying the same functions. \r\n   1. The same principle applies to other computations or services that may be shared between components.\r\n1. Declare props and state explicitly so that it is easy to understand what information the component depends on.\r\n   1. Declare every* prop in `propTypes`. (*Except if you are passing through gobs of options to React Bootstrap components. Don't declare those ones unless you are feeling masochistic.)\r\n   1. Initialize every* state variable in `constructor`. (*No exceptions. Initialize to `null` or `undefined` if necessary.)\r\n   1.  Comment props and state declarations if it's not obvious what they're for.\r\n\r\nThe end result of this is a kind of splitting of components into two types, with little mixing of the qualities of the two.\r\n1.  Components higher up the hierarchy are mainly responsible for \"wiring up\" their children with the right connections of props and state. They tend to have more lines of code, but it is mostly simple code devoted to the \"wiring\".\r\n1. Components lower in the hierarchy provide a single specialized functionality, e.g. a selector, a map layer. These components have fewer lines of code, but it may be more complex because it is mostly devoted to functionality, not wiring.\r\n\r\nNote: \"Wiring up\" state and props is tedious, bulky, and repetitive. Which is why tools like Flux and Redux were invented. But we should stick with a bit of tediousness for now. Those tools come with their own complexity and tediousness.\r\n\r\n## Overview of the new componentization\r\n\r\nThe following is a compact distillation of the new components:\r\n\r\n```\r\nAppController / DualController:\r\n    state:\r\n        meta\r\n        comparandMeta  // in DualController\r\n        area\r\n\r\n    render:\r\n        ...\r\n        MapController | AltMapController  // controlled by env. variable\r\n        ...\r\n\r\n\r\nAltMapContoller:\r\n    props:\r\n        meta: PropTypes.array,\r\n        comparandMeta: PropTypes.array,\r\n        area: PropTypes.object,\r\n        onSetArea: PropTypes.func\r\n     \r\n    state:\r\n      run: undefined,\r\n      start_date: undefined,\r\n      end_date: undefined,\r\n\r\n      variable: undefined,\r\n      variableTimes: undefined,\r\n      variableTimeIdx: undefined,\r\n      variableWmsTime: undefined,\r\n\r\n      comparand: undefined,\r\n      comparandTimes: undefined,\r\n      comparandTimeIdx: undefined,\r\n      comparandWmsTime: undefined,\r\n\r\n      rasterLogscale: 'false',\r\n      rasterPalette: this.props.comparandMeta ? 'seq-Greys' : 'x-Occam',\r\n\r\n      isolineLogscale: 'false',\r\n      isolinePalette: 'x-Occam',\r\n      numberOfContours: 10,\r\n\r\n      rasterRange: {},\r\n      isolineRange: {},\r\n\r\n    render:\r\n        DataMap\r\n            StaticControl\r\n                GeoLoader\r\n            StaticControl\r\n                GeoExporter.Modal\r\n            StaticControl\r\n                MapSettings\r\n            StaticControl\r\n                MapFooter\r\n\r\n\r\nDataMap:\r\n    props:\r\n        rasterDataset: PropTypes.string,\r\n        rasterVariable: PropTypes.string,\r\n        rasterTime: PropTypes.string,\r\n        rasterPalette: PropTypes.string,\r\n        rasterLogscale: PropTypes.string, // arg for ncwms: 'true' | 'false'\r\n        rasterRange: PropTypes.object,\r\n        onChangeRasterRange: PropTypes.func.isRequired,\r\n    \r\n        isolineDataset: PropTypes.string,\r\n        isolineVariable: PropTypes.string,\r\n        isolineTime: PropTypes.string,\r\n        isolinePalette: PropTypes.string,\r\n        numberOfContours: PropTypes.number,\r\n        isolineLogscale: PropTypes.string, // arg for ncwms: 'true' | 'false'\r\n        isolineRange: PropTypes.object,\r\n        onChangeIsolineRange: PropTypes.func.isRequired,\r\n    \r\n        area: PropTypes.object,\r\n        onSetArea: PropTypes.func.isRequired,\r\n        \r\n    state:\r\n      // set by ref callbacks so that colorbars and autoscaler can coordinate\r\n      // on them after layers are created\r\n      rasterLayer: null,\r\n      isolineLayer: null,\r\n    \r\n    render:\r\n        CanadaBaseMap\r\n            DataLayer   // raster\r\n            DataLayer   // isoline\r\n            NcWMSColorbarControl   // raster\r\n            NcWMSAutosetColorscaleControl\r\n            NcWMSColorbarControl   // isoline\r\n            FeatureGroup  // (react-leaflet)\r\n                EditControl // (react-leaflet-draw)\r\n            {props.area && GeoJson}  // \"area\" layer (react-leaflet)\r\n            {children}\r\n\r\n\r\nCanadaBaseMap:\r\n    render:\r\n        Map // (react-leaflet)\r\n            TileLayer // base map (react-leaflet)  // TODO: ? extract as CanadaBaseLayer\r\n            {children}\r\n\r\n\r\nDataLayer:\r\n    props:\r\n        layerType: PropTypes.string, // 'raster' | 'isoline'\r\n        dataset: PropTypes.string,\r\n        variable: PropTypes.string,\r\n        time: PropTypes.string,\r\n        palette: PropTypes.string,\r\n        logscale: PropTypes.string,\r\n        range: PropTypes.object,\r\n        onLayerRef: PropTypes.func,\r\n        onChangeRange: PropTypes.func.isRequired,\r\n\r\n    render:\r\n        // special behaviour to facilitate coordination with sibling controls\r\n        if props.dataset:\r\n            return WMSTileLayer\r\n        else:\r\n            onLayerRef(null)\r\n            return null\r\n\r\n\r\nNcWMSColorbarControl:\r\n    props:\r\n        layer\r\n        options\r\n        // unused except to force updates on changes\r\n        dataset\r\n        variable\r\n        time\r\n        palette\r\n        logscale\r\n        \r\n    render:\r\n        // wraps a Leaflet control in a React component\r\n\r\n\r\nNcWMSAutosetColorscaleControl\r\n    props:\r\n        layers\r\n        options\r\n    \r\n    render:\r\n        // wraps a Leaflet control in a React component\r\n    \r\n\r\nGeoLoader:\r\n    A:\r\n        render:\r\n            Button\r\n                ...\r\n\r\n    B:\r\n        render:\r\n            DialogWithError\r\n\r\n    ButtonWithModal = compAcontrolsB(A, B)\r\n\r\n    render:\r\n        ButtonWithModal\r\n\r\n\r\nDialogWithError:\r\n    A:  // main dialog\r\n        render:\r\n            Modal\r\n                ...\r\n\r\n    B:  // error dialog\r\n        render:\r\n            Modal\r\n                ...\r\n\r\n    ModalControlsModal = compAcontrolsB(A, B)\r\n\r\n    render:\r\n        ModalControlsModal\r\n\r\n\r\n\r\nGeoExporter.Modal:\r\n    A:\r\n        render:\r\n            Button\r\n                ...\r\n    B:\r\n        render:\r\n            Modal\r\n                ...\r\n\r\n    ButtonWithModal = compAcontrolsB(A, B)\r\n\r\n    render:\r\n        ButtonWithModal\r\n\r\n\r\nMapSettings:\r\n    props:\r\n        title: PropTypes.string,\r\n        meta: PropTypes.array,\r\n        comparandMeta: PropTypes.array,\r\n    \r\n        dataset: PropTypes.string,  // current dataset selection, encoded as JSON string\r\n        onDatasetChange: PropTypes.func.isRequired,  // callback, arg is enocded JSON string\r\n    \r\n        variableTimes: PropTypes.object,\r\n        variableTimeIdx: PropTypes.string,\r\n        onChangeVariableTime: PropTypes.func.isRequired,\r\n    \r\n        hasComparand: PropTypes.bool,\r\n        comparandTimes: PropTypes.object,\r\n        comparandTimeIdx: PropTypes.string,\r\n        onChangeComparandTime: PropTypes.func.isRequired, // required???\r\n    \r\n        timesLinkable: PropTypes.bool,\r\n    \r\n        rasterPalette: PropTypes.string,\r\n        onChangeRasterPalette: PropTypes.func.isRequired,\r\n    \r\n        isolinePalette: PropTypes.string,\r\n        onChangeIsolinePalette: PropTypes.func.isRequired, // required???\r\n    \r\n        rasterLayerMin: PropTypes.number,\r\n        rasterLogscale: PropTypes.string,\r\n        onChangeRasterScale: PropTypes.func.isRequired,\r\n    \r\n        isolineLayerMin: PropTypes.number,\r\n        isolineLogscale: PropTypes.string,\r\n        onChangeIsolineScale: PropTypes.func.isRequired,\r\n    \r\n    state:\r\n      linkTimes: this.props.timesLinkable,\r\n    \r\n    MapSettingsButton:\r\n        render:\r\n            Button\r\n                ...\r\n\r\n    MapSettingsDialog:\r\n        render:\r\n            Modal\r\n                DatasetSelector\r\n                DataDisplayControls  // raster\r\n                {props.hasComparand && LinkControls}\r\n                {props.hasComparand && DataDisplayControls}  // isoline\r\n\r\n    ButtonWithModal = compAcontrolsB(MapSettingsButton, MapSettingsDialog)\r\n\r\n    render:\r\n        ButtonWithModal\r\n\r\n\r\nDatasetSelector:\r\n    render:\r\n        Selector  // datasets\r\n\r\n\r\nDataDisplayControls:\r\n    render:\r\n        div\r\n            TimeSelector\r\n            PaletteSelector\r\n            ScaleSelector\r\n\r\n\r\nLinkControls:\r\n    render:\r\n        Button\r\n            ...\r\n\r\nMapFooter:\r\n    render:\r\n        StaticControl\r\n            h5\r\n                ...\r\n\r\n\r\nStaticControl:\r\n    // custom react-leaflet control that renders its React children inside\r\n    // a leaflet Control element\r\n```","created_at":"2018-01-27T00:47:13Z","updated_at":"2018-02-03T01:46:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ad3b457bb5e9028d7c2156095da572fed3c384ab","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"milestone":null,"commits_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113/commits","review_comments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113/comments","review_comment_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/113/comments","statuses_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/statuses/786900ccaf6fcc169882779b2431f3f81eeaf981","head":{"label":"pacificclimate:issue112-react-leaflet","ref":"issue112-react-leaflet","sha":"786900ccaf6fcc169882779b2431f3f81eeaf981","user":{"login":"pacificclimate","id":7443397,"avatar_url":"https://avatars0.githubusercontent.com/u/7443397?v=4","gravatar_id":"","url":"https://api.github.com/users/pacificclimate","html_url":"https://github.com/pacificclimate","followers_url":"https://api.github.com/users/pacificclimate/followers","following_url":"https://api.github.com/users/pacificclimate/following{/other_user}","gists_url":"https://api.github.com/users/pacificclimate/gists{/gist_id}","starred_url":"https://api.github.com/users/pacificclimate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pacificclimate/subscriptions","organizations_url":"https://api.github.com/users/pacificclimate/orgs","repos_url":"https://api.github.com/users/pacificclimate/repos","events_url":"https://api.github.com/users/pacificclimate/events{/privacy}","received_events_url":"https://api.github.com/users/pacificclimate/received_events","type":"Organization","site_admin":false},"repo":{"id":47588182,"name":"climate-explorer-frontend","full_name":"pacificclimate/climate-explorer-frontend","owner":{"login":"pacificclimate","id":7443397,"avatar_url":"https://avatars0.githubusercontent.com/u/7443397?v=4","gravatar_id":"","url":"https://api.github.com/users/pacificclimate","html_url":"https://github.com/pacificclimate","followers_url":"https://api.github.com/users/pacificclimate/followers","following_url":"https://api.github.com/users/pacificclimate/following{/other_user}","gists_url":"https://api.github.com/users/pacificclimate/gists{/gist_id}","starred_url":"https://api.github.com/users/pacificclimate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pacificclimate/subscriptions","organizations_url":"https://api.github.com/users/pacificclimate/orgs","repos_url":"https://api.github.com/users/pacificclimate/repos","events_url":"https://api.github.com/users/pacificclimate/events{/privacy}","received_events_url":"https://api.github.com/users/pacificclimate/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pacificclimate/climate-explorer-frontend","description":null,"fork":false,"url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend","forks_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/forks","keys_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/teams","hooks_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/hooks","issue_events_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/events{/number}","events_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/events","assignees_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/assignees{/user}","branches_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/branches{/branch}","tags_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/tags","blobs_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/refs{/sha}","trees_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/statuses/{sha}","languages_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/languages","stargazers_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/stargazers","contributors_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/contributors","subscribers_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/subscribers","subscription_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/subscription","commits_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/commits{/sha}","git_commits_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/commits{/sha}","comments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/comments{/number}","issue_comment_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/comments{/number}","contents_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/contents/{+path}","compare_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/merges","archive_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/downloads","issues_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues{/number}","pulls_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls{/number}","milestones_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/milestones{/number}","notifications_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/labels{/name}","releases_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/releases{/id}","deployments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/deployments","created_at":"2015-12-08T00:31:18Z","updated_at":"2017-05-18T02:16:37Z","pushed_at":"2018-02-03T01:24:58Z","git_url":"git://github.com/pacificclimate/climate-explorer-frontend.git","ssh_url":"git@github.com:pacificclimate/climate-explorer-frontend.git","clone_url":"https://github.com/pacificclimate/climate-explorer-frontend.git","svn_url":"https://github.com/pacificclimate/climate-explorer-frontend","homepage":null,"size":916,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":23,"license":null,"forks":0,"open_issues":23,"watchers":1,"default_branch":"master"}},"base":{"label":"pacificclimate:master","ref":"master","sha":"47ee4b79b0b504422394f14d4953ed0944184ef7","user":{"login":"pacificclimate","id":7443397,"avatar_url":"https://avatars0.githubusercontent.com/u/7443397?v=4","gravatar_id":"","url":"https://api.github.com/users/pacificclimate","html_url":"https://github.com/pacificclimate","followers_url":"https://api.github.com/users/pacificclimate/followers","following_url":"https://api.github.com/users/pacificclimate/following{/other_user}","gists_url":"https://api.github.com/users/pacificclimate/gists{/gist_id}","starred_url":"https://api.github.com/users/pacificclimate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pacificclimate/subscriptions","organizations_url":"https://api.github.com/users/pacificclimate/orgs","repos_url":"https://api.github.com/users/pacificclimate/repos","events_url":"https://api.github.com/users/pacificclimate/events{/privacy}","received_events_url":"https://api.github.com/users/pacificclimate/received_events","type":"Organization","site_admin":false},"repo":{"id":47588182,"name":"climate-explorer-frontend","full_name":"pacificclimate/climate-explorer-frontend","owner":{"login":"pacificclimate","id":7443397,"avatar_url":"https://avatars0.githubusercontent.com/u/7443397?v=4","gravatar_id":"","url":"https://api.github.com/users/pacificclimate","html_url":"https://github.com/pacificclimate","followers_url":"https://api.github.com/users/pacificclimate/followers","following_url":"https://api.github.com/users/pacificclimate/following{/other_user}","gists_url":"https://api.github.com/users/pacificclimate/gists{/gist_id}","starred_url":"https://api.github.com/users/pacificclimate/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pacificclimate/subscriptions","organizations_url":"https://api.github.com/users/pacificclimate/orgs","repos_url":"https://api.github.com/users/pacificclimate/repos","events_url":"https://api.github.com/users/pacificclimate/events{/privacy}","received_events_url":"https://api.github.com/users/pacificclimate/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pacificclimate/climate-explorer-frontend","description":null,"fork":false,"url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend","forks_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/forks","keys_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/teams","hooks_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/hooks","issue_events_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/events{/number}","events_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/events","assignees_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/assignees{/user}","branches_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/branches{/branch}","tags_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/tags","blobs_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/refs{/sha}","trees_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/statuses/{sha}","languages_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/languages","stargazers_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/stargazers","contributors_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/contributors","subscribers_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/subscribers","subscription_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/subscription","commits_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/commits{/sha}","git_commits_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/git/commits{/sha}","comments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/comments{/number}","issue_comment_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/comments{/number}","contents_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/contents/{+path}","compare_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/merges","archive_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/downloads","issues_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues{/number}","pulls_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls{/number}","milestones_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/milestones{/number}","notifications_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/labels{/name}","releases_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/releases{/id}","deployments_url":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/deployments","created_at":"2015-12-08T00:31:18Z","updated_at":"2017-05-18T02:16:37Z","pushed_at":"2018-02-03T01:24:58Z","git_url":"git://github.com/pacificclimate/climate-explorer-frontend.git","ssh_url":"git@github.com:pacificclimate/climate-explorer-frontend.git","clone_url":"https://github.com/pacificclimate/climate-explorer-frontend.git","svn_url":"https://github.com/pacificclimate/climate-explorer-frontend","homepage":null,"size":916,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":23,"license":null,"forks":0,"open_issues":23,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113"},"html":{"href":"https://github.com/pacificclimate/climate-explorer-frontend/pull/113"},"issue":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/113"},"comments":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/issues/113/comments"},"review_comments":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113/comments"},"review_comment":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/pulls/113/commits"},"statuses":{"href":"https://api.github.com/repos/pacificclimate/climate-explorer-frontend/statuses/786900ccaf6fcc169882779b2431f3f81eeaf981"}},"author_association":"NONE"}}
{"actor":{"display_login":"rod-glover"}}