<commit_before>import React from 'react';
import PartyMatchComparison from './PartyMatchComparison';
import { Link } from 'react-router-dom';
import { voteValueDescription } from '../../utils';

function clickHandler(castVote, issueId, value) {
  return function (e) {
    castVote(issueId, value);
    e.preventDefault();
  };
}

export default function MotionDetails({_case, castVote, nextCase, partyAgreements, userVote}) {
  var opaqueBackground = {
      backgroundColor: 'rgba(255, 255, 255, 0.6)'
    };
  let nextLink = null;
  let comparison = null;
  let voteButtons = null;

  const divStyle = {
    backgroundImage: `url(/img/motions/${_case.issue_id}.jpg)`
  };

  let userVoteView = null;
  if (userVote !== undefined) {
    userVoteView =

{ voteValueDescription(userVote)}

;
  }
  else {
    voteButtons = (

                EI

                TYHJÄ

                JAA

      );
  }
  if (nextCase !== _case.issue_id) {
    nextLink = (

          <Link to={`/motion/${nextCase}`} className="f4 no-underline bg-animate hover-bg-light-green inline-flex items-center justify-center pa3 ba br-pill bw2 w4 mr1 dark-green">
            Seuraava
          </Link>

);
    comparison = <PartyMatchComparison agreements={partyAgreements}/>;
  }
  return (

{_case.user_question}

                { userVoteView }

            { voteButtons }
            { nextLink }
            { comparison }

);
}
<commit_msg>Hide party vote agreements on tyhjä.<commit_after>import React from 'react';
import PartyMatchComparison from './PartyMatchComparison';
import { Link } from 'react-router-dom';
import { voteValueDescription } from '../../utils';

function clickHandler(castVote, issueId, value) {
  return function (e) {
    castVote(issueId, value);
    e.preventDefault();
  };
}

export default function MotionDetails({_case, castVote, nextCase, partyAgreements, userVote}) {
  var opaqueBackground = {
      backgroundColor: 'rgba(255, 255, 255, 0.6)'
    };
  let nextLink = null;
  let comparison = null;
  let voteButtons = null;

  const divStyle = {
    backgroundImage: `url(/img/motions/${_case.issue_id}.jpg)`
  };

  let userVoteView = null;
  if (userVote !== undefined) {
    userVoteView =

{ voteValueDescription(userVote)}

;
  }
  else {
    voteButtons = (

                EI

                TYHJÄ

                JAA

      );
  }
  if (nextCase !== _case.issue_id) {
    nextLink = (

          <Link to={`/motion/${nextCase}`} className="f4 no-underline bg-animate hover-bg-light-green inline-flex items-center justify-center pa3 ba br-pill bw2 w4 mr1 dark-green">
            Seuraava
          </Link>

);
    if (userVote !== 0) {
      comparison = <PartyMatchComparison agreements={partyAgreements}/>;
    }
  }
  return (

{_case.user_question}

                { userVoteView }

            { voteButtons }
            { nextLink }
            { comparison }

);
}
<|endoftext|>


<commit_before>import pkg from "../../../package.json"
import pjt from "../../../project.json"

export default () => {
  const contents = [
    {
      title: "Author",
      text: "Qrac",
      url: "https://twitter.com/" + pjt.site.twitter_id
    },
    {
      title: "GitHub",
      text: "Repository",
      url: pkg.repository
    },
    {
      text: "Releases",
      url: pkg.repository + "/releases"
    }
  ]
  return (

          Licence: {pkg.license}

      {contents.map((content, i) => (

            {content.title ? (
              {content.title}:
            ) : (
              ""
            )}

              {content.text}
              <svg
                className="icon is-external-link is-opacity-xs"
                xmlns="http://www.w3.org/2000/svg"
                aria-hidden="true"
                viewBox="0 0 100 100"
              >
                <path d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path>
                <polygon points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon>
              </svg>

      ))}

  )
}
<commit_msg>Fix demo footer<commit_after>import pkg from "../../../package.json"
import pjt from "../../../project.json"

export default () => {
  const contents = [
    {
      title: "Licence",
      text: pkg.license
    },
    {
      title: "Author",
      text: "Qrac",
      url: "https://twitter.com/" + pjt.site.twitter_id
    },
    {
      title: "GitHub",
      text: "Repository",
      url: pkg.repository
    },
    {
      text: "Releases",
      url: pkg.repository + "/releases"
    }
  ]
  return (

      {contents.map((content, i) => (

            {content.title ? (
              {content.title}:
            ) : (
              ""
            )}
            {content.url ? (

                {content.text}
                <svg
                  className="icon is-external-link is-opacity-xs"
                  xmlns="http://www.w3.org/2000/svg"
                  aria-hidden="true"
                  viewBox="0 0 100 100"
                >
                  <path d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path>
                  <polygon points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon>
                </svg>

            ) : (
              {content.text}
            )}

      ))}

  )
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Card slider implementation. Allows you to create interactions
 * that have items that can slide left to right to reveal additional items.
 * Works by adding the necessary event handlers to a specific DOM structure
 * including a frame, container and cards.
 * - The frame defines the boundary of one item. Each card will be expanded to
 *   fill the width of the frame. This element is also overflow hidden so that
 *   the additional items left / right do not trigger horizontal scrolling.
 * - The container is what all the touch events are attached to. This element
 *   will be expanded to be the width of all cards.
 * - The cards are the individual viewable items. There should be one card for
 *   each item in the list. Only one card will be visible at a time. Two cards
 *   will be visible while you are transitioning between cards.
 *
 * This class is designed to work well on any hardware-accelerated touch device.
 * It should still work on pre-hardware accelerated devices it just won't feel
 * very good. It should also work well with a mouse.
 */

// Use an anonymous function to enable strict mode just for this file (which
// will be concatenated with other files when embedded in Chrome
var CardSlider = (function() {
  'use strict';

  /**
   * @constructor
   * @param {!Element} frame The bounding rectangle that cards are visible in.
   * @param {!Element} container The surrounding element that will have event
   *     listeners attached to it.
   * @param {number} cardWidth The width of each card should have.
   */
  function CardSlider(frame, container, cardWidth) {
    /**
     * @type {!Element}
     * @private
     */
    this.frame_ = frame;

    /**
     * @type {!Element}
     * @private
     */
    this.container_ = container;

    /**
     * Array of card elements.
     * @type {!Array.<!Element>}
     * @private
     */
    this.cards_ = [];

    /**
     * Index of currently shown card.
     * @type {number}
     * @private
     */
    this.currentCard_ = 0;

    /**
     * @type {number}
     * @private
     */
    this.cardWidth_ = cardWidth;

    /**
     * @type {!TouchHandler}
     * @private
     */
    this.touchHandler_ = new TouchHandler(this.container_);

  }

  /**
   * Events fired by the slider.
   * Events are fired at the container.
   */
  CardSlider.EventType = {
    // Fired when the user slides to another card.
    CARD_CHANGED: 'cardSlider:card_changed'
  };

  /**
   * The time to transition between cards when animating. Measured in ms.
   * @type {number}
   * @private
   * @const
   */
  CardSlider.TRANSITION_TIME_ = 200;

  /**
   * The minimum velocity required to transition cards if they did not drag past
   * the halfway point between cards. Measured in pixels / ms.
   * @type {number}
   * @private
   * @const
   */
  CardSlider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2;

  CardSlider.prototype = {
    /**
     * The current left offset of the container relative to the frame.
     * @type {number}
     * @private
     */
    currentLeft_: 0,

    /**
     * Initialize all elements and event handlers. Must call after construction
     * and before usage.
     */
    initialize: function() {
      var view = this.container_.ownerDocument.defaultView;
      assert(view.getComputedStyle(this.container_).display == '-webkit-box',
          'Container should be display -webkit-box.');
      assert(view.getComputedStyle(this.frame_).overflow == 'hidden',
          'Frame should be overflow hidden.');
      assert(view.getComputedStyle(this.container_).position == 'static',
          'Container should be position static.');

      this.updateCardWidths_();

      this.mouseWheelScrollAmount_ = 0;
      this.scrollClearTimeout_ = null;
      this.frame_.addEventListener('mousewheel',
                                   this.onMouseWheel_.bind(this));

      if (document.documentElement.getAttribute('touchui')) {
        this.container_.addEventListener(TouchHandler.EventType.TOUCH_START,
                                         this.onTouchStart_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_START,
                                         this.onDragStart_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE,
                                         this.onDragMove_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_END,
                                         this.onDragEnd_.bind(this));

        this.touchHandler_.enable(/* opt_capture */ false);
      }
    },

    /**
     * Use in cases where the width of the frame has changed in order to update
     * the width of cards. For example should be used when orientation changes
     * in full width sliders.
     * @param {number} newCardWidth Width all cards should have, in pixels.
     */
    resize: function(newCardWidth) {
      if (newCardWidth != this.cardWidth_) {
        this.cardWidth_ = newCardWidth;

        this.updateCardWidths_();

        // Must upate the transform on the container to show the correct card.
        this.transformToCurrentCard_();
      }
    },

    /**
     * Sets the cards used. Can be called more than once to switch card sets.
     * @param {!Array.<!Element>} cards The individual viewable cards.
     * @param {number} index Index of the card to in the new set of cards to
     *     navigate to.
     */
    setCards: function(cards, index) {
      assert(index >= 0 && index < cards.length,
          'Invalid index in CardSlider#setCards');
      this.cards_ = cards;

      this.updateCardWidths_();

      // Jump to the given card index.
      this.selectCard(index);
    },

    /**
     * Updates the width of each card.
     * @private
     */
    updateCardWidths_: function() {
      for (var i = 0, card; card = this.cards_[i]; i++)
        card.style.width = this.cardWidth_ + 'px';
    },

    /**
     * Returns the index of the current card.
     * @return {number} index of the current card.
     */
    get currentCard() {
      return this.currentCard_;
    },

    /**
     * Returns the number of cards.
     * @return {number} number of cards.
     */
    get cardCount() {
      return this.cards_.length;
    },

    /**
     * Returns the current card itself.
     * @return {!Element} the currently shown card.
     */
    get currentCardValue() {
      return this.cards_[this.currentCard_];
    },

    /**
     * Handle horizontal scrolls to flip between pages.
     * @private
     */
    onMouseWheel_: function(e) {
      if (e.wheelDeltaX == 0)
        return;

      var preventDefault = true;
      // If the horizontal scroll didn't change cards (on the far right of
      // the NTP), let the browser take a shot at the event as well.
      if (this.currentCard === 0 && this.mouseWheelScrollAmount_ > 0)
        preventDefault = false;
      if (this.currentCard === this.cards_.length - 1 &&
          this.mouseWheelScrollAmount_ < 0) {
        preventDefault = false;
      }

      // Mac value feels ok with multitouch trackpads and magic mice
      // (with physical scrollwheel, too), but not so great with logitech
      // mice.
      var scrollAmountPerPage = cr.isMac ? 400 : 120;
      if (!ntp4.isRTL())
        scrollAmountPerPage *= -1;
      this.mouseWheelScrollAmount_ += e.wheelDeltaX;
      if (Math.abs(this.mouseWheelScrollAmount_) >=
          Math.abs(scrollAmountPerPage)) {
        var pagesToScroll = this.mouseWheelScrollAmount_ / scrollAmountPerPage;
        pagesToScroll =
            (pagesToScroll > 0 ? Math.floor : Math.ceil)(pagesToScroll);
        var newCardIndex = this.currentCard + pagesToScroll;
        newCardIndex = Math.min(this.cards_.length - 1,
                                Math.max(0, newCardIndex));
        this.selectCard(newCardIndex, true);
        this.mouseWheelScrollAmount_ -= pagesToScroll * scrollAmountPerPage;
      }

      // We got a mouse wheel event, so cancel any pending scroll wheel timeout.
      if (this.scrollClearTimeout_ != null)
        clearTimeout(this.scrollClearTimeout_);
      // If we didn't use up all the scroll, hold onto it for a little bit, but
      // drop it after a delay.
      if (this.mouseWheelScrollAmount_ != 0) {
        this.scrollClearTimeout_ =
            setTimeout(this.clearMouseWheelScroll_.bind(this), 500);
      }

      if (preventDefault)
        e.preventDefault();
    },

    /**
     * Resets the amount of horizontal scroll we've seen to 0. See
     * onMouseWheel_.
     * @private
     */
    clearMouseWheelScroll_: function() {
      this.mouseWheelScrollAmount_ = 0;
    },

    /**
     * Selects a new card, ensuring that it is a valid index, transforming the
     * view and possibly calling the change card callback.
     * @param {number} newCardIndex Index of card to show.
     * @param {boolean=} opt_animate If true will animate transition from
     *     current position to new position.
     */
    selectCard: function(newCardIndex, opt_animate) {
      var isChangingCard =
          !this.cards_[newCardIndex].classList.contains('selected-card');

      if (isChangingCard) {
        this.currentCardValue.classList.remove('selected-card');
        // If we have a new card index and it is valid then update the left
        // position and current card index.
        this.currentCard_ = newCardIndex;
        this.currentCardValue.classList.add('selected-card');
      }

      this.transformToCurrentCard_(opt_animate);

      if (isChangingCard) {
        var event = document.createEvent('Event');
        event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true);
        event.cardSlider = this;
        this.container_.dispatchEvent(event);
      }
    },

    /**
     * Selects a card from the stack. Passes through to selectCard.
     * @param {Node} newCard The card that should be selected.
     * @param {boolean=} opt_animate Whether to animate.
     */
    selectCardByValue: function(newCard, opt_animate) {
      var i = this.cards_.indexOf(newCard);
      assert(i != -1);
      this.selectCard(i, opt_animate);
    },

    /**
     * Centers the view on the card denoted by this.currentCard. Can either
     * animate to that card or snap to it.
     * @param {boolean=} opt_animate If true will animate transition from
     *     current position to new position.
     * @private
     */
    transformToCurrentCard_: function(opt_animate) {
      this.currentLeft_ = -this.cardWidth_ *
          (ntp4.isRTL() ? this.cards_.length - this.currentCard - 1 :
                          this.currentCard);

      // Animate to the current card, which will either transition if the
      // current card is new, or reset the existing card if we didn't drag
      // enough to change cards.
      var transition = '';
      if (opt_animate) {
        transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ +
                     'ms ease-in-out';
      }
      this.container_.style.WebkitTransition = transition;
      this.translateTo_(this.currentLeft_);
    },

    /**
     * Moves the view to the specified position.
     * @param {number} x Horizontal position to move to.
     * @private
     */
    translateTo_: function(x) {
      // We use a webkitTransform to slide because this is GPU accelerated on
      // Chrome and iOS.  Once Chrome does GPU acceleration on the position
      // fixed-layout elements we could simply set the element's position to
      // fixed and modify 'left' instead.
      this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)';
    },

    /* Touch ******************************************************************/

    /**
     * Clear any transition that is in progress and enable dragging for the
     * touch.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onTouchStart_: function(e) {
      this.container_.style.WebkitTransition = '';
      e.enableDrag = true;
    },

    /**
     * Tell the TouchHandler that dragging is acceptable when the user begins by
     * scrolling horizontally.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragStart_: function(e) {
      e.enableDrag = Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY);
    },

    /**
     * On each drag move event reposition the container appropriately so the
     * cards look like they are sliding.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragMove_: function(e) {
      var deltaX = e.dragDeltaX;
      // If dragging beyond the first or last card then apply a backoff so the
      // dragging feels stickier than usual.
      if (!this.currentCard && deltaX > 0 ||
          this.currentCard == (this.cards_.length - 1) && deltaX < 0) {
        deltaX /= 2;
      }
      this.translateTo_(this.currentLeft_ + deltaX);
    },

    /**
     * On drag end events we may want to transition to another card, depending
     * on the ending position of the drag and the velocity of the drag.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragEnd_: function(e) {
      var deltaX = e.dragDeltaX;
      var velocity = this.touchHandler_.getEndVelocity().x;
      var newX = this.currentLeft_ + deltaX;
      var newCardIndex = Math.round(-newX / this.cardWidth_);

      if (newCardIndex == this.currentCard && Math.abs(velocity) >
          CardSlider.TRANSITION_VELOCITY_THRESHOLD_) {
        // If the drag wasn't far enough to change cards but the velocity was
        // high enough to transition anyways. If the velocity is to the left
        // (negative) then the user wishes to go right (card +1).
        newCardIndex += velocity > 0 ? -1 : 1;
      }

      this.selectCard(newCardIndex, /* animate */ true);
    },

    /**
     * Cancel any current touch/slide as if we saw a touch end
     */
    cancelTouch: function() {
      // Stop listening to any current touch
      this.touchHandler_.cancelTouch();

      // Ensure we're at a card bounary
      this.transformToCurrentCard_(true);
    },
  };

  return CardSlider;
})();
<commit_msg>mac: Completely disable history swiping on the ntp.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Card slider implementation. Allows you to create interactions
 * that have items that can slide left to right to reveal additional items.
 * Works by adding the necessary event handlers to a specific DOM structure
 * including a frame, container and cards.
 * - The frame defines the boundary of one item. Each card will be expanded to
 *   fill the width of the frame. This element is also overflow hidden so that
 *   the additional items left / right do not trigger horizontal scrolling.
 * - The container is what all the touch events are attached to. This element
 *   will be expanded to be the width of all cards.
 * - The cards are the individual viewable items. There should be one card for
 *   each item in the list. Only one card will be visible at a time. Two cards
 *   will be visible while you are transitioning between cards.
 *
 * This class is designed to work well on any hardware-accelerated touch device.
 * It should still work on pre-hardware accelerated devices it just won't feel
 * very good. It should also work well with a mouse.
 */

// Use an anonymous function to enable strict mode just for this file (which
// will be concatenated with other files when embedded in Chrome
var CardSlider = (function() {
  'use strict';

  /**
   * @constructor
   * @param {!Element} frame The bounding rectangle that cards are visible in.
   * @param {!Element} container The surrounding element that will have event
   *     listeners attached to it.
   * @param {number} cardWidth The width of each card should have.
   */
  function CardSlider(frame, container, cardWidth) {
    /**
     * @type {!Element}
     * @private
     */
    this.frame_ = frame;

    /**
     * @type {!Element}
     * @private
     */
    this.container_ = container;

    /**
     * Array of card elements.
     * @type {!Array.<!Element>}
     * @private
     */
    this.cards_ = [];

    /**
     * Index of currently shown card.
     * @type {number}
     * @private
     */
    this.currentCard_ = 0;

    /**
     * @type {number}
     * @private
     */
    this.cardWidth_ = cardWidth;

    /**
     * @type {!TouchHandler}
     * @private
     */
    this.touchHandler_ = new TouchHandler(this.container_);

  }

  /**
   * Events fired by the slider.
   * Events are fired at the container.
   */
  CardSlider.EventType = {
    // Fired when the user slides to another card.
    CARD_CHANGED: 'cardSlider:card_changed'
  };

  /**
   * The time to transition between cards when animating. Measured in ms.
   * @type {number}
   * @private
   * @const
   */
  CardSlider.TRANSITION_TIME_ = 200;

  /**
   * The minimum velocity required to transition cards if they did not drag past
   * the halfway point between cards. Measured in pixels / ms.
   * @type {number}
   * @private
   * @const
   */
  CardSlider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2;

  CardSlider.prototype = {
    /**
     * The current left offset of the container relative to the frame.
     * @type {number}
     * @private
     */
    currentLeft_: 0,

    /**
     * Initialize all elements and event handlers. Must call after construction
     * and before usage.
     */
    initialize: function() {
      var view = this.container_.ownerDocument.defaultView;
      assert(view.getComputedStyle(this.container_).display == '-webkit-box',
          'Container should be display -webkit-box.');
      assert(view.getComputedStyle(this.frame_).overflow == 'hidden',
          'Frame should be overflow hidden.');
      assert(view.getComputedStyle(this.container_).position == 'static',
          'Container should be position static.');

      this.updateCardWidths_();

      this.mouseWheelScrollAmount_ = 0;
      this.scrollClearTimeout_ = null;
      this.frame_.addEventListener('mousewheel',
                                   this.onMouseWheel_.bind(this));

      if (document.documentElement.getAttribute('touchui')) {
        this.container_.addEventListener(TouchHandler.EventType.TOUCH_START,
                                         this.onTouchStart_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_START,
                                         this.onDragStart_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE,
                                         this.onDragMove_.bind(this));
        this.container_.addEventListener(TouchHandler.EventType.DRAG_END,
                                         this.onDragEnd_.bind(this));

        this.touchHandler_.enable(/* opt_capture */ false);
      }
    },

    /**
     * Use in cases where the width of the frame has changed in order to update
     * the width of cards. For example should be used when orientation changes
     * in full width sliders.
     * @param {number} newCardWidth Width all cards should have, in pixels.
     */
    resize: function(newCardWidth) {
      if (newCardWidth != this.cardWidth_) {
        this.cardWidth_ = newCardWidth;

        this.updateCardWidths_();

        // Must upate the transform on the container to show the correct card.
        this.transformToCurrentCard_();
      }
    },

    /**
     * Sets the cards used. Can be called more than once to switch card sets.
     * @param {!Array.<!Element>} cards The individual viewable cards.
     * @param {number} index Index of the card to in the new set of cards to
     *     navigate to.
     */
    setCards: function(cards, index) {
      assert(index >= 0 && index < cards.length,
          'Invalid index in CardSlider#setCards');
      this.cards_ = cards;

      this.updateCardWidths_();

      // Jump to the given card index.
      this.selectCard(index);
    },

    /**
     * Updates the width of each card.
     * @private
     */
    updateCardWidths_: function() {
      for (var i = 0, card; card = this.cards_[i]; i++)
        card.style.width = this.cardWidth_ + 'px';
    },

    /**
     * Returns the index of the current card.
     * @return {number} index of the current card.
     */
    get currentCard() {
      return this.currentCard_;
    },

    /**
     * Returns the number of cards.
     * @return {number} number of cards.
     */
    get cardCount() {
      return this.cards_.length;
    },

    /**
     * Returns the current card itself.
     * @return {!Element} the currently shown card.
     */
    get currentCardValue() {
      return this.cards_[this.currentCard_];
    },

    /**
     * Handle horizontal scrolls to flip between pages.
     * @private
     */
    onMouseWheel_: function(e) {
      if (e.wheelDeltaX == 0)
        return;

      // Prevent OS X 10.7+ history swiping on the NTP.
      e.preventDefault();

      // Mac value feels ok with multitouch trackpads and magic mice
      // (with physical scrollwheel, too), but not so great with logitech
      // mice.
      var scrollAmountPerPage = cr.isMac ? 400 : 120;
      if (!ntp4.isRTL())
        scrollAmountPerPage *= -1;
      this.mouseWheelScrollAmount_ += e.wheelDeltaX;
      if (Math.abs(this.mouseWheelScrollAmount_) >=
          Math.abs(scrollAmountPerPage)) {
        var pagesToScroll = this.mouseWheelScrollAmount_ / scrollAmountPerPage;
        pagesToScroll =
            (pagesToScroll > 0 ? Math.floor : Math.ceil)(pagesToScroll);
        var newCardIndex = this.currentCard + pagesToScroll;
        newCardIndex = Math.min(this.cards_.length - 1,
                                Math.max(0, newCardIndex));
        this.selectCard(newCardIndex, true);
        this.mouseWheelScrollAmount_ -= pagesToScroll * scrollAmountPerPage;
      }

      // We got a mouse wheel event, so cancel any pending scroll wheel timeout.
      if (this.scrollClearTimeout_ != null)
        clearTimeout(this.scrollClearTimeout_);
      // If we didn't use up all the scroll, hold onto it for a little bit, but
      // drop it after a delay.
      if (this.mouseWheelScrollAmount_ != 0) {
        this.scrollClearTimeout_ =
            setTimeout(this.clearMouseWheelScroll_.bind(this), 500);
      }
    },

    /**
     * Resets the amount of horizontal scroll we've seen to 0. See
     * onMouseWheel_.
     * @private
     */
    clearMouseWheelScroll_: function() {
      this.mouseWheelScrollAmount_ = 0;
    },

    /**
     * Selects a new card, ensuring that it is a valid index, transforming the
     * view and possibly calling the change card callback.
     * @param {number} newCardIndex Index of card to show.
     * @param {boolean=} opt_animate If true will animate transition from
     *     current position to new position.
     */
    selectCard: function(newCardIndex, opt_animate) {
      var isChangingCard =
          !this.cards_[newCardIndex].classList.contains('selected-card');

      if (isChangingCard) {
        this.currentCardValue.classList.remove('selected-card');
        // If we have a new card index and it is valid then update the left
        // position and current card index.
        this.currentCard_ = newCardIndex;
        this.currentCardValue.classList.add('selected-card');
      }

      this.transformToCurrentCard_(opt_animate);

      if (isChangingCard) {
        var event = document.createEvent('Event');
        event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true);
        event.cardSlider = this;
        this.container_.dispatchEvent(event);
      }
    },

    /**
     * Selects a card from the stack. Passes through to selectCard.
     * @param {Node} newCard The card that should be selected.
     * @param {boolean=} opt_animate Whether to animate.
     */
    selectCardByValue: function(newCard, opt_animate) {
      var i = this.cards_.indexOf(newCard);
      assert(i != -1);
      this.selectCard(i, opt_animate);
    },

    /**
     * Centers the view on the card denoted by this.currentCard. Can either
     * animate to that card or snap to it.
     * @param {boolean=} opt_animate If true will animate transition from
     *     current position to new position.
     * @private
     */
    transformToCurrentCard_: function(opt_animate) {
      this.currentLeft_ = -this.cardWidth_ *
          (ntp4.isRTL() ? this.cards_.length - this.currentCard - 1 :
                          this.currentCard);

      // Animate to the current card, which will either transition if the
      // current card is new, or reset the existing card if we didn't drag
      // enough to change cards.
      var transition = '';
      if (opt_animate) {
        transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ +
                     'ms ease-in-out';
      }
      this.container_.style.WebkitTransition = transition;
      this.translateTo_(this.currentLeft_);
    },

    /**
     * Moves the view to the specified position.
     * @param {number} x Horizontal position to move to.
     * @private
     */
    translateTo_: function(x) {
      // We use a webkitTransform to slide because this is GPU accelerated on
      // Chrome and iOS.  Once Chrome does GPU acceleration on the position
      // fixed-layout elements we could simply set the element's position to
      // fixed and modify 'left' instead.
      this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)';
    },

    /* Touch ******************************************************************/

    /**
     * Clear any transition that is in progress and enable dragging for the
     * touch.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onTouchStart_: function(e) {
      this.container_.style.WebkitTransition = '';
      e.enableDrag = true;
    },

    /**
     * Tell the TouchHandler that dragging is acceptable when the user begins by
     * scrolling horizontally.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragStart_: function(e) {
      e.enableDrag = Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY);
    },

    /**
     * On each drag move event reposition the container appropriately so the
     * cards look like they are sliding.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragMove_: function(e) {
      var deltaX = e.dragDeltaX;
      // If dragging beyond the first or last card then apply a backoff so the
      // dragging feels stickier than usual.
      if (!this.currentCard && deltaX > 0 ||
          this.currentCard == (this.cards_.length - 1) && deltaX < 0) {
        deltaX /= 2;
      }
      this.translateTo_(this.currentLeft_ + deltaX);
    },

    /**
     * On drag end events we may want to transition to another card, depending
     * on the ending position of the drag and the velocity of the drag.
     * @param {!TouchHandler.Event} e The TouchHandler event.
     * @private
     */
    onDragEnd_: function(e) {
      var deltaX = e.dragDeltaX;
      var velocity = this.touchHandler_.getEndVelocity().x;
      var newX = this.currentLeft_ + deltaX;
      var newCardIndex = Math.round(-newX / this.cardWidth_);

      if (newCardIndex == this.currentCard && Math.abs(velocity) >
          CardSlider.TRANSITION_VELOCITY_THRESHOLD_) {
        // If the drag wasn't far enough to change cards but the velocity was
        // high enough to transition anyways. If the velocity is to the left
        // (negative) then the user wishes to go right (card +1).
        newCardIndex += velocity > 0 ? -1 : 1;
      }

      this.selectCard(newCardIndex, /* animate */ true);
    },

    /**
     * Cancel any current touch/slide as if we saw a touch end
     */
    cancelTouch: function() {
      // Stop listening to any current touch
      this.touchHandler_.cancelTouch();

      // Ensure we're at a card bounary
      this.transformToCurrentCard_(true);
    },
  };

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


<commit_before>jQuery.noConflict(); // Allow the test to run with other libs or jQuery's.

// jQuery-specific QUnit.reset
(function() {
	var reset = QUnit.reset,
		ajaxSettings = jQuery.ajaxSettings;

	QUnit.reset = function() {
		reset.apply(this, arguments);
		jQuery.event.global = {};
		jQuery.ajaxSettings = jQuery.extend({}, ajaxSettings);
	};
})();

// load testswarm agent
(function() {
	var url = window.location.search;
	url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) );
	if ( !url || url.indexOf("http") !== 0 ) {
		return;
	}

	// (Temporarily) Disable Ajax tests to reduce network strain
	isLocal = QUnit.isLocal = true;

	document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>");
})();

// QUnit Aliases
(function() {
	window.equals = window.equal;
	window.same = window.deepEqual;
})();
<commit_msg>Run ajax tests in swarm<commit_after>jQuery.noConflict(); // Allow the test to run with other libs or jQuery's.

// jQuery-specific QUnit.reset
(function() {
	var reset = QUnit.reset,
		ajaxSettings = jQuery.ajaxSettings;

	QUnit.reset = function() {
		reset.apply(this, arguments);
		jQuery.event.global = {};
		jQuery.ajaxSettings = jQuery.extend({}, ajaxSettings);
	};
})();

// load testswarm agent
(function() {
	var url = window.location.search;
	url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) );
	if ( !url || url.indexOf("http") !== 0 ) {
		return;
	}

	// (Temporarily) Disable Ajax tests to reduce network strain
	// isLocal = QUnit.isLocal = true;

	document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>");
})();

// QUnit Aliases
(function() {
	window.equals = window.equal;
	window.same = window.deepEqual;
})();
<|endoftext|>


<commit_before>import React, { Component } from 'react';
// Gets weather from state.
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as weatherActionCreators from './actions-weather';
import * as forecastActionCreators from './actions-forecast';
import { saveState } from '../../helpers/manage-localStorage';
import moment from 'moment';
import Hammer from 'hammerjs';
import Modal from 'react-modal';
import styled from 'styled-components';
import Sortable from 'sortablejs';

import WeatherOneCity from './weather-onecity';

const StyledWeatherList = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  margin-bottom: 4rem;
`;

const NoCities = styled.div`
  padding: 20px;
  font-size: 2.5rem;
  text-align: center;
`;

const StyledModal = styled(Modal)`
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: solid 2px #000;
  padding: 30px;
  background: #fff;
  text-align: center;
`;

export class WeatherList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      itemsWithSwipe: [],
      modalIsOpen: false,
      cityToDelete: ''
    };

    this.fetchQueue = [];
    // Create draggable during DidUpdate, but keep it from being created more
    // than once. DOM element isn't available during DidMount.
    this.dragCreated = false;
  }

  openModal = city => {
    this.setState({ modalIsOpen: true, cityToDelete: city });
  };

  closeModal = () => {
    this.setState({ modalIsOpen: false });
  };

  deleteCity = () => {
    // When using the 'x' button, this.rowToDelete isn't set.
    if (!this.rowToDelete) {
      this.rowToDelete = document.getElementById(this.state.cityToDelete.id);
    }

    this.setState({ modalIsOpen: false });
    this.rowToDelete.classList.add('animated');
    this.rowToDelete.classList.add('faster');
    this.rowToDelete.classList.add('zoomOut');
    setTimeout(() => {
      this.props.actions.deleteCity(this.state.cityToDelete.id);
      this.props.actions.deleteForecast(this.state.cityToDelete.id);
      saveState('weather', this.props.weather);
      // Reset rowToDelete, otherwise deleting with the 'x' button won't work
      // after one city is deleted.
      this.rowToDelete = null;
    }, 500);
  };

  componentDidMount() {
    this.props.actions.fetchWeatherFromLocalStorage();
    this.props.actions.fetchForecastFromLocalStorage();
    this.rowToDelete;
    // Store swipe object in an array. Was going to access objects later
    // and remove swipeleft but that definitely didn't work.
    // I could add a swiperight event later, but I could not disable (.off)
    // or overwrite (use a new empty function) swipeleft.
    this.swipeItems = [];
  }

  componentDidUpdate(prevProps) {
    const el = document.getElementById('draggable');
    if (el && this.dragCreated === false) {
      this.dragCreated = true;
      const sortable = Sortable.create(el, {
        // From the docs:
        store: {
          /**
           * Get the order of elements. Called once during initialization.
           * @param   {Sortable}  sortable
           * @returns {Array}
           */
          get: function(sortable) {
            var order = localStorage.getItem(sortable.options.group.name);
            return order ? order.split('|') : [];
          },

          /**
           * Save the order of elements. Called onEnd (when the item is dropped).
           * @param {Sortable}  sortable
           */
          set: function(sortable) {
            var order = sortable.toArray();
            localStorage.setItem(sortable.options.group.name, order.join('|'));
          }
        },
        handle: '.handle'
      });
    }

    if (prevProps.weather !== this.props.weather) {
      // Something (most likely a this.props.actions.fetchWeatherUpdate, but
      // also maybe a city got deleted?) caused prevProps.weather to not equal
      // this.props.weather. Save this.props.weather to storage.
      saveState('weather', this.props.weather);
    }

    // Used to check how old the current weather is.
    const now = new Date().getTime();

    this.props.weather.map(city => {
      const id = city.id;

      // Track if city.id has swipe attached to it. If it does not, add city.id
      // to state.itemsWithSwipe and add swipe to that city.
      if (!this.state.itemsWithSwipe.includes(city.id)) {
        this.setState(prevState => ({
          itemsWithSwipe: [...prevState.itemsWithSwipe, city.id]
        }));

        const swipeRow = document.getElementById(city.id);
        // Currently swipeRow doesn't exist in testing but this is a good check
        // either way...
        if (swipeRow) {
          this.swipeItems[id] = new Hammer(swipeRow);
          this.swipeItems[id].get('swipe').set({ velocity: 0.5 });

          this.swipeItems[id].on('swipeleft', e => {
            // Can't figure out how to unbind/disable swipe left, so using
            // this instead to disable the modal when needed.
            if (!this.props.options.useSwipeToDelete) return;
            // If delete is confirmed, use css to animate-out this div.
            this.rowToDelete = e.target.closest('.row-swipe');
            // Open a confirmation asking to delete the city or cancel.
            this.openModal(city);
          });
        }
      } // End track cities with swipe and add swipe.

      // Update current weather if > 30 min old.
      const { timeFetched } = city;
      // Convert milliseconds to minutes.
      const timeDifference = (now - timeFetched) / 1000 / 60;

      // Request weather update if fetched over 30 minutes ago.
      if (timeDifference > 30 || timeFetched === undefined) {
        this.props.actions.fetchWeatherUpdate(city.id);
      } // End update current weather if > 30 min old.

      if (this.props.forecast && this.props.forecast[city.id]) {
        // Should forecast be updated.
        // forecast = YYYY-MM-DD of the first item in the forecast.
        const forecast = this.props.forecast[city.id][0].dt_txt.split(' ')[0];
        const today = moment().format('YYYY-MM-DD');
        // If forecast date !== today, update.
        if (forecast === today) {
          if (!this.fetchQueue.includes(id)) {
            this.fetchQueue = [...this.fetchQueue, id];
            this.props.actions.fetchForecastFromOpenWeather(city);
          }
        }
      } else {
        if (!this.fetchQueue.includes(id)) {
          this.fetchQueue = [...this.fetchQueue, id];
          this.props.actions.fetchForecastFromOpenWeather(city);
        }
      }
    }); // this.props.weather.map(city)
  }

  render() {
    if (this.props.weather.length === 0) {
      return (
        <NoCities data-cy="nocities">No cities here - search for one!</NoCities>
      );
    }

    return (

        {this.props.weather.map(function(city) {
          return (
            <WeatherOneCity
              city={city}
              options={this.props.options}
              openModal={this.openModal}
              key={city.id}
            />
          );
        }, this)}
      </StyledWeatherList>
    );
  }
}

// Gets weather from state.
function mapStateToProps({ weather, forecast }) {
  return { weather, forecast }; // same as { weather: weather}
}

function mapDispatchToProps(dispatch) {
  // Assign all actions (import * as actionCreators) to props.actions
  const actions = { ...weatherActionCreators, ...forecastActionCreators };
  return {
    actions: bindActionCreators(actions, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(WeatherList);
<commit_msg>fix missing forecast<commit_after>import React, { Component } from 'react';
// Gets weather from state.
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as weatherActionCreators from './actions-weather';
import * as forecastActionCreators from './actions-forecast';
import { saveState } from '../../helpers/manage-localStorage';
import moment from 'moment';
import Hammer from 'hammerjs';
import Modal from 'react-modal';
import styled from 'styled-components';
import Sortable from 'sortablejs';

import WeatherOneCity from './weather-onecity';

const StyledWeatherList = styled.div`
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  margin-bottom: 4rem;
`;

const NoCities = styled.div`
  padding: 20px;
  font-size: 2.5rem;
  text-align: center;
`;

const StyledModal = styled(Modal)`
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: solid 2px #000;
  padding: 30px;
  background: #fff;
  text-align: center;
`;

export class WeatherList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      itemsWithSwipe: [],
      modalIsOpen: false,
      cityToDelete: ''
    };

    this.fetchQueue = [];
    // Create draggable during DidUpdate, but keep it from being created more
    // than once. DOM element isn't available during DidMount.
    this.dragCreated = false;
  }

  openModal = city => {
    this.setState({ modalIsOpen: true, cityToDelete: city });
  };

  closeModal = () => {
    this.setState({ modalIsOpen: false });
  };

  deleteCity = () => {
    // When using the 'x' button, this.rowToDelete isn't set.
    if (!this.rowToDelete) {
      this.rowToDelete = document.getElementById(this.state.cityToDelete.id);
    }

    this.setState({ modalIsOpen: false });
    this.rowToDelete.classList.add('animated');
    this.rowToDelete.classList.add('faster');
    this.rowToDelete.classList.add('zoomOut');
    setTimeout(() => {
      this.props.actions.deleteCity(this.state.cityToDelete.id);
      this.props.actions.deleteForecast(this.state.cityToDelete.id);
      saveState('weather', this.props.weather);
      // Reset rowToDelete, otherwise deleting with the 'x' button won't work
      // after one city is deleted.
      this.rowToDelete = null;
    }, 500);
  };

  componentDidMount() {
    this.props.actions.fetchWeatherFromLocalStorage();
    this.props.actions.fetchForecastFromLocalStorage();
    this.rowToDelete;
    // Store swipe object in an array. Was going to access objects later
    // and remove swipeleft but that definitely didn't work.
    // I could add a swiperight event later, but I could not disable (.off)
    // or overwrite (use a new empty function) swipeleft.
    this.swipeItems = [];
  }

  componentDidUpdate(prevProps) {
    const el = document.getElementById('draggable');
    if (el && this.dragCreated === false) {
      this.dragCreated = true;
      const sortable = Sortable.create(el, {
        // From the docs:
        store: {
          /**
           * Get the order of elements. Called once during initialization.
           * @param   {Sortable}  sortable
           * @returns {Array}
           */
          get: function(sortable) {
            var order = localStorage.getItem(sortable.options.group.name);
            return order ? order.split('|') : [];
          },

          /**
           * Save the order of elements. Called onEnd (when the item is dropped).
           * @param {Sortable}  sortable
           */
          set: function(sortable) {
            var order = sortable.toArray();
            localStorage.setItem(sortable.options.group.name, order.join('|'));
          }
        },
        handle: '.handle'
      });
    }

    if (prevProps.weather !== this.props.weather) {
      // Something (most likely a this.props.actions.fetchWeatherUpdate, but
      // also maybe a city got deleted?) caused prevProps.weather to not equal
      // this.props.weather. Save this.props.weather to storage.
      saveState('weather', this.props.weather);
    }

    // Used to check how old the current weather is.
    const now = new Date().getTime();

    this.props.weather.map(city => {
      const id = city.id;

      // Track if city.id has swipe attached to it. If it does not, add city.id
      // to state.itemsWithSwipe and add swipe to that city.
      if (!this.state.itemsWithSwipe.includes(city.id)) {
        this.setState(prevState => ({
          itemsWithSwipe: [...prevState.itemsWithSwipe, city.id]
        }));

        const swipeRow = document.getElementById(city.id);
        // Currently swipeRow doesn't exist in testing but this is a good check
        // either way...
        if (swipeRow) {
          this.swipeItems[id] = new Hammer(swipeRow);
          this.swipeItems[id].get('swipe').set({ velocity: 0.5 });

          this.swipeItems[id].on('swipeleft', e => {
            // Can't figure out how to unbind/disable swipe left, so using
            // this instead to disable the modal when needed.
            if (!this.props.options.useSwipeToDelete) return;
            // If delete is confirmed, use css to animate-out this div.
            this.rowToDelete = e.target.closest('.row-swipe');
            // Open a confirmation asking to delete the city or cancel.
            this.openModal(city);
          });
        }
      } // End track cities with swipe and add swipe.

      // Update current weather if > 30 min old.
      const { timeFetched } = city;
      // Convert milliseconds to minutes.
      const timeDifference = (now - timeFetched) / 1000 / 60;

      // Request weather update if fetched over 30 minutes ago.
      if (timeDifference > 30 || timeFetched === undefined) {
        this.props.actions.fetchWeatherUpdate(city.id);
      } // End update current weather if > 30 min old.

      if (this.props.forecast && this.props.forecast[city.id]) {
        // Should forecast be updated.
        // forecast = YYYY-MM-DD of the first item in the forecast.
        const forecast = this.props.forecast[city.id][0].dt_txt.split(' ')[0];
        const today = moment().format('YYYY-MM-DD');
        // If forecast date !== today, update.
        if (forecast === today) {
          if (!this.fetchQueue.includes(id)) {
            this.fetchQueue = [...this.fetchQueue, id];
            this.props.actions.fetchForecastFromOpenWeather(city);
          }
        }
      } else {
        if (!this.fetchQueue.includes(id)) {
          this.fetchQueue = [...this.fetchQueue, id];
          this.props.actions.fetchForecastFromOpenWeather(city);
        }
      }
    }); // this.props.weather.map(city)
  }

  render() {
    if (this.props.weather.length === 0) {
      return (
        <NoCities data-cy="nocities">No cities here - search for one!</NoCities>
      );
    }

    return (

        {this.props.weather.map(function(city) {
          return (
            <WeatherOneCity
              city={city}
              forecast={this.props.forecast}
              options={this.props.options}
              openModal={this.openModal}
              key={city.id}
            />
          );
        }, this)}
      </StyledWeatherList>
    );
  }
}

// Gets weather from state.
function mapStateToProps({ weather, forecast }) {
  return { weather, forecast }; // same as { weather: weather}
}

function mapDispatchToProps(dispatch) {
  // Assign all actions (import * as actionCreators) to props.actions
  const actions = { ...weatherActionCreators, ...forecastActionCreators };
  return {
    actions: bindActionCreators(actions, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(WeatherList);
<|endoftext|>


<commit_before>importScripts('openpgp.worker.min.js');

sha256 = window.openpgp.crypto.hash.sha256;

var iterateOverSHA = function(seed) {
  var i;
  for (i = 0; i < 1024; i++) {
    var x = sha256(seed + i);

    if (x.charCodeAt(31) == 0) {
      postMessage(i);
    }
  }

  postMessage("12345");
};

onmessage = function(e) {
  iterateOverSHA(e.data.pow);
};
<commit_msg>Align proof of work to the api change in openpgpjs<commit_after>importScripts('openpgp.worker.min.js');

var sha256 = window.openpgp.crypto.hash.sha256;

var iterateOverSHA = function(seed) {
  var i;
  for (i = 0; i < 1024; i++) {
    var x = sha256(seed + i);

    if (x[31] == 0) {
      postMessage(i);
    }
  }

  postMessage("12345");
};

var onmessage = function(e) {
  iterateOverSHA(e.data.pow);
};
<|endoftext|>


<commit_before>/* globals NAMESPACE */
/* eslint-disable fecs-camelcase */
/**
 * Slider 组件
 * Created by dingquan on 15-02-03
 * @file slider.js
 * @author dingquan
 */
'use strict';
$.widget('blend.slider', {
    /**
     * 组件的默认选项，可以由多重覆盖关系
     */
    options: {
        autoSwipe: true,            // 自动滚动,默认开启
        continuousScroll: true,     // 连续滚动
        axisX: true,                // 滚动方向,默认x轴滚动
        transitionType: 'ease',     // 过渡类型
        // duration: 0.6,
        speed: 2000,                // 切换的时间间隔
        theme: 'd2',
        // needDirection: false,    // 是否需要左右切换的按钮
        ratio: 'normal'     // normal/wide/square/small
    },
    /**
     * 创建组件调用一次
     * @private
     */
    _create: function () {

        /**
         * this.element 组件对应的单个 Zepto/jQuery 对象
         */
        var $el = this.element;
        /**
         * 经过继承的 options
         */
        var options = this.options;

        var ratioClass = NAMESPACE + 'slider-';
        switch (options.ratio) {
            case 'wide':
            case 'square':
            case 'small':
                ratioClass += options.ratio;
                break;
            default :
                ratioClass += 'normal';
        }
        $el.addClass(ratioClass);

        this.$container = $el;
        this.$ul = $el.find('.' + NAMESPACE + 'slides');
        this.$li = $el.find('.' + NAMESPACE + 'slides li');

        this._liWidth = this.$li.width();
        this._liHeight = this.$li.height();
        this._liLength = this.$li.length;

        this.autoScroll = null;     // 自动播放interval对象
        this._index = 0;            // 当前幻灯片位置

        if (typeof options.theme !== 'string') {
            options.theme = 'default';
        }

        this._addComponents(options.theme);
    },
    /**
     * _init 初始化的时候调用
     * @private
     */
    _init: function () {

        var opts = this.options;
        var that = this;
        var $ul = this.$ul;
        var $li = this.$li;

        /**
         * 连续滚动，需要复制dom
         */
        if (opts.continuousScroll) {
            $ul.prepend($li.last().clone()).append($li.first().clone());

            var widthOrHeight = opts.axisX ? that._liWidth : that._liHeight;
            that._fnTranslate($ul.children().first(), widthOrHeight * -1);
            that._fnTranslate($ul.children().last(), widthOrHeight * that._liLength);

        }

        // 给初始图片定位
        $li.each(function (i) {
            that._fnTranslate($(this), (opts.axisX ? that._liWidth : that._liHeight) * i);
        });

        that._fnAutoSwipe();
        this._initEvent();
        // this._initView();
    },
    /**
     * 初始化事件绑定
     * @private
     */
    _initEvent: function () {
        var that = this;
        var device = this._device();
        // 绑定触摸
        that.$ul[0].addEventListener(device.startEvt, function (evt){
            that.startX = device.hasTouch ? evt.targetTouches[0].pageX : evt.pageX;
            that.startY = device.hasTouch ? evt.targetTouches[0].pageY : evt.pageY;

            that.$ul[0].addEventListener(device.moveEvt, moveHandler, false);
            that.$ul[0].addEventListener(device.endEvt, endHandler, false);
        }, false);

        function moveHandler (evt){
            if (that.options.autoSwipe) {
                clearInterval(that.autoScroll);
            }

            that.curX = device.hasTouch ? evt.targetTouches[0].pageX : evt.pageX;
            that.curY = device.hasTouch ? evt.targetTouches[0].pageY : evt.pageY;

            that.moveX = that.curX - that.startX;
            that.moveY = that.curY - that.startY;

            that._transitionHandle(that.$ul, 0);

            if (that.options.axisX) {
                evt.preventDefault();
                that._fnTranslate(that.$ul, -(that._liWidth * (parseInt(that._index, 10)) - that.moveX));
            }
        };

        function endHandler (evt){
            var opts = that.options;
            var _touchDistance = 50;

            if (opts.axisX) {
                that.moveDistance = that.moveX;
            }
            else {
                that.moveDistance = that.moveY;
            }

            // 距离小
            if (Math.abs(that.moveDistance) <= _touchDistance) {
                that._fnScroll(.3);
            }
            else {
                // 距离大
                // 手指触摸上一屏滚动
                if (that.moveDistance > _touchDistance) {
                    that._fnMovePrev();
                    that._fnAutoSwipe();
                // 手指触摸下一屏滚动
                }
                else if (that.moveDistance < -_touchDistance) {
                    that._fnMoveNext();
                    that._fnAutoSwipe();
                }
            }

            that.moveX = 0;
            that.moveY = 0;

            that.$ul[0].removeEventListener(device.moveEvt, moveHandler, false);
            that.$ul[0].removeEventListener(device.endEvt, endHandler, false);
        };
    },
    /**
     * 根据不同的theme添加组件和初始化样式
     * @private
     * @param {string} theme 幻灯片主题,目前支持有限的几个
     */
    _addComponents: function (theme) {

        var $el = this.$container;

        if (theme === 'a1') {
            $el.addClass(NAMESPACE + 'slider-a1');
            this._initControl();
        }
        if (theme === 'a2') {
            $el.addClass(NAMESPACE + 'slider-a2');
            this._initControl();
        }
        if (theme === 'd1') {
            $el.addClass(NAMESPACE + 'slider-title');
        }
        if (theme === 'd2') {
            $el.addClass(NAMESPACE + 'slider-title');
            this._initControl();
        }
    },
    /**
     * 初始化control控件
     * @private
     */
    _initControl: function () {

        var $el = this.$container;
        var liLength = this._liLength;

        var html = '';
        for (var i = 0; i < liLength; i++) {
            html += (i === 0) ? '

' : '

';
        }

        var $ol = $('
' + html + '
');

        $el.append($ol);

        this.$controlOl = $ol;
    },
    /**
     * 初始化title
     * @private
     */
    _initTitle: function () {
        // to do
        // var $el = this.$container;
    },
    /*
     * css 过渡
     * @private
     * @param {Object} dom  zepto object
     * @param {number} num - transition number
     */
    _transitionHandle: function (dom, num) {

        var opts = this.options;
        dom.css({
            '-webkit-transition': 'all ' + num + 's ' + opts.transitionType,
            'transition': 'all ' + num + 's ' + opts.transitionType
        });
    },
    /**
     * css 滚动
     * @private
     * @param  {Object} dom    zepto object
     * @param  {number} result translate number
     */
    _fnTranslate: function (dom, result) {

        var opts = this.options;

        if (opts.axisX) {
            dom.css({
                '-webkit-transform': 'translate3d(' + result + 'px,0,0)',
                'transform': 'translate3d(' + result + 'px,0,0)'
            });
        }
        else {
            dom.css({
                '-webkit-transform': 'translate3d(0,' + result + 'px,0)',
                'transform': 'translate3d(0,' + result + 'px,0)'
            });
        }
    },
    /**
     * 下一屏滚动
     * @private
     */
    _fnMoveNext: function () {
        this._index ++;
        this._fnMove();
        /*if(opts.lazyLoad){
            if(opts.continuousScroll){
                fnLazyLoad(_index+2);
            }else{
                fnLazyLoad(_index+1);
            }
        }*/
    },
    /**
     * 上一屏滚动
     * @private
     */
    _fnMovePrev: function () {
        this._index --;
        this._fnMove();
        // 第一次往右滚动懒加载图片
        /*if(firstMovePrev && opts.lazyLoad){
            var i = _liLength-1;
            for(i; i <= (_liLength+1); i++){
                fnLazyLoad(i);
            }
            firstMovePrev = false;
            return;
        }
        if(!firstMovePrev && opts.lazyLoad){
            fnLazyLoad(_index);
        }*/
    },
    /**
     * 自动滑动
     * @private
     */
    _fnAutoSwipe: function () {
        var that = this;
        var opts = this.options;

        if (opts.autoSwipe) {
            this.autoScroll = setInterval(function () {
                that._fnMoveNext();
            }, opts.speed);
        }
    },
    /**
     * [_fnMove description]
     * @private
     */
    _fnMove: function () {
        var that = this;
        var opts = this.options;
        // var _vars = this._vars;
        // var _liLength = this._liLength;

        if (opts.continuousScroll) {
            if (that._index >= that._liLength) {
                that._fnScroll(.3);
                that._index = 0;
                setTimeout(function () {
                    that._fnScroll(0);
                }, 300);
            }
            else if (that._index < 0) {
                that._fnScroll(.3);
                that._index = that._liLength - 1;
                setTimeout(function () {
                    that._fnScroll(0);
                }, 300);
            }
            else {
                that._fnScroll(.3);
            }
        }
        else {
            if (that._index >= that._liLength) {
                that._index = 0;
            }
            else if (that._index < 0) {
                that._index = that._liLength - 1;
            }
            that._fnScroll(.3);
        }

        that._setDotActive();

        // callback(_index);
    },
    /**
     * 滑动
     * @private
     * @param  {number} num num
     */
    _fnScroll: function (num) {
        var $ul = this.$ul;
        var _index = this._index;
        var _liWidth = this._liWidth;
        var _liHeight = this._liHeight;
        var opts = this.options;

        this._transitionHandle($ul, num);
        if (opts.axisX) {
            this._fnTranslate($ul, -_index * _liWidth);
        }
        else {
            this._fnTranslate($ul, -_index * _liHeight);
        }
    },
    /**
     * 设置圆点的状态
     * @private
     */
    _setDotActive: function () {
        this.$controlOl.find('li a').removeClass(NAMESPACE + 'slider-active');
        this.$controlOl.find('li').eq(this._index).find('a').addClass(NAMESPACE + 'slider-active');
    },
    /**
     * judge the device
     * @private
     * @return {Object} 事件
     */
    _device: function () {
        var hasTouch = !!('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch);
        var startEvt = hasTouch ? 'touchstart' : 'mousedown';
        var moveEvt = hasTouch ? 'touchmove' : 'mousemove';
        var endEvt = hasTouch ? 'touchend' : 'mouseup';
        return {
            hasTouch: hasTouch,
            startEvt: startEvt,
            moveEvt: moveEvt,
            endEvt: endEvt
        };
    },
    /**
     * 下一张幻灯片
     * @return {Object} 当前Zepto对象
     */
    next: function () {
        this._fnMoveNext();
        return this.$container;
    },
    /**
     * 上一张幻灯片
     * @return {Object} 当前Zepto对象
     */
    prev: function () {
        this._fnMovePrev();
        return this.$container;
    },
    /**
     * 暂停
     * @return {Object} 当前Zepto对象
     */
    paused: function () {
        clearInterval(this.autoScroll);
        return this.$container;
    },
    start: function () {
        clearInterval(this.autoScroll);
        this._fnAutoSwipe();
        return this.$container;
    }

});
<commit_msg>if speed is 0, then autoSwipe is false<commit_after>/* globals NAMESPACE */
/* eslint-disable fecs-camelcase */
/**
 * Slider 组件
 * Created by dingquan on 15-02-03
 * @file slider.js
 * @author dingquan
 */
'use strict';
$.widget('blend.slider', {
    /**
     * 组件的默认选项，可以由多重覆盖关系
     */
    options: {
        autoSwipe: true,            // 自动滚动,默认开启
        continuousScroll: true,     // 连续滚动
        axisX: true,                // 滚动方向,默认x轴滚动
        transitionType: 'ease',     // 过渡类型
        // duration: 0.6,
        speed: 2000,                // 切换的时间间隔
        theme: 'd2',
        // needDirection: false,    // 是否需要左右切换的按钮
        ratio: 'normal'     // normal/wide/square/small
    },
    /**
     * 创建组件调用一次
     * @private
     */
    _create: function () {

        /**
         * this.element 组件对应的单个 Zepto/jQuery 对象
         */
        var $el = this.element;
        /**
         * 经过继承的 options
         */
        var options = this.options;

        var ratioClass = NAMESPACE + 'slider-';
        switch (options.ratio) {
            case 'wide':
            case 'square':
            case 'small':
                ratioClass += options.ratio;
                break;
            default :
                ratioClass += 'normal';
        }
        $el.addClass(ratioClass);

        this.$container = $el;
        this.$ul = $el.find('.' + NAMESPACE + 'slides');
        this.$li = $el.find('.' + NAMESPACE + 'slides li');

        this._liWidth = this.$li.width();
        this._liHeight = this.$li.height();
        this._liLength = this.$li.length;

        this.autoScroll = null;     // 自动播放interval对象
        this._index = 0;            // 当前幻灯片位置

        if (typeof options.theme !== 'string') {
            options.theme = 'default';
        }

        this._addComponents(options.theme);
    },
    /**
     * _init 初始化的时候调用
     * @private
     */
    _init: function () {

        var opts = this.options;
        var that = this;
        var $ul = this.$ul;
        var $li = this.$li;

        // 如果speed是0, 不自动滚动
        if (this.options.speed <= 0) {
            this.options.autoSwipe = false;
        }

        /**
         * 连续滚动，需要复制dom
         */
        if (opts.continuousScroll) {
            $ul.prepend($li.last().clone()).append($li.first().clone());

            var widthOrHeight = opts.axisX ? that._liWidth : that._liHeight;
            that._fnTranslate($ul.children().first(), widthOrHeight * -1);
            that._fnTranslate($ul.children().last(), widthOrHeight * that._liLength);

        }

        // 给初始图片定位
        $li.each(function (i) {
            that._fnTranslate($(this), (opts.axisX ? that._liWidth : that._liHeight) * i);
        });

        that._fnAutoSwipe();
        this._initEvent();
        // this._initView();
    },
    /**
     * 初始化事件绑定
     * @private
     */
    _initEvent: function () {
        var that = this;
        var device = this._device();
        // 绑定触摸
        that.$ul[0].addEventListener(device.startEvt, function (evt){
            that.startX = device.hasTouch ? evt.targetTouches[0].pageX : evt.pageX;
            that.startY = device.hasTouch ? evt.targetTouches[0].pageY : evt.pageY;

            that.$ul[0].addEventListener(device.moveEvt, moveHandler, false);
            that.$ul[0].addEventListener(device.endEvt, endHandler, false);
        }, false);

        function moveHandler (evt){
            if (that.options.autoSwipe) {
                clearInterval(that.autoScroll);
            }

            that.curX = device.hasTouch ? evt.targetTouches[0].pageX : evt.pageX;
            that.curY = device.hasTouch ? evt.targetTouches[0].pageY : evt.pageY;

            that.moveX = that.curX - that.startX;
            that.moveY = that.curY - that.startY;

            that._transitionHandle(that.$ul, 0);

            if (that.options.axisX) {
                evt.preventDefault();
                that._fnTranslate(that.$ul, -(that._liWidth * (parseInt(that._index, 10)) - that.moveX));
            }
        };

        function endHandler (evt){
            var opts = that.options;
            var _touchDistance = 50;

            if (opts.axisX) {
                that.moveDistance = that.moveX;
            }
            else {
                that.moveDistance = that.moveY;
            }

            // 距离小
            if (Math.abs(that.moveDistance) <= _touchDistance) {
                that._fnScroll(.3);
            }
            else {
                // 距离大
                // 手指触摸上一屏滚动
                if (that.moveDistance > _touchDistance) {
                    that._fnMovePrev();
                    that._fnAutoSwipe();
                // 手指触摸下一屏滚动
                }
                else if (that.moveDistance < -_touchDistance) {
                    that._fnMoveNext();
                    that._fnAutoSwipe();
                }
            }

            that.moveX = 0;
            that.moveY = 0;

            that.$ul[0].removeEventListener(device.moveEvt, moveHandler, false);
            that.$ul[0].removeEventListener(device.endEvt, endHandler, false);
        };
    },
    /**
     * 根据不同的theme添加组件和初始化样式
     * @private
     * @param {string} theme 幻灯片主题,目前支持有限的几个
     */
    _addComponents: function (theme) {

        var $el = this.$container;

        if (theme === 'a1') {
            $el.addClass(NAMESPACE + 'slider-a1');
            this._initControl();
        }
        if (theme === 'a2') {
            $el.addClass(NAMESPACE + 'slider-a2');
            this._initControl();
        }
        if (theme === 'd1') {
            $el.addClass(NAMESPACE + 'slider-title');
        }
        if (theme === 'd2') {
            $el.addClass(NAMESPACE + 'slider-title');
            this._initControl();
        }
    },
    /**
     * 初始化control控件
     * @private
     */
    _initControl: function () {

        var $el = this.$container;
        var liLength = this._liLength;

        var html = '';
        for (var i = 0; i < liLength; i++) {
            html += (i === 0) ? '

' : '

';
        }

        var $ol = $('
' + html + '
');

        $el.append($ol);

        this.$controlOl = $ol;
    },
    /**
     * 初始化title
     * @private
     */
    _initTitle: function () {
        // to do
        // var $el = this.$container;
    },
    /*
     * css 过渡
     * @private
     * @param {Object} dom  zepto object
     * @param {number} num - transition number
     */
    _transitionHandle: function (dom, num) {

        var opts = this.options;
        dom.css({
            '-webkit-transition': 'all ' + num + 's ' + opts.transitionType,
            'transition': 'all ' + num + 's ' + opts.transitionType
        });
    },
    /**
     * css 滚动
     * @private
     * @param  {Object} dom    zepto object
     * @param  {number} result translate number
     */
    _fnTranslate: function (dom, result) {

        var opts = this.options;

        if (opts.axisX) {
            dom.css({
                '-webkit-transform': 'translate3d(' + result + 'px,0,0)',
                'transform': 'translate3d(' + result + 'px,0,0)'
            });
        }
        else {
            dom.css({
                '-webkit-transform': 'translate3d(0,' + result + 'px,0)',
                'transform': 'translate3d(0,' + result + 'px,0)'
            });
        }
    },
    /**
     * 下一屏滚动
     * @private
     */
    _fnMoveNext: function () {
        this._index ++;
        this._fnMove();
        /*if(opts.lazyLoad){
            if(opts.continuousScroll){
                fnLazyLoad(_index+2);
            }else{
                fnLazyLoad(_index+1);
            }
        }*/
    },
    /**
     * 上一屏滚动
     * @private
     */
    _fnMovePrev: function () {
        this._index --;
        this._fnMove();
        // 第一次往右滚动懒加载图片
        /*if(firstMovePrev && opts.lazyLoad){
            var i = _liLength-1;
            for(i; i <= (_liLength+1); i++){
                fnLazyLoad(i);
            }
            firstMovePrev = false;
            return;
        }
        if(!firstMovePrev && opts.lazyLoad){
            fnLazyLoad(_index);
        }*/
    },
    /**
     * 自动滑动
     * @private
     */
    _fnAutoSwipe: function () {
        var that = this;
        var opts = this.options;

        if (opts.autoSwipe) {
            this.autoScroll = setInterval(function () {
                that._fnMoveNext();
            }, opts.speed);
        }
    },
    /**
     * [_fnMove description]
     * @private
     */
    _fnMove: function () {
        var that = this;
        var opts = this.options;
        // var _vars = this._vars;
        // var _liLength = this._liLength;

        if (opts.continuousScroll) {
            if (that._index >= that._liLength) {
                that._fnScroll(.3);
                that._index = 0;
                setTimeout(function () {
                    that._fnScroll(0);
                }, 300);
            }
            else if (that._index < 0) {
                that._fnScroll(.3);
                that._index = that._liLength - 1;
                setTimeout(function () {
                    that._fnScroll(0);
                }, 300);
            }
            else {
                that._fnScroll(.3);
            }
        }
        else {
            if (that._index >= that._liLength) {
                that._index = 0;
            }
            else if (that._index < 0) {
                that._index = that._liLength - 1;
            }
            that._fnScroll(.3);
        }

        that._setDotActive();

        // callback(_index);
    },
    /**
     * 滑动
     * @private
     * @param  {number} num num
     */
    _fnScroll: function (num) {
        var $ul = this.$ul;
        var _index = this._index;
        var _liWidth = this._liWidth;
        var _liHeight = this._liHeight;
        var opts = this.options;

        this._transitionHandle($ul, num);
        if (opts.axisX) {
            this._fnTranslate($ul, -_index * _liWidth);
        }
        else {
            this._fnTranslate($ul, -_index * _liHeight);
        }
    },
    /**
     * 设置圆点的状态
     * @private
     */
    _setDotActive: function () {
        this.$controlOl.find('li a').removeClass(NAMESPACE + 'slider-active');
        this.$controlOl.find('li').eq(this._index).find('a').addClass(NAMESPACE + 'slider-active');
    },
    /**
     * judge the device
     * @private
     * @return {Object} 事件
     */
    _device: function () {
        var hasTouch = !!('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch);
        var startEvt = hasTouch ? 'touchstart' : 'mousedown';
        var moveEvt = hasTouch ? 'touchmove' : 'mousemove';
        var endEvt = hasTouch ? 'touchend' : 'mouseup';
        return {
            hasTouch: hasTouch,
            startEvt: startEvt,
            moveEvt: moveEvt,
            endEvt: endEvt
        };
    },
    /**
     * 下一张幻灯片
     * @return {Object} 当前Zepto对象
     */
    next: function () {
        this._fnMoveNext();
        return this.$container;
    },
    /**
     * 上一张幻灯片
     * @return {Object} 当前Zepto对象
     */
    prev: function () {
        this._fnMovePrev();
        return this.$container;
    },
    /**
     * 暂停
     * @return {Object} 当前Zepto对象
     */
    paused: function () {
        clearInterval(this.autoScroll);
        return this.$container;
    },
    start: function () {
        clearInterval(this.autoScroll);
        this._fnAutoSwipe();
        return this.$container;
    }

});
<|endoftext|>