<commit_before>// ==========================================================================
// Vimeo plugin
// ==========================================================================

import captions from '../captions';
import controls from '../controls';
import ui from '../ui';
import { createElement, replaceElement, toggleClass } from '../utils/elements';
import { triggerEvent } from '../utils/events';
import fetch from '../utils/fetch';
import is from '../utils/is';
import loadScript from '../utils/load-script';
import { format, stripHTML } from '../utils/strings';
import { setAspectRatio } from '../utils/style';
import { buildUrlParams } from '../utils/urls';

// Parse Vimeo ID from URL
function parseId(url) {
  if (is.empty(url)) {
    return null;
  }

  if (is.number(Number(url))) {
    return url;
  }

  const regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
  return url.match(regex) ? RegExp.$2 : url;
}

// Set playback state and trigger change (only on actual change)
function assurePlaybackState(play) {
  if (play && !this.embed.hasPlayed) {
    this.embed.hasPlayed = true;
  }
  if (this.media.paused === play) {
    this.media.paused = !play;
    triggerEvent.call(this, this.media, play ? 'play' : 'pause');
  }
}

const vimeo = {
  setup() {
    const player = this;

    // Add embed class for responsive
    toggleClass(player.elements.wrapper, player.config.classNames.embed, true);

    // Set speed options from config
    player.options.speed = player.config.speed.options;

    // Set intial ratio
    setAspectRatio.call(player);

    // Load the SDK if not already
    if (!is.object(window.Vimeo)) {
      loadScript(player.config.urls.vimeo.sdk)
        .then(() => {
          vimeo.ready.call(player);
        })
        .catch((error) => {
          player.debug.warn('Vimeo SDK (player.js) failed to load', error);
        });
    } else {
      vimeo.ready.call(player);
    }
  },

  // API Ready
  ready() {
    const player = this;
    const config = player.config.vimeo;
    const { premium, referrerPolicy, ...frameParams } = config;

    // If the owner has a pro or premium account then we can hide controls etc
    if (premium) {
      Object.assign(frameParams, {
        controls: false,
        sidedock: false,
      });
    }

    // Get Vimeo params for the iframe
    const params = buildUrlParams({
      loop: player.config.loop.active,
      autoplay: player.autoplay,
      muted: player.muted,
      gesture: 'media',
      playsinline: !this.config.fullscreen.iosNative,
      ...frameParams,
    });

    // Get the source URL or ID
    let source = player.media.getAttribute('src');

    // Get from <div> if needed
    if (is.empty(source)) {
      source = player.media.getAttribute(player.config.attributes.embed.id);
    }

    const id = parseId(source);
    // Build an iframe
    const iframe = createElement('iframe');
    const src = format(player.config.urls.vimeo.iframe, id, params);
    iframe.setAttribute('src', src);
    iframe.setAttribute('allowfullscreen', '');
    iframe.setAttribute('allow', ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; '));

    // Set the referrer policy if required
    if (!is.empty(referrerPolicy)) {
      iframe.setAttribute('referrerPolicy', referrerPolicy);
    }

    // Inject the package
    if (premium || !config.customControls) {
      iframe.setAttribute('data-poster', player.poster);
      player.media = replaceElement(iframe, player.media);
    } else {
      const wrapper = createElement('div', {
        class: player.config.classNames.embedContainer,
        'data-poster': player.poster,
      });
      wrapper.appendChild(iframe);
      player.media = replaceElement(wrapper, player.media);
    }

    // Get poster image
    if (!config.customControls) {
      fetch(format(player.config.urls.vimeo.api, src)).then((response) => {
        if (is.empty(response) || !response.thumbnail_url) {
          return;
        }

        // Set and show poster
        ui.setPoster.call(player, response.thumbnail_url).catch(() => {});
      });
    }

    // Setup instance
    // https://github.com/vimeo/player.js
    player.embed = new window.Vimeo.Player(iframe, {
      autopause: player.config.autopause,
      muted: player.muted,
    });

    player.media.paused = true;
    player.media.currentTime = 0;

    // Disable native text track rendering
    if (player.supported.ui) {
      player.embed.disableTextTrack();
    }

    // Create a faux HTML5 API using the Vimeo API
    player.media.play = () => {
      assurePlaybackState.call(player, true);
      return player.embed.play();
    };

    player.media.pause = () => {
      assurePlaybackState.call(player, false);
      return player.embed.pause();
    };

    player.media.stop = () => {
      player.pause();
      player.currentTime = 0;
    };

    // Seeking
    let { currentTime } = player.media;
    Object.defineProperty(player.media, 'currentTime', {
      get() {
        return currentTime;
      },
      set(time) {
        // Vimeo will automatically play on seek if the video hasn't been played before

        // Get current paused state and volume etc
        const { embed, media, paused, volume } = player;
        const restorePause = paused && !embed.hasPlayed;

        // Set seeking state and trigger event
        media.seeking = true;
        triggerEvent.call(player, media, 'seeking');

        // If paused, mute until seek is complete
        Promise.resolve(restorePause && embed.setVolume(0))
          // Seek
          .then(() => embed.setCurrentTime(time))
          // Restore paused
          .then(() => restorePause && embed.pause())
          // Restore volume
          .then(() => restorePause && embed.setVolume(volume))
          .catch(() => {
            // Do nothing
          });
      },
    });

    // Playback speed
    let speed = player.config.speed.selected;
    Object.defineProperty(player.media, 'playbackRate', {
      get() {
        return speed;
      },
      set(input) {
        player.embed
          .setPlaybackRate(input)
          .then(() => {
            speed = input;
            triggerEvent.call(player, player.media, 'ratechange');
          })
          .catch(() => {
            // Cannot set Playback Rate, Video is probably not on Pro account
            player.options.speed = [1];
          });
      },
    });

    // Volume
    let { volume } = player.config;
    Object.defineProperty(player.media, 'volume', {
      get() {
        return volume;
      },
      set(input) {
        player.embed.setVolume(input).then(() => {
          volume = input;
          triggerEvent.call(player, player.media, 'volumechange');
        });
      },
    });

    // Muted
    let { muted } = player.config;
    Object.defineProperty(player.media, 'muted', {
      get() {
        return muted;
      },
      set(input) {
        const toggle = is.boolean(input) ? input : false;

        player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => {
          muted = toggle;
          triggerEvent.call(player, player.media, 'volumechange');
        });
      },
    });

    // Loop
    let { loop } = player.config;
    Object.defineProperty(player.media, 'loop', {
      get() {
        return loop;
      },
      set(input) {
        const toggle = is.boolean(input) ? input : player.config.loop.active;

        player.embed.setLoop(toggle).then(() => {
          loop = toggle;
        });
      },
    });

    // Source
    let currentSrc;
    player.embed
      .getVideoUrl()
      .then((value) => {
        currentSrc = value;
        controls.setDownloadUrl.call(player);
      })
      .catch((error) => {
        this.debug.warn(error);
      });

    Object.defineProperty(player.media, 'currentSrc', {
      get() {
        return currentSrc;
      },
    });

    // Ended
    Object.defineProperty(player.media, 'ended', {
      get() {
        return player.currentTime === player.duration;
      },
    });

    // Set aspect ratio based on video size
    Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
      const [width, height] = dimensions;
      player.embed.ratio = [width, height];
      setAspectRatio.call(this);
    });

    // Set autopause
    player.embed.setAutopause(player.config.autopause).then((state) => {
      player.config.autopause = state;
    });

    // Get title
    player.embed.getVideoTitle().then((title) => {
      player.config.title = title;
      ui.setTitle.call(this);
    });

    // Get current time
    player.embed.getCurrentTime().then((value) => {
      currentTime = value;
      triggerEvent.call(player, player.media, 'timeupdate');
    });

    // Get duration
    player.embed.getDuration().then((value) => {
      player.media.duration = value;
      triggerEvent.call(player, player.media, 'durationchange');
    });

    // Get captions
    player.embed.getTextTracks().then((tracks) => {
      player.media.textTracks = tracks;
      captions.setup.call(player);
    });

    player.embed.on('cuechange', ({ cues = [] }) => {
      const strippedCues = cues.map((cue) => stripHTML(cue.text));
      captions.updateCues.call(player, strippedCues);
    });

    player.embed.on('loaded', () => {
      // Assure state and events are updated on autoplay
      player.embed.getPaused().then((paused) => {
        assurePlaybackState.call(player, !paused);
        if (!paused) {
          triggerEvent.call(player, player.media, 'playing');
        }
      });

      if (is.element(player.embed.element) && player.supported.ui) {
        const frame = player.embed.element;

        // Fix keyboard focus issues
        // https://github.com/sampotts/plyr/issues/317
        frame.setAttribute('tabindex', -1);
      }
    });

    player.embed.on('bufferstart', () => {
      triggerEvent.call(player, player.media, 'waiting');
    });

    player.embed.on('bufferend', () => {
      triggerEvent.call(player, player.media, 'playing');
    });

    player.embed.on('play', () => {
      assurePlaybackState.call(player, true);
      triggerEvent.call(player, player.media, 'playing');
    });

    player.embed.on('pause', () => {
      assurePlaybackState.call(player, false);
    });

    player.embed.on('timeupdate', (data) => {
      player.media.seeking = false;
      currentTime = data.seconds;
      triggerEvent.call(player, player.media, 'timeupdate');
    });

    player.embed.on('progress', (data) => {
      player.media.buffered = data.percent;
      triggerEvent.call(player, player.media, 'progress');

      // Check all loaded
      if (parseInt(data.percent, 10) === 1) {
        triggerEvent.call(player, player.media, 'canplaythrough');
      }

      // Get duration as if we do it before load, it gives an incorrect value
      // https://github.com/sampotts/plyr/issues/891
      player.embed.getDuration().then((value) => {
        if (value !== player.media.duration) {
          player.media.duration = value;
          triggerEvent.call(player, player.media, 'durationchange');
        }
      });
    });

    player.embed.on('seeked', () => {
      player.media.seeking = false;
      triggerEvent.call(player, player.media, 'seeked');
    });

    player.embed.on('ended', () => {
      player.media.paused = true;
      triggerEvent.call(player, player.media, 'ended');
    });

    player.embed.on('error', (detail) => {
      player.media.error = detail;
      triggerEvent.call(player, player.media, 'error');
    });

    // Rebuild UI
    if (config.customControls) {
      setTimeout(() => ui.build.call(player), 0);
    }
  },
};

export default vimeo;
<commit_msg>chore: linting<commit_after>// ==========================================================================
// Vimeo plugin
// ==========================================================================

import captions from '../captions';
import controls from '../controls';
import ui from '../ui';
import { createElement, replaceElement, toggleClass } from '../utils/elements';
import { triggerEvent } from '../utils/events';
import fetch from '../utils/fetch';
import is from '../utils/is';
import loadScript from '../utils/load-script';
import { format, stripHTML } from '../utils/strings';
import { setAspectRatio } from '../utils/style';
import { buildUrlParams } from '../utils/urls';

// Parse Vimeo ID from URL
function parseId(url) {
  if (is.empty(url)) {
    return null;
  }

  if (is.number(Number(url))) {
    return url;
  }

  const regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
  return url.match(regex) ? RegExp.$2 : url;
}

// Set playback state and trigger change (only on actual change)
function assurePlaybackState(play) {
  if (play && !this.embed.hasPlayed) {
    this.embed.hasPlayed = true;
  }
  if (this.media.paused === play) {
    this.media.paused = !play;
    triggerEvent.call(this, this.media, play ? 'play' : 'pause');
  }
}

const vimeo = {
  setup() {
    const player = this;

    // Add embed class for responsive
    toggleClass(player.elements.wrapper, player.config.classNames.embed, true);

    // Set speed options from config
    player.options.speed = player.config.speed.options;

    // Set intial ratio
    setAspectRatio.call(player);

    // Load the SDK if not already
    if (!is.object(window.Vimeo)) {
      loadScript(player.config.urls.vimeo.sdk)
        .then(() => {
          vimeo.ready.call(player);
        })
        .catch((error) => {
          player.debug.warn('Vimeo SDK (player.js) failed to load', error);
        });
    } else {
      vimeo.ready.call(player);
    }
  },

  // API Ready
  ready() {
    const player = this;
    const config = player.config.vimeo;
    const { premium, referrerPolicy, ...frameParams } = config;

    // If the owner has a pro or premium account then we can hide controls etc
    if (premium) {
      Object.assign(frameParams, {
        controls: false,
        sidedock: false,
      });
    }

    // Get Vimeo params for the iframe
    const params = buildUrlParams({
      loop: player.config.loop.active,
      autoplay: player.autoplay,
      muted: player.muted,
      gesture: 'media',
      playsinline: !this.config.fullscreen.iosNative,
      ...frameParams,
    });

    // Get the source URL or ID
    let source = player.media.getAttribute('src');

    // Get from <div> if needed
    if (is.empty(source)) {
      source = player.media.getAttribute(player.config.attributes.embed.id);
    }

    const id = parseId(source);
    // Build an iframe
    const iframe = createElement('iframe');
    const src = format(player.config.urls.vimeo.iframe, id, params);
    iframe.setAttribute('src', src);
    iframe.setAttribute('allowfullscreen', '');
    iframe.setAttribute(
      'allow',
      ['autoplay', 'fullscreen', 'picture-in-picture', 'encrypted-media', 'accelerometer', 'gyroscope'].join('; '),
    );

    // Set the referrer policy if required
    if (!is.empty(referrerPolicy)) {
      iframe.setAttribute('referrerPolicy', referrerPolicy);
    }

    // Inject the package
    if (premium || !config.customControls) {
      iframe.setAttribute('data-poster', player.poster);
      player.media = replaceElement(iframe, player.media);
    } else {
      const wrapper = createElement('div', {
        class: player.config.classNames.embedContainer,
        'data-poster': player.poster,
      });
      wrapper.appendChild(iframe);
      player.media = replaceElement(wrapper, player.media);
    }

    // Get poster image
    if (!config.customControls) {
      fetch(format(player.config.urls.vimeo.api, src)).then((response) => {
        if (is.empty(response) || !response.thumbnail_url) {
          return;
        }

        // Set and show poster
        ui.setPoster.call(player, response.thumbnail_url).catch(() => {});
      });
    }

    // Setup instance
    // https://github.com/vimeo/player.js
    player.embed = new window.Vimeo.Player(iframe, {
      autopause: player.config.autopause,
      muted: player.muted,
    });

    player.media.paused = true;
    player.media.currentTime = 0;

    // Disable native text track rendering
    if (player.supported.ui) {
      player.embed.disableTextTrack();
    }

    // Create a faux HTML5 API using the Vimeo API
    player.media.play = () => {
      assurePlaybackState.call(player, true);
      return player.embed.play();
    };

    player.media.pause = () => {
      assurePlaybackState.call(player, false);
      return player.embed.pause();
    };

    player.media.stop = () => {
      player.pause();
      player.currentTime = 0;
    };

    // Seeking
    let { currentTime } = player.media;
    Object.defineProperty(player.media, 'currentTime', {
      get() {
        return currentTime;
      },
      set(time) {
        // Vimeo will automatically play on seek if the video hasn't been played before

        // Get current paused state and volume etc
        const { embed, media, paused, volume } = player;
        const restorePause = paused && !embed.hasPlayed;

        // Set seeking state and trigger event
        media.seeking = true;
        triggerEvent.call(player, media, 'seeking');

        // If paused, mute until seek is complete
        Promise.resolve(restorePause && embed.setVolume(0))
          // Seek
          .then(() => embed.setCurrentTime(time))
          // Restore paused
          .then(() => restorePause && embed.pause())
          // Restore volume
          .then(() => restorePause && embed.setVolume(volume))
          .catch(() => {
            // Do nothing
          });
      },
    });

    // Playback speed
    let speed = player.config.speed.selected;
    Object.defineProperty(player.media, 'playbackRate', {
      get() {
        return speed;
      },
      set(input) {
        player.embed
          .setPlaybackRate(input)
          .then(() => {
            speed = input;
            triggerEvent.call(player, player.media, 'ratechange');
          })
          .catch(() => {
            // Cannot set Playback Rate, Video is probably not on Pro account
            player.options.speed = [1];
          });
      },
    });

    // Volume
    let { volume } = player.config;
    Object.defineProperty(player.media, 'volume', {
      get() {
        return volume;
      },
      set(input) {
        player.embed.setVolume(input).then(() => {
          volume = input;
          triggerEvent.call(player, player.media, 'volumechange');
        });
      },
    });

    // Muted
    let { muted } = player.config;
    Object.defineProperty(player.media, 'muted', {
      get() {
        return muted;
      },
      set(input) {
        const toggle = is.boolean(input) ? input : false;

        player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => {
          muted = toggle;
          triggerEvent.call(player, player.media, 'volumechange');
        });
      },
    });

    // Loop
    let { loop } = player.config;
    Object.defineProperty(player.media, 'loop', {
      get() {
        return loop;
      },
      set(input) {
        const toggle = is.boolean(input) ? input : player.config.loop.active;

        player.embed.setLoop(toggle).then(() => {
          loop = toggle;
        });
      },
    });

    // Source
    let currentSrc;
    player.embed
      .getVideoUrl()
      .then((value) => {
        currentSrc = value;
        controls.setDownloadUrl.call(player);
      })
      .catch((error) => {
        this.debug.warn(error);
      });

    Object.defineProperty(player.media, 'currentSrc', {
      get() {
        return currentSrc;
      },
    });

    // Ended
    Object.defineProperty(player.media, 'ended', {
      get() {
        return player.currentTime === player.duration;
      },
    });

    // Set aspect ratio based on video size
    Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then((dimensions) => {
      const [width, height] = dimensions;
      player.embed.ratio = [width, height];
      setAspectRatio.call(this);
    });

    // Set autopause
    player.embed.setAutopause(player.config.autopause).then((state) => {
      player.config.autopause = state;
    });

    // Get title
    player.embed.getVideoTitle().then((title) => {
      player.config.title = title;
      ui.setTitle.call(this);
    });

    // Get current time
    player.embed.getCurrentTime().then((value) => {
      currentTime = value;
      triggerEvent.call(player, player.media, 'timeupdate');
    });

    // Get duration
    player.embed.getDuration().then((value) => {
      player.media.duration = value;
      triggerEvent.call(player, player.media, 'durationchange');
    });

    // Get captions
    player.embed.getTextTracks().then((tracks) => {
      player.media.textTracks = tracks;
      captions.setup.call(player);
    });

    player.embed.on('cuechange', ({ cues = [] }) => {
      const strippedCues = cues.map((cue) => stripHTML(cue.text));
      captions.updateCues.call(player, strippedCues);
    });

    player.embed.on('loaded', () => {
      // Assure state and events are updated on autoplay
      player.embed.getPaused().then((paused) => {
        assurePlaybackState.call(player, !paused);
        if (!paused) {
          triggerEvent.call(player, player.media, 'playing');
        }
      });

      if (is.element(player.embed.element) && player.supported.ui) {
        const frame = player.embed.element;

        // Fix keyboard focus issues
        // https://github.com/sampotts/plyr/issues/317
        frame.setAttribute('tabindex', -1);
      }
    });

    player.embed.on('bufferstart', () => {
      triggerEvent.call(player, player.media, 'waiting');
    });

    player.embed.on('bufferend', () => {
      triggerEvent.call(player, player.media, 'playing');
    });

    player.embed.on('play', () => {
      assurePlaybackState.call(player, true);
      triggerEvent.call(player, player.media, 'playing');
    });

    player.embed.on('pause', () => {
      assurePlaybackState.call(player, false);
    });

    player.embed.on('timeupdate', (data) => {
      player.media.seeking = false;
      currentTime = data.seconds;
      triggerEvent.call(player, player.media, 'timeupdate');
    });

    player.embed.on('progress', (data) => {
      player.media.buffered = data.percent;
      triggerEvent.call(player, player.media, 'progress');

      // Check all loaded
      if (parseInt(data.percent, 10) === 1) {
        triggerEvent.call(player, player.media, 'canplaythrough');
      }

      // Get duration as if we do it before load, it gives an incorrect value
      // https://github.com/sampotts/plyr/issues/891
      player.embed.getDuration().then((value) => {
        if (value !== player.media.duration) {
          player.media.duration = value;
          triggerEvent.call(player, player.media, 'durationchange');
        }
      });
    });

    player.embed.on('seeked', () => {
      player.media.seeking = false;
      triggerEvent.call(player, player.media, 'seeked');
    });

    player.embed.on('ended', () => {
      player.media.paused = true;
      triggerEvent.call(player, player.media, 'ended');
    });

    player.embed.on('error', (detail) => {
      player.media.error = detail;
      triggerEvent.call(player, player.media, 'error');
    });

    // Rebuild UI
    if (config.customControls) {
      setTimeout(() => ui.build.call(player), 0);
    }
  },
};

export default vimeo;
<|endoftext|>


<commit_before>'use strict';

function generate(path) {

	return path.toLowerCase().replace(/([^a-z ])/g, '').replace(/ /g, '-');

}

module.exports = generate;
<commit_msg>Added documentation to the Question Path transformer<commit_after>'use strict';

/**
 * Question path.
 * @module ./question-path
 */

/**
 * Transforms the heading from each feed question into an applicable portion of the URL path.
 * @example ‘This is a Heading!’ => ‘this-is-a-heading’
 * @param {string} raw - The original question heading.
 * @return {string} The transformed heading.
 */
function transform(raw) {

	return raw.toLowerCase().replace(/([^a-z ])/g, '').replace(/ /g, '-');

}

/** Question path. */
module.exports = transform;
<|endoftext|>


<commit_before>
var _ = require('underscore');
var utils = require('./utils.js');

var Refugee = require('./refugee.js');
var moment = require('moment');

var EU_COUNTRIES = ["AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", "FIN", "FRA", "DEU", "GRC", "HUN", "IRL", "ITA", "LVA", "LTU", "LUX", "MLT", "NLD", "POL", "PRT", "ROU", "SVK", "SVN", "ESP", "SWE", "UKR", "GBR", "CHE", "NOR"];

var RefugeeModel = function(fc, asylumData, regionalData, divider) {
	this.fc = fc;
	this.asylumData = asylumData;
	this.regionalData = regionalData;
	this.refugees = [];
	this.activeRefugees = [];
	this.divider = divider;
	this.initialize();
};

RefugeeModel.prototype.initialize = function() {
	console.time("refugee adding");
	this.asylumData.forEach(this._addPeopleFromValidCountries.bind(this));
	console.timeEnd("refugee adding");
	console.time("regional adding");
	this.regionalData.forEach(this._addPeopleFromValidCountries.bind(this));
	console.timeEnd("regional adding");
	console.time("refugee sorting");
	this.refugees.sort(function(a, b) {
		return a.startMomentUnix - b.startMomentUnix;
	});
	console.timeEnd("refugee sorting");

	this.refugeeIndex = 0;
}

RefugeeModel.prototype._addPeopleFromValidCountries = function(item) {
	if (utils.getFeatureForCountry(this.fc, item.ac) == null) {
		console.log("asylum country " + item.ac +  "not in map, skipping");
	} else if (utils.getFeatureForCountry(this.fc, item.oc) == null) {
		console.log("origin country " + item.oc +  "not in map, skipping");
	} else if (item.count > 0) {
		this.addRefugees(item.oc, item.ac, item.count / this.divider, item.month - 1, item.year);
	}
}

// RefugeeModel.prototype.removeRefugee = function(r) {
// 	var index = this.activeRefugees.indexOf(r);
// 	if (index == -1) {
// 		console.log("failaa" + r);
// 	} else {
// 		this.activeRefugees.splice(index, 1);
// 	}
// }

RefugeeModel.prototype.updateActiveRefugees = function() {
	// filter out the ones that have arrived
	this.activeRefugees = this.activeRefugees.filter(function(r) {
		return r.arrived === false;
	});

	// add new ones
	do {
		var r = this.refugees[this.refugeeIndex];
		if (r != null && r.isPastStartMoment(this.currentMoment)) {
			this.activeRefugees.push(r);
			this.refugeeIndex++;
		} else {
			return;
		}
	} while (true);
}

RefugeeModel.prototype.addRefugees = function(startCountry, endCountry, count, month, year) {
	_.times(Math.round(count), function() { // should it be Math.floor?
		this.refugees.push(this.createRefugee(startCountry, endCountry, month, year));
	}.bind(this));
}

RefugeeModel.prototype.kmhToDegsPerH = function(kmh) {
	return kmh / 111;
}

/*
 * Return the center point of the given country
 */
RefugeeModel.prototype.createCenterCountryPoint = function(country) {
	var feature = utils.getFeatureForCountry(this.fc, country);
	if (feature == null) {
		throw "could not find feature for " + country;
	}
	return utils.getCenterPointForCountryBorderFeature(feature);
}

/*
 * Create a random point within the given country
 */
RefugeeModel.prototype.createRandomCountryPoint = function(country) {
	var feature = utils.getFeatureForCountry(this.fc, country);
	if (feature == null) {
		throw "could not find feature for " + country;
	}
	return utils.getRandomPointForCountryBorderFeature(feature);
}

function daysInMonth(month,year) {
	return new Date(year, month, 0).getDate();
}

/*
 * Get a speed for a new refugee in km / h;
 */
RefugeeModel.prototype.prepareRefugeeSpeed = function() {
	return Math.random() * 2 + 4;
}

RefugeeModel.prototype.prepareRefugeeEndMoment = function(month, year) {
	var days = daysInMonth(month, year);
	var mom = moment(new Date(year, month, 1));

	var maxSeconds = days * 24 * 60 * 60;
	var diff = Math.random() * maxSeconds;
	mom.add(diff, 'seconds');
	return mom;
}

RefugeeModel.prototype.createRefugee = function(startCountry, endCountry, month, year) {
	var isEu = (EU_COUNTRIES.indexOf(endCountry) > -1);
	var r = new Refugee(
		this.createRandomCountryPoint(startCountry),
		this.createCenterCountryPoint(endCountry),
		this.prepareRefugeeSpeed(),
		this.prepareRefugeeEndMoment(month, year),
		isEu
	);
	return r;
}

module.exports = RefugeeModel;

// // kludge
// RefugeeModel.prototype.createSyrianPoint = function() {
// 	return utils.getRandomPoint(this.fc.features[1].geometry.coordinates[0]);
// }

// // kludge
// RefugeeModel.prototype.createFinnishPoint = function() {
// 	return utils.getRandomPoint(utils.getLargestPolygon(this.fc.features[0].geometry.coordinates));
// }<commit_msg>Use center point for countries instead of random point<commit_after>
var _ = require('underscore');
var utils = require('./utils.js');

var Refugee = require('./refugee.js');
var moment = require('moment');

var EU_COUNTRIES = ["AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", "FIN", "FRA", "DEU", "GRC", "HUN", "IRL", "ITA", "LVA", "LTU", "LUX", "MLT", "NLD", "POL", "PRT", "ROU", "SVK", "SVN", "ESP", "SWE", "UKR", "GBR", "CHE", "NOR"];

var RefugeeModel = function(fc, asylumData, regionalData, divider) {
	this.fc = fc;
	this.asylumData = asylumData;
	this.regionalData = regionalData;
	this.refugees = [];
	this.activeRefugees = [];
	this.divider = divider;
	this.initialize();
};

RefugeeModel.prototype.initialize = function() {
	console.time("refugee adding");
	this.asylumData.forEach(this._addPeopleFromValidCountries.bind(this));
	console.timeEnd("refugee adding");
	console.time("regional adding");
	this.regionalData.forEach(this._addPeopleFromValidCountries.bind(this));
	console.timeEnd("regional adding");
	console.time("refugee sorting");
	this.refugees.sort(function(a, b) {
		return a.startMomentUnix - b.startMomentUnix;
	});
	console.timeEnd("refugee sorting");

	this.refugeeIndex = 0;
}

RefugeeModel.prototype._addPeopleFromValidCountries = function(item) {
	if (utils.getFeatureForCountry(this.fc, item.ac) == null) {
		console.log("asylum country " + item.ac +  "not in map, skipping");
	} else if (utils.getFeatureForCountry(this.fc, item.oc) == null) {
		console.log("origin country " + item.oc +  "not in map, skipping");
	} else if (item.count > 0) {
		this.addRefugees(item.oc, item.ac, item.count / this.divider, item.month - 1, item.year);
	}
}

// RefugeeModel.prototype.removeRefugee = function(r) {
// 	var index = this.activeRefugees.indexOf(r);
// 	if (index == -1) {
// 		console.log("failaa" + r);
// 	} else {
// 		this.activeRefugees.splice(index, 1);
// 	}
// }

RefugeeModel.prototype.updateActiveRefugees = function() {
	// filter out the ones that have arrived
	this.activeRefugees = this.activeRefugees.filter(function(r) {
		return r.arrived === false;
	});

	// add new ones
	do {
		var r = this.refugees[this.refugeeIndex];
		if (r != null && r.isPastStartMoment(this.currentMoment)) {
			this.activeRefugees.push(r);
			this.refugeeIndex++;
		} else {
			return;
		}
	} while (true);
}

RefugeeModel.prototype.addRefugees = function(startCountry, endCountry, count, month, year) {
	_.times(Math.round(count), function() { // should it be Math.floor?
		this.refugees.push(this.createRefugee(startCountry, endCountry, month, year));
	}.bind(this));
}

RefugeeModel.prototype.kmhToDegsPerH = function(kmh) {
	return kmh / 111;
}

/*
 * Return the center point of the given country
 */
RefugeeModel.prototype.createCenterCountryPoint = function(country) {
	var feature = utils.getFeatureForCountry(this.fc, country);
	if (feature == null) {
		throw "could not find feature for " + country;
	}
	return utils.getCenterPointForCountryBorderFeature(feature);
}

/*
 * Create a random point within the given country
 */
RefugeeModel.prototype.createRandomCountryPoint = function(country) {
	var feature = utils.getFeatureForCountry(this.fc, country);
	if (feature == null) {
		throw "could not find feature for " + country;
	}
	return utils.getCenterPointForCountryBorderFeature(feature);
}

function daysInMonth(month,year) {
	return new Date(year, month, 0).getDate();
}

/*
 * Get a speed for a new refugee in km / h;
 */
RefugeeModel.prototype.prepareRefugeeSpeed = function() {
	return Math.random() * 2 + 4;
}

RefugeeModel.prototype.prepareRefugeeEndMoment = function(month, year) {
	var days = daysInMonth(month, year);
	var mom = moment(new Date(year, month, 1));

	var maxSeconds = days * 24 * 60 * 60;
	var diff = Math.random() * maxSeconds;
	mom.add(diff, 'seconds');
	return mom;
}

RefugeeModel.prototype.createRefugee = function(startCountry, endCountry, month, year) {
	var isEu = (EU_COUNTRIES.indexOf(endCountry) > -1);
	var r = new Refugee(
		this.createRandomCountryPoint(startCountry),
		this.createCenterCountryPoint(endCountry),
		this.prepareRefugeeSpeed(),
		this.prepareRefugeeEndMoment(month, year),
		isEu
	);
	return r;
}

module.exports = RefugeeModel;

// // kludge
// RefugeeModel.prototype.createSyrianPoint = function() {
// 	return utils.getRandomPoint(this.fc.features[1].geometry.coordinates[0]);
// }

// // kludge
// RefugeeModel.prototype.createFinnishPoint = function() {
// 	return utils.getRandomPoint(utils.getLargestPolygon(this.fc.features[0].geometry.coordinates));
// }<|endoftext|>


<commit_before>var util         = require('util');
var utils        = require('./utils');
var EventEmitter = require('events').EventEmitter;

/**
 *  @constructor Server
 *  @param {Remote} Reference to a Remote object
 *  @param {Object} Options
 *
 *    host:   String
 *    port:   String or Number
 *    secure: Boolean
 */

function Server(remote, opts) {
  EventEmitter.call(this);

  if (typeof opts !== 'object') {
    throw new Error('Invalid server configuration.');
  }

  var self = this;

  this._remote        = remote;
  this._opts          = opts;
  this._host          = opts.host;
  this._port          = opts.port;
  this._secure        = (typeof opts.secure === 'boolean') ? opts.secure : true;
  this._ws            = void(0);
  this._connected     = false;
  this._shouldConnect = false;
  this._state         = 'offline';
  this._id            = 0;
  this._retry         = 0;
  this._requests      = { };

  this._opts.url = (opts.secure ? 'wss://' : 'ws://') + opts.host + ':' + opts.port;

  this.on('message', function(message) {
    self._handleMessage(message);
  });

  this.on('response_subscribe', function(message) {
    self._handleResponseSubscribe(message);
  });

  function checkServerActivity() {
    if (isNaN(self._lastLedgerClose)) return;

    var delta = (Date.now() - self._lastLedgerClose);

    if (delta > (1000 * 20)) {
      self.reconnect();
    }
  };

  function setActivityInterval() {
    self._activityInterval = setInterval(checkServerActivity, 1000);
  };

  this.on('disconnect', function onDisconnect() {
    clearInterval(self._activityInterval);
    //self.once('ledger_closed', setActivityInterval);
  });

  this.once('ledger_closed', function() {
    //setActiviyInterval();
  });
};

util.inherits(Server, EventEmitter);

/** This is the final interface between client code and a socket connection to a
 * `rippled` server. As such, this is a decent hook point to allow a WebSocket
 * interface conforming object to be used as a basis to mock rippled. This
 * avoids the need to bind a websocket server to a port and allows a more
 * synchronous style of code to represent a client <-> server message sequence.
 * We can also use this to log a message sequence to a buffer.
 *
 * @api private
 */

Server.websocketConstructor = function() {
  // We require this late, because websocket shims may be loaded after
  // ripple-lib in the browser
  return require('ws');
};

/**
 * Server states that we will treat as the server being online.
 *
 * Our requirements are that the server can process transactions and notify
 * us of changes.
 */

Server.onlineStates = [
  'syncing',
  'tracking',
  'proposing',
  'validating',
  'full'
];

/**
 * Set server state
 *
 * @param {String} state
 */

Server.prototype._setState = function(state) {
  if (state !== this._state) {
    this._remote._trace('server: set_state: %s', state);

    this._state = state;

    this.emit('state', state);

    switch (state) {
      case 'online':
        this._connected = true;
        this.emit('connect');
        break;
      case 'offline':
        this._connected = false;
        this.emit('disconnect');
        break;
    }
  }
};

/**
 * Get the remote address for a server.
 * Incompatible with ripple-lib client build
 */

Server.prototype._remoteAddress = function() {
  try { var address = this._ws._socket.remoteAddress; } catch (e) { }
  return address;
};

/**
 * Connect to rippled WebSocket server and subscribe to events that are
 * internally requisite. Automatically retry connections with a gradual
 * back-off
 *
 * @api public
 */

Server.prototype.connect = function() {
  var self = this;

  // We don't connect if we believe we're already connected. This means we have
  // recently received a message from the server and the WebSocket has not
  // reported any issues either. If we do fail to ping or the connection drops,
  // we will automatically reconnect.
  if (this._connected) return;

  this._remote._trace('server: connect: %s', this._opts.url);

  // Ensure any existing socket is given the command to close first.
  if (this._ws) this._ws.close();

  var WebSocket = Server.websocketConstructor();

  if (!WebSocket) {
    throw new Error("No websocket support detected!");
  }

  var ws = this._ws = new WebSocket(this._opts.url);

  this._shouldConnect = true;

  self.emit('connecting');

  ws.onopen = function() {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self.emit('socket_open');
      // Subscribe to events
      self.request(self._remote._serverPrepareSubscribe());
    }
  };

  ws.onmessage = function onMessage(msg) {
    self.emit('message', msg.data);
  };

  ws.onerror = function(e) {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self.emit('socket_error');
      self._remote._trace('server: onerror: %s %s', self._opts.url, e.data || e);

      // Most connection errors for WebSockets are conveyed as 'close' events with
      // code 1006. This is done for security purposes and therefore unlikely to
      // ever change.

      // This means that this handler is hardly ever called in practice. If it is,
      // it probably means the server's WebSocket implementation is corrupt, or
      // the connection is somehow producing corrupt data.

      // Most WebSocket applications simply log and ignore this error. Once we
      // support for multiple servers, we may consider doing something like
      // lowering this server's quality score.

      // However, in Node.js this event may be triggered instead of the close
      // event, so we need to handle it.
      handleConnectionClose();
    }
  };

  // Failure to open.
  ws.onclose = function() {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self._remote._trace('server: onclose: %s %s', self._opts.url, ws.readyState);
      handleConnectionClose();
    }
  };

  function handleConnectionClose() {
    self.emit('socket_close');
    self._setState('offline');

    // Prevent additional events from this socket
    ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function() {};

    // Should we be connected?
    if (!self._shouldConnect) return;

    // Delay and retry.
    self._retry += 1;

    var retryTimeout = (self._retry < 40)
        ? 1000 / 20            // First, for 2 seconds: 20 times per second
        : (self._retry < 40 + 60)
          ? 1000               // Then, for 1 minute: once per second
          : (self._retry < 40 + 60 + 60)
            ? (10 * 1000)      // Then, for 10 minutes: once every 10 seconds
            : (30 * 1000);     // Then: once every 30 seconds

    function connectionRetry() {
      if (self._shouldConnect) {
        self._remote._trace('server: retry %s', self._opts.url);
        self.connect();
      }
    };

    self._retry_timer = setTimeout(connectionRetry, retryTimeout);
  };
};

/**
 * Disconnect from rippled WebSocket server
 *
 * @api public
 */

Server.prototype.disconnect = function() {
  this._shouldConnect = false;
  this._setState('offline');
  if (this._ws) this._ws.close();
};

/**
 * Reconnect to rippled WebSocket server
 */

Server.prototype.reconnect = function() {
  if (this._ws) {
    this.once('disconnect', this.connect.bind(this));
    this.disconnect();
  }
};

/**
 * Submit a Request object.
 *
 * Requests are indexed by message ID, which is repeated
 * in the response from rippled WebSocket server
 *
 * @param {Request} request
 * @api public
 */

Server.prototype.request = function(request) {
  var self  = this;

  // Only bother if we are still connected.
  if (!this._ws) {
    this._remote._trace('server: request: DROPPING: %s %s', self._opts.url, request.message);
    return;
  }

  request.server = this;
  request.message.id = this._id;

  this._requests[request.message.id] = request;

  // Advance message ID
  this._id++;

  var is_connected = this._connected || (request.message.command === 'subscribe' && this._ws.readyState === 1);

  if (is_connected) {
    this.sendMessage(request.message);
  } else {
    // XXX There are many ways to make this smarter.
    function serverReconnected() {
      self.sendMessage(request.message);
    }
    this.once('connect', serverReconnected);
  }
};

/**
 * Send JSON message to rippled WebSocket server
 *
 * @param {JSON-Stringifiable} message
 */

Server.prototype.sendMessage = function(message) {
  if (this._ws) {
    this._remote._trace('server: request: %s, %s', this._opts.url, message);
    this._ws.send(JSON.stringify(message));
  }
};

/**
 * Handle incoming messages from rippled WebSocket server
 *
 * @param {JSON-parseable} message
 * @api private
 */

Server.prototype._handleMessage = function(message) {
  var self = this;

  try { message = JSON.parse(message); } catch(e) { }

  var isValid = message && (typeof message === 'object') && (typeof message.type === 'string');

  if (!isValid) return;

  this.emit('before_receive_message', message)

  switch (message.type) {
    case 'server_status':
      // This message is only received when online.
      // As we are connected, it is the definitive final state.
      this._setState(~(Server._onlineStates.indexOf(message.server_status)) ? 'online' : 'offline');
      break;

    case 'ledgerClosed':
      this._lastLedgerClose = Date.now();
      this.emit('ledger_closed', message);
      break;

    case 'path_find':
      this._remote._trace('server: path_find: %s %s', self._opts.url, message);
      break;

    case 'response':
      // A response to a request.
      var request = self._requests[message.id];
      delete self._requests[message.id];

      if (!request) {
        this._remote._trace('server: UNEXPECTED: %s %s', self._opts.url, message);
      } else if (message.status === 'success') {
        this._remote._trace('server: response: %s %s', self._opts.url, message);

        request.emit('success', message.result);

        [ self, self._remote ].forEach(function(emitter) {
          emitter.emit('response_' + request.message.command, message.result, request, message);
        });
      } else if (message.error) {
        this._remote._trace('server: error: %s %s', self._opts.url, message);

        request.emit('error', {
          error         : 'remoteError',
          error_message : 'Remote reported an error.',
          remote        : message
        });
      }
      break;
  }
};

/**
 * Handle subscription response messages. Subscription response
 * messages indicate that a connection to the server is ready
 */

Server.prototype._handleResponseSubscribe = function(message) {
  if (~(Server.onlineStates.indexOf(message.server_status))) {
    this._setState('online');
  }
};

exports.Server = Server;

// vim:sw=2:sts=2:ts=8:et
<commit_msg>Server logging<commit_after>var util         = require('util');
var utils        = require('./utils');
var EventEmitter = require('events').EventEmitter;

/**
 *  @constructor Server
 *  @param {Remote} Reference to a Remote object
 *  @param {Object} Options
 *
 *    host:   String
 *    port:   String or Number
 *    secure: Boolean
 */

function Server(remote, opts) {
  EventEmitter.call(this);

  if (typeof opts !== 'object') {
    throw new Error('Invalid server configuration.');
  }

  var self = this;

  this._remote        = remote;
  this._opts          = opts;
  this._host          = opts.host;
  this._port          = opts.port;
  this._secure        = (typeof opts.secure === 'boolean') ? opts.secure : true;
  this._ws            = void(0);
  this._connected     = false;
  this._shouldConnect = false;
  this._state         = 'offline';
  this._id            = 0;
  this._retry         = 0;
  this._requests      = { };

  this._opts.url = (opts.secure ? 'wss://' : 'ws://') + opts.host + ':' + opts.port;

  this.on('message', function(message) {
    self._handleMessage(message);
  });

  this.on('response_subscribe', function(message) {
    self._handleResponseSubscribe(message);
  });

  function checkServerActivity() {
    if (isNaN(self._lastLedgerClose)) return;

    var delta = (Date.now() - self._lastLedgerClose);

    if (delta > (1000 * 20)) {
      self.reconnect();
    }
  };

  function setActivityInterval() {
    self._activityInterval = setInterval(checkServerActivity, 1000);
  };

  this.on('disconnect', function onDisconnect() {
    clearInterval(self._activityInterval);
    //self.once('ledger_closed', setActivityInterval);
  });

  this.once('ledger_closed', function() {
    //setActiviyInterval();
  });
};

util.inherits(Server, EventEmitter);

/** This is the final interface between client code and a socket connection to a
 * `rippled` server. As such, this is a decent hook point to allow a WebSocket
 * interface conforming object to be used as a basis to mock rippled. This
 * avoids the need to bind a websocket server to a port and allows a more
 * synchronous style of code to represent a client <-> server message sequence.
 * We can also use this to log a message sequence to a buffer.
 *
 * @api private
 */

Server.websocketConstructor = function() {
  // We require this late, because websocket shims may be loaded after
  // ripple-lib in the browser
  return require('ws');
};

/**
 * Server states that we will treat as the server being online.
 *
 * Our requirements are that the server can process transactions and notify
 * us of changes.
 */

Server.onlineStates = [
  'syncing',
  'tracking',
  'proposing',
  'validating',
  'full'
];

/**
 * Set server state
 *
 * @param {String} state
 */

Server.prototype._setState = function(state) {
  if (state !== this._state) {
    this._remote._trace('server: set_state:', state);

    this._state = state;

    this.emit('state', state);

    switch (state) {
      case 'online':
        this._connected = true;
        this.emit('connect');
        break;
      case 'offline':
        this._connected = false;
        this.emit('disconnect');
        break;
    }
  }
};

/**
 * Get the remote address for a server.
 * Incompatible with ripple-lib client build
 */

Server.prototype._remoteAddress = function() {
  try { var address = this._ws._socket.remoteAddress; } catch (e) { }
  return address;
};

/**
 * Connect to rippled WebSocket server and subscribe to events that are
 * internally requisite. Automatically retry connections with a gradual
 * back-off
 *
 * @api public
 */

Server.prototype.connect = function() {
  var self = this;

  // We don't connect if we believe we're already connected. This means we have
  // recently received a message from the server and the WebSocket has not
  // reported any issues either. If we do fail to ping or the connection drops,
  // we will automatically reconnect.
  if (this._connected) return;

  this._remote._trace('server: connect:', this._opts.url);

  // Ensure any existing socket is given the command to close first.
  if (this._ws) this._ws.close();

  var WebSocket = Server.websocketConstructor();

  if (!WebSocket) {
    throw new Error('No websocket support detected!');
  }

  var ws = this._ws = new WebSocket(this._opts.url);

  this._shouldConnect = true;

  self.emit('connecting');

  ws.onopen = function() {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self.emit('socket_open');
      // Subscribe to events
      self.request(self._remote._serverPrepareSubscribe());
    }
  };

  ws.onmessage = function onMessage(msg) {
    self.emit('message', msg.data);
  };

  ws.onerror = function(e) {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self.emit('socket_error');
      self._remote._trace('server: onerror:', self._opts.url, e.data || e);

      // Most connection errors for WebSockets are conveyed as 'close' events with
      // code 1006. This is done for security purposes and therefore unlikely to
      // ever change.

      // This means that this handler is hardly ever called in practice. If it is,
      // it probably means the server's WebSocket implementation is corrupt, or
      // the connection is somehow producing corrupt data.

      // Most WebSocket applications simply log and ignore this error. Once we
      // support for multiple servers, we may consider doing something like
      // lowering this server's quality score.

      // However, in Node.js this event may be triggered instead of the close
      // event, so we need to handle it.
      handleConnectionClose();
    }
  };

  // Failure to open.
  ws.onclose = function() {
    // If we are no longer the active socket, simply ignore any event
    if (ws === self._ws) {
      self._remote._trace('server: onclose:', self._opts.url, ws.readyState);
      handleConnectionClose();
    }
  };

  function handleConnectionClose() {
    self.emit('socket_close');
    self._setState('offline');

    // Prevent additional events from this socket
    ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function() {};

    // Should we be connected?
    if (!self._shouldConnect) return;

    // Delay and retry.
    self._retry += 1;

    var retryTimeout = (self._retry < 40)
        ? 1000 / 20            // First, for 2 seconds: 20 times per second
        : (self._retry < 40 + 60)
          ? 1000               // Then, for 1 minute: once per second
          : (self._retry < 40 + 60 + 60)
            ? (10 * 1000)      // Then, for 10 minutes: once every 10 seconds
            : (30 * 1000);     // Then: once every 30 seconds

    function connectionRetry() {
      if (self._shouldConnect) {
        self._remote._trace('server: retry', self._opts.url);
        self.connect();
      }
    };

    self._retry_timer = setTimeout(connectionRetry, retryTimeout);
  };
};

/**
 * Disconnect from rippled WebSocket server
 *
 * @api public
 */

Server.prototype.disconnect = function() {
  this._shouldConnect = false;
  this._setState('offline');
  if (this._ws) this._ws.close();
};

/**
 * Reconnect to rippled WebSocket server
 */

Server.prototype.reconnect = function() {
  if (this._ws) {
    this.once('disconnect', this.connect.bind(this));
    this.disconnect();
  }
};

/**
 * Submit a Request object.
 *
 * Requests are indexed by message ID, which is repeated
 * in the response from rippled WebSocket server
 *
 * @param {Request} request
 * @api public
 */

Server.prototype.request = function(request) {
  var self  = this;

  // Only bother if we are still connected.
  if (!this._ws) {
    this._remote._trace('server: request: DROPPING:', self._opts.url, request.message);
    return;
  }

  request.server = this;
  request.message.id = this._id;

  this._requests[request.message.id] = request;

  // Advance message ID
  this._id++;

  var is_connected = this._connected || (request.message.command === 'subscribe' && this._ws.readyState === 1);

  if (is_connected) {
    this.sendMessage(request.message);
  } else {
    // XXX There are many ways to make this smarter.
    function serverReconnected() {
      self.sendMessage(request.message);
    }
    this.once('connect', serverReconnected);
  }
};

/**
 * Send JSON message to rippled WebSocket server
 *
 * @param {JSON-Stringifiable} message
 */

Server.prototype.sendMessage = function(message) {
  if (this._ws) {
    this._remote._trace('server: request:', this._opts.url, message);
    this._ws.send(JSON.stringify(message));
  }
};

/**
 * Handle incoming messages from rippled WebSocket server
 *
 * @param {JSON-parseable} message
 * @api private
 */

Server.prototype._handleMessage = function(message) {
  var self = this;

  try { message = JSON.parse(message); } catch(e) { }

  var isValid = message && (typeof message === 'object') && (typeof message.type === 'string');

  if (!isValid) return;

  this.emit('before_receive_message', message)

  switch (message.type) {
    case 'server_status':
      // This message is only received when online.
      // As we are connected, it is the definitive final state.
      this._setState(~(Server._onlineStates.indexOf(message.server_status)) ? 'online' : 'offline');
      break;

    case 'ledgerClosed':
      this._lastLedgerClose = Date.now();
      this.emit('ledger_closed', message);
      break;

    case 'path_find':
      this._remote._trace('server: path_find:', self._opts.url, message);
      break;

    case 'response':
      // A response to a request.
      var request = self._requests[message.id];
      delete self._requests[message.id];

      if (!request) {
        this._remote._trace('server: UNEXPECTED:', self._opts.url, message);
      } else if (message.status === 'success') {
        this._remote._trace('server: response:', self._opts.url, message);

        request.emit('success', message.result);

        [ self, self._remote ].forEach(function(emitter) {
          emitter.emit('response_' + request.message.command, message.result, request, message);
        });
      } else if (message.error) {
        this._remote._trace('server: error:', self._opts.url, message);

        request.emit('error', {
          error         : 'remoteError',
          error_message : 'Remote reported an error.',
          remote        : message
        });
      }
      break;
  }
};

/**
 * Handle subscription response messages. Subscription response
 * messages indicate that a connection to the server is ready
 */

Server.prototype._handleResponseSubscribe = function(message) {
  if (~(Server.onlineStates.indexOf(message.server_status))) {
    this._setState('online');
  }
};

exports.Server = Server;

// vim:sw=2:sts=2:ts=8:et
<|endoftext|>


<commit_before>var _ = require('lodash');
var async = require('async');

module.exports = widget;

function widget(options) {
  return new widget.Widget(options);
}

widget.Widget = function(options) {
  var self = this;
  self.apos = options.apos;
  self.snippets = options.snippets;
  self.app = options.app;
  self.icon = options.icon;

  self.name = options.name || 'snippets';
  self.label = options.label || 'Snippets';

  // One asset folder for the whole snippets module is fine
  self.pushAsset = function(type, name, options) {
    self.snippets.pushAsset(type, name, options);
  };

  // This widget should be part of the default set of widgets for areas
  // (note devs can still override the list)
  self.apos.defaultControls.push(self.name);

  // For use in titling the "type part of a title" field
  var titleField = _.find(self.snippets.schema, function(field) {
    return field.name === 'title';
  }) || { label: 'Title' };

  // Include our editor template in the markup when aposTemplates is called
  self.pushAsset('template', 'widgetEditor', {
    when: 'user',
    data: {
      widgetEditorClass: 'apos-' + self.snippets._pluralCss + '-widget-editor',
      instanceLabel: self.snippets.instanceLabel,
      pluralLabel: self.snippets.pluralLabel,
      titleLabel: titleField.label || 'Title'
    }
  });

  // So far we've always kept this in the same file with the rest of the module's CSS,
  // so don't clutter up the console with 404s in dev
  // self.pushAsset('stylesheet', 'widget');

  self.addCriteria = function(item, criteria, options) {
    if ((item.by === 'tag') && (item.tags)) {
      if (item.tags.length) {
        criteria.tags = { $in: item.tags };
      }
      if (item.limit) {
        options.limit = item.limit;
      } else {
        // Always set an upper limit
        options.limit = 1000;
      }
    } else if ((item.by === 'id') && (item.ids)) {
      // Specific IDs were selected, do not look at the limit
      criteria._id = { $in: item.ids };
    }
  };

  self.widget = true;
  self.css = self.apos.cssName(self.name);
  self.sanitize = function(item) {
    item.by += '';
    item.tags = self.apos.sanitizeTags(item.tags);
    if (!Array.isArray(item.ids)) {
      item.ids = [];
    }
    item.ids = _.map(item.ids, function(id) {
      // Must be string
      id = self.apos.sanitizeString(id);
      return id;
    });
    item.limit = self.apos.sanitizeInteger(item.limit, 5, 1, 1000);
  };

  self.renderWidget = function(data) {
    return self.snippets.render('widget', data);
  };

  // Snippet text contributes to the plaintext of a page
  self.getPlaintext = function(item, lines) {
    var s = '';
    _.each(item._snippets, function(snippet) {
      s += self.apos.getSearchTextsForPage(snippet) + "\n";
    });
    return s;
  };

  // Snippet text contributes to search text of page only if the link is
  // firm - made via id - and not dynamic - made via tag
  self.addSearchTexts = function(item, texts) {
    if (item.by === 'id') {
      _.each(item._snippets, function(snippet) {
        var pageTexts = self.apos.getSearchTextsForPage(snippet);
        // We have to do this because we are updating texts by reference
        _.each(pageTexts, function (text) {
          texts.push(text);
        });
      });
    }
  };

  self.addDiffLines = function(item, lines) {
    if (item.by === 'id') {
      lines.push(self.label + ': items selected: ' + ((item.ids && item.ids.length) || 0));
    } else {
      lines.push(self.label + ': tags selected: ' + item.tags.join(', '));
    }
  };

  // Asynchronously load the content of the snippets we're reusing.
  // The properties you add should start with an _ to denote that
  // they shouldn't become data attributes or get stored back to MongoDB

  self.load = function(req, item, callback) {
    var criteria = {};
    var options = {};

    self.addCriteria(item, criteria, options);

    // If the criteria are simple enough check a local cache that is kept
    // for the duration of this request. This can lead to a large speedup
    // if there are many snippet widgets in the content being displayed that
    // reference the same content. This simplicity test keeps us out of trouble
    // if something that can't be serialized neatly as JSON is part of a
    // criteria object in some subclass of snippets (examples: dates, regexes)

    var key;
    var cacheable = true;
    for (key in criteria) {
      if ((key !== '_id') && (key !== 'tags')) {
        cacheable = false;
      }
    }

    if (cacheable) {
      key = JSON.stringify(criteria);

      if (req.aposSnippetLoadCache && req.aposSnippetLoadCache[key]) {
        item._snippets = req.aposSnippetLoadCache[key];
        return setImmediate(function() {
          return callback(null);
        });
      }
    }

    return self.snippets.get(req, criteria, options, function(err, results) {
      if (err) {
        item._snippets = [];
        console.log(err);
        return callback(err);
      }
      var snippets = results.snippets;
      if (item.by === 'id') {
        snippets = self.apos.orderById(item.ids, snippets);
        // Put them in the same order as the ids that were manually selected
        // (you can't do this with mongodb sort, so we do it here)
        var snippetsById = {};
        _.each(snippets, function(snippet) {
          snippetsById[snippet._id] = snippet;
        });
        snippets = [];
        _.each(item.ids, function(id) {
          // Careful, we must politely ignore ids that don't exist anymore
          if (snippetsById.hasOwnProperty(id)) {
            snippets.push(snippetsById[id]);
          }
        });
      }
      self.snippets.addUrls(req, snippets, send);
      function send(err) {
        if (err) {
          return callback(err);
        }
        if (cacheable) {
          req.aposSnippetLoadCache = req.aposSnippetLoadCache || {};
          req.aposSnippetLoadCache[key] = snippets;
        }
        item._snippets = snippets;
        return callback(null);
      }
    });
  };

  self.empty = function(item) {
    return (!item._snippets) || (!item._snippets.length);
  };
};

<commit_msg>Cache key must include name of widget<commit_after>var _ = require('lodash');
var async = require('async');

module.exports = widget;

function widget(options) {
  return new widget.Widget(options);
}

widget.Widget = function(options) {
  var self = this;
  self.apos = options.apos;
  self.snippets = options.snippets;
  self.app = options.app;
  self.icon = options.icon;

  self.name = options.name || 'snippets';
  self.label = options.label || 'Snippets';

  // One asset folder for the whole snippets module is fine
  self.pushAsset = function(type, name, options) {
    self.snippets.pushAsset(type, name, options);
  };

  // This widget should be part of the default set of widgets for areas
  // (note devs can still override the list)
  self.apos.defaultControls.push(self.name);

  // For use in titling the "type part of a title" field
  var titleField = _.find(self.snippets.schema, function(field) {
    return field.name === 'title';
  }) || { label: 'Title' };

  // Include our editor template in the markup when aposTemplates is called
  self.pushAsset('template', 'widgetEditor', {
    when: 'user',
    data: {
      widgetEditorClass: 'apos-' + self.snippets._pluralCss + '-widget-editor',
      instanceLabel: self.snippets.instanceLabel,
      pluralLabel: self.snippets.pluralLabel,
      titleLabel: titleField.label || 'Title'
    }
  });

  // So far we've always kept this in the same file with the rest of the module's CSS,
  // so don't clutter up the console with 404s in dev
  // self.pushAsset('stylesheet', 'widget');

  self.addCriteria = function(item, criteria, options) {
    if ((item.by === 'tag') && (item.tags)) {
      if (item.tags.length) {
        criteria.tags = { $in: item.tags };
      }
      if (item.limit) {
        options.limit = item.limit;
      } else {
        // Always set an upper limit
        options.limit = 1000;
      }
    } else if ((item.by === 'id') && (item.ids)) {
      // Specific IDs were selected, do not look at the limit
      criteria._id = { $in: item.ids };
    }
  };

  self.widget = true;
  self.css = self.apos.cssName(self.name);
  self.sanitize = function(item) {
    item.by += '';
    item.tags = self.apos.sanitizeTags(item.tags);
    if (!Array.isArray(item.ids)) {
      item.ids = [];
    }
    item.ids = _.map(item.ids, function(id) {
      // Must be string
      id = self.apos.sanitizeString(id);
      return id;
    });
    item.limit = self.apos.sanitizeInteger(item.limit, 5, 1, 1000);
  };

  self.renderWidget = function(data) {
    return self.snippets.render('widget', data);
  };

  // Snippet text contributes to the plaintext of a page
  self.getPlaintext = function(item, lines) {
    var s = '';
    _.each(item._snippets, function(snippet) {
      s += self.apos.getSearchTextsForPage(snippet) + "\n";
    });
    return s;
  };

  // Snippet text contributes to search text of page only if the link is
  // firm - made via id - and not dynamic - made via tag
  self.addSearchTexts = function(item, texts) {
    if (item.by === 'id') {
      _.each(item._snippets, function(snippet) {
        var pageTexts = self.apos.getSearchTextsForPage(snippet);
        // We have to do this because we are updating texts by reference
        _.each(pageTexts, function (text) {
          texts.push(text);
        });
      });
    }
  };

  self.addDiffLines = function(item, lines) {
    if (item.by === 'id') {
      lines.push(self.label + ': items selected: ' + ((item.ids && item.ids.length) || 0));
    } else {
      lines.push(self.label + ': tags selected: ' + item.tags.join(', '));
    }
  };

  // Asynchronously load the content of the snippets we're reusing.
  // The properties you add should start with an _ to denote that
  // they shouldn't become data attributes or get stored back to MongoDB

  self.load = function(req, item, callback) {
    var criteria = {};
    var options = {};

    self.addCriteria(item, criteria, options);

    // If the criteria are simple enough check a local cache that is kept
    // for the duration of this request. This can lead to a large speedup
    // if there are many snippet widgets in the content being displayed that
    // reference the same content. This simplicity test keeps us out of trouble
    // if something that can't be serialized neatly as JSON is part of a
    // criteria object in some subclass of snippets (examples: dates, regexes)

    var key;
    var cacheable = true;
    for (key in criteria) {
      if ((key !== '_id') && (key !== 'tags')) {
        cacheable = false;
      }
    }

    if (cacheable) {
      // Make sure the type is part of the key!
      key = self.name + '#' + JSON.stringify(criteria);
      if (req.aposSnippetLoadCache && req.aposSnippetLoadCache[key]) {
        item._snippets = req.aposSnippetLoadCache[key];
        return setImmediate(function() {
          return callback(null);
        });
      }
    }

    return self.snippets.get(req, criteria, options, function(err, results) {
      if (err) {
        item._snippets = [];
        console.log(err);
        return callback(err);
      }
      var snippets = results.snippets;
      if (item.by === 'id') {
        snippets = self.apos.orderById(item.ids, snippets);
        // Put them in the same order as the ids that were manually selected
        // (you can't do this with mongodb sort, so we do it here)
        var snippetsById = {};
        _.each(snippets, function(snippet) {
          snippetsById[snippet._id] = snippet;
        });
        snippets = [];
        _.each(item.ids, function(id) {
          // Careful, we must politely ignore ids that don't exist anymore
          if (snippetsById.hasOwnProperty(id)) {
            snippets.push(snippetsById[id]);
          }
        });
      }
      self.snippets.addUrls(req, snippets, send);
      function send(err) {
        if (err) {
          return callback(err);
        }
        if (cacheable) {
          req.aposSnippetLoadCache = req.aposSnippetLoadCache || {};
          req.aposSnippetLoadCache[key] = snippets;
        }
        item._snippets = snippets;
        return callback(null);
      }
    });
  };

  self.empty = function(item) {
    return (!item._snippets) || (!item._snippets.length);
  };
};

<|endoftext|>


<commit_before>export class Enemy extends Phaser.Sprite {
    constructor(game, x, y) {
        super(game, x, y, 'enemy');
        this.anchor.setTo(0.5);
        this.game.add.existing(this);
        this.game.physics.arcade.enable(this);

        // Disable gravity because it's flying
        this.body.allowGravity = false;

        // Create a patrol behavior with a simple yoyo tween
        this.movementTween = this.game.add.tween(this).to({ x: "+200" }, 800, "Linear", true, 0, -1);
        this.movementTween.yoyo(true, 0);
    }
}
<commit_msg>Refactor and clean code<commit_after>export class Enemy extends Phaser.Sprite {
    constructor(game, x, y) {
        super(game, x, y, 'enemy');
        this.anchor.setTo(0.5);
        this.game.add.existing(this);
        this.game.physics.arcade.enable(this);

        // Disable gravity because it's flying
        this.body.allowGravity = false;

        // Start patrol behavior by default
        // TODO: using constants or as a difficulty params
        this.startPatrolBehavior(200, 800);
    }

    // Patrols between current position covering a distance (px) in time (ms)
    startPatrolBehavior(distance, time) {
        // Create a patrol behavior using a simple yoyo tween
        this.movementTween = this.game.add.tween(this).to({ x: distance }, time, "Linear", true, 0, -1);
        this.movementTween.yoyo(true, 0);
    }
}
<|endoftext|>


<commit_before>/*
   _            _     __
  | |_ __ _ ___| | __/ _| ___  _ __ ___ ___
  | __/ _` / __| |/ / |_ / _ \| '__/ __/ _ \
  | || (_| \__ \   <|  _| (_) | | | (_|  __/
   \__\__,_|___/_|\_\_|  \___/|_|  \___\___|

  ===============================================================================

  A generic widget include script which we will use to show further campaigns etc

  <thomasalwyndavis@gmail.com> or http://taskforce.is for support

  ===============================================================================

  TODO
    - Use underscores or camelcase?

*/

// Wrap widget in function to protect scope
var _tfrce_config = (typeof tfrce_config  !== 'undefined') ? tfrce_config  : {};

(function(window, widget_config){

  // Do configuration

  widget_config.show_style = widget_config.show_style || 'center_modal';
  widget_config.disableGeo = widget_config.disableGeo || false;
  widget_config.debug = widget_config.debug || false;
  widget_config.disableDate = widget_config.disableDate || false;
  widget_config.campaign = widget_config.campaign || 'stopwatchingus';
  widget_config.cookieTimeout = widget_config.cookieTimeout || 86400;

  // Setup
  var active_campaign;
  var ASSET_URL, COOKIE_TIMEOUT;

  if(widget_config.debug) {
    ASSET_URL = '../stopwatchingus/';
    COOKIE_TIMEOUT = 20;
  } else {
    ASSET_URL = '//widget.taskforce.is/widget/stopwatchingus/';
    COOKIE_TIMEOUT = widget_config.cookieTimeout;
  }

  // Cookie helpers, taken from w3schools
  function setCookie(c_name,value,seconds) {
    var exdate = new Date(new Date().getTime() + seconds*1000);
    console.log(new Date(), exdate);
    var c_value=escape(value) + ((seconds==null) ? "" : "; expires="+exdate.toUTCString());
    document.cookie=c_name + "=" + c_value;
  }

  function getCookie(c_name) {
    var c_value = document.cookie;
    var c_start = c_value.indexOf(" " + c_name + "=");
    if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); };
    if (c_start == -1) {
      c_value = null;
    } else {
      c_start = c_value.indexOf("=", c_start) + 1;
      var c_end = c_value.indexOf(";", c_start);
      if (c_end == -1) { c_end = c_value.length; }
      c_value = unescape(c_value.substring(c_start,c_end));
    }
    return c_value;
  }

  // Define checks

  var checks = {
    betweenDate: function(startDate, endDate) {
      var startEpoch = startDate.getTime();
      var endEpoch = endDate.getTime();
      var currentEpoch = new Date().getTime();
      if(currentEpoch > startEpoch && currentEpoch < endEpoch) {
        return true;
      } else {
        return false;
      }
    },
    isMobile: function() {
      var ismobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i);
      return ismobile ? false : true;
    },
    hasSeenCampaign: function (cookieName) {
      var cookie = getCookie(cookieName)
      if(cookie === null) {
        return false;
      } else {
        return true
      }
    },
    nearDC: function (callback) {
      window.nearDC = callback;
      var script = document.createElement('script');
      script.src = '//geoip.taskforce.is/?callback=nearDC'
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  }

  // Define campaigns

  var campaign = {
    stopwatchingus: {
      cookieName: 'stopwatchingus_hasseen6',
      startDate: new Date(2011, 10, 30, 0),
      endDate: new Date(2014, 10, 30, 0),
      hide: function (el, callback) {
        el.remove();
        setCookie(active_campaign.cookieName, 'true', COOKIE_TIMEOUT);
        if(callback) { callback(); };
      },
      styles: {
        modal: {
          campaign_container: '',
          iframe_container: '',
          iframe: '',
          closeButton: ''
        },
        banner: {
          campaign_container: 'position:fixed;width:100%;bottom:0;z-index:100; padding: 0 20px;',
          iframe_container: 'position: relative; height: 100px; max-width:1201px; margin: 0px auto 8px auto; background: #444;border-radius: 10px;',
          iframe: 'width: 100%;height: 100%;border: 0;margin:0;padding:0;border-radius: 10px;',
          closeButton: 'border: 0;height: 28px;width: 28px;cursor: pointer;position: absolute;top:33px;right:20px;background: url("' + ASSET_URL +'images/close-button.png");'
        },
        dev: {
          campaign_container: 'position:fixed;width:100%;bottom:0;z-index:100; padding: 0 20px;',
          iframe_container: 'position: relative; height: 100px; max-width:1201px; margin: 0px auto 8px auto; background: #444;border-radius: 10px;',
          iframe: 'width: 100%;height: 100%;border: 0;margin:0;padding:0;border-radius: 10px;',
          closeButton: 'border: 0;height: 28px;width: 28px;cursor: pointer;position: absolute;top:33px;right:20px;background: url("' + ASSET_URL +'images/close-button.png");'
        },
        strip: {
          campaign_container: 'position:fixed;width:100%;bottom:0;opacity:1;z-index:100;',
          iframe_container: 'position:relative;height:50px;margin:0 20px 0px 20px;background:#444;border-radius:10px 10px 0 0;',
          iframe: 'width:100%;height:100%;border:0;margin:0;padding:0;border-radius:10px;',
          closeButton: 'border:0;height:28px;width:28px;cursor:pointer;position:absolute;top:11px;right:10px;background:url("' + ASSET_URL +'images/close-button.png");'
        },
        modal: {
          overlay: 'position:fixed; background:#000; width:100%; height:100%;z-index:30;top:0; left:0; opacity:0.5',
          campaign_container: 'position:fixed;left:50%;margin-left:-250px;margin-top:40px;width:500px;top:0;opacity:1;z-index:100;',
          iframe_container: 'position:relative;height:365px; width:500px;background:#444;border-radius:10px;',
          iframe: 'width:100%;height:100%;border:0;margin:0;padding:0;border-radius:10px;',
          closeButton: 'border:0;height:28px;width:28px;cursor:pointer;position:absolute;top:15px;right:15px;background:url("' + ASSET_URL +'images/close-button.png");'
        }
      },
      show: function () {

        var style = active_campaign.styles[active_campaign.config.show_style]

        if(style.overlay) {
          var overlay = document.createElement('div');
          overlay.style.cssText = style.overlay;
          document.body.appendChild(overlay);
        }

        // Create a container
        var campaign_container = document.createElement('div');
        campaign_container.style.cssText = style.campaign_container;

        // Create a container for the iframe so we can do padding and border-radius properly
        var iframe_container = document.createElement('div');
        iframe_container.style.cssText = style.iframe_container;

        // Append Iframe and campaign container to document
        campaign_container.appendChild(iframe_container);
        document.body.appendChild(campaign_container);

        var iframe = document.createElement('iframe');
        iframe.style.cssText = style.iframe;

        // Set the source of the iframe to the configured show_style type
        iframe.src = ASSET_URL + active_campaign.config.show_style + '.html';
        iframe_container.appendChild(iframe);

        // Setup a close button
        var closeButton = document.createElement('button');
        closeButton.style.cssText = style.closeButton;
        iframe_container.appendChild(closeButton);
        closeButton.onclick = function() {
          active_campaign.hide(campaign_container);
          if(style.overlay) {
            overlay.remove();
          }
        }

      },
      init: function (config) {
        active_campaign.config = config;
        // Check cookie for this campaign
        if(checks.hasSeenCampaign(active_campaign.cookieName)) {
          return false;
        }

        // Check between date
        if(!checks.betweenDate(active_campaign.startDate, active_campaign.endDate) || active_campaign.config.disableDate) {
          return false;
        }

        // Check if is mobile
        if(!checks.isMobile()){
          return false;
        }

        // Check if they are near Washington?
        if(active_campaign.config.disableGeo) {
          active_campaign.show();
        } else {
          checks.nearDC(function (res) {
            if(res.kilometersFromDC <= 700) {
              active_campaign.show();
            }
          })
        }
      }
    }
  }

  // Load campaign if exist

  if(typeof campaign[widget_config.campaign] !== 'undefined') {
    active_campaign = campaign[widget_config.campaign];
    active_campaign.init(widget_config);
  } else {
    return false;
  }

})(window, _tfrce_config);
<commit_msg>opacity fix<commit_after>/*
   _            _     __
  | |_ __ _ ___| | __/ _| ___  _ __ ___ ___
  | __/ _` / __| |/ / |_ / _ \| '__/ __/ _ \
  | || (_| \__ \   <|  _| (_) | | | (_|  __/
   \__\__,_|___/_|\_\_|  \___/|_|  \___\___|

  ===============================================================================

  A generic widget include script which we will use to show further campaigns etc

  <thomasalwyndavis@gmail.com> or http://taskforce.is for support

  ===============================================================================

  TODO
    - Use underscores or camelcase?

*/

// Wrap widget in function to protect scope
var _tfrce_config = (typeof tfrce_config  !== 'undefined') ? tfrce_config  : {};

(function(window, widget_config){

  // Do configuration

  widget_config.show_style = widget_config.show_style || 'center_modal';
  widget_config.disableGeo = widget_config.disableGeo || false;
  widget_config.debug = widget_config.debug || false;
  widget_config.disableDate = widget_config.disableDate || false;
  widget_config.campaign = widget_config.campaign || 'stopwatchingus';
  widget_config.cookieTimeout = widget_config.cookieTimeout || 86400;

  // Setup
  var active_campaign;
  var ASSET_URL, COOKIE_TIMEOUT;

  if(widget_config.debug) {
    ASSET_URL = '../stopwatchingus/';
    COOKIE_TIMEOUT = 20;
  } else {
    ASSET_URL = '//widget.taskforce.is/widget/stopwatchingus/';
    COOKIE_TIMEOUT = widget_config.cookieTimeout;
  }

  // Cookie helpers, taken from w3schools
  function setCookie(c_name,value,seconds) {
    var exdate = new Date(new Date().getTime() + seconds*1000);
    console.log(new Date(), exdate);
    var c_value=escape(value) + ((seconds==null) ? "" : "; expires="+exdate.toUTCString());
    document.cookie=c_name + "=" + c_value;
  }

  function getCookie(c_name) {
    var c_value = document.cookie;
    var c_start = c_value.indexOf(" " + c_name + "=");
    if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); };
    if (c_start == -1) {
      c_value = null;
    } else {
      c_start = c_value.indexOf("=", c_start) + 1;
      var c_end = c_value.indexOf(";", c_start);
      if (c_end == -1) { c_end = c_value.length; }
      c_value = unescape(c_value.substring(c_start,c_end));
    }
    return c_value;
  }

  // Define checks

  var checks = {
    betweenDate: function(startDate, endDate) {
      var startEpoch = startDate.getTime();
      var endEpoch = endDate.getTime();
      var currentEpoch = new Date().getTime();
      if(currentEpoch > startEpoch && currentEpoch < endEpoch) {
        return true;
      } else {
        return false;
      }
    },
    isMobile: function() {
      var ismobile = navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i);
      return ismobile ? false : true;
    },
    hasSeenCampaign: function (cookieName) {
      var cookie = getCookie(cookieName)
      if(cookie === null) {
        return false;
      } else {
        return true
      }
    },
    nearDC: function (callback) {
      window.nearDC = callback;
      var script = document.createElement('script');
      script.src = '//geoip.taskforce.is/?callback=nearDC'
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  }

  // Define campaigns

  var campaign = {
    stopwatchingus: {
      cookieName: 'stopwatchingus_hasseen6',
      startDate: new Date(2011, 10, 30, 0),
      endDate: new Date(2014, 10, 30, 0),
      hide: function (el, callback) {
        document.body.removeChild(el);
        setCookie(active_campaign.cookieName, 'true', COOKIE_TIMEOUT);
        if(callback) { callback(); };
      },
      styles: {
        modal: {
          campaign_container: '',
          iframe_container: '',
          iframe: '',
          closeButton: ''
        },
        banner: {
          campaign_container: 'position:fixed;width:100%;bottom:0;z-index:100; padding: 0 20px;',
          iframe_container: 'position: relative; height: 100px; max-width:1201px; margin: 0px auto 8px auto; background: #444;border-radius: 10px;',
          iframe: 'width: 100%;height: 100%;border: 0;margin:0;padding:0;border-radius: 10px;',
          closeButton: 'border: 0;height: 28px;width: 28px;cursor: pointer;position: absolute;top:33px;right:20px;background: url("' + ASSET_URL +'images/close-button.png");'
        },
        dev: {
          campaign_container: 'position:fixed;width:100%;bottom:0;z-index:100; padding: 0 20px;',
          iframe_container: 'position: relative; height: 100px; max-width:1201px; margin: 0px auto 8px auto; background: #444;border-radius: 10px;',
          iframe: 'width: 100%;height: 100%;border: 0;margin:0;padding:0;border-radius: 10px;',
          closeButton: 'border: 0;height: 28px;width: 28px;cursor: pointer;position: absolute;top:33px;right:20px;background: url("' + ASSET_URL +'images/close-button.png");'
        },
        strip: {
          campaign_container: 'position:fixed;width:100%;bottom:0;opacity:1;z-index:100;',
          iframe_container: 'position:relative;height:50px;margin:0 20px 0px 20px;background:#444;border-radius:10px 10px 0 0;',
          iframe: 'width:100%;height:100%;border:0;margin:0;padding:0;border-radius:10px;',
          closeButton: 'border:0;height:28px;width:28px;cursor:pointer;position:absolute;top:11px;right:10px;background:url("' + ASSET_URL +'images/close-button.png");'
        },
        modal: {
          overlay: 'position:fixed; background:#000; width:100%; height:100%;z-index:30;top:0; left:0; opacity:0.5;-moz-opacity: 0.50;filter: alpha(opacity=50);',
          campaign_container: 'position:fixed;left:50%;margin-left:-250px;margin-top:40px;width:500px;top:0;opacity:1;z-index:100;',
          iframe_container: 'position:relative;height:365px; width:500px;background:#444;border-radius:10px;',
          iframe: 'width:100%;height:100%;border:0;margin:0;padding:0;border-radius:10px;',
          closeButton: 'border:0;height:28px;width:28px;cursor:pointer;position:absolute;top:15px;right:15px;background:url("' + ASSET_URL +'images/close-button.png");'
        }
      },
      show: function () {

        var style = active_campaign.styles[active_campaign.config.show_style]

        if(style.overlay) {
          var overlay = document.createElement('div');
          overlay.style.cssText = style.overlay;
          document.body.appendChild(overlay);
        }

        // Create a container
        var campaign_container = document.createElement('div');
        campaign_container.style.cssText = style.campaign_container;

        // Create a container for the iframe so we can do padding and border-radius properly
        var iframe_container = document.createElement('div');
        iframe_container.style.cssText = style.iframe_container;

        // Append Iframe and campaign container to document
        campaign_container.appendChild(iframe_container);
        document.body.appendChild(campaign_container);

        var iframe = document.createElement('iframe');
        iframe.style.cssText = style.iframe;

        // Set the source of the iframe to the configured show_style type
        iframe.src = ASSET_URL + active_campaign.config.show_style + '.html';
        iframe_container.appendChild(iframe);

        // Setup a close button
        var closeButton = document.createElement('button');
        closeButton.style.cssText = style.closeButton;
        iframe_container.appendChild(closeButton);
        closeButton.onclick = function() {
          active_campaign.hide(campaign_container);
          if(style.overlay) {
            document.body.removeChild(overlay);
          }
        }

      },
      init: function (config) {
        active_campaign.config = config;
        // Check cookie for this campaign
        if(checks.hasSeenCampaign(active_campaign.cookieName)) {
          return false;
        }

        // Check between date
        if(!checks.betweenDate(active_campaign.startDate, active_campaign.endDate) || active_campaign.config.disableDate) {
          return false;
        }

        // Check if is mobile
        if(!checks.isMobile()){
          return false;
        }

        // Check if they are near Washington?
        if(active_campaign.config.disableGeo) {
          active_campaign.show();
        } else {
          checks.nearDC(function (res) {
            if(res.kilometersFromDC <= 700) {
              active_campaign.show();
            }
          })
        }
      }
    }
  }

  // Load campaign if exist

  if(typeof campaign[widget_config.campaign] !== 'undefined') {
    active_campaign = campaign[widget_config.campaign];
    active_campaign.init(widget_config);
  } else {
    return false;
  }

})(window, _tfrce_config);
<|endoftext|>