<commit_before>"use strict";

var App = App || {};

let documentPromise = new Promise(function(resolve, reject) {
  $(document).ready(function() {
    console.log("$(document).ready done");
    resolve();
  });
});

let windowPromise = new Promise(function(resolve, reject) {
  $(window).on("load", function() {
    console.log("$(window).on('load') done");
    resolve();
  });
});

// make sure both the document and CSS are loaded
Promise.all([documentPromise, windowPromise, less.pageLoadFinished])
  .then(function() {
    setTimeout(function() {
      App.init();
    }, 500);
  })
  .catch(function(err) {
    console.log(err);
  });

(function() {
  App.models = {};
  App.views = {};
  App.controllers = {};

  // models
  App.models.socialServices = new SocialServiceModel();
  App.models.serviceTaxonomy = new ServiceTaxonomyModel();
  App.models.censusData = new CensusDataModel();
  App.models.boundaryData = new BoundaryDataModel();
  App.models.landInventory = new LandInventoryModel();
  App.models.crimeData = new CrimeDataModel();

  // views

  // controllers
  App.controllers.serviceFilterDropdown = new FilterDropdownController();
  App.controllers.mapData = new MapDataController();
  // App.controllers.locationButton = new LocationButtonController();
  App.controllers.search = new SearchController();

  App.init = function () {
    $('[data-toggle="tooltip"]').tooltip(); //needed for button tooltips
    App.views.loadingMessage = new LoadingMessageView("#loading-indicator");

    console.log("Loading Analytics");
    App.views.loadingMessage.startLoading("Loading Map");
    App.views.map = new MapView("serviceMap");
    App.views.map.drawEnglewoodOutline();

    App.views.loadingMessage.updateAndRaise("Initializing buttons and interface elements");
    App.views.chartList = new ChartListView("#chartList");
    App.views.chartList.makeCollapsing("#toggleHideChartsButton", "#chartListWrapper");

    // App.controllers.serviceFilterDropdown.setFilterDropdown("#filterDropdownList");
    App.controllers.serviceFilterDropdown.setFilterDropdown("#filterDropdownList", "#filterDropdownButton");
    App.controllers.serviceFilterDropdown.attachAllServicesButton("#allServicesButton");

    App.controllers.serviceMarkerView = new LeafletMarkerViewController("#toggleServiceView","#serviceViewText", "Service");
    App.controllers.landMarkerView = new LeafletMarkerViewController("#toggleLotView", "#lotViewText", "Vacant Lot");

    // App.controllers.mapData.setDataDropdown("#mapSettingsPanel");
    App.controllers.mapData.setupDataPanel("#mapPanelToggle", "#mapSettingsPanel");
    // App.controllers.mapData.setPanelToggle("#mapPanelToggle");
    App.controllers.mapData.attachResetOverlayButton("#resetMaps");

    // App.controllers.locationButton.attachLocationButton("#locationButton");
    // App.controllers.locationButton.attachAddressLookupButton("#findAddressButton");

    App.controllers.search.attachDOMElements("#searchInput", "#searchButton");

    // App.controllers.rectSelector = new RectSelectorController("#newRectSelector");
    // App.controllers.rectSelector.attachDragLayer("#serviceMap");
    // App.controllers.rectSelector.attachSpecificSelector("#rectSelector1", "1");
    // App.controllers.rectSelector.attachSpecificSelector("#rectSelector2", "2");

    App.views.loadingMessage.updateAndRaise("Loading location, service, and census data");
    let socialServiceP = App.models.socialServices.loadData("./data/EnglewoodLocations.csv")
      .then((data) => {
        console.log("Loaded Social Services");
        return data;
      });
    let serviceTaxonomyP = App.models.serviceTaxonomy.loadData("./data/serviceTaxonomy.json")
      .then((data) => {
        console.log("Loaded Service Taxonomy");
        return data;
      });
    let boundaryDataP = App.models.boundaryData.loadData()
      .then((data) => {
        console.log("Loaded Boundary Data");
        return data;
      });
    let censusDataP = App.models.censusData.loadData()
      .then(function (data) {
        let overlayData = data[0];
        let overlayCategories = data[1];

        App.controllers.mapData.populateDropdown(overlayCategories, max_subdropdown_height);

        console.log("Loaded Census Data");
        return data;
      });
    let landInventoryP = App.models.landInventory.loadData()
      .then((data) => {
        console.log("Loaded Land Inventory Data");
        return data;
      });
    // let crimeDataP = App.models.crimeData.loadData()
    //   .then((data) => {
    //     console.log("Loaded Crime Data");
    //     return data;
    //   });

    App.controllers.mapData.setCensusClearButton();

    // load other data sources when asked to plot
    let max_subdropdown_height = d3.select('body').node().clientHeight * 0.4;

    Promise.all([socialServiceP, serviceTaxonomyP, boundaryDataP, censusDataP, landInventoryP, /*crimeDataP*/])
      .then(function(values) {
        // App.views.map.createMap();

        App.views.loadingMessage.updateAndRaise("Plotting services and lots");
        App.views.map.plotServices(App.models.socialServices.getData());
        App.views.map.plotLandInventory(App.models.landInventory.getDataByFilter());

        // App.views.chartList...
        App.views.chartList.addLotChart();

        App.controllers.serviceFilterDropdown.populateDropdown(max_subdropdown_height);

        //start off with markers hidden
        App.controllers.serviceMarkerView.setVisibilityState(false);
        App.controllers.landMarkerView.setVisibilityState(false);

        //set two selections to be west englewood and englewood
        App.views.loadingMessage.updateAndRaise("Filtering data for West Englewood and Englewood");
        let westEnglewoodPoly = App.models.boundaryData.getWestEnglewoodPolygon();
        let englewoodPoly = App.models.boundaryData.getEnglewoodPolygon();
        let selectionData = {
          westEnglewood: {
            data: {
              census: App.models.censusData.getDataWithinPolygon(westEnglewoodPoly).dataTotals,
              // service: App.models.socialServices.getDataByFilter((service) => {
              //   let point = [parseFloat(service.X), parseFloat(service.Y)];
              //   return App.models.boundaryData.isInWestEnglewood(point);
              // }),
              lot: App.models.landInventory.getDataByFilter((a) => { return a.Area === "West Englewood"; })
            },
            area: turf.area(westEnglewoodPoly),
            color: "#1f77b4",
            id: "West Englewood"
          },
          englewood: {
            data: {
              census: App.models.censusData.getDataWithinPolygon(englewoodPoly).dataTotals,
              // service: App.models.socialServices.getDataByFilter((service) => {
              //   let point = [parseFloat(service.X), parseFloat(service.Y)];
              //   return App.models.boundaryData.isInEnglewood(point);
              // }),
              lot: App.models.landInventory.getDataByFilter((a) => { return a.Area === "Englewood"; })
            },
            area: turf.area(englewoodPoly),
            color: "#ff7f0e",
            id: "Englewood"
          }
        };

        console.log(selectionData);

        App.views.chartList.addSelection(selectionData.westEnglewood);
        App.views.chartList.addSelection(selectionData.englewood);

        App.views.loadingMessage.finishLoading();
      })
      .catch(function(err) {
        console.log(err);
      });
  };

})();
<commit_msg>Fix click to zoom on analytics charts<commit_after>"use strict";

var App = App || {};

let documentPromise = new Promise(function(resolve, reject) {
  $(document).ready(function() {
    console.log("$(document).ready done");
    resolve();
  });
});

let windowPromise = new Promise(function(resolve, reject) {
  $(window).on("load", function() {
    console.log("$(window).on('load') done");
    resolve();
  });
});

// make sure both the document and CSS are loaded
Promise.all([documentPromise, windowPromise, less.pageLoadFinished])
  .then(function() {
    setTimeout(function() {
      App.init();
    }, 500);
  })
  .catch(function(err) {
    console.log(err);
  });

(function() {
  App.models = {};
  App.views = {};
  App.controllers = {};

  // models
  App.models.socialServices = new SocialServiceModel();
  App.models.serviceTaxonomy = new ServiceTaxonomyModel();
  App.models.censusData = new CensusDataModel();
  App.models.boundaryData = new BoundaryDataModel();
  App.models.landInventory = new LandInventoryModel();
  App.models.crimeData = new CrimeDataModel();

  // views

  // controllers
  App.controllers.serviceFilterDropdown = new FilterDropdownController();
  App.controllers.mapData = new MapDataController();
  // App.controllers.locationButton = new LocationButtonController();
  App.controllers.search = new SearchController();

  App.init = function () {
    $('[data-toggle="tooltip"]').tooltip(); //needed for button tooltips
    App.views.loadingMessage = new LoadingMessageView("#loading-indicator");

    console.log("Loading Analytics");
    App.views.loadingMessage.startLoading("Loading Map");
    App.views.map = new MapView("serviceMap");
    App.views.map.drawEnglewoodOutline();

    App.views.loadingMessage.updateAndRaise("Initializing buttons and interface elements");
    App.views.chartList = new ChartListView("#chartList");
    App.views.chartList.makeCollapsing("#toggleHideChartsButton", "#chartListWrapper");

    // App.controllers.serviceFilterDropdown.setFilterDropdown("#filterDropdownList");
    App.controllers.serviceFilterDropdown.setFilterDropdown("#filterDropdownList", "#filterDropdownButton");
    App.controllers.serviceFilterDropdown.attachAllServicesButton("#allServicesButton");

    App.controllers.serviceMarkerView = new LeafletMarkerViewController("#toggleServiceView","#serviceViewText", "Service");
    App.controllers.landMarkerView = new LeafletMarkerViewController("#toggleLotView", "#lotViewText", "Vacant Lot");

    // App.controllers.mapData.setDataDropdown("#mapSettingsPanel");
    App.controllers.mapData.setupDataPanel("#mapPanelToggle", "#mapSettingsPanel");
    // App.controllers.mapData.setPanelToggle("#mapPanelToggle");
    App.controllers.mapData.attachResetOverlayButton("#resetMaps");

    // App.controllers.locationButton.attachLocationButton("#locationButton");
    // App.controllers.locationButton.attachAddressLookupButton("#findAddressButton");

    App.controllers.search.attachDOMElements("#searchInput", "#searchButton");

    // App.controllers.rectSelector = new RectSelectorController("#newRectSelector");
    // App.controllers.rectSelector.attachDragLayer("#serviceMap");
    // App.controllers.rectSelector.attachSpecificSelector("#rectSelector1", "1");
    // App.controllers.rectSelector.attachSpecificSelector("#rectSelector2", "2");

    App.views.loadingMessage.updateAndRaise("Loading location, service, and census data");
    let socialServiceP = App.models.socialServices.loadData("./data/EnglewoodLocations.csv")
      .then((data) => {
        console.log("Loaded Social Services");
        return data;
      });
    let serviceTaxonomyP = App.models.serviceTaxonomy.loadData("./data/serviceTaxonomy.json")
      .then((data) => {
        console.log("Loaded Service Taxonomy");
        return data;
      });
    let boundaryDataP = App.models.boundaryData.loadData()
      .then((data) => {
        console.log("Loaded Boundary Data");
        return data;
      });
    let censusDataP = App.models.censusData.loadData()
      .then(function (data) {
        let overlayData = data[0];
        let overlayCategories = data[1];

        App.controllers.mapData.populateDropdown(overlayCategories, max_subdropdown_height);

        console.log("Loaded Census Data");
        return data;
      });
    let landInventoryP = App.models.landInventory.loadData()
      .then((data) => {
        console.log("Loaded Land Inventory Data");
        return data;
      });
    // let crimeDataP = App.models.crimeData.loadData()
    //   .then((data) => {
    //     console.log("Loaded Crime Data");
    //     return data;
    //   });

    App.controllers.mapData.setCensusClearButton();

    // load other data sources when asked to plot
    let max_subdropdown_height = d3.select('body').node().clientHeight * 0.4;

    Promise.all([socialServiceP, serviceTaxonomyP, boundaryDataP, censusDataP, landInventoryP, /*crimeDataP*/])
      .then(function(values) {
        // App.views.map.createMap();

        App.views.loadingMessage.updateAndRaise("Plotting services and lots");
        App.views.map.plotServices(App.models.socialServices.getData());
        App.views.map.plotLandInventory(App.models.landInventory.getDataByFilter());

        // App.views.chartList...
        App.views.chartList.addLotChart();

        App.controllers.serviceFilterDropdown.populateDropdown(max_subdropdown_height);

        //start off with markers hidden
        App.controllers.serviceMarkerView.setVisibilityState(false);
        App.controllers.landMarkerView.setVisibilityState(false);

        //set two selections to be west englewood and englewood
        App.views.loadingMessage.updateAndRaise("Filtering data for West Englewood and Englewood");
        let westEnglewoodPoly = App.models.boundaryData.getWestEnglewoodPolygon();
        let englewoodPoly = App.models.boundaryData.getEnglewoodPolygon();
        let selectionData = {
          westEnglewood: {
            data: {
              census: App.models.censusData.getDataWithinPolygon(westEnglewoodPoly).dataTotals,
              // service: App.models.socialServices.getDataByFilter((service) => {
              //   let point = [parseFloat(service.X), parseFloat(service.Y)];
              //   return App.models.boundaryData.isInWestEnglewood(point);
              // }),
              lot: App.models.landInventory.getDataByFilter((a) => { return a.Area === "West Englewood"; })
            },
            area: turf.area(westEnglewoodPoly),
            color: "#1f77b4",
            id: "West Englewood",
            bounds: (function(poly){
              let coordsArr = poly.geometry.coordinates[0];
              let latExtent = d3.extent(coordsArr, (d) => { return d[1]; });
              let lngExtent = d3.extent(coordsArr, (d) => { return d[0]; });

              return [
                [latExtent[0], lngExtent[0]],
                [latExtent[1], lngExtent[1]],
              ];
            })(westEnglewoodPoly)
          },
          englewood: {
            data: {
              census: App.models.censusData.getDataWithinPolygon(englewoodPoly).dataTotals,
              // service: App.models.socialServices.getDataByFilter((service) => {
              //   let point = [parseFloat(service.X), parseFloat(service.Y)];
              //   return App.models.boundaryData.isInEnglewood(point);
              // }),
              lot: App.models.landInventory.getDataByFilter((a) => { return a.Area === "Englewood"; })
            },
            area: turf.area(englewoodPoly),
            color: "#ff7f0e",
            id: "Englewood",
            bounds: (function (poly) {
              let coordsArr = poly.geometry.coordinates[0];
              let latExtent = d3.extent(coordsArr, (d) => { return d[1]; });
              let lngExtent = d3.extent(coordsArr, (d) => { return d[0]; });

              return [
                [latExtent[0], lngExtent[0]],
                [latExtent[1], lngExtent[1]],
              ];
            })(englewoodPoly)
          }
        };

        console.log(selectionData);

        App.views.chartList.addSelection(selectionData.westEnglewood);
        App.views.chartList.addSelection(selectionData.englewood);

        App.views.loadingMessage.finishLoading();
      })
      .catch(function(err) {
        console.log(err);
      });
  };

})();
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require underscore
//= require v3_epoly
//= require map
//= require bootstrap.min
//= require cbpAnimatedHeader.min
//= require classie
//= require custom.js
//= require imagesloaded.pkgd.min
//= isotope.min
//= jqBootstrapValidation.js
//= jquery.flexsider-min
//= jquery.magnific-popup.min
//= jquery.mb.YTPLayer
//= jquery.nav
//= jquery.scrollTo
//= owl.carousel.min
//= scrollReveal
//= require modernizr.custom
//= require jquery.nav
//= require_tree .<commit_msg>Fix application.js<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require underscore
//= require v3_epoly
//= require map
//= require bootstrap.min
//= require cbpAnimatedHeader.min
//= require classie
//= require scrollReveal
//= require custom.js
//= require imagesloaded.pkgd.min
//= require isotope.min
//= require jqBootstrapValidation.js
//= require jquery.flexslider-min
//= require jquery.magnific-popup.min
//= require jquery.mb.YTPlayer
//= require jquery.nav
//= require jquery.scrollTo
//= require modernizr.custom
//= require owl.carousel.min
//= require_tree .<|endoftext|>


<commit_before>import { _ }            from 'meteor/underscore';
import { Router }       from './_init.js';
import { Meteor }       from 'meteor/meteor';
import { Tracker }      from 'meteor/tracker';
import { ReactiveDict } from 'meteor/reactive-dict';
import './yield.html';

class Route {
  constructor(router = new Router(), pathDef, options = {}, group) {
    this.render           = router.Renderer.render.bind(router.Renderer);
    this.options          = options;
    this.globals          = router.globals;
    this.pathDef          = pathDef;

    // Route.path is deprecated and will be removed in 3.0
    this.path             = pathDef;
    this.conf             = options.conf || {};
    this.group            = group;
    this._data            = options.data || null;
    this._router          = router;
    this._action          = options.action || Function.prototype;
    this._waitOn          = options.waitOn || null;
    this._subsMap         = {};
    this._onNoData        = options.onNoData || null;
    this._currentData     = null;
    this._triggersExit    = options.triggersExit || [];
    this._whileWaiting    = options.whileWaiting || null;
    this._triggersEnter   = options.triggersEnter || [];
    this._subscriptions   = options.subscriptions || Function.prototype;
    this._waitOnResources = options.waitOnResources || null;

    this._params          = new ReactiveDict();
    this._queryParams     = new ReactiveDict();
    this._routeCloseDep   = new Tracker.Dependency();
    this._pathChangeDep   = new Tracker.Dependency();

    if (options.name) {
      this.name = options.name;
    }
  }

  clearSubscriptions() {
    this._subsMap = {};
  }

  register(name, sub) {
    this._subsMap[name] = sub;
  }

  getSubscription(name) {
    return this._subsMap[name];
  }

  getAllSubscriptions() {
    return this._subsMap;
  }

  checkSubscriptions(subscriptions) {
    const results = [];
    for (let i = 0; i < subscriptions.length; i++) {
      results.push((subscriptions[i] && subscriptions[i].ready) ? subscriptions[i].ready() : false);
    }

    return !~results.indexOf(false);
  }

  waitOn(current = {}, next) {
    let subscriptions = [];
    let trackers = [];
    let timer;
    let _data = null;
    let _preloaded = 0;
    let _isWaiting = false;
    let _resources = false;

    if (current.route.globals.length) {
      for (let i = 0; i < current.route.globals.length; i++) {
        if (typeof current.route.globals[i] === 'object' && current.route.globals[i].waitOnResources) {
          if (!_resources) { _resources = []; }
          _resources.push(current.route.globals[i].waitOnResources);
        }
      }
    }

    if (this._waitOnResources) {
      if (!_resources) { _resources = []; }
      _resources.push(this._waitOnResources);
    }

    const preload = (len, __data) => {
      _preloaded++;
      if (_preloaded >= len) {
        next(current, __data);
      }
    };

    const getData = () => {
      if (this._data) {
        if (!_data) {
          _data = this._currentData = this._data(current.params, current.queryParams);
        } else {
          _data = this._currentData;
        }
      }
      return _data;
    };

    const getResources = () => {
      _data = getData();
      let   len    = 0;
      let   items;
      let images = [];
      let other  = [];
      for (let i = _resources.length - 1; i >= 0; i--) {
        items = _resources[i].call(this, current.params, current.queryParams, _data);
        if (items) {
          if (items.images && items.images.length) {
            images = images.concat(items.images);
          }
          if (items.other && items.other.length) {
            other = other.concat(items.other);
          }
        }
      }

      if ((other && other.length) || (images && images.length)) {
        if (other && other.length && typeof XMLHttpRequest != 'undefined') {
          other = other.filter((elem, index, self) => {
            return index == self.indexOf(elem);
          });
          len += other.length;
          const prefetch = {};
          for (let k = other.length - 1; k >= 0; k--) {
            prefetch[k] = new XMLHttpRequest();
            prefetch[k].onload  = () => { preload(len, _data); };
            prefetch[k].onerror = () => { preload(len, _data); };
            prefetch[k].open('GET', other[k]);
            prefetch[k].send(null);
          }
        }

        if (images && images.length){
          images = images.filter((elem, index, self) => {
            return index == self.indexOf(elem);
          });
          len += images.length;
          const imgs = {};
          for (let j = images.length - 1; j >= 0; j--) {
            imgs[j]         = new Image();
            imgs[j].onload  = () => { preload(len, _data); };
            imgs[j].onerror = () => { preload(len, _data); };
            imgs[j].src     = images[j];
          }
        }
      } else {
        next(current, _data);
      }
    };

    const whileWaitingAction = () => {
      if (!_isWaiting) {
        this._whileWaiting && this._whileWaiting(current.params, current.queryParams);
        _isWaiting = true;
      }
    };

    if (this._waitOn) {
      const wait = (delay) => {
        timer = Meteor.setTimeout(() => {
          if (this.checkSubscriptions(subscriptions)) {
            Meteor.clearTimeout(timer);
            _data = getData();
            if (_resources) {
              whileWaitingAction();
              getResources();
            } else {
              next(current, _data);
            }
          } else {
            wait(25);
          }
        }, delay);
      };

      const processSubData = (subData) => {
        const placeIn = (d) => {
          if (d.flush) {
            trackers.push(d);
          } else if (d.ready) {
            subscriptions.push(d);
          }
        };

        if (subData instanceof Array) {
          for (let i = subData.length - 1; i >= 0; i--) {
            if (subData[i] !== null && typeof subData[i] === 'object') {
              placeIn(subData[i]);
            }
          }
        } else if (subData !== null && typeof subData === 'object') {
          placeIn(subData);
        }
      };

      const stopSubs = () => {
        for (let i = subscriptions.length - 1; i >= 0; i--) {
          if (subscriptions[i].stop) {
            subscriptions[i].stop();
          }
        }
        subscriptions = [];
      };

      const done = (subscribtion) => {
        processSubData(subscribtion());
      };

      processSubData(this._waitOn(current.params, current.queryParams, done));

      this._triggersExit.push(() => {
        stopSubs();
        for (let i = trackers.length - 1; i >= 0; i--) {
          if (trackers[i].stop) {
            trackers[i].stop();
          }
        }
        trackers = [];
        _data = this._currentData = null;
      });

      whileWaitingAction();
      wait(0);
    } else if (_resources) {
      whileWaitingAction();
      getResources();
    } else if (this._data) {
      next(current, getData());
    } else {
      next(current);
    }
  }

  callAction(current) {
    if (this._data) {
      if (this._onNoData && !this._currentData) {
        this._onNoData(current.params, current.queryParams);
      } else {
        this._action(current.params, current.queryParams, this._currentData);
      }
    } else {
      this._action(current.params, current.queryParams, this._currentData);
    }
  }

  callSubscriptions(current) {
    this.clearSubscriptions();
    if (this.group) {
      this.group.callSubscriptions(current);
    }

    this._subscriptions(current.params, current.queryParams);
  }

  getRouteName() {
    this._routeCloseDep.depend();
    return this.name;
  }

  getParam(key) {
    this._routeCloseDep.depend();
    return this._params.get(key);
  }

  getQueryParam(key) {
    this._routeCloseDep.depend();
    return this._queryParams.get(key);
  }

  watchPathChange() {
    this._pathChangeDep.depend();
  }

  registerRouteClose() {
    this._params = new ReactiveDict();
    this._queryParams = new ReactiveDict();
    this._routeCloseDep.changed();
    this._pathChangeDep.changed();
  }

  registerRouteChange(currentContext, routeChanging) {
    // register params
    this._updateReactiveDict(this._params, currentContext.params);

    // register query params
    this._updateReactiveDict(this._queryParams, currentContext.queryParams);

    // if the route is changing, we need to defer triggering path changing
    // if we did this, old route's path watchers will detect this
    // Real issue is, above watcher will get removed with the new route
    // So, we don't need to trigger it now
    // We are doing it on the route close event. So, if they exists they'll
    // get notify that
    if(!routeChanging) {
      this._pathChangeDep.changed();
    }
  }

  _updateReactiveDict(dict, newValues) {
    const currentKeys = _.keys(newValues);
    const oldKeys = _.keys(dict.keyDeps);

    // set new values
    //  params is an array. So, _.each(params) does not works
    //  to iterate params
    _.each(currentKeys, (key) => {
      dict.set(key, newValues[key]);
    });

    // remove keys which does not exisits here
    const removedKeys = _.difference(oldKeys, currentKeys);
    _.each(removedKeys, (key) => {
      dict.set(key, undefined);
    });
  }
}

export default Route;
<commit_msg>endWaiting option<commit_after>import { _ }            from 'meteor/underscore';
import { Router }       from './_init.js';
import { Meteor }       from 'meteor/meteor';
import { Tracker }      from 'meteor/tracker';
import { ReactiveDict } from 'meteor/reactive-dict';
import './yield.html';

class Route {
  constructor(router = new Router(), pathDef, options = {}, group) {
    this.render           = router.Renderer.render.bind(router.Renderer);
    this.options          = options;
    this.globals          = router.globals;
    this.pathDef          = pathDef;

    // Route.path is deprecated and will be removed in 3.0
    this.path             = pathDef;
    this.conf             = options.conf || {};
    this.group            = group;
    this._data            = options.data || null;
    this._router          = router;
    this._action          = options.action || Function.prototype;
    this._waitOn          = options.waitOn || null;
    this._subsMap         = {};
    this._onNoData        = options.onNoData || null;
    this._endWaiting      = options.endWaiting || null;
    this._currentData     = null;
    this._triggersExit    = options.triggersExit || [];
    this._whileWaiting    = options.whileWaiting || null;
    this._triggersEnter   = options.triggersEnter || [];
    this._subscriptions   = options.subscriptions || Function.prototype;
    this._waitOnResources = options.waitOnResources || null;

    this._params          = new ReactiveDict();
    this._queryParams     = new ReactiveDict();
    this._routeCloseDep   = new Tracker.Dependency();
    this._pathChangeDep   = new Tracker.Dependency();

    if (options.name) {
      this.name = options.name;
    }
  }

  clearSubscriptions() {
    this._subsMap = {};
  }

  register(name, sub) {
    this._subsMap[name] = sub;
  }

  getSubscription(name) {
    return this._subsMap[name];
  }

  getAllSubscriptions() {
    return this._subsMap;
  }

  checkSubscriptions(subscriptions) {
    const results = [];
    for (let i = 0; i < subscriptions.length; i++) {
      results.push((subscriptions[i] && subscriptions[i].ready) ? subscriptions[i].ready() : false);
    }

    return !~results.indexOf(false);
  }

  waitOn(current = {}, next) {
    let subscriptions = [];
    let trackers = [];
    let timer;
    let _data = null;
    let _preloaded = 0;
    let _isWaiting = false;
    let _resources = false;

    if (current.route.globals.length) {
      for (let i = 0; i < current.route.globals.length; i++) {
        if (typeof current.route.globals[i] === 'object' && current.route.globals[i].waitOnResources) {
          if (!_resources) { _resources = []; }
          _resources.push(current.route.globals[i].waitOnResources);
        }
      }
    }

    if (this._waitOnResources) {
      if (!_resources) { _resources = []; }
      _resources.push(this._waitOnResources);
    }

    const preload = (len, __data) => {
      _preloaded++;
      if (_preloaded >= len) {
        next(current, __data);
      }
    };

    const getData = () => {
      if (this._data) {
        if (!_data) {
          _data = this._currentData = this._data(current.params, current.queryParams);
        } else {
          _data = this._currentData;
        }
      }
      return _data;
    };

    const getResources = () => {
      _data = getData();
      let   len    = 0;
      let   items;
      let images = [];
      let other  = [];
      for (let i = _resources.length - 1; i >= 0; i--) {
        items = _resources[i].call(this, current.params, current.queryParams, _data);
        if (items) {
          if (items.images && items.images.length) {
            images = images.concat(items.images);
          }
          if (items.other && items.other.length) {
            other = other.concat(items.other);
          }
        }
      }

      if ((other && other.length) || (images && images.length)) {
        if (other && other.length && typeof XMLHttpRequest != 'undefined') {
          other = other.filter((elem, index, self) => {
            return index == self.indexOf(elem);
          });
          len += other.length;
          const prefetch = {};
          for (let k = other.length - 1; k >= 0; k--) {
            prefetch[k] = new XMLHttpRequest();
            prefetch[k].onload  = () => { preload(len, _data); };
            prefetch[k].onerror = () => { preload(len, _data); };
            prefetch[k].open('GET', other[k]);
            prefetch[k].send(null);
          }
        }

        if (images && images.length){
          images = images.filter((elem, index, self) => {
            return index == self.indexOf(elem);
          });
          len += images.length;
          const imgs = {};
          for (let j = images.length - 1; j >= 0; j--) {
            imgs[j]         = new Image();
            imgs[j].onload  = () => { preload(len, _data); };
            imgs[j].onerror = () => { preload(len, _data); };
            imgs[j].src     = images[j];
          }
        }
      } else {
        next(current, _data);
      }
    };

    const whileWaitingAction = () => {
      if (!_isWaiting) {
        this._whileWaiting && this._whileWaiting(current.params, current.queryParams);
        _isWaiting = true;
      }
    };

    if (this._waitOn) {
      const wait = (delay) => {
        timer = Meteor.setTimeout(() => {
          if (this.checkSubscriptions(subscriptions)) {
            Meteor.clearTimeout(timer);
            _data = getData();
            if (_resources) {
              whileWaitingAction();
              getResources();
            } else {
              next(current, _data);
            }
          } else {
            wait(25);
          }
        }, delay);
      };

      const processSubData = (subData) => {
        const placeIn = (d) => {
          if (d.flush) {
            trackers.push(d);
          } else if (d.ready) {
            subscriptions.push(d);
          }
        };

        if (subData instanceof Array) {
          for (let i = subData.length - 1; i >= 0; i--) {
            if (subData[i] !== null && typeof subData[i] === 'object') {
              placeIn(subData[i]);
            }
          }
        } else if (subData !== null && typeof subData === 'object') {
          placeIn(subData);
        }
      };

      const stopSubs = () => {
        for (let i = subscriptions.length - 1; i >= 0; i--) {
          if (subscriptions[i].stop) {
            subscriptions[i].stop();
          }
        }
        subscriptions = [];
      };

      const done = (subscribtion) => {
        processSubData(subscribtion());
      };

      processSubData(this._waitOn(current.params, current.queryParams, done));

      this._triggersExit.push(() => {
        stopSubs();
        for (let i = trackers.length - 1; i >= 0; i--) {
          if (trackers[i].stop) {
            trackers[i].stop();
          }
        }
        trackers = [];
        _data = this._currentData = null;
      });

      whileWaitingAction();
      wait(0);
    } else if (_resources) {
      whileWaitingAction();
      getResources();
    } else if (this._data) {
      next(current, getData());
    } else {
      next(current);
    }
  }

  callAction(current) {
    if (this._data) {
      if (this._onNoData && !this._currentData) {
        this._endWaiting && this._endWaiting();
        this._onNoData(current.params, current.queryParams);
      } else {
        this._endWaiting && this._endWaiting();
        this._action(current.params, current.queryParams, this._currentData);
      }
    } else {
      this._endWaiting && this._endWaiting();
      this._action(current.params, current.queryParams, this._currentData);
    }
  }

  callSubscriptions(current) {
    this.clearSubscriptions();
    if (this.group) {
      this.group.callSubscriptions(current);
    }

    this._subscriptions(current.params, current.queryParams);
  }

  getRouteName() {
    this._routeCloseDep.depend();
    return this.name;
  }

  getParam(key) {
    this._routeCloseDep.depend();
    return this._params.get(key);
  }

  getQueryParam(key) {
    this._routeCloseDep.depend();
    return this._queryParams.get(key);
  }

  watchPathChange() {
    this._pathChangeDep.depend();
  }

  registerRouteClose() {
    this._params = new ReactiveDict();
    this._queryParams = new ReactiveDict();
    this._routeCloseDep.changed();
    this._pathChangeDep.changed();
  }

  registerRouteChange(currentContext, routeChanging) {
    // register params
    this._updateReactiveDict(this._params, currentContext.params);

    // register query params
    this._updateReactiveDict(this._queryParams, currentContext.queryParams);

    // if the route is changing, we need to defer triggering path changing
    // if we did this, old route's path watchers will detect this
    // Real issue is, above watcher will get removed with the new route
    // So, we don't need to trigger it now
    // We are doing it on the route close event. So, if they exists they'll
    // get notify that
    if(!routeChanging) {
      this._pathChangeDep.changed();
    }
  }

  _updateReactiveDict(dict, newValues) {
    const currentKeys = _.keys(newValues);
    const oldKeys = _.keys(dict.keyDeps);

    // set new values
    //  params is an array. So, _.each(params) does not works
    //  to iterate params
    _.each(currentKeys, (key) => {
      dict.set(key, newValues[key]);
    });

    // remove keys which does not exisits here
    const removedKeys = _.difference(oldKeys, currentKeys);
    _.each(removedKeys, (key) => {
      dict.set(key, undefined);
    });
  }
}

export default Route;
<|endoftext|>


<commit_before>'use strict';

var DocumentTitle = require('react-document-title');
var React = require('react');
var Router = require('react-router');
var _ = require('underscore');
var accounting = require('accounting');
var Helper = require('./../../lib/helper.js');

var Link = Router.Link;
var Route = Router.Route;
var RouteHandler = Router.RouteHandler;
var DefaultRoute = Router.DefaultRoute;
var NotFoundRoute = Router.NotFoundRoute;

var SearchInput = React.createClass({
  mixins: [Router.Navigation],
  render: function() {
    return (
      <input
        type="text"
        className="searchInput form-control"
        placeholder="Search"
        autoComplete="off"
        ref="input"
      />
    );
  },
  componentDidMount: function() {
    var ts = [];
    for (var k in window.Tournaments) {
      var t = window.Tournaments[k];
      ts.push({
        id: t.id,
        name: t.name + ' (' + t.date.substr(-4) + ')'
      });
    }
    $(this.getDOMNode()).typeahead({
      source: ts.concat(_.values(window.Players)),
      afterSelect: function(item) {
        if (item.tournaments) {
          this.transitionTo('player', {id: item.id});
        } else {
          this.transitionTo('tournament', {id: item.id});
        }
        this.refs.input.getDOMNode().value = '';
      }.bind(this)
    });
  }
});

var App = React.createClass({
  mixins: [Router.State],
  render: function() {
    var route = _.last(this.getRoutes());
    return (

              <Link className="navbar-brand" to="default">
                MTG Pro Tour Results
              </Link>

                <Link to="rankings" params={{col: 'total'}}>
                  Player Rankings
                </Link>

              <SearchInput />

        <RouteHandler />

    );
  }
});

var NotFound = React.createClass({
  render: function() {
    return (
      <DocumentTitle title="MTG Pro Tour Results | Not Found">

Not Found

Unfortunately, no content was found at this URL.

      </DocumentTitle>
    );
  }
});

var Player = React.createClass({
  mixins: [Router.State],
  render: function() {
    var id = this.getParams().id;
    var p = window.Players[id];
    if (!p) {
      return (<NotFound />);
    }
    return (

        <DocumentTitle title={p.name} />

{p.name}

              {p.stats.t1} / {p.stats.t8} / {p.stats.t16} / {p.stats.total}

              <Link to="rankings" params={{col: 't1'}}>Wins</Link>
              {' / '}
              <Link to="rankings" params={{col: 't8'}}>T8</Link>
              {' / '}
              <Link to="rankings" params={{col: 't16'}}>T16</Link>
              {' / '}
              <Link to="rankings" params={{col: 'total'}}>Total</Link>

              {accounting.formatMoney(p.stats.money, '$', 0)}

              <Link to="rankings" params={{col: 'money'}}>
                Total Money
              </Link>

              {p.stats.points}

              <Link to="rankings" params={{col: 'money'}}>
                Total Pro Points
              </Link>

              Date
              Tournament
              Finish
              Pro Points
              Prize Money

            {_.map(p.tournaments, function(t, index) {
              var tdata = window.Tournaments[t.tid];
              if (!tdata) { return null; }
              var c = null;
              if (t.finish === 1) {
                c = 'success';
              } else if (tdata.team && t.finish <= 4) {
                c = 'warning';
              } else if (!tdata.team && t.finish <= 8) {
                c = 'warning';
              }
              return (

                  {tdata.date}

                    <Link to="tournament" params={{id: t.tid}}>
                      {tdata.name}
                    </Link>

                  {t.finish}
                  {t.propoints}

                    {t.money ? accounting.formatMoney(t.money, '$', 0) : null}

              );
            })}

    );
  }
});

var Tournament = React.createClass({
  mixins: [Router.State],
  render: function() {
    var id = this.getParams().id;
    var t = window.Tournaments[id];
    if (!t) {
      return (<NotFound />);
    }
    return (

        <DocumentTitle title={t.name} />

            {t.coverage ? (
              {t.name}
            ) : t.name}

            {t.formats.join(' / ')}

            {t.date}

            {t.location}

              Player
              Pro Points
              Prize Money

            {_.map(t.standings, function(p, index) {
              var c = null;
              if (t.team2hg) {
                index = (index / 2) | 0;
              }
              else if (t.team) {
                index = (index / 3) | 0;
              }
              var top_limit = t.team ? 4 : 8;
              if (index === 0) {
                c = 'success';
              } else if (index < top_limit) {
                c = 'warning';
              }
              return (

                  {index + 1}

		    <Link to="player" params={{id: p.id}}>{p.name}</Link>{' '}
                    {p.report ? (
                      (report)
                    ) : null}

                  {p.propoints}

                    {p.money ? accounting.formatMoney(p.money, '$', 0) : null}

              );
            })}

    );
  }
});

var Rankings = React.createClass({
  mixins: [Router.State],
  render: function() {
    var col = this.getParams().col;
    var sortedPlayers =
      _.chain(window.Players)
        .values()
        .sortBy(function(player) { return player.stats[col]; })
        .reverse()
        .value();

    // Include anyone tied with the 100th rank
    var cutoff = sortedPlayers[99].stats[col];
    var players = _.filter(sortedPlayers, function(p) { return p.stats[col] >= cutoff; });

    var prev = {value: null};
    var sortImage = ;
    return (

        <DocumentTitle title="Player Rankings" />

Player Rankings

              Player

                <Link to="rankings" params={{col: 'total'}}>
                  Total PTs
                </Link>
                {col === 'total' ? sortImage : null}

                <Link to="rankings" params={{col: 't1'}}>
                  Wins
                </Link>
                {col === 't1' ? sortImage : null}

                <Link to="rankings" params={{col: 't8'}}>
                  Top 8s
                </Link>
                {col === 't8' ? sortImage : null}

                <Link to="rankings" params={{col: 't16'}}>
                  Top 16s
                </Link>
                {col === 't16' ? sortImage : null}

                <Link to="rankings" params={{col: 'points'}}>
                  Pro Points
                </Link>
                {col === 'points' ? sortImage : null}

                <Link to="rankings" params={{col: 'money'}}>
                  Money
                </Link>
                {col === 'money' ? sortImage : null}

            {_.map(players, function(player, index) {
              if (prev.value !== player.stats[col]) {
                prev.value = player.stats[col];
                prev.index = index;
                ++index;
              } else {
                index = null;
              }
              return (

                  {index}

                    <Link to="player" params={{id: player.id}}>
                      {player.name}
                    </Link>

                  {player.stats.total}
                  {player.stats.t1}
                  {player.stats.t8}
                  {player.stats.t16}
                  {player.stats.points}

                    {accounting.formatMoney(player.stats.money, '$', 0)}

              );
            })}

    );
  }
});

var RecentTournaments = React.createClass({
  render: function() {
    var that = this;
    return (

          {'This site is now open source on '}
          GitHub.

        <DocumentTitle title="MTG Pro Tour Results" />
        {_.map(window.Recent, function(tournament) {
          var id = tournament.id;
          return (

                  <Link to="tournament" params={{id: id}}>
                    {tournament.name}
                  </Link>

                        {that._renderLogo(id, tournament)}

                          {tournament.formats.join(' / ')}

                          {tournament.date}

                          {tournament.location}

                        {
                          _.map(tournament.top, function(player, idx) {
                            idx = Helper.getPlayerIndex(
                              idx,
                              tournament.team,
                              tournament.team2hg
                            );
                            return (

                                {idx + 1}{'. '}
                                <Link className="List-link" to="player" params={{id: player.id}}>
                                  {player.name}
                                </Link>

                            );
                          })
                        }

          );
        })}

    );
  },
  _renderLogo: function(id, t) {
    if (!t.logo) {
      return null;
    }
    return (
      <Link to="tournament" params={{id: id}}>

      </Link>
    );
  }
});

var routes = (
  <Route handler={App} path="/">
    <Route name="player" path="/player/:id" handler={Player} />
    <Route name="tournament" path="/tournament/:id" handler={Tournament} />
    <DefaultRoute name="default" handler={RecentTournaments} />
    <Route name="rankings" path="/rankings/:col" handler={Rankings} />
    <NotFoundRoute name="notfound" handler={NotFound} />
  </Route>
);

Router.run(routes, Router.HistoryLocation, function(Handler) {
  React.render(<Handler />, document.getElementById('approot'));
});
<commit_msg>make default rankings top8<commit_after>'use strict';

var DocumentTitle = require('react-document-title');
var React = require('react');
var Router = require('react-router');
var _ = require('underscore');
var accounting = require('accounting');
var Helper = require('./../../lib/helper.js');

var Link = Router.Link;
var Route = Router.Route;
var RouteHandler = Router.RouteHandler;
var DefaultRoute = Router.DefaultRoute;
var NotFoundRoute = Router.NotFoundRoute;

var SearchInput = React.createClass({
  mixins: [Router.Navigation],
  render: function() {
    return (
      <input
        type="text"
        className="searchInput form-control"
        placeholder="Search"
        autoComplete="off"
        ref="input"
      />
    );
  },
  componentDidMount: function() {
    var ts = [];
    for (var k in window.Tournaments) {
      var t = window.Tournaments[k];
      ts.push({
        id: t.id,
        name: t.name + ' (' + t.date.substr(-4) + ')'
      });
    }
    $(this.getDOMNode()).typeahead({
      source: ts.concat(_.values(window.Players)),
      afterSelect: function(item) {
        if (item.tournaments) {
          this.transitionTo('player', {id: item.id});
        } else {
          this.transitionTo('tournament', {id: item.id});
        }
        this.refs.input.getDOMNode().value = '';
      }.bind(this)
    });
  }
});

var App = React.createClass({
  mixins: [Router.State],
  render: function() {
    var route = _.last(this.getRoutes());
    return (

              <Link className="navbar-brand" to="default">
                MTG Pro Tour Results
              </Link>

                <Link to="rankings" params={{col: 't8'}}>
                  Player Rankings
                </Link>

              <SearchInput />

        <RouteHandler />

    );
  }
});

var NotFound = React.createClass({
  render: function() {
    return (
      <DocumentTitle title="MTG Pro Tour Results | Not Found">

Not Found

Unfortunately, no content was found at this URL.

      </DocumentTitle>
    );
  }
});

var Player = React.createClass({
  mixins: [Router.State],
  render: function() {
    var id = this.getParams().id;
    var p = window.Players[id];
    if (!p) {
      return (<NotFound />);
    }
    return (

        <DocumentTitle title={p.name} />

{p.name}

              {p.stats.t1} / {p.stats.t8} / {p.stats.t16} / {p.stats.total}

              <Link to="rankings" params={{col: 't1'}}>Wins</Link>
              {' / '}
              <Link to="rankings" params={{col: 't8'}}>T8</Link>
              {' / '}
              <Link to="rankings" params={{col: 't16'}}>T16</Link>
              {' / '}
              <Link to="rankings" params={{col: 'total'}}>Total</Link>

              {accounting.formatMoney(p.stats.money, '$', 0)}

              <Link to="rankings" params={{col: 'money'}}>
                Total Money
              </Link>

              {p.stats.points}

              <Link to="rankings" params={{col: 'money'}}>
                Total Pro Points
              </Link>

              Date
              Tournament
              Finish
              Pro Points
              Prize Money

            {_.map(p.tournaments, function(t, index) {
              var tdata = window.Tournaments[t.tid];
              if (!tdata) { return null; }
              var c = null;
              if (t.finish === 1) {
                c = 'success';
              } else if (tdata.team && t.finish <= 4) {
                c = 'warning';
              } else if (!tdata.team && t.finish <= 8) {
                c = 'warning';
              }
              return (

                  {tdata.date}

                    <Link to="tournament" params={{id: t.tid}}>
                      {tdata.name}
                    </Link>

                  {t.finish}
                  {t.propoints}

                    {t.money ? accounting.formatMoney(t.money, '$', 0) : null}

              );
            })}

    );
  }
});

var Tournament = React.createClass({
  mixins: [Router.State],
  render: function() {
    var id = this.getParams().id;
    var t = window.Tournaments[id];
    if (!t) {
      return (<NotFound />);
    }
    return (

        <DocumentTitle title={t.name} />

            {t.coverage ? (
              {t.name}
            ) : t.name}

            {t.formats.join(' / ')}

            {t.date}

            {t.location}

              Player
              Pro Points
              Prize Money

            {_.map(t.standings, function(p, index) {
              var c = null;
              if (t.team2hg) {
                index = (index / 2) | 0;
              }
              else if (t.team) {
                index = (index / 3) | 0;
              }
              var top_limit = t.team ? 4 : 8;
              if (index === 0) {
                c = 'success';
              } else if (index < top_limit) {
                c = 'warning';
              }
              return (

                  {index + 1}

		    <Link to="player" params={{id: p.id}}>{p.name}</Link>{' '}
                    {p.report ? (
                      (report)
                    ) : null}

                  {p.propoints}

                    {p.money ? accounting.formatMoney(p.money, '$', 0) : null}

              );
            })}

    );
  }
});

var Rankings = React.createClass({
  mixins: [Router.State],
  render: function() {
    var col = this.getParams().col;
    var sortedPlayers =
      _.chain(window.Players)
        .values()
        .sortBy(function(player) { return player.stats[col]; })
        .reverse()
        .value();

    // Include anyone tied with the 100th rank
    var cutoff = sortedPlayers[99].stats[col];
    var players = _.filter(sortedPlayers, function(p) { return p.stats[col] >= cutoff; });

    var prev = {value: null};
    var sortImage = ;
    return (

        <DocumentTitle title="Player Rankings" />

Player Rankings

              Player

                <Link to="rankings" params={{col: 'total'}}>
                  Total PTs
                </Link>
                {col === 'total' ? sortImage : null}

                <Link to="rankings" params={{col: 't1'}}>
                  Wins
                </Link>
                {col === 't1' ? sortImage : null}

                <Link to="rankings" params={{col: 't8'}}>
                  Top 8s
                </Link>
                {col === 't8' ? sortImage : null}

                <Link to="rankings" params={{col: 't16'}}>
                  Top 16s
                </Link>
                {col === 't16' ? sortImage : null}

                <Link to="rankings" params={{col: 'points'}}>
                  Pro Points
                </Link>
                {col === 'points' ? sortImage : null}

                <Link to="rankings" params={{col: 'money'}}>
                  Money
                </Link>
                {col === 'money' ? sortImage : null}

            {_.map(players, function(player, index) {
              if (prev.value !== player.stats[col]) {
                prev.value = player.stats[col];
                prev.index = index;
                ++index;
              } else {
                index = null;
              }
              return (

                  {index}

                    <Link to="player" params={{id: player.id}}>
                      {player.name}
                    </Link>

                  {player.stats.total}
                  {player.stats.t1}
                  {player.stats.t8}
                  {player.stats.t16}
                  {player.stats.points}

                    {accounting.formatMoney(player.stats.money, '$', 0)}

              );
            })}

    );
  }
});

var RecentTournaments = React.createClass({
  render: function() {
    var that = this;
    return (

          {'This site is now open source on '}
          GitHub.

        <DocumentTitle title="MTG Pro Tour Results" />
        {_.map(window.Recent, function(tournament) {
          var id = tournament.id;
          return (

                  <Link to="tournament" params={{id: id}}>
                    {tournament.name}
                  </Link>

                        {that._renderLogo(id, tournament)}

                          {tournament.formats.join(' / ')}

                          {tournament.date}

                          {tournament.location}

                        {
                          _.map(tournament.top, function(player, idx) {
                            idx = Helper.getPlayerIndex(
                              idx,
                              tournament.team,
                              tournament.team2hg
                            );
                            return (

                                {idx + 1}{'. '}
                                <Link className="List-link" to="player" params={{id: player.id}}>
                                  {player.name}
                                </Link>

                            );
                          })
                        }

          );
        })}

    );
  },
  _renderLogo: function(id, t) {
    if (!t.logo) {
      return null;
    }
    return (
      <Link to="tournament" params={{id: id}}>

      </Link>
    );
  }
});

var routes = (
  <Route handler={App} path="/">
    <Route name="player" path="/player/:id" handler={Player} />
    <Route name="tournament" path="/tournament/:id" handler={Tournament} />
    <DefaultRoute name="default" handler={RecentTournaments} />
    <Route name="rankings" path="/rankings/:col" handler={Rankings} />
    <NotFoundRoute name="notfound" handler={NotFound} />
  </Route>
);

Router.run(routes, Router.HistoryLocation, function(Handler) {
  React.render(<Handler />, document.getElementById('approot'));
});
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require ./webpacked/metamaps.bundle
//= require_directory ./lib
//= require ./src/Metamaps.GlobalUI
//= require ./src/Metamaps.Router
//= require ./src/Metamaps.Backbone
//= require ./src/Metamaps.Views
//= require ./src/views/chatView
//= require ./src/views/videoView
//= require ./src/views/room
//= require ./src/JIT
//= require ./src/check-canvas-support
//= require ./src/Metamaps
//= require ./src/Metamaps.Create
//= require ./src/Metamaps.TopicCard
//= require ./src/Metamaps.SynapseCard
//= require ./src/Metamaps.Visualize
//= require ./src/Metamaps.Util
//= require ./src/Metamaps.Realtime
//= require ./src/Metamaps.Control
//= require ./src/Metamaps.Filter
//= require ./src/Metamaps.Listeners
//= require ./src/Metamaps.Organize
//= require ./src/Metamaps.Topic
//= require ./src/Metamaps.Synapse
//= require ./src/Metamaps.Map
//= require ./src/Metamaps.Account
//= require ./src/Metamaps.Mapper
//= require ./src/Metamaps.Mobile
//= require ./src/Metamaps.Admin
//= require ./src/Metamaps.Import
//= require ./src/Metamaps.JIT
<commit_msg>enable metamaps.debug whoops<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require ./webpacked/metamaps.bundle
//= require_directory ./lib
//= require ./src/Metamaps.GlobalUI
//= require ./src/Metamaps.Router
//= require ./src/Metamaps.Backbone
//= require ./src/Metamaps.Views
//= require ./src/views/chatView
//= require ./src/views/videoView
//= require ./src/views/room
//= require ./src/JIT
//= require ./src/check-canvas-support
//= require ./src/Metamaps
//= require ./src/Metamaps.Create
//= require ./src/Metamaps.TopicCard
//= require ./src/Metamaps.SynapseCard
//= require ./src/Metamaps.Visualize
//= require ./src/Metamaps.Util
//= require ./src/Metamaps.Realtime
//= require ./src/Metamaps.Control
//= require ./src/Metamaps.Filter
//= require ./src/Metamaps.Listeners
//= require ./src/Metamaps.Organize
//= require ./src/Metamaps.Topic
//= require ./src/Metamaps.Synapse
//= require ./src/Metamaps.Map
//= require ./src/Metamaps.Account
//= require ./src/Metamaps.Mapper
//= require ./src/Metamaps.Mobile
//= require ./src/Metamaps.Admin
//= require ./src/Metamaps.Import
//= require ./src/Metamaps.JIT
//= require ./src/Metamaps.Debug
<|endoftext|>


<commit_before>import { createStore, compse} from 'redux';
import { syncHistoryWithStore} from 'react-router-redux';
import { browserHistory } from 'react-router';

// Import the root reducer
import rootReducer from './reducers/index';

// data
import comments from './data/comments';
import posts from './data/posts';

// create an object for the default data
const defaultState = {
	posts,
	comments
};

const store = createStore(rootReducer, defaultState);

export const history = syncHistoryWithStore(browserHistory, store);

if (module.hot) {
	module.hot.accept('./reducers/', () => {
		const nextRootReducer = require('./reducers/index').default;
		store.replaceReducer(nextRootReducer);
	});
}

export default store;<commit_msg>Learn Redux #19 - Redux Dev Tools<commit_after>import { createStore, compose } from 'redux';
import { syncHistoryWithStore} from 'react-router-redux';
import { browserHistory } from 'react-router';

// Import the root reducer
import rootReducer from './reducers/index';

// data
import comments from './data/comments';
import posts from './data/posts';

// create an object for the default data
const defaultState = {
	posts,
	comments
};

const enhancers = compose(
	window.devToolsExtension ? window.devToolsExtension() : f => f
);

const store = createStore(rootReducer, defaultState, enhancers);

export const history = syncHistoryWithStore(browserHistory, store);

if (module.hot) {
	module.hot.accept('./reducers/', () => {
		const nextRootReducer = require('./reducers/index').default;
		store.replaceReducer(nextRootReducer);
	});
}

export default store;<|endoftext|>


<commit_before>
    function toggleSideNav(){
      $(".ui_menu").toggleClass("out");
      $("#sideNavToggle").toggleClass("out");
    }

    function getTweets(){

      var string = $("#twitterSearchInput").val()

      if (!string) return;

      DV.api.get("twitter/search/" + string, function(resp){

        var layer = {
          name: "Twitter " + string,
          type: "scatterplot",
          color: getColor(Math.random(0, 100)),
          data : resp,
          width: 3,
        }

        console.log(layer);
        addLayer(layer);

      }, function(resp){
        console.log(resp);
      });
    }

    function addLayer(layer){
      options.layers.push(layer);
      addLayers(options.layers);
    }

    /************************************
         Fix Checkboxs in Side Menu

         - problem with z-index

         - click label instead of checkbox
           to toggle checkbox
     ************************************/

    $("label").click(function(){
        var checkbox = $(this).siblings("input[type=checkbox]").prop("checked");
        if(checkbox == true){
          $(this).siblings("input[type=checkbox]").prop("checked", false);
        }
        else {
          $(this).siblings("input[type=checkbox]").prop("checked", true);
        }
    });

    var showOption = function(){
      $("input[type=radio]").each(function(){
        if($(this).prop("checked") == true){
          $(this).closest(".scaleOption").children(".optionBody").css("display", "block");
        }
        else{
          $(this).closest(".scaleOption").children(".optionBody").css("display", "none");
        }
      });
    }

    showOption();

    $("label").click(function(){
        var checkbox = $(this).siblings("input[type=radio]").prop("checked");
        if(checkbox == true){
          $(this).siblings("input[type=radio]").prop("checked", false);
          showOption();
        }
        else {
          $(this).siblings("input[type=radio]").prop("checked", true);
          showOption();
        }
    });

      /*****************************************************
         Set Up Range Slider
     ******************************************************/

     // Set up the ranges for each class of slider

    $(".range.price").noUiSlider({
      start: [500, 4000],
      connect: true,
      range: {
        'min': 500,
        'max': 4000
      }
    });

    $(".range.footage").noUiSlider({
      start: [500, 4000],
      connect: true,
      range: {
        'min': 1000,
        'max': 2000
      }
    });

    // Link the range sliders spans
    // Display current values of upper and lower handles

    var linkInput = function(target){
      var thing = '.range.' + target+ '';
      console.log(thing);
      $('.range.' + target+ '').Link('lower').to($('.'+ target+ '_link_lower'));
      $('.range.' + target+ '').Link('upper').to($('.'+ target+ '_link_upper'));
    }

    linkInput('price');
    linkInput('footage');

      /*****************************************************
         set colors for hashtags
     ******************************************************/

    var colors = ["#A0E181", "#AE7AA9", "#718ECB", "#718ECB", "#FDB12E", "#00BCB2", "#00BCB2", "#8E2440"],
       hashtags = Array.prototype.slice.call(document.querySelectorAll(".hashtag"));

    for(var i = 0; i < hashtags.length; i++){
    hashtags[i].setAttribute("color", ""+colors[i]+"");
    }

    function colorize(){

      $(".hashtag").each(function(){
        if($(this).hasClass("active")){
          $(this).css("background-color", $(this).attr("color")).css("color", "#fff");
        } else{
          $(this).css("background-color", "#E0E0E0").css("color", "rgba(0,0,0,0.8)");
        }
      });
    }

    $(".hashtag").click(function(){

      $(this).toggleClass("active");
      colorize();

    });

    $(".hashtag").hover(
      function(){
        if($(this).hasClass("active") != true){
          $(this).css("background-color", $(this).attr("color")).css("color", "#fff");
        }
      },
      function(){
        if($(this).hasClass("active") != true){
          $(this).css("background-color", "#E0E0E0").css("color", "rgba(0,0,0,0.8)");
        }
      }

    );

    colorize();

    /*****************************************************
       Adding a Hashtag to the map
    ******************************************************/

    $("#addHashtagButton").click(function(){

    });

     /*****************************************************
       Add Tooptip functionality for hexagons/points ??
    ******************************************************/<commit_msg>updated hastag colors<commit_after>
    function toggleSideNav(){
      $(".ui_menu").toggleClass("out");
      $("#sideNavToggle").toggleClass("out");
    }

    function getTweets(){

      var string = $("#twitterSearchInput").val()

      if (!string) return;

      DV.api.get("twitter/search/" + string, function(resp){

        var layer = {
          name: "Twitter " + string,
          type: "scatterplot",
          color: getColor(Math.random(0, 100)),
          data : resp,
          width: 3,
        }

        console.log(layer);
        addLayer(layer);

      }, function(resp){
        console.log(resp);
      });
    }

    function addLayer(layer){
      options.layers.push(layer);
      addLayers(options.layers);
    }

    /************************************
         Fix Checkboxs in Side Menu

         - problem with z-index

         - click label instead of checkbox
           to toggle checkbox
     ************************************/

    $("label").click(function(){
        var checkbox = $(this).siblings("input[type=checkbox]").prop("checked");
        if(checkbox == true){
          $(this).siblings("input[type=checkbox]").prop("checked", false);
        }
        else {
          $(this).siblings("input[type=checkbox]").prop("checked", true);
        }
    });

    var showOption = function(){
      $("input[type=radio]").each(function(){
        if($(this).prop("checked") == true){
          $(this).closest(".scaleOption").children(".optionBody").css("display", "block");
        }
        else{
          $(this).closest(".scaleOption").children(".optionBody").css("display", "none");
        }
      });
    }

    showOption();

    $("label").click(function(){
        var checkbox = $(this).siblings("input[type=radio]").prop("checked");
        if(checkbox == true){
          $(this).siblings("input[type=radio]").prop("checked", false);
          showOption();
        }
        else {
          $(this).siblings("input[type=radio]").prop("checked", true);
          showOption();
        }
    });

      /*****************************************************
         Set Up Range Slider
     ******************************************************/

     // Set up the ranges for each class of slider

    $(".range.price").noUiSlider({
      start: [500, 4000],
      connect: true,
      range: {
        'min': 500,
        'max': 4000
      }
    });

    $(".range.footage").noUiSlider({
      start: [500, 4000],
      connect: true,
      range: {
        'min': 1000,
        'max': 2000
      }
    });

    // Link the range sliders spans
    // Display current values of upper and lower handles

    var linkInput = function(target){
      var thing = '.range.' + target+ '';
      console.log(thing);
      $('.range.' + target+ '').Link('lower').to($('.'+ target+ '_link_lower'));
      $('.range.' + target+ '').Link('upper').to($('.'+ target+ '_link_upper'));
    }

    linkInput('price');
    linkInput('footage');

      /*****************************************************
         set colors for hashtags
     ******************************************************/

    var colors = ["#A0E181", "#AE7AA9", "#718ECB", "#EA7572", "#FDB12E", "#00BCB2", "#7935FF", "#8E2440"],
       hashtags = Array.prototype.slice.call(document.querySelectorAll(".hashtag"));

    for(var i = 0; i < hashtags.length; i++){
    hashtags[i].setAttribute("color", ""+colors[i]+"");
    }

    function colorize(){

      $(".hashtag").each(function(){
        if($(this).hasClass("active")){
          $(this).css("background-color", $(this).attr("color")).css("color", "#fff");
        } else{
          $(this).css("background-color", "#E0E0E0").css("color", "rgba(0,0,0,0.8)");
        }
      });
    }

    $(".hashtag").click(function(){

      $(this).toggleClass("active");
      colorize();

    });

    $(".hashtag").hover(
      function(){
        if($(this).hasClass("active") != true){
          $(this).css("background-color", $(this).attr("color")).css("color", "#fff");
        }
      },
      function(){
        if($(this).hasClass("active") != true){
          $(this).css("background-color", "#E0E0E0").css("color", "rgba(0,0,0,0.8)");
        }
      }

    );

    colorize();

    /*****************************************************
       Adding a Hashtag to the map
    ******************************************************/

    $("#addHashtagButton").click(function(){

    });

     /*****************************************************
       Add Tooptip functionality for hexagons/points ??
    ******************************************************/<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap

// Bryan: removed require_tree . because mercury causes problems if loaded on every page

$(function() {
  function ready() {
    var TxtRotate = function(el, toRotate, period) {
      this.toRotate = toRotate;
      this.el = el;
      this.loopNum = 0;
      this.period = parseInt(period, 10) || 2000;
      this.txt = '';
      this.tick();
      this.isDeleting = false;
    };

    TxtRotate.prototype.tick = function() {
      var i = this.loopNum % this.toRotate.length;
      var fullTxt = this.toRotate[i];

      if (this.isDeleting) {
        this.txt = fullTxt.substring(0, this.txt.length - 1);
      } else {
        this.txt = fullTxt.substring(0, this.txt.length + 1);
      }

      this.el.innerHTML = ''+this.txt+'';

      var that = this;
      var delta = 300 - Math.random() * 100;

      if (this.isDeleting) { delta /= 2; }

      if (!this.isDeleting && this.txt === fullTxt) {
        delta = this.period;
        this.isDeleting = true;
      } else if (this.isDeleting && this.txt === '') {
        this.isDeleting = false;
        this.loopNum++;
        delta = 500;
      }

      setTimeout(function() {
        that.tick();
      }, delta);
    };

    var elements = document.getElementsByClassName('txt-rotate');
    for (var i=0; i<elements.length; i++) {
      var toRotate = elements[i].getAttribute('data-rotate');
      var period = elements[i].getAttribute('data-period');
      if (toRotate) {
        new TxtRotate(elements[i], JSON.parse(toRotate), period);
      }
    }
    // INJECT CSS
    var css = document.createElement("style");
    css.type = "text/css";
    css.innerHTML = ".txt-rotate > .wrap";
    document.body.appendChild(css);

    /*
      AFFIX BEHAVIOUR RELATED CODE
     */

    var affixedNav = $('#nav'),
        header = $('#main_header'),
        wrapper = $('#wrap'),
        sidebar = $('#sidebar'),
        notSidebar = $('#not-sidebar'),
        headerHeight = header.height();

    // only worry about the complex sidebar behaviour if the sidebar is shorter than the actual document
    var worryAboutSidebar = (sidebar != null) && (sidebar.height() < notSidebar.height());

    if (sidebar != null) {
      sidebar.css({ 'max-height': 500 });
    }

    function adjustSidebarPosition() {
      // TODO Bryan: avoid hardcoded values if possible
      var diff = $(window).scrollTop() + sidebar.height() + 700 - $(document).height();
      if (diff > 0) {
        sidebar.css({ 'top': affixedNav.height() + 125 - diff });
      }
    }

    // TODO Bryan: this works but gives jittery animation
    var h1 = 0,
        h2 = 0,
        checkDelay = 30; // can't seem to go too low
    function checkSidebarHeight() {
      h2 = sidebar.height();
      worryAboutSidebar = (h2 < notSidebar.height());
      if (h1 != h2) {
        adjustSidebarPosition();
        h1 = h2;
        setTimeout(checkSidebarHeight, checkDelay);
      }

      if (!worryAboutSidebar) {
        sidebar.removeClass('affix');
        sidebar.css({ 'top': 0 });
      }
    }

    // a hack to follow collapse animation, ideally should find the right animation callbacks
    $('.project-collapse').on('click', function() {
      h1 = h2 = sidebar.height();
      if ($(window).scrollTop() > headerHeight) {
        setTimeout(checkSidebarHeight, checkDelay);
      }
    });

    $(window).scroll(function() {
      if ($(this).scrollTop() > headerHeight) {
        if (!affixedNav.hasClass('affix')) {
          affixedNav.addClass('affix');
          wrapper.css({ 'padding-top': affixedNav.height() + parseInt(affixedNav.css('margin-bottom')) });
          if (worryAboutSidebar) {
            sidebar.addClass('affix');
            // TODO Bryan: avoid hardcoded values if possible
            sidebar.css({ 'top': affixedNav.height() + 125 });
          }
        }
        if (worryAboutSidebar) {
          adjustSidebarPosition();
        }
      } else if (affixedNav.hasClass('affix')) {
        affixedNav.removeClass('affix');
        wrapper.css({ 'padding-top': 0 });
        if (worryAboutSidebar) {
          sidebar.removeClass('affix');
          sidebar.css({ 'top': 0 });
        }
      }
    });
  }

  $(document).ready(ready);
  $(document).on('page:load', ready);
});
<commit_msg>resolved merge conflicts with develop<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap

// Bryan: removed require_tree . because mercury causes problems if loaded on every page

$(function() {
  function ready() {
    var TxtRotate = function(el, toRotate, period) {
      this.toRotate = toRotate;
      this.el = el;
      this.loopNum = 0;
      this.period = parseInt(period, 10) || 2000;
      this.txt = '';
      this.tick();
      this.isDeleting = false;
    };

    TxtRotate.prototype.tick = function() {
      var i = this.loopNum % this.toRotate.length;
      var fullTxt = this.toRotate[i];

      if (this.isDeleting) {
        this.txt = fullTxt.substring(0, this.txt.length - 1);
      } else {
        this.txt = fullTxt.substring(0, this.txt.length + 1);
      }

      this.el.innerHTML = ''+this.txt+'';

      var that = this;
      var delta = 300 - Math.random() * 100;

      if (this.isDeleting) { delta /= 2; }

      if (!this.isDeleting && this.txt === fullTxt) {
        delta = this.period;
        this.isDeleting = true;
      } else if (this.isDeleting && this.txt === '') {
        this.isDeleting = false;
        this.loopNum++;
        delta = 500;
      }

      setTimeout(function() {
        that.tick();
      }, delta);
    };

    var elements = document.getElementsByClassName('txt-rotate');
    for (var i=0; i<elements.length; i++) {
      var toRotate = elements[i].getAttribute('data-rotate');
      var period = elements[i].getAttribute('data-period');
      if (toRotate) {
        new TxtRotate(elements[i], JSON.parse(toRotate), period);
      }
    }
    // INJECT CSS
    var css = document.createElement("style");
    css.type = "text/css";
    css.innerHTML = ".txt-rotate > .wrap";
    document.body.appendChild(css);

    /*
      AFFIX BEHAVIOUR RELATED CODE
     */

    var affixedNav = $('#nav'),
        header = $('#main_header'),
        wrapper = $('#wrap'),
        sidebar = $('#sidebar'),
        notSidebar = $('#not-sidebar'),
        thresholdHeight = header.height();

    // only worry about the complex sidebar behaviour if the sidebar is shorter than the actual document
    var worryAboutSidebar = (sidebar != null) && (sidebar.height() < notSidebar.height());

    if (sidebar != null) {
      sidebar.css({ 'max-height': 500 });
    }

    function adjustSidebarPosition() {
      // TODO Bryan: avoid hardcoded values if possible
      var diff = $(window).scrollTop() + sidebar.height() + 700 - $(document).height();
      if (diff > 0) {
        sidebar.css({ 'top': affixedNav.height() + 125 - diff });
      }
    }

    // TODO Bryan: this works but gives jittery animation
    var h1 = 0,
        h2 = 0,
        checkDelay = 30; // can't seem to go too low
    function checkSidebarHeight() {
      h2 = sidebar.height();
      worryAboutSidebar = (h2 < notSidebar.height());
      if (h1 != h2) {
        adjustSidebarPosition();
        h1 = h2;
        setTimeout(checkSidebarHeight, checkDelay);
      }

      if (!worryAboutSidebar) {
        sidebar.removeClass('affix');
        sidebar.css({ 'top': 0 });
      }
    }

    // a hack to follow collapse animation, ideally should find the right animation callbacks
    $('.project-collapse').on('click', function() {
      h1 = h2 = sidebar.height();
      if ($(window).scrollTop() > thresholdHeight) {
        setTimeout(checkSidebarHeight, checkDelay);
      }
    });

    $(window).scroll(function() {
      if ($(this).scrollTop() > thresholdHeight) {
        if (!affixedNav.hasClass('affix')) {
          affixedNav.addClass('affix');
          wrapper.css({ 'padding-top': affixedNav.height() + parseInt(affixedNav.css('margin-bottom')) });
          if (worryAboutSidebar) {
            sidebar.addClass('affix');
            // TODO Bryan: avoid hardcoded values if possible
            sidebar.css({ 'top': affixedNav.height() + 125 });
          }
        }
        if (worryAboutSidebar) {
          adjustSidebarPosition();
        }
      } else if (affixedNav.hasClass('affix')) {
        affixedNav.removeClass('affix');
        wrapper.css({ 'padding-top': 0 });
        if (worryAboutSidebar) {
          sidebar.removeClass('affix');
          sidebar.css({ 'top': 0 });
        }
      }
    });
  }

  $(document).ready(ready);
  $(document).on('page:load', ready);
});
<|endoftext|>


<commit_before>
/**
 * cf-tables
 * https://github.com/cfpb/cf-tables
 *
 * A public domain work of the Consumer Financial Protection Bureau
 */

( function( $ ) {
  'use strict';

  var SortableTable = function( table, options ) {
    /*  At the moment, there are no default settings, but here's an object
        for their future use! */
    var defaults = {
        },
    // settings is defaults combined with user options
    settings = {},
    // rows is an Array of Arrays, serving as a model of the table
    rows = [],
    $table = $( table ),
    $headercells = $( table ).find( '.sortable' );

    /**
     * Initializes the SortableTable
     * @param { object } options - Customizble options object
     */
    function _init( options ) {
      settings = $.extend( {}, defaults, options );
      _clickHandler();
      // If the following classes exist, start by sorting those columns.
      $table.find( '.sorter_up, .sorter_down' ).click();
    }

    /**
       * Sorting function for Array.sort()
       *
       * @param { number } sign - A number where a negative number indicates a
       * reverse sort.
       * @param { sortType } sortType - A string used for options such as integer sorting
       * @returns function - A function to be used by the Array.sort method, where
       * the parameters 'a' and 'b' is each an Array (of Arrays) to be sorted
       */

    function _arraySorter( sign, sortType ) {
      return function( a, b ) {
        // Set a and b to the first Array in each Array-of-Arrays
        a = a[0];
        b = b[0];

        // For number sort, convert a & b to numbers.
        if ( sortType === 'number' ) {
          a = Number( a.replace(/[^\d.-]/g, '') );
          b = Number( b.replace(/[^\d.-]/g, '') );
        }

        // Sort the values
        if ( a < b ) {
          return sign * -1;
        }
        if ( a > b ) {
          return sign;
        }
        return 0;
      };
    }

    /**
     * Updates internal model of table (rows[])
     * @param { index } - The index of the column used for sorting, which is
     * used as the "key" for rows[] - it is set as the only value in the first
     * array.
     */
    function _getRows( index ) {
      // Clear the model
      rows.length = 0;
      // Find the value in each row of the column we're sorting by,
      // add it to the rows Array
      $table.find( 'tbody tr' ).each( function() {
        // indices count from 0, but nth-child counts from 1
        var child = index + 1,
            key = $( this ).find( 'td:nth-child(' + child + ')' ).text().trim();
        rows.push( [key, $( this )] );
      });
    }

    /**
     * Updates the table in the DOM
     * @param { index } - The index of the column used for sorting
     */
    function _updateTable( index ) {
      // Empty the tbody to prepare for sorted rows
      $table.find( 'tbody' ).empty();

      // Insert sorted rows
      for( var i = 0; i < rows.length; i++ ) {
        $table.find( 'tbody' ).append( rows[i][1] );
      }
    }

    /**
     * Handler for click events on the column header
     * No parameters - uses SortableTable properties, updates the DOM.
     */
    function _clickHandler() {
      $headercells.on( 'click', function() {
        var sortType = $( this ).attr( 'data-sort_type' ),
            sign = 1,
            index = $table.find('tr:first-child').children( 'th, td' ).index( $( this ) );

        _getRows( index );

        // For reverse sorting, reverse the sign
        if ( $(this).hasClass( 'sorted_up' ) === true ) {
          sign = -1;
          $( '.sortable' ).removeClass( 'sorted_up sorted_down' );
          $( this ).addClass( 'sorted_down' );
        }
        else {
          $( '.sortable' ).removeClass( 'sorted_up sorted_down' );
          $( this ).addClass( 'sorted_up' );
        }

        // Perform the row sort
        rows.sort( _arraySorter( sign, sortType ) );

        _updateTable( index );

      });
    }

    _init( options );

  };

  /**
   * Instatiates the SortableTable
   */
  $.fn.sortableTable = function( options ) {
    return this.each( function() {
      ( options || ( options = {} ) ).$element = $( this );
      // SortableTable.init.call( this, options );
      var scol = new SortableTable( this, options );
    });
  };

  $( document ).ready( function() {
    $( '.table__sortable' ).sortableTable();
  });

}( jQuery ));<commit_msg>Removed unused code from cf-tables.js<commit_after>
/**
 * cf-tables
 * https://github.com/cfpb/cf-tables
 *
 * A public domain work of the Consumer Financial Protection Bureau
 */

( function( $ ) {
  'use strict';

  var SortableTable = function( table, options ) {
    /*  At the moment, there are no default settings, but here's an object
        for their future use! */
    var defaults = {
        },
    // settings is defaults combined with user options
    settings = {},
    // rows is an Array of Arrays, serving as a model of the table
    rows = [],
    $table = $( table ),
    $headercells = $( table ).find( '.sortable' );

    /**
     * Initializes the SortableTable
     * @param { object } options - Customizble options object
     */
    function _init( options ) {
      settings = $.extend( {}, defaults, options );
      _clickHandler();
      // If the following classes exist, start by sorting those columns.
      $table.find( '.sorter_up, .sorter_down' ).click();
    }

    /**
       * Sorting function for Array.sort()
       *
       * @param { number } sign - A number where a negative number indicates a
       * reverse sort.
       * @param { sortType } sortType - A string used for options such as integer sorting
       * @returns function - A function to be used by the Array.sort method, where
       * the parameters 'a' and 'b' is each an Array (of Arrays) to be sorted
       */

    function _arraySorter( sign, sortType ) {
      return function( a, b ) {
        // Set a and b to the first Array in each Array-of-Arrays
        a = a[0];
        b = b[0];

        // For number sort, convert a & b to numbers.
        if ( sortType === 'number' ) {
          a = Number( a.replace(/[^\d.-]/g, '') );
          b = Number( b.replace(/[^\d.-]/g, '') );
        }

        // Sort the values
        if ( a < b ) {
          return sign * -1;
        }
        if ( a > b ) {
          return sign;
        }
        return 0;
      };
    }

    /**
     * Updates internal model of table (rows[])
     * @param { index } - The index of the column used for sorting, which is
     * used as the "key" for rows[] - it is set as the only value in the first
     * array.
     */
    function _getRows( index ) {
      // Clear the model
      rows.length = 0;
      // Find the value in each row of the column we're sorting by,
      // add it to the rows Array
      $table.find( 'tbody tr' ).each( function() {
        // indices count from 0, but nth-child counts from 1
        var child = index + 1,
            key = $( this ).find( 'td:nth-child(' + child + ')' ).text().trim();
        rows.push( [key, $( this )] );
      });
    }

    /**
     * Updates the table in the DOM
     * @param { index } - The index of the column used for sorting
     */
    function _updateTable( index ) {
      // Empty the tbody to prepare for sorted rows
      $table.find( 'tbody' ).empty();

      // Insert sorted rows
      for( var i = 0; i < rows.length; i++ ) {
        $table.find( 'tbody' ).append( rows[i][1] );
      }
    }

    /**
     * Handler for click events on the column header
     * No parameters - uses SortableTable properties, updates the DOM.
     */
    function _clickHandler() {
      $headercells.on( 'click', function() {
        var sortType = $( this ).attr( 'data-sort_type' ),
            sign = 1,
            index = $table.find('tr:first-child').children( 'th, td' ).index( $( this ) );

        _getRows( index );

        // For reverse sorting, reverse the sign
        if ( $(this).hasClass( 'sorted_up' ) === true ) {
          sign = -1;
          $( '.sortable' ).removeClass( 'sorted_up sorted_down' );
          $( this ).addClass( 'sorted_down' );
        }
        else {
          $( '.sortable' ).removeClass( 'sorted_up sorted_down' );
          $( this ).addClass( 'sorted_up' );
        }

        // Perform the row sort
        rows.sort( _arraySorter( sign, sortType ) );

        _updateTable( index );

      });
    }

    _init( options );

  };

  /**
   * Instatiates the SortableTable
   */
  $.fn.sortableTable = function( options ) {
    return this.each( function() {
      ( options || ( options = {} ) ).$element = $( this );
      var scol = new SortableTable( this, options );
    });
  };

  $( document ).ready( function() {
    $( '.table__sortable' ).sortableTable();
  });

}( jQuery ));<|endoftext|>


<commit_before>var alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
var base = alphabet.length;

// Create a lookup table to fetch character index
var alphabetLookup = alphabet.split("").reduce(function(lookup, char, index) {
  lookup[char] = index;
  return lookup;
}, {});

function assertNonNegativeSafeInteger(val) {
  if (
    typeof val !== "number" ||
    isNaN(val) ||
    val < 0 ||
    val > Number.MAX_SAFE_INTEGER ||
    Math.floor(val) !== val
  ) {
    throw new Error("Value passed is not a non-negative safe integer.");
  }
}

function assertString(str) {
  if (typeof str !== "string") {
    throw new Error("Value passed is not a string.");
  }
}

function assertBase58Character(character) {
  if (alphabetLookup[character] === undefined) {
    throw new Error("Value passed is not a valid Base58 string.");
  }
}

exports.encode = function(num) {
  var str = "";
  var modulus;

  num = Number(num);

  assertNonNegativeSafeInteger(num);

  while (num >= base) {
    modulus = num % base;
    str = alphabet[modulus] + str;
    num = Math.floor(num / base);
  }

  return alphabet[num] + str;
};

exports.decode = function(str) {
  assertString(str);

  return str.split("").reverse().reduce(function(num, character, index) {
    assertBase58Character(character);
    return num + alphabetLookup[character] * Math.pow(base, index);
  }, 0);
};
<commit_msg>Update to newer ES features<commit_after>const alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
const base = alphabet.length;

// Create a lookup table to fetch character index
const alphabetLookup = [...alphabet].reduce((lookup, char, index) => {
  lookup[char] = index;
  return lookup;
}, {});

function assertNonNegativeSafeInteger(val) {
  if (
    typeof val !== "number" ||
    isNaN(val) ||
    val < 0 ||
    val > Number.MAX_SAFE_INTEGER ||
    Math.floor(val) !== val
  ) {
    throw new Error("Value passed is not a non-negative safe integer.");
  }
}

function assertString(str) {
  if (typeof str !== "string") {
    throw new Error("Value passed is not a string.");
  }
}

function assertBase58Character(character) {
  if (alphabetLookup[character] === undefined) {
    throw new Error("Value passed is not a valid Base58 string.");
  }
}

exports.encode = function(num) {
  let str = "";
  let modulus;

  num = Number(num);

  assertNonNegativeSafeInteger(num);

  while (num >= base) {
    modulus = num % base;
    str = alphabet[modulus] + str;
    num = Math.floor(num / base);
  }

  return alphabet[num] + str;
};

exports.decode = function(str) {
  assertString(str);

  return [...str].reverse().reduce((num, character, index) => {
    assertBase58Character(character);
    return num + alphabetLookup[character] * Math.pow(base, index);
  }, 0);
};
<|endoftext|>


<commit_before>/*
   This copies all the files from

   arguments:

   folderTree is an array
        each object is an array with two elements
        first element is original folder Id
        second element is new folder Id, corresponding to original
        third element is the path name for the folder

    todo: is folderId a necessary argument?
    folderId is the ID of the root src folder

    copyPermissions is a boolean indicating whether or not to copy original sharing permissions

    added is an array of email addresses for collaborators with whom the files
        have already been shared due to parent folder privileges
*/

var $ = jQuery = require('jquery');
require('../../node_modules/jquery-ui/effect-blind.js');

exports.run = function(folderTree, folderId, copyPermissions, added) {
    return copyFiles(folderTree, folderId, copyPermissions, added);
}

function copyFiles(folderTree, folderId, copyPermissions, added) {

    // deconstruct element from folderTree
    var pair = folderTree.shift();
    var fromId = pair[0];
    var toId = pair[1];
    var folderName = pair[2];

    $("#" + toId).html("Copying files ").addClass("disabled");

    google.script.run
      .withSuccessHandler(function() {
        // Respond to success conditions here.
        $("#" + toId).html("Complete").addClass("bg-success").removeClass("disabled");

        if (folderTree.length > 0) {
          copyFiles(folderTree, folderId, copyPermissions, added);
        } else {
          $("#status-title").html("Folder copy complete");
          $("#complete").html("Successfully copied folder to your Google Drive.");
          $("#complete").show("blind");
          $("#please-review").show("blind");
        }
      })

      .withFailureHandler(function(msg) {
        // Respond to failure conditions here.

        $("#errors").append("
Error: There was an error copying files to folder '" + toId + "'.
Error message: " + msg + ".
");

        if (folderTree.length > 0) {
          copyFiles(folderTree);
        } else {
          $("#status-title").html("Folder copy complete");
          $("#complete").html("Successfully copied folder to your Google Drive.");
          $("#complete").show("blind");
          $("#please-review").show("blind");

        }

      })

      .copyFiles(fromId, toId, copyPermissions, added);
}<commit_msg>changed exports.run to exports.copy to avoid ambiguity when calling<commit_after>/*
   This copies all the files from

   arguments:

   folderTree is an array
        each object is an array with two elements
        first element is original folder Id
        second element is new folder Id, corresponding to original
        third element is the path name for the folder

    todo: is folderId a necessary argument?
    folderId is the ID of the root src folder

    copyPermissions is a boolean indicating whether or not to copy original sharing permissions

    added is an array of email addresses for collaborators with whom the files
        have already been shared due to parent folder privileges
*/

var $ = jQuery = require('jquery');
require('../../node_modules/jquery-ui/effect-blind.js');

exports.copy = function(folderTree, folderId, copyPermissions, added) {
    return copyFiles(folderTree, folderId, copyPermissions, added);
}

function copyFiles(folderTree, folderId, copyPermissions, added) {

    // deconstruct element from folderTree
    var pair = folderTree.shift();
    var fromId = pair[0];
    var toId = pair[1];
    var folderName = pair[2];

    $("#" + toId).html("Copying files ").addClass("disabled");

    google.script.run
      .withSuccessHandler(function() {
        // Respond to success conditions here.
        $("#" + toId).html("Complete").addClass("bg-success").removeClass("disabled");

        if (folderTree.length > 0) {
          copyFiles(folderTree, folderId, copyPermissions, added);
        } else {
          $("#status-title").html("Folder copy complete");
          $("#complete").html("Successfully copied folder to your Google Drive.");
          $("#complete").show("blind");
          $("#please-review").show("blind");
        }
      })

      .withFailureHandler(function(msg) {
        // Respond to failure conditions here.

        $("#errors").append("
Error: There was an error copying files to folder '" + toId + "'.
Error message: " + msg + ".
");

        if (folderTree.length > 0) {
          copyFiles(folderTree);
        } else {
          $("#status-title").html("Folder copy complete");
          $("#complete").html("Successfully copied folder to your Google Drive.");
          $("#complete").show("blind");
          $("#please-review").show("blind");

        }

      })

      .copyFiles(fromId, toId, copyPermissions, added);
}<|endoftext|>