<commit_before>import SocialSharingNetwork from './social-sharing-network';
import BaseNetworks from './networks.json';
import Vue from 'vue';

const inBrowser = typeof window !== 'undefined';
let $window = inBrowser ? window : null;

export function mockWindow (self) {
  $window = self || window; // mock window for unit testing
}

export default {
  props: {
    /**
     * URL to share.
     * @var string
     */
    url: {
      type: String,
      default: inBrowser ? window.location.href : ''
    },

    /**
     * Sharing title, if available by network.
     * @var string
     */
    title: {
      type: String,
      default: ''
    },

    /**
     * Sharing description, if available by network.
     * @var string
     */
    description: {
      type: String,
      default: ''
    },

    /**
     * Facebook quote
     * @var string
     */
    quote: {
      type: String,
      default: ''
    },

    /**
     * Twitter hashtags
     * @var string
     */
    hashtags: {
      type: String,
      default: ''
    },

    /**
     * Twitter user.
     * @var string
     */
    twitterUser: {
      type: String,
      default: ''
    },

    /**
     * Flag that indicates if counts should be retrieved.
     * - NOT WORKING IN CURRENT VERSION
     * @var mixed
     */
    withCounts: {
      type: [String, Boolean],
      default: false
    },

    /**
     * Google plus key.
     * @var string
     */
    googleKey: {
      type: String,
      default: undefined
    },

    /**
     * Pinterest Media URL.
     * Specifies the image/media to be used.
     */
    media: {
      type: String,
      default: ''
    },

    /**
     * Network sub component tag.
     * Default to span tag
     */
    networkTag: {
      type: String,
      default: 'span'
    },

    /**
     * Additional or overridden networks.
     * Default to BaseNetworks
     */
    networks: {
      type: Object,
      default: function () {
        return {};
      }
    }
  },

  data () {
    return {
      /**
       * Available sharing networks.
       * @param object
       */
      baseNetworks: BaseNetworks,

      /**
       * Popup settings.
       * @param object
       */
      popup: {
        status: false,
        resizable: true,
        toolbar: false,
        menubar: false,
        scrollbars: false,
        location: false,
        directories: false,
        width: 626,
        height: 436,
        top: 0,
        left: 0,
        window: undefined,
        interval: null
      }
    };
  },

  methods: {
    /**
     * Returns generated sharer url.
     *
     * @param network Social network key.
     */
    createSharingUrl (network) {
      return this.baseNetworks[network].sharer
        .replace(/@url/g, encodeURIComponent(this.url))
        .replace(/@title/g, encodeURIComponent(this.title))
        .replace(/@description/g, encodeURIComponent(this.description))
        .replace(/@quote/g, encodeURIComponent(this.quote))
        .replace(/@hashtags/g, this.generateHashtags(network, this.hashtags))
        .replace(/@media/g, this.media)
        .replace(/@twitteruser/g, this.twitterUser ? '&via=' + this.twitterUser : '');
    },
    /**
     * Encode hashtags for the specified social network.
     *
     * @param  network Social network key
     * @param  hashtags All hashtags specified
     */
    generateHashtags (network, hashtags) {
      if (network === 'facebook') {
        return '%23' + hashtags.split(',')[0];
      }

      return hashtags;
    },
    /**
     * Shares URL in specified network.
     *
     * @param network Social network key.
     */
    share (network) {
      this.openSharer(network, this.createSharingUrl(network));

      this.$root.$emit('social_shares_open', network, this.url);
      this.$emit('open', network, this.url);
    },

    /**
     * Touches network and emits click event.
     *
     * @param network Social network key.
     */
    touch (network) {
      window.open(this.createSharingUrl(network), '_self');

      this.$root.$emit('social_shares_open', network, this.url);
      this.$emit('open', network, this.url);
    },

    /**
     * Opens sharer popup.
     *
     * @param network Social network key
     * @param url Url to share.
     */
    openSharer (network, url) {
      // If a popup window already exist it will be replaced, trigger a close event.
      if (this.popup.window && this.popup.interval) {
        clearInterval(this.popup.interval);

        this.popup.window.close();// Force close (for Facebook)

        this.$root.$emit('social_shares_change', network, this.url);
        this.$emit('change', network, this.url);
      }

      this.popup.window = window.open(
        url,
        'sharer',
        'status=' + (this.popup.status ? 'yes' : 'no') +
        ',height=' + this.popup.height +
        ',width=' + this.popup.width +
        ',resizable=' + (this.popup.resizable ? 'yes' : 'no') +
        ',left=' + this.popup.left +
        ',top=' + this.popup.top +
        ',screenX=' + this.popup.left +
        ',screenY=' + this.popup.top +
        ',toolbar=' + (this.popup.toolbar ? 'yes' : 'no') +
        ',menubar=' + (this.popup.menubar ? 'yes' : 'no') +
        ',scrollbars=' + (this.popup.scrollbars ? 'yes' : 'no') +
        ',location=' + (this.popup.location ? 'yes' : 'no') +
        ',directories=' + (this.popup.directories ? 'yes' : 'no')
      );

      this.popup.window.focus();

      // Create an interval to detect popup closing event
      this.popup.interval = setInterval(() => {
        if (this.popup.window.closed) {
          clearInterval(this.popup.interval);

          this.popup.window = undefined;

          this.$root.$emit('social_shares_close', network, this.url);
          this.$emit('close', network, this.url);
        }
      }, 500);
    }
  },

  /**
   * Merge base networks list with user's list
   */
  beforeMount () {
    this.baseNetworks = Vue.util.extend(this.baseNetworks, this.networks);
  },

  /**
   * Sets popup default dimensions.
   */
  mounted () {
    if (!inBrowser) {
      return;
    }

    /**
     * Center the popup on dual screens
     * http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen/32261263
     */
    const dualScreenLeft = $window.screenLeft !== undefined ? $window.screenLeft : screen.left;
    const dualScreenTop = $window.screenTop !== undefined ? $window.screenTop : screen.top;

    const width = $window.innerWidth ? $window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width);
    const height = $window.innerHeight ? $window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height);

    this.popup.left = ((width / 2) - (this.popup.width / 2)) + dualScreenLeft;
    this.popup.top = ((height / 2) - (this.popup.height / 2)) + dualScreenTop;
  },

  /**
   * Set component aliases for buttons and links.
   */
  components: {
    'network': SocialSharingNetwork
  }
};
<commit_msg>Fix issue #123 with Facebook hashtags (#125)<commit_after>import SocialSharingNetwork from './social-sharing-network';
import BaseNetworks from './networks.json';
import Vue from 'vue';

const inBrowser = typeof window !== 'undefined';
let $window = inBrowser ? window : null;

export function mockWindow (self) {
  $window = self || window; // mock window for unit testing
}

export default {
  props: {
    /**
     * URL to share.
     * @var string
     */
    url: {
      type: String,
      default: inBrowser ? window.location.href : ''
    },

    /**
     * Sharing title, if available by network.
     * @var string
     */
    title: {
      type: String,
      default: ''
    },

    /**
     * Sharing description, if available by network.
     * @var string
     */
    description: {
      type: String,
      default: ''
    },

    /**
     * Facebook quote
     * @var string
     */
    quote: {
      type: String,
      default: ''
    },

    /**
     * Twitter hashtags
     * @var string
     */
    hashtags: {
      type: String,
      default: ''
    },

    /**
     * Twitter user.
     * @var string
     */
    twitterUser: {
      type: String,
      default: ''
    },

    /**
     * Flag that indicates if counts should be retrieved.
     * - NOT WORKING IN CURRENT VERSION
     * @var mixed
     */
    withCounts: {
      type: [String, Boolean],
      default: false
    },

    /**
     * Google plus key.
     * @var string
     */
    googleKey: {
      type: String,
      default: undefined
    },

    /**
     * Pinterest Media URL.
     * Specifies the image/media to be used.
     */
    media: {
      type: String,
      default: ''
    },

    /**
     * Network sub component tag.
     * Default to span tag
     */
    networkTag: {
      type: String,
      default: 'span'
    },

    /**
     * Additional or overridden networks.
     * Default to BaseNetworks
     */
    networks: {
      type: Object,
      default: function () {
        return {};
      }
    }
  },

  data () {
    return {
      /**
       * Available sharing networks.
       * @param object
       */
      baseNetworks: BaseNetworks,

      /**
       * Popup settings.
       * @param object
       */
      popup: {
        status: false,
        resizable: true,
        toolbar: false,
        menubar: false,
        scrollbars: false,
        location: false,
        directories: false,
        width: 626,
        height: 436,
        top: 0,
        left: 0,
        window: undefined,
        interval: null
      }
    };
  },

  methods: {
    /**
     * Returns generated sharer url.
     *
     * @param network Social network key.
     */
    createSharingUrl (network) {
      return this.baseNetworks[network].sharer
        .replace(/@url/g, encodeURIComponent(this.url))
        .replace(/@title/g, encodeURIComponent(this.title))
        .replace(/@description/g, encodeURIComponent(this.description))
        .replace(/@quote/g, encodeURIComponent(this.quote))
        .replace(/@hashtags/g, this.generateHashtags(network, this.hashtags))
        .replace(/@media/g, this.media)
        .replace(/@twitteruser/g, this.twitterUser ? '&via=' + this.twitterUser : '');
    },
    /**
     * Encode hashtags for the specified social network.
     *
     * @param  network Social network key
     * @param  hashtags All hashtags specified
     */
    generateHashtags (network, hashtags) {
      if (network === 'facebook' && hashtags.length > 0) {
        return '%23' + hashtags.split(',')[0];
      }

      return hashtags;
    },
    /**
     * Shares URL in specified network.
     *
     * @param network Social network key.
     */
    share (network) {
      this.openSharer(network, this.createSharingUrl(network));

      this.$root.$emit('social_shares_open', network, this.url);
      this.$emit('open', network, this.url);
    },

    /**
     * Touches network and emits click event.
     *
     * @param network Social network key.
     */
    touch (network) {
      window.open(this.createSharingUrl(network), '_self');

      this.$root.$emit('social_shares_open', network, this.url);
      this.$emit('open', network, this.url);
    },

    /**
     * Opens sharer popup.
     *
     * @param network Social network key
     * @param url Url to share.
     */
    openSharer (network, url) {
      // If a popup window already exist it will be replaced, trigger a close event.
      if (this.popup.window && this.popup.interval) {
        clearInterval(this.popup.interval);

        this.popup.window.close();// Force close (for Facebook)

        this.$root.$emit('social_shares_change', network, this.url);
        this.$emit('change', network, this.url);
      }

      this.popup.window = window.open(
        url,
        'sharer',
        'status=' + (this.popup.status ? 'yes' : 'no') +
        ',height=' + this.popup.height +
        ',width=' + this.popup.width +
        ',resizable=' + (this.popup.resizable ? 'yes' : 'no') +
        ',left=' + this.popup.left +
        ',top=' + this.popup.top +
        ',screenX=' + this.popup.left +
        ',screenY=' + this.popup.top +
        ',toolbar=' + (this.popup.toolbar ? 'yes' : 'no') +
        ',menubar=' + (this.popup.menubar ? 'yes' : 'no') +
        ',scrollbars=' + (this.popup.scrollbars ? 'yes' : 'no') +
        ',location=' + (this.popup.location ? 'yes' : 'no') +
        ',directories=' + (this.popup.directories ? 'yes' : 'no')
      );

      this.popup.window.focus();

      // Create an interval to detect popup closing event
      this.popup.interval = setInterval(() => {
        if (this.popup.window.closed) {
          clearInterval(this.popup.interval);

          this.popup.window = undefined;

          this.$root.$emit('social_shares_close', network, this.url);
          this.$emit('close', network, this.url);
        }
      }, 500);
    }
  },

  /**
   * Merge base networks list with user's list
   */
  beforeMount () {
    this.baseNetworks = Vue.util.extend(this.baseNetworks, this.networks);
  },

  /**
   * Sets popup default dimensions.
   */
  mounted () {
    if (!inBrowser) {
      return;
    }

    /**
     * Center the popup on dual screens
     * http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen/32261263
     */
    const dualScreenLeft = $window.screenLeft !== undefined ? $window.screenLeft : screen.left;
    const dualScreenTop = $window.screenTop !== undefined ? $window.screenTop : screen.top;

    const width = $window.innerWidth ? $window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width);
    const height = $window.innerHeight ? $window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height);

    this.popup.left = ((width / 2) - (this.popup.width / 2)) + dualScreenLeft;
    this.popup.top = ((height / 2) - (this.popup.height / 2)) + dualScreenTop;
  },

  /**
   * Set component aliases for buttons and links.
   */
  components: {
    'network': SocialSharingNetwork
  }
};
<|endoftext|>


<commit_before>/* globals __DEV__ */
import Phaser from 'phaser';
import Collectible from '../sprites/Collectible';

import config from '../config';
import collectibles from '../config/collectibles';
import faces from '../config/faces';

const DEBUG_GAME = false;

// TODO Move to a file
const gameConfig = {
    drinkDecrementPerTimeUnit: 1,
    drinkDecrementTimeMilliSeconds: 1000,
    drinkMinimumAmount: -250,
    drinkMaximumAmount: 250,
    foodDecrementPerMove: 2,
    foodMinimumAmount: -250,
    foodMaximumAmount: 250,
    playerInitialMoveSpeed: 400,
    playerMaxDragMultiplier: 0.4,
    playerYPosition: 860,
    minimumSpawnTime: 1800,
    maximumSpawnTime: 3300,
};

function randomIntegerBetween(min, max) {
    return Math.floor((Math.random() * (max - min)) + min);
}

export default class extends Phaser.State {
    init() {
        this._player = null;
        this._collectibleGroup = null;
        this._spawnTimer = 0;
        this._nextSpawnTime = 0;
        this._drinkTimer = 0;
        this._fontStyle = null;
        this._face = null;

        this._scoreText = null;
        this._foodBar = null;
        this._drinksBar = null;
        this._score = 0;
        this._cursors = null;
        this._facing = 'idle';
        this._foodMaximumTotal = gameConfig.foodMaximumAmount - gameConfig.foodMinimumAmount;
        this._drinkMaximumTotal = gameConfig.drinkMaximumAmount - gameConfig.drinkMinimumAmount;
        this._foodAmount = this._foodMaximumTotal / 2;
        this._drinkAmount = this._drinkMaximumTotal / 2;
    }

    create() {
        this.game.time.advancedTiming = true;
        this.physics.startSystem(Phaser.Physics.ARCADE);

        this.physics.arcade.gravity.y = 200;

        this.add.sprite(0, 0, 'background-game');
        this.add.button(
            config.gameWidth * 0.86,
            config.gameHeight * 0.03,
            'pause-button',
            this.managePause,
            this,
            1,
            0
        );

        this._fontStyle = {
            font: '40px Arial',
            fill: '#FFCC00',
            stroke: '#333',
            strokeThickness: 5,
            align: 'center',
        };

        this._spawnTimer = 0;
        this._drinkTimer = 0;

        this._collectibleGroup = this.add.group();

        this.initialisePlayer();

        this.initialiseUi();

        this.setDrinksBar();
        this.setFoodBar();

//        this.createBannerText();
    }

    managePause() {
        this.game.paused = true;
        const pausedText = this.add.text(
                300,
                400,
                'Game paused.\nTap anywhere to continue.',
                this._fontStyle
        );

        this.input.onDown.add(() => {
            pausedText.destroy();
            this.game.paused = false;
        }, this);
    }

    update() {
        this._spawnTimer += this.time.elapsed;

        this.updatePlayer();
        this.updateCollectibles();
        this.updateDrinkAmount();

        this.updateFace();

        if (this.isGameOver()) {
            this.showGameOver();
        }
    }

    // TODO - needs working out properly once rules are clear
    updateFace() {
        const foodDifference = this._foodAmount - this._drinkAmount;
        const drinkDifference = this._drinkAmount - this._foodAmount;
        let newFace = faces.FACE_NEUTRAL;

        if (foodDifference > 200) {
            newFace = faces.FACE_SICK;
        } else if (foodDifference > 100) {
            newFace = faces.FACE_GLUTTONOUS;
        } else if (foodDifference > 50) {
            newFace = faces.FACE_GOURMAND;
        }

        if (drinkDifference > 200) {
            newFace = faces.FACE_HAMMERED;
        } else if (drinkDifference > 100) {
            newFace = faces.FACE_DRUNK;
        } else if (drinkDifference > 50) {
            newFace = faces.FACE_MERRY;
        }

        this.setFace(newFace);
    }

    setFace(faceFrame) {
        if (faceFrame !== this._face.frame) {
            this._face.frame = faceFrame;
        }
    }

    isGameOver() {
        const noDrinkPoints = (this._drinkAmount <= 0);
        const noFoodPoints = (this._foodAmount <= 0);
        return noDrinkPoints || noFoodPoints;
    }

    showGameOver() {
        this.add.sprite((config.gameWidth - 594) / 2, (config.gameHeight - 271) / 2, 'game-over');
        this.game.paused = true;
    }

    updateCollectibles() {
        if (this._spawnTimer > this._nextSpawnTime) {
            this._spawnTimer = 0;
            this.spawnCollectible();
        }

        this._collectibleGroup.forEach((collectible) => {
            collectible.update();
        });

        this.physics.arcade.overlap(
            this._player,
            this._collectibleGroup,
            this.collideWithPlayer,
            null,
            this
        );
    }

    getDrunkSpeedMultiplier() {
        const drunkPercentage = this._drinkAmount / gameConfig.drinkMaximumAmount;
        const speedMultiplier = 1 - (drunkPercentage * gameConfig.playerMaxDragMultiplier);
        return speedMultiplier;
    }

    updatePlayer() {
        const speedMultiplier = this.getDrunkSpeedMultiplier();
        const moveSpeed = gameConfig.playerInitialMoveSpeed * speedMultiplier;
        this._player.body.velocity.x = 0;

        if (this.isLeftPressed()) {
            this._player.body.velocity.x = -moveSpeed;

            if (this._facing !== 'left') {
                this._facing = 'left';
                this._player.animations.play('move-left');
                this.updateFoodAmount();
            }
        } else if (this.isRightPressed()) {
            this._player.body.velocity.x = moveSpeed;

            if (this._facing !== 'right') {
                this._facing = 'right';
                this._player.animations.play('move-right');
                this.updateFoodAmount();
            }
        } else if (this._facing !== 'idle') {
            this._facing = 'idle';
            this._player.animations.play('idle');
        }
    }

    isLeftPressed() {
        return this._cursors.left.isDown || this.hasTouchedLeft();
    }

    isRightPressed() {
        return this._cursors.right.isDown || this.hasTouchedRight();
    }

    hasTouchedLeft() {
        if (this.input.activePointer.isDown) {
            return (this.input.activePointer.worldX < (config.gameWidth / 2));
        }

        return false;
    }

    hasTouchedRight() {
        if (this.input.activePointer.isDown) {
            return (this.input.activePointer.worldX > (config.gameWidth / 2));
        }

        return false;
    }

    initialisePlayer() {
        this._player = this.add.sprite(config.gameWidth * 0.5, gameConfig.playerYPosition, 'player');
        this._player.scale = {x: 1, y: 1};
        this._player.animations.add('idle', [0, 1, 2, 3], 10, true);
        this._player.animations.add('move-left', [4, 5, 6, 7], 10, true);
        this._player.animations.add('move-right', [8, 9, 10, 11], 10, true);
        this._player.animations.play('idle');

        this._cursors = this.game.input.keyboard.createCursorKeys();
        this.game.physics.enable(this._player, Phaser.Physics.ARCADE);
        this._player.body.allowGravity = false;
        this._player.body.collideWorldBounds = true;

        // Original player spritesheet: 436x436
        this._player.body.setSize(218, 436, 109, 0);

        this._player.anchor.setTo(0.5, 0);
    }

    // TODO - Unify UI positions
    initialiseUi() {
        this.add.sprite(0, 0, 'frame');

        const faceBg = {
            x: config.gameWidth * 0.83,
            y: config.gameHeight * 0.89
        };
        const faceBackground = this.add.sprite(faceBg.x, faceBg.y, 'background-face');
        faceBackground.anchor.setTo(0.5, 0.5);

        this._face = this.add.sprite(faceBg.x + 4, faceBg.y + 10, 'faces');
        this._face.anchor.setTo(0.5, 0.5);
        this.setFace(faces.FACE_NEUTRAL);

        const scorePos = {
            x: faceBg.x,
            y: faceBg.y + 130
        };
        const scoreBar = this.add.sprite(scorePos.x, scorePos.y, 'score-bar');
        scoreBar.anchor.setTo(0.5, 0.5);

        this.createScoreText(scorePos);

        this.add.sprite(config.gameWidth * 0.02, config.gameHeight * 0.82, 'food-bar');
        this._foodBar = this.add.sprite(config.gameWidth * 0.18, config.gameHeight * 0.845, 'bars', 0);
        this._foodBar.scale.x = 1;
        this._foodBar.anchor.setTo(0, 0.5);

        this.add.sprite(config.gameWidth * 0.03, config.gameHeight * 0.9, 'drinks-bar');
        this._drinksBar = this.add.sprite(config.gameWidth * 0.18, config.gameHeight * 0.935, 'bars', 2);
        this._drinksBar.scale.x = 1;
        this._drinksBar.anchor.setTo(0, 0.5);

        this.setDrinksBar();
        this.setFoodBar();
    }

    collideWithPlayer(player, collectible) {
        this.playerCollectedItem(collectible);
    }

    updateDrinkAmount() {
        this._drinkTimer += this.time.elapsed;
        if (this._drinkTimer > gameConfig.drinkDecrementTimeMilliSeconds) {
            this._drinkTimer = 0;

            this._drinkAmount -= gameConfig.drinkDecrementPerTimeUnit;
            if (this._drinkAmount < 0) {
                this._drinkAmount = 0;
            }

            this.setDrinksBar();
        }
    }

    updateFoodAmount() {
        this._foodAmount -= gameConfig.foodDecrementPerMove;
        if (this._foodAmount < 0) {
            this._foodAmount = 0;
        }
        this.setFoodBar();
    }

    setDrinksBar() {
        this._drinksBar.scale.x = (this._drinkAmount / this._drinkMaximumTotal);
    }

    setFoodBar() {
        this._foodBar.scale.x = (this._foodAmount / this._foodMaximumTotal);
    }

    spawnCollectible() {
        this._nextSpawnTime = randomIntegerBetween(
            gameConfig.minimumSpawnTime,
            gameConfig.maximumSpawnTime
        );

        const dropPos = randomIntegerBetween(config.gameWidth * 0.05, config.gameWidth * 0.95);
        const dropOffset = 0;

        // Choose collectible type
        const foodOrDrink = randomIntegerBetween(0, 100);
        const isFood = (foodOrDrink >= 50);
        const collectibleTypeList = isFood ? collectibles.food : collectibles.drinks;

        const randomTypeIndex = randomIntegerBetween(0, collectibleTypeList.length - 1);
        const collectibleType = collectibleTypeList[randomTypeIndex];

        const collectible = new Collectible({
            game: this,
            x: dropPos,
            y: dropOffset,
            asset: 'collectibles',
            collectibleType,
            onOutOfBounds: this.onCollectibleOutOfBounds,
            isFood
        });

        this._collectibleGroup.add(collectible);
    }

    playerCollectedItem(collectible) {
        collectible.kill();

        const points = collectible.collectibleType.points;
        this._score += points;

        this._scoreText.setText(this._score);

        if (collectible.isFood) {
            this._foodAmount += points;

            if (this._foodAmount > gameConfig.foodMaximumAmount) {
                this._foodAmount = gameConfig.foodMaximumAmount;
            }

            this.setFoodBar();
        } else {
            this._drinkAmount += points;

            if (this._drinkAmount > gameConfig.drinkMaximumAmount) {
                this._drinkAmount = gameConfig.drinkMaximumAmount;
            }

            this.setDrinksBar();
        }
    }

    onCollectibleOutOfBounds(collectible) {
        collectible.kill();
    }

    createBannerText() {
        const bannerText = 'See It Off';
        const banner = this.add.text(this.world.centerX, this.game.height - 80, bannerText);
        banner.font = 'eraserregular';
        banner.padding.set(10, 16);
        banner.fontSize = 80;
        banner.fill = '#fff';
        banner.smoothed = false;
        banner.anchor.setTo(0.5);
    }

    createScoreText(position) {
        this._scoreText = this.add.text(position.x, position.y, '0', this._fontStyle);
        this._scoreText.font = 'dinregular';
        this._scoreText.fontSize = 40;
        this._scoreText.fill = '#111';
        this._scoreText.smoothed = false;
        this._scoreText.anchor.setTo(0.5);
    }

    render() {
        if (DEBUG_GAME) {
            this.game.debug.text(this.game.time.fps, 2, 14, '#fff');

            this._collectibleGroup.forEach((collectible) => {
                this.game.debug.body(collectible);
            });

            this.game.debug.body(this._player);
        }
    }
}
<commit_msg>Remove test banner<commit_after>/* globals __DEV__ */
import Phaser from 'phaser';
import Collectible from '../sprites/Collectible';

import config from '../config';
import collectibles from '../config/collectibles';
import faces from '../config/faces';

const DEBUG_GAME = false;

// TODO Move to a file
const gameConfig = {
    drinkDecrementPerTimeUnit: 1,
    drinkDecrementTimeMilliSeconds: 1000,
    drinkMinimumAmount: -250,
    drinkMaximumAmount: 250,
    foodDecrementPerMove: 2,
    foodMinimumAmount: -250,
    foodMaximumAmount: 250,
    playerInitialMoveSpeed: 400,
    playerMaxDragMultiplier: 0.4,
    playerYPosition: 860,
    minimumSpawnTime: 1800,
    maximumSpawnTime: 3300,
};

function randomIntegerBetween(min, max) {
    return Math.floor((Math.random() * (max - min)) + min);
}

export default class extends Phaser.State {
    init() {
        this._player = null;
        this._collectibleGroup = null;
        this._spawnTimer = 0;
        this._nextSpawnTime = 0;
        this._drinkTimer = 0;
        this._fontStyle = null;
        this._face = null;

        this._scoreText = null;
        this._foodBar = null;
        this._drinksBar = null;
        this._score = 0;
        this._cursors = null;
        this._facing = 'idle';
        this._foodMaximumTotal = gameConfig.foodMaximumAmount - gameConfig.foodMinimumAmount;
        this._drinkMaximumTotal = gameConfig.drinkMaximumAmount - gameConfig.drinkMinimumAmount;
        this._foodAmount = this._foodMaximumTotal / 2;
        this._drinkAmount = this._drinkMaximumTotal / 2;
    }

    create() {
        this.game.time.advancedTiming = true;
        this.physics.startSystem(Phaser.Physics.ARCADE);

        this.physics.arcade.gravity.y = 200;

        this.add.sprite(0, 0, 'background-game');
        this.add.button(
            config.gameWidth * 0.86,
            config.gameHeight * 0.03,
            'pause-button',
            this.managePause,
            this,
            1,
            0
        );

        this._fontStyle = {
            font: '40px Arial',
            fill: '#FFCC00',
            stroke: '#333',
            strokeThickness: 5,
            align: 'center',
        };

        this._spawnTimer = 0;
        this._drinkTimer = 0;

        this._collectibleGroup = this.add.group();

        this.initialisePlayer();

        this.initialiseUi();

        this.setDrinksBar();
        this.setFoodBar();
    }

    managePause() {
        this.game.paused = true;
        const pausedText = this.add.text(
                300,
                400,
                'Game paused.\nTap anywhere to continue.',
                this._fontStyle
        );

        this.input.onDown.add(() => {
            pausedText.destroy();
            this.game.paused = false;
        }, this);
    }

    update() {
        this._spawnTimer += this.time.elapsed;

        this.updatePlayer();
        this.updateCollectibles();
        this.updateDrinkAmount();

        this.updateFace();

        if (this.isGameOver()) {
            this.showGameOver();
        }
    }

    // TODO - needs working out properly once rules are clear
    updateFace() {
        const foodDifference = this._foodAmount - this._drinkAmount;
        const drinkDifference = this._drinkAmount - this._foodAmount;
        let newFace = faces.FACE_NEUTRAL;

        if (foodDifference > 200) {
            newFace = faces.FACE_SICK;
        } else if (foodDifference > 100) {
            newFace = faces.FACE_GLUTTONOUS;
        } else if (foodDifference > 50) {
            newFace = faces.FACE_GOURMAND;
        }

        if (drinkDifference > 200) {
            newFace = faces.FACE_HAMMERED;
        } else if (drinkDifference > 100) {
            newFace = faces.FACE_DRUNK;
        } else if (drinkDifference > 50) {
            newFace = faces.FACE_MERRY;
        }

        this.setFace(newFace);
    }

    setFace(faceFrame) {
        if (faceFrame !== this._face.frame) {
            this._face.frame = faceFrame;
        }
    }

    isGameOver() {
        const noDrinkPoints = (this._drinkAmount <= 0);
        const noFoodPoints = (this._foodAmount <= 0);
        return noDrinkPoints || noFoodPoints;
    }

    showGameOver() {
        this.add.sprite((config.gameWidth - 594) / 2, (config.gameHeight - 271) / 2, 'game-over');
        this.game.paused = true;
    }

    updateCollectibles() {
        if (this._spawnTimer > this._nextSpawnTime) {
            this._spawnTimer = 0;
            this.spawnCollectible();
        }

        this._collectibleGroup.forEach((collectible) => {
            collectible.update();
        });

        this.physics.arcade.overlap(
            this._player,
            this._collectibleGroup,
            this.collideWithPlayer,
            null,
            this
        );
    }

    getDrunkSpeedMultiplier() {
        const drunkPercentage = this._drinkAmount / gameConfig.drinkMaximumAmount;
        const speedMultiplier = 1 - (drunkPercentage * gameConfig.playerMaxDragMultiplier);
        return speedMultiplier;
    }

    updatePlayer() {
        const speedMultiplier = this.getDrunkSpeedMultiplier();
        const moveSpeed = gameConfig.playerInitialMoveSpeed * speedMultiplier;
        this._player.body.velocity.x = 0;

        if (this.isLeftPressed()) {
            this._player.body.velocity.x = -moveSpeed;

            if (this._facing !== 'left') {
                this._facing = 'left';
                this._player.animations.play('move-left');
                this.updateFoodAmount();
            }
        } else if (this.isRightPressed()) {
            this._player.body.velocity.x = moveSpeed;

            if (this._facing !== 'right') {
                this._facing = 'right';
                this._player.animations.play('move-right');
                this.updateFoodAmount();
            }
        } else if (this._facing !== 'idle') {
            this._facing = 'idle';
            this._player.animations.play('idle');
        }
    }

    isLeftPressed() {
        return this._cursors.left.isDown || this.hasTouchedLeft();
    }

    isRightPressed() {
        return this._cursors.right.isDown || this.hasTouchedRight();
    }

    hasTouchedLeft() {
        if (this.input.activePointer.isDown) {
            return (this.input.activePointer.worldX < (config.gameWidth / 2));
        }

        return false;
    }

    hasTouchedRight() {
        if (this.input.activePointer.isDown) {
            return (this.input.activePointer.worldX > (config.gameWidth / 2));
        }

        return false;
    }

    initialisePlayer() {
        this._player = this.add.sprite(config.gameWidth * 0.5, gameConfig.playerYPosition, 'player');
        this._player.scale = {x: 1, y: 1};
        this._player.animations.add('idle', [0, 1, 2, 3], 10, true);
        this._player.animations.add('move-left', [4, 5, 6, 7], 10, true);
        this._player.animations.add('move-right', [8, 9, 10, 11], 10, true);
        this._player.animations.play('idle');

        this._cursors = this.game.input.keyboard.createCursorKeys();
        this.game.physics.enable(this._player, Phaser.Physics.ARCADE);
        this._player.body.allowGravity = false;
        this._player.body.collideWorldBounds = true;

        // Original player spritesheet: 436x436
        this._player.body.setSize(218, 436, 109, 0);

        this._player.anchor.setTo(0.5, 0);
    }

    // TODO - Unify UI positions
    initialiseUi() {
        this.add.sprite(0, 0, 'frame');

        const faceBg = {
            x: config.gameWidth * 0.83,
            y: config.gameHeight * 0.89
        };
        const faceBackground = this.add.sprite(faceBg.x, faceBg.y, 'background-face');
        faceBackground.anchor.setTo(0.5, 0.5);

        this._face = this.add.sprite(faceBg.x + 4, faceBg.y + 10, 'faces');
        this._face.anchor.setTo(0.5, 0.5);
        this.setFace(faces.FACE_NEUTRAL);

        const scorePos = {
            x: faceBg.x,
            y: faceBg.y + 130
        };
        const scoreBar = this.add.sprite(scorePos.x, scorePos.y, 'score-bar');
        scoreBar.anchor.setTo(0.5, 0.5);

        this.createScoreText(scorePos);

        this.add.sprite(config.gameWidth * 0.02, config.gameHeight * 0.82, 'food-bar');
        this._foodBar = this.add.sprite(config.gameWidth * 0.18, config.gameHeight * 0.845, 'bars', 0);
        this._foodBar.scale.x = 1;
        this._foodBar.anchor.setTo(0, 0.5);

        this.add.sprite(config.gameWidth * 0.03, config.gameHeight * 0.9, 'drinks-bar');
        this._drinksBar = this.add.sprite(config.gameWidth * 0.18, config.gameHeight * 0.935, 'bars', 2);
        this._drinksBar.scale.x = 1;
        this._drinksBar.anchor.setTo(0, 0.5);

        this.setDrinksBar();
        this.setFoodBar();
    }

    collideWithPlayer(player, collectible) {
        this.playerCollectedItem(collectible);
    }

    updateDrinkAmount() {
        this._drinkTimer += this.time.elapsed;
        if (this._drinkTimer > gameConfig.drinkDecrementTimeMilliSeconds) {
            this._drinkTimer = 0;

            this._drinkAmount -= gameConfig.drinkDecrementPerTimeUnit;
            if (this._drinkAmount < 0) {
                this._drinkAmount = 0;
            }

            this.setDrinksBar();
        }
    }

    updateFoodAmount() {
        this._foodAmount -= gameConfig.foodDecrementPerMove;
        if (this._foodAmount < 0) {
            this._foodAmount = 0;
        }
        this.setFoodBar();
    }

    setDrinksBar() {
        this._drinksBar.scale.x = (this._drinkAmount / this._drinkMaximumTotal);
    }

    setFoodBar() {
        this._foodBar.scale.x = (this._foodAmount / this._foodMaximumTotal);
    }

    spawnCollectible() {
        this._nextSpawnTime = randomIntegerBetween(
            gameConfig.minimumSpawnTime,
            gameConfig.maximumSpawnTime
        );

        const dropPos = randomIntegerBetween(config.gameWidth * 0.05, config.gameWidth * 0.95);
        const dropOffset = 0;

        // Choose collectible type
        const foodOrDrink = randomIntegerBetween(0, 100);
        const isFood = (foodOrDrink >= 50);
        const collectibleTypeList = isFood ? collectibles.food : collectibles.drinks;

        const randomTypeIndex = randomIntegerBetween(0, collectibleTypeList.length - 1);
        const collectibleType = collectibleTypeList[randomTypeIndex];

        const collectible = new Collectible({
            game: this,
            x: dropPos,
            y: dropOffset,
            asset: 'collectibles',
            collectibleType,
            onOutOfBounds: this.onCollectibleOutOfBounds,
            isFood
        });

        this._collectibleGroup.add(collectible);
    }

    playerCollectedItem(collectible) {
        collectible.kill();

        const points = collectible.collectibleType.points;
        this._score += points;

        this._scoreText.setText(this._score);

        if (collectible.isFood) {
            this._foodAmount += points;

            if (this._foodAmount > gameConfig.foodMaximumAmount) {
                this._foodAmount = gameConfig.foodMaximumAmount;
            }

            this.setFoodBar();
        } else {
            this._drinkAmount += points;

            if (this._drinkAmount > gameConfig.drinkMaximumAmount) {
                this._drinkAmount = gameConfig.drinkMaximumAmount;
            }

            this.setDrinksBar();
        }
    }

    onCollectibleOutOfBounds(collectible) {
        collectible.kill();
    }

    createScoreText(position) {
        this._scoreText = this.add.text(position.x, position.y, '0', this._fontStyle);
        this._scoreText.font = 'dinregular';
        this._scoreText.fontSize = 40;
        this._scoreText.fill = '#111';
        this._scoreText.smoothed = false;
        this._scoreText.anchor.setTo(0.5);
    }

    render() {
        if (DEBUG_GAME) {
            this.game.debug.text(this.game.time.fps, 2, 14, '#fff');

            this._collectibleGroup.forEach((collectible) => {
                this.game.debug.body(collectible);
            });

            this.game.debug.body(this._player);
        }
    }
}
<|endoftext|>


<commit_before>/* eslint strict: 0 */
/* global jQuery */

(function($) {
  'use strict';

  $('.progress-bar').css('width', '100%');

  // POST key form
  $('#addKey form').submit(e => {
    e.preventDefault();
    $('#addKey .alert').addClass('hidden');
    $('#addKey .progress').removeClass('hidden');
    $.ajax({
      method: 'POST',
      url: '/api/v1/key',
      data: JSON.stringify({publicKeyArmored: $('#addKey textarea').val()}),
      contentType: 'application/json',
    }).done((data, textStatus, xhr) => {
      if (xhr.status === 304) {
        alert('addKey', 'danger', 'Key already exists!');
      } else {
        alert('addKey', 'success', xhr.responseText);
      }
    })
    .fail(xhr => {
      alert('addKey', 'danger', xhr.responseText);
    });
  });

  // DELETE key form
  $('#removeKey form').submit(e => {
    e.preventDefault();
    $('#removeKey .alert').addClass('hidden');
    $('#removeKey .progress').removeClass('hidden');
    const email = $('#removeKey input[type="email"]').val();
    $.ajax({
      method: 'DELETE',
      url: `/api/v1/key?email=${encodeURIComponent(email)}`
    }).done((data, textStatus, xhr) => {
      alert('removeKey', 'success', xhr.responseText);
    })
    .fail(xhr => {
      alert('removeKey', 'danger', xhr.responseText);
    });
  });

  function alert(region, outcome, text) {
    $(`#${region} .progress`).addClass('hidden');
    $(`#${region} .alert-${outcome} span`).text(text);
    $(`#${region} .alert-${outcome}`).removeClass('hidden');
  }
}(jQuery));
<commit_msg>Revert static/demo.js since it is not transpiled<commit_after>/* eslint-disable */

;(function($) {
  'use strict';

  $('.progress-bar').css('width', '100%');

  // POST key form
  $('#addKey form').submit(function(e) {
    e.preventDefault();
    $('#addKey .alert').addClass('hidden');
    $('#addKey .progress').removeClass('hidden');
    $.ajax({
      method: 'POST',
      url: '/api/v1/key',
      data: JSON.stringify({ publicKeyArmored:$('#addKey textarea').val() }),
      contentType: 'application/json',
    }).done(function(data, textStatus, xhr) {
      if (xhr.status === 304) {
        alert('addKey', 'danger', 'Key already exists!');
      } else {
        alert('addKey', 'success', xhr.responseText);
      }
    })
    .fail(function(xhr) {
      alert('addKey', 'danger', xhr.responseText);
    });
  });

  // DELETE key form
  $('#removeKey form').submit(function(e) {
    e.preventDefault();
    $('#removeKey .alert').addClass('hidden');
    $('#removeKey .progress').removeClass('hidden');
    var email = $('#removeKey input[type="email"]').val();
    $.ajax({
      method: 'DELETE',
      url: '/api/v1/key?email=' + encodeURIComponent(email)
    }).done(function(data, textStatus, xhr) {
      alert('removeKey', 'success', xhr.responseText);
    })
    .fail(function(xhr) {
      alert('removeKey', 'danger', xhr.responseText);
    });
  });

  function alert(region, outcome, text) {
    $('#' + region + ' .progress').addClass('hidden');
    $('#' + region + ' .alert-' + outcome + ' span').text(text);
    $('#' + region + ' .alert-' + outcome).removeClass('hidden');
  }

}(jQuery));<|endoftext|>


<commit_before>// connections.js v. 0.1 part of the Application Frame

$('new')({
    name :'connections',
    object : {
        classes : {
            Socket : function(type, url){
                this._type= type;
                this._url= url;
                this._lastMessage= null;
                this._open= false;
            }
        },
        functions : {
            request : function(url){
                var { Promise } = $('classes');
                return new Promise(function(okay, fail){
                    var xhr= new $$.XMLHttpRequest();
                    xhr.onreadystatechange= function(){
                        if(this.readyState == 4){
                            if(this.status == 200)
                                okay(this.responseText);
                            else
                                fail({ status : this.status, statusText : this.statusText });
                        }
                    };
                    xhr.open('GET', url, true);
                    xhr.send();
                });
            },
            require : function(url){
                var xhr= new $$.XMLHttpRequest();
                xhr.open('GET', url, false);
                xhr.send();
                if(xhr.status == 200)
                    return xhr.responseText;
                else
                    return null;
            }
        }
    },
    _init : function(me){
        var { Promise } = $('classes');
        me.classes.Socket.prototype= {
            get url(){
                return this._url;
            },

            get lastMessage(){
                return this._lastMessage;
            },

            get open(){
                return this._open;
            },

            push : function(message){
                var self= this;
                var xhr= new $$.XMLHttpRequest();
                return new Promise(function(setValue, setError){
                    xhr.onreadystatechange= function(){
                        if(this.readyState == 4){
                            if(this.status == 200){
                                setValue(this.responseText);
                            }else{
                                setError({status : this.status, statusText : this.statusText});
                            }
                        self._lastMessage= this.statusText;
                        }
                    };
                    if(self._type == me.classes.Socket.HTTP_POST){
                        xhr.open('POST', self._url, true);
                        xhr.send(message);
                    }else if(self._type == me.classes.Socket.HTTP_GET){
                        xhr.open('GET', self._url + '?' + message, true);
                        xhr.send();
                    }
                });
            }
        };
    me.classes.Socket.HTTP_GET= 'http_get';
    me.classes.Socket.HTTP_POST= 'http_post';
    me.classes.Socket.HTTP= 'http_post';
    }
});
<commit_msg>file header ajustment<commit_after>// connections.js v.0.1 part of the Application Frame

$('new')({
    name :'connections',
    object : {
        classes : {
            Socket : function(type, url){
                this._type= type;
                this._url= url;
                this._lastMessage= null;
                this._open= false;
            }
        },
        functions : {
            request : function(url){
                var { Promise } = $('classes');
                return new Promise(function(okay, fail){
                    var xhr= new $$.XMLHttpRequest();
                    xhr.onreadystatechange= function(){
                        if(this.readyState == 4){
                            if(this.status == 200)
                                okay(this.responseText);
                            else
                                fail({ status : this.status, statusText : this.statusText });
                        }
                    };
                    xhr.open('GET', url, true);
                    xhr.send();
                });
            },
            require : function(url){
                var xhr= new $$.XMLHttpRequest();
                xhr.open('GET', url, false);
                xhr.send();
                if(xhr.status == 200)
                    return xhr.responseText;
                else
                    return null;
            }
        }
    },
    _init : function(me){
        var { Promise } = $('classes');
        me.classes.Socket.prototype= {
            get url(){
                return this._url;
            },

            get lastMessage(){
                return this._lastMessage;
            },

            get open(){
                return this._open;
            },

            push : function(message){
                var self= this;
                var xhr= new $$.XMLHttpRequest();
                return new Promise(function(setValue, setError){
                    xhr.onreadystatechange= function(){
                        if(this.readyState == 4){
                            if(this.status == 200){
                                setValue(this.responseText);
                            }else{
                                setError({status : this.status, statusText : this.statusText});
                            }
                        self._lastMessage= this.statusText;
                        }
                    };
                    if(self._type == me.classes.Socket.HTTP_POST){
                        xhr.open('POST', self._url, true);
                        xhr.send(message);
                    }else if(self._type == me.classes.Socket.HTTP_GET){
                        xhr.open('GET', self._url + '?' + message, true);
                        xhr.send();
                    }
                });
            }
        };
    me.classes.Socket.HTTP_GET= 'http_get';
    me.classes.Socket.HTTP_POST= 'http_post';
    me.classes.Socket.HTTP= 'http_post';
    }
});
<|endoftext|>


<commit_before>
// main.js
// for meavisa.org, by @f03lipe

var Tag = (function (window, undefined) {

	var TagItem = Backbone.Model.extend({

		idAttribute: "hashtag",

		defaults: { children: [], description: null },

		initialize: function () {
			this.children = new TagList;
			this.collection.on('reset', this.loadChildren, this);
		},

		// The solo interaction of the user with the tags.
		toggleChecked: function (callback) {
			callback = callback || function(){};
			var checked = this.get('checked');
			// Also check for string, just to be safe.
			if (checked && checked !== 'false')
				this.set({'checked': false});
			else
				this.set({'checked': true});
			// .save() through tagsList.save(), right?
			// this.save(['checked'], {patch:true}); // , success: callback, error: callback});
		},

		// Returns true if this element has a checked child
		// TODO: improve this, for it only performs searches 1-level deep AND
		// this way of doing this through a method is not right. It shoudl be
		// triggered by the children or smthing.
		hasCheckedChild: function (callback) {
			if (!this.children.length) // optimize?
				return false;
			return !_.all(this.children.map(function(t){return !t.get('checked');}))
		},

		// Load the content from this.attributes['children'] into this.children
		// (a TagList). This is essential to allow the nested strategy to work.
		loadChildren: function () {
			this.children.parseAndReset(this.get('children'));
		},
	});

	var TagView = Backbone.View.extend({

		tagName: 'li',

		// template: _.template($("#template-tagview").html()),

		initialize: function () {
			this.hideChildren = true;
			this.childrenView = new TagListView({collection: this.model.children, className:'children'});
		},

		render: function () {
			console.log('rendering', this.model.toJSON())
			TemplateManager.get('/api/tags/template', function (err, tmpl) {
				this.$el.html(_.template(tmpl, {
					tag: _.extend(this.model.toJSON(), {hasCheckedChild: this.model.hasCheckedChild()})
				}));
				if (this.hideChildren) {
					this.childrenView.$el.hide();
				}
				// render childrenViews
				this.$el.append(this.childrenView.el);
				this.$("> .tag .info").popover('destroy');
				this.$("> .tag .info").popover({
					content: this.model.get("description"),
					placement: 'bottom',
					trigger: 'hover',
					container: 'body',
					delay: { show: 100, hide: 300 },
					title: " "+this.model.get("hashtag"),
					html: true,
				});
			}, this);
			return this;
		},

		events: {
			'click >.tag': 'tgChecked',
			'click >.expand':  'tgShowChildren',
		},

		tgChecked: function (e) {
			console.log('getChecked')
			e.preventDefault();
			this.model.toggleChecked();
			this.render();
			this.model.children.trigger('reset')
		},

		tgShowChildren: function (e) {
			e.preventDefault();
			this.hideChildren = !this.hideChildren;
			this.$('>.expand i').toggleClass("icon-angle-down");
			this.$('>.expand i').toggleClass("icon-angle-up");
			this.childrenView.$el.toggle();
		},
	});

	var TagList = Backbone.Collection.extend({
		model: TagItem,
		url: '/api/tags',

		initialize: function () {
			this.on({'reset': this.onReset});
		},

		parse: function (res) {
			return _.toArray(res);
		},

		parseAndReset: function (object) {
			this.reset(_.toArray(object));
		},

		onReset: function() {
			this.each(function(t){t.loadChildren();});
		},

		save: function (callback) {
			$.post(this.url, {'checked': this.getCheckedTags()}, callback);
		},

		getChecked: function () {
			var tags = this.chain()
				.map(function rec(t){return [t].concat((t.children.length)?t.children.map(rec):[]) })
				.flatten()
				.value();
			return tags.filter(function(t){return t.get('checked') === true;});
		},

		getCheckedTags: function () {
			return this.getChecked().map(function(t){return t.get('hashtag');});
		}
	});

	var TagListView = Backbone.View.extend({
		tagName: "ul",
		_views: [],

		initialize: function () {
			this.collection.on('reset', this.addAll, this);
		},

		addAll: function () {
			this._views = [];
			this.collection.each(function(tagItem) {
				this._views.push(new TagView({model:tagItem}));
			}, this);
			return this.render();
		},

		render: function () {
			var container = document.createDocumentFragment();
			// render each tagView
			_.each(this._views, function (tagView) {
				container.appendChild(tagView.render().el)
			}, this);
			this.$el.empty();
			this.$el.append(container);
			return this;
		}
	});

	return {
		item: TagItem,
		list: TagList,
		view: TagView,
		listView: TagListView,
	}
})(this);

var Post = (function (window, undefined) {
	var PostItem = Backbone.Model.extend({
	});

	var PostView = Backbone.View.extend({
		tagName: 'li',
		// template: _.template($("#template-tagview").html()),
		initialize: function () {
			this.model.collection.on('reset', this.destroy, this);
		},
		destroy: function () {
			console.log('Removing me ="(', this);
			this.remove();
		},
		render: function () {
			TemplateManager.get('/api/posts/template', function (err, tmpl) {
				this.$el.html(_.template(tmpl, {post: this.model.toJSON()}));
			}, this);
			return this;
		},
	});

	var PostList = Backbone.Collection.extend({
		model: PostItem,
		url: '/api/posts',
	});

	var PostListView = Backbone.View.extend({
		el: "#posts",
		_views: [],
		template: _.template('<% if (!length) { %>\

Ops! Você não está seguindo tag nenhuma. :/
\
			<% } %>\

'),

		initialize: function () {
			this.collection.on('reset', this.addAll, this);
		},

		addAll: function () {
			console.log('doing new stuff', this);
			var views = [];
			this.collection.each(function(postItem) {
				views.push(new PostView({model:postItem}));
			}, this);
			this._views = views;
			return this.render();
		},

		render: function () {
			var container = document.createDocumentFragment();
			// render each postView
			_.each(this._views, function (postView) {
				container.appendChild(postView.render().el)
			}, this);
			this.$el.empty();
			this.$el.append(container);
			return this;
		}
	});

	return {
		item: PostItem,
		list: PostList,
		view: PostView,
		listView: PostListView,
	}
})(this);

// Quick first-attempt at a TemplateManager for the views.
// Not sure if this is production-quality solution, but for now it'll save
// me the pain of having to wait for the app to reload everytime the html is
// changed.
var TemplateManager = {
	get: function (url, callback, context) {
		var template = this.templates[url];
		if (template)
			callback.call(context, null, template);
		else {
			var that = this;
			$.get(url, function (tmpl) {
				that.templates[url] = tmpl;
				callback.call(context, null, tmpl);
			})
		}
	},
	templates: {},
}

// Extend PATCH:true option of Backbone.
// When model.save([attrs], {patch:true}) is called:
// - the method is changed to PUT;
// - the data sent is a hash with the passed attributes and their values;
var originalSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
	if (method === 'patch' && options.attrs instanceof Array) {
		// pop attributes and add their values
		while (e = options.attrs.pop())
			options.attrs[e] = model.get(e);
		options.type = 'PUT';
		// turn options.attrs into an Object
		options.attrs = _.extend({}, options.attrs);
	}
	return originalSync(method, model, options);
};

// Central functionality for of the app.
var app = new (Backbone.Router.extend({

	initialize: function () { },

	start: function () {
		Backbone.history.start({pushState: false});

		this.tagList = new Tag.list;
		this.tagListView = new Tag.listView({collection: this.tagList});
		$("#tags").prepend(this.tagListView.$el);
		this.tagList.parseAndReset(window._tags);

		this.postList = new Post.list;
		this.postListView = new Post.listView({collection: this.postList});
		$("#posts").prepend(this.postListView.$el);
		this.postList.reset(window._posts);
	},

	previewPosts: function () {
		this.postList.fetch({
			data: {tags: app.tagList.getCheckedTags().join(',')},
			processData: true,
			reset: true,
		})
	},

	confirmTags: function (callback) {
		this.tagList.save(callback);
	},

	routes: { "": "index", },
	index: function () { },
}));

$(function () {
	app.start();
});
<commit_msg>Bug i fix<commit_after>
// main.js
// for meavisa.org, by @f03lipe

var Tag = (function (window, undefined) {

	var TagItem = Backbone.Model.extend({

		idAttribute: "hashtag",

		defaults: { children: [], description: null },

		initialize: function () {
			this.children = new TagList;
			this.collection.on('reset', this.loadChildren, this);
		},

		// The solo interaction of the user with the tags.
		toggleChecked: function (callback) {
			callback = callback || function(){};
			var checked = this.get('checked');
			// Also check for string, just to be safe.
			if (checked && checked !== 'false')
				this.set({'checked': false});
			else
				this.set({'checked': true});
			// .save() through tagsList.save(), right?
			// this.save(['checked'], {patch:true}); // , success: callback, error: callback});
		},

		// Returns true if this element has a checked child
		// TODO: improve this, for it only performs searches 1-level deep AND
		// this way of doing this through a method is not right. It shoudl be
		// triggered by the children or smthing.
		hasCheckedChild: function (callback) {
			if (!this.children.length) // optimize?
				return false;
			return !_.all(this.children.map(function(t){return !t.get('checked');}))
		},

		// Load the content from this.attributes['children'] into this.children
		// (a TagList). This is essential to allow the nested strategy to work.
		loadChildren: function () {
			this.children.parseAndReset(this.get('children'));
		},
	});

	var TagView = Backbone.View.extend({

		tagName: 'li',

		// template: _.template($("#template-tagview").html()),

		initialize: function () {
			this.hideChildren = true;
			this.childrenView = new TagListView({collection: this.model.children, className:'children'});
		},

		render: function () {
			console.log('rendering', this.model.toJSON())
			TemplateManager.get('/api/tags/template', function (err, tmpl) {
				this.$el.html(_.template(tmpl, {
					tag: _.extend(this.model.toJSON(), {hasCheckedChild: this.model.hasCheckedChild()})
				}));
				if (this.hideChildren) {
					this.childrenView.$el.hide();
				}
				// render childrenViews
				this.$el.append(this.childrenView.el);
				this.$("> .tag .info").popover('destroy');
				this.$("> .tag .info").click(function (e) {
					e.stopPropagation();
				})

					this.$("> .tag .info").popover({
						content: this.model.get("description"),
						placement: 'bottom',
						trigger: 'hover',
						container: 'body',
						delay: { show: 100, hide: 300 },
						title: " "+this.model.get("hashtag"),
						html: true,
					});
			}, this);
			return this;
		},

		events: {
			'click >.tag': 'tgChecked',
			'click >.expand':  'tgShowChildren',
		},

		tgChecked: function (e) {
			console.log('getChecked')
			e.preventDefault();
			this.model.toggleChecked();
			this.render();
			this.model.children.trigger('reset')
		},

		tgShowChildren: function (e) {
			e.preventDefault();
			this.hideChildren = !this.hideChildren;
			this.$('>.expand i').toggleClass("icon-angle-down");
			this.$('>.expand i').toggleClass("icon-angle-up");
			this.childrenView.$el.toggle();
		},
	});

	var TagList = Backbone.Collection.extend({
		model: TagItem,
		url: '/api/tags',

		initialize: function () {
			this.on({'reset': this.onReset});
		},

		parse: function (res) {
			return _.toArray(res);
		},

		parseAndReset: function (object) {
			this.reset(_.toArray(object));
		},

		onReset: function() {
			this.each(function(t){t.loadChildren();});
		},

		save: function (callback) {
			$.post(this.url, {'checked': this.getCheckedTags()}, callback);
		},

		getChecked: function () {
			var tags = this.chain()
				.map(function rec(t){return [t].concat((t.children.length)?t.children.map(rec):[]) })
				.flatten()
				.value();
			return tags.filter(function(t){return t.get('checked') === true;});
		},

		getCheckedTags: function () {
			return this.getChecked().map(function(t){return t.get('hashtag');});
		}
	});

	var TagListView = Backbone.View.extend({
		tagName: "ul",
		_views: [],

		initialize: function () {
			this.collection.on('reset', this.addAll, this);
		},

		addAll: function () {
			this._views = [];
			this.collection.each(function(tagItem) {
				this._views.push(new TagView({model:tagItem}));
			}, this);
			return this.render();
		},

		render: function () {
			var container = document.createDocumentFragment();
			// render each tagView
			_.each(this._views, function (tagView) {
				container.appendChild(tagView.render().el)
			}, this);
			this.$el.empty();
			this.$el.append(container);
			return this;
		}
	});

	return {
		item: TagItem,
		list: TagList,
		view: TagView,
		listView: TagListView,
	}
})(this);

var Post = (function (window, undefined) {
	var PostItem = Backbone.Model.extend({
	});

	var PostView = Backbone.View.extend({
		tagName: 'li',
		// template: _.template($("#template-tagview").html()),
		initialize: function () {
			this.model.collection.on('reset', this.destroy, this);
		},
		destroy: function () {
			console.log('Removing me ="(', this);
			this.remove();
		},
		render: function () {
			TemplateManager.get('/api/posts/template', function (err, tmpl) {
				this.$el.html(_.template(tmpl, {post: this.model.toJSON()}));
			}, this);
			return this;
		},
	});

	var PostList = Backbone.Collection.extend({
		model: PostItem,
		url: '/api/posts',
	});

	var PostListView = Backbone.View.extend({
		el: "#posts",
		_views: [],
		template: _.template('<% if (!length) { %>\

Ops! Você não está seguindo tag nenhuma. :/
\
			<% } %>\

'),

		initialize: function () {
			this.collection.on('reset', this.addAll, this);
		},

		addAll: function () {
			console.log('doing new stuff', this);
			var views = [];
			this.collection.each(function(postItem) {
				views.push(new PostView({model:postItem}));
			}, this);
			this._views = views;
			return this.render();
		},

		render: function () {
			var container = document.createDocumentFragment();
			// render each postView
			_.each(this._views, function (postView) {
				container.appendChild(postView.render().el)
			}, this);
			this.$el.empty();
			this.$el.append(container);
			return this;
		}
	});

	return {
		item: PostItem,
		list: PostList,
		view: PostView,
		listView: PostListView,
	}
})(this);

// Quick first-attempt at a TemplateManager for the views.
// Not sure if this is production-quality solution, but for now it'll save
// me the pain of having to wait for the app to reload everytime the html is
// changed.
var TemplateManager = {
	get: function (url, callback, context) {
		var template = this.templates[url];
		if (template)
			callback.call(context, null, template);
		else {
			var that = this;
			$.get(url, function (tmpl) {
				that.templates[url] = tmpl;
				callback.call(context, null, tmpl);
			})
		}
	},
	templates: {},
}

// Extend PATCH:true option of Backbone.
// When model.save([attrs], {patch:true}) is called:
// - the method is changed to PUT;
// - the data sent is a hash with the passed attributes and their values;
var originalSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
	if (method === 'patch' && options.attrs instanceof Array) {
		// pop attributes and add their values
		while (e = options.attrs.pop())
			options.attrs[e] = model.get(e);
		options.type = 'PUT';
		// turn options.attrs into an Object
		options.attrs = _.extend({}, options.attrs);
	}
	return originalSync(method, model, options);
};

// Central functionality for of the app.
var app = new (Backbone.Router.extend({

	initialize: function () { },

	start: function () {
		Backbone.history.start({pushState: false});

		this.tagList = new Tag.list;
		this.tagListView = new Tag.listView({collection: this.tagList});
		$("#tags").prepend(this.tagListView.$el);
		this.tagList.parseAndReset(window._tags);

		this.postList = new Post.list;
		this.postListView = new Post.listView({collection: this.postList});
		$("#posts").prepend(this.postListView.$el);
		this.postList.reset(window._posts);
	},

	previewPosts: function () {
		this.postList.fetch({
			data: {tags: app.tagList.getCheckedTags().join(',')},
			processData: true,
			reset: true,
		})
	},

	confirmTags: function (callback) {
		this.tagList.save(callback);
	},

	routes: { "": "index", },
	index: function () { },
}));

$(function () {
	app.start();
});
<|endoftext|>


<commit_before>"use strict";

var popupGlobal = {
    feeds: []
}

window.onresize = resizeWindows;

self.port.on("feedsUpdated", function (feedsData) {
    renderFeeds(feedsData);
});

self.port.on("feedMarkedAsRead", function (feedsData) {
    removeFeedFromList(feedsData);
});

self.port.on("showLoader", function () {
    showLoader();
    resizeWindows();
});

$("#login").click(function () {
    self.port.emit("updateToken", null);
});

$("#feed").on("mousedown", "a.title", function (event) {
    if (event.which === 1 || event.which === 2) {
        markAsRead([$(this).closest(".item").data("id")], true);
    }
});

$("#feed").on("click", ".mark-read", function (event) {
    var feed = $(this).closest(".item");
    markAsRead([feed.data("id")], false);
});

$("#feed").on("click", ".show-content", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var contentContainer = feed.find(".content");
    var feedId = feed.data("id");
    if (contentContainer.html() === "") {
        var content;
        for (var i = 0; i < popupGlobal.feeds.length; i++) {
            if (popupGlobal.feeds[i].id === feedId) {
                content = popupGlobal.feeds[i].content
            }
        }
        if (content) {
            contentContainer.html(content);
            //For open links in new tab
            contentContainer.find("a").each(function (key, value) {
                var link = $(value);
                link.attr("target", "_blank");
            });
        }
    }
    contentContainer.slideToggle(function () {
        $this.css("background-position", contentContainer.is(":visible") ? "-288px -120px" : "-313px -119px");
        if (contentContainer.is(":visible") && contentContainer.text().length > 350) {
            $(".item").css("width", "700px");
            $("#feedly").css("width", "700px");
            $(".article-title").css("width", "660px");
        } else {
            $(".item").css("width", "350px");
            $("#feedly").css("width", "350px");
            $(".article-title").css("width", "310px");
        }
        resizeWindows();
    });
});

$("#popup-content").on("click", "#mark-all-read", function (event) {
    var feedIds = [];
    $(".item").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsRead(feedIds);
});

function requestFeeds() {
    self.port.emit("getFeeds", null);
}

function markAsRead(feedIds, isLinkOpened) {
    self.port.emit("markRead", {feedIds: feedIds, isLinkOpened: isLinkOpened});
}

function removeFeedFromList(feedIds) {
    for (var i = 0; i < feedIds.length; i++) {
        $(".item[data-id='" + feedIds[i] + "']").fadeOut("fast", function () {
            $(this).remove();
            resizeWindows();
            if ($("#feed").find(".item").size() === 0) {
                requestFeeds();
            }
        });
    }
}

function showLoader() {
    $("body").children("div").hide();
    $("#loading").show();
}

function showLogin() {
    $("body").children("div").hide();
    $("#login").show();
}

function showContent() {
    $("body").children("div").hide();
    $("#popup-content").show();
}

function renderFeeds(data) {

    $("#feed").empty();

    popupGlobal.feeds = data.feeds;
    if (data.isLoggedIn === false) {
        showLogin();
    } else {
        if (data.feeds.length === 0) {
            $("#feed-empty").show();
            $("#all-read-section").hide();
        } else {
            $("#all-read-section").show();
            $("#feed-empty").hide();

            var feeds = data.feeds;
            var container = $("#feed");
            for (var i = 0; i < feeds.length; i++) {
                var item = $("
").attr("data-id", feeds[i].id);
                var articleTitle = $("")
                    .append($("").text(feeds[i].title + " "));
                item.append(articleTitle);
                var articleMenu = $("").append($(""))
                    .append($(""));
                item.append(articleMenu);

                var blogTitle = $("
")
                    .append($("").attr("src", feeds[i].blogIcon))
                    .append($("")
                        .attr("href", feeds[i].blogUrl)
                        .text(" " + feeds[i].blog + ", ")
                        .addClass(feeds[i].titleDirection))
                    .append($("").attr("title", feeds[i].isoDate));

                var content = $("
").addClass(feeds[i].contentDirection);

                item.append(blogTitle);
                item.append(content);
                container.append(item);
            }
            $(".timeago").timeago();
        }
        showContent();
    }
    resizeWindows();
}

function resizeWindows() {
    var maxHeight = 600;
    var width = $("body").outerWidth(true);
    var height = $("body").outerHeight(true);
    if (height > maxHeight) {
        height = maxHeight;
        width += getScrollbarWidth();
    }
    var height = height > maxHeight ? maxHeight : height;
    self.port.emit("resizePanel", {width: width, height: height});
}

function getScrollbarWidth() {
    var div = document.createElement('div');

    div.style.overflowY = 'scroll';
    div.style.width =  '50px';
    div.style.height = '50px';

    div.style.visibility = 'hidden';

    document.body.appendChild(div);
    var scrollWidth = div.offsetWidth - div.clientWidth;
    document.body.removeChild(div);

    return scrollWidth;
}<commit_msg>Fixed bug with scroll in popup on MacOS<commit_after>"use strict";

var popupGlobal = {
    feeds: []
}

window.onresize = resizeWindows;

self.port.on("feedsUpdated", function (feedsData) {
    renderFeeds(feedsData);
});

self.port.on("feedMarkedAsRead", function (feedsData) {
    removeFeedFromList(feedsData);
});

self.port.on("showLoader", function () {
    showLoader();
    resizeWindows();
});

$("#login").click(function () {
    self.port.emit("updateToken", null);
});

$("#feed").on("mousedown", "a.title", function (event) {
    if (event.which === 1 || event.which === 2) {
        markAsRead([$(this).closest(".item").data("id")], true);
    }
});

$("#feed").on("click", ".mark-read", function (event) {
    var feed = $(this).closest(".item");
    markAsRead([feed.data("id")], false);
});

$("#feed").on("click", ".show-content", function () {
    var $this = $(this);
    var feed = $this.closest(".item");
    var contentContainer = feed.find(".content");
    var feedId = feed.data("id");
    if (contentContainer.html() === "") {
        var content;
        for (var i = 0; i < popupGlobal.feeds.length; i++) {
            if (popupGlobal.feeds[i].id === feedId) {
                content = popupGlobal.feeds[i].content
            }
        }
        if (content) {
            contentContainer.html(content);
            //For open links in new tab
            contentContainer.find("a").each(function (key, value) {
                var link = $(value);
                link.attr("target", "_blank");
            });
        }
    }
    contentContainer.slideToggle(function () {
        $this.css("background-position", contentContainer.is(":visible") ? "-288px -120px" : "-313px -119px");
        if (contentContainer.is(":visible") && contentContainer.text().length > 350) {
            $(".item").css("width", "700px");
            $("#feedly").css("width", "700px");
            $(".article-title").css("width", "660px");
        } else {
            $(".item").css("width", "350px");
            $("#feedly").css("width", "350px");
            $(".article-title").css("width", "310px");
        }
        resizeWindows();
    });
});

$("#popup-content").on("click", "#mark-all-read", function (event) {
    var feedIds = [];
    $(".item").each(function (key, value) {
        feedIds.push($(value).data("id"));
    });
    markAsRead(feedIds);
});

function requestFeeds() {
    self.port.emit("getFeeds", null);
}

function markAsRead(feedIds, isLinkOpened) {
    self.port.emit("markRead", {feedIds: feedIds, isLinkOpened: isLinkOpened});
}

function removeFeedFromList(feedIds) {
    for (var i = 0; i < feedIds.length; i++) {
        $(".item[data-id='" + feedIds[i] + "']").fadeOut("fast", function () {
            $(this).remove();
            resizeWindows();
            if ($("#feed").find(".item").size() === 0) {
                requestFeeds();
            }
        });
    }
}

function showLoader() {
    $("body").children("div").hide();
    $("#loading").show();
}

function showLogin() {
    $("body").children("div").hide();
    $("#login").show();
}

function showContent() {
    $("body").children("div").hide();
    $("#popup-content").show();
}

function renderFeeds(data) {

    $("#feed").empty();

    popupGlobal.feeds = data.feeds;
    if (data.isLoggedIn === false) {
        showLogin();
    } else {
        if (data.feeds.length === 0) {
            $("#feed-empty").show();
            $("#all-read-section").hide();
        } else {
            $("#all-read-section").show();
            $("#feed-empty").hide();

            var feeds = data.feeds;
            var container = $("#feed");
            for (var i = 0; i < feeds.length; i++) {
                var item = $("
").attr("data-id", feeds[i].id);
                var articleTitle = $("")
                    .append($("").text(feeds[i].title + " "));
                item.append(articleTitle);
                var articleMenu = $("").append($(""))
                    .append($(""));
                item.append(articleMenu);

                var blogTitle = $("
")
                    .append($("").attr("src", feeds[i].blogIcon))
                    .append($("")
                        .attr("href", feeds[i].blogUrl)
                        .text(" " + feeds[i].blog + ", ")
                        .addClass(feeds[i].titleDirection))
                    .append($("").attr("title", feeds[i].isoDate));

                var content = $("
").addClass(feeds[i].contentDirection);

                item.append(blogTitle);
                item.append(content);
                container.append(item);
            }
            $(".timeago").timeago();
        }
        showContent();
    }
    resizeWindows();
}

function resizeWindows() {
    var maxHeight = 600;
    var width = $("body").outerWidth(true);
    var height = $("body").outerHeight(true);
    if (height > maxHeight) {
        height = maxHeight;
        width += getScrollbarWidth();
    }
    var height = height > maxHeight ? maxHeight : height;

    //For fix bug with scroll on Mac
    var verticalMargin = 2;
    self.port.emit("resizePanel", {width: width, height: height + verticalMargin});
}

function getScrollbarWidth() {
    var div = document.createElement('div');

    div.style.overflowY = 'scroll';
    div.style.width =  '50px';
    div.style.height = '50px';

    div.style.visibility = 'hidden';

    document.body.appendChild(div);
    var scrollWidth = div.offsetWidth - div.clientWidth;
    document.body.removeChild(div);

    return scrollWidth;
}<|endoftext|>


<commit_before>var config = require('../config');
var request = require('request');
var fs = require('fs');

var telegramAPI = {};

telegramAPI.setupWebhook = function(token, url, callback){
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_SETUP_WEBHOOK.replace(":url", url);
  request(requestUrl, {}, function (err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null);
    } else {
      return callback(new Error("Unable to setup webhook. Code " + res.statusCode));
    }
  });
};

telegramAPI.postMessage = function(token, chatId, message, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_MESSAGE;
  var formData = {
    chat_id: chatId,
    text: message
  };
  request.post({url:requestUrl, formData: formData}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error("Unable to post message. Code " + res.statusCode));
    }
  });
};

telegramAPI.postImage = function(token, imagePath, chatId, callback) {
  fs.access(imagePath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_IMAGE;
      var formData = {
        chat_id: chatId,
        photo: fs.createReadStream(imagePath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(imagePath, function(error) {
          if (error)
            return callback(error);
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post image. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.postDocument = function(token, documentPath, chatId, callback) {
  fs.access(documentPath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_DOCUMENT;
      var formData = {
        chat_id: chatId,
        document: fs.createReadStream(documentPath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(documentPath, function(error) {
          if (error)
            throw error;
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post document. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.answerQueryWithMedia = function(token, queryId, mediaURLs, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_ANSWER_QUERY;
  console.log('results count: '+mediaURLs.length);
  var results = mediaURLs.map(function(url) {
    var fileExtension = url.split('.').pop();
    var fileName = url.split('/').pop().split('.')[0];
    var thumbnailUrl = url.replace('.' + fileExtension, 's.jpg');
    var result = {};

    switch (fileExtension) {
      case 'webm':
      result.type = 'video';
      result.mime_type = 'text/html';
      result.mpeg4_url = url;
      console.log('webm!');
      break;
      case 'gif':
      result.type = 'gif';
      result.gif_url = url;
      break;
      default:
      result.type = 'article';
      result.url = url;
    }
    // Common properties
    result.id = fileName;
    result.title = fileName;
    result.message_text = url;
    result.thumb_url = thumbnailUrl;
    return result;
  });
  request.post(requestUrl, {form:{inline_query_id:queryId, cache_time:10, results: JSON.stringify(results)}}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      console.log(res);
      console.log(body.error_code);
      return callback(new Error(body.error_code));
    }
  });
};

module.exports = telegramAPI;
<commit_msg>Fixed typo.<commit_after>var config = require('../config');
var request = require('request');
var fs = require('fs');

var telegramAPI = {};

telegramAPI.setupWebhook = function(token, url, callback){
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_SETUP_WEBHOOK.replace(":url", url);
  request(requestUrl, {}, function (err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null);
    } else {
      return callback(new Error("Unable to setup webhook. Code " + res.statusCode));
    }
  });
};

telegramAPI.postMessage = function(token, chatId, message, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_MESSAGE;
  var formData = {
    chat_id: chatId,
    text: message
  };
  request.post({url:requestUrl, formData: formData}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      return callback(new Error("Unable to post message. Code " + res.statusCode));
    }
  });
};

telegramAPI.postImage = function(token, imagePath, chatId, callback) {
  fs.access(imagePath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_IMAGE;
      var formData = {
        chat_id: chatId,
        photo: fs.createReadStream(imagePath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(imagePath, function(error) {
          if (error)
            return callback(error);
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post image. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.postDocument = function(token, documentPath, chatId, callback) {
  fs.access(documentPath, fs.F_OK, function(err) {
    if (err) {
      callback(err);
    } else {
      var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_POST_DOCUMENT;
      var formData = {
        chat_id: chatId,
        document: fs.createReadStream(documentPath)
      };
      request.post({url:requestUrl, formData: formData}, function(err, res, body) {
        fs.unlink(documentPath, function(error) {
          if (error)
            throw error;
        });
        if (err) {
          return callback(err);
        } else if (res.statusCode == 200) {
          return callback(null, res, body);
        } else {
          return callback(new Error("Unable to post document. Code " + res.statusCode));
        }
      });
    }
  });
};

telegramAPI.answerQueryWithMedia = function(token, queryId, mediaURLs, callback) {
  var requestUrl = config.TELEGRAM_BASE_URL + token + config.TELEGRAM_ANSWER_QUERY;
  console.log('results count: '+mediaURLs.length);
  var results = mediaURLs.map(function(url) {
    var fileExtension = url.split('.').pop();
    var fileName = url.split('/').pop().split('.')[0];
    var thumbnailUrl = url.replace('.' + fileExtension, 's.jpg');
    var result = {};

    switch (fileExtension) {
      case 'webm':
      result.type = 'video';
      result.mime_type = 'text/html';
      result.video_url = url;
      console.log('webm!');
      break;
      case 'gif':
      result.type = 'gif';
      result.gif_url = url;
      break;
      default:
      result.type = 'article';
      result.url = url;
    }
    // Common properties
    result.id = fileName;
    result.title = fileName;
    result.message_text = url;
    result.thumb_url = thumbnailUrl;
    return result;
  });
  request.post(requestUrl, {form:{inline_query_id:queryId, cache_time:10, results: JSON.stringify(results)}}, function(err, res, body) {
    if (err) {
      return callback(err);
    } else if (res.statusCode == 200) {
      return callback(null, res, body);
    } else {
      console.log(res);
      console.log(body.error_code);
      return callback(new Error(body.error_code));
    }
  });
};

module.exports = telegramAPI;
<|endoftext|>


<commit_before>(function(env) {
  "use strict";

  env.ddg_spice_wgha = function(api_result) {

   if (!(api_result && api_result.length)) {
     return Spice.failed('wgha');
   }

   var query = DDG.get_query();
   var sourceUrl = 'http://www.wasgehtheuteab.de';

   Spice.add({
    id: 'wgha',
    name: 'Events',

    data: api_result,

    meta: {
      sourceUrl: sourceUrl,
      sourceName: 'Was geht heute ab?',
      sourceIcon: true,
      itemType: 'Events'
    },
    templates: {
      group: 'text',
      detail: false,
      item_detail: false,
      options: {
        footer: Spice.wgha.footer
      },
    },
  });
 };

 Handlebars.registerHelper("WGHA_formatDate", function(created_at) {
  var date = new Date(created_at);
  if (isNaN(date)) {
   var pattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
   var match = pattern.exec(created_at);
   date = new Date(match[1], parseInt(match[2])-1, match[3], match[4], match[5], match[6]);
 }
 return date.getUTCDate() + "." + (date.getUTCMonth() + 1) + "." + date.getUTCFullYear() +" ab " + date.getUTCHours() + ":" + (date.getMinutes()<10?'0':'') + date.getMinutes() + "Uhr";
});
}(this));
<commit_msg>fixed date match<commit_after>(function(env) {
  "use strict";

  env.ddg_spice_wgha = function(api_result) {

   if (!(api_result && api_result.length)) {
     return Spice.failed('wgha');
   }

   var query = DDG.get_query();
   var sourceUrl = 'http://www.wasgehtheuteab.de';

   Spice.add({
    id: 'wgha',
    name: 'Events',

    data: api_result,

    meta: {
      sourceUrl: sourceUrl,
      sourceName: 'Was geht heute ab?',
      sourceIcon: true,
      itemType: 'Events'
    },
    templates: {
      group: 'text',
      detail: false,
      item_detail: false,
      options: {
        footer: Spice.wgha.footer
      },
    },
  });
 };

 Handlebars.registerHelper("WGHA_formatDate", function(created_at) {
   var pattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
   var match = pattern.exec(created_at);
   if(match != null) {
   var event_date = new Date(match[1], parseInt(match[2])-1, match[3], match[4], match[5], match[6]);
   return event_date.getUTCDate() + "." + (event_date.getUTCMonth() + 1) + "." + event_date.getUTCFullYear() +" ab " + event_date.getUTCHours() + ":" + (event_date.getMinutes()<10?'0':'') + event_date.getMinutes() + "Uhr";
 } else {return;}

 });
}(this));
<|endoftext|>