<commit_before>import * as Sajari from 'sajari'

class NamespaceState {
  constructor(namespace) {
    this.namespace = namespace;
    this.values = {};

    this.trackingData = {};

    this.tracking = null;

    this.trackingSeq = 0;
    this.trackingQid = "";

    this.listeners = [];
  }

  getPipeline() {
    return this.pipeline;
  }

  setPipeline(pipeline) {
    this.pipeline = pipeline;
  }

  setProject(project) {
    this.project = project;
  }

  setCollection(collection) {
    this.collection = collection;
  }

  setTrackingData(data) {
    this.trackingData = data;
  }

  _runSearch() {
    // If q == "" then clear the results immediately and don't run a search.
    if (this.values["q"] === "") {
      this.results = undefined;
    }

    const client = new Sajari.Client(this.project, this.collection);
    if (!this.tracking) {
      this.tracking = new Sajari.Tracking();
    }

    if (this.pipeline === 'website') {
      this.tracking.clickTokens('url') // NOTE: this will not be cleared if the pipeline is changed
    }

    // trackingData is merged into the tracking.data (to avoid clearing existing values unless
    // another explicitly replaces it).
    if (this.trackingData) {
      for (let k in this.trackingData) {
        this.tracking.data[k] = this.trackingData[k];
      }
    }

    client.searchPipeline(this.pipeline, this.values, this.tracking, (err, res) => {
      if (res.values) {
        // TODO: Move this out into a method.
        // This stuff is specific to autocomplete only.
        let state = this.getValues();
        if (res.values["q"] && res.values["q"] !== state["q"]) {
          res.values["q.used"] = res.values["q"];
          delete res.values["q"];
        } else {
          res.values["q.used"] = null;
        }
        this.setValues(res.values, false);
      }
      this.results = res.searchResponse;
      this.notify();
    })
  }

  // Changes to values and also changes to results.
  registerChangeListener(listener) {
    this.listeners.push(listener)
  }

  unregisterChangeListener(listener) {
    this.listeners = this.listeners.filter(l => l !== listener)
  }

  notify() {
    this.listeners.forEach(l => l())
  }

  getStatus() {
    return this.status;
  }

  getResults() {
    return this.results;
  }

  getValues() {
    return this.values;
  }

  setValues(values, runSearch = false) {
    // Only if values["q"] is set do we check if the value has changed.
    if (values["q"] !== undefined) {
      let prevQ = this.values["q"] || "";
      let currQ = values["q"];

      if (!currQ.startsWith(prevQ.substr(0, 3))) {
        this.tracking.resetID();
      }
    }

    for (let k in values) {
      this.values[k] = values[k]
    }
    if (runSearch) {
      this._runSearch();
    }
    this.notify();
  }
}

class State {
  constructor() {
    this._ns = {}

    this.ns = this.ns.bind(this);
  }

  default() {
    return this.ns("default");
  }

  ns(namespace) {
    let n = this._ns[namespace]
    if (!n) {
      n = new NamespaceState();
      this._ns[namespace] = n
    }
    return n;
  }
}

const _state = new State();

export default _state<commit_msg>pipeline: Reset page = 1 when new query entered.<commit_after>import * as Sajari from 'sajari'

class NamespaceState {
  constructor(namespace) {
    this.namespace = namespace;
    this.values = {};

    this.trackingData = {};

    this.tracking = null;

    this.trackingSeq = 0;
    this.trackingQid = "";

    this.listeners = [];
  }

  getPipeline() {
    return this.pipeline;
  }

  setPipeline(pipeline) {
    this.pipeline = pipeline;
  }

  setProject(project) {
    this.project = project;
  }

  setCollection(collection) {
    this.collection = collection;
  }

  setTrackingData(data) {
    this.trackingData = data;
  }

  _runSearch() {
    // If q == "" then clear the results immediately and don't run a search.
    if (this.values["q"] === "") {
      this.results = undefined;
    }

    const client = new Sajari.Client(this.project, this.collection);
    if (!this.tracking) {
      this.tracking = new Sajari.Tracking();
    }

    if (this.pipeline === 'website') {
      this.tracking.clickTokens('url') // NOTE: this will not be cleared if the pipeline is changed
    }

    // trackingData is merged into the tracking.data (to avoid clearing existing values unless
    // another explicitly replaces it).
    if (this.trackingData) {
      for (let k in this.trackingData) {
        this.tracking.data[k] = this.trackingData[k];
      }
    }

    client.searchPipeline(this.pipeline, this.values, this.tracking, (err, res) => {
      if (res.values) {
        // TODO: Move this out into a method.
        // This stuff is specific to autocomplete only.
        let state = this.getValues();
        if (res.values["q"] && res.values["q"] !== state["q"]) {
          res.values["q.used"] = res.values["q"];
          delete res.values["q"];
        } else {
          res.values["q.used"] = null;
        }
        this.setValues(res.values, false);
      }
      this.results = res.searchResponse;
      this.notify();
    })
  }

  // Changes to values and also changes to results.
  registerChangeListener(listener) {
    this.listeners.push(listener)
  }

  unregisterChangeListener(listener) {
    this.listeners = this.listeners.filter(l => l !== listener)
  }

  notify() {
    this.listeners.forEach(l => l())
  }

  getStatus() {
    return this.status;
  }

  getResults() {
    return this.results;
  }

  getValues() {
    return this.values;
  }

  setValues(values, runSearch = false) {
    // Only if values["q"] is set do we check if the value has changed.
    if (values["q"] !== undefined) {
      let prevQ = this.values["q"] || "";
      let currQ = values["q"];

      if (!currQ.startsWith(prevQ.substr(0, 3))) {
        this.tracking.resetID();
      }

      // Reset the page, but only if we're not already trying
      // to set it.
      if (values["page"] === undefined) {
        this.values["page"] = "1";
      }
    }

    for (let k in values) {
      this.values[k] = values[k]
    }
    if (runSearch) {
      this._runSearch();
    }
    this.notify();
  }
}

class State {
  constructor() {
    this._ns = {}

    this.ns = this.ns.bind(this);
  }

  default() {
    return this.ns("default");
  }

  ns(namespace) {
    let n = this._ns[namespace]
    if (!n) {
      n = new NamespaceState();
      this._ns[namespace] = n
    }
    return n;
  }
}

const _state = new State();

export default _state<|endoftext|>


<commit_before>/* @flow */

import React, { Component } from 'react';
import {
  Text,
  View,
  ScrollView,
  Image,
  Alert
} from 'react-native';
import Button from '../../components/Button';
import ScoreBoard from '../../components/ScoreBoard';
import styles from './styles';
import I18n, {gameT} from '../../i18n'
import defaultGame from './DefaultGame'
import type Game from './DefaultGame'
import _ from 'lodash'
import layout from '../../layouts'

type props = {
  navigation: any,
};

class DrawRandomNoRepetitions{
  all: Array<string>
  remainder: Array<string>
  name: string
  constructor(teamName: string, teamMembers: Array<string>){
    this.all = [...teamMembers]
    this.name = teamName
    this.reset()
  }
  reset(){
    this.remainder = [...this.all]
  }
  nextRandom(): string{
    if(this.remainder.length === 0){
      this.reset()
    }
    return this.remainder.splice(Math.floor(Math.random()*this.remainder.length),1)[0]
  }
  getRandomPlayers(n: number): string{
    if(n === 0){
      return this.name
    }
    let players: Array<string> = Array()
    while(players.length < n){
      players.push(this.nextRandom())
      if(this.all.length>=n){
        players = _.uniq(players)
      }
    }
    return players.join(', ')
  }
}

class GameScreen extends Component {
  state: {
    game: Game,
    teams: Array<Array<string>>,
    gameNumber: number,
    standing: Array<number>,
    players: [string, string],
    goingFirst: number,
  }
  static navigationOptions = {
    header: null
  };

  drawRandomTeam1: DrawRandomNoRepetitions
  drawRandomTeam2: DrawRandomNoRepetitions

  constructor(props: props) {
    super(props)
    const {teams, gameNumber, game} = props.navigation.state.params;
    this.drawRandomTeam1 = new DrawRandomNoRepetitions(I18n.t('team1'), teams[0])
    this.drawRandomTeam2 = new DrawRandomNoRepetitions(I18n.t('team2'), teams[1])
    let goingFirst = Math.floor(Math.random()*2)
    this.state = {
      game: game,
      gameNumber: gameNumber,
      teams: teams,
      players: [this.drawRandomTeam1.getRandomPlayers(game.activePlayers), this.drawRandomTeam2.getRandomPlayers(game.activePlayers)],
      standing: [0,0],
      goingFirst: goingFirst,
    }
  }

  handleScore(teamIndex: number){
    let st = [...this.state.standing]
    if(teamIndex === -1){
      st[0]++
      st[1]++
    }
    if(teamIndex >= 0){
      st[teamIndex]++
      if(st[teamIndex]>(this.state.game.bestOf/2) && st[teamIndex]>st[1-teamIndex]){
        this.props.navigation.state.params.gameOver(teamIndex)
      }
    }
    this.setState({
      standing: st,
      players: [
        this.drawRandomTeam1.getRandomPlayers(this.state.game.activePlayers),
        this.drawRandomTeam2.getRandomPlayers(this.state.game.activePlayers)
      ],
      goingFirst: Math.floor(Math.random()*2),
    });
  }

  render() {
    const {teams, game, players, gameNumber} = this.state
    const { navigate } = this.props.navigation

    return (
      <View style={layout.main}>
        <View></View>
        <View style={layout.content}>
          <Text style={layout.title}>
            {I18n.t('game')} {gameNumber}: {gameT('name', game)}
          </Text>
          <Text alignSelf="center">
            {I18n.t('bestOf')}: {game.bestOf}
          </Text>
          <ScoreBoard
            standing={this.state.standing}
          />
          {
            game.tiePossible &&
            <View style={styles.tieButtonView}>
              <Button
                text={I18n.t('both')}
                onPress={()=>this.handleScore(-1)}
              />
            </View>
          }
          <View style={styles.buttonView}>
            <View style={styles.multiButtonView}>
            <Button
              text={players[0]}
              onPress={()=>this.handleScore(0)}
            />
            </View>
            <View style={styles.multiButtonView}>
            <Button
              text={players[1]}
              onPress={()=>this.handleScore(1)}
            />
            </View>
          </View>
          {
            game.tiePossible &&
            <View style={styles.tieButtonView}>
              <Button
                style={styles.tieButton}
                text={I18n.t('nobody')}
                onPress={()=>this.handleScore(-2)}
              />
            </View>
          }
          {
            game.randomStarter &&
            <Text>{I18n.t('goingFirst')}: {players[this.state.goingFirst]}</Text>
          }
        </View>
        <View style={styles.buttonView}>
          <View style={styles.multiButtonView}>
            <Button
              icon="users"
              onPress={()=>Alert.alert(
                I18n.t('teams'),
                I18n.t('team1')+":\n"+teams[0].join(", ")+"\n\n"+I18n.t('team2')+":\n"+teams[1].join(", "),
                [ {text: 'OK', onPress: () => {}} ],
                { cancelable: true }
              )}
            />
          </View>
          <View style={styles.multiButtonView}>
            <Button
              icon="infoCircle"
              onPress={()=>Alert.alert( I18n.t('instructions'), gameT('instructions', game), [ {text: 'OK', onPress: () => {}} ], { cancelable: true } )}
            />
          </View>
          <View style={styles.multiButtonView}>
            <Button
              icon="exchange"
              onPress={()=>Alert.alert( I18n.t('skipGame'), I18n.t('skipGameDialog'), [ {text: I18n.t('yes'), onPress: () => {
                this.props.navigation.state.params.gameOver(-1)}
              }, {text: I18n.t('no'), onPress: () => {}} ], { cancelable: true } )}
            />
          </View>
        </View>
      </View>
    );
  }
};

export default GameScreen
<commit_msg>Suppress backButton action in game<commit_after>/* @flow */

import React, { Component } from 'react';
import {
  Text,
  View,
  ScrollView,
  Image,
  Alert,
  BackHandler,
} from 'react-native';
import Button from '../../components/Button';
import ScoreBoard from '../../components/ScoreBoard';
import styles from './styles';
import I18n, {gameT} from '../../i18n'
import defaultGame from './DefaultGame'
import type Game from './DefaultGame'
import _ from 'lodash'
import layout from '../../layouts'

type props = {
  navigation: any,
};

class DrawRandomNoRepetitions{
  all: Array<string>
  remainder: Array<string>
  name: string
  constructor(teamName: string, teamMembers: Array<string>){
    this.all = [...teamMembers]
    this.name = teamName
    this.reset()
  }
  reset(){
    this.remainder = [...this.all]
  }
  nextRandom(): string{
    if(this.remainder.length === 0){
      this.reset()
    }
    return this.remainder.splice(Math.floor(Math.random()*this.remainder.length),1)[0]
  }
  getRandomPlayers(n: number): string{
    if(n === 0){
      return this.name
    }
    let players: Array<string> = Array()
    while(players.length < n){
      players.push(this.nextRandom())
      if(this.all.length>=n){
        players = _.uniq(players)
      }
    }
    return players.join(', ')
  }
}

class GameScreen extends Component {
  state: {
    game: Game,
    teams: Array<Array<string>>,
    gameNumber: number,
    standing: Array<number>,
    players: [string, string],
    goingFirst: number,
  }
  static navigationOptions = {
    header: null
  };

  drawRandomTeam1: DrawRandomNoRepetitions
  drawRandomTeam2: DrawRandomNoRepetitions

  constructor(props: props) {
    super(props)
    const {teams, gameNumber, game} = props.navigation.state.params;
    this.drawRandomTeam1 = new DrawRandomNoRepetitions(I18n.t('team1'), teams[0])
    this.drawRandomTeam2 = new DrawRandomNoRepetitions(I18n.t('team2'), teams[1])
    let goingFirst = Math.floor(Math.random()*2)
    this.state = {
      game: game,
      gameNumber: gameNumber,
      teams: teams,
      players: [this.drawRandomTeam1.getRandomPlayers(game.activePlayers), this.drawRandomTeam2.getRandomPlayers(game.activePlayers)],
      standing: [0,0],
      goingFirst: goingFirst,
    }
  }

  componentWillMount(){
    BackHandler.addEventListener('hardwareBackPress', () => {
      return true;
    });
  }

  handleScore(teamIndex: number){
    let st = [...this.state.standing]
    if(teamIndex === -1){
      st[0]++
      st[1]++
    }
    if(teamIndex >= 0){
      st[teamIndex]++
      if(st[teamIndex]>(this.state.game.bestOf/2) && st[teamIndex]>st[1-teamIndex]){
        this.props.navigation.state.params.gameOver(teamIndex)
      }
    }
    this.setState({
      standing: st,
      players: [
        this.drawRandomTeam1.getRandomPlayers(this.state.game.activePlayers),
        this.drawRandomTeam2.getRandomPlayers(this.state.game.activePlayers)
      ],
      goingFirst: Math.floor(Math.random()*2),
    });
  }

  render() {
    const {teams, game, players, gameNumber} = this.state
    const { navigate } = this.props.navigation

    return (
      <View style={layout.main}>
        <View></View>
        <View style={layout.content}>
          <Text style={layout.title}>
            {I18n.t('game')} {gameNumber}: {gameT('name', game)}
          </Text>
          <Text alignSelf="center">
            {I18n.t('bestOf')}: {game.bestOf}
          </Text>
          <ScoreBoard
            standing={this.state.standing}
          />
          {
            game.tiePossible &&
            <View style={styles.tieButtonView}>
              <Button
                text={I18n.t('both')}
                onPress={()=>this.handleScore(-1)}
              />
            </View>
          }
          <View style={styles.buttonView}>
            <View style={styles.multiButtonView}>
            <Button
              text={players[0]}
              onPress={()=>this.handleScore(0)}
            />
            </View>
            <View style={styles.multiButtonView}>
            <Button
              text={players[1]}
              onPress={()=>this.handleScore(1)}
            />
            </View>
          </View>
          {
            game.tiePossible &&
            <View style={styles.tieButtonView}>
              <Button
                style={styles.tieButton}
                text={I18n.t('nobody')}
                onPress={()=>this.handleScore(-2)}
              />
            </View>
          }
          {
            game.randomStarter &&
            <Text>{I18n.t('goingFirst')}: {players[this.state.goingFirst]}</Text>
          }
        </View>
        <View style={styles.buttonView}>
          <View style={styles.multiButtonView}>
            <Button
              icon="users"
              onPress={()=>Alert.alert(
                I18n.t('teams'),
                I18n.t('team1')+":\n"+teams[0].join(", ")+"\n\n"+I18n.t('team2')+":\n"+teams[1].join(", "),
                [ {text: 'OK', onPress: () => {}} ],
                { cancelable: true }
              )}
            />
          </View>
          <View style={styles.multiButtonView}>
            <Button
              icon="infoCircle"
              onPress={()=>Alert.alert( I18n.t('instructions'), gameT('instructions', game), [ {text: 'OK', onPress: () => {}} ], { cancelable: true } )}
            />
          </View>
          <View style={styles.multiButtonView}>
            <Button
              icon="exchange"
              onPress={()=>Alert.alert( I18n.t('skipGame'), I18n.t('skipGameDialog'), [ {text: I18n.t('yes'), onPress: () => {
                this.props.navigation.state.params.gameOver(-1)}
              }, {text: I18n.t('no'), onPress: () => {}} ], { cancelable: true } )}
            />
          </View>
        </View>
      </View>
    );
  }
};

export default GameScreen
<|endoftext|>


<commit_before>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     481
// ==/UserScript==
<commit_msg>AUTO-CREATED Release Artifact v482 c/o Travis<commit_after>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     482
// ==/UserScript==
<|endoftext|>


<commit_before>const Router = require('koa-router')
const Review = require('../models/Review')

module.exports = new Router({ prefix: '/review' })
  .get('/', async (ctx, next) =>
    (ctx.body = await Review.find().toArray()))
<commit_msg>Fixed review route<commit_after>const Router = require('koa-router')
const Review = require('../models/Review')

module.exports = new Router({ prefix: '/review' })
  .get('/', async (ctx, next) =>
    (ctx.body = await Review.find().exec()))
<|endoftext|>


<commit_before>import { forEach, getElement, getSelectorAll } from '../core';
import { remove, append, find, parent, indexInParent, addClass, removeClass } from '../dom';
import { size, height, width, style, transform, screenPosition } from '../styles';
import { resize, unresize, load, on, off } from '../events';
import { defaultify } from '../utils/defaultify';
import { Cover } from './cover';

let defaults = {
  infinite: false,
  slideWidth: '100%',
  slideHeight: '100%',
  size: 'container',
  touchDirection: 'horizontal',
  touchThreshold: '50%',
  swipeTime: 300,
  swipeThreshold: 10,
  gutter: 0,
  direction: 'horizontal',
  cover: false,
  reverse: false,
  touchEnabled: true,
  mouseEnabled: false
};

export class Slider {
  constructor(options) {
    this.options = defaultify(options, defaults);

    this.gutter = this.options.gutter;
    this.halfGutter = this.gutter/2;
    this.onDrag = this.options.onDrag;
    this.onDragEnd = this.options.onDragEnd;
    this.swipeTime = this.options.swipeTime;

    this.callbacks = this.options.callback ? [this.options.callback] : [];

    this.coverManager = new Cover();

    this.current = 0;
    this.animating = false;

    if (typeof this.options.size != 'object') {
      this.options.size = {
        width: this.options.size,
        height: this.options.size
      };
    }

    if (this.options.bullets) this.bulletClickCallback = this.bulletClick.bind(this);

    this.refresh();
    this.resizeCallback = resize(this.resize.bind(this));

    if (this.options.touchEnabled) {
      this.touchstartCallback = this.touchstart.bind(this);
      on(this.container, 'touchstart', this.touchstartCallback);
      this.touchmoveCallback = this.touchmove.bind(this);
      on(this.container, 'touchmove', this.touchmoveCallback);
      this.touchendCallback = this.touchend.bind(this);
      on(this.container, 'touchend', this.touchendCallback);
    }

    if (this.options.mouseEnabled) {
      this.mousestartCallback = this.mousestart.bind(this);
      on(this.container, 'mousedown', this.mousestartCallback);
      this.mousemoveCallback = this.mousemove.bind(this);
      on(this.container, 'mousemove', this.mousemoveCallback);
      this.mouseendCallback = this.mouseend.bind(this);
      on(this.container, 'mouseup', this.mouseendCallback);
    }

    if (this.options.auto) this.nextTimeout = setTimeout(this.slideUp.bind(this), this.options.auto);
  }

  refresh() {
    this.container = getElement(this.options.container);
    this.wrapper = find(this.container, this.options.wrapper);
    this.slides = find(this.container, this.options.slides);
    this.nbSlide = this.slides.length;

    if (this.options.direction == 'horizontal') {
        style(this.slides, {
          marginLeft: this.halfGutter+'px',
          marginRight: this.halfGutter+'px'
        });

        style(this.wrapper, {
          marginLeft: -this.halfGutter+'px',
          marginRight: -this.halfGutter+'px'
        });
    }
    else if (this.options.direction == 'vertical') {
        style(this.slides, {
          marginTop: this.halfGutter+'px',
          marginBottom: this.halfGutter+'px'
        });

        style(this.wrapper, {
          marginTop: -this.halfGutter+'px',
          marginBottom: -this.halfGutter+'px'
        });
    }

    if (this.options.cover) {
      this.coverManager.addElements({
          elements: find(this.container, '.cover'),
          mode: this.options.cover
      });
    }

    if (this.options.reverse) {
      let i = this.slides.length;
      while(i--) {
        style(this.slides[i], {
            'z-index': this.slides.length - i
        });
      }
    }

    if (this.options.bullets) {
        remove(find(this.container, '.'+this.options.bullets.wrapper));

        append(this.container, '

');
        let bulletsWrapper = find(this.container, '.'+this.options.bullets.wrapper),
            i = -1;

        while(++i < this.slides.length) {
            append(bulletsWrapper, '
'+this.options.bullets.element.replace('$index', i+1)+'
');
        }

        addClass('.'+this.options.bullets.wrapper+' > li:first-child', 'active');

        on('.'+this.options.bullets.wrapper+' > li', 'click', this.bulletClickCallback);
    }

    load(find(this.wrapper, 'img'), null, () => {
        this.resize();
        this.coverManager.resizeAll();
        if (this.options.initialize) this.options.initialize(this);
    });
  }

  bulletClick(event) {
      event.preventDefault();

      this.slideTo(indexInParent(event.currentTarget));
  }

  updateActiveBullet(index) {
      if (!this.options.bullets) return;

      let bullets = find(this.container, '.'+this.options.bullets.wrapper+' > li');
      removeClass(bullets, 'active');
      addClass(bullets[index], 'active');
  }

  resize() {
    this.containerSize = {};
    size(this.container, {
      width: '',
      height: ''
    });

    if (this.options.size.width == 'container') {
      this.containerSize.width = width(this.container);
    }
    else if (this.options.size.width == 'slide') {
      this.containerSize.width = 0;
      forEach(this.slides, (slide) => {
        this.containerSize.width = Math.max(this.containerSize.width, width(slide));
      });
    }
    else {
      this.containerSize.width = parseInt(this.options.size.width, 10);
    }

    if (this.options.size.height == 'container') {
      this.containerSize.height = height(this.container);
    }
    else if (this.options.size.height == 'slide') {
      this.containerSize.height = 0;
      forEach(this.slides, (slide) => {
        this.containerSize.height = Math.max(this.containerSize.height, height(slide));
      });
    }
    else {
      this.containerSize.height = parseInt(this.options.size.height, 10);
    }

    size(this.container, this.containerSize);

    this.slideSize = {
      width: null,
      height: null
    };

    if (typeof this.options.slideWidth == 'string' && this.options.slideWidth.indexOf('%') != -1)
      this.slideSize.width = this.containerSize.width * parseInt(this.options.slideWidth, 10) / 100;
    else
      this.slideSize.width = parseInt(this.options.slideWidth, 10);

    if (typeof this.options.slideHeight == 'string' && this.options.slideHeight.indexOf('%') != -1)
      this.slideSize.height = this.containerSize.height * parseInt(this.options.slideHeight, 10) / 100;
    else
      this.slideSize.height = parseInt(this.options.slideHeight, 10);

    size(this.slides, this.slideSize);

    if (this.options.touchEnabled || this.options.mouseEnabled) {
      let slideTouchSize = this.slideSize[(this.options.touchDirection == 'horizontal') ? 'width' : 'height'];

      if (typeof this.options.touchThreshold == 'string' && this.options.touchThreshold.indexOf('%') != -1)
        this.touchThreshold = slideTouchSize * parseInt(this.options.touchThreshold, 10) / 100;
      else
        this.touchThreshold = this.options.touchThreshold;

      if (typeof this.options.swipeThreshold == 'string' && this.options.swipeThreshold.indexOf('%') != -1)
        this.swipeThreshold = slideTouchSize * parseInt(this.options.swipeThreshold, 10) / 100;
      else
        this.swipeThreshold = this.options.swipeThreshold;
    }

    let wrapperSize;
    switch (this.options.direction) {
      case 'horizontal':
        wrapperSize = {
          width: (this.slideSize.width + this.gutter) * this.nbSlide,
          height: this.containerSize.height
        };

        break;

      case 'vertical':
        wrapperSize = {
          width: this.containerSize.width,
          height: (this.slideSize.height + this.gutter) * this.nbSlide
        };

        break;

      default:
        wrapperSize = this.containerSize;
    }

    size(this.wrapper, wrapperSize);

    if (this.options.resize) this.options.resize(this);
  }

  animationCallback() {
    this.animating = false;
    this.touchOrig = null;

    let i = this.callbacks.length;
    while(i--) this.callbacks[i](this.target, this.current);

    this.current = this.target;
    this.updateActiveBullet(this.current);

    if (this.options.auto) {
        clearTimeout(this.nextTimeout);
        this.nextTimeout = setTimeout(this.slideUp.bind(this), this.options.auto);
    }
  }

  slideDown() {
    this.slideTo(this.current-1);
  }

  slideUp() {
    this.slideTo(this.current+1);
  }

  slideTo(target, paused = false) {
    if (this.animating) return;

    if (target < 0) this.target = target - (target % -this.nbSlide);
    else this.target = target % this.nbSlide;

    let tween = this.options.animationTween(this, this.animationCallback.bind(this));

    if (paused) tween.pause();
    else this.updateActiveBullet(this.target);

    this.animating = !paused;

    return tween;
  }

  touchstart(event) {
    let touch = event.touches[0];

    this.dragStart({
      x: touch.pageX,
      y: touch.pageY
    });

    // event.stopPropagation();
  }

  touchmove(event) {
    if (!this.touchOrig) return;

    let touch = event.touches[0];

    this.dragMove({
      x: touch.pageX,
      y: touch.pageY
    });

    // event.stopPropagation();
  }

  touchend(event) {
    this.dragEnd();

    // event.stopPropagation();
  }

  mousestart(event) {
    this.dragStart({
      x: event.pageX,
      y: event.pageY
    });

    // event.preventDefault();
  }

  mousemove(event) {
    if (!this.touchOrig) return;

    this.dragMove({
      x: event.pageX,
      y: event.pageY
    });

    // event.preventDefault();
  }

  mouseend(event) {
    this.dragEnd();
    // event.preventDefault();
  }

  dragStart(position) {
    this.touchOrig = position;
    this.touchLength = 0;

    this.swipeNext = null;
    this.time = new Date().getTime();
  }

  dragMove(position) {
    if (!this.touchOrig) return;

    this.touchLength = this.options.touchDirection == 'horizontal' ? position.x - this.touchOrig.x : position.y - this.touchOrig.y;
    let forward = this.touchLength < 0;

    if (this.swipeNext != forward) {
      this.swipeNext = forward;

      this.target = this.current + (this.swipeNext ? 1 : -1);
    }

    if (this.onDrag) {
      this.onDrag(this, this.touchLength);
    }
    else {
      let tween = this.slideTo(this.target, true);

      if (tween) {
        tween.progress(Math.abs(this.touchLength) / this.slideWidth);
      }
    }
  }

  dragEnd() {
    if (!this.touchOrig) return;

    let absLength = Math.abs(this.touchLength);
    if (absLength > this.touchThreshold || new Date().getTime() - this.time < this.swipeTime && absLength > this.swipeThreshold) {
      this.slideTo(this.target);
    }
    else {
      this.target = this.current;

      if (this.onDragEnd) {
        this.onDragEnd(this);
        this.touchOrig = null;
      }
      else {
        this.slideTo(this.current);
      }
    }
  }

  on(callback) {
    this.callbacks.push(callback);
  }

  off(callback) {
    this.callbacks.splice(this.slide.indexOf(callback));
  }

  kill() {
    unresize(this.resizeCallback);

    if (this.options.touchEnabled) {
      off(this.container, 'touchstart', this.touchstartCallback);
      off(this.container, 'touchmove', this.touchmoveCallback);
      off(this.container, 'touchend', this.touchendCallback);
    }

    if (this.options.mouseEnabled) {
      off(this.container, 'mousedown', this.mousestartCallback);
      off(this.container, 'mousemove', this.mousemoveCallback);
      off(this.container, 'mouseup', this.mouseendCallback);
    }

    if (this.options.bullets) {
      off('.'+this.options.bullets.wrapper+' > li', 'click', this.bulletClickCallback);
      remove('.'+this.options.bullets.wrapper);
    }

    size(this.slides, {
      width: '',
      height: ''
    });

    size(this.wrapper, {
      width: '',
      height: ''
    });

    size(this.container, {
      width: '',
      height: ''
    });

    if (this.options.clearAnimation) this.options.clearAnimation(this);
    if (this.nextTimeout) clearTimeout(this.nextTimeout);

    if (this.coverManager) this.coverManager.removeElements(find(this.container, '.cover'));
  }
}
<commit_msg>slider - fixes<commit_after>import { forEach, getElement, getSelectorAll } from '../core';
import { remove, append, find, parent, indexInParent, addClass, removeClass } from '../dom';
import { size, height, width, style, transform, screenPosition } from '../styles';
import { resize, unresize, load, on, off } from '../events';
import { defaultify } from '../utils/defaultify';
import { Cover } from './cover';

let defaults = {
  infinite: false,
  slideWidth: '100%',
  slideHeight: '100%',
  size: 'container',
  touchDirection: 'horizontal',
  touchThreshold: '50%',
  swipeTime: 300,
  swipeThreshold: 10,
  gutter: 0,
  direction: 'horizontal',
  cover: false,
  reverse: false,
  touchEnabled: true,
  mouseEnabled: false
};

export class Slider {
  constructor(options) {
    this.options = defaultify(options, defaults);

    this.gutter = this.options.gutter;
    this.halfGutter = this.gutter/2;
    this.onDrag = this.options.onDrag;
    this.onDragEnd = this.options.onDragEnd;
    this.swipeTime = this.options.swipeTime;

    this.callbacks = this.options.callback ? [this.options.callback] : [];

    this.coverManager = new Cover();

    this.current = 0;
    this.animating = false;

    if (typeof this.options.size != 'object') {
      this.options.size = {
        width: this.options.size,
        height: this.options.size
      };
    }

    if (this.options.bullets) this.bulletClickCallback = this.bulletClick.bind(this);

    this.refresh();
    this.resizeCallback = resize(this.resize.bind(this));

    if (this.options.touchEnabled) {
      this.touchstartCallback = this.touchstart.bind(this);
      on(this.container, 'touchstart', this.touchstartCallback);
      this.touchmoveCallback = this.touchmove.bind(this);
      on(this.container, 'touchmove', this.touchmoveCallback);
      this.touchendCallback = this.touchend.bind(this);
      on(this.container, 'touchend', this.touchendCallback);
    }

    if (this.options.mouseEnabled) {
      this.mousestartCallback = this.mousestart.bind(this);
      on(this.container, 'mousedown', this.mousestartCallback);
      this.mousemoveCallback = this.mousemove.bind(this);
      on(this.container, 'mousemove', this.mousemoveCallback);
      this.mouseendCallback = this.mouseend.bind(this);
      on(this.container, 'mouseup', this.mouseendCallback);
    }

    if (this.options.auto) this.nextTimeout = setTimeout(this.slideUp.bind(this), this.options.auto);
  }

  refresh() {
    this.container = getElement(this.options.container);
    this.wrapper = find(this.container, this.options.wrapper);
    this.slides = find(this.container, this.options.slides);
    this.nbSlide = this.slides.length;

    if (this.options.direction == 'horizontal') {
        style(this.slides, {
          marginLeft: this.halfGutter+'px',
          marginRight: this.halfGutter+'px'
        });

        style(this.wrapper, {
          marginLeft: -this.halfGutter+'px',
          marginRight: -this.halfGutter+'px'
        });
    }
    else if (this.options.direction == 'vertical') {
        style(this.slides, {
          marginTop: this.halfGutter+'px',
          marginBottom: this.halfGutter+'px'
        });

        style(this.wrapper, {
          marginTop: -this.halfGutter+'px',
          marginBottom: -this.halfGutter+'px'
        });
    }

    if (this.options.cover) {
      this.coverManager.addElements({
          elements: find(this.container, '.cover'),
          mode: this.options.cover
      });
    }

    if (this.options.reverse) {
      let i = this.slides.length;
      while(i--) {
        style(this.slides[i], {
            'z-index': this.slides.length - i
        });
      }
    }

    if (this.options.bullets) {
        remove(find(this.container, '.'+this.options.bullets.wrapper));

        append(this.container, '

');
        let bulletsWrapper = find(this.container, '.'+this.options.bullets.wrapper),
            i = -1;

        while(++i < this.slides.length) {
            append(bulletsWrapper, '
'+this.options.bullets.element.replace('$index', i+1)+'
');
        }

        addClass('.'+this.options.bullets.wrapper+' > li:first-child', 'active');

        on(find(this.container, '.'+this.options.bullets.wrapper+' > li'), 'click', this.bulletClickCallback);
    }

    load(find(this.wrapper, 'img'), null, () => {
        this.resize();
        this.coverManager.resizeAll();
        if (this.options.initialize) this.options.initialize(this);
    });
  }

  bulletClick(event) {
      event.preventDefault();

      this.slideTo(indexInParent(event.currentTarget));
  }

  updateActiveBullet(index) {
      if (!this.options.bullets) return;

      let bullets = find(this.container, '.'+this.options.bullets.wrapper+' > li');
      removeClass(bullets, 'active');
      addClass(bullets[index], 'active');
  }

  resize() {
    this.containerSize = {};
    size(this.container, {
      width: '',
      height: ''
    });

    if (this.options.size.width == 'container') {
      this.containerSize.width = width(this.container);
    }
    else if (this.options.size.width == 'slide') {
      this.containerSize.width = 0;
      forEach(this.slides, (slide) => {
        this.containerSize.width = Math.max(this.containerSize.width, width(slide));
      });
    }
    else {
      this.containerSize.width = parseInt(this.options.size.width, 10);
    }

    if (this.options.size.height == 'container') {
      this.containerSize.height = height(this.container);
    }
    else if (this.options.size.height == 'slide') {
      this.containerSize.height = 0;
      forEach(this.slides, (slide) => {
        this.containerSize.height = Math.max(this.containerSize.height, height(slide));
      });
    }
    else {
      this.containerSize.height = parseInt(this.options.size.height, 10);
    }

    size(this.container, this.containerSize);

    this.slideSize = {
      width: null,
      height: null
    };

    if (typeof this.options.slideWidth == 'string' && this.options.slideWidth.indexOf('%') != -1)
      this.slideSize.width = this.containerSize.width * parseInt(this.options.slideWidth, 10) / 100;
    else
      this.slideSize.width = parseInt(this.options.slideWidth, 10);

    if (typeof this.options.slideHeight == 'string' && this.options.slideHeight.indexOf('%') != -1)
      this.slideSize.height = this.containerSize.height * parseInt(this.options.slideHeight, 10) / 100;
    else
      this.slideSize.height = parseInt(this.options.slideHeight, 10);

    size(this.slides, this.slideSize);

    if (this.options.touchEnabled || this.options.mouseEnabled) {
      let slideTouchSize = this.slideSize[(this.options.touchDirection == 'horizontal') ? 'width' : 'height'];

      if (typeof this.options.touchThreshold == 'string' && this.options.touchThreshold.indexOf('%') != -1)
        this.touchThreshold = slideTouchSize * parseInt(this.options.touchThreshold, 10) / 100;
      else
        this.touchThreshold = this.options.touchThreshold;

      if (typeof this.options.swipeThreshold == 'string' && this.options.swipeThreshold.indexOf('%') != -1)
        this.swipeThreshold = slideTouchSize * parseInt(this.options.swipeThreshold, 10) / 100;
      else
        this.swipeThreshold = this.options.swipeThreshold;
    }

    let wrapperSize;
    switch (this.options.direction) {
      case 'horizontal':
        wrapperSize = {
          width: (this.slideSize.width + this.gutter) * this.nbSlide,
          height: this.containerSize.height
        };

        break;

      case 'vertical':
        wrapperSize = {
          width: this.containerSize.width,
          height: (this.slideSize.height + this.gutter) * this.nbSlide
        };

        break;

      default:
        wrapperSize = this.containerSize;
    }

    size(this.wrapper, wrapperSize);

    if (this.options.resize) this.options.resize(this);
  }

  animationCallback() {
      console.log('animationCallback');
    this.animating = false;
    this.touchOrig = null;

    let i = this.callbacks.length;
    while(i--) this.callbacks[i](this.target, this.current);

    this.current = this.target;
    this.updateActiveBullet(this.current);

    if (this.options.auto) {
        clearTimeout(this.nextTimeout);
        this.nextTimeout = setTimeout(this.slideUp.bind(this), this.options.auto);
    }
  }

  slideDown() {
    this.slideTo(this.current-1);
  }

  slideUp() {
    this.slideTo(this.current+1);
  }

  computeTarget(index) {
    if (index < 0) index = this.nbSlide + index;
    return index % this.nbSlide;
  }

  slideTo(target, paused = false) {
    console.log('animating', this.animating, paused);
    if (this.animating) return;

    this.animating = !paused;

    this.target = this.computeTarget(target);

    let tween = this.options.animationTween(this, this.animationCallback.bind(this));

    if (paused) tween.pause();
    else this.updateActiveBullet(this.target);

    return tween;
  }

  touchstart(event) {
    let touch = event.touches[0];

    this.dragStart({
      x: touch.pageX,
      y: touch.pageY
    });

    // event.stopPropagation();
  }

  touchmove(event) {
    if (!this.touchOrig) return;

    let touch = event.touches[0];

    this.dragMove({
      x: touch.pageX,
      y: touch.pageY
    });

    // event.stopPropagation();
  }

  touchend(event) {
    this.dragEnd();

    // event.stopPropagation();
  }

  mousestart(event) {
    this.dragStart({
      x: event.pageX,
      y: event.pageY
    });

    // event.preventDefault();
  }

  mousemove(event) {
    if (!this.touchOrig) return;

    this.dragMove({
      x: event.pageX,
      y: event.pageY
    });

    // event.preventDefault();
  }

  mouseend(event) {
    this.dragEnd();
    // event.preventDefault();
  }

  dragStart(position) {
    if (this.animating) return;

    this.touchOrig = position;
    this.touchLength = 0;

    this.swipeNext = null;
    this.time = new Date().getTime();
  }

  dragMove(position) {
    if (!this.touchOrig || this.animating) return;

    this.touchLength = this.options.touchDirection == 'horizontal' ? position.x - this.touchOrig.x : position.y - this.touchOrig.y;
    let forward = this.touchLength < 0;

    if (this.swipeNext != forward) {
      this.swipeNext = forward;

      this.target = this.computeTarget(this.current + (this.swipeNext ? 1 : -1));
    }

    if (this.onDrag) {
      this.onDrag(this, this.touchLength);
    }
    else {
      let tween = this.slideTo(this.target, true);

      if (tween) {
        tween.progress(Math.abs(this.touchLength) / this.slideWidth);
      }
    }
  }

  dragEnd() {
    if (!this.touchOrig || this.animating) {
        this.touchOrig = null;

        return;
    }

    let absLength = Math.abs(this.touchLength);
    if (absLength > this.touchThreshold || new Date().getTime() - this.time < this.swipeTime && absLength > this.swipeThreshold) {
      this.slideTo(this.target);
    }
    else {
      this.target = this.current;

      if (this.onDragEnd) {
        this.onDragEnd(this);
        this.touchOrig = null;
      }
      else {
        this.slideTo(this.current);
      }
    }
  }

  on(callback) {
    this.callbacks.push(callback);
  }

  off(callback) {
    this.callbacks.splice(this.slide.indexOf(callback));
  }

  kill() {
    unresize(this.resizeCallback);

    if (this.options.touchEnabled) {
      off(this.container, 'touchstart', this.touchstartCallback);
      off(this.container, 'touchmove', this.touchmoveCallback);
      off(this.container, 'touchend', this.touchendCallback);
    }

    if (this.options.mouseEnabled) {
      off(this.container, 'mousedown', this.mousestartCallback);
      off(this.container, 'mousemove', this.mousemoveCallback);
      off(this.container, 'mouseup', this.mouseendCallback);
    }

    if (this.options.bullets) {
      off(find(this.container, '.'+this.options.bullets.wrapper+' > li'), 'click', this.bulletClickCallback);
      remove(find(this.container, '.'+this.options.bullets.wrapper));
    }

    size(this.slides, {
      width: '',
      height: ''
    });

    size(this.wrapper, {
      width: '',
      height: ''
    });

    size(this.container, {
      width: '',
      height: ''
    });

    if (this.options.clearAnimation) this.options.clearAnimation(this);
    if (this.nextTimeout) clearTimeout(this.nextTimeout);

    if (this.coverManager) this.coverManager.removeElements(find(this.container, '.cover'));
  }
}
<|endoftext|>


<commit_before>/* ScriptInclude
 * https://github.com/EvanHahn/ScriptInclude
 * by Evan Hahn
 * License: Unlicense
 */

;(function() {

	document.head || (document.head = document.getElementsByTagName("head")[0]);

	var noop = function() {};

	var include = function() {

		var toLoad = arguments.length;

		var callback;
		var hasCallback = arguments[toLoad - 1] instanceof Function;
		if (hasCallback) {
			toLoad --;
			callback = arguments[arguments.length - 1];
		} else {
			callback = noop;
		}

		var script;
		for (var i = 0; i < toLoad; i ++) {

			script = document.createElement("script");
			script.src = arguments[i];

			script.onload = script.onerror = function() {
				toLoad --;
				if (toLoad === 0)
					callback.call();
			};

			document.head.appendChild(script);

		}

	};

	if (typeof exports !== "undefined")
		exports = include;
	else
		this.include = include;

})();
<commit_msg>minor changes<commit_after>/* ScriptInclude
 * https://github.com/EvanHahn/ScriptInclude
 * by Evan Hahn
 * License: Unlicense
 */

;(function() {

	var doc = document;
	var head = doc.head || (doc.getElementsByTagName("head")[0]);

	var noop = function() {};

	var include = function() {

		var toLoad = arguments.length;

		var callback;
		var hasCallback = arguments[toLoad - 1] instanceof Function;
		if (hasCallback) {
			toLoad --;
			callback = arguments[arguments.length - 1];
		} else {
			callback = noop;
		}

		var script;
		for (var i = 0; i < toLoad; i ++) {

			script = doc.createElement("script");
			script.src = arguments[i];

			script.onload = script.onerror = function() {
				toLoad --;
				if (toLoad === 0)
					callback.call();
			};

			head.appendChild(script);

		}

	};

	if (typeof exports !== "undefined")
		exports = include;
	else
		this.include = include;

})();
<|endoftext|>


<commit_before>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     461
// ==/UserScript==
<commit_msg>AUTO-CREATED Release Artifact v462 c/o Travis<commit_after>// ==UserScript==
// @author      Robert Slifka (github @rslifka)
// @connect     docs.google.com
// @connect     rslifka.github.io
// @copyright   2017-2018, Robert Slifka (https://github.com/rslifka/fate_of_all_fools)
// @description Enhancements to the Destiny Item Manager
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @grant       GM_getValue
// @grant       GM_log
// @grant       GM_setValue
// @grant       GM_xmlhttpRequest
// @homepage    https://github.com/rslifka/fate_of_all_fools
// @license     MIT; https://raw.githubusercontent.com/rslifka/fate_of_all_fools/master/LICENSE.txt
// @match       https://*.destinyitemmanager.com/*
// @name        Fate of All Fools
// @require     https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.js
// @resource    fateOfAllFoolsCSS https://rslifka.github.io/fate_of_all_fools/fateOfAllFools.css
// @run-at      document-start
// @supportURL  https://github.com/rslifka/fate_of_all_fools/issues
// @version     462
// ==/UserScript==
<|endoftext|>


<commit_before>import forEach from '../core/for-each'
import forElements from '../core/for-elements'

import remove from '../dom/remove'
import data from '../dom/data'
import find from '../dom/find'
import createElement from '../dom/create-element'
import append from '../dom/append'
import clone from '../dom/clone'

import style from '../styles/style'
import screenPosition from '../styles/screen-position'
import height from '../styles/height'
import transform from '../styles/transform'
import size from '../styles/size'

import resize from '../events/resize'
import unresize from '../events/unresize'
import load from '../events/load'
import scroll from '../events/scroll'
import unscroll from '../events/unscroll'

import defaultify from '../utils/defaultify'

let defaults = {
  debug: false,
  offset: 0,
  ease: 0.2,
  stepMinSize: 5,
  snapOffset: 0,
  handle: {
    top: 'top',
    bottom: 'bottom'
  }
}

function randomColor () {
    return '#' + Math.floor(Math.random()*16777215).toString(16)
}

//Scroll manager
export class Wasabi {
  constructor(config) {
    this.config = defaultify(config, defaults)

    this.wrapper = this.scroller ? this.scroller.wrapper : document.body

    if (!this.config.scroller) {
      this.wrapper = document.body
      this.scrollTop = this.previousScrollTop = screenPosition(this.wrapper).top

      this.scrollEventsCallback = scroll(this.onScrollEvent.bind(this))
    }
    else {
        this.scroller = this.config.scroller

        this.wrapper = this.scroller.wrapper
        this.scrollTop = this.previousScrollTop = this.scroller.scroll.y

        this.scrollerCallback = this.onScroller.bind(this)
        this.scroller.on('update', this.scrollerCallback)
    }

    this.resizeCallback = resize(() => {
        requestAnimationFrame(this.refresh.bind(this))
    })

    if (this.config.debug) {
      this.debugWrapper = createElement('

')
      style(this.debugWrapper, {
        'z-index': 10000,
        width: 25,
        height: height(this.wrapper),
        position: 'absolute',
        top: 0,
        right: 0,
        background: '#2d2d2d'
      })
      append(this.wrapper, this.debugWrapper)
    }

    this.currentSnapIndex = 0
    this.running = true
    this.refresh()
    this.update()

    load(find(this.wrapper, 'img'), this.refresh.bind(this))
  }

  refresh() {
    if (this.config.debug) console.log('%c WASABI DEBUG ', 'background: #2d2d2d color: #b0dd44')

    this.zones = []
    this.snaps = []

    this.windowHeight = window.innerHeight
    this.halfHeight = this.windowHeight/2

    if (this.wrapper) this.wrapperHeight = height(this.wrapper)
    if (this.config.debug) remove('#wasabi-debug .wasabi-marker')

    if (typeof this.config.zones == 'string') {
      forElements(this.config.zones, (element) => {
        this.addZone({}, element)
      })
    }
    else {
      let i = this.config.zones.length
      while (i--) {
        let zoneConfig = this.config.zones[i]

        if (zoneConfig.selector) {
          forElements(zoneConfig.selector, (element) => {
            this.addZone(zoneConfig, element)
          })
        }
        else {
          this.addZone(zoneConfig)
        }
      }
    }

    if (this.snaps.length) {
      if (!this.scroller) {
        console.error('snap option needs a SmoothScroller instance')
      }

      this.snaps.sort((a, b) => {
        return a.top - b.top
      })

      this.currentSnap = this.snaps[this.currentSnapIndex]
    }
  }

  addZone(zoneConfig, element) {
    let zone = {},
        top, bottom

    if (element) {
      zone.element = element
      zone.selector = zoneConfig.selector
      top = screenPosition(element).top + this.scrollTop
      bottom = top + height(element)

      zone.parallax = []
      forElements(find(element, '[data-wasabi]'), (pxElement) => {
        let options = eval('('+data(pxElement, 'wasabi')+')')

        let toX = (typeof options.x !== 'undefined') ? options.x : ((options.to && options.to.x) || 0),
            toY = (typeof options.y !== 'undefined') ? options.y : ((options.to && options.to.y) || 0),
            fromX = (options.from && options.from.x) || 0,
            fromY = (options.from && options.from.y) || 0,
            parentSize = size(element.parentNode)

        if (typeof toX == 'string' && toX.indexOf('%') != -1)
            toX = parseInt(toX, 10) * parentSize.width

        if (typeof toY == 'string' && toY.indexOf('%') != -1)
            toY = parseInt(toY, 10) * parentSize.height

        if (typeof fromX == 'string' && fromX.indexOf('%') != -1)
            fromX = parseInt(fromX, 10) * parentSize.width

        if (typeof fromY == 'string' && fromY.indexOf('%') != -1)
            fromY = parseInt(fromY, 10) * parentSize.height

        zone.parallax.push({
            element: pxElement,
            toX: toX,
            toY: toY,
            fromX: fromX,
            fromY: fromY
        })
      })
    }
    else {
      top = zoneConfig.top
      bottom = zoneConfig.bottom
    }

    let offset = defaultify(zoneConfig.offset, this.config.offset)
    zone.offset = {
        top: defaultify(offset.top, offset),
        bottom: defaultify(offset.bottom, offset)
    }

    zone.top = top - zone.offset.top
    zone.bottom = bottom + zone.offset.bottom

    if (this.config.debug) {
      let color = randomColor()

      console.log(zone.selector +' %c ' + color, 'color:'+color)
      console.log(zone.element)

      let topDebug = createElement(`<div class="wasabi-marker"></div>`)
      append(this.debugWrapper, topDebug)
      style(topDebug, {
        position: 'absolute',
        top: zone.top,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })

      let bottomDebug = clone(topDebug)
      append(this.debugWrapper, bottomDebug)
      style(bottomDebug, {
        'z-index': 9999,
        position: 'absolute',
        top: zone.bottom,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })
    }

    zone.size = zone.bottom - zone.top
    zone.handle = zoneConfig.handle || this.config.handle
    zone.progress = zoneConfig.progress || this.config.progress
    zone.snap = zoneConfig.snap || this.config.snap
    zone.handler = zoneConfig.handler || this.config.handler
    zone.enter = zoneConfig.enter || this.config.enter
    zone.leave = zoneConfig.leave || this.config.leave

    if (zoneConfig.tween) {
      zone.tween = zoneConfig.tween
      if (zone.tween.pause) zone.tween.pause()
    }

    if (zoneConfig.progressTween) {
      zone.progressTween = zoneConfig.progressTween
      if (zone.progressTween.pause) zone.progressTween.pause()
    }

    let handles = {}
    let handleForward = zone.handle.forward || zone.handle
    handles.forward = {
      top: handleForward.top || handleForward,
      bottom: handleForward.bottom || handleForward
    }
    let handleBackward = zone.handle.forward || zone.handle
    handles.backward = {
      top: handleBackward.top || handleBackward,
      bottom: handleBackward.bottom || handleBackward
    }

    if (handles.forward.top == 'middle') {
      zone.forwardTop = zone.top + this.halfHeight
    }
    else if (handles.forward.top == 'bottom') {
      zone.forwardTop = zone.top - this.windowHeight
    }
    else {
      zone.forwardTop = zone.top
    }

    if (handles.forward.bottom == 'middle') {
      zone.forwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.forward.bottom == 'bottom') {
      zone.forwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.forwardBottom = zone.bottom
    }

    zone.forwardSize = Math.max(this.config.stepMinSize, zone.forwardTop)

    if (handles.backward.top == 'middle') {
      zone.backwardTop = zone.top + this.halfHeight
    }
    else if (handles.backward.top == 'bottom') {
      zone.backwardTop = zone.top - this.windowHeight
    }
    else {
      zone.backwardTop = zone.top
    }

    if (handles.backward.bottom == 'middle') {
      zone.backwardBottom = zone.bottom + this.halfHeight
    }
    else if (handles.backward.bottom == 'bottom') {
      zone.backwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.backwardBottom = zone.bottom
    }

    zone.backwardSize = Math.max(this.config.stepMinSize, zone.backwardBottom - zone.backwardTop)

    if (zone.snap) this.snaps.push(zone)

    this.zones.push(zone)
  }

  testSnapping(scrollTarget) {
    if (this.scroller.scrollDisabled || !this.snaps.length) return

    let previous = this.snaps[this.currentSnapIndex-1],
        next = this.snaps[this.currentSnapIndex+1]

    if (previous && scrollTarget.y < this.currentSnap.top) {
      --this.currentSnapIndex
      this.currentSnap = previous

      this.scroller.scrollTarget.y = this.currentSnap.top + this.currentSnap.offset.top
      this.scroller.scrollTo({
        x: 0,
        y: (previous.bottom - this.currentSnap.offset.bottom) - Math.min(previous.size, this.windowHeight)-1
      })
    }
    else if (next && scrollTarget.y > this.currentSnap.bottom - this.windowHeight) {
      ++this.currentSnapIndex
      this.currentSnap = next

      this.scroller.scrollTarget.y = this.currentSnap.bottom - this.windowHeight - this.currentSnap.offset.bottom
      this.scroller.scrollTo({
        x: 0,
        y: (next.top + this.currentSnap.offset.top)+1
      })
    }
  }

  onScroller(scrollTarget) {
    this.scrollTop = this.scroller.scroll.y
    this.testSnapping(scrollTarget)
  }

  onScrollEvent(event) {
    this.scrollTop = -screenPosition(this.wrapper).top
  }

  update() {
    if (!this.running) return

    let i = this.zones.length,
        direction = this.previousScrollTop < this.scrollTop ? 'forward' : 'backward'

    while (i--) {
      let zone = this.zones[i], entered, progress

      progress = (this.scrollTop - zone[direction+'Top'])/zone[direction+'Size']

      entered = progress >= 0 && progress <= 1

      if (!zone.entered && entered) {
        if (zone.tween) zone.tween.resume()
        if(zone.handler) zone.handler('enter', direction, zone.selector, zone.element)
        if(zone.enter) zone.enter(direction, zone.selector, zone.element)

        if(zone.snap) {
            this.currentSnapIndex = this.snaps.indexOf(zone)
            this.currentSnap = this.snaps[this.currentSnapIndex]
        }
      }
      else if (zone.entered && !entered) {
          if(zone.handler) zone.handler('leave', direction, zone.selector, zone.element)
        if(zone.leave) zone.leave(direction, zone.selector, zone.element)
      }

      forEach(zone.parallax, (item) => {
        transform(item.element, {
          x: item.fromX + (item.toX - item.fromX) * progress,
          y: item.fromY + (item.toY - item.fromY) * progress
        })
      })

      zone.entered = entered
      if (zone.entered) {
        if (zone.progress) zone.progress(direction, progress, zone.selector)
        if (zone.progressTween && zone.progressTween.progress) zone.progressTween.progress(progress)
      }
    }

    this.previousScrollTop = this.scrollTop

    this.updateRequest = requestAnimationFrame(this.update.bind(this))
  }

  kill() {
    this.running = false

    unresize(this.resizeCallback)
    cancelAnimationFrame(this.updateRequest)

    remove(this.debugWrapper)

    if (this.scrollEventsCallback) {
      unscroll(this.scrollEventsCallback)
    }
    else if (this.scrollerCallback) {
      this.scroller.off('update', this.scrollerCallback)
    }

    let i = this.zones.length
    while(i--) {
      let zone = this.zones[i]

      if (zone.tween) this.killTimeline(zone.tween)
      if (zone.progressTween) this.killTimeline(zone.progressTween)
    }

    this.zones = this.snaps = null
  }

  concatenateVars(object) {
    if (!object) return

    let keys = Object.keys(object),
        i = keys.length,
        vars = []

    while(i--) {
        if (typeof object[keys[i]] == 'object') {
            vars = vars.concat(this.concatenateVars(object[keys[i]]))
        }
        else {
            let key = keys[i]
            vars.push(key == 'x' || key == 'y' || key == 'scale' || key == 'rotate' ? 'transform' : key)
        }
    }

    return vars
  }

  killTimeline(timeline) {
    let tweens = timeline.getChildren()
    timeline.kill()

    let i = tweens.length, tween
    while(i--) {
      tween = tweens[i]

      if (tween.target) {
        TweenLite.set(tween.target, {
          clearProps: this.concatenateVars(tween.vars).join(',')
        })
      }
    }
  }
}

export default Wasabi
<commit_msg>fix wasabi handles<commit_after>import forEach from '../core/for-each'
import forElements from '../core/for-elements'

import remove from '../dom/remove'
import data from '../dom/data'
import find from '../dom/find'
import createElement from '../dom/create-element'
import append from '../dom/append'
import clone from '../dom/clone'

import style from '../styles/style'
import screenPosition from '../styles/screen-position'
import height from '../styles/height'
import transform from '../styles/transform'
import size from '../styles/size'

import resize from '../events/resize'
import unresize from '../events/unresize'
import load from '../events/load'
import scroll from '../events/scroll'
import unscroll from '../events/unscroll'

import defaultify from '../utils/defaultify'

let defaults = {
  debug: false,
  offset: 0,
  ease: 0.2,
  stepMinSize: 5,
  snapOffset: 0,
  handle: {
    top: 'top',
    bottom: 'bottom'
  }
}

function randomColor () {
    return '#' + Math.floor(Math.random()*16777215).toString(16)
}

//Scroll manager
export class Wasabi {
  constructor(config) {
    this.config = defaultify(config, defaults)

    this.wrapper = this.scroller ? this.scroller.wrapper : document.body

    if (!this.config.scroller) {
      this.wrapper = document.body
      this.scrollTop = this.previousScrollTop = screenPosition(this.wrapper).top

      this.scrollEventsCallback = scroll(this.onScrollEvent.bind(this))
    }
    else {
        this.scroller = this.config.scroller

        this.wrapper = this.scroller.wrapper
        this.scrollTop = this.previousScrollTop = this.scroller.scroll.y

        this.scrollerCallback = this.onScroller.bind(this)
        this.scroller.on('update', this.scrollerCallback)
    }

    this.resizeCallback = resize(() => {
        requestAnimationFrame(this.refresh.bind(this))
    })

    if (this.config.debug) {
      this.debugWrapper = createElement('

')
      style(this.debugWrapper, {
        'z-index': 10000,
        width: 25,
        height: height(this.wrapper),
        position: 'absolute',
        top: 0,
        right: 0,
        background: '#2d2d2d'
      })
      append(this.wrapper, this.debugWrapper)
    }

    this.currentSnapIndex = 0
    this.running = true
    this.refresh()
    this.update()

    load(find(this.wrapper, 'img'), this.refresh.bind(this))
  }

  refresh() {
    if (this.config.debug) console.log('%c WASABI DEBUG ', 'background: #2d2d2d color: #b0dd44')

    this.zones = []
    this.snaps = []

    this.windowHeight = window.innerHeight
    this.halfHeight = this.windowHeight/2

    if (this.wrapper) this.wrapperHeight = height(this.wrapper)
    if (this.config.debug) remove('#wasabi-debug .wasabi-marker')

    if (typeof this.config.zones == 'string') {
      forElements(this.config.zones, (element) => {
        this.addZone({}, element)
      })
    }
    else {
      let i = this.config.zones.length
      while (i--) {
        let zoneConfig = this.config.zones[i]

        if (zoneConfig.selector) {
          forElements(zoneConfig.selector, (element) => {
            this.addZone(zoneConfig, element)
          })
        }
        else {
          this.addZone(zoneConfig)
        }
      }
    }

    if (this.snaps.length) {
      if (!this.scroller) {
        console.error('snap option needs a SmoothScroller instance')
      }

      this.snaps.sort((a, b) => {
        return a.top - b.top
      })

      this.currentSnap = this.snaps[this.currentSnapIndex]
    }

    console.log(this.zones)
  }

  addZone(zoneConfig, element) {
    let zone = {},
        top, bottom

    if (element) {
      zone.element = element
      zone.selector = zoneConfig.selector
      top = screenPosition(element).top + this.scrollTop
      bottom = top + height(element)

      zone.parallax = []
      forElements(find(element, '[data-wasabi]'), (pxElement) => {
        let options = eval('('+data(pxElement, 'wasabi')+')')

        let toX = (typeof options.x !== 'undefined') ? options.x : ((options.to && options.to.x) || 0),
            toY = (typeof options.y !== 'undefined') ? options.y : ((options.to && options.to.y) || 0),
            fromX = (options.from && options.from.x) || 0,
            fromY = (options.from && options.from.y) || 0,
            parentSize = size(element.parentNode)

        if (typeof toX == 'string' && toX.indexOf('%') != -1)
            toX = parseInt(toX, 10) * parentSize.width

        if (typeof toY == 'string' && toY.indexOf('%') != -1)
            toY = parseInt(toY, 10) * parentSize.height

        if (typeof fromX == 'string' && fromX.indexOf('%') != -1)
            fromX = parseInt(fromX, 10) * parentSize.width

        if (typeof fromY == 'string' && fromY.indexOf('%') != -1)
            fromY = parseInt(fromY, 10) * parentSize.height

        zone.parallax.push({
            element: pxElement,
            toX: toX,
            toY: toY,
            fromX: fromX,
            fromY: fromY
        })
      })
    }
    else {
      top = zoneConfig.top
      bottom = zoneConfig.bottom
    }

    let offset = defaultify(zoneConfig.offset, this.config.offset)
    zone.offset = {
        top: defaultify(offset.top, offset),
        bottom: defaultify(offset.bottom, offset)
    }

    zone.top = top - zone.offset.top
    zone.bottom = bottom + zone.offset.bottom

    if (this.config.debug) {
      let color = randomColor()

      console.log(zone.selector +' %c ' + color, 'color:'+color)
      console.log(zone.element)

      let topDebug = createElement(`<div class="wasabi-marker"></div>`)
      append(this.debugWrapper, topDebug)
      style(topDebug, {
        position: 'absolute',
        top: zone.top,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })

      let bottomDebug = clone(topDebug)
      append(this.debugWrapper, bottomDebug)
      style(bottomDebug, {
        'z-index': 9999,
        position: 'absolute',
        top: zone.bottom,
        right: 0,
        width: 25,
        height: 2,
        background: color
      })
    }

    zone.size = zone.bottom - zone.top
    zone.handle = zoneConfig.handle || this.config.handle
    zone.progress = zoneConfig.progress || this.config.progress
    zone.snap = zoneConfig.snap || this.config.snap
    zone.handler = zoneConfig.handler || this.config.handler
    zone.enter = zoneConfig.enter || this.config.enter
    zone.leave = zoneConfig.leave || this.config.leave

    if (zoneConfig.tween) {
      zone.tween = zoneConfig.tween
      if (zone.tween.pause) zone.tween.pause()
    }

    if (zoneConfig.progressTween) {
      zone.progressTween = zoneConfig.progressTween
      if (zone.progressTween.pause) zone.progressTween.pause()
    }

    let handles = {}
    let handleForward = zone.handle.forward || zone.handle
    handles.forward = {
      top: handleForward.top || handleForward,
      bottom: handleForward.bottom || handleForward
    }
    let handleBackward = zone.handle.forward || zone.handle
    handles.backward = {
      top: handleBackward.top || handleBackward,
      bottom: handleBackward.bottom || handleBackward
    }

    if (handles.forward.top == 'middle') {
      zone.forwardTop = zone.top - this.halfHeight
    }
    else if (handles.forward.top == 'bottom') {
      zone.forwardTop = zone.top - this.windowHeight
    }
    else {
      zone.forwardTop = zone.top
    }

    if (handles.forward.bottom == 'middle') {
      zone.forwardBottom = zone.bottom - this.halfHeight
    }
    else if (handles.forward.bottom == 'bottom') {
      zone.forwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.forwardBottom = zone.bottom
    }

    zone.forwardSize = Math.max(this.config.stepMinSize, zone.forwardTop - zone.forwardBottom)

    if (handles.backward.top == 'middle') {
      zone.backwardTop = zone.top - this.halfHeight
    }
    else if (handles.backward.top == 'bottom') {
      zone.backwardTop = zone.top - this.windowHeight
    }
    else {
      zone.backwardTop = zone.top
    }

    if (handles.backward.bottom == 'middle') {
      zone.backwardBottom = zone.bottom - this.halfHeight
    }
    else if (handles.backward.bottom == 'bottom') {
      zone.backwardBottom = zone.bottom - this.windowHeight
    }
    else {
      zone.backwardBottom = zone.bottom
    }

    zone.backwardSize = Math.max(this.config.stepMinSize, zone.backwardBottom - zone.backwardTop)

    if (zone.snap) this.snaps.push(zone)

    this.zones.push(zone)
  }

  testSnapping(scrollTarget) {
    if (this.scroller.scrollDisabled || !this.snaps.length) return

    let previous = this.snaps[this.currentSnapIndex-1],
        next = this.snaps[this.currentSnapIndex+1]

    if (previous && scrollTarget.y < this.currentSnap.top) {
      --this.currentSnapIndex
      this.currentSnap = previous

      this.scroller.scrollTarget.y = this.currentSnap.top + this.currentSnap.offset.top
      this.scroller.scrollTo({
        x: 0,
        y: (previous.bottom - this.currentSnap.offset.bottom) - Math.min(previous.size, this.windowHeight)-1
      })
    }
    else if (next && scrollTarget.y > this.currentSnap.bottom - this.windowHeight) {
      ++this.currentSnapIndex
      this.currentSnap = next

      this.scroller.scrollTarget.y = this.currentSnap.bottom - this.windowHeight - this.currentSnap.offset.bottom
      this.scroller.scrollTo({
        x: 0,
        y: (next.top + this.currentSnap.offset.top)+1
      })
    }
  }

  onScroller(scrollTarget) {
    this.scrollTop = this.scroller.scroll.y
    this.testSnapping(scrollTarget)
  }

  onScrollEvent(event) {
    this.scrollTop = -screenPosition(this.wrapper).top
  }

  update() {
    if (!this.running) return

    let i = this.zones.length,
        direction = this.previousScrollTop < this.scrollTop ? 'forward' : 'backward'

    while (i--) {
      let zone = this.zones[i], entered, progress

      progress = (this.scrollTop - zone[direction+'Top'])/zone[direction+'Size']

      entered = progress >= 0 && progress <= 1

      if (!zone.entered && entered) {
        if (zone.tween) zone.tween.resume()
        if(zone.handler) zone.handler('enter', direction, zone.selector, zone.element)
        if(zone.enter) zone.enter(direction, zone.selector, zone.element)

        if(zone.snap) {
            this.currentSnapIndex = this.snaps.indexOf(zone)
            this.currentSnap = this.snaps[this.currentSnapIndex]
        }
      }
      else if (zone.entered && !entered) {
          if(zone.handler) zone.handler('leave', direction, zone.selector, zone.element)
        if(zone.leave) zone.leave(direction, zone.selector, zone.element)
      }

      forEach(zone.parallax, (item) => {
        transform(item.element, {
          x: item.fromX + (item.toX - item.fromX) * progress,
          y: item.fromY + (item.toY - item.fromY) * progress
        })
      })

      zone.entered = entered
      if (zone.entered) {
        if (zone.progress) zone.progress(direction, progress, zone.selector)
        if (zone.progressTween && zone.progressTween.progress) zone.progressTween.progress(progress)
      }
    }

    this.previousScrollTop = this.scrollTop

    this.updateRequest = requestAnimationFrame(this.update.bind(this))
  }

  kill() {
    this.running = false

    unresize(this.resizeCallback)
    cancelAnimationFrame(this.updateRequest)

    remove(this.debugWrapper)

    if (this.scrollEventsCallback) {
      unscroll(this.scrollEventsCallback)
    }
    else if (this.scrollerCallback) {
      this.scroller.off('update', this.scrollerCallback)
    }

    let i = this.zones.length
    while(i--) {
      let zone = this.zones[i]

      if (zone.tween) this.killTimeline(zone.tween)
      if (zone.progressTween) this.killTimeline(zone.progressTween)
    }

    this.zones = this.snaps = null
  }

  concatenateVars(object) {
    if (!object) return

    let keys = Object.keys(object),
        i = keys.length,
        vars = []

    while(i--) {
        if (typeof object[keys[i]] == 'object') {
            vars = vars.concat(this.concatenateVars(object[keys[i]]))
        }
        else {
            let key = keys[i]
            vars.push(key == 'x' || key == 'y' || key == 'scale' || key == 'rotate' ? 'transform' : key)
        }
    }

    return vars
  }

  killTimeline(timeline) {
    let tweens = timeline.getChildren()
    timeline.kill()

    let i = tweens.length, tween
    while(i--) {
      tween = tweens[i]

      if (tween.target) {
        TweenLite.set(tween.target, {
          clearProps: this.concatenateVars(tween.vars).join(',')
        })
      }
    }
  }
}

export default Wasabi
<|endoftext|>


<commit_before>import fetchp from 'fetch-jsonp';
import config from '../../config.json';

const baseUrl = 'https://external.api.yle.fi/v1';
/**
 * Fetch the current TV shows using JSONP and fetch JSONP polyfill.
 *
 * @return {Array<Object>} YLE program metadata in unparsed form
 */
export async function fetchCurrentPrograms() {
  const url = new URL(`${baseUrl}/programs/schedules/now.json`);
  const params = url.searchParams;
  params.set('app_id', config.appId);
  params.set('app_key', config.appKey);

  // Fix the jsonp callback function name for service worker compatibility
  const options = {jsonpCallbackFunction: 'jsonp_options'};

  const response = await fetchp(url.href, options);
  // TODO Validate response
  const json = await response.json();
  console.log('data', json.data);
  return json.data;
}
<commit_msg>removed console log<commit_after>import fetchp from 'fetch-jsonp';
import config from '../../config.json';

const baseUrl = 'https://external.api.yle.fi/v1';
/**
 * Fetch the current TV shows using JSONP and fetch JSONP polyfill.
 *
 * @return {Array<Object>} YLE program metadata in unparsed form
 */
export async function fetchCurrentPrograms() {
  const url = new URL(`${baseUrl}/programs/schedules/now.json`);
  const params = url.searchParams;
  params.set('app_id', config.appId);
  params.set('app_key', config.appKey);

  // Fix the jsonp callback function name for service worker compatibility
  const options = {jsonpCallbackFunction: 'jsonp_options'};

  const response = await fetchp(url.href, options);
  // TODO Validate response
  const json = await response.json();
  return json.data;
}
<|endoftext|>


<commit_before>'use strict';

class Whirl {

  /**
   * Sets all property defaults, custom values
   *
   * @param {Object} zone
   * - The drop zone html element
   */
  constructor(zone = {}) {

    // Inital values
    this.start = 0;
    this.total = 0;
    this.current = 0;
    this.min = 5;
    this.dragging = false;
    this.images = {};
    this.size = {
      width: 0,
      height: window.outerHeight,
    };

    this.canvas = this.createCanvas();

    // Bind methods to the event listeners
    this.canvas.onmousedown = () => this.dragging = true;
    this.canvas.onmouseup = () => this.dragging = false;
    this.canvas.onmousemove = this.changeImage.bind(this);

    this.context = this.canvas.getContext('2d');

    // Set values
    this.zone = zone;

    this.classes = {
      splash: 'whirl-splash',
      loading: 'whirl-loading',
    };

    // Prevent the default drag events
    this.zone.ondragover = this.drag;
    this.zone.ondragenter = this.drag;
    this.zone.ondragleave = this.drag;

    // Add drop listener to the slider element
    this.zone.ondrop = () => this.drop();

    let children = this.zone.children;

    // Check for custom splash and/or loading elments
    if (this.zone.hasChildNodes) {
      for (let i = 0; i < children.length; i++) {
        if (this.zone.children[i].className === this.classes.loading) {
          this.loading = children[i];

          this.hideLoading();
        } else if (this.zone.children[i].className === this.classes.splash) {
          this.splash = children[i];

          this.showSplash();
        }
      }
    }

    // Show splash screen
    this.showSplash();
  }

  /**
   * Creat the canvas element
   *
   * @return {Object}
   * - Referece to the canvas element
   */
  createCanvas() {
    let canvas = this.canvas = document.createElement('canvas');

    return canvas;
  }

  /**
   * Insert the canvas into the DOM
   *
   * @param {Object} size
   * - Width and height to make the canvas
   */
  insertCanvas(size) {
    this.canvas.width = size.width;
    this.canvas.height = size.height;
    this.canvas.style.cursor = 'ew-resize';
    this.canvas.hidden = true;

    // Insert canvas right before the drop zone
    document.body.insertBefore(this.canvas, zone);
  }

  /**
   * Adds active class to the drop zone
   * to indicate that files are being dragged over
   *
   * @param {Object} event
   * - Mouse event
   *
   * @return {Boolean}
   */
  drag(event) {
    event.preventDefault && event.preventDefault();

    let classes = event.target.classList;

    if (!classes.contains('drag-active')) {
      classes.add('drag-active');
    } else if (event.type === 'dragleave') {
      classes.remove('drag-active');
    }

    return false;
  }

  /**
   * Reads the dropped files
   *
   * @param {Object} event
   * - mouse event
   */
  drop(event) {
    event = event || window.event;

    event.preventDefault && event.preventDefault();

    // Don't allow any images to be added after the initial drop
    if (this.total > 0) {
      return alert('You cant add anymore images.');
    }

    // Get the array of files that were dropped in
    let files = event.dataTransfer.files;

    let file;
    let reader;

    this.total = files.length;

    // Make sure the min has been added
    // Some weird bugs occur otherwise
    if (this.total < this.min) {
      this.total = 0;
      files = null;

      event = null;

      return alert('You need to select at least 5 images.');
    }

    this.showLoading();
    this.hideSplash();

    for (let i = 0; i < files.length; i++) {
      file = files[i];

      reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onloadend = this.loadImage.bind(this, reader, i);
    }
  }

  /**
   * Draw the next or previos image
   *
   * @param {Object} event
   * - the mouse event
   */
  changeImage(event) {
    let left = event.offsetX - event.target.offsetLeft;
    let _current;

    if (this.dragging) {

      // Calculate the current position by scaling mouse position
      _current = Math.floor((this.total * left) / this.size.width);

      // Only redraw if we need to go to another image
      if (_current !== this.current) {
        this.current = _current;

        if (_current === 0) {

          // No more images to the left
          this.canvas.style.cursor = 'e-resize';
        } else if (_current === this.total - 1) {
          this.canvas.style.cursor = 'w-resize';
        } else {
          this.canvas.style.cursor = 'ew-resize';
        }

        // Draw the new image
        this.context.drawImage(this.images[_current], 0, 0, this.size.width, this.size.height);
      }
    }
  }

  /**
   * Scale the original image size to defined size
   *
   * @param {Object} image
   * - The original image
   *
   * @return {Object}
   * - The scaled dimensions
   */
  getScaledSize(image) {
    let ratio = image.width / image.height;

    let height = this.size.height;
    let width = height * ratio;

    this.size.width = width;
    this.size.height = height;

    return {
      width: width,
      height: height,
    };
  }

  /**
   * Convert the original image
   * to a scaled version from the canvas
   *
   * @return {String}
   * - The DataURI of the new scaled image
   */
  getScaledImage() {
    return this.canvas.toDataURL();
  }

  /**
   * Load the image from the reader
   * build the images array
   *
   * @param {Object} reader
   * - The reader object which holds the original image
   *
   * @param {Integer} index
   * - Current index relative to all files
   */
  loadImage(reader, index) {
    let image = new Image();
    let scaled = new Image();

    image.onload = () => {

      // Insert the canvas, based on image dimensions
      this.insertCanvas(this.getScaledSize(image));

      // Insert the original image into the canvas context
      this.insertImage(image);

      scaled.onload = () => {

        // scaled.name = file.name;
        scaled.width = image.width;
        scaled.height = image.height;

        this.images[index] = scaled;

        // On the last image
        if (index === this.total - 1) {
          this.canvas.hidden = false;

          // hide the loading screen
          this.hideLoading();

          this.hideZone();
        }

        // Remove active class from drag element
        this.zone.classList.remove('drag-active');
      };

      scaled.src = this.getScaledImage();
    };

    image.src = reader.result;

    if (image.complete || image.readyState === 4) image.onload();
  }

  /**
   * Draw the scaled image into the canvas
   *
   * @param {Object} image
   * - The original image
   */
  insertImage(image) {
    let size = this.getScaledSize(image);

    this.context.drawImage(image, 0, 0, size.width, size.height);
  }

  /**
   * Remove the drop zone from the DOM
   */
  hideZone() {
    this.zone && document.body.removeChild(this.zone);
  }

  /**
   * Display the splash screen
   */
  showSplash() {
    let node;

    // Custom splash element wasn't found
    // so create a default one
    if (!this.splash) {
      node = document.createElement('div');

      node.className = this.classes.splash;
      node.textContent = 'Drag & drop your images';

      // Append into the slider element
      this.zone.appendChild(node);

      this.splash = node;
    }

    this.splash.hidden = false;
  }

  /**
   * Hide the splash screen
   */
  hideSplash() {
    this.splash.hidden = true;
  }

  /**
   * Display the loading screen
   */
  showLoading() {
    let node;

    if (!this.loading) {
      node = document.createElement('div');

      node.className = this.classes.loading;
      node.textContent = 'Loading...';

      // Append into the slider element
      this.zone.appendChild(node);

      this.loading = node;
    } else {
      this.loading.hidden = false;
    }
  }

  /**
   * Hide the loading screen
   */
  hideLoading() {

    // If the node exists, remove it
    this.loading.hidden = true;
  }
}
<commit_msg>Add license comment<commit_after>/*!
 * (c) 2015 Jaden Dessureault
 * This code is licensed under MIT license (see LICENSE for details)
 */

'use strict';

class Whirl {

  /**
   * Sets all property defaults, custom values
   *
   * @param {Object} zone
   * - The drop zone html element
   */
  constructor(zone = {}) {

    // Inital values
    this.start = 0;
    this.total = 0;
    this.current = 0;
    this.min = 5;
    this.dragging = false;
    this.images = {};
    this.size = {
      width: 0,
      height: window.outerHeight,
    };

    this.canvas = this.createCanvas();

    // Bind methods to the event listeners
    this.canvas.onmousedown = () => this.dragging = true;
    this.canvas.onmouseup = () => this.dragging = false;
    this.canvas.onmousemove = this.changeImage.bind(this);

    this.context = this.canvas.getContext('2d');

    // Set values
    this.zone = zone;

    this.classes = {
      splash: 'whirl-splash',
      loading: 'whirl-loading',
    };

    // Prevent the default drag events
    this.zone.ondragover = this.drag;
    this.zone.ondragenter = this.drag;
    this.zone.ondragleave = this.drag;

    // Add drop listener to the slider element
    this.zone.ondrop = () => this.drop();

    let children = this.zone.children;

    // Check for custom splash and/or loading elments
    if (this.zone.hasChildNodes) {
      for (let i = 0; i < children.length; i++) {
        if (this.zone.children[i].className === this.classes.loading) {
          this.loading = children[i];

          this.hideLoading();
        } else if (this.zone.children[i].className === this.classes.splash) {
          this.splash = children[i];

          this.showSplash();
        }
      }
    }

    // Show splash screen
    this.showSplash();
  }

  /**
   * Creat the canvas element
   *
   * @return {Object}
   * - Referece to the canvas element
   */
  createCanvas() {
    let canvas = this.canvas = document.createElement('canvas');

    return canvas;
  }

  /**
   * Insert the canvas into the DOM
   *
   * @param {Object} size
   * - Width and height to make the canvas
   */
  insertCanvas(size) {
    this.canvas.width = size.width;
    this.canvas.height = size.height;
    this.canvas.style.cursor = 'ew-resize';
    this.canvas.hidden = true;

    // Insert canvas right before the drop zone
    document.body.insertBefore(this.canvas, zone);
  }

  /**
   * Adds active class to the drop zone
   * to indicate that files are being dragged over
   *
   * @param {Object} event
   * - Mouse event
   *
   * @return {Boolean}
   */
  drag(event) {
    event.preventDefault && event.preventDefault();

    let classes = event.target.classList;

    if (!classes.contains('drag-active')) {
      classes.add('drag-active');
    } else if (event.type === 'dragleave') {
      classes.remove('drag-active');
    }

    return false;
  }

  /**
   * Reads the dropped files
   *
   * @param {Object} event
   * - mouse event
   */
  drop(event) {
    event = event || window.event;

    event.preventDefault && event.preventDefault();

    // Don't allow any images to be added after the initial drop
    if (this.total > 0) {
      return alert('You cant add anymore images.');
    }

    // Get the array of files that were dropped in
    let files = event.dataTransfer.files;

    let file;
    let reader;

    this.total = files.length;

    // Make sure the min has been added
    // Some weird bugs occur otherwise
    if (this.total < this.min) {
      this.total = 0;
      files = null;

      event = null;

      return alert('You need to select at least 5 images.');
    }

    this.showLoading();
    this.hideSplash();

    for (let i = 0; i < files.length; i++) {
      file = files[i];

      reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onloadend = this.loadImage.bind(this, reader, i);
    }
  }

  /**
   * Draw the next or previos image
   *
   * @param {Object} event
   * - the mouse event
   */
  changeImage(event) {
    let left = event.offsetX - event.target.offsetLeft;
    let _current;

    if (this.dragging) {

      // Calculate the current position by scaling mouse position
      _current = Math.floor((this.total * left) / this.size.width);

      // Only redraw if we need to go to another image
      if (_current !== this.current) {
        this.current = _current;

        if (_current === 0) {

          // No more images to the left
          this.canvas.style.cursor = 'e-resize';
        } else if (_current === this.total - 1) {
          this.canvas.style.cursor = 'w-resize';
        } else {
          this.canvas.style.cursor = 'ew-resize';
        }

        // Draw the new image
        this.context.drawImage(this.images[_current], 0, 0, this.size.width, this.size.height);
      }
    }
  }

  /**
   * Scale the original image size to defined size
   *
   * @param {Object} image
   * - The original image
   *
   * @return {Object}
   * - The scaled dimensions
   */
  getScaledSize(image) {
    let ratio = image.width / image.height;

    let height = this.size.height;
    let width = height * ratio;

    this.size.width = width;
    this.size.height = height;

    return {
      width: width,
      height: height,
    };
  }

  /**
   * Convert the original image
   * to a scaled version from the canvas
   *
   * @return {String}
   * - The DataURI of the new scaled image
   */
  getScaledImage() {
    return this.canvas.toDataURL();
  }

  /**
   * Load the image from the reader
   * build the images array
   *
   * @param {Object} reader
   * - The reader object which holds the original image
   *
   * @param {Integer} index
   * - Current index relative to all files
   */
  loadImage(reader, index) {
    let image = new Image();
    let scaled = new Image();

    image.onload = () => {

      // Insert the canvas, based on image dimensions
      this.insertCanvas(this.getScaledSize(image));

      // Insert the original image into the canvas context
      this.insertImage(image);

      scaled.onload = () => {

        // scaled.name = file.name;
        scaled.width = image.width;
        scaled.height = image.height;

        this.images[index] = scaled;

        // On the last image
        if (index === this.total - 1) {
          this.canvas.hidden = false;

          // hide the loading screen
          this.hideLoading();

          this.hideZone();
        }

        // Remove active class from drag element
        this.zone.classList.remove('drag-active');
      };

      scaled.src = this.getScaledImage();
    };

    image.src = reader.result;

    if (image.complete || image.readyState === 4) image.onload();
  }

  /**
   * Draw the scaled image into the canvas
   *
   * @param {Object} image
   * - The original image
   */
  insertImage(image) {
    let size = this.getScaledSize(image);

    this.context.drawImage(image, 0, 0, size.width, size.height);
  }

  /**
   * Remove the drop zone from the DOM
   */
  hideZone() {
    this.zone && document.body.removeChild(this.zone);
  }

  /**
   * Display the splash screen
   */
  showSplash() {
    let node;

    // Custom splash element wasn't found
    // so create a default one
    if (!this.splash) {
      node = document.createElement('div');

      node.className = this.classes.splash;
      node.textContent = 'Drag & drop your images';

      // Append into the slider element
      this.zone.appendChild(node);

      this.splash = node;
    }

    this.splash.hidden = false;
  }

  /**
   * Hide the splash screen
   */
  hideSplash() {
    this.splash.hidden = true;
  }

  /**
   * Display the loading screen
   */
  showLoading() {
    let node;

    if (!this.loading) {
      node = document.createElement('div');

      node.className = this.classes.loading;
      node.textContent = 'Loading...';

      // Append into the slider element
      this.zone.appendChild(node);

      this.loading = node;
    } else {
      this.loading.hidden = false;
    }
  }

  /**
   * Hide the loading screen
   */
  hideLoading() {

    // If the node exists, remove it
    this.loading.hidden = true;
  }
}
<|endoftext|>