<commit_before>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');
var isMobile = require('../utils/').isMobile();

// To avoid recalculation at every mouse movement tick
var PI_2 = Math.PI / 2;
var radToDeg = THREE.Math.radToDeg;

module.exports.Component = registerComponent('look-controls', {
  dependencies: ['position', 'rotation'],

  schema: {
    enabled: { default: true },
    hmdEnabled: { default: true },
    standing: { default: true }
  },

  init: function () {
    this.previousHMDPosition = new THREE.Vector3();
    this.setupMouseControls();
    this.setupHMDControls();
    this.bindMethods();
  },

  update: function (oldData) {
    var data = this.data;
    var hmdEnabled = data.hmdEnabled;
    if (!data.enabled) { return; }
    if (!hmdEnabled && oldData && hmdEnabled !== oldData.hmdEnabled) {
      this.pitchObject.rotation.set(0, 0, 0);
      this.yawObject.rotation.set(0, 0, 0);
    }
    this.controls.standing = data.standing;
    this.controls.update();
    this.updateOrientation();
    this.updatePosition();
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  tick: function (t) {
    this.update();
  },

  remove: function () {
    this.pause();
  },

  bindMethods: function () {
    this.onMouseDown = this.onMouseDown.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.releaseMouse = this.releaseMouse.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
  },

  setupMouseControls: function () {
    // The canvas where the scene is painted
    this.mouseDown = false;
    this.pitchObject = new THREE.Object3D();
    this.yawObject = new THREE.Object3D();
    this.yawObject.position.y = 10;
    this.yawObject.add(this.pitchObject);
  },

  setupHMDControls: function () {
    this.dolly = new THREE.Object3D();
    this.euler = new THREE.Euler();
    this.controls = new THREE.VRControls(this.dolly);
  },

  addEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    var canvasEl = sceneEl.canvas;

    // listen for canvas to load.
    if (!canvasEl) {
      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
      return;
    }

    // Mouse Events
    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
    canvasEl.addEventListener('mouseup', this.releaseMouse, false);
    canvasEl.addEventListener('mouseout', this.releaseMouse, false);

    // Touch events
    canvasEl.addEventListener('touchstart', this.onTouchStart);
    canvasEl.addEventListener('touchmove', this.onTouchMove);
    canvasEl.addEventListener('touchend', this.onTouchEnd);
  },

  removeEventListeners: function () {
    var sceneEl = document.querySelector('a-scene');
    var canvasEl = sceneEl && sceneEl.canvas;
    if (!canvasEl) { return; }

    // Mouse Events
    canvasEl.removeEventListener('mousedown', this.onMouseDown);
    canvasEl.removeEventListener('mousemove', this.onMouseMove);
    canvasEl.removeEventListener('mouseup', this.releaseMouse);
    canvasEl.removeEventListener('mouseout', this.releaseMouse);

    // Touch events
    canvasEl.removeEventListener('touchstart', this.onTouchStart);
    canvasEl.removeEventListener('touchmove', this.onTouchMove);
    canvasEl.removeEventListener('touchend', this.onTouchEnd);
  },

  updateOrientation: (function () {
    var hmdEuler = new THREE.Euler();
    return function () {
      var pitchObject = this.pitchObject;
      var yawObject = this.yawObject;
      var hmdQuaternion = this.calculateHMDQuaternion();
      var sceneEl = this.el.sceneEl;
      var rotation;
      hmdEuler.setFromQuaternion(hmdQuaternion, 'YXZ');
      if (!sceneEl.is('vr-mode') || isNullVector(hmdEuler) || !this.data.hmdEnabled) {
        // Mouse look only if HMD disabled or no info coming from the sensors
        rotation = {
          x: radToDeg(pitchObject.rotation.x),
          y: radToDeg(yawObject.rotation.y),
          z: 0
        };
      } else {
        if (isMobile) {
          // In mobile we allow camera rotation with touch events and sensors
          rotation = {
            x: radToDeg(hmdEuler.x) + radToDeg(pitchObject.rotation.x),
            y: radToDeg(hmdEuler.y) + radToDeg(yawObject.rotation.y),
            z: radToDeg(hmdEuler.z)
          };
        } else {
          // Mouse rotation ignored with an active headset.
          // The user head rotation takes priority
          rotation = {
            x: radToDeg(hmdEuler.x),
            y: radToDeg(hmdEuler.y),
            z: radToDeg(hmdEuler.z)
          };
        }
      }
      this.el.setAttribute('rotation', rotation);
    };
  })(),

  calculateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      hmdQuaternion.copy(this.dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  updatePosition: (function () {
    var deltaHMDPosition = new THREE.Vector3();
    return function () {
      var el = this.el;
      var currentPosition = el.getComputedAttribute('position');
      var currentHMDPosition;
      var previousHMDPosition = this.previousHMDPosition;
      var sceneEl = this.el.sceneEl;
      currentHMDPosition = this.calculateHMDPosition();
      deltaHMDPosition.copy(currentHMDPosition).sub(previousHMDPosition);
      if (!sceneEl.is('vr-mode') || isNullVector(deltaHMDPosition)) { return; }
      previousHMDPosition.copy(currentHMDPosition);
      // Do nothing if we have not moved.
      if (!sceneEl.is('vr-mode')) { return; }
      el.setAttribute('position', {
        x: currentPosition.x + deltaHMDPosition.x,
        y: currentPosition.y + deltaHMDPosition.y,
        z: currentPosition.z + deltaHMDPosition.z
      });
    };
  })(),

  calculateHMDPosition: function () {
    var dolly = this.dolly;
    var position = new THREE.Vector3();
    dolly.updateMatrix();
    position.setFromMatrixPosition(dolly.matrix);
    return position;
  },

  onMouseMove: function (event) {
    var pitchObject = this.pitchObject;
    var yawObject = this.yawObject;
    var previousMouseEvent = this.previousMouseEvent;

    if (!this.mouseDown || !this.data.enabled) { return; }

    var movementX = event.movementX || event.mozMovementX;
    var movementY = event.movementY || event.mozMovementY;

    if (movementX === undefined || movementY === undefined) {
      movementX = event.screenX - previousMouseEvent.screenX;
      movementY = event.screenY - previousMouseEvent.screenY;
    }
    this.previousMouseEvent = event;

    yawObject.rotation.y -= movementX * 0.002;
    pitchObject.rotation.x -= movementY * 0.002;
    pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x));
  },

  onMouseDown: function (event) {
    this.mouseDown = true;
    this.previousMouseEvent = event;
  },

  releaseMouse: function () {
    this.mouseDown = false;
  },

  onTouchStart: function (e) {
    if (e.touches.length !== 1) { return; }
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
    this.touchStarted = true;
  },

  onTouchMove: function (e) {
    var deltaY;
    var yawObject = this.yawObject;
    if (!this.touchStarted) { return; }
    deltaY = 2 * Math.PI * (e.touches[0].pageX - this.touchStart.x) /
            this.el.sceneEl.canvas.clientWidth;
    // Limits touch orientaion to to yaw (y axis)
    yawObject.rotation.y -= deltaY * 0.5;
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
  },

  onTouchEnd: function () {
    this.touchStarted = false;
  }
});

function isNullVector (vector) {
  return vector.x === 0 && vector.y === 0 && vector.z === 0;
}
<commit_msg>Fix of head tracking disabled on non vr mode on mobile<commit_after>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');
var isMobile = require('../utils/').isMobile();

// To avoid recalculation at every mouse movement tick
var PI_2 = Math.PI / 2;
var radToDeg = THREE.Math.radToDeg;

module.exports.Component = registerComponent('look-controls', {
  dependencies: ['position', 'rotation'],

  schema: {
    enabled: { default: true },
    hmdEnabled: { default: true },
    standing: { default: true }
  },

  init: function () {
    this.previousHMDPosition = new THREE.Vector3();
    this.setupMouseControls();
    this.setupHMDControls();
    this.bindMethods();
  },

  update: function (oldData) {
    var data = this.data;
    var hmdEnabled = data.hmdEnabled;
    if (!data.enabled) { return; }
    if (!hmdEnabled && oldData && hmdEnabled !== oldData.hmdEnabled) {
      this.pitchObject.rotation.set(0, 0, 0);
      this.yawObject.rotation.set(0, 0, 0);
    }
    this.controls.standing = data.standing;
    this.controls.update();
    this.updateOrientation();
    this.updatePosition();
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
  },

  tick: function (t) {
    this.update();
  },

  remove: function () {
    this.pause();
  },

  bindMethods: function () {
    this.onMouseDown = this.onMouseDown.bind(this);
    this.onMouseMove = this.onMouseMove.bind(this);
    this.releaseMouse = this.releaseMouse.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchMove = this.onTouchMove.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
  },

  setupMouseControls: function () {
    // The canvas where the scene is painted
    this.mouseDown = false;
    this.pitchObject = new THREE.Object3D();
    this.yawObject = new THREE.Object3D();
    this.yawObject.position.y = 10;
    this.yawObject.add(this.pitchObject);
  },

  setupHMDControls: function () {
    this.dolly = new THREE.Object3D();
    this.euler = new THREE.Euler();
    this.controls = new THREE.VRControls(this.dolly);
  },

  addEventListeners: function () {
    var sceneEl = this.el.sceneEl;
    var canvasEl = sceneEl.canvas;

    // listen for canvas to load.
    if (!canvasEl) {
      sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this));
      return;
    }

    // Mouse Events
    canvasEl.addEventListener('mousedown', this.onMouseDown, false);
    canvasEl.addEventListener('mousemove', this.onMouseMove, false);
    canvasEl.addEventListener('mouseup', this.releaseMouse, false);
    canvasEl.addEventListener('mouseout', this.releaseMouse, false);

    // Touch events
    canvasEl.addEventListener('touchstart', this.onTouchStart);
    canvasEl.addEventListener('touchmove', this.onTouchMove);
    canvasEl.addEventListener('touchend', this.onTouchEnd);
  },

  removeEventListeners: function () {
    var sceneEl = document.querySelector('a-scene');
    var canvasEl = sceneEl && sceneEl.canvas;
    if (!canvasEl) { return; }

    // Mouse Events
    canvasEl.removeEventListener('mousedown', this.onMouseDown);
    canvasEl.removeEventListener('mousemove', this.onMouseMove);
    canvasEl.removeEventListener('mouseup', this.releaseMouse);
    canvasEl.removeEventListener('mouseout', this.releaseMouse);

    // Touch events
    canvasEl.removeEventListener('touchstart', this.onTouchStart);
    canvasEl.removeEventListener('touchmove', this.onTouchMove);
    canvasEl.removeEventListener('touchend', this.onTouchEnd);
  },

  updateOrientation: (function () {
    var hmdEuler = new THREE.Euler();
    return function () {
      var pitchObject = this.pitchObject;
      var yawObject = this.yawObject;
      var hmdQuaternion = this.calculateHMDQuaternion();
      var sceneEl = this.el.sceneEl;
      var rotation;
      hmdEuler.setFromQuaternion(hmdQuaternion, 'YXZ');
      if (isMobile) {
        // In mobile we allow camera rotation with touch events and sensors
        rotation = {
          x: radToDeg(hmdEuler.x) + radToDeg(pitchObject.rotation.x),
          y: radToDeg(hmdEuler.y) + radToDeg(yawObject.rotation.y),
          z: radToDeg(hmdEuler.z)
        };
      } else if (!sceneEl.is('vr-mode') || isNullVector(hmdEuler) || !this.data.hmdEnabled) {
        // Mouse look only if HMD disabled or no info coming from the sensors
        rotation = {
          x: radToDeg(pitchObject.rotation.x),
          y: radToDeg(yawObject.rotation.y),
          z: 0
        };
      } else {
        // Mouse rotation ignored with an active headset.
        // The user head rotation takes priority
        rotation = {
          x: radToDeg(hmdEuler.x),
          y: radToDeg(hmdEuler.y),
          z: radToDeg(hmdEuler.z)
        };
      }
      this.el.setAttribute('rotation', rotation);
    };
  })(),

  calculateHMDQuaternion: (function () {
    var hmdQuaternion = new THREE.Quaternion();
    return function () {
      hmdQuaternion.copy(this.dolly.quaternion);
      return hmdQuaternion;
    };
  })(),

  updatePosition: (function () {
    var deltaHMDPosition = new THREE.Vector3();
    return function () {
      var el = this.el;
      var currentPosition = el.getComputedAttribute('position');
      var currentHMDPosition;
      var previousHMDPosition = this.previousHMDPosition;
      var sceneEl = this.el.sceneEl;
      currentHMDPosition = this.calculateHMDPosition();
      deltaHMDPosition.copy(currentHMDPosition).sub(previousHMDPosition);
      if (!sceneEl.is('vr-mode') || isNullVector(deltaHMDPosition)) { return; }
      previousHMDPosition.copy(currentHMDPosition);
      // Do nothing if we have not moved.
      if (!sceneEl.is('vr-mode')) { return; }
      el.setAttribute('position', {
        x: currentPosition.x + deltaHMDPosition.x,
        y: currentPosition.y + deltaHMDPosition.y,
        z: currentPosition.z + deltaHMDPosition.z
      });
    };
  })(),

  calculateHMDPosition: function () {
    var dolly = this.dolly;
    var position = new THREE.Vector3();
    dolly.updateMatrix();
    position.setFromMatrixPosition(dolly.matrix);
    return position;
  },

  onMouseMove: function (event) {
    var pitchObject = this.pitchObject;
    var yawObject = this.yawObject;
    var previousMouseEvent = this.previousMouseEvent;

    if (!this.mouseDown || !this.data.enabled) { return; }

    var movementX = event.movementX || event.mozMovementX;
    var movementY = event.movementY || event.mozMovementY;

    if (movementX === undefined || movementY === undefined) {
      movementX = event.screenX - previousMouseEvent.screenX;
      movementY = event.screenY - previousMouseEvent.screenY;
    }
    this.previousMouseEvent = event;

    yawObject.rotation.y -= movementX * 0.002;
    pitchObject.rotation.x -= movementY * 0.002;
    pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x));
  },

  onMouseDown: function (event) {
    this.mouseDown = true;
    this.previousMouseEvent = event;
  },

  releaseMouse: function () {
    this.mouseDown = false;
  },

  onTouchStart: function (e) {
    if (e.touches.length !== 1) { return; }
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
    this.touchStarted = true;
  },

  onTouchMove: function (e) {
    var deltaY;
    var yawObject = this.yawObject;
    if (!this.touchStarted) { return; }
    deltaY = 2 * Math.PI * (e.touches[0].pageX - this.touchStart.x) /
            this.el.sceneEl.canvas.clientWidth;
    // Limits touch orientaion to to yaw (y axis)
    yawObject.rotation.y -= deltaY * 0.5;
    this.touchStart = {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
  },

  onTouchEnd: function () {
    this.touchStarted = false;
  }
});

function isNullVector (vector) {
  return vector.x === 0 && vector.y === 0 && vector.z === 0;
}
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'gatsby'
import Headroom from 'react-headroom'

import { Flex, Box, Heading, Text } from 'rebass'

const PostHead = props => {
  return (
    <Headroom
      className="relativeHeadroom"
      style={{
        position: 'fixed',
        zIndex: '300',
        transition: 'all .5s ease-in-out',
      }}
    >
      <Flex
        p={[4, 5]}
        width={[1]}
        flexWrap="wrap"
        flexDirection="column"
        bg="var(--color-base-95)"
      >
        <Box>
          <Link
            to={`/`}
            className="noUnderline linkAccentReset scopedLinkAccent"
          >
            <Heading css={{ display: 'inline-block' }}>⬅ Back</Heading>
          </Link>
        </Box>
        <Heading pt={3} pb={1} fontSize={5}>
          {props.title}
        </Heading>

        <Flex
          width={[1]}
          flexWrap="wrap"
          alignContent="center"
          flexDirection="row"
        >
          {props.tags.map(tag => (
            <Box
              key={tag.id}
              mr={2}
              mb={2}
              className="tag linkAccentReset scopedLinkAccent"
            >
              <Link to={`/tag/${tag.slug}/`}>
                <Heading fontSize={3}>🏷 {tag.title}</Heading>
              </Link>
            </Box>
          ))}
        </Flex>
      </Flex>
    </Headroom>
  )
}

export default PostHead
<commit_msg>Fix blog back link<commit_after>import React from 'react'
import { Link } from 'gatsby'
import Headroom from 'react-headroom'

import { Flex, Box, Heading } from 'rebass'

const PostHead = props => {
  return (
    <Headroom
      className="relativeHeadroom"
      style={{
        position: 'fixed',
        zIndex: '300',
        transition: 'all .5s ease-in-out',
      }}
    >
      <Flex
        p={[4, 5]}
        width={[1]}
        flexWrap="wrap"
        flexDirection="column"
        bg="var(--color-base-95)"
      >
        <Box>
          <Link
            to={`/blog/`}
            className="noUnderline linkAccentReset scopedLinkAccent"
          >
            <Heading css={{ display: 'inline-block' }}>⬅ Back</Heading>
          </Link>
        </Box>
        <Heading pt={3} pb={1} fontSize={5}>
          {props.title}
        </Heading>

        <Flex
          width={[1]}
          flexWrap="wrap"
          alignContent="center"
          flexDirection="row"
        >
          {props.tags.map(tag => (
            <Box
              key={tag.id}
              mr={2}
              mb={2}
              className="tag linkAccentReset scopedLinkAccent"
            >
              <Link to={`/tag/${tag.slug}/`}>
                <Heading fontSize={3}>🏷 {tag.title}</Heading>
              </Link>
            </Box>
          ))}
        </Flex>
      </Flex>
    </Headroom>
  )
}

export default PostHead
<|endoftext|>


<commit_before>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
import React from 'react';

//import FollowButton from './follow-button';
import moment from 'moment';
import FollowTagButton from './follow-tag-button';
//import { getUrl, URL_NAMES } from '../utils/urlGenerator';

export default class SchoolHeader extends React.Component {
  static displayName = 'SchoolHeader'

  render () {
    const {
      school,
      current_user,
      followTriggers
      /*
      i_am_following,
      following,
      followers
      */
    } = this.props;

    let name = school.url_name;
    let updated_at = moment(school.updated_at).format('MMMM D, HH:MM');
    /*
    let followingCount;
    let followersCount;
    */
    if (school.name) {
      name = school.name;
    }

    /*
    if (following && following[school.id]) {
      if (current_user.id != school.id) {
        followingCount = (

            {following[school.id].length}

            Following

        );
      } else {
        followingCount = (

            {following[school.id].length}

            <Link to={getUrl(URL_NAMES.MANAGE_FOLLOWERS)}>Following</Link>

        );
      }
    }
    if (followers && followers[school.id]) {
      if (current_user.id != school.id) {
        followersCount = (

            {followers[school.id].length}

            Followers

        );
      } else {
        followersCount = (

            {followers[school.id].length}

            <Link to={getUrl(URL_NAMES.MANAGE_FOLLOWERS)}>Followers</Link>

        );
      }

    }
    */
    name = name.trim();

    return (

{name}

{updated_at}

                <FollowTagButton
                  current_user={current_user}
                  followed_tags={current_user.followed_schools}
                  tag={school.url_name}
                  triggers={followTriggers}
                />

              {/*

                {followingCount}

                {followersCount}

                <FollowButton active_school={current_user} school={school} following={i_am_following} triggers={this.props.triggers} />

              */}

    )
  }
}
<commit_msg>Fix SchoolHeader<commit_after>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
import React from 'react';

//import FollowButton from './follow-button';
import moment from 'moment';
import FollowTagButton from './follow-tag-button';
//import { getUrl, URL_NAMES } from '../utils/urlGenerator';

export default class SchoolHeader extends React.Component {
  static displayName = 'SchoolHeader'

  render () {
    const {
      school,
      current_user,
      followTriggers
      /*
      i_am_following,
      following,
      followers
      */
    } = this.props;

    let name = school.url_name;
    let updated_at = moment(school.updated_at).format('MMMM D, HH:MM');
    /*
    let followingCount;
    let followersCount;
    */
    if (school.name) {
      name = school.name;
    }

    /*
    if (following && following[school.id]) {
      if (current_user.id != school.id) {
        followingCount = (

            {following[school.id].length}

            Following

        );
      } else {
        followingCount = (

            {following[school.id].length}

            <Link to={getUrl(URL_NAMES.MANAGE_FOLLOWERS)}>Following</Link>

        );
      }
    }
    if (followers && followers[school.id]) {
      if (current_user.id != school.id) {
        followersCount = (

            {followers[school.id].length}

            Followers

        );
      } else {
        followersCount = (

            {followers[school.id].length}

            <Link to={getUrl(URL_NAMES.MANAGE_FOLLOWERS)}>Followers</Link>

        );
      }

    }
    */
    name = name.trim();

    let followedSchools = {};

    for (let followedSchool of current_user.followed_schools) {
      followedSchools[followedSchool.url_name] = followedSchool;
    }

    return (

{name}

{updated_at}

                <FollowTagButton
                  current_user={current_user}
                  followed_tags={followedSchools}
                  tag={school.url_name}
                  triggers={followTriggers}
                />

              {/*

                {followingCount}

                {followersCount}

                <FollowButton active_school={current_user} school={school} following={i_am_following} triggers={this.props.triggers} />

              */}

    )
  }
}
<|endoftext|>


<commit_before>/* Copyright 2012 The Go Authors.   All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */
'use strict';

/* Services */

angular.module('tour.services', []).

// Google Analytics
factory('analytics', ['$window',
    function(win) {
        var track = win.trackPageview || (function() {});
        return {
            trackView: track
        };
    }
]).

// Internationalization
factory('i18n', ['translation',
    function(translation) {
        return {
            l: function(key) {
                if (translation[key]) return translation[key];
                return '(no translation for ' + key + ')';
            }
        };
    }
]).

// Running code
factory('run', ['$window', 'editor',
    function(win, editor) {
        var writeInterceptor = function(writer) {
            return function(write) {
                if (write.Kind == 'stderr') {
                    var lines = write.Body.split('\n');
                    for (var i in lines) {
                        var match = lines[i].match(/prog\.go:([0-9]+): ([^\n]*)/);
                        if (match !== null) {
                            editor.highlight(match[1], match[2]);
                        }
                    }
                }
                writer(write);
            };
        };
        return function(code, output, options) {
            // PlaygroundOutput is defined in playground.js which is prepended
            // to the generated script.js in gotour/tour.go.
            // The next line removes the jshint warning.
            // global PlaygroundOutput
            win.transport.Run(code, writeInterceptor(PlaygroundOutput(output)), options);
        };
    }
]).

// Formatting code
factory('fmt', ['$http',
    function($http) {
        return function(body) {
            var params = $.param({
                'body': body
            });
            var headers = {
                'Content-Type': 'application/x-www-form-urlencoded'
            };
            return $http.post('/fmt', params, {
                headers: headers
            });
        };
    }
]).

// Local storage, persistent to page refreshing.
factory('storage', ['$window',
    function(win) {
        if (win.localStorage) {
            return {
                get: function(key) {
                    return win.localStorage.getItem(key);
                },
                set: function(key, val) {
                    win.localStorage.setItem(key, val);
                }
            };
        }
        return {
            get: function() {
                return null;
            },
            set: function() {}
        };
    }
]).

// Editor context service, kept through the whole app.
factory('editor', ['$window', 'storage',
    function(win, storage) {
        var ctx = {
            syntax: storage.get('syntax') === 'true',
            toggleSyntax: function() {
                ctx.syntax = !ctx.syntax;
                storage.set('syntax', ctx.syntax);
                ctx.paint();
            },
            paint: function() {
                var mode = ctx.syntax && 'text/x-go' || 'text/x-go-comment';
                // Wait for codemirror to start.
                var set = function() {
                    if ($('.CodeMirror').length > 0) {
                        var cm = $('.CodeMirror')[0].CodeMirror;
                        if (cm.getOption('mode') == mode) {
                            cm.refresh();
                            return;
                        }
                        cm.setOption('mode', mode);
                    }
                    win.setTimeout(set, 10);
                };
                set();
            },
            highlight: function(line, message) {
                $('.CodeMirror-code > div:nth-child(' + line + ')')
                    .addClass('line-error').attr('title', message);
            },
            onChange: function() {
                $('.line-error').removeClass('line-error').attr('title', null);
            }
        };
        // Set in the window so the onChange function in the codemirror config
        // can call it.
        win.codeChanged = ctx.onChange;
        return ctx;
    }
]).

// Table of contents management and navigation
factory('toc', ['$http', '$q', '$log', 'tableOfContents', 'storage',
    function($http, $q, $log, tableOfContents, storage) {
        var modules = tableOfContents;

        var lessons = {};

        var prevLesson = function(id) {
            var mod = lessons[id].module;
            var idx = mod.lessons.indexOf(id);
            if (idx < 0) return '';
            if (idx > 0) return mod.lessons[idx - 1];

            idx = modules.indexOf(mod);
            if (idx <= 0) return '';
            mod = modules[idx - 1];
            return mod.lessons[mod.lessons.length - 1];
        };

        var nextLesson = function(id) {
            var mod = lessons[id].module;
            var idx = mod.lessons.indexOf(id);
            if (idx < 0) return '';
            if (idx + 1 < mod.lessons.length) return mod.lessons[idx + 1];

            idx = modules.indexOf(mod);
            if (idx < 0 || modules.length <= idx + 1) return '';
            mod = modules[idx + 1];
            return mod.lessons[0];
        };

        $http.get('/lesson/').then(
            function(data) {
                lessons = data.data;
                for (var m = 0; m < modules.length; m++) {
                    var module = modules[m];
                    module.lesson = {};
                    for (var l = 0; l < modules[m].lessons.length; l++) {
                        var lessonName = module.lessons[l];
                        var lesson = lessons[lessonName];
                        lesson.module = module;
                        module.lesson[lessonName] = lesson;

                        // replace file contents with locally stored copies.
                        for (var p = 0; p < lesson.Pages.length; p++) {
                            var page = lesson.Pages[p];
                            for (var f = 0; f < page.Files.length; f++) {
                                page.Files[f].OrigContent = page.Files[f].Content;
                                var val = storage.get(lessonName + '.' + p + '.' + f);
                                if (val !== null) {
                                    page.Files[f].Content = val;
                                }
                            }
                        }
                    }
                }
                moduleQ.resolve(modules);
                lessonQ.resolve(lessons);
            },
            function(error) {
                $log.error('error loading lessons : ', error);
                moduleQ.reject(error);
                lessonQ.reject(error);
            }
        );

        var moduleQ = $q.defer();
        var lessonQ = $q.defer();

        return {
            modules: moduleQ.promise,
            lessons: lessonQ.promise,
            prevLesson: prevLesson,
            nextLesson: nextLesson
        };
    }
]);
<commit_msg>go-tour: fix error highlighting when running locally<commit_after>/* Copyright 2012 The Go Authors.   All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */
'use strict';

/* Services */

angular.module('tour.services', []).

// Google Analytics
factory('analytics', ['$window',
    function(win) {
        var track = win.trackPageview || (function() {});
        return {
            trackView: track
        };
    }
]).

// Internationalization
factory('i18n', ['translation',
    function(translation) {
        return {
            l: function(key) {
                if (translation[key]) return translation[key];
                return '(no translation for ' + key + ')';
            }
        };
    }
]).

// Running code
factory('run', ['$window', 'editor',
    function(win, editor) {
        var writeInterceptor = function(writer) {
            return function(write) {
                if (write.Kind == 'stderr') {
                    var lines = write.Body.split('\n');
                    for (var i in lines) {
                        var match = lines[i].match(/.*\.go:([0-9]+): ([^\n]*)/);
                        if (match !== null) {
                            editor.highlight(match[1], match[2]);
                        }
                    }
                }
                writer(write);
            };
        };
        return function(code, output, options) {
            // PlaygroundOutput is defined in playground.js which is prepended
            // to the generated script.js in gotour/tour.go.
            // The next line removes the jshint warning.
            // global PlaygroundOutput
            win.transport.Run(code, writeInterceptor(PlaygroundOutput(output)), options);
        };
    }
]).

// Formatting code
factory('fmt', ['$http',
    function($http) {
        return function(body) {
            var params = $.param({
                'body': body
            });
            var headers = {
                'Content-Type': 'application/x-www-form-urlencoded'
            };
            return $http.post('/fmt', params, {
                headers: headers
            });
        };
    }
]).

// Local storage, persistent to page refreshing.
factory('storage', ['$window',
    function(win) {
        if (win.localStorage) {
            return {
                get: function(key) {
                    return win.localStorage.getItem(key);
                },
                set: function(key, val) {
                    win.localStorage.setItem(key, val);
                }
            };
        }
        return {
            get: function() {
                return null;
            },
            set: function() {}
        };
    }
]).

// Editor context service, kept through the whole app.
factory('editor', ['$window', 'storage',
    function(win, storage) {
        var ctx = {
            syntax: storage.get('syntax') === 'true',
            toggleSyntax: function() {
                ctx.syntax = !ctx.syntax;
                storage.set('syntax', ctx.syntax);
                ctx.paint();
            },
            paint: function() {
                var mode = ctx.syntax && 'text/x-go' || 'text/x-go-comment';
                // Wait for codemirror to start.
                var set = function() {
                    if ($('.CodeMirror').length > 0) {
                        var cm = $('.CodeMirror')[0].CodeMirror;
                        if (cm.getOption('mode') == mode) {
                            cm.refresh();
                            return;
                        }
                        cm.setOption('mode', mode);
                    }
                    win.setTimeout(set, 10);
                };
                set();
            },
            highlight: function(line, message) {
                $('.CodeMirror-code > div:nth-child(' + line + ')')
                    .addClass('line-error').attr('title', message);
            },
            onChange: function() {
                $('.line-error').removeClass('line-error').attr('title', null);
            }
        };
        // Set in the window so the onChange function in the codemirror config
        // can call it.
        win.codeChanged = ctx.onChange;
        return ctx;
    }
]).

// Table of contents management and navigation
factory('toc', ['$http', '$q', '$log', 'tableOfContents', 'storage',
    function($http, $q, $log, tableOfContents, storage) {
        var modules = tableOfContents;

        var lessons = {};

        var prevLesson = function(id) {
            var mod = lessons[id].module;
            var idx = mod.lessons.indexOf(id);
            if (idx < 0) return '';
            if (idx > 0) return mod.lessons[idx - 1];

            idx = modules.indexOf(mod);
            if (idx <= 0) return '';
            mod = modules[idx - 1];
            return mod.lessons[mod.lessons.length - 1];
        };

        var nextLesson = function(id) {
            var mod = lessons[id].module;
            var idx = mod.lessons.indexOf(id);
            if (idx < 0) return '';
            if (idx + 1 < mod.lessons.length) return mod.lessons[idx + 1];

            idx = modules.indexOf(mod);
            if (idx < 0 || modules.length <= idx + 1) return '';
            mod = modules[idx + 1];
            return mod.lessons[0];
        };

        $http.get('/lesson/').then(
            function(data) {
                lessons = data.data;
                for (var m = 0; m < modules.length; m++) {
                    var module = modules[m];
                    module.lesson = {};
                    for (var l = 0; l < modules[m].lessons.length; l++) {
                        var lessonName = module.lessons[l];
                        var lesson = lessons[lessonName];
                        lesson.module = module;
                        module.lesson[lessonName] = lesson;

                        // replace file contents with locally stored copies.
                        for (var p = 0; p < lesson.Pages.length; p++) {
                            var page = lesson.Pages[p];
                            for (var f = 0; f < page.Files.length; f++) {
                                page.Files[f].OrigContent = page.Files[f].Content;
                                var val = storage.get(lessonName + '.' + p + '.' + f);
                                if (val !== null) {
                                    page.Files[f].Content = val;
                                }
                            }
                        }
                    }
                }
                moduleQ.resolve(modules);
                lessonQ.resolve(lessons);
            },
            function(error) {
                $log.error('error loading lessons : ', error);
                moduleQ.reject(error);
                lessonQ.reject(error);
            }
        );

        var moduleQ = $q.defer();
        var lessonQ = $q.defer();

        return {
            modules: moduleQ.promise,
            lessons: lessonQ.promise,
            prevLesson: prevLesson,
            nextLesson: nextLesson
        };
    }
]);
<|endoftext|>


<commit_before>var tutorial = (function () {

var exports = {};

function set_tutorial_status(status, callback) {
    return channel.post({
        url:      '/json/tutorial_status',
        data:     {status: JSON.stringify(status)},
        success:  callback,
    });
}

function finale() {
    set_tutorial_status("finished");

    var sender_bot = "welcome-bot@zulip.com";
    narrow.by('pm-with', sender_bot, {select_first_unread: true, trigger: 'sidebar'});
    compose_actions.cancel();
}

exports.start = function () {
    finale();
};

exports.initialize = function () {
    if (page_params.needs_tutorial) {
        exports.start();
    }
};

return exports;
}());
if (typeof module !== 'undefined') {
    module.exports = tutorial;
}
<commit_msg>tutorial: Narrow to /is/private instead of pm-with welcome-bot.<commit_after>var tutorial = (function () {

var exports = {};

function set_tutorial_status(status, callback) {
    return channel.post({
        url:      '/json/tutorial_status',
        data:     {status: JSON.stringify(status)},
        success:  callback,
    });
}

function finale() {
    set_tutorial_status("finished");
    narrow.by('is', 'private', {select_first_unread: true, trigger: 'sidebar'});
}

exports.start = function () {
    finale();
};

exports.initialize = function () {
    if (page_params.needs_tutorial) {
        exports.start();
    }
};

return exports;
}());
if (typeof module !== 'undefined') {
    module.exports = tutorial;
}
<|endoftext|>


<commit_before>var viewport = (function () {
var exports = {};

var jwindow;
var dimensions = {};
var in_stoppable_autoscroll = false;

exports.at_top = function () {
    return (exports.scrollTop() <= 0);
};

exports.message_viewport_info = function () {
    // Return a structure that tells us details of the viewport
    // accounting for fixed elements like the top navbar.
    //
    // The message_header is NOT considered to be part of the visible
    // message pane, which should make sense for callers, who will
    // generally be concerned about whether actual message content is
    // visible.

    var res = {};

    var element_just_above_us = $("#tab_bar_underpadding");

    res.visible_top =
        element_just_above_us.position().top
        + element_just_above_us.height()
        + $(".message_header").outerHeight();

    var element_just_below_us = $("#compose");

    res.visible_height =
        element_just_below_us.position().top
        - element_just_above_us.position().top
        - $(".message_header").outerHeight();

    return res;
};

exports.at_bottom = function () {
    var bottom = exports.scrollTop() + exports.height();
    var full_height = exports.message_pane.prop('scrollHeight');

    // We only know within a pixel or two if we're
    // exactly at the bottom, due to browser quirkiness,
    // and we err on the side of saying that we are at
    // the bottom.
    return bottom + 2 >= full_height;
};

// This differs from at_bottom in that it only requires the bottom message to
// be visible, but you may be able to scroll down further.
exports.bottom_message_visible = function () {
    var last_row = rows.last_visible();
    if (last_row.length) {
        var message_bottom = last_row[0].getBoundingClientRect().bottom;
        var bottom_of_feed = $("#compose")[0].getBoundingClientRect().top;
        return bottom_of_feed > message_bottom;
    } else {
        return false;
    }
};

exports.is_below_visible_bottom = function (offset) {
    return offset > exports.scrollTop() + exports.height() - $("#compose").height();
};

exports.set_message_position = function (message_top, message_height, viewport_info, ratio) {
    // message_top = offset of the top of a message that you are positioning
    // message_height = height of the message that you are positioning
    // viewport_info = result of calling viewport.message_viewport_info
    // ratio = fraction indicating how far down the screen the msg should be

    var how_far_down_in_visible_page = viewport_info.visible_height * ratio;

    // special case: keep large messages fully on the screen
    if (how_far_down_in_visible_page + message_height > viewport_info.visible_height) {
        how_far_down_in_visible_page = viewport_info.visible_height - message_height;

        // Next handle truly gigantic messages.  We just say that the top of the
        // message goes to the top of the viewing area.  Realistically, gigantic
        // messages should either be condensed, socially frowned upon, or scrolled
        // with the mouse.
        if (how_far_down_in_visible_page < 0) {
            how_far_down_in_visible_page = 0;
        }
    }

    var hidden_top =
        viewport_info.visible_top
        - exports.scrollTop();

    var message_offset =
        how_far_down_in_visible_page
        + hidden_top;

    var new_scroll_top =
        message_top
        - message_offset;

    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    exports.scrollTop(new_scroll_top);
};

function in_viewport_or_tall(rect, top_of_feed, bottom_of_feed,
                             require_fully_visible) {
    if (require_fully_visible) {
        return ((rect.top > top_of_feed) && // Message top is in view and
                ((rect.bottom < bottom_of_feed) || // message is fully in view or
                 ((rect.height > bottom_of_feed - top_of_feed) &&
                  (rect.top < bottom_of_feed)))); // message is tall.
    } else {
        return (rect.bottom > top_of_feed && rect.top < bottom_of_feed);
    }
}

function add_to_visible_messages(candidates, visible_messages,
                                 top_of_feed, bottom_of_feed,
                                 require_fully_visible) {
    _.every(candidates, function (row) {
        var row_rect = row.getBoundingClientRect();
        // Mark very tall messages as read once we've gotten past them
        if (in_viewport_or_tall(row_rect, top_of_feed, bottom_of_feed,
                                require_fully_visible)) {
            visible_messages.push(current_msg_list.get(rows.id($(row))));
            return true;
        } else {
            return false;
        }
    });
}

var top_of_feed = new util.CachedValue({
    compute_value: function () {
        return $("#tab_bar_underpadding")[0].getBoundingClientRect().bottom;
    }
});

var bottom_of_feed = new util.CachedValue({
    compute_value: function () {
        return $("#compose")[0].getBoundingClientRect().top;
    }
});

exports.visible_messages = function (require_fully_visible) {
    // Note that when using getBoundingClientRect() we are getting offsets
    // relative to the visible window, but when using jQuery's offset() we are
    // getting offsets relative to the full scrollable window. You can't try to
    // compare heights from these two methods.

    var selected = current_msg_list.selected_message();
    var height = bottom_of_feed.get() - top_of_feed.get();

    // Being simplistic about this, the smallest message is 25 px high.
    var selected_row = current_msg_list.selected_row();
    var num_neighbors = Math.floor(height / 25);

    // We do this explicitly without merges and without recalculating
    // the feed bounds to keep this computation as cheap as possible.
    var visible_messages = [];
    var messages_above_pointer = selected_row.prevAll("div.message_row[zid]:lt(" + num_neighbors + ")");
    var messages_below_pointer = selected_row.nextAll("div.message_row[zid]:lt(" + num_neighbors + ")");
    add_to_visible_messages(selected_row, visible_messages,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible);
    add_to_visible_messages(messages_above_pointer, visible_messages,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible);
    add_to_visible_messages(messages_below_pointer, visible_messages,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible);

    return visible_messages;
};

exports.scrollTop = function viewport_scrollTop (target_scrollTop) {
    var orig_scrollTop = exports.message_pane.scrollTop();
    if (target_scrollTop === undefined) {
        return orig_scrollTop;
    }
    var ret = exports.message_pane.scrollTop(target_scrollTop);
    var new_scrollTop = exports.message_pane.scrollTop();
    var space_to_scroll = $("#bottom_whitespace").offset().top - viewport.height();

    // Check whether our scrollTop didn't move even though one could have scrolled down
    if (space_to_scroll > 0 && target_scrollTop > 0 &&
        orig_scrollTop === 0 && new_scrollTop === 0) {
        // Chrome has a bug where sometimes calling
        // window.scrollTop(x) has no effect, resulting in the browser
        // staying at 0 -- and afterwards if you call
        // window.scrollTop(x) again, it will still do nothing.  To
        // fix this, we need to first scroll to some other place.
        blueslip.info("ScrollTop did nothing when scrolling to " + target_scrollTop + ", fixing...");
        // First scroll to 1 in order to clear the stuck state
        exports.message_pane.scrollTop(1);
        // And then scroll where we intended to scroll to
        ret = exports.message_pane.scrollTop(target_scrollTop);
        if (exports.message_pane.scrollTop() === 0) {
            blueslip.info("ScrollTop fix did not work when scrolling to " + target_scrollTop +
                          "!  space_to_scroll was " + space_to_scroll);
        }
    }
    return ret;
};

exports.set_message_offset = function viewport_set_message_offset(offset) {
    var msg = current_msg_list.selected_row();
    exports.scrollTop(exports.scrollTop() + msg.offset().top - offset);
};

function make_dimen_wrapper(dimen_name, dimen_func) {
    dimensions[dimen_name] = new util.CachedValue({
        compute_value: function () {
            return dimen_func.call(exports.message_pane);
        }
    });
    return function viewport_dimension_wrapper() {
        if (arguments.length !== 0) {
            dimensions[dimen_name].reset();
            return dimen_func.apply(exports.message_pane, arguments);
        }
        return dimensions[dimen_name].get();
    };
}

exports.height = make_dimen_wrapper('height', $(exports.message_pane).height);
exports.width  = make_dimen_wrapper('width',  $(exports.message_pane).width);

exports.stop_auto_scrolling = function () {
    if (in_stoppable_autoscroll) {
        exports.message_pane.stop();
    }
};

exports.is_narrow = function () {
    // This basically returns true when we hide the right sidebar for
    // skinny mode.  It would be nice to have a less brittle
    // test for this.  See the "@media (max-width: 975px)" section in
    // zulip.css.
    return window.innerWidth <= 975;
};

exports.system_initiated_animate_scroll = function (scroll_amount) {
    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    var viewport_offset = exports.scrollTop();
    in_stoppable_autoscroll = true;
    exports.message_pane.animate({
        scrollTop: viewport_offset + scroll_amount,
        always: function () {
            in_stoppable_autoscroll = false;
        }
    });
};

exports.user_initiated_animate_scroll = function (scroll_amount) {
    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    in_stoppable_autoscroll = false; // defensive

    var viewport_offset = exports.scrollTop();

    exports.message_pane.animate({
        scrollTop: viewport_offset + scroll_amount
    });
};

$(function () {
    jwindow = $(window);
    exports.message_pane = $(".app");
    // This handler must be placed before all resize handlers in our application
    jwindow.resize(function () {
        dimensions.height.reset();
        dimensions.width.reset();
        top_of_feed.reset();
        bottom_of_feed.reset();
    });

    $(document).on('compose_started compose_canceled compose_finished', function () {
        bottom_of_feed.reset();
    });
});

return exports;
}());
<commit_msg>Refactor viewport.visible_messages and add a viewport.visible_groups<commit_after>var viewport = (function () {
var exports = {};

var jwindow;
var dimensions = {};
var in_stoppable_autoscroll = false;

exports.at_top = function () {
    return (exports.scrollTop() <= 0);
};

exports.message_viewport_info = function () {
    // Return a structure that tells us details of the viewport
    // accounting for fixed elements like the top navbar.
    //
    // The message_header is NOT considered to be part of the visible
    // message pane, which should make sense for callers, who will
    // generally be concerned about whether actual message content is
    // visible.

    var res = {};

    var element_just_above_us = $("#tab_bar_underpadding");

    res.visible_top =
        element_just_above_us.position().top
        + element_just_above_us.height()
        + $(".message_header").outerHeight();

    var element_just_below_us = $("#compose");

    res.visible_height =
        element_just_below_us.position().top
        - element_just_above_us.position().top
        - $(".message_header").outerHeight();

    return res;
};

exports.at_bottom = function () {
    var bottom = exports.scrollTop() + exports.height();
    var full_height = exports.message_pane.prop('scrollHeight');

    // We only know within a pixel or two if we're
    // exactly at the bottom, due to browser quirkiness,
    // and we err on the side of saying that we are at
    // the bottom.
    return bottom + 2 >= full_height;
};

// This differs from at_bottom in that it only requires the bottom message to
// be visible, but you may be able to scroll down further.
exports.bottom_message_visible = function () {
    var last_row = rows.last_visible();
    if (last_row.length) {
        var message_bottom = last_row[0].getBoundingClientRect().bottom;
        var bottom_of_feed = $("#compose")[0].getBoundingClientRect().top;
        return bottom_of_feed > message_bottom;
    } else {
        return false;
    }
};

exports.is_below_visible_bottom = function (offset) {
    return offset > exports.scrollTop() + exports.height() - $("#compose").height();
};

exports.set_message_position = function (message_top, message_height, viewport_info, ratio) {
    // message_top = offset of the top of a message that you are positioning
    // message_height = height of the message that you are positioning
    // viewport_info = result of calling viewport.message_viewport_info
    // ratio = fraction indicating how far down the screen the msg should be

    var how_far_down_in_visible_page = viewport_info.visible_height * ratio;

    // special case: keep large messages fully on the screen
    if (how_far_down_in_visible_page + message_height > viewport_info.visible_height) {
        how_far_down_in_visible_page = viewport_info.visible_height - message_height;

        // Next handle truly gigantic messages.  We just say that the top of the
        // message goes to the top of the viewing area.  Realistically, gigantic
        // messages should either be condensed, socially frowned upon, or scrolled
        // with the mouse.
        if (how_far_down_in_visible_page < 0) {
            how_far_down_in_visible_page = 0;
        }
    }

    var hidden_top =
        viewport_info.visible_top
        - exports.scrollTop();

    var message_offset =
        how_far_down_in_visible_page
        + hidden_top;

    var new_scroll_top =
        message_top
        - message_offset;

    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    exports.scrollTop(new_scroll_top);
};

function in_viewport_or_tall(rect, top_of_feed, bottom_of_feed,
                             require_fully_visible) {
    if (require_fully_visible) {
        return ((rect.top > top_of_feed) && // Message top is in view and
                ((rect.bottom < bottom_of_feed) || // message is fully in view or
                 ((rect.height > bottom_of_feed - top_of_feed) &&
                  (rect.top < bottom_of_feed)))); // message is tall.
    } else {
        return (rect.bottom > top_of_feed && rect.top < bottom_of_feed);
    }
}

function add_to_visible(candidates, visible,
                                 top_of_feed, bottom_of_feed,
                                 require_fully_visible,
                                 row_to_id) {
    _.every(candidates, function (row) {
        var row_rect = row.getBoundingClientRect();
        // Mark very tall messages as read once we've gotten past them
        if (in_viewport_or_tall(row_rect, top_of_feed, bottom_of_feed,
                                require_fully_visible)) {
            visible.push(row_to_id(row));
            return true;
        } else {
            return false;
        }
    });
}

var top_of_feed = new util.CachedValue({
    compute_value: function () {
        return $("#tab_bar_underpadding")[0].getBoundingClientRect().bottom;
    }
});

var bottom_of_feed = new util.CachedValue({
    compute_value: function () {
        return $("#compose")[0].getBoundingClientRect().top;
    }
});

function _visible_divs(selected_row, row_min_height, row_to_output, div_class, require_fully_visible) {
    // Note that when using getBoundingClientRect() we are getting offsets
    // relative to the visible window, but when using jQuery's offset() we are
    // getting offsets relative to the full scrollable window. You can't try to
    // compare heights from these two methods.
    var height = bottom_of_feed.get() - top_of_feed.get();
    var num_neighbors = Math.floor(height / row_min_height);

    // We do this explicitly without merges and without recalculating
    // the feed bounds to keep this computation as cheap as possible.
    var visible = [];
    var above_pointer = selected_row.prevAll("div." + div_class + ":lt(" + num_neighbors + ")");
    var below_pointer = selected_row.nextAll("div." + div_class + ":lt(" + num_neighbors + ")");
    add_to_visible(selected_row, visible,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible, row_to_output);
    add_to_visible(above_pointer, visible,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible, row_to_output);
    add_to_visible(below_pointer, visible,
                            top_of_feed.get(), bottom_of_feed.get(), require_fully_visible, row_to_output);

    return visible;
}

exports.visible_groups = function (require_fully_visible) {
    var selected_row = current_msg_list.selected_row();
    if (selected_row === undefined || selected_row.length === 0) {
        return;
    }

    var selected_group = rows.get_message_recipient_row(selected_row);

    function get_row(row) {
        return row;
    }

    // Being simplistic about this, the smallest group is about 75 px high.
    return _visible_divs(selected_group, 75, get_row, "recipient_row", require_fully_visible);
};

exports.visible_messages = function (require_fully_visible) {
    var selected_row = current_msg_list.selected_row();

    function row_to_id(row) {
        return current_msg_list.get(rows.id($(row)));
    }

    // Being simplistic about this, the smallest message is 25 px high.
    return _visible_divs(selected_row, 25, row_to_id, "message_row", require_fully_visible);
};

exports.scrollTop = function viewport_scrollTop (target_scrollTop) {
    var orig_scrollTop = exports.message_pane.scrollTop();
    if (target_scrollTop === undefined) {
        return orig_scrollTop;
    }
    var ret = exports.message_pane.scrollTop(target_scrollTop);
    var new_scrollTop = exports.message_pane.scrollTop();
    var space_to_scroll = $("#bottom_whitespace").offset().top - viewport.height();

    // Check whether our scrollTop didn't move even though one could have scrolled down
    if (space_to_scroll > 0 && target_scrollTop > 0 &&
        orig_scrollTop === 0 && new_scrollTop === 0) {
        // Chrome has a bug where sometimes calling
        // window.scrollTop(x) has no effect, resulting in the browser
        // staying at 0 -- and afterwards if you call
        // window.scrollTop(x) again, it will still do nothing.  To
        // fix this, we need to first scroll to some other place.
        blueslip.info("ScrollTop did nothing when scrolling to " + target_scrollTop + ", fixing...");
        // First scroll to 1 in order to clear the stuck state
        exports.message_pane.scrollTop(1);
        // And then scroll where we intended to scroll to
        ret = exports.message_pane.scrollTop(target_scrollTop);
        if (exports.message_pane.scrollTop() === 0) {
            blueslip.info("ScrollTop fix did not work when scrolling to " + target_scrollTop +
                          "!  space_to_scroll was " + space_to_scroll);
        }
    }
    return ret;
};

exports.set_message_offset = function viewport_set_message_offset(offset) {
    var msg = current_msg_list.selected_row();
    exports.scrollTop(exports.scrollTop() + msg.offset().top - offset);
};

function make_dimen_wrapper(dimen_name, dimen_func) {
    dimensions[dimen_name] = new util.CachedValue({
        compute_value: function () {
            return dimen_func.call(exports.message_pane);
        }
    });
    return function viewport_dimension_wrapper() {
        if (arguments.length !== 0) {
            dimensions[dimen_name].reset();
            return dimen_func.apply(exports.message_pane, arguments);
        }
        return dimensions[dimen_name].get();
    };
}

exports.height = make_dimen_wrapper('height', $(exports.message_pane).height);
exports.width  = make_dimen_wrapper('width',  $(exports.message_pane).width);

exports.stop_auto_scrolling = function () {
    if (in_stoppable_autoscroll) {
        exports.message_pane.stop();
    }
};

exports.is_narrow = function () {
    // This basically returns true when we hide the right sidebar for
    // skinny mode.  It would be nice to have a less brittle
    // test for this.  See the "@media (max-width: 975px)" section in
    // zulip.css.
    return window.innerWidth <= 975;
};

exports.system_initiated_animate_scroll = function (scroll_amount) {
    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    var viewport_offset = exports.scrollTop();
    in_stoppable_autoscroll = true;
    exports.message_pane.animate({
        scrollTop: viewport_offset + scroll_amount,
        always: function () {
            in_stoppable_autoscroll = false;
        }
    });
};

exports.user_initiated_animate_scroll = function (scroll_amount) {
    suppress_scroll_pointer_update = true; // Gets set to false in the scroll handler.
    in_stoppable_autoscroll = false; // defensive

    var viewport_offset = exports.scrollTop();

    exports.message_pane.animate({
        scrollTop: viewport_offset + scroll_amount
    });
};

$(function () {
    jwindow = $(window);
    exports.message_pane = $(".app");
    // This handler must be placed before all resize handlers in our application
    jwindow.resize(function () {
        dimensions.height.reset();
        dimensions.width.reset();
        top_of_feed.reset();
        bottom_of_feed.reset();
    });

    $(document).on('compose_started compose_canceled compose_finished', function () {
        bottom_of_feed.reset();
    });
});

return exports;
}());
<|endoftext|>


<commit_before>/**
 * Helper function for cross-browser CSS3 support, prepends all possible prefixes to all properties passed in
 * @param {Object} props Key/value pairs of CSS3 properties
 */
$.fn.css3 = function(props) {
	var css = {};
	var prefixes = ['webkit', 'moz', 'ms', 'o'];

	for(var prop in props)
	{
		for(var i=0; i<prefixes.length; i++) {
			css['-' + prefixes[i] + '-' + prop] = props[prop];
		}
		css[prop] = props[prop];
	}

	this.css(css);
	return this;
};

/**
 * This function provides the tile creation and the transition handling as the name says
 * @param {string} way The way of the transition (= in/out)
 * @param {Object} settings The animation perferences given by this.settings.animation
 * @param {string} activeClass The activeClass from this.settings
 * @param {string} image The url of the image get by jQuery like "url(http://domain.com/assets/slide1.jpg)"
 */
$.fn.transition = function(way,settings,activeClass,image) {

	var $container = $(this),
		width = $container.width(),
		height = $container.height(),
		$img = $container.find('.tiles'),
		initialized = !!($img.find('.tile').length > 0),
		imageBackground = (initialized ? image : $img.css('background-image')),
		n_tiles = settings.cols * settings.rows,
		tiles = [],
		$tiles,
		range = function ( min, max, rand ) {
			var arr = ( new Array( ++max - min ) )
				.join('.').split('.')
				.map(function( v,i ){ return min + i });
			return rand
				? arr.map(function( v ) { return [ Math.random(), v ] })
					.sort().map(function( v ) { return v[ 1 ] })
				: arr;
		},
		createTilesByImage = function() {
			for (var i = 0; i < n_tiles; i++) {
				tiles.push('

');
			}
			$tiles = $(tiles.join(''));
			$img.append($tiles);
			$tiles.css({
				width: width / settings.cols,
				height: height / settings.rows,
				backgroundImage: imageBackground
			}).css3({
				transition : 'none'
			});
			$tiles.each(function() {
				var pos = $(this).position();
				$(this).css('backgroundPosition', -pos.left + 'px ' + -pos.top + 'px').css3({
					'transition-property' : 'all',
					'transition-duration' : '.3s',
					'transition-timing-function' : 'cubic-bezier(0.445, 0.050, 0.550, 0.950)'
				});
			});
		},
		doAnimationWithTheCreatedTiles = function() {
			var tilesArr = range(0, n_tiles, settings.random),
				tileSpeed = settings.speed / n_tiles,
				animateTilesWithDelay = function() {
					tilesArr.forEach(function(tile, i) {
						$tiles.eq(tile).css3({'transition-delay':((i*tileSpeed)/1000)+'s'});
					});
				};

			switch (way) {
				case 'out':
					$tiles.css3({transition:'none'});
					$container.removeClass(activeClass);
					$img.css('background-image',imageBackground);
				break;
				case 'in':
					$img.css('background-image','');
					$tiles.css3({
						'transition-property' : 'all',
						'transition-duration' : '.3s',
						'transition-timing-function' : 'cubic-bezier(0.445, 0.050, 0.550, 0.950)'
					});
					animateTilesWithDelay();
					$container.addClass(activeClass);
					break;
			}
		},
		checkInitialization = function() {
			if (initialized) {
				$tiles = $img.find('.tile');
			} else createTilesByImage();
		};

	checkInitialization();
	doAnimationWithTheCreatedTiles();
};

Prometheus = function($slider, options) {
	var _this = this;

	this.$slider = $slider;
	this.settings = (options != undefined) ? $.extend({}, this.defaults, options) : this.defaults;
	this.settings.animation = (options.animation != undefined) ? $.extend({}, this.defaults.animation, options.animation) : this.defaults.animation;
	this.$slides = this.$slider.find(this.settings.slidesSelector);
	this.sliderLocked = false;
	this.timer = undefined;
    this.totalImages = (this.$slides.length);
    this.currentPos = 0;
	this.randomizeTransition = false;

	if (this.$slider.data('initialized')) return;
	this.$slider.data('initialized', true);

	this.initializeRandomTransitions();

	this.loadNecessaryPlugins();
	this.turnImagesIntoContainers();
	this.timer = setTimeout(function(){ _this.slide(1); }, this.settings.startDuration);
};

Prometheus.prototype.initializeRandomTransitions = function() {
	if (this.settings.animation.type === 'random') {
		this.$slider.find('ul').addClass(this.transitions[Math.floor((Math.random() * 3))]);
		this.randomizeTransition = true;
	}
};

Prometheus.prototype.turnImagesIntoContainers = function() {
	var _this = this;

	this.images = [];

	this.$slides.each(function() {
		var $this = $(this),
			$img = $this.find('img'),
			$tiles = $('

');

		$img.hide();
		$this.append($tiles);
		$tiles.css('background-image', 'url(' + $img.attr('src') + ')');
		_this.images.push($tiles.css('background-image'));
		$img.remove();
	});
};

Prometheus.prototype.controlNavigation = function() {
	var _this = this;

	_this.$slider.append('

');
	for(var i=0;i<_this.totalImages;i++)
		$('#pagination').append('

');

	Prometheus.prototype.slide = function(direction, forceSlide) {
		var _this = this;

		if (!_this.sliderLocked || forceSlide) {
			var nextPos = _this.getNextIndex(direction),
				$paginationPage = $('#pagination').find('li');

			_this.$slides.eq(_this.currentPos).removeClass(_this.settings.activeClass);
			_this.$slides.eq(nextPos).addClass(_this.settings.activeClass);

			$paginationPage.eq(_this.currentPos).removeClass(_this.settings.activeClass);
			$paginationPage.eq(nextPos).addClass(_this.settings.activeClass);

			_this.currentPos = nextPos;
		}

		if (_this.timer) clearTimeout(_this.timer);
	    _this.timer = setTimeout(function(){ _this.slide(1); }, _this.settings.duration);
	};
};

Prometheus.prototype.directionNavigation = function() {
	var _this = this;

	this.settings.directionNavigationNext.unbind('click.slide').bind('click.slide', function() {
		_this.slide(1, true);
	});
	this.settings.directionNavigationPrev.unbind('click.slide').bind('click.slide', function() {
		_this.slide(-1, true);
	});
};

Prometheus.prototype.stopOnHover = function() {
	var _this = this;

	this.$slider.unbind('mouseenter.lockSlide').bind('mouseenter.lockSlide', function() {
		_this.sliderLocked = true;
	});
	this.$slider.unbind('mouseleave.lockSlide').bind('mouseleave.lockSlide', function() {
		_this.sliderLocked = false;
	});
};

Prometheus.prototype.loadNecessaryPlugins = function() {
	var i = 0;
	while (i < this.modifiers.length) {
		var modifier = this.modifiers[i];

		if (modifier.indexOf(this.settings) && (this.settings[modifier] === true)) this[modifier]();
		i++;
	}
};

Prometheus.prototype.slide = function(direction, forceSlide) {
	var _this = this;

	if (!this.sliderLocked || forceSlide) {
		var nextPos = this.getNextIndex(direction),
			$currentSlide = this.$slides.eq(this.currentPos),
			$nextSlide = this.$slides.eq(nextPos);

		if(this.randomizeTransition) {
			this.$slider.find('ul').removeClass().addClass(this.transitions[Math.floor((Math.random()*3))]);
		}
		$currentSlide.transition('out',this.settings.animation,this.settings.activeClass,this.images[this.currentPos]);
		$nextSlide.transition('in',this.settings.animation,this.settings.activeClass);

		this.currentPos = nextPos;
	}

	if (this.settings.autoSlide) {
		if (this.timer) clearTimeout(this.timer);
	    this.timer = setTimeout(function(){ _this.slide(1); }, this.settings.duration);
	}
};

Prometheus.prototype.getNextIndex = function(to) {
	if ((this.currentPos + to) >= this.totalImages) return 0;
	else if ((this.currentPos + to) < 0) return this.totalImages - 1;
	else return this.currentPos + to;
};

Prometheus.prototype.defaults = {
	activeClass : 'active',
	slidesSelector : "li",
	autoSlide : true,
	startDuration : 0,
	duration : 3000,
	animation : {
		type : 'fade',
	    cols : 7,
	    rows : 2,
	    random : true,
		speed : 2000
	},
	stopOnHover : false,
	directionNavigation : false,
	directionNavigationPrev : '',
	directionNavigationNext : '',
	controlNavigation : false
};

Prometheus.prototype.modifiers = [
	'stopOnHover',
	'directionNavigation',
	'controlNavigation'
];

Prometheus.prototype.transitions = [
	'fade',
	'slide',
	'blocks',
	'blocksZoom'
];

$.fn.Prometheus = function(options) {
	return $(this).each(
		function() {
			new Prometheus($(this),options)
		}
	);
};<commit_msg>Random animation handling fix<commit_after>/**
 * Helper function for cross-browser CSS3 support, prepends all possible prefixes to all properties passed in
 * @param {Object} props Key/value pairs of CSS3 properties
 */
$.fn.css3 = function(props) {
	var css = {};
	var prefixes = ['webkit', 'moz', 'ms', 'o'];

	for(var prop in props)
	{
		for(var i=0; i<prefixes.length; i++) {
			css['-' + prefixes[i] + '-' + prop] = props[prop];
		}
		css[prop] = props[prop];
	}

	this.css(css);
	return this;
};

/**
 * This function provides the tile creation and the transition handling as the name says
 * @param {string} way The way of the transition (= in/out)
 * @param {Object} settings The animation perferences given by this.settings.animation
 * @param {string} activeClass The activeClass from this.settings
 * @param {string} image The url of the image get by jQuery like "url(http://domain.com/assets/slide1.jpg)"
 */
$.fn.transition = function(way,settings,activeClass,image) {

	var $container = $(this),
		width = $container.width(),
		height = $container.height(),
		$img = $container.find('.tiles'),
		initialized = !!($img.find('.tile').length > 0),
		imageBackground = (initialized ? image : $img.css('background-image')),
		n_tiles = settings.cols * settings.rows,
		tiles = [],
		$tiles,
		range = function ( min, max, rand ) {
			var arr = ( new Array( ++max - min ) )
				.join('.').split('.')
				.map(function( v,i ){ return min + i });
			return rand
				? arr.map(function( v ) { return [ Math.random(), v ] })
					.sort().map(function( v ) { return v[ 1 ] })
				: arr;
		},
		createTilesByImage = function() {
			for (var i = 0; i < n_tiles; i++) {
				tiles.push('

');
			}
			$tiles = $(tiles.join(''));
			$img.append($tiles);
			$tiles.css({
				width: width / settings.cols,
				height: height / settings.rows,
				backgroundImage: imageBackground
			}).css3({
				transition : 'none'
			});
			$tiles.each(function() {
				var pos = $(this).position();
				$(this).css('backgroundPosition', -pos.left + 'px ' + -pos.top + 'px').css3({
					'transition-property' : 'all',
					'transition-duration' : '.3s',
					'transition-timing-function' : 'cubic-bezier(0.445, 0.050, 0.550, 0.950)'
				});
			});
		},
		doAnimationWithTheCreatedTiles = function() {
			var tilesArr = range(0, n_tiles, settings.random),
				tileSpeed = settings.speed / n_tiles,
				animateTilesWithDelay = function() {
					tilesArr.forEach(function(tile, i) {
						$tiles.eq(tile).css3({'transition-delay':((i*tileSpeed)/1000)+'s'});
					});
				};

			switch (way) {
				case 'out':
					$tiles.css3({transition:'none'});
					$container.removeClass(activeClass);
					$img.css('background-image',imageBackground);
				break;
				case 'in':
					$img.css('background-image','');
					$tiles.css3({
						'transition-property' : 'all',
						'transition-duration' : '.3s',
						'transition-timing-function' : 'cubic-bezier(0.445, 0.050, 0.550, 0.950)'
					});
					animateTilesWithDelay();
					$container.addClass(activeClass);
					break;
			}
		},
		checkInitialization = function() {
			if (initialized) {
				$tiles = $img.find('.tile');
			} else createTilesByImage();
		};

	checkInitialization();
	doAnimationWithTheCreatedTiles();
};

Prometheus = function($slider, options) {
	var _this = this;

	this.$slider = $slider;
	this.settings = (options != undefined) ? $.extend({}, this.defaults, options) : this.defaults;
	this.settings.animation = (options.animation != undefined) ? $.extend({}, this.defaults.animation, options.animation) : this.defaults.animation;
	this.$slides = this.$slider.find(this.settings.slidesSelector);
	this.sliderLocked = false;
	this.timer = undefined;
    this.totalImages = (this.$slides.length);
    this.currentPos = 0;
	this.randomizeTransition = false;

	if (this.$slider.data('initialized')) return;
	this.$slider.data('initialized', true);

	this.initializeRandomTransitions();

	this.loadNecessaryPlugins();
	this.turnImagesIntoContainers();
	this.timer = setTimeout(function(){ _this.slide(1); }, this.settings.startDuration);
};

Prometheus.prototype.initializeRandomTransitions = function() {
	if (this.settings.animation.type === 'random') {
		this.$slider.find('ul').addClass(this.transitions[Math.floor(Math.random() * (this.transitions.length - 1))]);
		this.randomizeTransition = true;
	}
};

Prometheus.prototype.turnImagesIntoContainers = function() {
	var _this = this;

	this.images = [];

	this.$slides.each(function() {
		var $this = $(this),
			$img = $this.find('img'),
			$tiles = $('

');

		$img.hide();
		$this.append($tiles);
		$tiles.css('background-image', 'url(' + $img.attr('src') + ')');
		_this.images.push($tiles.css('background-image'));
		$img.remove();
	});
};

Prometheus.prototype.controlNavigation = function() {
	var _this = this;

	_this.$slider.append('

');
	for(var i=0;i<_this.totalImages;i++)
		$('#pagination').append('

');

	Prometheus.prototype.slide = function(direction, forceSlide) {
		var _this = this;

		if (!_this.sliderLocked || forceSlide) {
			var nextPos = _this.getNextIndex(direction),
				$paginationPage = $('#pagination').find('li');

			_this.$slides.eq(_this.currentPos).removeClass(_this.settings.activeClass);
			_this.$slides.eq(nextPos).addClass(_this.settings.activeClass);

			$paginationPage.eq(_this.currentPos).removeClass(_this.settings.activeClass);
			$paginationPage.eq(nextPos).addClass(_this.settings.activeClass);

			_this.currentPos = nextPos;
		}

		if (_this.timer) clearTimeout(_this.timer);
	    _this.timer = setTimeout(function(){ _this.slide(1); }, _this.settings.duration);
	};
};

Prometheus.prototype.directionNavigation = function() {
	var _this = this;

	this.settings.directionNavigationNext.unbind('click.slide').bind('click.slide', function() {
		_this.slide(1, true);
	});
	this.settings.directionNavigationPrev.unbind('click.slide').bind('click.slide', function() {
		_this.slide(-1, true);
	});
};

Prometheus.prototype.stopOnHover = function() {
	var _this = this;

	this.$slider.unbind('mouseenter.lockSlide').bind('mouseenter.lockSlide', function() {
		_this.sliderLocked = true;
	});
	this.$slider.unbind('mouseleave.lockSlide').bind('mouseleave.lockSlide', function() {
		_this.sliderLocked = false;
	});
};

Prometheus.prototype.loadNecessaryPlugins = function() {
	var i = 0;
	while (i < this.modifiers.length) {
		var modifier = this.modifiers[i];

		if (modifier.indexOf(this.settings) && (this.settings[modifier] === true)) this[modifier]();
		i++;
	}
};

Prometheus.prototype.slide = function(direction, forceSlide) {
	var _this = this;

	if (!this.sliderLocked || forceSlide) {
		var nextPos = this.getNextIndex(direction),
			$currentSlide = this.$slides.eq(this.currentPos),
			$nextSlide = this.$slides.eq(nextPos);

		if(this.randomizeTransition) {
			this.$slider.find('ul').removeClass().addClass(this.transitions[Math.floor(Math.random() * (this.transitions.length - 1))]);
		}
		$currentSlide.transition('out',this.settings.animation,this.settings.activeClass,this.images[this.currentPos]);
		$nextSlide.transition('in',this.settings.animation,this.settings.activeClass);

		this.currentPos = nextPos;
	}

	if (this.settings.autoSlide) {
		if (this.timer) clearTimeout(this.timer);
	    this.timer = setTimeout(function(){ _this.slide(1); }, this.settings.duration);
	}
};

Prometheus.prototype.getNextIndex = function(to) {
	if ((this.currentPos + to) >= this.totalImages) return 0;
	else if ((this.currentPos + to) < 0) return this.totalImages - 1;
	else return this.currentPos + to;
};

Prometheus.prototype.defaults = {
	activeClass : 'active',
	slidesSelector : "li",
	autoSlide : true,
	startDuration : 0,
	duration : 3000,
	animation : {
		type : 'fade',
	    cols : 7,
	    rows : 2,
	    random : true,
		speed : 2000
	},
	stopOnHover : false,
	directionNavigation : false,
	directionNavigationPrev : '',
	directionNavigationNext : '',
	controlNavigation : false
};

Prometheus.prototype.modifiers = [
	'stopOnHover',
	'directionNavigation',
	'controlNavigation'
];

Prometheus.prototype.transitions = [
	'fade',
	'slide',
	'blocks',
	'blocksZoom'
];

$.fn.Prometheus = function(options) {
	return $(this).each(
		function() {
			new Prometheus($(this),options)
		}
	);
};<|endoftext|>


<commit_before>module("Charge from customer selector", {
  setup: resetAll,
  tearDown: resetAll
});

test("lists week days for 18.4.2009", function() {
  $("#continuousCalendar").continuousCalendar(18, 4, 2009);
  equals($("#continuousCalendar").html(), [
    "
",
    "
",
    "
",
    "13	",
    "14	",
    "15	",
    "16	",
    "17	",
    "18	",
    "19	",
    "
",
    "
",
    "
"
  ].join("\n"));
});

function resetAll() {}<commit_msg>works also with changhing months<commit_after>module("Charge from customer selector", {
  setup: resetAll,
  tearDown: resetAll
});

test("lists week days for vappu 2009", function() {
  $("#continuousCalendar").continuousCalendar(30, 4, 2009);
  equals($("#continuousCalendar").html(), [
    "
",
    "
",
    "
",
    "27	",
    "28	",
    "29	",
    "30	",
    "1	",
    "2	",
    "3	",
    "
",
    "
",
    "
"
  ].join("\n"));
});

function resetAll() {}<|endoftext|>


<commit_before>'use strict';

var async = require("async");
var RequestPromise = require("request-promise");
var Promise = require("bluebird");
var _ = require('lodash');

var regionModel = require('./../models/universe/regionModel');
var constellationModel = require('./../models/universe/constellationModel');
var systemModel = require('./../models/universe/systemModel');

var eveRegionCrestEndpoint = 'https://crest-tq.eveonline.com/regions/';
var eveConstellationCrestEndpoint = 'https://crest-tq.eveonline.com/constellations/';
var eveSystemsCrestEndpoint = 'https://crest-tq.eveonline.com/systems/';

/*
* 1) Call Regions API "https://crest-tq.eveonline.com/regions/{RegionId}
* ---- a) Create Region in DB
* --------i)Iterate Through Constellations
* -----------ii)Create Constellations
* --------------iii)Iterate Through Systems
* -------------------iiii) Create System
*
* */

exports.updateUniverseData = getAllRegions;

function getAllRegions(req, res){
    getAllRegionsHrefs().then(function (hrefs){
        var chunks = _.chunk(hrefs, 20);
        return Promise.map(chunks, function(chunk) {
            // tune the delay to what you need it to be
            // it will wait the delay (in ms) before starting the next chunk of requests
            return Promise.map(chunk, getRegion).then(function (getRegionResults){
                console.log('----------------~~~~~~~~~~~~~~~~~~-------------------~~~~~~~~~~~~~~');
                for(var item in getRegionResults){
                    Promise.map(getRegionResults[item].constellations, getConstellationInfo);
                }
            }).delay(200);
        });
    });
}

function getConstellationInfo(whatsComingIN) {
    var options = {
        uri: whatsComingIN.href,
        json: true
    };
    return RequestPromise(options).then(function (responseItem){
        var arrayOfSystems = [];
        for(var system in responseItem.systems){
            var newSystem = new systemModel({
                _id: responseItem.systems[system].id,
                href: responseItem.systems[system].href
            });
            newSystem.save();
            arrayOfSystems.push(newSystem);
        }
        constellationModel.findOne({ _id: whatsComingIN._id }, function (err, doc){
            doc.name = responseItem.name;
            doc.solarSystems = arrayOfSystems;
            doc.save();
        });
    });
}

function getRegion(href) {
    var options = {
        uri: href,
        json: true
    };
    return RequestPromise(options).then(function (responseItem){
        var constellationObjects = [];
        for(var item in responseItem.constellations){
            var newConstellation = constellationModel({
                _id: responseItem.constellations[item].id,
                href: eveConstellationCrestEndpoint + responseItem.constellations[item].id+'/'
            });
            newConstellation.save();
            constellationObjects.push(newConstellation);
        }
        var newRegion = regionModel({
            _id: responseItem.id,
            name: responseItem.name,
            description: responseItem.description,
            href: eveRegionCrestEndpoint + responseItem.id+'/',
            constellations: constellationObjects
        });
        newRegion.save();
        return newRegion;
    });
}

function getAllRegionsHrefs(){
    var options = {
        uri: eveRegionCrestEndpoint,
        json: true
    };
   return RequestPromise(options).then(function (responseItems){
        var regionHrefs = [];
        for(var item in responseItems.items){
                regionHrefs.push(responseItems.items[item].href);
        }
        return regionHrefs;
    });
}

<commit_msg>holy moly this is a mess<commit_after>'use strict';

var async = require("async");
var RequestPromise = require("request-promise");
var Promise = require("bluebird");
var _ = require('lodash');

var regionModel = require('./../models/universe/regionModel');
var constellationModel = require('./../models/universe/constellationModel');
var systemModel = require('./../models/universe/systemModel');

var eveRegionCrestEndpoint = 'https://crest-tq.eveonline.com/regions/';
var eveConstellationCrestEndpoint = 'https://crest-tq.eveonline.com/constellations/';
var eveSystemsCrestEndpoint = 'https://crest-tq.eveonline.com/systems/';

/*
* 1) Call Regions API "https://crest-tq.eveonline.com/regions/{RegionId}
* ---- a) Create Region in DB
* --------i)Iterate Through Constellations
* -----------ii)Create Constellations
* --------------iii)Iterate Through Systems
* -------------------iiii) Create System
*
* */

exports.updateUniverseData = getAllRegions;

function getAllRegions(req, res){
    getAllRegionsHrefs().then(function (hrefs){
        var chunks = _.chunk(hrefs, 25);
        return Promise.map(chunks, function(chunk) {
            return Promise.map(chunk, getRegion).then(function (getRegionResults){
                for(var item in getRegionResults) {
                    Promise.map(getRegionResults[item].constellations, getConstellationInfo).then(function (constellationInfo) {
                        var chunks = _.chunk(constellationInfo, 150);
                        return Promise.map(chunks, function (chunk) {
                            return Promise.map(chunk, getSystem).delay(20000);
                        })
                    }).delay(20000);
                }
            }).delay(200000);
        });
    });
}

function getSystem(systems){
    for(var updateSystem in systems){
        var options = {
            uri: systems[updateSystem].href,
            json: true
        };
         RequestPromise(options).then(function (responseItem){
             //Grab the system in the db and update it with its info
            systemModel.findOne({ _id: systems[updateSystem]._id }, function (err, doc){
                doc.name = responseItem.name;
                doc.save();
            });

        });
    }
}

function getConstellationInfo(constellation) {
    var options = {
        uri: constellation.href,
        json: true
    };
    return RequestPromise(options).then(function (responseItem){
        var arrayOfSystems = [];
        for(var system in responseItem.systems){
            var newSystem = new systemModel({
                _id: responseItem.systems[system].id,
                href: responseItem.systems[system].href
            });
            newSystem.save();
            arrayOfSystems.push(newSystem);
        }
        //find the constellation and update it with its info
        constellationModel.findOne({ _id: constellation._id }, function (err, doc){
            doc.name = responseItem.name;
            doc.solarSystems = arrayOfSystems;
            doc.save();
        });
        return arrayOfSystems;
    });
}

function getRegion(href) {
    var options = {
        uri: href,
        json: true
    };
    return RequestPromise(options).then(function (responseItem){
        var constellationObjects = [];
        for(var item in responseItem.constellations){
            var newConstellation = constellationModel({
                _id: responseItem.constellations[item].id,
                href: eveConstellationCrestEndpoint + responseItem.constellations[item].id+'/'
            });
            newConstellation.save();
            constellationObjects.push(newConstellation);
        }
        var newRegion = regionModel({
            _id: responseItem.id,
            name: responseItem.name,
            description: responseItem.description,
            href: eveRegionCrestEndpoint + responseItem.id+'/',
            constellations: constellationObjects
        });
        newRegion.save();
        return newRegion;
    });
}

function getAllRegionsHrefs(){
    var options = {
        uri: eveRegionCrestEndpoint,
        json: true
    };
   return RequestPromise(options).then(function (responseItems){
        var regionHrefs = [];
        for(var item in responseItems.items){
                regionHrefs.push(responseItems.items[item].href);
        }
        return regionHrefs;
    });
}

<|endoftext|>


<commit_before>import * as css from '../services/css';
import { Fastdom } from '../services/fastdom';

class Entry {
	constructor(element, sheets, markDirty) {
		this.element = element;
		this.list = new Set();
		this.sheets = sheets;
		this.markDirty = markDirty;
	}

	class(key, style) {
		key = css.escape(key);
		this.list.add(key);
		this.markDirty(this);
		if (style) {
			const sheets = this.sheets;
			if (!sheets.has(key)) {
				sheets.set(key, style);
			}
		}
	}
}

export class Monitor {
	constructor(model) {
		this.model = model;
		this.entries = new Set();
		this.newSheets = new Map();
		this.oldSheets = new Map();
	}

	enter() {
		const newSheets = this.newSheets;
		let entries = this.entries;
		for (let entry of entries) {
			for (let cls of entry.list) {
				entry.element.removeClass(cls, true);
			}
		}

		entries = this.entries = new Set();
		const markDirty = entry => entries.add(entry);

		return element => {
			const entry = new Entry(element, newSheets, markDirty);
			return entry.class.bind(entry);
		};
	}

	exit() {
		const entries = this.entries;
		for (let entry of entries) {
			for (let cls of entry.list) {
				entry.element.addClass(cls, true);
			}
		}

		const newSheets = this.newSheets;
		const oldSheets = this.oldSheets;
		const id = this.model.grid().id;
		for (let cls of oldSheets.keys()) {
			if (!newSheets.has(cls)) {
				const sheet = css.sheet(id, cls);
				Fastdom.mutate(() => sheet.remove());
			}
		}

		for (let [cls, style] of newSheets.entries()) {
			if (!oldSheets.has(cls)) {
				const sheet = css.sheet(id, cls);
				Fastdom.mutate(() => sheet.set({ [`.${cls}`]: style }));
			}
		}

		this.oldSheets = newSheets;
		this.newSheets = new Map();
	}
}<commit_msg>revert style.monitor<commit_after>import * as css from '../services/css';

class Entry {
	constructor(element, sheets, markDirty) {
		this.element = element;
		this.list = new Set();
		this.sheets = sheets;
		this.markDirty = markDirty;
	}

	class(key, style) {
		key = css.escape(key);
		this.list.add(key);
		this.markDirty(this);
		if (style) {
			const sheets = this.sheets;
			if (!sheets.has(key)) {
				sheets.set(key, style);
			}
		}
	}
}

export class Monitor {
	constructor(model) {
		this.model = model;
		this.entries = new Set();
		this.newSheets = new Map();
		this.oldSheets = new Map();
	}

	enter() {
		const newSheets = this.newSheets;
		let entries = this.entries;
		for (let entry of entries) {
			for (let cls of entry.list) {
				entry.element.removeClass(cls, true);
			}
		}

		entries = this.entries = new Set();
		const markDirty = entry => entries.add(entry);

		return element => {
			const entry = new Entry(element, newSheets, markDirty);
			return entry.class.bind(entry);
		};
	}

	exit() {
		const entries = this.entries;
		for (let entry of entries) {
			for (let cls of entry.list) {
				entry.element.addClass(cls, true);
			}
		}

		const newSheets = this.newSheets;
		const oldSheets = this.oldSheets;
		const id = this.model.grid().id;
		for (let cls of oldSheets.keys()) {
			if (!newSheets.has(cls)) {
				const sheet = css.sheet(id, cls);
				sheet.remove();
			}
		}

		for (let [cls, style] of newSheets.entries()) {
			if (!oldSheets.has(cls)) {
				const sheet = css.sheet(id, cls);
				sheet.set({ [`.${cls}`]: style });
			}
		}

		this.oldSheets = newSheets;
		this.newSheets = new Map();
	}
}<|endoftext|>


<commit_before>(function () {
  const version = 'v1';
  const cacheName = ':fourjuaneight:';
  const staticCacheName = version + cacheName + 'static';
  const pagesCacheName = cacheName + 'pages';
  const imagesCacheName = cacheName + 'images';
  const staticAssets = [
    '/',
    '/articles/',
    '/bookshelf/',
    '/bookmarks/',
    '/microposts/',
    '/offline/',
    '/css/main.min.css',
    '/css/single.min.css',
    '/fonts/Rubik.woff',
    '/fonts/Rubik.woff2',
    '/fonts/Rubik-Italic.woff',
    '/fonts/Rubik-Italic.woff2',
    '/fonts/Rubik-Bold.woff',
    '/fonts/Rubik-Bold.woff2',
    '/fonts/OpenDyslexic.woff',
    '/fonts/OpenDyslexic.woff2',
    '/fonts/OpenDyslexic-Bold.woff',
    '/fonts/OpenDyslexic-Bold.woff2',
    '/fonts/PlexMono.woff',
    '/fonts/PlexMono.woff2',
    '/fonts/PlexMono-Italic.woff',
    '/fonts/PlexMono-Italic.woff2',
    '/js/barefoot.min.js',
    '/js/lazyloading.min.js'
  ];
  function updateStaticCache() {
    // These items must be cached for the Service Worker to complete installation
    return caches.open(staticCacheName)
      .then(cache => {
        return cache.addAll(staticAssets.map(url => new Request(url, { credentials: 'include' })));
      });
  }
  function stashInCache(cacheName, request, response) {
    caches.open(cacheName)
      .then(cache => cache.put(request, response));
  }
  // Limit the number of items in a specified cache.
  function trimCache(cacheName, maxItems) {
    caches.open(cacheName)
      .then(cache => {
        cache.keys()
          .then(keys => {
            if (keys.length > maxItems) {
              cache.delete(keys[0])
                .then(trimCache(cacheName, maxItems));
            }
          });
      });
  }
  // Remove caches whose name is no longer valid
  function clearOldCaches() {
    return caches.keys()
      .then(keys => {
        return Promise.all(keys
          .filter(key => key.indexOf(version) !== 0)
          .map(key => caches.delete(key))
        );
      });
  }
  // Events!
  self.addEventListener('message', event => {
    if (event.data.command === 'trimCaches') {
      trimCache(pagesCacheName, 35);
      trimCache(imagesCacheName, 20);
    }
  });
  self.addEventListener('install', event => {
    event.waitUntil(updateStaticCache()
      .then(() => self.skipWaiting())
    );
  });
  self.addEventListener('activate', event => {
    event.waitUntil(clearOldCaches()
      .then(() => self.clients.claim())
    );
  });
  self.addEventListener('fetch', event => {
    const request = event.request;
    const url = new URL(request.url);
    if (url.href.indexOf('https://www.fourjuaneight.com') !== 0) return;
    if (request.method !== 'GET') return;
    if (url.href.indexOf('?') !== -1) return;
    if (request.headers.get('Accept').includes('text/html')) {
      event.respondWith(
        fetch(request)
          .then(response => {
            let copy = response.clone();
            if (staticAssets.includes(url.pathname) || staticAssets.includes(url.pathname + '/')) {
              stashInCache(staticCacheName, request, copy);
            } else {
              stashInCache(pagesCacheName, request, copy);
            }
            return response;
          })
          .catch(() => {
            // CACHE or FALLBACK
            return caches.match(request)
              .then(response => response || caches.match('/offline/'));
          })
      );
      return;
    }
    event.respondWith(
      fetch(request)
        .then(response => {
          if (request.headers.get('Accept').includes('image')) {
            let copy = response.clone();
            stashInCache(imagesCacheName, request, copy);
          }
          return response;
        })
        .catch(() => {
          return caches.match(request)
            .then(response => response)
            .catch(console.error)
        })
    );
  });
})();<commit_msg>Added logo image to SW.<commit_after>(function () {
  const version = 'v2';
  const cacheName = ':fourjuaneight:';
  const staticCacheName = version + cacheName + 'static';
  const pagesCacheName = cacheName + 'pages';
  const imagesCacheName = cacheName + 'images';
  const staticAssets = [
    '/',
    '/articles/',
    '/bookshelf/',
    '/bookmarks/',
    '/microposts/',
    '/offline/',
    '/icons/logo.jpg',
    '/css/main.min.css',
    '/css/single.min.css',
    '/fonts/Rubik.woff',
    '/fonts/Rubik.woff2',
    '/fonts/Rubik-Italic.woff',
    '/fonts/Rubik-Italic.woff2',
    '/fonts/Rubik-Bold.woff',
    '/fonts/Rubik-Bold.woff2',
    '/fonts/OpenDyslexic.woff',
    '/fonts/OpenDyslexic.woff2',
    '/fonts/OpenDyslexic-Bold.woff',
    '/fonts/OpenDyslexic-Bold.woff2',
    '/fonts/PlexMono.woff',
    '/fonts/PlexMono.woff2',
    '/fonts/PlexMono-Italic.woff',
    '/fonts/PlexMono-Italic.woff2',
    '/js/barefoot.min.js',
    '/js/lazyloading.min.js'
  ];
  function updateStaticCache() {
    // These items must be cached for the Service Worker to complete installation
    return caches.open(staticCacheName)
      .then(cache => {
        return cache.addAll(staticAssets.map(url => new Request(url, { credentials: 'include' })));
      });
  }
  function stashInCache(cacheName, request, response) {
    caches.open(cacheName)
      .then(cache => cache.put(request, response));
  }
  // Limit the number of items in a specified cache.
  function trimCache(cacheName, maxItems) {
    caches.open(cacheName)
      .then(cache => {
        cache.keys()
          .then(keys => {
            if (keys.length > maxItems) {
              cache.delete(keys[0])
                .then(trimCache(cacheName, maxItems));
            }
          });
      });
  }
  // Remove caches whose name is no longer valid
  function clearOldCaches() {
    return caches.keys()
      .then(keys => {
        return Promise.all(keys
          .filter(key => key.indexOf(version) !== 0)
          .map(key => caches.delete(key))
        );
      });
  }
  // Events!
  self.addEventListener('message', event => {
    if (event.data.command === 'trimCaches') {
      trimCache(pagesCacheName, 35);
      trimCache(imagesCacheName, 20);
    }
  });
  self.addEventListener('install', event => {
    event.waitUntil(updateStaticCache()
      .then(() => self.skipWaiting())
    );
  });
  self.addEventListener('activate', event => {
    event.waitUntil(clearOldCaches()
      .then(() => self.clients.claim())
    );
  });
  self.addEventListener('fetch', event => {
    const request = event.request;
    const url = new URL(request.url);
    if (url.href.indexOf('https://www.fourjuaneight.com') !== 0) return;
    if (request.method !== 'GET') return;
    if (url.href.indexOf('?') !== -1) return;
    if (request.headers.get('Accept').includes('text/html')) {
      event.respondWith(
        fetch(request)
          .then(response => {
            let copy = response.clone();
            if (staticAssets.includes(url.pathname) || staticAssets.includes(url.pathname + '/')) {
              stashInCache(staticCacheName, request, copy);
            } else {
              stashInCache(pagesCacheName, request, copy);
            }
            return response;
          })
          .catch(() => {
            // CACHE or FALLBACK
            return caches.match(request)
              .then(response => response || caches.match('/offline/'));
          })
      );
      return;
    }
    event.respondWith(
      fetch(request)
        .then(response => {
          if (request.headers.get('Accept').includes('image')) {
            let copy = response.clone();
            stashInCache(imagesCacheName, request, copy);
          }
          return response;
        })
        .catch(() => {
          return caches.match(request)
            .then(response => response)
            .catch(console.error)
        })
    );
  });
})();<|endoftext|>