<commit_before>// flow-typed signature: 1ff75a0f1841b2c07a22a11bbd6dc9f9
// flow-typed version: 492180217b/react-native-google-analytics-bridge_v5.3.x/flow_>=v0.56.x

// @flow
declare module "react-native-google-analytics-bridge" {
  declare export type Product = {
    id: string,
    name: string,
    category?: string,
    brand?: string,
    variant?: string,
    price?: number,
    quantity?: number,
    couponCode?: string
  };

  declare export type Transaction = {
    id: string,
    affiliation?: string,
    revenue?: number,
    tax?: number,
    shipping?: number,
    couponCode?: string
  };

  declare export type OptionalValue = {
    label: string,
    value: number
  };

  declare export type OptionalTimingValue = {
    name: string,
    label?: string
  };

  declare export type CustomDimensionsFieldIndexMap = {
    [key: string]: number
  };

  declare export type CustomDimensionsByIndex = {
    [key: number]: any
  };

  declare export type CustomDimensionsByField = {
    [key: string]: any
  };

  declare export type CustomMetrics = {
    [key: number]: number
  };

  /**
   * Used to bridge tracker data to native Google analytics.
   * Saves necessary tracker (specific) data to format data as native part of Google analytics expect.
   */
  declare export class GoogleAnalyticsTracker {
    /**
     * Save all tracker related data that is needed to call native methods with proper data.
     * @param {String} trackerId
     * @param {{fieldName: fieldIndex}} customDimensionsFieldsIndexMap Custom dimensions field/index pairs
     */
    constructor(
      trackerId: string,
      customDimensionsFieldsIndexMap?: CustomDimensionsFieldIndexMap
    ): GoogleAnalyticsTracker,

    /**
     * If Tracker has customDimensionsFieldsIndexMap, it will transform
     * customDimensions map pairs {field: value} to {fieldIndex: value}.
     * Otherwise customDimensions are passed trough untouched.
     * Underlay native methods will transform provided customDimensions map to expected format.
     * Google analytics expect dimensions to be tracker with 'dimension{index}' keys,
     * not dimension field names.
     * @param {CustomDimensionsByIndex} customDimensions
     * @returns {CustomDimensionsByField}
     */
    transformCustomDimensionsFieldsToIndexes(
      customDimensions: CustomDimensionsByIndex
    ): CustomDimensionsByField,

    /**
     * Track the current screen/view
     * @param  {String} screenName The name of the current screen
     */
    trackScreenView(screenName: string): void,

    /**
     * Track the campaign from url
     * @param  {String} urlString The url of the deep link
     */
    trackCampaignFromUrl(urlString: string): void,

    /**
     * Track an event that has occured
     * @param  {String} category       The event category
     * @param  {String} action         The event action
     * @param  {OptionalValue} optionalValues An object containing optional label and value
     */
    trackEvent(
      category: string,
      action: string,
      optionalValues?: OptionalValue
    ): void,

    /**
     * Track the current screen/view with custom dimension values
     * @param  {String} screenName The name of the current screen
     * @param  {CustomDimensionsByIndex | CustomDimensionsByField} customDimensionValues An object containing custom dimension key/value pairs
     */
    trackScreenViewWithCustomDimensionValues(
      screenName: string,
      customDimensionValues: CustomDimensionsByIndex | CustomDimensionsByField
    ): void,

    /**
     * Track an event that has occured with custom dimension values
     * @param  {String} category       The event category
     * @param  {String} action         The event action
     * @param  {OptionalValue} optionalValues An object containing optional label and value
     * @param  {CustomDimensionsByIndex | CustomDimensionsByField} customDimensionValues An object containing custom dimension key/value pairs
     */
    trackEventWithCustomDimensionValues(
      category: string,
      action: string,
      optionalValues?: OptionalValue,
      customDimensionValues?: CustomDimensionsByIndex | CustomDimensionsByField
    ): void,
    /**
     * Track an event that has occured with custom dimension and metric values.
     * @param  {String} category       The event category
     * @param  {String} action         The event action
     * @param  {OptionalValue} optionalValues An object containing optional label and value
     * @param  {CustomDimensionsByIndex | CustomDimensionsByField} customDimensionValues An object containing custom dimension key/value pairs
     * @param  {CustomMetrics} customMetricValues An object containing custom metric key/value pairs
     */
    trackEventWithCustomDimensionAndMetricValues(
      category: string,
      action: string,
      optionalValues?: OptionalValue,
      customDimensionValues?: CustomDimensionsByIndex | CustomDimensionsByField,
      customMetricValues?: CustomMetrics
    ): void,

    /**
     * Track an event that has occured
     * @param  {String} category       The event category
     * @param  {Number} value         	The timing measurement in milliseconds
     * @param  {OptionalTimingValue} optionalValues An object containing optional name and label
     */
    trackTiming(
      category: string,
      value: number,
      optionalValues: OptionalTimingValue
    ): void,

    /**
     * Track a purchase event. This uses the Enhanced Ecommerce GA feature.
     * @param  {Product} product       An object with product values
     * @param  {Transaction} transaction   An object with transaction values
     * @param  {String} eventCategory The event category, defaults to Ecommerce
     * @param  {String} eventAction   The event action, defaults to Purchase
     */
    trackPurchaseEvent(
      product: Product,
      transaction: Transaction,
      eventCategory?: string,
      eventAction?: string
    ): void,

    /**
     * Track a purchase event. This uses the Enhanced Ecommerce GA feature.
     * @param  {Product[]} products       An array with products
     * @param  {Transaction} transaction   An object with transaction values
     * @param  {String} eventCategory The event category, defaults to Ecommerce
     * @param  {String} eventAction   The event action, defaults to Purchase
     */
    trackMultiProductsPurchaseEvent(
      products: Product[],
      transaction: Transaction,
      eventCategory?: string,
      eventAction?: string
    ): void,

    /**
     * Track a purchase event with custom dimensions. This uses the Enhanced Ecommerce GA feature.
     * @param  {Product[]} products       An array with products
     * @param  {Transaction} transaction   An object with transaction values
     * @param  {String} eventCategory The event category, defaults to Ecommerce
     * @param  {String} eventAction   The event action, defaults to Purchase
     * @param  {CustomDimensionsByIndex | CustomDimensionsByField} customDimensionValues An object containing custom dimension key/value pairs
     */
    trackMultiProductsPurchaseEventWithCustomDimensionValues(
      products: Product[],
      transaction: Transaction,
      eventCategory?: string,
      eventAction?: string,
      customDimensions?: CustomDimensionsByIndex | CustomDimensionsByField
    ): void,

    /**
     * Track an exception
     * @param  {String} error The description of the error
     * @param  {Boolean} fatal A value indiciating if the error was fatal, defaults to false
     */
    trackException(error: string, fatal?: boolean): void,

    /**
     * Sets the current userId for tracking.
     * @param {String} userId The current userId
     */
    setUser(userId: string): void,

    /**
     * Sets the current clientId for tracking.
     * @param {String} clientId The current userId
     */
    setClient(clientId: string): void,

    /**
     * Sets if IDFA (identifier for advertisers) collection should be enabled
     * @param  {Boolean} enabled Defaults to true
     */
    allowIDFA(enabled: boolean): void,

    /**
     * Track a social interaction, Facebook, Twitter, etc.
     * @param  {String} network
     * @param  {String} action
     * @param  {String} targetUrl
     */
    trackSocialInteraction(
      network: string,
      action: string,
      targetUrl?: string
    ): void,

    /**
     * Sets if uncaught exceptions should be tracked
     * @param {Boolean} enabled
     */
    setTrackUncaughtExceptions(enabled: boolean): void,

    /**
     * Sets the trackers appName
     * The Bundle name is used by default
     * @param {String} appName
     */
    setAppName(appName: string): void,

    /**
     * Sets the trackers appVersion
     * @param {String} appVersion
     */
    setAppVersion(appVersion: string): void,

    /**
     * Sets if AnonymizeIp is enabled
     * If enabled the last octet of the IP address will be removed
     * @param {Boolean} enabled
     */
    setAnonymizeIp(enabled: boolean): void,
    /**
     * Sets tracker sampling rate.
     * @param {Float} sampleRatio Percentage 0 - 100
     */
    setSamplingRate(sampleRatio: number): void,

    /**
     * Sets the currency for tracking.
     * @param {String} currencyCode The currency ISO 4217 code
     */
    setCurrency(currencyCode: string): void,

    /**
     * This function lets you create a session manually. By default, Google Analytics will group hits that are received
     * within 30 minutes of one another into the same session. So it is strictly not necessary to create a session manually.
     * @param {String} screenName The current screen which the session started on
     */
    createNewSession(screenName: string): void
  }

  /**
   * Google analytics settings shared across all GoogleAnalyticsTracker instances.
   */
  declare export class GoogleAnalyticsSettings {
    /**
     * Sets if OptOut is active and disables Google Analytics
     * This has to be set each time the App starts
     * @param {Boolean} enabled
     */
    static setOptOut(enabled: boolean): void,

    /**
     * Sets the trackers dispatch interval
     * This will influence how often batches of events, screen views, etc
     * are sent to your tracker.
     * @param {Number} intervalInSeconds
     */
    static setDispatchInterval(intervalInSeconds: number): void,

    /**
     * Sets if the tracker should have dry run enabled.
     * If dry run is enabled, no analytics data will be sent to your tracker.
     * @param {Boolean} enabled
     */
    static setDryRun(enabled: boolean): void
  }

  declare export type DataLayerEvent = {
    event: string,
    [key: string]: any
  };

  declare export class GoogleTagManager {
    /**
     * Call once to open the container for all subsequent static calls.
     * @param {String} containerId
     * @returns {Promise<boolean>}
     */
    static openContainerWithId(containerId: string): Promise<boolean>,

    /**
     * Retrieves a boolean value with the given key from the opened container.
     * @param {String} key
     * @returns {Promise<boolean>}
     */
    static boolForKey(key: string): Promise<boolean>,

    /**
     * Retrieves a string with the given key from the opened container.
     * @param {String} key
     * @returns {String}
     */
    static stringForKey(key: string): Promise<string>,

    /**
     * Retrieves a number with the given key from the opened container.
     * @param {String} key
     * @returns {Promise<number>}
     */
    static doubleForKey(key: string): Promise<number>,

    /**
     * Push a datalayer event for Google Analytics through Google Tag Manager. The event must have at least one key "event" with event name.
     * You can add optional values on top of that, example: {event: "eventName", pageId: "/home"}
     * @param {Object} event An Map<String, Object> containing key and value pairs. It must have at least one key "event" with event name
     * @returns {Promise<boolean>}
     */
    static pushDataLayerEvent(event: DataLayerEvent): Promise<boolean>
  }
}
<commit_msg>Remove ga-bridge flowtype<commit_after><|endoftext|>


<commit_before>document.addEventListener("DOMContentLoaded", function() {
  var PRIORITY_STATES = [ 'LA' ];

  var STATES = {
    "Alabama": "AL",
    "Alaska": "AK",
    "American Samoa": "AS",
    "Arizona": "AZ",
    "Arkansas": "AR",
    "California": "CA",
    "Colorado": "CO",
    "Connecticut": "CT",
    "Delaware": "DE",
    "District Of Columbia": "DC",
    "Federated States Of Micronesia": "FM",
    "Florida": "FL",
    "Georgia": "GA",
    "Guam": "GU",
    "Hawaii": "HI",
    "Idaho": "ID",
    "Illinois": "IL",
    "Indiana": "IN",
    "Iowa": "IA",
    "Kansas": "KS",
    "Kentucky": "KY",
    "Louisiana": "LA",
    "Maine": "ME",
    "Marshall Islands": "MH",
    "Maryland": "MD",
    "Massachusetts": "MA",
    "Michigan": "MI",
    "Minnesota": "MN",
    "Mississippi": "MS",
    "Missouri": "MO",
    "Montana": "MT",
    "Nebraska": "NE",
    "Nevada": "NV",
    "New Hampshire": "NH",
    "New Jersey": "NJ",
    "New Mexico": "NM",
    "New York": "NY",
    "North Carolina": "NC",
    "North Dakota": "ND",
    "Northern Mariana Islands": "MP",
    "Ohio": "OH",
    "Oklahoma": "OK",
    "Oregon": "OR",
    "Palau": "PW",
    "Pennsylvania": "PA",
    "Puerto Rico": "PR",
    "Rhode Island": "RI",
    "South Carolina": "SC",
    "South Dakota": "SD",
    "Tennessee": "TN",
    "Texas": "TX",
    "Utah": "UT",
    "Vermont": "VT",
    "Virgin Islands": "VI",
    "Virginia": "VA",
    "Washington": "WA",
    "West Virginia": "WV",
    "Wisconsin": "WI",
    "Wyoming": "WY"
  };

  var formatNumber = function(x) {
    return x ? x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
  };

  var pluralize = function(number, singular, plural){
    if (parseFloat(number) === 1.0) {
      return number + ' ' + singular;
    }
    else {
      if (!plural) {
        plural = singular + 's';
      }

      return formatNumber(number) + ' ' + plural;
    }
  };

  var shuffle = function(array) {
    for (var i = array.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  };

  // send event to Google Analytics
  function trackEvent(category, action, label, value) {
    if (window.ga) {
      var params = {
        hitType: 'event',
        eventCategory: category,
        eventAction: action
      };

      if (label) {
        params.eventLabel = label;
      }

      if (value) {
        params.eventValue = value;
      }

      window.ga('send', params);
    }
  }

  Vue.filter('formatNumber', formatNumber);
  Vue.filter('pluralize', pluralize);

  var app = new Vue({
    el: '#app',

    data: function() {
      return {
        states: STATES,
        selectedState: null,
        isLoaded: false,
        businesses: [],
        tweetCount: 0,
        name: null,
        email: null,
        zipCode: null,
        phone: null,
        hasLargeAudience: false,
        actionComment: null,
        isLoading: false,
        formMessage: null,
        modalVisible: false
      }
    },

    // watch: {
    //   tweetCount: function(val) {
    //     if (val === 4) {
    //       this.showModal()
    //     }
    //   }
    // },

    computed: {
      localBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return b.state === self.selectedState;
        }).slice(0, 20);
      },

      priorityBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return PRIORITY_STATES.indexOf(b.state) !== -1;
        }).slice(0, 20);
      },

      uniqueishBusinesses: function() {
        var uids = [];
        var businesses = [];

        for (var i = 0; i < this.businesses.length; i++) {
          var biz = this.businesses[i];
          var uid = biz.twitter + biz.state;

          if (uids.indexOf(uid) === -1) {
            uids.push(uid);
            businesses.push(biz);
          }
        }

        return businesses;
      }
    },

    created: function() {
      this.geocodeSelectedState();
      this.fetchBusinesses();
    },

    methods: {
      geocodeSelectedState: function() {
        var self = this;
        this.$http.get('https://fftf-geocoder.herokuapp.com').then(function(response){
          if (response.ok) {
            var geo = response.body;

            if (
              geo.country.iso_code === 'US' &&
              geo.subdivisions &&
              geo.subdivisions[0] &&
              geo.subdivisions[0].names &&
              geo.subdivisions[0].names.en
            ) {
              self.selectedState = geo.subdivisions[0].iso_code;
            }
          }
        });
      },

      fetchBusinesses: function() {
        var self = this;
        this.$http.get('https://data.battleforthenet.com/businesses.json').then(function(response){
          if (response.ok) {
            self.businesses = response.body;
            shuffle(self.businesses);
            self.isLoaded = true;
          }
        });
      },

      showModal: function() {
        this.modalVisible = true;
        document.querySelector('body').classList.add('modal-open');
      },

      hideModal: function() {
        this.modalVisible = false;
        document.querySelector('body').classList.remove('modal-open');
      },

      getMetaContent: function(name) {
        var el = document.querySelector('meta[name="' + name + '"]') || document.querySelector('meta[property="' + name + '"]');

        if (el) {
          return el.getAttribute('content');
        }

        return null;
      },

      openPopup: function(url, title='popup', w=600, h=500) {
        // Fixes dual-screen position
        var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
        var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;

        var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
        var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;

        var left = ((width / 2) - (w / 2)) + dualScreenLeft;
        var top = ((height / 2) - (h / 2)) + dualScreenTop;
        var newWindow = window.open(url, title, 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);

        // Puts focus on the newWindow
        if (window.focus) {
          newWindow.focus();
        }
      },

      shareOnFacebook: function() {
        var url = this.getMetaContent('og:url');
        this.openPopup('https://www.facebook.com/sharer.php?u=' + encodeURIComponent(url), 'facebook');
      },

      shareOnTwitter: function() {
        var tweetText = this.getMetaContent('twitter:description') + ' ' + this.getMetaContent('twitter:url');
        this.openPopup('https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText), 'twitter');
      },

      submitForm: function() {
        var self = this;
        self.isLoading = true;
        self.$http.post('https://queue.fightforthefuture.org/action', {
          member: {
            first_name: self.name,
            email: self.email,
            postcode: self.zipCode,
            phone_number: self.phone,
            country: 'US'
          },
          hp_enabled: 'true',
          guard: '',
          contact_congress: 0,
          org: 'fftf',
          an_tags: "[\"net-neutrality\"]",
          an_petition_id: '11f84b38-e65b-4259-b0ae-e879a4044ca9',
          volunteer: self.hasLargeAudience,
          action_comment: self.actionComment
        }, { emulateJSON: true })
        .then(function(response){
          self.isLoading = false;

          if (response.ok) {
            self.resetForm();
            self.showModal();
            trackEvent('signup', 'success', 'Step 4 - Sign Up');
          }
          else {
            self.formMessage = "That didn't work for some reason :(";
          }
        })
        .catch(function(error){
          self.isLoading = false;
          self.formMessage = "That didn't work for some reason :(";
        })
      },

      resetForm() {
        this.phone = null;
        this.name = null;
        this.email = null;
        this.zipCode = null;
        this.hasLargeAudience = false;
        this.actionComment = null;
        this.formMessage = null;
      },

      openSpreadsheet() {
        window.open('https://docs.google.com/spreadsheets/d/12MkAWz8VGyIIiCgVdGXWfLKROBegZNew8-xKyPVv6vU/edit#gid=0', '_blank');
        trackEvent('button', 'click', 'Step 5 - Get Started');
      }
    }
  });

  Vue.component('business-card', {
    template: '#business-card-template',

    props: {
      business: {
        type: Object
      },
      national: {
        type: Boolean,
        default: false
      }
    },

    computed: {
      tweetURL: function() {
        var tweetText = '';

        if (this.national) {
          tweetText = ".@" + this.business.twitter + " members of Congress REALLY listen to businesses. Can you sign on to this open letter supporting #NetNeutrality so lawmakers know how important this is to companies across the country?";
        }
        else {
          tweetText = ".@" + this.business.twitter + " your members of Congress aren't all supporting #NetNeutrality, and will soon face a CRA vote to overturn the FCC’s repeal.\n\nIt’s crucial that all businesses in " + this.$parent.selectedState + " take a stand. Can you sign on to this letter?";
        }

        return 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText) + '&url=' + encodeURIComponent('https://www.businessesfornetneutrality.com');
      },

      locationString: function() {
        if (this.business.city && this.business.state) {
          return [this.business.city, this.business.state].join(', ');
        }
        else if (this.business.city) {
          return this.business.city;
        }
        else if (this.business.state) {
          return this.business.state;
        }
      }
    },

    methods: {
      openTweetURL: function() {
        this.$parent.tweetCount += 1;
        window.open(this.tweetURL, '_blank');

        var stepNumber = this.national ? 3 : 1;
        trackEvent('business', 'tweet', 'Step ' + stepNumber + ' - ' + this.business.twitter);
      }
    }
  });
});<commit_msg>Update tweet text on business page<commit_after>document.addEventListener("DOMContentLoaded", function() {
  var PRIORITY_STATES = [ 'LA' ];

  var STATES = {
    "Alabama": "AL",
    "Alaska": "AK",
    "American Samoa": "AS",
    "Arizona": "AZ",
    "Arkansas": "AR",
    "California": "CA",
    "Colorado": "CO",
    "Connecticut": "CT",
    "Delaware": "DE",
    "District Of Columbia": "DC",
    "Federated States Of Micronesia": "FM",
    "Florida": "FL",
    "Georgia": "GA",
    "Guam": "GU",
    "Hawaii": "HI",
    "Idaho": "ID",
    "Illinois": "IL",
    "Indiana": "IN",
    "Iowa": "IA",
    "Kansas": "KS",
    "Kentucky": "KY",
    "Louisiana": "LA",
    "Maine": "ME",
    "Marshall Islands": "MH",
    "Maryland": "MD",
    "Massachusetts": "MA",
    "Michigan": "MI",
    "Minnesota": "MN",
    "Mississippi": "MS",
    "Missouri": "MO",
    "Montana": "MT",
    "Nebraska": "NE",
    "Nevada": "NV",
    "New Hampshire": "NH",
    "New Jersey": "NJ",
    "New Mexico": "NM",
    "New York": "NY",
    "North Carolina": "NC",
    "North Dakota": "ND",
    "Northern Mariana Islands": "MP",
    "Ohio": "OH",
    "Oklahoma": "OK",
    "Oregon": "OR",
    "Palau": "PW",
    "Pennsylvania": "PA",
    "Puerto Rico": "PR",
    "Rhode Island": "RI",
    "South Carolina": "SC",
    "South Dakota": "SD",
    "Tennessee": "TN",
    "Texas": "TX",
    "Utah": "UT",
    "Vermont": "VT",
    "Virgin Islands": "VI",
    "Virginia": "VA",
    "Washington": "WA",
    "West Virginia": "WV",
    "Wisconsin": "WI",
    "Wyoming": "WY"
  };

  var formatNumber = function(x) {
    return x ? x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
  };

  var pluralize = function(number, singular, plural){
    if (parseFloat(number) === 1.0) {
      return number + ' ' + singular;
    }
    else {
      if (!plural) {
        plural = singular + 's';
      }

      return formatNumber(number) + ' ' + plural;
    }
  };

  var shuffle = function(array) {
    for (var i = array.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  };

  // send event to Google Analytics
  function trackEvent(category, action, label, value) {
    if (window.ga) {
      var params = {
        hitType: 'event',
        eventCategory: category,
        eventAction: action
      };

      if (label) {
        params.eventLabel = label;
      }

      if (value) {
        params.eventValue = value;
      }

      window.ga('send', params);
    }
  }

  Vue.filter('formatNumber', formatNumber);
  Vue.filter('pluralize', pluralize);

  var app = new Vue({
    el: '#app',

    data: function() {
      return {
        states: STATES,
        selectedState: null,
        isLoaded: false,
        businesses: [],
        tweetCount: 0,
        name: null,
        email: null,
        zipCode: null,
        phone: null,
        hasLargeAudience: false,
        actionComment: null,
        isLoading: false,
        formMessage: null,
        modalVisible: false
      }
    },

    // watch: {
    //   tweetCount: function(val) {
    //     if (val === 4) {
    //       this.showModal()
    //     }
    //   }
    // },

    computed: {
      localBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return b.state === self.selectedState;
        }).slice(0, 20);
      },

      priorityBusinesses: function() {
        var self = this;
        return this.uniqueishBusinesses.filter(function(b){
          return PRIORITY_STATES.indexOf(b.state) !== -1;
        }).slice(0, 20);
      },

      uniqueishBusinesses: function() {
        var uids = [];
        var businesses = [];

        for (var i = 0; i < this.businesses.length; i++) {
          var biz = this.businesses[i];
          var uid = biz.twitter + biz.state;

          if (uids.indexOf(uid) === -1) {
            uids.push(uid);
            businesses.push(biz);
          }
        }

        return businesses;
      }
    },

    created: function() {
      this.geocodeSelectedState();
      this.fetchBusinesses();
    },

    methods: {
      geocodeSelectedState: function() {
        var self = this;
        this.$http.get('https://fftf-geocoder.herokuapp.com').then(function(response){
          if (response.ok) {
            var geo = response.body;

            if (
              geo.country.iso_code === 'US' &&
              geo.subdivisions &&
              geo.subdivisions[0] &&
              geo.subdivisions[0].names &&
              geo.subdivisions[0].names.en
            ) {
              self.selectedState = geo.subdivisions[0].iso_code;
            }
          }
        });
      },

      fetchBusinesses: function() {
        var self = this;
        this.$http.get('https://data.battleforthenet.com/businesses.json').then(function(response){
          if (response.ok) {
            self.businesses = response.body;
            shuffle(self.businesses);
            self.isLoaded = true;
          }
        });
      },

      showModal: function() {
        this.modalVisible = true;
        document.querySelector('body').classList.add('modal-open');
      },

      hideModal: function() {
        this.modalVisible = false;
        document.querySelector('body').classList.remove('modal-open');
      },

      getMetaContent: function(name) {
        var el = document.querySelector('meta[name="' + name + '"]') || document.querySelector('meta[property="' + name + '"]');

        if (el) {
          return el.getAttribute('content');
        }

        return null;
      },

      openPopup: function(url, title='popup', w=600, h=500) {
        // Fixes dual-screen position
        var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
        var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;

        var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
        var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;

        var left = ((width / 2) - (w / 2)) + dualScreenLeft;
        var top = ((height / 2) - (h / 2)) + dualScreenTop;
        var newWindow = window.open(url, title, 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left);

        // Puts focus on the newWindow
        if (window.focus) {
          newWindow.focus();
        }
      },

      shareOnFacebook: function() {
        var url = this.getMetaContent('og:url');
        this.openPopup('https://www.facebook.com/sharer.php?u=' + encodeURIComponent(url), 'facebook');
      },

      shareOnTwitter: function() {
        var tweetText = this.getMetaContent('twitter:description') + ' ' + this.getMetaContent('twitter:url');
        this.openPopup('https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText), 'twitter');
      },

      submitForm: function() {
        var self = this;
        self.isLoading = true;
        self.$http.post('https://queue.fightforthefuture.org/action', {
          member: {
            first_name: self.name,
            email: self.email,
            postcode: self.zipCode,
            phone_number: self.phone,
            country: 'US'
          },
          hp_enabled: 'true',
          guard: '',
          contact_congress: 0,
          org: 'fftf',
          an_tags: "[\"net-neutrality\"]",
          an_petition_id: '11f84b38-e65b-4259-b0ae-e879a4044ca9',
          volunteer: self.hasLargeAudience,
          action_comment: self.actionComment
        }, { emulateJSON: true })
        .then(function(response){
          self.isLoading = false;

          if (response.ok) {
            self.resetForm();
            self.showModal();
            trackEvent('signup', 'success', 'Step 4 - Sign Up');
          }
          else {
            self.formMessage = "That didn't work for some reason :(";
          }
        })
        .catch(function(error){
          self.isLoading = false;
          self.formMessage = "That didn't work for some reason :(";
        })
      },

      resetForm() {
        this.phone = null;
        this.name = null;
        this.email = null;
        this.zipCode = null;
        this.hasLargeAudience = false;
        this.actionComment = null;
        this.formMessage = null;
      },

      openSpreadsheet() {
        window.open('https://docs.google.com/spreadsheets/d/12MkAWz8VGyIIiCgVdGXWfLKROBegZNew8-xKyPVv6vU/edit#gid=0', '_blank');
        trackEvent('button', 'click', 'Step 5 - Get Started');
      }
    }
  });

  Vue.component('business-card', {
    template: '#business-card-template',

    props: {
      business: {
        type: Object
      },
      national: {
        type: Boolean,
        default: false
      }
    },

    computed: {
      tweetURL: function() {
        var tweetText = "Hi @" + this.business.twitter + ", I’m volunteering to save #NetNeutrality, especially because losing it will hurt businesses like you. Will you join other businesses in your area by signing this letter?";

        return 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(tweetText) + '&url=' + encodeURIComponent('https://www.businessesfornetneutrality.com');
      },

      locationString: function() {
        if (this.business.city && this.business.state) {
          return [this.business.city, this.business.state].join(', ');
        }
        else if (this.business.city) {
          return this.business.city;
        }
        else if (this.business.state) {
          return this.business.state;
        }
      }
    },

    methods: {
      openTweetURL: function() {
        this.$parent.tweetCount += 1;
        window.open(this.tweetURL, '_blank');

        var stepNumber = this.national ? 3 : 1;
        trackEvent('business', 'tweet', 'Step ' + stepNumber + ' - ' + this.business.twitter);
      }
    }
  });
});<|endoftext|>


<commit_before>define([
    "use!backbone",
    "use!handlebars",
    "confidentiality_encryption/Confidential",
    "datum/Datum",
    "text!datum/datum.handlebars",
    "datum_status/DatumStatus",
    "datum_status/DatumStatusView",
    "datum_menu/DatumMenu",
    "datum_menu/DatumMenuView",
    "datum_tag/DatumTag",
    "datum_tag/DatumTagView",
    "datum_field/DatumField",
    "datum_field/DatumFieldView",
    "libs/Utils"
], function(
    Backbone,
    Handlebars,
    Confidential,
    Datum,
    datumTemplate,
    DatumStatus,
    DatumStatusView,
    DatumMenu,
    DatumMenuView,
    DatumTag,
    DatumTagView,
    DatumField,
    DatumFieldView
) {
  var DatumView = Backbone.View.extend(
  /** @lends DatumView.prototype */
  {
    /**
     * @class The layout of a single Datum. It contains a datum status, datumFields,
     * datumTags and a datum menu.
     *
     * @extends Backbone.View
     * @constructs
     */
    initialize : function() {
      // Create a DatumStatusView
    	this.statusview = new DatumStatusView({model: this.model.get("status")});

    	// Create a DatumMenuView
      this.menuview = new DatumMenuView({model: this.model.get("datumMenu")});

      // Create a DatumTagView
      this.tagview = new DatumTagView({model: this.model.get("datumTag")});

      // Create a DatumFieldView
      this.fieldview = new DatumFieldView({model: this.model.get("datumField")});

      // If the model changes, re-render
      this.model.bind('change', this.render, this);
    },

    /**
     * The underlying model of the DatumView is a Datum.
     */
    model : Datum,

    /**
     * The statusview is a partial of the DatumView.
     */
    statusview: DatumStatusView,

    /**
     * The menuview is a partial of the DatumView.
     */
    menuview: DatumStatusView,

    /**
     * The tagview is a partial of the DatumView.
     */
    tagview: DatumStatusView,

    /**
     * The fieldview is a partial of the DatumView.
     */
    fieldview: DatumStatusView,

    /**
     * Events that the DatumView is listening to and their handlers.
     */
    events:{
    	"click #new" : "newDatum",
    	"click .icon-lock": "encryptDatum",
    	"click .icon-unlock": "decryptDatum",
    	"blur .utterance" : "updateUtterance",
    	"blur .morphemes" : "updateMorphemes",
    	"blur .gloss" : "updateGloss",
    	"blur .translation" : "updateTranslation",
    	"change" : "updatePouch"
    },

    /**
     * The Handlebars template rendered as the DatumView.
     */
    template: Handlebars.compile(datumTemplate),

    /**
     * Renders the DatumView and all of its partials.
     */
    render : function() {
      Utils.debug("DATUM render: " + this.el);
      if (this.model != undefined) {
        // Register all the partials
      	Handlebars.registerPartial("datum_status", this.statusview.template(this.statusview.model.toJSON()) );
      	Handlebars.registerPartial("datum_menu", this.menuview.template(this.model.toJSON()) );
      	Handlebars.registerPartial("datum_tag", this.tagview.template(this.tagview.model.toJSON()) );
      	Handlebars.registerPartial("datum_field", this.fieldview.template(this.model.toJSON()) );

        // Display the DatumView
        this.setElement($("#datum-view"));
        $(this.el).html(this.template(this.model.toJSON()));

      } else {
        Utils.debug("\tDatum model was undefined");
      }

      return this;
    },

    needsSave : false,

    /**
     * Change the model's utterance.
     */
    updateUtterance : function() {
      this.model.set("utterance", $(".utterance").val());
    },

    /**
     * Change the model's morpheme.
     */
    updateMorphemes : function() {
      this.model.set("morphemes", $(".morphemes").val());
    },

    /**
     * Chnage the model's gloss.
     */
    updateGloss : function() {
      this.model.set("gloss", $(".gloss").val());
    },

    /**
     * Change the model's translation.
     */
    updateTranslation : function() {
      this.model.set("translation", $(".translation").val());
    },

    /**
     * Registers this datum to be saved in PouchDB.
     */
    updatePouch : function() {
      this.needsSave = true;
    },
    /**
     * Encrypts the datum if it is confidential
     *
     * @returns {Boolean}
     */
    encryptDatum: function(){
//      console.log("Fake encrypting");
      var confidential = appView.corpusView.model.confidential ;

      if(confidential == undefined){
        appView.corpusView.model.confidential = new Confidential();
        confidential = appView.corpusView.model.confidential;
      }

      this.model.set("utterance", confidential.encrypt(this.model.get("utterance")));
      this.model.set("morphemes", confidential.encrypt(this.model.get("morphemes")));
      this.model.set("gloss", confidential.encrypt(this.model.get("gloss")));
      this.model.set("translation", confidential.encrypt(this.model.get("translation")));

//      this.model.set("utterance", this.model.get("utterance").replace(/[^ -.]/g,"x"));
//      this.model.set("morphemes", this.model.get("morphemes").replace(/[^ -.]/g,"x"));
//      this.model.set("gloss", this.model.get("gloss").replace(/[^ -.]/g,"x"));
//      this.model.set("translation", this.model.get("translation").replace(/[^ -.]/g,"x"));
      this.render();
      $(".icon-lock").toggleClass("icon-lock icon-unlock");

//      console.log(confidential);
//      this.model.set()
    },
    decryptDatum: function(){
      var confidential = appView.corpusView.model.confidential ;
      this.model.set("utterance", confidential.decrypt(this.model.get("utterance")));
      this.model.set("morphemes", confidential.decrypt(this.model.get("morphemes")));
      this.model.set("gloss", confidential.decrypt(this.model.get("gloss")));
      this.model.set("translation", confidential.decrypt(this.model.get("translation")));
      this.render();
      $(".icon-lock").toggleClass("icon-lock icon-unlock");

    },
    newDatum: function() {
    	/* First, we build a new datum model,
    	 * this datum model then asks if it belongs to a session
    	 * if it belongs to a session it goes ahead and renders a new datum
    	 * if it does not belong to a session, it builds a new session and renders a new session view.
    	 * after the new session is sent to pouch, then a new datumview can be rendered.
         */

//        	var newDatum = new DatumView({model: new Datum()});
//         	$("#fullscreen-datum-view").append(newDatum.render().el);
//         	var sID = this.newDatum.get("sessionID");
//         	console.log(sID);
//
//         	if(newDatum.sessionID == 0){
//         		var newSession = new SessionView({model: new Session()});
//             	$("#new-session-view").append(newSession.render().el);
//
//         	}

      Utils.debug("I'm a new datum!");
      return true;
    },

    /**
     * If the model needs to be saved, saves it.
     */
    saveScreen : function() {
      if (this.needsSave) {
        // Change the needsSave flag before saving just in case another change happens
        // before the saving is done
        this.needsSave = false;

        Utils.debug("Saving the Datum");
        this.model.save(null, {
          success : function(model, response) {
            if (location.hash.indexOf("/new") != -1) {
              location.hash = location.hash.replace("/new", "/" + model.id);
            }
          }
        });
      }
    }

  });

  return DatumView;
}); <commit_msg>changing the datumview to have classes in the member items rather than null<commit_after>define([
    "use!backbone",
    "use!handlebars",
    "confidentiality_encryption/Confidential",
    "datum/Datum",
    "text!datum/datum.handlebars",
    "datum_status/DatumStatus",
    "datum_status/DatumStatusView",
    "datum_menu/DatumMenu",
    "datum_menu/DatumMenuView",
    "datum_tag/DatumTag",
    "datum_tag/DatumTagView",
    "datum_field/DatumField",
    "datum_field/DatumFieldView",
    "libs/Utils"
], function(
    Backbone,
    Handlebars,
    Confidential,
    Datum,
    datumTemplate,
    DatumStatus,
    DatumStatusView,
    DatumMenu,
    DatumMenuView,
    DatumTag,
    DatumTagView,
    DatumField,
    DatumFieldView
) {
  var DatumView = Backbone.View.extend(
  /** @lends DatumView.prototype */
  {
    /**
     * @class The layout of a single Datum. It contains a datum status, datumFields,
     * datumTags and a datum menu.
     *
     * @extends Backbone.View
     * @constructs
     */
    initialize : function() {
      // Create a DatumStatusView
    	this.statusview = new DatumStatusView({model: this.model.get("status")});

    	// Create a DatumMenuView
      this.menuview = new DatumMenuView({model: this.model.get("datumMenu")});

      // Create a DatumTagView
      this.tagview = new DatumTagView({model: this.model.get("datumTag")});

      // Create a DatumFieldView
      this.fieldview = new DatumFieldView({model: this.model.get("datumField")});

      // If the model changes, re-render
      this.model.bind('change', this.render, this);
    },

    /**
     * The underlying model of the DatumView is a Datum.
     */
    model : Datum,

    /**
     * The statusview is a partial of the DatumView.
     */
    statusview: DatumStatusView,

    /**
     * The menuview is a partial of the DatumView.
     */
    menuview: DatumMenuView,

    /**
     * The tagview is a partial of the DatumView.
     */
    tagview: DatumTagView,

    /**
     * The fieldview is a partial of the DatumView.
     */
    fieldview: DatumFieldView,

    /**
     * Events that the DatumView is listening to and their handlers.
     */
    events:{
    	"click #new" : "newDatum",
    	"click .icon-lock": "encryptDatum",
    	"click .icon-unlock": "decryptDatum",
    	"blur .utterance" : "updateUtterance",
    	"blur .morphemes" : "updateMorphemes",
    	"blur .gloss" : "updateGloss",
    	"blur .translation" : "updateTranslation",
    	"change" : "updatePouch"
    },

    /**
     * The Handlebars template rendered as the DatumView.
     */
    template: Handlebars.compile(datumTemplate),

    /**
     * Renders the DatumView and all of its partials.
     */
    render : function() {
      Utils.debug("DATUM render: " + this.el);
      if (this.model != undefined) {
        // Register all the partials
      	Handlebars.registerPartial("datum_status", this.statusview.template(this.statusview.model.toJSON()) );
      	Handlebars.registerPartial("datum_menu", this.menuview.template(this.model.toJSON()) );
      	Handlebars.registerPartial("datum_tag", this.tagview.template(this.tagview.model.toJSON()) );
      	Handlebars.registerPartial("datum_field", this.fieldview.template(this.model.toJSON()) );

        // Display the DatumView
        this.setElement($("#datum-view"));
        $(this.el).html(this.template(this.model.toJSON()));

      } else {
        Utils.debug("\tDatum model was undefined");
      }

      return this;
    },

    needsSave : false,

    /**
     * Change the model's utterance.
     */
    updateUtterance : function() {
      this.model.set("utterance", $(".utterance").val());
    },

    /**
     * Change the model's morpheme.
     */
    updateMorphemes : function() {
      this.model.set("morphemes", $(".morphemes").val());
    },

    /**
     * Chnage the model's gloss.
     */
    updateGloss : function() {
      this.model.set("gloss", $(".gloss").val());
    },

    /**
     * Change the model's translation.
     */
    updateTranslation : function() {
      this.model.set("translation", $(".translation").val());
    },

    /**
     * Registers this datum to be saved in PouchDB.
     */
    updatePouch : function() {
      this.needsSave = true;
    },
    /**
     * Encrypts the datum if it is confidential
     *
     * @returns {Boolean}
     */
    encryptDatum: function(){
//      console.log("Fake encrypting");
      var confidential = appView.corpusView.model.confidential ;

      if(confidential == undefined){
        appView.corpusView.model.confidential = new Confidential();
        confidential = appView.corpusView.model.confidential;
      }

      this.model.set("utterance", confidential.encrypt(this.model.get("utterance")));
      this.model.set("morphemes", confidential.encrypt(this.model.get("morphemes")));
      this.model.set("gloss", confidential.encrypt(this.model.get("gloss")));
      this.model.set("translation", confidential.encrypt(this.model.get("translation")));

//      this.model.set("utterance", this.model.get("utterance").replace(/[^ -.]/g,"x"));
//      this.model.set("morphemes", this.model.get("morphemes").replace(/[^ -.]/g,"x"));
//      this.model.set("gloss", this.model.get("gloss").replace(/[^ -.]/g,"x"));
//      this.model.set("translation", this.model.get("translation").replace(/[^ -.]/g,"x"));
      this.render();
      $(".icon-lock").toggleClass("icon-lock icon-unlock");

//      console.log(confidential);
//      this.model.set()
    },
    decryptDatum: function(){
      var confidential = appView.corpusView.model.confidential ;
      this.model.set("utterance", confidential.decrypt(this.model.get("utterance")));
      this.model.set("morphemes", confidential.decrypt(this.model.get("morphemes")));
      this.model.set("gloss", confidential.decrypt(this.model.get("gloss")));
      this.model.set("translation", confidential.decrypt(this.model.get("translation")));
      this.render();
      $(".icon-lock").toggleClass("icon-lock icon-unlock");

    },
    newDatum: function() {
    	/* First, we build a new datum model,
    	 * this datum model then asks if it belongs to a session
    	 * if it belongs to a session it goes ahead and renders a new datum
    	 * if it does not belong to a session, it builds a new session and renders a new session view.
    	 * after the new session is sent to pouch, then a new datumview can be rendered.
         */

//        	var newDatum = new DatumView({model: new Datum()});
//         	$("#fullscreen-datum-view").append(newDatum.render().el);
//         	var sID = this.newDatum.get("sessionID");
//         	console.log(sID);
//
//         	if(newDatum.sessionID == 0){
//         		var newSession = new SessionView({model: new Session()});
//             	$("#new-session-view").append(newSession.render().el);
//
//         	}

      Utils.debug("I'm a new datum!");
      return true;
    },

    /**
     * If the model needs to be saved, saves it.
     */
    saveScreen : function() {
      if (this.needsSave) {
        // Change the needsSave flag before saving just in case another change happens
        // before the saving is done
        this.needsSave = false;

        Utils.debug("Saving the Datum");
        this.model.save(null, {
          success : function(model, response) {
            if (location.hash.indexOf("/new") != -1) {
              location.hash = location.hash.replace("/new", "/" + model.id);
            }
          }
        });
      }
    }

  });

  return DatumView;
}); <|endoftext|>


<commit_before>// pump.js
//
// Entrypoint for the pump.io client UI
//
// Copyright 2011-2012, E14N https://e14n.com/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Make sure this exists

if (!window.Pump) {
    window.Pump = {};
}

(function(_, $, Backbone, Pump) {

    // This is overwritten by inline script in layout.utml

    Pump.config = {};

    // Main entry point

    $(document).ready(function() {

        // XXX: set up initial models

        // Set up router

        Pump.router   = new Pump.Router();

        // Set up initial view

        Pump.body     = new Pump.BodyView({el: $("body")});
        Pump.body.nav = new Pump.AnonymousNav({el: ".navbar-inner .container"});

        // XXX: Make this more complete

        Pump.initialContentView();

        $("abbr.easydate").easydate();

        Backbone.history.start({pushState: true, silent: true});

        Pump.setupWysiHTML5();

        // Refresh the streams automatically every 60 seconds
        // This is a fallback in case something gets lost in the
        // SockJS conversation

        Pump.refreshStreamsID = setInterval(Pump.refreshStreams, 60000);

        // Connect to current server

        if (Pump.config.sockjs) {
            Pump.setupSocket();
        }

        Pump.setupInfiniteScroll();

        if (Pump.principalUser) {
            Pump.principalUser = Pump.User.unique(Pump.principalUser);
            Pump.principal = Pump.Person.unique(Pump.principal);
            Pump.body.nav = new Pump.UserNav({el: Pump.body.$(".navbar-inner .container"),
                                              model: Pump.principalUser,
                                              data: {
                                                  messages: Pump.principalUser.majorDirectInbox,
                                                  notifications: Pump.principalUser.minorDirectInbox
                                              }});
        } else if (Pump.principal) {
            Pump.principal = Pump.Person.unique(Pump.principal);
            Pump.body.nav = new Pump.RemoteNav({el: Pump.body.$(".navbar-inner .container"),
                                                model: Pump.principal});
        } else {
            // Check if we have stored OAuth credentials

            Pump.ensureCred(function(err, cred) {

                var nickname, pair;

                if (err) {
                    Pump.error(err.message);
                    return;
                }

                pair = Pump.getUserCred();

                if (pair) {

                    // We need to renew the session, for images and objects and so on.

                    Pump.renewSession(function(err, data) {

                        var user, major, minor;

                        if (err) {
                            Pump.error(err);
                            Pump.clearUserCred();
                            return;
                        }

                        user = Pump.principalUser = Pump.User.unique(data);
                        Pump.principal = Pump.principalUser.profile;

                        major = user.majorDirectInbox;
                        minor = user.minorDirectInbox;

                        Pump.fetchObjects([major, minor], function(err, objs) {
                            var sp, continueTo;

                            if (err) {
                                Pump.clearUserCred();
                                Pump.error(err);
                                return;
                            }

                            Pump.principalUser = user;

                            Pump.body.nav = new Pump.UserNav({el: ".navbar-inner .container",
                                                              model: user,
                                                              data: {
                                                                  messages: major,
                                                                  notifications: minor
                                                              }});
                            Pump.body.nav.render();

                            // If we're on the login page, and there's a current
                            // user, redirect to the actual page

                            switch (window.location.pathname) {
                            case "/main/login":
                                Pump.body.content = new Pump.LoginContent();
                                continueTo = Pump.getContinueTo();
                                Pump.router.navigate(continueTo, true);
                                break;
                            case "/":
                                Pump.router.home();
                                break;
                            }
                        });
                    });
                }
            });
        }
    });

    // Renew the cookie session

    Pump.renewSession = function(callback) {

        var options = {
            dataType: "json",
            type: "POST",
            url: "/main/renew",
            success: function(data, textStatus, jqXHR) {
                callback(null, data);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                callback(new Error("Failed to renew"), null);
            }
        };

        Pump.ajax(options);
    };

    // When errors happen, and you don't know what to do with them,
    // send them here and I'll figure it out.

    Pump.error = function(err) {
        console.log(err);
        if (err.stack) {
            console.log(err.stack);
        }
    };

    // Given a relative URL like /main/register, make a fully-qualified
    // URL on the current server

    Pump.fullURL = function(url) {

        var here = window.location;

        if (url.indexOf(':') == -1) {
            if (url.substr(0, 1) == '/') {
                url = here.protocol + '//' + here.host + url;
            } else {
                url = here.href.substr(0, here.href.lastIndexOf('/') + 1) + url;
            }
        }

        return url;
    };

    // Add some OAuth magic to the arguments for a $.ajax() call

    Pump.oauthify = function(options) {

        options.url = Pump.fullURL(options.url);

        var message = {action: options.url,
                       method: options.type,
                       parameters: [["oauth_version", "1.0"],
                                    ["oauth_consumer_key", options.consumerKey]]};

        if (options.token) {
            message.parameters.push(["oauth_token", options.token]);
        }

        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message,
                                   {consumerSecret: options.consumerSecret,
                                    tokenSecret: options.tokenSecret});

        var header =  OAuth.getAuthorizationHeader("OAuth", message.parameters);

        options.headers = {Authorization: header};

        return options;
    };

    Pump.fetchObjects = function(orig, callback) {
        var fetched = 0,
            objs = (orig.length) > 0 ? orig.slice(0) : [], // make a dupe in case arg is changed
            count = objs.length,
            done = false,
            onSuccess = function() {
                if (!done) {
                    fetched++;
                    if (fetched >= count) {
                        done = true;
                        callback(null, objs);
                    }
                }
            },
            onError = function(xhr, status, thrown) {
                if (!done) {
                    done = true;
                    if (thrown) {
                        callback(thrown, null);
                    } else {
                        callback(new Error(status), null);
                    }
                }
            };

        _.each(objs, function(obj) {
            try {
                if (_.isFunction(obj.prevLink) && obj.prevLink()) {
                    obj.getPrev(function(err) {
                        if (err) {
                            onError(null, null, err);
                        } else {
                            onSuccess();
                        }
                    });
                } else {
                    obj.fetch({update: true,
                               success: onSuccess,
                               error: onError});
                }
            } catch (e) {
                onError(null, null, e);
            }
        });
    };

    // Not the most lovely, but it works
    // XXX: change this to use UTML templating instead

    Pump.wysihtml5Tmpl = {
        "emphasis": function(locale) {
            return "
" +
                "
" +
                "" +
                "" +
                "_" +
                "
" +
                "
";
        }
    };

    // Most long-form descriptions and notes use this lib for editing

    Pump.setupWysiHTML5 = function() {

        // Set wysiwyg defaults

        $.fn.wysihtml5.defaultOptions["font-styles"] = false;
        $.fn.wysihtml5.defaultOptions["image"] = false;
        $.fn.wysihtml5.defaultOptions["customTemplates"] = Pump.wysihtml5Tmpl;
    };

    // Turn the querystring into an object

    Pump.searchParams = function(str) {
        var params = {},
            pl     = /\+/g,
            decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
            pairs;

        if (!str) {
            str = window.location.search;
        }

        pairs = str.substr(1).split("&");

        _.each(pairs, function(pairStr) {
            var pair = pairStr.split("=", 2),
                key = decode(pair[0]),
                value = (pair.length > 1) ? decode(pair[1]) : null;

            params[key] = value;
        });

        return params;
    };

    Pump.continueTo = null;

    // Get the "continue" param

    Pump.getContinueTo = function() {
        var sp = Pump.searchParams(),
            continueTo = (_.has(sp, "continue")) ? sp["continue"] : null;

        if (continueTo && continueTo.length > 0 && continueTo[0] == "/") {
            return continueTo;
        } else if (Pump.continueTo) {
            continueTo = Pump.continueTo;
            return continueTo;
        } else {
            return "";
        }
    };

    Pump.saveContinueTo = function() {
        Pump.continueTo = window.location.pathname + window.location.search;
    };

    Pump.clearContinueTo = function() {
        Pump.continueTo = null;
    };

    // We clear out cached stuff when login state changes

    Pump.clearCaches = function() {
        Pump.Model.clearCache();
        Pump.User.clearCache();
    };

    Pump.ajax = function(options) {
        // For remote users, we use session auth
        if (Pump.principal && !Pump.principalUser && options.type == "GET") {
            $.ajax(options);
        } else {
            Pump.ensureCred(function(err, cred) {
                var pair;
                if (err) {
                    Pump.error("Couldn't get OAuth credentials. :(");
                } else {
                    options.consumerKey = cred.clientID;
                    options.consumerSecret = cred.clientSecret;
                    pair = Pump.getUserCred();

                    if (pair) {
                        options.token = pair.token;
                        options.tokenSecret = pair.secret;
                    }

                    options = Pump.oauthify(options);
                    $.ajax(options);
                }
            });
        }
    };

    Pump.setupInfiniteScroll = function() {

        var didScroll = false;

        // scroll fires too fast, so just use the handler
        // to set a flag, and check that flag with an interval

        // From http://ejohn.org/blog/learning-from-twitter/

        $(window).scroll(function() {
            didScroll = true;
        });

        setInterval(function() {
            var streams;
            if (didScroll) {
                didScroll = false;
                if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
                    streams = Pump.getStreams();
                    if (streams.major && streams.major.nextLink) {
                        Pump.body.startLoad();
                        streams.major.getNext(function() {
                            Pump.body.endLoad();
                        });
                    }
                }
            }
        }, 250);
    };

    // XXX: this is cheeseball.

    Pump.rel = function(url) {

        var a = document.createElement('a'),
            pathname;

        a.href = url;
        pathname = a.pathname;

        return pathname;
    };

    Pump.htmlEncode = function(value) {
        return $('
').text(value).html();
    };

    Pump.htmlDecode = function(value) {
        return $('
').html(value).text();
    };

    // Sets up the initial view and sub-views

    Pump.initialContentView = function() {

        var $content = $("#content"),
            selectorToView = {
                "#main": {View: Pump.MainContent},
                "#loginpage": {View: Pump.LoginContent},
                "#register": {View: Pump.RegisterContent},
                "#inbox": {View: Pump.InboxContent, models: {major: Pump.ActivityStream, minor: Pump.ActivityStream}},
                ".object-page": {View: Pump.ObjectContent, models: {object: Pump.ActivityObject}},
                ".major-activity-page": {View: Pump.ActivityContent, models: {activity: Pump.Activity}},
                ".user-activities": {View: Pump.UserPageContent, models: {profile: Pump.Person,
                                                                          major: Pump.ActivityStream,
                                                                          minor: Pump.ActivityStream}},
                ".user-favorites": {View: Pump.FavoritesContent, models: {profile: Pump.Person,
                                                                          favorites: Pump.ActivityObjectStream}},
                ".user-followers": {View: Pump.FollowersContent, models: {profile: Pump.Person,
                                                                          followers: Pump.PeopleStream}},
                ".user-following": {View: Pump.FollowingContent, models: {profile: Pump.Person,
                                                                          following: Pump.PeopleStream}},
                ".user-lists": {View: Pump.ListsContent, models: {profile: Pump.Person,
                                                                  lists: Pump.ActivityObjectStream}},
                ".user-list": {View: Pump.ListContent, models: {profile: Pump.Person,
                                                                lists: Pump.ActivityObjectStream,
                                                                list: Pump.ActivityObject}}
            },
            selector,
            $el,
            model,
            options,
            def,
            data,
            View;

        // When I say "view" the crowd say "selector"

        for (selector in selectorToView) {
            if (_.has(selectorToView, selector)) {
                $el = $content.find(selector);
                if ($el.length > 0) {
                    def = selectorToView[selector];
                    View = def.View;
                    options = {el: $el, data: {}};
                    data = Pump.initialData;
                    _.each(data, function(value, name) {
                        if (name == View.prototype.modelName) {
                            options.model = def.models[name].unique(value);
                        } else if (def.models[name]) {
                            options.data[name] = def.models[name].unique(value);
                        } else {
                            options.data[name] = value;
                        }
                    });
                    Pump.body.content = new View(options);
                    Pump.initialData = null;
                    break;
                }
            }
        }

        // XXX: set up initial data
    };

    Pump.newMinorActivity = function(act, callback) {
        if (Pump.principalUser) {
            Pump.addToStream(Pump.principalUser.minorStream, act, callback);
        } else {
            Pump.proxyActivity(act, callback);
        }
    };

    Pump.newMajorActivity = function(act, callback) {
        if (Pump.principalUser) {
            Pump.addToStream(Pump.principalUser.majorStream, act, callback);
        } else {
            Pump.proxyActivity(act, callback);
        }
    };

    Pump.addToStream = function(stream, act, callback) {
        stream.items.create(act, {
            success: function(act) {
                callback(null, act);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var type, response;
                type = jqXHR.getResponseHeader("Content-Type");
                if (type && type.indexOf("application/json") !== -1) {
                    response = JSON.parse(jqXHR.responseText);
                    callback(new Error(response.error), null);
                } else {
                    callback(new Error(errorThrown), null);
                }
            }
        });
    };

    // XXX: This POSTs with session auth; subject to XSS.

    Pump.proxyActivity = function(act, callback) {
        $.ajax({
            contentType: "application/json",
            data: JSON.stringify(act),
            dataType: "json",
            type: "POST",
            url: "/main/proxy",
            success: function(act) {
                callback(null, act);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var type, response;
                type = jqXHR.getResponseHeader("Content-Type");
                if (type && type.indexOf("application/json") !== -1) {
                    response = JSON.parse(jqXHR.responseText);
                    callback(new Error(response.error), null);
                } else {
                    callback(new Error(errorThrown), null);
                }
            }
        });
    };

    Pump.setTitle = function(title) {
        $("title").html(title + " - " + Pump.config.site);
    };

})(window._, window.$, window.Backbone, window.Pump);
<commit_msg>Remove XXX for initial models<commit_after>// pump.js
//
// Entrypoint for the pump.io client UI
//
// Copyright 2011-2012, E14N https://e14n.com/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Make sure this exists

if (!window.Pump) {
    window.Pump = {};
}

(function(_, $, Backbone, Pump) {

    // This is overwritten by inline script in layout.utml

    Pump.config = {};

    // Main entry point

    $(document).ready(function() {

        // Set up router

        Pump.router   = new Pump.Router();

        // Set up initial view

        Pump.body     = new Pump.BodyView({el: $("body")});
        Pump.body.nav = new Pump.AnonymousNav({el: ".navbar-inner .container"});

        // XXX: Make this more complete

        Pump.initialContentView();

        $("abbr.easydate").easydate();

        Backbone.history.start({pushState: true, silent: true});

        Pump.setupWysiHTML5();

        // Refresh the streams automatically every 60 seconds
        // This is a fallback in case something gets lost in the
        // SockJS conversation

        Pump.refreshStreamsID = setInterval(Pump.refreshStreams, 60000);

        // Connect to current server

        if (Pump.config.sockjs) {
            Pump.setupSocket();
        }

        Pump.setupInfiniteScroll();

        if (Pump.principalUser) {
            Pump.principalUser = Pump.User.unique(Pump.principalUser);
            Pump.principal = Pump.Person.unique(Pump.principal);
            Pump.body.nav = new Pump.UserNav({el: Pump.body.$(".navbar-inner .container"),
                                              model: Pump.principalUser,
                                              data: {
                                                  messages: Pump.principalUser.majorDirectInbox,
                                                  notifications: Pump.principalUser.minorDirectInbox
                                              }});
        } else if (Pump.principal) {
            Pump.principal = Pump.Person.unique(Pump.principal);
            Pump.body.nav = new Pump.RemoteNav({el: Pump.body.$(".navbar-inner .container"),
                                                model: Pump.principal});
        } else {
            // Check if we have stored OAuth credentials

            Pump.ensureCred(function(err, cred) {

                var nickname, pair;

                if (err) {
                    Pump.error(err.message);
                    return;
                }

                pair = Pump.getUserCred();

                if (pair) {

                    // We need to renew the session, for images and objects and so on.

                    Pump.renewSession(function(err, data) {

                        var user, major, minor;

                        if (err) {
                            Pump.error(err);
                            Pump.clearUserCred();
                            return;
                        }

                        user = Pump.principalUser = Pump.User.unique(data);
                        Pump.principal = Pump.principalUser.profile;

                        major = user.majorDirectInbox;
                        minor = user.minorDirectInbox;

                        Pump.fetchObjects([major, minor], function(err, objs) {
                            var sp, continueTo;

                            if (err) {
                                Pump.clearUserCred();
                                Pump.error(err);
                                return;
                            }

                            Pump.principalUser = user;

                            Pump.body.nav = new Pump.UserNav({el: ".navbar-inner .container",
                                                              model: user,
                                                              data: {
                                                                  messages: major,
                                                                  notifications: minor
                                                              }});
                            Pump.body.nav.render();

                            // If we're on the login page, and there's a current
                            // user, redirect to the actual page

                            switch (window.location.pathname) {
                            case "/main/login":
                                Pump.body.content = new Pump.LoginContent();
                                continueTo = Pump.getContinueTo();
                                Pump.router.navigate(continueTo, true);
                                break;
                            case "/":
                                Pump.router.home();
                                break;
                            }
                        });
                    });
                }
            });
        }
    });

    // Renew the cookie session

    Pump.renewSession = function(callback) {

        var options = {
            dataType: "json",
            type: "POST",
            url: "/main/renew",
            success: function(data, textStatus, jqXHR) {
                callback(null, data);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                callback(new Error("Failed to renew"), null);
            }
        };

        Pump.ajax(options);
    };

    // When errors happen, and you don't know what to do with them,
    // send them here and I'll figure it out.

    Pump.error = function(err) {
        console.log(err);
        if (err.stack) {
            console.log(err.stack);
        }
    };

    // Given a relative URL like /main/register, make a fully-qualified
    // URL on the current server

    Pump.fullURL = function(url) {

        var here = window.location;

        if (url.indexOf(':') == -1) {
            if (url.substr(0, 1) == '/') {
                url = here.protocol + '//' + here.host + url;
            } else {
                url = here.href.substr(0, here.href.lastIndexOf('/') + 1) + url;
            }
        }

        return url;
    };

    // Add some OAuth magic to the arguments for a $.ajax() call

    Pump.oauthify = function(options) {

        options.url = Pump.fullURL(options.url);

        var message = {action: options.url,
                       method: options.type,
                       parameters: [["oauth_version", "1.0"],
                                    ["oauth_consumer_key", options.consumerKey]]};

        if (options.token) {
            message.parameters.push(["oauth_token", options.token]);
        }

        OAuth.setTimestampAndNonce(message);
        OAuth.SignatureMethod.sign(message,
                                   {consumerSecret: options.consumerSecret,
                                    tokenSecret: options.tokenSecret});

        var header =  OAuth.getAuthorizationHeader("OAuth", message.parameters);

        options.headers = {Authorization: header};

        return options;
    };

    Pump.fetchObjects = function(orig, callback) {
        var fetched = 0,
            objs = (orig.length) > 0 ? orig.slice(0) : [], // make a dupe in case arg is changed
            count = objs.length,
            done = false,
            onSuccess = function() {
                if (!done) {
                    fetched++;
                    if (fetched >= count) {
                        done = true;
                        callback(null, objs);
                    }
                }
            },
            onError = function(xhr, status, thrown) {
                if (!done) {
                    done = true;
                    if (thrown) {
                        callback(thrown, null);
                    } else {
                        callback(new Error(status), null);
                    }
                }
            };

        _.each(objs, function(obj) {
            try {
                if (_.isFunction(obj.prevLink) && obj.prevLink()) {
                    obj.getPrev(function(err) {
                        if (err) {
                            onError(null, null, err);
                        } else {
                            onSuccess();
                        }
                    });
                } else {
                    obj.fetch({update: true,
                               success: onSuccess,
                               error: onError});
                }
            } catch (e) {
                onError(null, null, e);
            }
        });
    };

    // Not the most lovely, but it works
    // XXX: change this to use UTML templating instead

    Pump.wysihtml5Tmpl = {
        "emphasis": function(locale) {
            return "
" +
                "
" +
                "" +
                "" +
                "_" +
                "
" +
                "
";
        }
    };

    // Most long-form descriptions and notes use this lib for editing

    Pump.setupWysiHTML5 = function() {

        // Set wysiwyg defaults

        $.fn.wysihtml5.defaultOptions["font-styles"] = false;
        $.fn.wysihtml5.defaultOptions["image"] = false;
        $.fn.wysihtml5.defaultOptions["customTemplates"] = Pump.wysihtml5Tmpl;
    };

    // Turn the querystring into an object

    Pump.searchParams = function(str) {
        var params = {},
            pl     = /\+/g,
            decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
            pairs;

        if (!str) {
            str = window.location.search;
        }

        pairs = str.substr(1).split("&");

        _.each(pairs, function(pairStr) {
            var pair = pairStr.split("=", 2),
                key = decode(pair[0]),
                value = (pair.length > 1) ? decode(pair[1]) : null;

            params[key] = value;
        });

        return params;
    };

    Pump.continueTo = null;

    // Get the "continue" param

    Pump.getContinueTo = function() {
        var sp = Pump.searchParams(),
            continueTo = (_.has(sp, "continue")) ? sp["continue"] : null;

        if (continueTo && continueTo.length > 0 && continueTo[0] == "/") {
            return continueTo;
        } else if (Pump.continueTo) {
            continueTo = Pump.continueTo;
            return continueTo;
        } else {
            return "";
        }
    };

    Pump.saveContinueTo = function() {
        Pump.continueTo = window.location.pathname + window.location.search;
    };

    Pump.clearContinueTo = function() {
        Pump.continueTo = null;
    };

    // We clear out cached stuff when login state changes

    Pump.clearCaches = function() {
        Pump.Model.clearCache();
        Pump.User.clearCache();
    };

    Pump.ajax = function(options) {
        // For remote users, we use session auth
        if (Pump.principal && !Pump.principalUser && options.type == "GET") {
            $.ajax(options);
        } else {
            Pump.ensureCred(function(err, cred) {
                var pair;
                if (err) {
                    Pump.error("Couldn't get OAuth credentials. :(");
                } else {
                    options.consumerKey = cred.clientID;
                    options.consumerSecret = cred.clientSecret;
                    pair = Pump.getUserCred();

                    if (pair) {
                        options.token = pair.token;
                        options.tokenSecret = pair.secret;
                    }

                    options = Pump.oauthify(options);
                    $.ajax(options);
                }
            });
        }
    };

    Pump.setupInfiniteScroll = function() {

        var didScroll = false;

        // scroll fires too fast, so just use the handler
        // to set a flag, and check that flag with an interval

        // From http://ejohn.org/blog/learning-from-twitter/

        $(window).scroll(function() {
            didScroll = true;
        });

        setInterval(function() {
            var streams;
            if (didScroll) {
                didScroll = false;
                if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
                    streams = Pump.getStreams();
                    if (streams.major && streams.major.nextLink) {
                        Pump.body.startLoad();
                        streams.major.getNext(function() {
                            Pump.body.endLoad();
                        });
                    }
                }
            }
        }, 250);
    };

    // XXX: this is cheeseball.

    Pump.rel = function(url) {

        var a = document.createElement('a'),
            pathname;

        a.href = url;
        pathname = a.pathname;

        return pathname;
    };

    Pump.htmlEncode = function(value) {
        return $('
').text(value).html();
    };

    Pump.htmlDecode = function(value) {
        return $('
').html(value).text();
    };

    // Sets up the initial view and sub-views

    Pump.initialContentView = function() {

        var $content = $("#content"),
            selectorToView = {
                "#main": {View: Pump.MainContent},
                "#loginpage": {View: Pump.LoginContent},
                "#register": {View: Pump.RegisterContent},
                "#inbox": {View: Pump.InboxContent, models: {major: Pump.ActivityStream, minor: Pump.ActivityStream}},
                ".object-page": {View: Pump.ObjectContent, models: {object: Pump.ActivityObject}},
                ".major-activity-page": {View: Pump.ActivityContent, models: {activity: Pump.Activity}},
                ".user-activities": {View: Pump.UserPageContent, models: {profile: Pump.Person,
                                                                          major: Pump.ActivityStream,
                                                                          minor: Pump.ActivityStream}},
                ".user-favorites": {View: Pump.FavoritesContent, models: {profile: Pump.Person,
                                                                          favorites: Pump.ActivityObjectStream}},
                ".user-followers": {View: Pump.FollowersContent, models: {profile: Pump.Person,
                                                                          followers: Pump.PeopleStream}},
                ".user-following": {View: Pump.FollowingContent, models: {profile: Pump.Person,
                                                                          following: Pump.PeopleStream}},
                ".user-lists": {View: Pump.ListsContent, models: {profile: Pump.Person,
                                                                  lists: Pump.ActivityObjectStream}},
                ".user-list": {View: Pump.ListContent, models: {profile: Pump.Person,
                                                                lists: Pump.ActivityObjectStream,
                                                                list: Pump.ActivityObject}}
            },
            selector,
            $el,
            model,
            options,
            def,
            data,
            View;

        // When I say "view" the crowd say "selector"

        for (selector in selectorToView) {
            if (_.has(selectorToView, selector)) {
                $el = $content.find(selector);
                if ($el.length > 0) {
                    def = selectorToView[selector];
                    View = def.View;
                    options = {el: $el, data: {}};
                    data = Pump.initialData;
                    _.each(data, function(value, name) {
                        if (name == View.prototype.modelName) {
                            options.model = def.models[name].unique(value);
                        } else if (def.models[name]) {
                            options.data[name] = def.models[name].unique(value);
                        } else {
                            options.data[name] = value;
                        }
                    });
                    Pump.body.content = new View(options);
                    Pump.initialData = null;
                    break;
                }
            }
        }

        // XXX: set up initial data
    };

    Pump.newMinorActivity = function(act, callback) {
        if (Pump.principalUser) {
            Pump.addToStream(Pump.principalUser.minorStream, act, callback);
        } else {
            Pump.proxyActivity(act, callback);
        }
    };

    Pump.newMajorActivity = function(act, callback) {
        if (Pump.principalUser) {
            Pump.addToStream(Pump.principalUser.majorStream, act, callback);
        } else {
            Pump.proxyActivity(act, callback);
        }
    };

    Pump.addToStream = function(stream, act, callback) {
        stream.items.create(act, {
            success: function(act) {
                callback(null, act);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var type, response;
                type = jqXHR.getResponseHeader("Content-Type");
                if (type && type.indexOf("application/json") !== -1) {
                    response = JSON.parse(jqXHR.responseText);
                    callback(new Error(response.error), null);
                } else {
                    callback(new Error(errorThrown), null);
                }
            }
        });
    };

    // XXX: This POSTs with session auth; subject to XSS.

    Pump.proxyActivity = function(act, callback) {
        $.ajax({
            contentType: "application/json",
            data: JSON.stringify(act),
            dataType: "json",
            type: "POST",
            url: "/main/proxy",
            success: function(act) {
                callback(null, act);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var type, response;
                type = jqXHR.getResponseHeader("Content-Type");
                if (type && type.indexOf("application/json") !== -1) {
                    response = JSON.parse(jqXHR.responseText);
                    callback(new Error(response.error), null);
                } else {
                    callback(new Error(errorThrown), null);
                }
            }
        });
    };

    Pump.setTitle = function(title) {
        $("title").html(title + " - " + Pump.config.site);
    };

})(window._, window.$, window.Backbone, window.Pump);
<|endoftext|>


<commit_before>if (typeof (require) != 'undefined') {
	var loader = require('../../node_modules/Butterfly-js/dist/butterfly.min.js').loader;
}

/**
 * Entry point of the game, contains some part of the game engine and deals with
 * the different entities
 */
loader.executeModule('main',
'B', 'Canvas', 'Entities', 'Physics', 'Utils', 'Maps',
function (B, canvas, Entities, Physics, Utils, Maps) {
	var ball,
		tracks = [],
		// position of the mouse in the canvas, taking in account the scroll
		// and position of the canvas in the page
		mouseX,
		mouseY,
		fps = 30,
		urlParams = Utils.getUrlParams(window.location.search);

	const DEBUG = urlParams.debug || NO_DEBUG;
	console.log(DEBUG);

	// Init the view
	canvas.init(document.getElementById('game-canvas'));
	setInterval(updateAll, 1000 / fps);

	// Init the ball
	ball = new Entities.Ball(canvas.width() / 2, canvas.height() / 4, BALL_RADIUS, BALL_SPEED_X, BALL_SPEED_Y);
	// Position of the ball in the grid
	ball.setGridCoordinates(TRACK_SPACE_WIDTH, TRACK_SPACE_HEIGHT);

	/* Events */
	// Event to execute when the player wins
	B.Events.on('win', null, function () {
		ball.reset();
	});

	// Event to execute when the player loses
	B.Events.on('lost', null, function () {
	});

	// Event to execute when the mouse move
	B.Events.on('mouse-moved', null, function (mX, mY) {
		mouseX = mX;
		mouseY = mY;

		if (DEBUG) {
			ball.x = mouseX;
			ball.y = mouseY;
			ball.speedX = BALL_SPEED_X;
			ball.speedY = BALL_SPEED_Y * -1;
		}
	});
	/* End of Events */

	/*
	 * Create the tracks, The whole game is a grid and tracks are on the grid
	 * The tracks are organised on a rectangle of the grid started at the
	 * position (TRACK_GRID_START_COL, TRACK_GRID_START_COL) and ends at the
	 * position (TRACK_GRID_COL, TRACK_GRID_ROW)
	 * Each track is an instance of the class Entities.Track
	 */
	var col, row,
		trackWidth = canvas.width() / Maps[0].width,
		trackHeight = canvas.height() / Maps[0].height;
	for (row = 0; row < Maps[0].height; row++ ) {
		for (col = 0; col < Maps[0].width; col++ ) {
			tracks.push(new Entities.Track(
				// 5 is the initial left margin
				trackWidth * col, trackHeight * row,
				trackWidth, trackHeight,
				// @TODO remove destructable field
				true, Maps[0].map[row][col]
			));
		}
	}

	}

	/**
	 * Method to convert a pair of coordinates to the index of the cell in the
	 * grid the coordinates are in
	 */
	colRowToGridIndex = function (col, row) {
		return col - TRACK_GRID_START_COL +
			(TRACK_GRID_COL - TRACK_GRID_START_COL) * (row - TRACK_GRID_START_ROW);
	};

	/**
	 * Method to update the game state and the objects's position
	 */
	function moveAll () {
		// Update the ball position
		ball.updatePosition();
		ball.setGridCoordinates(TRACK_SPACE_WIDTH, TRACK_SPACE_HEIGHT);

		/* Ball and edges collision*/
		var wallBounded = Physics.sphereBounceAgainstInnerRectangle(ball, {x: 0, y: 0, w: canvas.width(), h: canvas.height()});
		if (wallBounded == 'down') {
			ball.reset();
			console.log('fire lost');
			B.Events.fire('lost');
		}
		/* End of Ball and edges collision*/

		/* Ball and active track collision */
		var track,
			trackSide,
			trackTopBot;

		track = tracks[colRowToGridIndex(
			ball.gridCellCol,
			ball.gridCellRow
		)];

		// if the ball is on an active track
		if (TRACK_GRID_START_COL <= ball.gridCellCol && ball.gridCellCol < TRACK_GRID_COL
			&& TRACK_GRID_START_COL <= ball.gridCellRow && ball.gridCellRow < TRACK_GRID_ROW
			&& track.state == Entities.Track.STATE_ACTIVE
		) {
			// track next to the current one, according to ball's old position
			trackSide = tracks[colRowToGridIndex(ball.oldGridCellCol, ball.gridCellRow)];
			trackSide = trackSide && trackSide.state == Entities.Track.STATE_ACTIVE && trackSide || undefined;

			// track under or above to the current one, according to ball's old position
			trackTopBot = tracks[colRowToGridIndex(ball.gridCellCol, ball.oldGridCellRow)];
			trackTopBot = trackTopBot && trackTopBot.state == Entities.Track.STATE_ACTIVE && trackTopBot || undefined;

			Physics.sphereBounceAgainstGridRectangle(ball, track, trackSide, trackTopBot);
		}
		/* End of Ball and active track collision */
	}

	/**
	 * Method to execute on each frame to update the game state and the
	 * objects's position and then redraw the canvas
	 */
	function updateAll () {
		moveAll();
		canvas.drawAll([ball, tracks]);

		if (DEBUG) {
			canvas.drawText('(' +
				Math.floor(mouseX / TRACK_SPACE_WIDTH) + ', ' +
				Math.floor(mouseY / TRACK_SPACE_HEIGHT) + ', ' +
				colRowToGridIndex(Math.floor(mouseX / TRACK_SPACE_WIDTH), Math.floor(mouseY / TRACK_SPACE_HEIGHT)) + ')', mouseX, mouseY, 'white');

			canvas.line([ball.x, ball.y], [ball.x + ball.speedX * 30, ball.y + ball.speedY * 30]);
		}
	}
});
<commit_msg>start set from the map<commit_after>if (typeof (require) != 'undefined') {
	var loader = require('../../node_modules/Butterfly-js/dist/butterfly.min.js').loader;
}

/**
 * Entry point of the game, contains some part of the game engine and deals with
 * the different entities
 */
loader.executeModule('main',
'B', 'Canvas', 'Entities', 'Physics', 'Utils', 'Maps',
function (B, canvas, Entities, Physics, Utils, Maps) {
	var ball,
		tracks = [],
		// position of the mouse in the canvas, taking in account the scroll
		// and position of the canvas in the page
		mouseX,
		mouseY,
		fps = 30,
		urlParams = Utils.getUrlParams(window.location.search);

	const DEBUG = urlParams.debug || NO_DEBUG;
	console.log(DEBUG);

	// Init the view
	canvas.init(document.getElementById('game-canvas'));
	setInterval(updateAll, 1000 / fps);

	/* Events */
	// Event to execute when the player wins
	B.Events.on('win', null, function () {
		ball.reset();
	});

	// Event to execute when the player loses
	B.Events.on('lost', null, function () {
	});

	// Event to execute when the mouse move
	B.Events.on('mouse-moved', null, function (mX, mY) {
		mouseX = mX;
		mouseY = mY;

		if (DEBUG) {
			ball.x = mouseX;
			ball.y = mouseY;
			ball.speedX = BALL_SPEED_X;
			ball.speedY = BALL_SPEED_Y * -1;
		}
	});
	/* End of Events */

	/*
	 * Create the tracks, The whole game is a grid and tracks are on the grid
	 * The tracks are organised on a rectangle of the grid started at the
	 * position (TRACK_GRID_START_COL, TRACK_GRID_START_COL) and ends at the
	 * position (TRACK_GRID_COL, TRACK_GRID_ROW)
	 * Each track is an instance of the class Entities.Track
	 */
	var col, row,
		trackWidth = canvas.width() / Maps[0].width,
		trackHeight = canvas.height() / Maps[0].height,
		startX, startY;
	for (row = 0; row < Maps[0].height; row++ ) {
		for (col = 0; col < Maps[0].width; col++ ) {
			tracks.push(new Entities.Track(
				// 5 is the initial left margin
				trackWidth * col, trackHeight * row,
				trackWidth, trackHeight,
				// @TODO remove destructable field
				true, Maps[0].map[row][col]
			));

			if (Maps[0].map[row][col] == TRACK_STATE_START) {
				startX = trackWidth * col;
				startY = trackHeight * row;
			}
		}
	}

	// Init the ball
	ball = new Entities.Ball(startX, startY, BALL_RADIUS, BALL_SPEED_X, BALL_SPEED_Y);
	// Position of the ball in the grid
	ball.setGridCoordinates(TRACK_SPACE_WIDTH, TRACK_SPACE_HEIGHT);

	/**
	 * Method to convert a pair of coordinates to the index of the cell in the
	 * grid the coordinates are in
	 */
	colRowToGridIndex = function (col, row) {
		return col - TRACK_GRID_START_COL +
			(TRACK_GRID_COL - TRACK_GRID_START_COL) * (row - TRACK_GRID_START_ROW);
	};

	/**
	 * Method to update the game state and the objects's position
	 */
	function moveAll () {
		// Update the ball position
		ball.updatePosition();
		ball.setGridCoordinates(TRACK_SPACE_WIDTH, TRACK_SPACE_HEIGHT);

		/* Ball and edges collision*/
		var wallBounded = Physics.sphereBounceAgainstInnerRectangle(ball, {x: 0, y: 0, w: canvas.width(), h: canvas.height()});
		if (wallBounded == 'down') {
			ball.reset();
			console.log('fire lost');
			B.Events.fire('lost');
		}
		/* End of Ball and edges collision*/

		/* Ball and active track collision */
		var track,
			trackSide,
			trackTopBot;

		track = tracks[colRowToGridIndex(
			ball.gridCellCol,
			ball.gridCellRow
		)];

		// if the ball is on an active track
		if (TRACK_GRID_START_COL <= ball.gridCellCol && ball.gridCellCol < TRACK_GRID_COL
			&& TRACK_GRID_START_COL <= ball.gridCellRow && ball.gridCellRow < TRACK_GRID_ROW
			&& track.state == Entities.Track.STATE_ACTIVE
		) {
			// track next to the current one, according to ball's old position
			trackSide = tracks[colRowToGridIndex(ball.oldGridCellCol, ball.gridCellRow)];
			trackSide = trackSide && trackSide.state == Entities.Track.STATE_ACTIVE && trackSide || undefined;

			// track under or above to the current one, according to ball's old position
			trackTopBot = tracks[colRowToGridIndex(ball.gridCellCol, ball.oldGridCellRow)];
			trackTopBot = trackTopBot && trackTopBot.state == Entities.Track.STATE_ACTIVE && trackTopBot || undefined;

			Physics.sphereBounceAgainstGridRectangle(ball, track, trackSide, trackTopBot);
		}
		/* End of Ball and active track collision */
	}

	/**
	 * Method to execute on each frame to update the game state and the
	 * objects's position and then redraw the canvas
	 */
	function updateAll () {
		moveAll();
		canvas.drawAll([ball, tracks]);

		if (DEBUG) {
			canvas.drawText('(' +
				Math.floor(mouseX / TRACK_SPACE_WIDTH) + ', ' +
				Math.floor(mouseY / TRACK_SPACE_HEIGHT) + ', ' +
				colRowToGridIndex(Math.floor(mouseX / TRACK_SPACE_WIDTH), Math.floor(mouseY / TRACK_SPACE_HEIGHT)) + ')', mouseX, mouseY, 'white');

			canvas.line([ball.x, ball.y], [ball.x + ball.speedX * 30, ball.y + ball.speedY * 30]);
		}
	}
});
<|endoftext|>