<commit_before>import React from 'react'
import { ShellScreen, Type, UserAvatar } from '@blockstack/ui'
import PropTypes from 'prop-types'

const Success = ({
  finish,
  app,
  goToRecovery,
  username,
  subdomainSuffix,
  id,
  ...rest
}) => {
  const user = {
    username,
    id,
    suffix: subdomainSuffix
  }

  const props = {
    headerLabel: 'Welcome to the New Internet',
    title: {
      icon: <UserAvatar {...user} camera />,
      children: <React.Fragment>{user.username}</React.Fragment>,
      variant: 'h2',
      subtitle: {
        light: true,
        children: (
          <React.Fragment>
            {user.username}.{user.suffix}
          </React.Fragment>
        )
      }
    },

    content: {
      grow: 1,
      children: (
        <React.Fragment>
          <Type.p>
            Your ID is ready and secure, but you must record your{' '}
            Secret Recovery Key to add new devices or to
            recover this account.
          </Type.p>
        </React.Fragment>
      )
    },
    actions: {
      items: [
        {
          label: (
            <React.Fragment>
              Go to {app.name ? app.name : 'Blockstack'}
            </React.Fragment>
          ),
          primary: true,
          onClick: () => finish()
        },
        {
          label: 'View Secret Recovery Key',
          onClick: () => goToRecovery()
        }
      ]
    }
  }
  return <ShellScreen {...rest} {...props} />
}

Success.propTypes = {
  finish: PropTypes.func.isRequired,
  goToRecovery: PropTypes.func.isRequired,
  username: PropTypes.string.isRequired,
  subdomainSuffix: PropTypes.string.isRequired,
  id: PropTypes.string.isRequired,
  app: PropTypes.object
}

export default Success
<commit_msg>fix conditional for label<commit_after>import React from 'react'
import { ShellScreen, Type, UserAvatar } from '@blockstack/ui'
import PropTypes from 'prop-types'

const Success = ({
  finish,
  app,
  goToRecovery,
  username,
  subdomainSuffix,
  id,
  ...rest
}) => {
  const user = {
    username,
    id,
    suffix: subdomainSuffix
  }

  const props = {
    headerLabel: 'Welcome to the New Internet',
    title: {
      icon: <UserAvatar {...user} camera />,
      children: <React.Fragment>{user.username}</React.Fragment>,
      variant: 'h2',
      subtitle: {
        light: true,
        children: (
          <React.Fragment>
            {user.username}.{user.suffix}
          </React.Fragment>
        )
      }
    },

    content: {
      grow: 1,
      children: (
        <React.Fragment>
          <Type.p>
            Your ID is ready and secure, but you must record your{' '}
            Secret Recovery Key to add new devices or to
            recover this account.
          </Type.p>
        </React.Fragment>
      )
    },
    actions: {
      items: [
        {
          label: (
            <React.Fragment>
              Go to {app && app.name ? app.name : 'Blockstack'}
            </React.Fragment>
          ),
          primary: true,
          onClick: () => finish()
        },
        {
          label: 'View Secret Recovery Key',
          onClick: () => goToRecovery()
        }
      ]
    }
  }
  return <ShellScreen {...rest} {...props} />
}

Success.propTypes = {
  finish: PropTypes.func.isRequired,
  goToRecovery: PropTypes.func.isRequired,
  username: PropTypes.string.isRequired,
  subdomainSuffix: PropTypes.string.isRequired,
  id: PropTypes.string.isRequired,
  app: PropTypes.object
}

export default Success
<|endoftext|>


<commit_before>var ClientApp = require("../lib/app"),
    path = require("path");

// Set up the app

var exampleApp = new ClientApp({
    name: "Example",
    version: "0.1.0",
    hostname: "example.localhost",
    port: 8080,
    views: path.join(__dirname, "views")
});

// Add another view

exampleApp.get("/about", function(req, res, next) {
    res.render("about", {title: "About"});
});

exampleApp.run(function(err) {
});

<commit_msg>Listen on port 80<commit_after>var ClientApp = require("../lib/app"),
    path = require("path");

// Set up the app

var exampleApp = new ClientApp({
    name: "Example",
    version: "0.1.0",
    hostname: "example.localhost",
    port: 80,
    views: path.join(__dirname, "views")
});

// Add another view

exampleApp.get("/about", function(req, res, next) {
    res.render("about", {title: "About"});
});

exampleApp.run(function(err) {
});

<|endoftext|>


<commit_before>angular.module('Gistter.services', []).factory('twitterService', function($q) {

    var authorizationResult = false;

    return {
        initialize: function() {
            //initialize OAuth.io with public key of the application
            OAuth.initialize('aPN6TnBWF7isEd2atAUftS3UbOg', {
                cache: true
            });
            //try to create an authorization result when the page loads,
            // this means a returning user won't have to click the twitter button again
            authorizationResult = OAuth.create("twitter");
        },
        isReady: function() {
            return (authorizationResult);
        },
        connectTwitter: function() {
            var deferred = $q.defer();
            OAuth.popup("twitter", {
                cache: true
            }, function(error, result) {
                // cache means to execute the callback if the tokens are already present
                if (!error) {
                    authorizationResult = result;
                    deferred.resolve();
                } else {
                    //do something if there's an error

                }
            });
            return deferred.promise;
        },
        clearCache: function() {
            OAuth.clearCache('twitter');
            authorizationResult = false;
        },
        getLatestTweets: function(maxId) {
            //create a deferred object using Angular's $q service
            var deferred = $q.defer();
            var url = '/1.1/statuses/home_timeline.json';
            var count = 200; //defaults at 20, can go up to 200
            if (maxId) {
                url += '?max_id=' + maxId + '?count=' + count;
            }
            var promise = authorizationResult.get(url).done(function(data) {
                // https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
                // when the data is retrieved resolve the deferred object
                deferred.resolve(data);
            }).fail(function(err) {
                deferred.reject(err);
            });
            //return the promise of the deferred object
            return deferred.promise;
        }
    };
});
<commit_msg>Add alert for failed twitter login<commit_after>angular.module('Gistter.services', []).factory('twitterService', function($q) {

    var authorizationResult = false;

    return {
        initialize: function() {
            //initialize OAuth.io with public key of the application
            OAuth.initialize('aPN6TnBWF7isEd2atAUftS3UbOg', {
                cache: true
            });
            //try to create an authorization result when the page loads,
            // this means a returning user won't have to click the twitter button again
            authorizationResult = OAuth.create("twitter");
        },
        isReady: function() {
            return (authorizationResult);
        },
        connectTwitter: function() {
            var deferred = $q.defer();
            OAuth.popup("twitter", {
                cache: true
            }, function(error, result) {
                // cache means to execute the callback if the tokens are already present
                if (!error) {
                    authorizationResult = result;
                    deferred.resolve();
                } else {
                    //do something if there's an error
                    alert("We weren't able to log you in.");
                }
            });
            return deferred.promise;
        },
        clearCache: function() {
            OAuth.clearCache('twitter');
            authorizationResult = false;
        },
        getLatestTweets: function(maxId) {
            //create a deferred object using Angular's $q service
            var deferred = $q.defer();
            var url = '/1.1/statuses/home_timeline.json';
            var count = 200; //defaults at 20, can go up to 200
            if (maxId) {
                url += '?max_id=' + maxId + '?count=' + count;
            }
            var promise = authorizationResult.get(url).done(function(data) {
                // https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
                // when the data is retrieved resolve the deferred object
                deferred.resolve(data);
            }).fail(function(err) {
                deferred.reject(err);
            });
            //return the promise of the deferred object
            return deferred.promise;
        }
    };
});
<|endoftext|>


<commit_before>module.exports = AnimatedBinding;
var animation = require('./util/animation');
var Binding = require('./binding');
var _super = Binding.prototype;

/**
 * Bindings which extend AnimatedBinding have the ability to animate elements that are added to the DOM and removed from
 * the DOM. This allows menus to slide open and closed, elements to fade in or drop down, and repeated items to appear
 * to move (if you get creative enough).
 *
 * The following 5 methods are helper DOM methods that allow registered bindings to work with CSS transitions for
 * animating elements. If an element has the `animate` attribute or a matching JavaScript method, these helper methods
 * will set a class on the node to trigger the animation and/or call the JavaScript methods to handle it.
 *
 * An animation may be either a CSS transition, a CSS animation, or a set of JavaScript methods that will be called.
 *
 * If using CSS, classes are added and removed from the element. When an element is inserted it will receive the `will-
 * animate-in` class before being added to the DOM, then it will receive the `animate-in` class immediately after being
 * added to the DOM, then both clases will be removed after the animation is complete. When an element is being removed
 * from the DOM it will receive the `will-animate-out` and `animate-out` classes, then the classes will be removed once
 * the animation is complete.
 *
 * If using JavaScript, methods must be defined  to animate the element there are 3 supported methods which can b
 *
 * TODO cache by class-name (Angular)? Only support javascript-style (Ember)? Add a `will-animate-in` and
 * `did-animate-in` etc.?
 * IF has any classes, add the `will-animate-in|out` and get computed duration. If none, return. Cache.
 * RULE is use unique class to define an animation. Or attribute `animate="fade"` will add the class?
 * `.fade.will-animate-in`, `.fade.animate-in`, `.fade.will-animate-out`, `.fade.animate-out`
 *
 * Events will be triggered on the elements named the same as the class names (e.g. `animate-in`) which may be listened
 * to in order to cancel an animation or respond to it.
 *
 * If the node has methods `animateIn(done)`, `animateOut(done)`, `animateMoveIn(done)`, or `animateMoveOut(done)`
 * defined on them then the helpers will allow an animation in JavaScript to be run and wait for the `done` function to
 * be called to know when the animation is complete.
 *
 * Be sure to actually have an animation defined for elements with the `animate` class/attribute because the helpers use
 * the `transitionend` and `animationend` events to know when the animation is finished, and if there is no animation
 * these events will never be triggered and the operation will never complete.
 */
function AnimatedBinding(properties) {
  var element = properties.node;
  var animate = element.getAttribute(properties.fragments.animateAttribute);
  var fragments = properties.fragments;

  if (animate !== null) {
    if (element.nodeName === 'TEMPLATE' || element.nodeName === 'SCRIPT') {
      throw new Error('Cannot animate multiple nodes in a template or script. Remove the [animate] attribute.');
    }

    setTimeout(function() {
      // Allow multiple bindings to animate by not removing until they have all been created
      element.removeAttribute(properties.fragments.animateAttribute);
    });

    this.animate = true;

    if (fragments.isBound('attribute', animate)) {
      // javascript animation
      this.animateExpression = fragments.codifyExpression('attribute', animate);
    } else {
      if (animate[0] === '.') {
        // class animation
        this.animateClassName = animate.slice(1);
      } else if (animate) {
        // registered animation
        var animateObject = fragments.getAnimation(animate);
        if (typeof animateObject === 'function') animateObject = new animateObject(this);
        this.animateObject = animateObject;
      }
    }
  }

  Binding.call(this, properties);
}

Binding.extend(AnimatedBinding, {
  init: function() {
    _super.init.call(this);

    if (this.animateExpression) {
      this.animateObserver = new this.Observer(this.animateExpression, function(value) {
        this.animateObject = value;
      }, this);
    }
  },

  bind: function(context) {
    if (this.context == context) {
      return;
    }
    _super.bind.call(this, context);

    if (this.animateObserver) {
      this.animateObserver.bind(context);
    }
  },

  unbind: function() {
    if (this.context === null) {
      return;
    }
    _super.unbind.call(this);

    if (this.animateObserver) {
      this.animateObserver.unbind();
    }
  },

  /**
   * Helper method to remove a node from the DOM, allowing for animations to occur. `callback` will be called when
   * finished.
   */
  animateOut: function(node, callback) {
    if (node.firstViewNode) node = node.firstViewNode;

    this.animateNode('out', node, function() {
      if (callback) callback.call(this);
    });
  },

  /**
   * Helper method to insert a node in the DOM before another node, allowing for animations to occur. `callback` will
   * be called when finished. If `before` is not provided then the animation will be run without inserting the node.
   */
  animateIn: function(node, callback) {
    if (node.firstViewNode) node = node.firstViewNode;
    this.animateNode('in', node, callback, this);
  },

  /**
   * Allow an element to use CSS3 transitions or animations to animate in or out of the page.
   */
  animateNode: function(direction, node, callback) {
    var animateObject, className, classAnimateName, classWillName,
        methodAnimateName, methodWillName, methodDidName, dir, _this = this;

    if (this.animateObject && typeof this.animateObject === 'object') {
      animateObject = this.animateObject;
      animateObject.fragments = this.fragments;
    } else if (this.animateClassName) {
      className = this.animateClassName;
    } else if (typeof this.animateObject === 'string') {
      className = this.animateObject;
    }

    classAnimateName = 'animate-' + direction;
    classWillName = 'will-animate-' + direction;
    dir = direction === 'in' ? 'In' : 'Out';
    methodAnimateName = 'animate' + dir;
    methodWillName = 'willAnimate' + dir;
    methodDidName = 'didAnimate' + dir;

    if (className) node.classList.add(className);
    node.classList.add(classWillName);

    if (animateObject) {
      animation.makeElementAnimatable(node);
      if (animateObject[methodWillName]) {
        animateObject[methodWillName](node);
      }
    }

    // trigger reflow
    node.offsetWidth = node.offsetWidth;

    node.classList.add(classAnimateName);
    node.classList.remove(classWillName);
    node.dispatchEvent(new Event('animatestart'));

    var whenDone = function() {
      if (animateObject && animateObject[methodDidName]) animateObject[methodDidName](node);
      if (callback) callback.call(_this);
      node.classList.remove(classAnimateName);
      if (className) node.classList.remove(className);
      node.dispatchEvent(new Event('animateend'));
    };

    if (animateObject && animateObject[methodAnimateName]) {
      animateObject[methodAnimateName](node, whenDone);
    } else {
      var duration = getDuration.call(this, node, direction);
      if (duration) {
        onAnimationEnd(node, duration, whenDone);
      } else {
        // Takes a couple frames to really take hold (at least on chrome)
        requestAnimationFrame(function() {
          requestAnimationFrame(whenDone);
        });
      }
    }
  }
});

var transitionDurationName = 'transitionDuration';
var transitionDelayName = 'transitionDelay';
var animationDurationName = 'animationDuration';
var animationDelayName = 'animationDelay';
var transitionEventName = 'transitionend';
var animationEventName = 'animationend';
var style = document.documentElement.style;

['webkit', 'moz', 'ms', 'o'].forEach(function(prefix) {
  if (style.transitionDuration === undefined && style[prefix + 'TransitionDuration']) {
    transitionDurationName = prefix + 'TransitionDuration';
    transitionDelayName = prefix + 'TransitionDelay';
    transitionEventName = prefix + 'transitionend';
  }

  if (style.animationDuration === undefined && style[prefix + 'AnimationDuration']) {
    animationDurationName = prefix + 'AnimationDuration';
    animationDelayName = prefix + 'AnimationDelay';
    animationEventName = prefix + 'animationend';
  }
});

function getDuration(node, direction) {
  var milliseconds = this.clonedFrom['__animationDuration' + direction];
  if (!milliseconds) {
    // Recalc if node was out of DOM before and had 0 duration, assume there is always SOME duration.
    var styles = window.getComputedStyle(node);
    var seconds = Math.max(parseFloat(styles[transitionDurationName] || 0) +
                           parseFloat(styles[transitionDelayName] || 0),
                           parseFloat(styles[animationDurationName] || 0) +
                           parseFloat(styles[animationDelayName] || 0));
    milliseconds = seconds * 1000 || 0;
    this.clonedFrom.__animationDuration__ = milliseconds;
  }
  return milliseconds;
}

function onAnimationEnd(node, duration, callback) {
  var onEnd = function() {
    node.removeEventListener(transitionEventName, onEnd);
    node.removeEventListener(animationEventName, onEnd);
    clearTimeout(timeout);
    callback();
  };

  // contingency plan
  var timeout = setTimeout(onEnd, duration + 10);

  node.addEventListener(transitionEventName, onEnd);
  node.addEventListener(animationEventName, onEnd);
}<commit_msg>Fix cache, update animate event names<commit_after>module.exports = AnimatedBinding;
var animation = require('./util/animation');
var Binding = require('./binding');
var _super = Binding.prototype;

/**
 * Bindings which extend AnimatedBinding have the ability to animate elements that are added to the DOM and removed from
 * the DOM. This allows menus to slide open and closed, elements to fade in or drop down, and repeated items to appear
 * to move (if you get creative enough).
 *
 * The following 5 methods are helper DOM methods that allow registered bindings to work with CSS transitions for
 * animating elements. If an element has the `animate` attribute or a matching JavaScript method, these helper methods
 * will set a class on the node to trigger the animation and/or call the JavaScript methods to handle it.
 *
 * An animation may be either a CSS transition, a CSS animation, or a set of JavaScript methods that will be called.
 *
 * If using CSS, classes are added and removed from the element. When an element is inserted it will receive the `will-
 * animate-in` class before being added to the DOM, then it will receive the `animate-in` class immediately after being
 * added to the DOM, then both clases will be removed after the animation is complete. When an element is being removed
 * from the DOM it will receive the `will-animate-out` and `animate-out` classes, then the classes will be removed once
 * the animation is complete.
 *
 * If using JavaScript, methods must be defined  to animate the element there are 3 supported methods which can b
 *
 * TODO cache by class-name (Angular)? Only support javascript-style (Ember)? Add a `will-animate-in` and
 * `did-animate-in` etc.?
 * IF has any classes, add the `will-animate-in|out` and get computed duration. If none, return. Cache.
 * RULE is use unique class to define an animation. Or attribute `animate="fade"` will add the class?
 * `.fade.will-animate-in`, `.fade.animate-in`, `.fade.will-animate-out`, `.fade.animate-out`
 *
 * Events will be triggered on the elements named the same as the class names (e.g. `animate-in`) which may be listened
 * to in order to cancel an animation or respond to it.
 *
 * If the node has methods `animateIn(done)`, `animateOut(done)`, `animateMoveIn(done)`, or `animateMoveOut(done)`
 * defined on them then the helpers will allow an animation in JavaScript to be run and wait for the `done` function to
 * be called to know when the animation is complete.
 *
 * Be sure to actually have an animation defined for elements with the `animate` class/attribute because the helpers use
 * the `transitionend` and `animationend` events to know when the animation is finished, and if there is no animation
 * these events will never be triggered and the operation will never complete.
 */
function AnimatedBinding(properties) {
  var element = properties.node;
  var animate = element.getAttribute(properties.fragments.animateAttribute);
  var fragments = properties.fragments;

  if (animate !== null) {
    if (element.nodeName === 'TEMPLATE' || element.nodeName === 'SCRIPT') {
      throw new Error('Cannot animate multiple nodes in a template or script. Remove the [animate] attribute.');
    }

    setTimeout(function() {
      // Allow multiple bindings to animate by not removing until they have all been created
      element.removeAttribute(properties.fragments.animateAttribute);
    });

    this.animate = true;

    if (fragments.isBound('attribute', animate)) {
      // javascript animation
      this.animateExpression = fragments.codifyExpression('attribute', animate);
    } else {
      if (animate[0] === '.') {
        // class animation
        this.animateClassName = animate.slice(1);
      } else if (animate) {
        // registered animation
        var animateObject = fragments.getAnimation(animate);
        if (typeof animateObject === 'function') animateObject = new animateObject(this);
        this.animateObject = animateObject;
      }
    }
  }

  Binding.call(this, properties);
}

Binding.extend(AnimatedBinding, {
  init: function() {
    _super.init.call(this);

    if (this.animateExpression) {
      this.animateObserver = new this.Observer(this.animateExpression, function(value) {
        this.animateObject = value;
      }, this);
    }
  },

  bind: function(context) {
    if (this.context == context) {
      return;
    }
    _super.bind.call(this, context);

    if (this.animateObserver) {
      this.animateObserver.bind(context);
    }
  },

  unbind: function() {
    if (this.context === null) {
      return;
    }
    _super.unbind.call(this);

    if (this.animateObserver) {
      this.animateObserver.unbind();
    }
  },

  /**
   * Helper method to remove a node from the DOM, allowing for animations to occur. `callback` will be called when
   * finished.
   */
  animateOut: function(node, callback) {
    if (node.firstViewNode) node = node.firstViewNode;

    this.animateNode('out', node, function() {
      if (callback) callback.call(this);
    });
  },

  /**
   * Helper method to insert a node in the DOM before another node, allowing for animations to occur. `callback` will
   * be called when finished. If `before` is not provided then the animation will be run without inserting the node.
   */
  animateIn: function(node, callback) {
    if (node.firstViewNode) node = node.firstViewNode;
    this.animateNode('in', node, callback, this);
  },

  /**
   * Allow an element to use CSS3 transitions or animations to animate in or out of the page.
   */
  animateNode: function(direction, node, callback) {
    var animateObject, className, classAnimateName, classWillName,
        methodAnimateName, methodWillName, methodDidName, dir, _this = this;

    if (this.animateObject && typeof this.animateObject === 'object') {
      animateObject = this.animateObject;
      animateObject.fragments = this.fragments;
    } else if (this.animateClassName) {
      className = this.animateClassName;
    } else if (typeof this.animateObject === 'string') {
      className = this.animateObject;
    }

    classAnimateName = 'animate-' + direction;
    classWillName = 'will-animate-' + direction;
    dir = direction === 'in' ? 'In' : 'Out';
    methodAnimateName = 'animate' + dir;
    methodWillName = 'willAnimate' + dir;
    methodDidName = 'didAnimate' + dir;

    if (className) node.classList.add(className);
    node.classList.add(classWillName);

    if (animateObject) {
      animation.makeElementAnimatable(node);
      if (animateObject[methodWillName]) {
        animateObject[methodWillName](node);
      }
    }

    // trigger reflow
    node.offsetWidth = node.offsetWidth;

    node.classList.add(classAnimateName);
    node.classList.remove(classWillName);
    node.dispatchEvent(new Event('animatestart' + direction));

    var whenDone = function() {
      if (animateObject && animateObject[methodDidName]) animateObject[methodDidName](node);
      if (callback) callback.call(_this);
      node.classList.remove(classAnimateName);
      if (className) node.classList.remove(className);
      node.dispatchEvent(new Event('animateend' + direction));
    };

    if (animateObject && animateObject[methodAnimateName]) {
      animateObject[methodAnimateName](node, whenDone);
    } else {
      var duration = getDuration.call(this, node, direction);
      if (duration) {
        onAnimationEnd(node, duration, whenDone);
      } else {
        // Takes a couple frames to really take hold (at least on chrome)
        requestAnimationFrame(function() {
          requestAnimationFrame(whenDone);
        });
      }
    }
  }
});

var transitionDurationName = 'transitionDuration';
var transitionDelayName = 'transitionDelay';
var animationDurationName = 'animationDuration';
var animationDelayName = 'animationDelay';
var transitionEventName = 'transitionend';
var animationEventName = 'animationend';
var style = document.documentElement.style;

['webkit', 'moz', 'ms', 'o'].forEach(function(prefix) {
  if (style.transitionDuration === undefined && style[prefix + 'TransitionDuration']) {
    transitionDurationName = prefix + 'TransitionDuration';
    transitionDelayName = prefix + 'TransitionDelay';
    transitionEventName = prefix + 'transitionend';
  }

  if (style.animationDuration === undefined && style[prefix + 'AnimationDuration']) {
    animationDurationName = prefix + 'AnimationDuration';
    animationDelayName = prefix + 'AnimationDelay';
    animationEventName = prefix + 'animationend';
  }
});

function getDuration(node, direction) {
  var milliseconds = this.clonedFrom['__animationDuration' + direction];
  if (!milliseconds) {
    // Recalc if node was out of DOM before and had 0 duration, assume there is always SOME duration.
    var styles = window.getComputedStyle(node);
    var seconds = Math.max(parseFloat(styles[transitionDurationName] || 0) +
                           parseFloat(styles[transitionDelayName] || 0),
                           parseFloat(styles[animationDurationName] || 0) +
                           parseFloat(styles[animationDelayName] || 0));
    milliseconds = seconds * 1000 || 0;
    this.clonedFrom['__animationDuration' + direction] = milliseconds;
  }
  return milliseconds;
}

function onAnimationEnd(node, duration, callback) {
  var onEnd = function() {
    node.removeEventListener(transitionEventName, onEnd);
    node.removeEventListener(animationEventName, onEnd);
    clearTimeout(timeout);
    callback();
  };

  // contingency plan
  var timeout = setTimeout(onEnd, duration + 10);

  node.addEventListener(transitionEventName, onEnd);
  node.addEventListener(animationEventName, onEnd);
}<|endoftext|>


<commit_before>import { resolve, join } from 'path'
import { createHash } from 'crypto'
import webpack from 'webpack'
import glob from 'glob-promise'
import WriteFilePlugin from 'write-file-webpack-plugin'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
import UnlinkFilePlugin from './plugins/unlink-file-plugin'
import PagesPlugin from './plugins/pages-plugin'
import DynamicChunksPlugin from './plugins/dynamic-chunks-plugin'
import CombineAssetsPlugin from './plugins/combine-assets-plugin'
import getConfig from '../config'
import * as babelCore from 'babel-core'
import findBabelConfig from './babel/find-config'
import rootModuleRelativePath from './root-module-relative-path'

const documentPage = join('pages', '_document.js')
const defaultPages = [
  '_error.js',
  '_document.js'
]
const nextPagesDir = join(__dirname, '..', '..', 'pages')
const nextNodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
const interpolateNames = new Map(defaultPages.map((p) => {
  return [join(nextPagesDir, p), `dist/pages/${p}`]
}))

const relativeResolve = rootModuleRelativePath(require)

export default async function createCompiler (dir, { dev = false, quiet = false, buildDir } = {}) {
  dir = resolve(dir)
  const config = getConfig(dir)
  const defaultEntries = dev ? [
    join(__dirname, '..', '..', 'client', 'webpack-hot-middleware-client'),
    join(__dirname, '..', '..', 'client', 'on-demand-entries-client')
  ] : []
  const mainJS = dev
    ? require.resolve('../../client/next-dev') : require.resolve('../../client/next')

  let totalPages

  const entry = async () => {
    const entries = {
      'main.js': [
        ...defaultEntries,
        mainJS
      ]
    }

    const pages = await glob('pages/**/*.js', { cwd: dir })
    const devPages = pages.filter((p) => p === 'pages/_document.js' || p === 'pages/_error.js')

    // In the dev environment, on-demand-entry-handler will take care of
    // managing pages.
    if (dev) {
      for (const p of devPages) {
        entries[join('bundles', p)] = [`./${p}?entry`]
      }
    } else {
      for (const p of pages) {
        entries[join('bundles', p)] = [`./${p}?entry`]
      }
    }

    for (const p of defaultPages) {
      const entryName = join('bundles', 'pages', p)
      if (!entries[entryName]) {
        entries[entryName] = [join(nextPagesDir, p) + '?entry']
      }
    }

    totalPages = pages.filter((p) => p !== documentPage).length

    return entries
  }

  const plugins = [
    new webpack.IgnorePlugin(/(precomputed)/, /node_modules.+(elliptic)/),
    new webpack.LoaderOptionsPlugin({
      options: {
        context: dir,
        customInterpolateName (url, name, opts) {
          return interpolateNames.get(this.resourcePath) || url
        }
      }
    }),
    new WriteFilePlugin({
      exitOnErrors: false,
      log: false,
      // required not to cache removed files
      useHashIndex: false
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'commons',
      filename: 'commons.js',
      minChunks (module, count) {
        // In the dev we use on-demand-entries.
        // So, it makes no sense to use commonChunks based on the minChunks count.
        // Instead, we move all the code in node_modules into this chunk.
        // With that, we could gain better performance for page-rebuild process.
        if (dev) {
          return module.context && module.context.indexOf('node_modules') >= 0
        }

        // Move modules used in at-least 1/2 of the total pages into commons.
        return count >= totalPages * 0.5
      }
    }),
    // This chunk contains all the webpack related code. So, all the changes
    // related to that happens to this chunk.
    // It won't touch commons.js and that gives us much better re-build perf.
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      filename: 'manifest.js'
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production')
    }),
    new PagesPlugin(),
    new DynamicChunksPlugin(),
    new CaseSensitivePathPlugin()
  ]

  if (dev) {
    plugins.push(
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoEmitOnErrorsPlugin(),
      new UnlinkFilePlugin()
    )
    if (!quiet) {
      plugins.push(new FriendlyErrorsWebpackPlugin())
    }
  } else {
    plugins.push(
      new CombineAssetsPlugin({
        input: ['manifest.js', 'commons.js', 'main.js'],
        output: 'app.js'
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: { warnings: false },
        sourceMap: false
      })
    )
  }

  const nodePathList = (process.env.NODE_PATH || '')
    .split(process.platform === 'win32' ? ';' : ':')
    .filter((p) => !!p)

  const mainBabelOptions = {
    cacheDirectory: true,
    presets: []
  }

  const externalBabelConfig = findBabelConfig(dir)
  if (externalBabelConfig) {
    console.log(`> Using external babel configuration`)
    console.log(`> location: "${externalBabelConfig.loc}"`)
    // It's possible to turn off babelrc support via babelrc itself.
    // In that case, we should add our default preset.
    // That's why we need to do this.
    const { options } = externalBabelConfig
    mainBabelOptions.babelrc = options.babelrc !== false
  } else {
    mainBabelOptions.babelrc = false
  }

  // Add our default preset if the no "babelrc" found.
  if (!mainBabelOptions.babelrc) {
    mainBabelOptions.presets.push(require.resolve('./babel/preset'))
  }

  const rules = (dev ? [{
    test: /\.js(\?[^?]*)?$/,
    loader: 'hot-self-accept-loader',
    include: [
      join(dir, 'pages'),
      nextPagesDir
    ]
  }, {
    test: /\.js(\?[^?]*)?$/,
    loader: 'react-hot-loader/webpack',
    exclude: /node_modules/
  }] : [])
  .concat([{
    test: /\.json$/,
    loader: 'json-loader'
  }, {
    test: /\.(js|json)(\?[^?]*)?$/,
    loader: 'emit-file-loader',
    include: [dir, nextPagesDir],
    exclude (str) {
      return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
    },
    options: {
      name: 'dist/[path][name].[ext]',
      // By default, our babel config does not transpile ES2015 module syntax because
      // webpack knows how to handle them. (That's how it can do tree-shaking)
      // But Node.js doesn't know how to handle them. So, we have to transpile them here.
      transform ({ content, sourceMap, interpolatedName }) {
        // Only handle .js files
        if (!(/\.js$/.test(interpolatedName))) {
          return { content, sourceMap }
        }

        const transpiled = babelCore.transform(content, {
          babelrc: false,
          sourceMaps: dev ? 'both' : false,
          // Here we need to resolve all modules to the absolute paths.
          // Earlier we did it with the babel-preset.
          // But since we don't transpile ES2015 in the preset this is not resolving.
          // That's why we need to do it here.
          // See more: https://github.com/zeit/next.js/issues/951
          plugins: [
            [require.resolve('babel-plugin-transform-es2015-modules-commonjs')],
            [
              require.resolve('babel-plugin-module-resolver'),
              {
                alias: {
                  'babel-runtime': relativeResolve('babel-runtime/package'),
                  'next/link': relativeResolve('../../lib/link'),
                  'next/prefetch': relativeResolve('../../lib/prefetch'),
                  'next/css': relativeResolve('../../lib/css'),
                  'next/dynamic': relativeResolve('../../lib/dynamic'),
                  'next/head': relativeResolve('../../lib/head'),
                  'next/document': relativeResolve('../../server/document'),
                  'next/router': relativeResolve('../../lib/router'),
                  'next/error': relativeResolve('../../lib/error'),
                  'styled-jsx/style': relativeResolve('styled-jsx/style')
                }
              }
            ]
          ],
          inputSourceMap: sourceMap
        })

        return {
          content: transpiled.code,
          sourceMap: transpiled.map
        }
      }
    }
  }, {
    loader: 'babel-loader',
    include: nextPagesDir,
    exclude (str) {
      return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
    },
    options: {
      babelrc: false,
      cacheDirectory: true,
      presets: [require.resolve('./babel/preset')]
    }
  }, {
    test: /\.js(\?[^?]*)?$/,
    loader: 'babel-loader',
    include: [dir],
    exclude (str) {
      return /node_modules/.test(str)
    },
    options: mainBabelOptions
  }])

  let webpackConfig = {
    context: dir,
    entry,
    output: {
      path: buildDir ? join(buildDir, '.next') : join(dir, config.distDir),
      filename: '[name]',
      libraryTarget: 'commonjs2',
      publicPath: '/_next/webpack/',
      strictModuleExceptionHandling: true,
      devtoolModuleFilenameTemplate ({ resourcePath }) {
        const hash = createHash('sha1')
        hash.update(Date.now() + '')
        const id = hash.digest('hex').slice(0, 7)

        // append hash id for cache busting
        return `webpack:///${resourcePath}?${id}`
      },
      // This saves chunks with the name given via require.ensure()
      chunkFilename: '[name]'
    },
    resolve: {
      modules: [
        nextNodeModulesDir,
        'node_modules',
        ...nodePathList
      ]
    },
    resolveLoader: {
      modules: [
        nextNodeModulesDir,
        'node_modules',
        join(__dirname, 'loaders'),
        ...nodePathList
      ]
    },
    plugins,
    module: {
      rules
    },
    devtool: dev ? 'cheap-module-inline-source-map' : false,
    performance: { hints: false }
  }

  if (!dev) {
    // We do this to use the minified version of React in production.
    // This will significant file size redution when turned off uglifyjs.
    webpackConfig.resolve.alias = {
      'react': require.resolve('react/dist/react.min.js'),
      'react-dom': require.resolve('react-dom/dist/react-dom.min.js')
    }
  }

  if (config.webpack) {
    console.log(`> Using "webpack" config function defined in ${config.configOrigin}.`)
    webpackConfig = await config.webpack(webpackConfig, { dev })
  }
  return webpack(webpackConfig)
}
<commit_msg>Fixed an issue where two-page sites didn't split code. (#2329)<commit_after>import { resolve, join } from 'path'
import { createHash } from 'crypto'
import webpack from 'webpack'
import glob from 'glob-promise'
import WriteFilePlugin from 'write-file-webpack-plugin'
import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin'
import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin'
import UnlinkFilePlugin from './plugins/unlink-file-plugin'
import PagesPlugin from './plugins/pages-plugin'
import DynamicChunksPlugin from './plugins/dynamic-chunks-plugin'
import CombineAssetsPlugin from './plugins/combine-assets-plugin'
import getConfig from '../config'
import * as babelCore from 'babel-core'
import findBabelConfig from './babel/find-config'
import rootModuleRelativePath from './root-module-relative-path'

const documentPage = join('pages', '_document.js')
const defaultPages = [
  '_error.js',
  '_document.js'
]
const nextPagesDir = join(__dirname, '..', '..', 'pages')
const nextNodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
const interpolateNames = new Map(defaultPages.map((p) => {
  return [join(nextPagesDir, p), `dist/pages/${p}`]
}))

const relativeResolve = rootModuleRelativePath(require)

export default async function createCompiler (dir, { dev = false, quiet = false, buildDir } = {}) {
  dir = resolve(dir)
  const config = getConfig(dir)
  const defaultEntries = dev ? [
    join(__dirname, '..', '..', 'client', 'webpack-hot-middleware-client'),
    join(__dirname, '..', '..', 'client', 'on-demand-entries-client')
  ] : []
  const mainJS = dev
    ? require.resolve('../../client/next-dev') : require.resolve('../../client/next')

  let totalPages

  const entry = async () => {
    const entries = {
      'main.js': [
        ...defaultEntries,
        mainJS
      ]
    }

    const pages = await glob('pages/**/*.js', { cwd: dir })
    const devPages = pages.filter((p) => p === 'pages/_document.js' || p === 'pages/_error.js')

    // In the dev environment, on-demand-entry-handler will take care of
    // managing pages.
    if (dev) {
      for (const p of devPages) {
        entries[join('bundles', p)] = [`./${p}?entry`]
      }
    } else {
      for (const p of pages) {
        entries[join('bundles', p)] = [`./${p}?entry`]
      }
    }

    for (const p of defaultPages) {
      const entryName = join('bundles', 'pages', p)
      if (!entries[entryName]) {
        entries[entryName] = [join(nextPagesDir, p) + '?entry']
      }
    }

    totalPages = pages.filter((p) => p !== documentPage).length

    return entries
  }

  const plugins = [
    new webpack.IgnorePlugin(/(precomputed)/, /node_modules.+(elliptic)/),
    new webpack.LoaderOptionsPlugin({
      options: {
        context: dir,
        customInterpolateName (url, name, opts) {
          return interpolateNames.get(this.resourcePath) || url
        }
      }
    }),
    new WriteFilePlugin({
      exitOnErrors: false,
      log: false,
      // required not to cache removed files
      useHashIndex: false
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'commons',
      filename: 'commons.js',
      minChunks (module, count) {
        // In the dev we use on-demand-entries.
        // So, it makes no sense to use commonChunks based on the minChunks count.
        // Instead, we move all the code in node_modules into this chunk.
        // With that, we could gain better performance for page-rebuild process.
        if (dev) {
          return module.context && module.context.indexOf('node_modules') >= 0
        }

        // If there are one or two pages, only move modules to common if they are
        // used in all of the pages. Otherwise, move modules used in at-least
        // 1/2 of the total pages into commons.
        if (totalPages <= 2) {
          return count === totalPages
        }
        return count >= totalPages * 0.5
      }
    }),
    // This chunk contains all the webpack related code. So, all the changes
    // related to that happens to this chunk.
    // It won't touch commons.js and that gives us much better re-build perf.
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      filename: 'manifest.js'
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production')
    }),
    new PagesPlugin(),
    new DynamicChunksPlugin(),
    new CaseSensitivePathPlugin()
  ]

  if (dev) {
    plugins.push(
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoEmitOnErrorsPlugin(),
      new UnlinkFilePlugin()
    )
    if (!quiet) {
      plugins.push(new FriendlyErrorsWebpackPlugin())
    }
  } else {
    plugins.push(
      new CombineAssetsPlugin({
        input: ['manifest.js', 'commons.js', 'main.js'],
        output: 'app.js'
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: { warnings: false },
        sourceMap: false
      })
    )
  }

  const nodePathList = (process.env.NODE_PATH || '')
    .split(process.platform === 'win32' ? ';' : ':')
    .filter((p) => !!p)

  const mainBabelOptions = {
    cacheDirectory: true,
    presets: []
  }

  const externalBabelConfig = findBabelConfig(dir)
  if (externalBabelConfig) {
    console.log(`> Using external babel configuration`)
    console.log(`> location: "${externalBabelConfig.loc}"`)
    // It's possible to turn off babelrc support via babelrc itself.
    // In that case, we should add our default preset.
    // That's why we need to do this.
    const { options } = externalBabelConfig
    mainBabelOptions.babelrc = options.babelrc !== false
  } else {
    mainBabelOptions.babelrc = false
  }

  // Add our default preset if the no "babelrc" found.
  if (!mainBabelOptions.babelrc) {
    mainBabelOptions.presets.push(require.resolve('./babel/preset'))
  }

  const rules = (dev ? [{
    test: /\.js(\?[^?]*)?$/,
    loader: 'hot-self-accept-loader',
    include: [
      join(dir, 'pages'),
      nextPagesDir
    ]
  }, {
    test: /\.js(\?[^?]*)?$/,
    loader: 'react-hot-loader/webpack',
    exclude: /node_modules/
  }] : [])
  .concat([{
    test: /\.json$/,
    loader: 'json-loader'
  }, {
    test: /\.(js|json)(\?[^?]*)?$/,
    loader: 'emit-file-loader',
    include: [dir, nextPagesDir],
    exclude (str) {
      return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
    },
    options: {
      name: 'dist/[path][name].[ext]',
      // By default, our babel config does not transpile ES2015 module syntax because
      // webpack knows how to handle them. (That's how it can do tree-shaking)
      // But Node.js doesn't know how to handle them. So, we have to transpile them here.
      transform ({ content, sourceMap, interpolatedName }) {
        // Only handle .js files
        if (!(/\.js$/.test(interpolatedName))) {
          return { content, sourceMap }
        }

        const transpiled = babelCore.transform(content, {
          babelrc: false,
          sourceMaps: dev ? 'both' : false,
          // Here we need to resolve all modules to the absolute paths.
          // Earlier we did it with the babel-preset.
          // But since we don't transpile ES2015 in the preset this is not resolving.
          // That's why we need to do it here.
          // See more: https://github.com/zeit/next.js/issues/951
          plugins: [
            [require.resolve('babel-plugin-transform-es2015-modules-commonjs')],
            [
              require.resolve('babel-plugin-module-resolver'),
              {
                alias: {
                  'babel-runtime': relativeResolve('babel-runtime/package'),
                  'next/link': relativeResolve('../../lib/link'),
                  'next/prefetch': relativeResolve('../../lib/prefetch'),
                  'next/css': relativeResolve('../../lib/css'),
                  'next/dynamic': relativeResolve('../../lib/dynamic'),
                  'next/head': relativeResolve('../../lib/head'),
                  'next/document': relativeResolve('../../server/document'),
                  'next/router': relativeResolve('../../lib/router'),
                  'next/error': relativeResolve('../../lib/error'),
                  'styled-jsx/style': relativeResolve('styled-jsx/style')
                }
              }
            ]
          ],
          inputSourceMap: sourceMap
        })

        return {
          content: transpiled.code,
          sourceMap: transpiled.map
        }
      }
    }
  }, {
    loader: 'babel-loader',
    include: nextPagesDir,
    exclude (str) {
      return /node_modules/.test(str) && str.indexOf(nextPagesDir) !== 0
    },
    options: {
      babelrc: false,
      cacheDirectory: true,
      presets: [require.resolve('./babel/preset')]
    }
  }, {
    test: /\.js(\?[^?]*)?$/,
    loader: 'babel-loader',
    include: [dir],
    exclude (str) {
      return /node_modules/.test(str)
    },
    options: mainBabelOptions
  }])

  let webpackConfig = {
    context: dir,
    entry,
    output: {
      path: buildDir ? join(buildDir, '.next') : join(dir, config.distDir),
      filename: '[name]',
      libraryTarget: 'commonjs2',
      publicPath: '/_next/webpack/',
      strictModuleExceptionHandling: true,
      devtoolModuleFilenameTemplate ({ resourcePath }) {
        const hash = createHash('sha1')
        hash.update(Date.now() + '')
        const id = hash.digest('hex').slice(0, 7)

        // append hash id for cache busting
        return `webpack:///${resourcePath}?${id}`
      },
      // This saves chunks with the name given via require.ensure()
      chunkFilename: '[name]'
    },
    resolve: {
      modules: [
        nextNodeModulesDir,
        'node_modules',
        ...nodePathList
      ]
    },
    resolveLoader: {
      modules: [
        nextNodeModulesDir,
        'node_modules',
        join(__dirname, 'loaders'),
        ...nodePathList
      ]
    },
    plugins,
    module: {
      rules
    },
    devtool: dev ? 'cheap-module-inline-source-map' : false,
    performance: { hints: false }
  }

  if (!dev) {
    // We do this to use the minified version of React in production.
    // This will significant file size redution when turned off uglifyjs.
    webpackConfig.resolve.alias = {
      'react': require.resolve('react/dist/react.min.js'),
      'react-dom': require.resolve('react-dom/dist/react-dom.min.js')
    }
  }

  if (config.webpack) {
    console.log(`> Using "webpack" config function defined in ${config.configOrigin}.`)
    webpackConfig = await config.webpack(webpackConfig, { dev })
  }
  return webpack(webpackConfig)
}
<|endoftext|>


<commit_before>/*
    jquery.jasonlazyload.js
    2015-02-03
    v1.0.0

    The MIT License (MIT)

    Copyright (c) 2015 Joe Ridino

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

    JSONLazyLoad is a jQuery plugin that loads dynamic content from a server when
    the user scrolls or resizes the window.

    This plugin will handle the browser events and call the web service for
    items, then hand them off for rendering.  See the default_settings object
    below for detailed information about the possible settings.

    Usage:
    jQuery('#my-element').jsonlazyload(my_options);
*/

/*jslint browser: true, unparam: true */
/*global jQuery: false */

(function ($) {
    'use strict';

    var JSONLazyLoad = function (element, options) {
        var default_settings = {
            // Behavior-type settings.
            behavior: {
                // If true, does automatic Ajax calls to ensure there's always
                // a scrollbar present in the scroll target.
                ensure_scrollbar: false,

                // The number of items to fetch from the server.
                fetch_amount: 10,

                // If true, fetches items immediately after instantiating.
                fetch_immediate: true,

                // The initial offset for pagination.  Normally this will be 0.
                initial_offset: 0,

                // After this many milliseconds elements.loader will be shown.
                loader_delay: 100,

                // The number of pixels from the bottom of the scroll target
                // that triggers a new Ajax request.  Normally, this would be 0
                // if you want to have new items load when the scrollbar reaches
                // its bottom, but due to some browser quirks setting this to a
                // value of 1 is more reliable.
                scroll_threshold: 1
            },
            // Files-based web service.
            files: {
                // The file pattern to use when retrieving the next file.
                // e.g. If you specify "data[n].json" then the "[n]" will be
                // replaced with the current page number.  So if the initial
                // offset is 0, the first request will be to "data1.json".
                pattern: '',

                // The total number of files that exist on the server.
                num_files: 0
            },
            // jQuery elements.
            elements: {
                // The jQuery element that scroll events are attached to.
                scroll_target: $(window),

                // The jQuery element that will be displayed when N
                // (behavior.loader_delay) number of milliseconds have elapsed
                // since the start of an Ajax request.
                loader: null
            },
            // Callbacks and other settings pertaining to the Ajax response.
            results: {
                // Function that gets called right before an Ajax request.
                // Signature: function (lazy_loader, limit)
                before_callback: null,

                // Function that gets called when an Ajax request fails.
                // Signature: function (lazy_loader)
                fail_callback: null,

                // Function that gets called when an Ajax request succeeds.
                // Signature: function (lazy_loader, data)
                item_callback: null,

                // Specifies an object key inside the Ajax response that holds
                // the items array.  When null, it is assumed the items array
                // is the response itself.
                item_key: null
            },
            // Web service settings.
            ws: {
                // Content-type string suitable for jQuery.ajax() call.
                data_type: 'json',

                // The name of the limit parameter for the web service.
                limit_param: 'limit',

                // The name of the offset paraemter for the web service.
                offset_param: 'offset',

                // The URL to the web service.  Can have additional query
                // parameters outside of limit and offset if needed.
                url: ''
            }
        },
            added_event_handlers = false,
            ajaxing = false,
            check_add_event_handlers,
            files_offset = 0,
            has_scrollbar,
            init,
            jquery_element = $(element),
            loaded_all = false,
            loader_hide,
            loader_show,
            loader_timeout_id,
            offset = 0,
            prev_scroll_top = 0,
            promise = null,
            promise_always,
            promise_done,
            promise_fail,
            resize_check,
            scroll_check,
            self = this,
            settings = null,
            tear_down;

        /**
         * Returns the jQuery element that is the lazy load target.
         *
         * @return
         *     The jQuery element that is the lazy load target.
         */
        this.target = function () {
            return jquery_element;
        };

        /**
         * Fetches items from the server given the amount of items to fetch.
         *
         * This function is aware of the items that we have fetched previously
         * by way of the offset variable.  The fetch will start from the offset
         * and grab as many items as it can, up to the limit specified.
         *
         * Also, this function will trigger your callbacks when the Ajax request
         * finishes in addition to showing the loader if the Ajax request is
         * taking too long.
         *
         * @param limit
         *     The number of items to fetch.
         */
        this.fetch_items = function (limit) {
            if (ajaxing || loaded_all) {
                return;
            }

            var params = {}, url;
            if (settings.files.pattern) {
                url = settings.files.pattern.replace('[n]', files_offset + 1);
            } else {
                url = settings.ws.url;
                params[settings.ws.limit_param] = limit;
                params[settings.ws.offset_param] = offset;
            }

            if (settings.results.before_callback) {
                settings.results.before_callback(this, limit);
            }

            if (settings.elements.loader) {
                loader_timeout_id = setTimeout(loader_show, settings.behavior.loader_delay);
            }
            ajaxing = true;
            promise = $.ajax({
                url: url,
                data: params,
                dataType: settings.ws.data_type
            });

            promise.done(promise_done);
            promise.fail(promise_fail);
            promise.always(promise_always);
        };

        /**
         * If the scroll target does not have a vertical scrollbar more items
         * are fetched from the server to try to obtain one.
         */
        this.ensure_scrollbar = function () {
            if (!has_scrollbar()) {
                self.fetch_items(settings.behavior.fetch_amount);
            }
        };

        /**
         * Removes jQuery events and hides the loader if it's visible.
         */
        this.destroy = function () {
            tear_down();
        };

        /**
         * Removes jQuery events and hides the loader if it's visible.
         */
        tear_down = function () {
            settings.elements.scroll_target.off('scroll', scroll_check);
            $(window).off('resize', resize_check);
            if (settings.elements.loader) {
                loader_hide();
            }
        };

        /**
         * Returns true if the scroll target has a vertical scrollbar.
         *
         * @return
         *     Returns true if the scroll target has a vertical scrollbar.
         */
        has_scrollbar = function () {
            var client_height, scroll_height;
            client_height = settings.elements.scroll_target.innerHeight();
            if ($.isWindow(settings.elements.scroll_target[0])) {
                scroll_height = $(document).height();
            } else {
                scroll_height = settings.elements.scroll_target[0].scrollHeight;
            }

            return scroll_height > client_height;
        };

        /**
         * Fetches more items when the user is scrolling the scroll target and
         * the scroll position is near the bottom of the target.  The threshold
         * is also taken into account.
         */
        scroll_check = function () {
            var threshold, needs_ajax, new_scroll_top, scroll_height;
            new_scroll_top = settings.elements.scroll_target.scrollTop();
            if (new_scroll_top > prev_scroll_top && !(ajaxing || loaded_all)) {
                if ($.isWindow(settings.elements.scroll_target[0])) {
                    scroll_height = $(document).height();
                } else {
                    scroll_height = settings.elements.scroll_target[0].scrollHeight;
                }
                threshold = scroll_height - settings.behavior.scroll_threshold;
                needs_ajax = (new_scroll_top + settings.elements.scroll_target.innerHeight()) >= threshold;
                if (needs_ajax) {
                    self.fetch_items(settings.behavior.fetch_amount);
                }
            }

            prev_scroll_top = new_scroll_top;
        };

        /**
         * Calls ensure_scrollbar() to obtain a vertical scrollbar when the
         * window is resized.
         */
        resize_check = function () {
            if (ajaxing || loaded_all) {
                return;
            }

            if (settings.behavior.ensure_scrollbar) {
                self.ensure_scrollbar();
            }
        };

        /**
         * Adds 'scroll' and 'resize' event handlers if they have not been
         * added before.
         */
        check_add_event_handlers = function () {
            if (!added_event_handlers) {
                settings.elements.scroll_target.on('scroll', scroll_check);
                $(window).on('resize', resize_check);
                added_event_handlers = true;
            }
        };

        /**
         * Called when the Ajax request has a successful response.
         *
         * The item_callback is triggered to notify the client of new items.
         *
         * @param data
         *     The data from the server, which has already been transformed by
         *     jQuery into a JavaScript object based on the content type
         *     specified.
         */
        promise_done = function (data) {
            if (settings.results.item_key) {
                data = data[settings.results.item_key];
            }

            if (!$.isArray(data)) {
                return;
            }

            offset += data.length;
            if (settings.files.pattern) {
                files_offset += 1;
            }
            if (data.length) {
                settings.results.item_callback(self, data);
            } else {
                loaded_all = true;
                tear_down();
            }

            check_add_event_handlers();
        };

        /**
         * Called when the Ajax request fails.
         *
         * The fail_callback is triggered to notify the client of the failure.
         */
        promise_fail = function () {
            if (settings.results.fail_callback) {
                settings.results.fail_callback(self);
            }
        };

        /**
         * Called when the Ajax request either fails or succeeds.
         *
         * The main purpose of this function is to reset our flags, hide the
         * loader and ensure we have a scrollbar (which in turn calls the server
         * again if needed).
         *
         * @param data
         *     The data from the server, which has already been transformed by
         *     jQuery into a JavaScript object based on the content type
         *     specified.
         * @param status
         *     Status string from jQuery.ajax.  Normally, this is 'success'
         *     unless the ajax call fails.
         */
        promise_always = function (data, status) {
            ajaxing = false;

            if (settings.elements.loader) {
                loader_hide();
            }

            if (!loaded_all && status === 'success' && settings.behavior.ensure_scrollbar) {
                self.ensure_scrollbar();
            }
        };

        /**
         * Shows the loader.
         */
        loader_show = function () {
            settings.elements.loader.show();
        };

        /**
         * Hides the loader and clears our loader timeout.
         */
        loader_hide = function () {
            clearTimeout(loader_timeout_id);
            settings.elements.loader.hide();
        };

        /**
         * Merges in user settings and fetches some items to start if specified.
         */
        init = function () {
            settings = $.extend(true, {}, default_settings, options);
            offset = settings.behavior.initial_offset;
            if (settings.behavior.fetch_immediate) {
                self.fetch_items(settings.behavior.fetch_amount);
            } else {
                check_add_event_handlers();
            }
        };

        init();
    };

    /**
     * Iterates over each element and instantiates a new instance of
     * JSONLazyLoad.
     *
     * The JSONLazyLoad instance is stuffed into a data property of the element
     * called 'jsonlazyload'.
     *
     * Usage:
     * jQuery('#my-element').jsonlazyload(my_options);
     *
     * @param options
     *     An object representing the options for the instances.
     *     See default_settings object for detailed options information.
     *
     * @return
     *     The jQuery object that was used to call this plugin.
     */
    $.fn.jsonlazyload = function (options) {
        return this.each(function () {
            var jsonlazyload = new JSONLazyLoad(this, options);
            $(this).data('jsonlazyload', jsonlazyload);
        });
    };
}(jQuery));<commit_msg>Changed version number for release<commit_after>/*
    jquery.jasonlazyload.js
    2015-02-03
    v1.0.1

    The MIT License (MIT)

    Copyright (c) 2015 Joe Ridino

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

    JSONLazyLoad is a jQuery plugin that loads dynamic content from a server when
    the user scrolls or resizes the window.

    This plugin will handle the browser events and call the web service for
    items, then hand them off for rendering.  See the default_settings object
    below for detailed information about the possible settings.

    Usage:
    jQuery('#my-element').jsonlazyload(my_options);
*/

/*jslint browser: true, unparam: true */
/*global jQuery: false */

(function ($) {
    'use strict';

    var JSONLazyLoad = function (element, options) {
        var default_settings = {
            // Behavior-type settings.
            behavior: {
                // If true, does automatic Ajax calls to ensure there's always
                // a scrollbar present in the scroll target.
                ensure_scrollbar: false,

                // The number of items to fetch from the server.
                fetch_amount: 10,

                // If true, fetches items immediately after instantiating.
                fetch_immediate: true,

                // The initial offset for pagination.  Normally this will be 0.
                initial_offset: 0,

                // After this many milliseconds elements.loader will be shown.
                loader_delay: 100,

                // The number of pixels from the bottom of the scroll target
                // that triggers a new Ajax request.  Normally, this would be 0
                // if you want to have new items load when the scrollbar reaches
                // its bottom, but due to some browser quirks setting this to a
                // value of 1 is more reliable.
                scroll_threshold: 1
            },
            // Files-based web service.
            files: {
                // The file pattern to use when retrieving the next file.
                // e.g. If you specify "data[n].json" then the "[n]" will be
                // replaced with the current page number.  So if the initial
                // offset is 0, the first request will be to "data1.json".
                pattern: '',

                // The total number of files that exist on the server.
                num_files: 0
            },
            // jQuery elements.
            elements: {
                // The jQuery element that scroll events are attached to.
                scroll_target: $(window),

                // The jQuery element that will be displayed when N
                // (behavior.loader_delay) number of milliseconds have elapsed
                // since the start of an Ajax request.
                loader: null
            },
            // Callbacks and other settings pertaining to the Ajax response.
            results: {
                // Function that gets called right before an Ajax request.
                // Signature: function (lazy_loader, limit)
                before_callback: null,

                // Function that gets called when an Ajax request fails.
                // Signature: function (lazy_loader)
                fail_callback: null,

                // Function that gets called when an Ajax request succeeds.
                // Signature: function (lazy_loader, data)
                item_callback: null,

                // Specifies an object key inside the Ajax response that holds
                // the items array.  When null, it is assumed the items array
                // is the response itself.
                item_key: null
            },
            // Web service settings.
            ws: {
                // Content-type string suitable for jQuery.ajax() call.
                data_type: 'json',

                // The name of the limit parameter for the web service.
                limit_param: 'limit',

                // The name of the offset paraemter for the web service.
                offset_param: 'offset',

                // The URL to the web service.  Can have additional query
                // parameters outside of limit and offset if needed.
                url: ''
            }
        },
            added_event_handlers = false,
            ajaxing = false,
            check_add_event_handlers,
            files_offset = 0,
            has_scrollbar,
            init,
            jquery_element = $(element),
            loaded_all = false,
            loader_hide,
            loader_show,
            loader_timeout_id,
            offset = 0,
            prev_scroll_top = 0,
            promise = null,
            promise_always,
            promise_done,
            promise_fail,
            resize_check,
            scroll_check,
            self = this,
            settings = null,
            tear_down;

        /**
         * Returns the jQuery element that is the lazy load target.
         *
         * @return
         *     The jQuery element that is the lazy load target.
         */
        this.target = function () {
            return jquery_element;
        };

        /**
         * Fetches items from the server given the amount of items to fetch.
         *
         * This function is aware of the items that we have fetched previously
         * by way of the offset variable.  The fetch will start from the offset
         * and grab as many items as it can, up to the limit specified.
         *
         * Also, this function will trigger your callbacks when the Ajax request
         * finishes in addition to showing the loader if the Ajax request is
         * taking too long.
         *
         * @param limit
         *     The number of items to fetch.
         */
        this.fetch_items = function (limit) {
            if (ajaxing || loaded_all) {
                return;
            }

            var params = {}, url;
            if (settings.files.pattern) {
                url = settings.files.pattern.replace('[n]', files_offset + 1);
            } else {
                url = settings.ws.url;
                params[settings.ws.limit_param] = limit;
                params[settings.ws.offset_param] = offset;
            }

            if (settings.results.before_callback) {
                settings.results.before_callback(this, limit);
            }

            if (settings.elements.loader) {
                loader_timeout_id = setTimeout(loader_show, settings.behavior.loader_delay);
            }
            ajaxing = true;
            promise = $.ajax({
                url: url,
                data: params,
                dataType: settings.ws.data_type
            });

            promise.done(promise_done);
            promise.fail(promise_fail);
            promise.always(promise_always);
        };

        /**
         * If the scroll target does not have a vertical scrollbar more items
         * are fetched from the server to try to obtain one.
         */
        this.ensure_scrollbar = function () {
            if (!has_scrollbar()) {
                self.fetch_items(settings.behavior.fetch_amount);
            }
        };

        /**
         * Removes jQuery events and hides the loader if it's visible.
         */
        this.destroy = function () {
            tear_down();
        };

        /**
         * Removes jQuery events and hides the loader if it's visible.
         */
        tear_down = function () {
            settings.elements.scroll_target.off('scroll', scroll_check);
            $(window).off('resize', resize_check);
            if (settings.elements.loader) {
                loader_hide();
            }
        };

        /**
         * Returns true if the scroll target has a vertical scrollbar.
         *
         * @return
         *     Returns true if the scroll target has a vertical scrollbar.
         */
        has_scrollbar = function () {
            var client_height, scroll_height;
            client_height = settings.elements.scroll_target.innerHeight();
            if ($.isWindow(settings.elements.scroll_target[0])) {
                scroll_height = $(document).height();
            } else {
                scroll_height = settings.elements.scroll_target[0].scrollHeight;
            }

            return scroll_height > client_height;
        };

        /**
         * Fetches more items when the user is scrolling the scroll target and
         * the scroll position is near the bottom of the target.  The threshold
         * is also taken into account.
         */
        scroll_check = function () {
            var threshold, needs_ajax, new_scroll_top, scroll_height;
            new_scroll_top = settings.elements.scroll_target.scrollTop();
            if (new_scroll_top > prev_scroll_top && !(ajaxing || loaded_all)) {
                if ($.isWindow(settings.elements.scroll_target[0])) {
                    scroll_height = $(document).height();
                } else {
                    scroll_height = settings.elements.scroll_target[0].scrollHeight;
                }
                threshold = scroll_height - settings.behavior.scroll_threshold;
                needs_ajax = (new_scroll_top + settings.elements.scroll_target.innerHeight()) >= threshold;
                if (needs_ajax) {
                    self.fetch_items(settings.behavior.fetch_amount);
                }
            }

            prev_scroll_top = new_scroll_top;
        };

        /**
         * Calls ensure_scrollbar() to obtain a vertical scrollbar when the
         * window is resized.
         */
        resize_check = function () {
            if (ajaxing || loaded_all) {
                return;
            }

            if (settings.behavior.ensure_scrollbar) {
                self.ensure_scrollbar();
            }
        };

        /**
         * Adds 'scroll' and 'resize' event handlers if they have not been
         * added before.
         */
        check_add_event_handlers = function () {
            if (!added_event_handlers) {
                settings.elements.scroll_target.on('scroll', scroll_check);
                $(window).on('resize', resize_check);
                added_event_handlers = true;
            }
        };

        /**
         * Called when the Ajax request has a successful response.
         *
         * The item_callback is triggered to notify the client of new items.
         *
         * @param data
         *     The data from the server, which has already been transformed by
         *     jQuery into a JavaScript object based on the content type
         *     specified.
         */
        promise_done = function (data) {
            if (settings.results.item_key) {
                data = data[settings.results.item_key];
            }

            if (!$.isArray(data)) {
                return;
            }

            offset += data.length;
            if (settings.files.pattern) {
                files_offset += 1;
            }
            if (data.length) {
                settings.results.item_callback(self, data);
            } else {
                loaded_all = true;
                tear_down();
            }

            check_add_event_handlers();
        };

        /**
         * Called when the Ajax request fails.
         *
         * The fail_callback is triggered to notify the client of the failure.
         */
        promise_fail = function () {
            if (settings.results.fail_callback) {
                settings.results.fail_callback(self);
            }
        };

        /**
         * Called when the Ajax request either fails or succeeds.
         *
         * The main purpose of this function is to reset our flags, hide the
         * loader and ensure we have a scrollbar (which in turn calls the server
         * again if needed).
         *
         * @param data
         *     The data from the server, which has already been transformed by
         *     jQuery into a JavaScript object based on the content type
         *     specified.
         * @param status
         *     Status string from jQuery.ajax.  Normally, this is 'success'
         *     unless the ajax call fails.
         */
        promise_always = function (data, status) {
            ajaxing = false;

            if (settings.elements.loader) {
                loader_hide();
            }

            if (!loaded_all && status === 'success' && settings.behavior.ensure_scrollbar) {
                self.ensure_scrollbar();
            }
        };

        /**
         * Shows the loader.
         */
        loader_show = function () {
            settings.elements.loader.show();
        };

        /**
         * Hides the loader and clears our loader timeout.
         */
        loader_hide = function () {
            clearTimeout(loader_timeout_id);
            settings.elements.loader.hide();
        };

        /**
         * Merges in user settings and fetches some items to start if specified.
         */
        init = function () {
            settings = $.extend(true, {}, default_settings, options);
            offset = settings.behavior.initial_offset;
            if (settings.behavior.fetch_immediate) {
                self.fetch_items(settings.behavior.fetch_amount);
            } else {
                check_add_event_handlers();
            }
        };

        init();
    };

    /**
     * Iterates over each element and instantiates a new instance of
     * JSONLazyLoad.
     *
     * The JSONLazyLoad instance is stuffed into a data property of the element
     * called 'jsonlazyload'.
     *
     * Usage:
     * jQuery('#my-element').jsonlazyload(my_options);
     *
     * @param options
     *     An object representing the options for the instances.
     *     See default_settings object for detailed options information.
     *
     * @return
     *     The jQuery object that was used to call this plugin.
     */
    $.fn.jsonlazyload = function (options) {
        return this.each(function () {
            var jsonlazyload = new JSONLazyLoad(this, options);
            $(this).data('jsonlazyload', jsonlazyload);
        });
    };
}(jQuery));<|endoftext|>


<commit_before>var EventDispatcher = require('./EventDispatcher');
var MouseEvent      = require('./MouseEvent');

function Mouse() {
    EventDispatcher.call(this);

    this._x = 0;
    this._y = 0;
    this._prevX = 0;
    this._prevY = 0;
    this._deltaX = 0;
    this._deltaY = 0;
    this._isDown = false;
}

Mouse.prototype = Object.create(EventDispatcher.prototype);
Mouse.prototype.constructor = Mouse;

Mouse.prototype.getPosX = function() {
    return this._x;
};

Mouse.prototype.getPosY = function() {
    return this._y;
};

Mouse.prototype.getPos = function(out){
    if(out === undefined){
        return [this._x,this._y];
    }
    out[0] = this._x;
    out[1] = this._y;
    return out;
};

Mouse.prototype.getPrevPosX = function() {
    return this._prevX;
};

Mouse.prototype.getPrevPosY = function() {
    return this._prevY;
};

Mouse.prototype.getPrevPos = function(out){
    if(out === undefined){
        return [this._prevX,this._prevY];
    }
    out[0] = this._prevX;
    out[1] = this._prevY;
    return out;
};

Mouse.prototype.getDeltaX = function() {
    return this._deltaX;
};

Mouse.prototype.getDeltaY = function() {
    return this._deltaY;
};

Mouse.prototype.getDelta = function(out){
    if(out === undefined){
        return [this._deltaX,this._deltaY];
    }
    out[0] = this._deltaX;
    out[1] = this._deltaY;
    return out;
};

Mouse.prototype.handleMouseDown = function(e) {
    this._isDown = true;
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN, e));
}

Mouse.prototype.handleMouseUp = function(e) {
    this._isDown = false;
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP, e));
}

Mouse.prototype.handleMouseMove = function(e) {
    this._prevX = this._x;
    this._prevY = this._y;
    this._x = e.x;
    this._y = e.y;
    this._deltaX = this._x - this._prevX;
    this._deltaY = this._y - this._prevY;

    e.mouse = this;

    //don't fire mouse move events while dragging
    if (this._isDown) {
        this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DRAG, e));
    }
    else {
        this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE, e));
    }
}

Mouse.prototype.handleMouseScroll = function(e) {
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_SCROLL, e));
}

module.exports = Mouse;
<commit_msg>Mouse initial documentation<commit_after>var EventDispatcher = require('./EventDispatcher');
var MouseEvent      = require('./MouseEvent');

/**
 * EventDispatcher for mouse events
 * @class
 *
 * @example
 * var Window = require('pex-sys').Window;
 * var MouseEvent = require('pex-sys').MouseEvent;
 *
 * Window.create({
 * 	 init: function() {
 * 	 	var mouse = this.getMouse()
 * 	 	mouse.addEventListener(MouseEvent.MOUSE_DOWN, function(e){ });
 * 	 	mouse.addEventListener(MouseEvent.MOUSE_UP, function(e){ });
 * 	 	mouse.addEventListener(MouseEvent.MOUSE_MOVE, function(e){ });
 * 	 	mouse.addEventListener(MouseEvent.MOUSE_DRAG, function(e){ });
 * 	 	mouse.addEventListener(MouseEvent.MOUSE_SCROLL, function(e){ });
 * 	 }
 * })
 */
function Mouse() {
    EventDispatcher.call(this);

    this._x = 0;
    this._y = 0;
    this._prevX = 0;
    this._prevY = 0;
    this._deltaX = 0;
    this._deltaY = 0;
    this._isDown = false;
}

Mouse.prototype = Object.create(EventDispatcher.prototype);
Mouse.prototype.constructor = Mouse;

Mouse.prototype.getPosX = function() {
    return this._x;
};

Mouse.prototype.getPosY = function() {
    return this._y;
};

Mouse.prototype.getPos = function(out){
    if(out === undefined){
        return [this._x,this._y];
    }
    out[0] = this._x;
    out[1] = this._y;
    return out;
};

Mouse.prototype.getPrevPosX = function() {
    return this._prevX;
};

Mouse.prototype.getPrevPosY = function() {
    return this._prevY;
};

Mouse.prototype.getPrevPos = function(out){
    if(out === undefined){
        return [this._prevX,this._prevY];
    }
    out[0] = this._prevX;
    out[1] = this._prevY;
    return out;
};

Mouse.prototype.getDeltaX = function() {
    return this._deltaX;
};

Mouse.prototype.getDeltaY = function() {
    return this._deltaY;
};

Mouse.prototype.getDelta = function(out){
    if(out === undefined){
        return [this._deltaX,this._deltaY];
    }
    out[0] = this._deltaX;
    out[1] = this._deltaY;
    return out;
};

Mouse.prototype.handleMouseDown = function(e) {
    this._isDown = true;
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DOWN, e));
}

Mouse.prototype.handleMouseUp = function(e) {
    this._isDown = false;
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_UP, e));
}

Mouse.prototype.handleMouseMove = function(e) {
    this._prevX = this._x;
    this._prevY = this._y;
    this._x = e.x;
    this._y = e.y;
    this._deltaX = this._x - this._prevX;
    this._deltaY = this._y - this._prevY;

    e.mouse = this;

    //don't fire mouse move events while dragging
    if (this._isDown) {
        this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_DRAG, e));
    }
    else {
        this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_MOVE, e));
    }
}

Mouse.prototype.handleMouseScroll = function(e) {
    e.mouse = this;
    this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_SCROLL, e));
}

module.exports = Mouse;
<|endoftext|>