<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// Default container configuration. To change the configuration, you have two options:
//
// A. If you run the Java server: Create your own "myContainer.js" file and
// modify the value in web.xml.
//
//  B. If you run the PHP server: Create a myContainer.js, copy the contents of container.js to it,
//  change
//		{"gadgets.container" : ["default"],
//  to
//		{"gadgets.container" : ["myContainer"],
// And make your changes that you need to myContainer.js.
// Just make sure on the iframe URL you specify &container=myContainer
// for it to use that config.
//
// All configurations will automatically inherit values from this
// config, so you only need to provide configuration for items
// that you require explicit special casing for.
//
// Please namespace your attributes using the same conventions
// as you would for javascript objects, e.g. gadgets.features
// rather than "features".

// NOTE: Please _don't_ leave trailing commas because the php json parser
// errors out on this.

// Container must be an array; this allows multiple containers
// to share configuration.
{"gadgets.container" : ["default"],

// Set of regular expressions to validate the parent parameter. This is
// necessary to support situations where you want a single container to support
// multiple possible host names (such as for localized domains, such as
// <language>.example.org. If left as null, the parent parameter will be
// ignored; otherwise, any requests that do not include a parent
// value matching this set will return a 404 error.
"gadgets.parent" : null,

// Should all gadgets be forced on to a locked domain?
"gadgets.lockedDomainRequired" : false,

// DNS domain on which gadgets should render.
"gadgets.lockedDomainSuffix" : "-a.example.com:8080",

// This config data will be passed down to javascript. Please
// configure your object using the feature name rather than
// the javascript name.

// Only configuration for required features will be used.
// See individual feature.xml files for configuration details.
"gadgets.features" : {
  "core.io" : {
    // Note: /proxy is an open proxy. Be careful how you expose this!
    "proxyUrl" : "proxy?refresh=%refresh%&url=%url%",
    "jsonProxyUrl" : "makeRequest"
  },
  "views" : {
    "profile" : {
      "isOnlyVisible" : false,
      "urlTemplate" : "http://localhost/gadgets/profile?{var}",
      "aliases": ["DASHBOARD", "default"]
    },
    "canvas" : {
      "isOnlyVisible" : true,
      "urlTemplate" : "http://localhost/gadgets/canvas?{var}",
      "aliases" : ["FULL_PAGE"]
    }
  },
  "rpc" : {
    // Path to the relay file. Automatically appended to the parent
    /// parameter if it passes input validation and is not null.
    // This should never be on the same host in a production environment!
    // Only use this for TESTING!
    "parentRelayUrl" : "/gadgets/files/container/rpc_relay.html",

    // If true, this will use the legacy ifpc wire format when making rpc
    // requests.
    "useLegacyProtocol" : false
  },
  // Skin defaults
  "skins" : {
    "properties" : {
      "BG_COLOR": "",
      "BG_IMAGE": "",
      "BG_POSITION": "",
      "BG_REPEAT": "",
      "FONT_COLOR": "",
      "ANCHOR_COLOR": ""
    }
  },
  "opensocial-0.8" : {
    // Path to fetch opensocial data from
    // Must be on the same domain as the gadget rendering server
    "impl" : "rpc",  //Use "rpc" to enable JSON-RPC, "rest' for REST
    "path" : "/social",
    "domain" : "shindig",
    "enableCaja" : false,
    "supportedFields" : {
       "person" : ["id", "name", "thumbnailUrl", "profileUrl"],
       "activity" : ["id", "title"]
    }
  }

}}
<commit_msg>better compliance<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// Default container configuration. To change the configuration, you have two options:
//
// A. If you run the Java server: Create your own "myContainer.js" file and
// modify the value in web.xml.
//
//  B. If you run the PHP server: Create a myContainer.js, copy the contents of container.js to it,
//  change
//		{"gadgets.container" : ["default"],
//  to
//		{"gadgets.container" : ["myContainer"],
// And make your changes that you need to myContainer.js.
// Just make sure on the iframe URL you specify &container=myContainer
// for it to use that config.
//
// All configurations will automatically inherit values from this
// config, so you only need to provide configuration for items
// that you require explicit special casing for.
//
// Please namespace your attributes using the same conventions
// as you would for javascript objects, e.g. gadgets.features
// rather than "features".

// NOTE: Please _don't_ leave trailing commas because the php json parser
// errors out on this.

// Container must be an array; this allows multiple containers
// to share configuration.
{"gadgets.container" : ["default"],

// Set of regular expressions to validate the parent parameter. This is
// necessary to support situations where you want a single container to support
// multiple possible host names (such as for localized domains, such as
// <language>.example.org. If left as null, the parent parameter will be
// ignored; otherwise, any requests that do not include a parent
// value matching this set will return a 404 error.
"gadgets.parent" : null,

// Should all gadgets be forced on to a locked domain?
"gadgets.lockedDomainRequired" : false,

// DNS domain on which gadgets should render.
"gadgets.lockedDomainSuffix" : "-a.example.com:8080",

// This config data will be passed down to javascript. Please
// configure your object using the feature name rather than
// the javascript name.

// Only configuration for required features will be used.
// See individual feature.xml files for configuration details.
"gadgets.features" : {
  "core.io" : {
    // Note: /proxy is an open proxy. Be careful how you expose this!
    "proxyUrl" : "proxy?refresh=%refresh%&url=%url%",
    "jsonProxyUrl" : "makeRequest"
  },
  "views" : {
    "profile" : {
      "isOnlyVisible" : false,
      "urlTemplate" : "http://localhost/gadgets/profile?{var}",
      "aliases": ["DASHBOARD", "default"]
    },
    "canvas" : {
      "isOnlyVisible" : true,
      "urlTemplate" : "http://localhost/gadgets/canvas?{var}",
      "aliases" : ["FULL_PAGE"]
    }
  },
  "rpc" : {
    // Path to the relay file. Automatically appended to the parent
    /// parameter if it passes input validation and is not null.
    // This should never be on the same host in a production environment!
    // Only use this for TESTING!
    "parentRelayUrl" : "/gadgets/files/container/rpc_relay.html",

    // If true, this will use the legacy ifpc wire format when making rpc
    // requests.
    "useLegacyProtocol" : false
  },
  // Skin defaults
  "skins" : {
    "properties" : {
      "BG_COLOR": "",
      "BG_IMAGE": "",
      "BG_POSITION": "",
      "BG_REPEAT": "",
      "FONT_COLOR": "",
      "ANCHOR_COLOR": ""
    }
  },
  "opensocial-0.8" : {
    // Path to fetch opensocial data from
    // Must be on the same domain as the gadget rendering server
    "impl" : "rpc",  //Use "rpc" to enable JSON-RPC, "rest' for REST
    "path" : "/social",
    "domain" : "shindig",
    "enableCaja" : false,
    "supportedFields" : {
       "person" : ["id", {"name" : ["familyName", "givenName", "unstructured"]}, "thumbnailUrl", "profileUrl"],
       "activity" : ["id", "title"]
    }
  }

}}
<|endoftext|>


<commit_before>(function(w, d, undefined) {
  'use strict';

  /*
   * aliases
   * w: window global object
   * d: document
   * undefined: undefined
   */

  // polyfill
  function polyfill() {
    // return when scrollBehavior interface is supported
    if ('scrollBehavior' in d.documentElement.style) {
      return;
    }

    /**
     * indicates if a the current browser is made by Microsoft
     * @method isMicrosoftBrowser
     * @param {string} userAgent
     * @returns {Boolean}
     */
    function isMicrosoftBrowser(userAgent) {
      var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/'];
      return new RegExp(userAgentPatterns.join('|')).test(userAgent);
    }

    /*
     * globals
     */
    var Element = w.HTMLElement || w.Element;
    var SCROLL_TIME = 468;
    var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0;

    /*
     * object gathering original scroll methods
     */
    var original = {
      scroll: w.scroll || w.scrollTo,
      scrollBy: w.scrollBy,
      elScroll: Element.prototype.scroll || scrollElement,
      scrollIntoView: Element.prototype.scrollIntoView
    };

    /*
     * define timing method
     */
    var now = w.performance && w.performance.now
      ? w.performance.now.bind(w.performance) : Date.now;

    /**
     * changes scroll position inside an element
     * @method scrollElement
     * @param {Number} x
     * @param {Number} y
     */
    function scrollElement(x, y) {
      this.scrollLeft = x;
      this.scrollTop = y;
    }

    /**
     * returns result of applying ease math function to a number
     * @method ease
     * @param {Number} k
     * @returns {Number}
     */
    function ease(k) {
      return 0.5 * (1 - Math.cos(Math.PI * k));
    }

    /**
     * indicates if a smooth behavior should be applied
     * @method shouldBailOut
     * @param {Number|Object} x
     * @returns {Boolean}
     */
    function shouldBailOut(x) {
      if (typeof x !== 'object'
            || x === null
            || x.behavior === undefined
            || x.behavior === 'auto'
            || x.behavior === 'instant') {
        // first arg not an object/null
        // or behavior is auto, instant or undefined
        return true;
      }

      if (typeof x === 'object'
            && x.behavior === 'smooth') {
        // first argument is an object and behavior is smooth
        return false;
      }

      // throw error when behavior is not supported
      throw new TypeError('behavior not valid');
    }

    /**
     * indicates if an element has scrollable space in the provided axis
     * @method hasScrollableSpace
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function hasScrollableSpace(el, axis) {
      if (axis === 'Y') {
        return el.clientHeight + ROUNDING_TOLERANCE < el.scrollHeight;
      }

      if (axis === 'X') {
        return el.clientWidth + ROUNDING_TOLERANCE < el.scrollWidth;
      }

      throw new Error('axis invalid. Expected either `X` or `Y`.');
    }

    /**
     * indicates if an element has a scrollable overflow property in the axis
     * @method canOverflow
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function canOverflow(el, axis) {
      var overflowValue = w.getComputedStyle(el, null)['overflow' + axis];

      return overflowValue === 'auto' || overflowValue === 'scroll';
    }

    /**
     * indicates if an element can be scrolled in either axis
     * @method isScrollable
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function isScrollable(el) {
      var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y');
      var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X');

      return isScrollableY || isScrollableX;
    }

    /**
     * finds scrollable parent of an element
     * @method findScrollableParent
     * @param {Node} el
     * @returns {Node} el
     */
    function findScrollableParent(el) {
      var isBody;

      do {
        el = el.parentNode;

        // set condition variables
        isBody = el === d.body;
      } while (!isBody && !isScrollable(el));

      isBody = null;

      return el;
    }

    /**
     * self invoked function that, given a context, steps through scrolling
     * @method step
     * @param {Object} context
     */
    function step(context) {
      var time = now();
      var value;
      var currentX;
      var currentY;
      var elapsed = (time - context.startTime) / SCROLL_TIME;

      // avoid elapsed times higher than one
      elapsed = elapsed > 1 ? 1 : elapsed;

      // apply easing to elapsed time
      value = ease(elapsed);

      currentX = context.startX + (context.x - context.startX) * value;
      currentY = context.startY + (context.y - context.startY) * value;

      context.method.call(context.scrollable, currentX, currentY);

      // scroll more if we have not reached our destination
      if (currentX !== context.x || currentY !== context.y) {
        w.requestAnimationFrame(step.bind(w, context));
      }
    }

    /**
     * scrolls window with a smooth behavior
     * @method smoothScroll
     * @param {Object|Node} el
     * @param {Number} x
     * @param {Number} y
     */
    function smoothScroll(el, x, y) {
      var scrollable;
      var startX;
      var startY;
      var method;
      var startTime = now();

      // define scroll context
      if (el === d.body) {
        scrollable = w;
        startX = w.scrollX || w.pageXOffset;
        startY = w.scrollY || w.pageYOffset;
        method = original.scroll;
      } else {
        scrollable = el;
        startX = el.scrollLeft;
        startY = el.scrollTop;
        method = scrollElement;
      }

      // scroll looping over a frame
      step({
        scrollable: scrollable,
        method: method,
        startTime: startTime,
        startX: startX,
        startY: startY,
        x: x,
        y: y
      });
    }

    /*
     * ORIGINAL METHODS OVERRIDES
     */

    // w.scroll and w.scrollTo
    w.scroll = w.scrollTo = function() {
      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0])) {
        original.scroll.call(
          w,
          arguments[0].left || arguments[0],
          arguments[0].top || arguments[1]
        );
        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
        w,
        d.body,
        ~~arguments[0].left,
        ~~arguments[0].top
      );
    };

    // w.scrollBy
    w.scrollBy = function() {
      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0])) {
        original.scrollBy.call(
          w,
          arguments[0].left || arguments[0],
          arguments[0].top || arguments[1]
        );
        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
        w,
        d.body,
        ~~arguments[0].left + (w.scrollX || w.pageXOffset),
        ~~arguments[0].top + (w.scrollY || w.pageYOffset)
      );
    };

    // Element.prototype.scroll and Element.prototype.scrollTo
    Element.prototype.scroll = Element.prototype.scrollTo = function() {
      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0])) {
        original.elScroll.call(
            this,
            arguments[0].left || arguments[0],
            arguments[0].top || arguments[1]
        );
        return;
      }

      var left = arguments[0].left;
      var top = arguments[0].top;

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
          this,
          this,
          typeof left === 'number' ? left : this.scrollLeft,
          typeof top === 'number' ? top : this.scrollTop
      );
    };

    // Element.prototype.scrollBy
    Element.prototype.scrollBy = function() {
      var arg0 = arguments[0];

      if (typeof arg0 === 'object') {
        this.scroll({
          left: arg0.left + this.scrollLeft,
          top: arg0.top + this.scrollTop,
          behavior: arg0.behavior
        });
      } else {
        this.scroll(
          this.scrollLeft + arg0,
          this.scrollTop + arguments[1]
        );
      }
    };

    // Element.prototype.scrollIntoView
    Element.prototype.scrollIntoView = function() {
      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0])) {
        original.scrollIntoView.call(
          this,
          arguments[0] === undefined ? true : arguments[0]
        );
        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      var scrollableParent = findScrollableParent(this);
      var parentRects = scrollableParent.getBoundingClientRect();
      var clientRects = this.getBoundingClientRect();

      if (scrollableParent !== d.body) {
        // reveal element inside parent
        smoothScroll.call(
          this,
          scrollableParent,
          scrollableParent.scrollLeft + clientRects.left - parentRects.left,
          scrollableParent.scrollTop + clientRects.top - parentRects.top
        );
        // reveal parent in viewport
        w.scrollBy({
          left: parentRects.left,
          top: parentRects.top,
          behavior: 'smooth'
        });
      } else {
        // reveal element in viewport
        w.scrollBy({
          left: clientRects.left,
          top: clientRects.top,
          behavior: 'smooth'
        });
      }
    };
  }

  if (typeof exports === 'object') {
    // commonjs
    module.exports = { polyfill: polyfill };
  } else {
    // global
    polyfill();
  }
})(window, document);
<commit_msg>Improve reliability on scroll methods. Code style gardening.<commit_after>(function(w, d, undefined) {
  'use strict';

  /*
   * aliases
   * w: window global object
   * d: document
   * undefined: undefined
   */

   // polyfill
  function polyfill() {
    // return if scroll behavior is supported and polyfill is not forced
    if ('scrollBehavior' in d.documentElement.style
      && w.__forceSmoothScrollPolyfill__ !== true) {
      return;
    }

    // globals
    var Element = w.HTMLElement || w.Element;
    var ROUNDING_TOLERANCE = isMicrosoftBrowser(w.navigator.userAgent) ? 1 : 0;
    var SCROLL_TIME = typeof w.__smoothScrollPolyfillSpeed__ === 'number'
      ? w.__smoothScrollPolyfillSpeed__
      : 468;

    // object gathering original scroll methods
    var original = {
      scroll: w.scroll || w.scrollTo,
      scrollBy: w.scrollBy,
      elementScroll: Element.prototype.scroll || scrollElement,
      scrollIntoView: Element.prototype.scrollIntoView
    };

    // define timing method
    var now = w.performance && w.performance.now
      ? w.performance.now.bind(w.performance)
      : Date.now;

    /**
     * indicates if a the current browser is made by Microsoft
     * @method isMicrosoftBrowser
     * @param {string} userAgent
     * @returns {Boolean}
     */
    function isMicrosoftBrowser(userAgent) {
      var userAgentPatterns = ['MSIE ', 'Trident/', 'Edge/'];

      return new RegExp(userAgentPatterns.join('|')).test(userAgent);
    }

    /**
    * changes scroll position inside an element
    * @method scrollElement
    * @param {Number} x
    * @param {Number} y
    */
    function scrollElement(x, y) {
      this.scrollLeft = x;
      this.scrollTop = y;
    }

    /**
    * returns result of applying ease math function to a number
    * @method ease
    * @param {Number} k
    * @returns {Number}
    */
    function ease(k) {
      return 0.5 * (1 - Math.cos(Math.PI * k));
    }

    /**
    * indicates if a smooth behavior should be applied
    * @method shouldBailOut
    * @param {Number|Object} x
    * @returns {Boolean}
    */
    function shouldBailOut(firstArg) {
      if (firstArg === null
        || typeof firstArg !== 'object'
        || firstArg.behavior === undefined
        || firstArg.behavior === 'auto'
        || firstArg.behavior === 'instant') {
        // first argument is not an object/null
        // or behavior is auto, instant or undefined
        return true;
      }

      if (typeof firstArg === 'object' && firstArg.behavior === 'smooth') {
        // first argument is an object and behavior is smooth
        return false;
      }

      // throw error when behavior is not supported
      throw new TypeError('behavior not valid');
    }

    /**
     * indicates if an element has scrollable space in the provided axis
     * @method hasScrollableSpace
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function hasScrollableSpace(el, axis) {
      if (axis === 'Y') {
        return (el.clientHeight + ROUNDING_TOLERANCE) < el.scrollHeight;
      }

      if (axis === 'X') {
        return (el.clientWidth + ROUNDING_TOLERANCE) < el.scrollWidth;
      }
    }

    /**
     * indicates if an element has a scrollable overflow property in the axis
     * @method canOverflow
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function canOverflow(el, axis) {
      var overflowValue = w.getComputedStyle(el, null)['overflow' + axis];

      return overflowValue === 'auto' || overflowValue === 'scroll';
    }

    /**
     * indicates if an element can be scrolled in either axis
     * @method isScrollable
     * @param {Node} el
     * @param {String} axis
     * @returns {Boolean}
     */
    function isScrollable(el) {
      var isScrollableY = hasScrollableSpace(el, 'Y') && canOverflow(el, 'Y');
      var isScrollableX = hasScrollableSpace(el, 'X') && canOverflow(el, 'X');

      return isScrollableY || isScrollableX;
    }

    /**
     * finds scrollable parent of an element
     * @method findScrollableParent
     * @param {Node} el
     * @returns {Node} el
     */
    function findScrollableParent(el) {
      var isBody;

      do {
        el = el.parentNode;

        isBody = el === d.body;
      } while (isBody === false && isScrollable(el) === false);

      isBody = null;

      return el;
    }

    /**
     * self invoked function that, given a context, steps through scrolling
     * @method step
     * @param {Object} context
     */
    function step(context) {
      var time = now();
      var value;
      var currentX;
      var currentY;
      var elapsed = (time - context.startTime) / SCROLL_TIME;

      // avoid elapsed times higher than one
      elapsed = elapsed > 1 ? 1 : elapsed;

      // apply easing to elapsed time
      value = ease(elapsed);

      currentX = context.startX + (context.x - context.startX) * value;
      currentY = context.startY + (context.y - context.startY) * value;

      context.method.call(context.scrollable, currentX, currentY);

      // scroll more if we have not reached our destination
      if (currentX !== context.x || currentY !== context.y) {
        w.requestAnimationFrame(step.bind(w, context));
      }
    }

    /**
     * scrolls window or element with a smooth behavior
     * @method smoothScroll
     * @param {Object|Node} el
     * @param {Number} x
     * @param {Number} y
     */
    function smoothScroll(el, x, y) {
      var scrollable;
      var startX;
      var startY;
      var method;
      var startTime = now();

      // define scroll context
      if (el === d.body) {
        scrollable = w;
        startX = w.scrollX || w.pageXOffset;
        startY = w.scrollY || w.pageYOffset;
        method = original.scroll;
      } else {
        scrollable = el;
        startX = el.scrollLeft;
        startY = el.scrollTop;
        method = scrollElement;
      }

      // scroll looping over a frame
      step({
        scrollable: scrollable,
        method: method,
        startTime: startTime,
        startX: startX,
        startY: startY,
        x: x,
        y: y
      });
    }

    // ORIGINAL METHODS OVERRIDES
    // w.scroll and w.scrollTo
    w.scroll = w.scrollTo = function() {
      // avoid action when no arguments are passed
      if (arguments[0] === undefined) {
        return;
      }

      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0]) === true) {
        original.scroll.call(
          w,
          arguments[0].left !== undefined
            ? arguments[0].left
            : arguments[0],
          arguments[0].top !== undefined
            ? arguments[0].top
            : arguments[1] !== undefined
              ? arguments[1]
              : (w.scrollY || w.pageYOffset)
        );

        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
        w,
        d.body,
        arguments[0].left !== undefined
          ? ~~arguments[0].left
          : (w.scrollX || w.pageXOffset),
        arguments[0].top !== undefined
          ? ~~arguments[0].top
          : (w.scrollY || w.pageYOffset),
      );
    };

    // w.scrollBy
    w.scrollBy = function() {
      // avoid action when no arguments are passed
      if (arguments[0] === undefined) {
        return;
      }

      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0])) {
        original.scrollBy.call(
          w,
          arguments[0].left !== undefined ? arguments[0].left : arguments[0],
          arguments[0].top !== undefined ? arguments[0].top : arguments[1]
        );
        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
        w,
        d.body,
        ~~arguments[0].left + (w.scrollX || w.pageXOffset),
        ~~arguments[0].top + (w.scrollY || w.pageYOffset)
      );
    };

    // Element.prototype.scroll and Element.prototype.scrollTo
    Element.prototype.scroll = Element.prototype.scrollTo = function() {
      // avoid action when no arguments are passed
      if (arguments[0] === undefined) {
        return;
      }

      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0]) === true) {
        // if one number is passed, throw error to match Firefox implementation
        if (typeof arguments[0] === 'number' && arguments[1] === undefined) {
          throw new SyntaxError('Value couldn\'t be converted');
        }

        original.elementScroll.call(
          this,
          arguments[0].left !== undefined
            ? ~~arguments[0].left
            : ~~arguments[0],
          arguments[0].top !== undefined
            ? ~~arguments[0].top
            : arguments[1] !== undefined
              ? ~~arguments[1]
              : this.scrollTop
        );

        return;
      }

      var left = arguments[0].left;
      var top = arguments[0].top;

      // LET THE SMOOTHNESS BEGIN!
      smoothScroll.call(
        this,
        this,
        typeof left === 'undefined' ? this.scrollLeft : ~~left,
        typeof top === 'undefined' ? this.scrollTop : ~~top
      );
    };

    // Element.prototype.scrollBy
    Element.prototype.scrollBy = function() {
      // avoid action when no arguments are passed
      if (arguments[0] === undefined) {
        return;
      }

      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0]) === true) {
        original.elementScroll.call(
          this,
          arguments[0].left !== undefined
            ? ~~arguments[0].left + this.scrollLeft
            : ~~arguments[0] + this.scrollLeft,
          arguments[0].top !== undefined
            ? ~~arguments[0].top + this.scrollTop
            : ~~arguments[1] + this.scrollTop
        );

        return;
      }

      this.scroll({
        left: ~~arguments[0].left + this.scrollLeft,
        top: ~~arguments[0].top + this.scrollTop,
        behavior: arguments[0].behavior
      });
    };

    // Element.prototype.scrollIntoView
    Element.prototype.scrollIntoView = function() {
      // avoid smooth behavior if not required
      if (shouldBailOut(arguments[0]) === true) {
        original.scrollIntoView.call(
          this,
          arguments[0] === undefined
            ? true
            : arguments[0]
        );

        return;
      }

      // LET THE SMOOTHNESS BEGIN!
      var scrollableParent = findScrollableParent(this);
      var parentRects = scrollableParent.getBoundingClientRect();
      var clientRects = this.getBoundingClientRect();

      if (scrollableParent !== d.body) {
        // reveal element inside parent
        smoothScroll.call(
          this,
          scrollableParent,
          scrollableParent.scrollLeft + clientRects.left - parentRects.left,
          scrollableParent.scrollTop + clientRects.top - parentRects.top
        );

        // reveal parent in viewport unless is fixed
        if (w.getComputedStyle(scrollableParent).position !== 'fixed') {
          w.scrollBy({
            left: parentRects.left,
            top: parentRects.top,
            behavior: 'smooth'
          });
        }
      } else {
        // reveal element in viewport
        w.scrollBy({
          left: clientRects.left,
          top: clientRects.top,
          behavior: 'smooth'
        });
      }
    };
  }

  if (typeof exports === 'object') {
    // commonjs
    module.exports = { polyfill: polyfill };
  } else {
    // global
    polyfill();
  }
})(window, document);
<|endoftext|>


<commit_before><commit_msg>refactor(driver-sessions): explicit disallow use of ended sessions<commit_after><|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * A scenery node that is used to represent a draggable Measuring Tape.
 * It contains a tip and a base that can be dragged separately,
 * with a text indicating the measurement.
 * The motion of the measuring tape can be confined by drag bounds.
 *
 * @author Vasily Shakhov (Mlearner)
 * @author Siddhartha Chinthapally (ActualConcepts)
 * @author Aaron Davis (PhET)
 * @author Martin Veillette (Berea College)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Line = require( 'SCENERY/nodes/Line' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var Vector2 = require( 'DOT/Vector2' );

  // images
  var measuringTapeImage = require( 'image!SCENERY_PHET/measuringTape.png' );

  /**
   * Constructor for the measuring tape
   * @param {Bounds2} dragBounds - for the measuring tape (in the ScreenView Coordinates reference frame)
   * @param {Property.<number>} scaleProperty - ratio of scenery coordinates (view) over the model coordinates.
   * @param {Property.<Object>} unitsProperty - it has two fields, (1) name <string>  and (2) multiplier <number>, eg. {name: 'cm', multiplier: 100},
   * @param {Property.<boolean>} isVisibleProperty
   * @param {Object} [options]
   * @constructor
   */
  function MeasuringTape( dragBounds, scaleProperty, unitsProperty, isVisibleProperty, options ) {
    var measuringTape = this;

    Node.call( this );
    var options = _.extend( {
      basePosition: new Vector2( 40, 40 ), // base Position in View units (rightBottom position of the measuring tape image)
      unrolledTapeDistance: 100, // in scenery coordinates
      angle: 0.0, // angle of the tape in radians, recall that in the view, a positive angle means clockwise rotation.
      textPosition: new Vector2( 0, 24 ), // position of the text relative to center of the base image in view units
      significantFigures: 1,  // number of significant figures in the length measurement
      textColor: 'white',  // color of the length measurement and unit
      textFont: new PhetFont( {size: 16, weight: 'bold'} ), // font for the measurement text
      baseScale: 0.8, // control the size of the measuringTape Image (the base)
      lineColor: 'gray',  // color of the tapeline itself
      tapeLineWidth: 2, // linewidth of the tape line
      tipCircleColor: 'rgba(0,0,0,0.1)', // color of the circle at the tip
      tipCircleRadius: 10, // radius of the circle on the tip
      crosshairColor: 'rgba(224, 95, 32, 1)', // orange, color of the two crosshairs
      crosshairSize: 5,  // size of the crosshairs in scenery coordinates ( measured from center)
      crosshairLineWidth: 2, // linewidth of the crosshairs
      isBaseCrosshairRotating: true, // do crosshairs rotate around their own axis to line up with the tapeline
      isTipCrosshairRotating: true // do crosshairs rotate around their own axis to line up with the tapeline
    }, options );

    var erodedDragBounds = dragBounds.eroded( 4 );

    this.significantFigures = options.significantFigures;
    this.unitsProperty = unitsProperty; // @private
    this.scaleProperty = scaleProperty;  // @private
    this.tipToBaseDistance = options.unrolledTapeDistance; // @private

    this.basePositionProperty = new Property( options.basePosition );
    this.tipPositionProperty = new Property( options.basePosition.plus( Vector2.createPolar( options.unrolledTapeDistance, options.angle ) ) );

    var crosshairShape = new Shape().
      moveTo( -options.crosshairSize, 0 ).
      lineTo( options.crosshairSize, 0 ).
      moveTo( 0, -options.crosshairSize ).
      lineTo( 0, options.crosshairSize );

    var baseCrosshair = new Path( crosshairShape, {
      stroke: options.crosshairColor,
      lineWidth: options.crosshairLineWidth
    } );

    var tipCrosshair = new Path( crosshairShape, {
      stroke: options.crosshairColor,
      lineWidth: options.crosshairLineWidth
    } );

    var tipCircle = new Circle( options.tipCircleRadius, {fill: options.tipCircleColor} );

    var baseImage = new Image( measuringTapeImage, {
      scale: options.baseScale,
      cursor: 'pointer'
    } );

    // create tapeline (running from one crosshair to the other)
    var tapeLine = new Line( this.basePositionProperty.value, this.tipPositionProperty.value, {
      stroke: options.lineColor,
      lineWidth: options.tapeLineWidth
    } );

    // add tipCrosshair and tipCircle to the tip
    var tip = new Node( {children: [tipCircle, tipCrosshair], cursor: 'pointer'} );

    // create text
    var labelText = new Text( measuringTape.getText(), {
      font: options.textFont,
      fill: options.textColor
    } );

    // declare new variable for rotation purposes, used in update function
    var angle = options.angle;
    var oldAngle;
    var deltaAngle;

    /**
     * Update the measuring tape
     *
     * @param {Vector2} basePosition
     * @param {Vector2} tipPosition
     */
    this.update = function( basePosition, tipPosition ) {
      oldAngle = angle;
      angle = Math.atan2( tipPosition.y - basePosition.y, tipPosition.x - basePosition.x );
      deltaAngle = angle - oldAngle;

      // set position of the tip and the base crosshair
      baseCrosshair.center = basePosition;
      tip.center = tipPosition;

      // in order to avoid all kind of geometrical issues with position, let's reset the baseImage upright and then set its position and rotation
      baseImage.setRotation( 0 );
      baseImage.rightBottom = basePosition;
      baseImage.rotateAround( basePosition, angle );

      // reset the text
      measuringTape.tipToBaseDistance = tipPosition.distance( basePosition );
      labelText.centerTop = baseImage.center.plus( options.textPosition );
      labelText.setText( measuringTape.getText() );

      // reposition the tapeline
      tapeLine.setLine( basePosition.x, basePosition.y, tipPosition.x, tipPosition.y );

      // rotate the crosshairs
      if ( options.isTipCrosshairRotating ) {
        tip.rotateAround( tip.center, deltaAngle );
      }
      if ( options.isBaseCrosshairRotating ) {
        baseCrosshair.rotateAround( baseCrosshair.center, deltaAngle );
      }

    };

    // update the tip and base positions, orientation of base, the crosshairs and tapeline
    this.update( this.basePositionProperty.value, this.tipPositionProperty.value );

    // expand the area for touch
    tip.touchArea = tip.localBounds.dilatedXY( 10, 10 );
    baseImage.touchArea = baseImage.localBounds.dilatedXY( 10, 10 );

    this.addChild( tapeLine ); // tapeline going from one crosshair to the other
    this.addChild( baseCrosshair ); // crosshair near the base, (set at basePosition)
    this.addChild( baseImage );  // base of the measuring tape
    this.addChild( labelText ); // text
    this.addChild( tip ); // crosshair and circle at the tip (set at tipPosition)

    var startOffset;
    baseImage.addInputListener( new SimpleDragHandler( {
        allowTouchSnag: true,
        start: function( event, trail ) {

          // we want to find the ScreenView Node in order to enforce Bound constraints. Let's not make the assumption that it is the parent Node

          // Find the parent screen by moving up the scene graph.
          var testNode = measuringTape;
          while ( testNode !== null ) {
            if ( testNode instanceof ScreenView ) {
              this.parentScreen = testNode;
              break;
            }
            testNode = testNode.parents[0]; // Move up the scene graph by one level
          }

          // Determine the initial position of the new element as a function of the event position and this node's bounds.
          var upperLeftCornerGlobal = measuringTape.parentToGlobalPoint( measuringTape.basePositionProperty.value );
          var initialPositionOffset = upperLeftCornerGlobal.minus( event.pointer.point );
          // initial position in screenView coordinates
          var initialPosition = this.parentScreen.globalToLocalPoint( event.pointer.point.plus( initialPositionOffset ) );

          startOffset = initialPosition.minus( measuringTape.basePositionProperty.value );
        },

        translate: function( translationParams ) {
          // keep a reference to the old position
          var oldPosition = measuringTape.basePositionProperty.value;

          // position of the base crosshair (in screen View coordinates) if we didn't enforce bounds
          var screenViewBasePosition = oldPosition.plus( translationParams.delta ).plus( startOffset );

          // constrained  basePosition (aka. the base crosshair location) to be within bounds
          var constrainedLocation = constrainBounds( screenViewBasePosition, erodedDragBounds );

          // recast the position in the local coordinate frame
          measuringTape.basePositionProperty.value = constrainedLocation.minus( startOffset );

          // if the tip is not being dragged then let's move the position of the tip as well
          if ( !isDraggingTip ) {
            var deltaPosition = measuringTape.basePositionProperty.value.minus( oldPosition );
            measuringTape.tipPositionProperty.value = measuringTape.tipPositionProperty.value.plus( deltaPosition );
          }

          // update positions of the crosshairs, text, tapeline and rotation
          measuringTape.update( measuringTape.basePositionProperty.value, measuringTape.tipPositionProperty.value );
        }
      } )
    );

    // when the user is not holding onto the tip, dragging the body will also drag the tip
    var isDraggingTip = false;

    // init drag and drop for tip
    tip.addInputListener( new SimpleDragHandler( {
      allowTouchSnag: true,

      start: function( event, trail ) {
        isDraggingTip = true;
      },

      translate: function( translationParams ) {
        measuringTape.tipPositionProperty.value = measuringTape.tipPositionProperty.value.plus( translationParams.delta );

        // update positions of the crosshairs, text, tapeline and rotation
        measuringTape.update( measuringTape.basePositionProperty.value, measuringTape.tipPositionProperty.value );
      },

      end: function( event, trail ) {
        isDraggingTip = false;
      }
    } ) );

    // link visibility of this node
    isVisibleProperty.linkAttribute( this, 'visible' );

    unitsProperty.link( function( units ) {
      labelText.setText( measuringTape.getText() );
    } );

    scaleProperty.link( function( scale ) {
      labelText.setText( measuringTape.getText() );
    } );

    /**
     * Constrains a point to some bounds.
     * @param {Vector2} point
     * @param {Bounds2} bounds
     */
    function constrainBounds( point, bounds ) {
      if ( _.isUndefined( bounds ) || bounds.containsPoint( point ) ) {
        return point;
      }
      else {
        var xConstrained = Math.max( Math.min( point.x, bounds.maxX ), bounds.minX );
        var yConstrained = Math.max( Math.min( point.y, bounds.maxY ), bounds.minY );
        return new Vector2( xConstrained, yConstrained );
      }
    }
  }

  return inherit( Node, MeasuringTape, {
    /**
     *  reset the MeasuringTape to its initial configuration
     *  @public
     */
    reset: function() {
      this.basePositionProperty.reset();
      this.tipPositionProperty.reset();
      this.update( this.basePositionProperty.value, this.tipPositionProperty.value );
    },

    /**
     * returns a readout of the current measurement
     * @public
     * @returns {string}
     */
    getText: function() {
      return Util.toFixed( this.unitsProperty.value.multiplier * this.tipToBaseDistance / this.scaleProperty.value,
          this.significantFigures ) + ' ' + this.unitsProperty.value.name;
    }

  } );
} );

<commit_msg>Fixed lint issue in MeasuringTape.js<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * A scenery node that is used to represent a draggable Measuring Tape.
 * It contains a tip and a base that can be dragged separately,
 * with a text indicating the measurement.
 * The motion of the measuring tape can be confined by drag bounds.
 *
 * @author Vasily Shakhov (Mlearner)
 * @author Siddhartha Chinthapally (ActualConcepts)
 * @author Aaron Davis (PhET)
 * @author Martin Veillette (Berea College)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var Image = require( 'SCENERY/nodes/Image' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Line = require( 'SCENERY/nodes/Line' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var Vector2 = require( 'DOT/Vector2' );

  // images
  var measuringTapeImage = require( 'image!SCENERY_PHET/measuringTape.png' );

  /**
   * Constructor for the measuring tape
   * @param {Bounds2} dragBounds - for the measuring tape (in the ScreenView Coordinates reference frame)
   * @param {Property.<number>} scaleProperty - ratio of scenery coordinates (view) over the model coordinates.
   * @param {Property.<Object>} unitsProperty - it has two fields, (1) name <string>  and (2) multiplier <number>, eg. {name: 'cm', multiplier: 100},
   * @param {Property.<boolean>} isVisibleProperty
   * @param {Object} [options]
   * @constructor
   */
  function MeasuringTape( dragBounds, scaleProperty, unitsProperty, isVisibleProperty, options ) {
    var measuringTape = this;

    Node.call( this );
    options = _.extend( {
      basePosition: new Vector2( 40, 40 ), // base Position in View units (rightBottom position of the measuring tape image)
      unrolledTapeDistance: 100, // in scenery coordinates
      angle: 0.0, // angle of the tape in radians, recall that in the view, a positive angle means clockwise rotation.
      textPosition: new Vector2( 0, 24 ), // position of the text relative to center of the base image in view units
      significantFigures: 1,  // number of significant figures in the length measurement
      textColor: 'white',  // color of the length measurement and unit
      textFont: new PhetFont( {size: 16, weight: 'bold'} ), // font for the measurement text
      baseScale: 0.8, // control the size of the measuringTape Image (the base)
      lineColor: 'gray',  // color of the tapeline itself
      tapeLineWidth: 2, // linewidth of the tape line
      tipCircleColor: 'rgba(0,0,0,0.1)', // color of the circle at the tip
      tipCircleRadius: 10, // radius of the circle on the tip
      crosshairColor: 'rgba(224, 95, 32, 1)', // orange, color of the two crosshairs
      crosshairSize: 5,  // size of the crosshairs in scenery coordinates ( measured from center)
      crosshairLineWidth: 2, // linewidth of the crosshairs
      isBaseCrosshairRotating: true, // do crosshairs rotate around their own axis to line up with the tapeline
      isTipCrosshairRotating: true // do crosshairs rotate around their own axis to line up with the tapeline
    }, options );

    var erodedDragBounds = dragBounds.eroded( 4 );

    this.significantFigures = options.significantFigures;
    this.unitsProperty = unitsProperty; // @private
    this.scaleProperty = scaleProperty;  // @private
    this.tipToBaseDistance = options.unrolledTapeDistance; // @private

    this.basePositionProperty = new Property( options.basePosition );
    this.tipPositionProperty = new Property( options.basePosition.plus( Vector2.createPolar( options.unrolledTapeDistance, options.angle ) ) );

    var crosshairShape = new Shape().
      moveTo( -options.crosshairSize, 0 ).
      lineTo( options.crosshairSize, 0 ).
      moveTo( 0, -options.crosshairSize ).
      lineTo( 0, options.crosshairSize );

    var baseCrosshair = new Path( crosshairShape, {
      stroke: options.crosshairColor,
      lineWidth: options.crosshairLineWidth
    } );

    var tipCrosshair = new Path( crosshairShape, {
      stroke: options.crosshairColor,
      lineWidth: options.crosshairLineWidth
    } );

    var tipCircle = new Circle( options.tipCircleRadius, {fill: options.tipCircleColor} );

    var baseImage = new Image( measuringTapeImage, {
      scale: options.baseScale,
      cursor: 'pointer'
    } );

    // create tapeline (running from one crosshair to the other)
    var tapeLine = new Line( this.basePositionProperty.value, this.tipPositionProperty.value, {
      stroke: options.lineColor,
      lineWidth: options.tapeLineWidth
    } );

    // add tipCrosshair and tipCircle to the tip
    var tip = new Node( {children: [tipCircle, tipCrosshair], cursor: 'pointer'} );

    // create text
    var labelText = new Text( measuringTape.getText(), {
      font: options.textFont,
      fill: options.textColor
    } );

    // declare new variable for rotation purposes, used in update function
    var angle = options.angle;
    var oldAngle;
    var deltaAngle;

    /**
     * Update the measuring tape
     *
     * @param {Vector2} basePosition
     * @param {Vector2} tipPosition
     */
    this.update = function( basePosition, tipPosition ) {
      oldAngle = angle;
      angle = Math.atan2( tipPosition.y - basePosition.y, tipPosition.x - basePosition.x );
      deltaAngle = angle - oldAngle;

      // set position of the tip and the base crosshair
      baseCrosshair.center = basePosition;
      tip.center = tipPosition;

      // in order to avoid all kind of geometrical issues with position, let's reset the baseImage upright and then set its position and rotation
      baseImage.setRotation( 0 );
      baseImage.rightBottom = basePosition;
      baseImage.rotateAround( basePosition, angle );

      // reset the text
      measuringTape.tipToBaseDistance = tipPosition.distance( basePosition );
      labelText.centerTop = baseImage.center.plus( options.textPosition );
      labelText.setText( measuringTape.getText() );

      // reposition the tapeline
      tapeLine.setLine( basePosition.x, basePosition.y, tipPosition.x, tipPosition.y );

      // rotate the crosshairs
      if ( options.isTipCrosshairRotating ) {
        tip.rotateAround( tip.center, deltaAngle );
      }
      if ( options.isBaseCrosshairRotating ) {
        baseCrosshair.rotateAround( baseCrosshair.center, deltaAngle );
      }

    };

    // update the tip and base positions, orientation of base, the crosshairs and tapeline
    this.update( this.basePositionProperty.value, this.tipPositionProperty.value );

    // expand the area for touch
    tip.touchArea = tip.localBounds.dilatedXY( 10, 10 );
    baseImage.touchArea = baseImage.localBounds.dilatedXY( 10, 10 );

    this.addChild( tapeLine ); // tapeline going from one crosshair to the other
    this.addChild( baseCrosshair ); // crosshair near the base, (set at basePosition)
    this.addChild( baseImage );  // base of the measuring tape
    this.addChild( labelText ); // text
    this.addChild( tip ); // crosshair and circle at the tip (set at tipPosition)

    var startOffset;
    baseImage.addInputListener( new SimpleDragHandler( {
        allowTouchSnag: true,
        start: function( event, trail ) {

          // we want to find the ScreenView Node in order to enforce Bound constraints. Let's not make the assumption that it is the parent Node

          // Find the parent screen by moving up the scene graph.
          var testNode = measuringTape;
          while ( testNode !== null ) {
            if ( testNode instanceof ScreenView ) {
              this.parentScreen = testNode;
              break;
            }
            testNode = testNode.parents[0]; // Move up the scene graph by one level
          }

          // Determine the initial position of the new element as a function of the event position and this node's bounds.
          var upperLeftCornerGlobal = measuringTape.parentToGlobalPoint( measuringTape.basePositionProperty.value );
          var initialPositionOffset = upperLeftCornerGlobal.minus( event.pointer.point );
          // initial position in screenView coordinates
          var initialPosition = this.parentScreen.globalToLocalPoint( event.pointer.point.plus( initialPositionOffset ) );

          startOffset = initialPosition.minus( measuringTape.basePositionProperty.value );
        },

        translate: function( translationParams ) {
          // keep a reference to the old position
          var oldPosition = measuringTape.basePositionProperty.value;

          // position of the base crosshair (in screen View coordinates) if we didn't enforce bounds
          var screenViewBasePosition = oldPosition.plus( translationParams.delta ).plus( startOffset );

          // constrained  basePosition (aka. the base crosshair location) to be within bounds
          var constrainedLocation = constrainBounds( screenViewBasePosition, erodedDragBounds );

          // recast the position in the local coordinate frame
          measuringTape.basePositionProperty.value = constrainedLocation.minus( startOffset );

          // if the tip is not being dragged then let's move the position of the tip as well
          if ( !isDraggingTip ) {
            var deltaPosition = measuringTape.basePositionProperty.value.minus( oldPosition );
            measuringTape.tipPositionProperty.value = measuringTape.tipPositionProperty.value.plus( deltaPosition );
          }

          // update positions of the crosshairs, text, tapeline and rotation
          measuringTape.update( measuringTape.basePositionProperty.value, measuringTape.tipPositionProperty.value );
        }
      } )
    );

    // when the user is not holding onto the tip, dragging the body will also drag the tip
    var isDraggingTip = false;

    // init drag and drop for tip
    tip.addInputListener( new SimpleDragHandler( {
      allowTouchSnag: true,

      start: function( event, trail ) {
        isDraggingTip = true;
      },

      translate: function( translationParams ) {
        measuringTape.tipPositionProperty.value = measuringTape.tipPositionProperty.value.plus( translationParams.delta );

        // update positions of the crosshairs, text, tapeline and rotation
        measuringTape.update( measuringTape.basePositionProperty.value, measuringTape.tipPositionProperty.value );
      },

      end: function( event, trail ) {
        isDraggingTip = false;
      }
    } ) );

    // link visibility of this node
    isVisibleProperty.linkAttribute( this, 'visible' );

    unitsProperty.link( function( units ) {
      labelText.setText( measuringTape.getText() );
    } );

    scaleProperty.link( function( scale ) {
      labelText.setText( measuringTape.getText() );
    } );

    /**
     * Constrains a point to some bounds.
     * @param {Vector2} point
     * @param {Bounds2} bounds
     */
    function constrainBounds( point, bounds ) {
      if ( _.isUndefined( bounds ) || bounds.containsPoint( point ) ) {
        return point;
      }
      else {
        var xConstrained = Math.max( Math.min( point.x, bounds.maxX ), bounds.minX );
        var yConstrained = Math.max( Math.min( point.y, bounds.maxY ), bounds.minY );
        return new Vector2( xConstrained, yConstrained );
      }
    }
  }

  return inherit( Node, MeasuringTape, {
    /**
     *  reset the MeasuringTape to its initial configuration
     *  @public
     */
    reset: function() {
      this.basePositionProperty.reset();
      this.tipPositionProperty.reset();
      this.update( this.basePositionProperty.value, this.tipPositionProperty.value );
    },

    /**
     * returns a readout of the current measurement
     * @public
     * @returns {string}
     */
    getText: function() {
      return Util.toFixed( this.unitsProperty.value.multiplier * this.tipToBaseDistance / this.scaleProperty.value,
          this.significantFigures ) + ' ' + this.unitsProperty.value.name;
    }

  } );
} );

<|endoftext|>


<commit_before>const gulp 			= require('gulp')
const fs 			= require('fs')
const request 		= require('request')
const configPath	= process.cwd() + '/data/config.json'
const config     	= JSON.parse(fs.readFileSync(configPath, 'utf8'))
const methode 		= config.copy.methode
let imagesToDownload = []

const getImageDirectory = () =>
	`assets/${methode.imageDirectory || ''}`

const createPicturefill = ({ name, extension, caption, imageSizes, imageDirectory }) => {
	const srcset = methode.imageLibrary === 'picturefill' ? 'srcset' : 'data-srcset'
	const lazyload = methode.imageLibrary === 'picturefill' ? '' : 'lazyload'
	const defaultSrc = `${imageDirectory}${name}_${imageSizes[0]}.${extension}`

	const getPath = (sz) => `${imageDirectory}${name}_${sz}.${extension}`
	const getSource = (src, mq) => `<source ${srcset}='${src}' media='(min-width:${mq}px)'>`

	return `
		<picture>

			${
				imageSizes.reverse().map((sz, index) => {
					const src = getPath(sz)
					const mq = index > 0 ? Math.floor(imageSizes[index - 1] / 1.5) : 1
					return getSource(src, mq)
				}).join('')
			}

		</picture>
	`.trim()
}

const createFigureSource = ({ name, extension, caption, imageSizes }) => {
	const imageDirectory = getImageDirectory()

	if (methode.imageLibrary) {
		return createPicturefill({ name, extension, caption, imageSizes, imageDirectory })
	}
	// plain old image default
	const src = `${imageDirectory}${name}_${imageSizes[0]}.${extension}`
	return `<img src='${src}' alt='${caption}' />`
}

const getImageInfo = (imgPath) => {
	const imgFull = imgPath.substr(imgPath.lastIndexOf('/') + 1)
	const imgSplit = imgFull.split('.')
	const name = imgSplit[0]
	const extension = imgSplit[1]
	return { name, extension }
}

const replaceImageSize = ({ imgPath, imageSize }) =>
	imgPath.replace(/image_(.+\d)w/g, `image_${imageSize}w`)

const createFigure = ({ href, credit, caption, alt }) => {
	const imageSizes = methode.imageSizes || [1200]
	const imgPath = href.split('?')[0]
	const { name, extension } = getImageInfo(imgPath)

	// start generating markup
	const src = createFigureSource({ name, extension, caption, imageSizes })

	// add to download queue
	const images = imageSizes.map(imageSize => ({
		url: `http:${replaceImageSize({ imgPath, imageSize })}`,
		filename: `${name}_${imageSize}.${extension}`,
	}))

	imagesToDownload = imagesToDownload.concat(images)

	return `

			${src}
			${credit}

${caption}

	`.trim()
}

const cleanP = (content) => {
	const withoutOpenSpan = content.replace(//g, '')
	const withoutCloseSpan = withoutOpenSpan.replace(/<\/span>/g, '')
	return withoutCloseSpan
}

const createContentMarkup = (item) => {
	const types = {
		p: ({ content }) => `<p class='methode-graf'>${cleanP(content)}</p>`,
		image: ({ href, credit, caption, alt }) => createFigure({ href, credit, caption, alt }),
		ad: () => `<div class='ad'></div>`,
	}

	return types[item.type](item)
}

const createHTML = (stories) => {
	const html = stories.map(story => {
		const { content } = story.body
		// go thru item in content and create the proper markup
		return content.map(createContentMarkup).join('\n')
	})

	return html.join('\n')
}

const writeHTML = (html) =>
	fs.writeFileSync('src/html/partials/graphic/methode.hbs', html)

const downloadImages = (cb) => {
	const promises = imagesToDownload.map(image =>
		new Promise((resolve, reject) => {
			const { url, filename } = image
			const path = `src/${getImageDirectory()}/${filename}`
			try {
				const exists = fs.lstatSync(path)
				resolve()
			} catch (e) {
				console.log(`downloading: ${url}`)
				request(url, {encoding: 'binary'}, (error, response, body) => {
					if (error) reject(error)
					else {
						fs.writeFile(path, body, 'binary', (err) => {
							if (err) reject(err)
							else resolve()
						})
					}
				})
			}
		})
	)

	Promise.all(promises)
		.then(result => cb())
		.catch(error => {
			console.log(error)
			cb()
		})
}

const fetchMethode = (cb) => {
	const base = 'http://sports2.devedit.bostonglobe.com/precursor/story/'
	const promises = methode.story.map(story =>
		new Promise((resolve, reject) =>
			request(`${base}${story.loid}`, (error, response, body) => {
				if (!error && response.statusCode === 200) {
					resolve({...story, body: JSON.parse(body)})
				} else {
					reject(error)
				}
			})
		)
	)

	Promise.all(promises)
		.then(results => {
			const html = createHTML(results)
			writeHTML(html)
			downloadImages(cb)
		})
		.catch(error => {
			console.error(error)
			cb()
		})
}

gulp.task('fetch-methode', (cb) => {
	if (methode.story.length) fetchMethode(cb)
	else {
		console.error('No methode story in config')
		cb()
	}
})
<commit_msg>formatting<commit_after>const gulp 			= require('gulp')
const fs 			= require('fs')
const request 		= require('request')

const configPath = process.cwd() + '/data/config.json'
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
const methode = config.copy.methode
let imagesToDownload = []

const getImageDirectory = () =>
	`assets/${methode.imageDirectory || ''}`

const createPicturefill = ({ name, extension, caption, imageSizes, imageDirectory }) => {
	const srcset = methode.imageLibrary === 'picturefill' ? 'srcset' : 'data-srcset'
	const lazyload = methode.imageLibrary === 'picturefill' ? '' : 'lazyload'
	const defaultSrc = `${imageDirectory}${name}_${imageSizes[0]}.${extension}`

	const getPath = (sz) => `${imageDirectory}${name}_${sz}.${extension}`
	const getSource = (src, mq) => `<source ${srcset}='${src}' media='(min-width:${mq}px)'>`

	return `
		<picture>

			${
				imageSizes.reverse().map((sz, index) => {
					const src = getPath(sz)
					const mq = index > 0 ? Math.floor(imageSizes[index - 1] / 1.5) : 1
					return getSource(src, mq)
				}).join('')
			}

		</picture>
	`.trim()
}

const createFigureSource = ({ name, extension, caption, imageSizes }) => {
	const imageDirectory = getImageDirectory()

	if (methode.imageLibrary) {
		return createPicturefill({ name, extension, caption, imageSizes, imageDirectory })
	}
	// plain old image default
	const src = `${imageDirectory}${name}_${imageSizes[0]}.${extension}`
	return `<img src='${src}' alt='${caption}' />`
}

const getImageInfo = (imgPath) => {
	const imgFull = imgPath.substr(imgPath.lastIndexOf('/') + 1)
	const imgSplit = imgFull.split('.')
	const name = imgSplit[0]
	const extension = imgSplit[1]
	return { name, extension }
}

const replaceImageSize = ({ imgPath, imageSize }) =>
	imgPath.replace(/image_(.+\d)w/g, `image_${imageSize}w`)

const createFigure = ({ href, credit, caption, alt }) => {
	const imageSizes = methode.imageSizes || [1200]
	const imgPath = href.split('?')[0]
	const { name, extension } = getImageInfo(imgPath)

	// start generating markup
	const src = createFigureSource({ name, extension, caption, imageSizes })

	// add to download queue
	const images = imageSizes.map(imageSize => ({
		url: `http:${replaceImageSize({ imgPath, imageSize })}`,
		filename: `${name}_${imageSize}.${extension}`,
	}))

	imagesToDownload = imagesToDownload.concat(images)

	return `

			${src}
			${credit}

${caption}

	`.trim()
}

const cleanP = (content) => {
	const withoutOpenSpan = content.replace(//g, '')
	const withoutCloseSpan = withoutOpenSpan.replace(/<\/span>/g, '')
	return withoutCloseSpan
}

const createContentMarkup = (item) => {
	const types = {
		p: ({ content }) => `<p class='methode-graf'>${cleanP(content)}</p>`,
		image: ({ href, credit, caption, alt }) => createFigure({ href, credit, caption, alt }),
		ad: () => `<div class='ad'></div>`,
	}

	return types[item.type](item)
}

const createHTML = (stories) => {
	const html = stories.map(story => {
		const { content } = story.body
		// go thru item in content and create the proper markup
		return content.map(createContentMarkup).join('\n')
	})

	return html.join('\n')
}

const writeHTML = (html) =>
	fs.writeFileSync('src/html/partials/graphic/methode.hbs', html)

const downloadImages = (cb) => {
	const promises = imagesToDownload.map(image =>
		new Promise((resolve, reject) => {
			const { url, filename } = image
			const path = `src/${getImageDirectory()}/${filename}`
			try {
				const exists = fs.lstatSync(path)
				resolve()
			} catch (e) {
				console.log(`downloading: ${url}`)
				request(url, {encoding: 'binary'}, (error, response, body) => {
					if (error) reject(error)
					else {
						fs.writeFile(path, body, 'binary', (err) => {
							if (err) reject(err)
							else resolve()
						})
					}
				})
			}
		})
	)

	Promise.all(promises)
		.then(result => cb())
		.catch(error => {
			console.log(error)
			cb()
		})
}

const fetchMethode = (cb) => {
	const base = 'http://sports2.devedit.bostonglobe.com/precursor/story/'
	const promises = methode.story.map(story =>
		new Promise((resolve, reject) =>
			request(`${base}${story.loid}`, (error, response, body) => {
				if (!error && response.statusCode === 200) {
					resolve({...story, body: JSON.parse(body)})
				} else {
					reject(error)
				}
			})
		)
	)

	Promise.all(promises)
		.then(results => {
			const html = createHTML(results)
			writeHTML(html)
			downloadImages(cb)
		})
		.catch(error => {
			console.error(error)
			cb()
		})
}

gulp.task('fetch-methode', (cb) => {
	if (methode.story.length) fetchMethode(cb)
	else {
		console.error('No methode story in config')
		cb()
	}
})
<|endoftext|>


<commit_before>CollectionAPI = function(options) {
  var self = this;

  self.version = '0.1.17';
  self._url = Npm.require('url');
  self._querystring = Npm.require('querystring');
  self._fiber = Npm.require('fibers');
  self._collections = {};
  self.options = {
    apiPath: 'collectionapi',
    standAlone: false,
    sslEnabled: false,
    listenPort: 3005,
    listenHost: undefined,
    authToken: undefined,
    privateKeyFile: 'privatekey.pem',
    certificateFile: 'certificate.pem'
  };
  _.extend(self.options, options || {});
};

CollectionAPI.prototype.addCollection = function(collection, path, options) {
  var self = this;

  var collectionOptions = {};
  collectionOptions[path] = {
    collection: collection,
    options: options || {}
  };
  _.extend(self._collections, collectionOptions);
};

CollectionAPI.prototype.start = function() {
  var self = this;
  var httpServer, httpOptions, scheme;

  var startupMessage = 'Collection API v' + self.version;

  if (self.options.standAlone === true) {
    if (self.options.sslEnabled === true) {
      scheme = 'https://';
      httpServer = Npm.require('https');
      var fs = Npm.require('fs');

      httpOptions = {
        key: fs.readFileSync(self.options.privateKeyFile),
        cert: fs.readFileSync(self.options.certificateFile)
      };
    } else {
      scheme = 'http://';
      httpServer = Npm.require('http');
    }

    self._httpServer = httpServer.createServer(httpOptions);
    self._httpServer.addListener('request', function(request, response) { new CollectionAPI._requestListener(self, request, response); });
    self._httpServer.listen(self.options.listenPort, self.options.listenHost);
    console.log(startupMessage + ' running as a stand-alone server on ' +  scheme + (self.options.listenHost || 'localhost') + ':' + self.options.listenPort + '/' + (self.options.apiPath || ''));
  } else {

    RoutePolicy.declare('/' + this.options.apiPath + '/', 'network');

    WebApp.connectHandlers.use(function(req, res, next) {
      if (req.url.split('/')[1] !== self.options.apiPath) {
        next();
        return;
      }
      self._fiber(function () {
        new CollectionAPI._requestListener(self, req, res);
      }).run();
    });

    console.log(startupMessage + ' running at /' + this.options.apiPath);
  }
};

CollectionAPI.prototype._collectionOptions = function(requestPath) {
  var self = this;
  return self._collections[requestPath.collectionPath] ? self._collections[requestPath.collectionPath].options : undefined;
};

CollectionAPI._requestListener = function (server, request, response) {
  var self = this;

  self._server = server;
  self._request = request;
  self._response = response;

  self._requestUrl = self._server._url.parse(self._request.url, true);

  // Check for the X-Auth-Token header or auth-token in the query string
  self._requestAuthToken = self._request.headers['x-auth-token'] ? self._request.headers['x-auth-token'] : self._server._querystring.parse(self._requestUrl.query)['auth-token'];

  var requestPath;
  var pathName = self._requestUrl.pathname;
  if (pathName.charAt(pathName.length - 1) === '/') {
    pathName = pathName.substring(0, pathName.length - 1);
  }

  if (self._server.options.standAlone === true && ! self._server.options.apiPath) {
    requestPath = pathName.split('/').slice(1);
  } else {
    requestPath = pathName.split('/').slice(2);
  }

  self._requestPath = {
    collectionPath: requestPath[0],
    collectionId: requestPath[1],
    fields: requestPath.slice(2),
    query: self._requestUrl.query
  };

  self._requestCollection = self._server._collections[self._requestPath.collectionPath] ? self._server._collections[self._requestPath.collectionPath].collection : undefined;

  if (!self._authenticate()) {
    return self._unauthorizedResponse('Invalid/Missing Auth Token');
  }

  if (!self._requestCollection) {
    return self._notFoundResponse('Collection Object Not Found');
  }

  return self._handleRequest();
};

CollectionAPI._requestListener.prototype._authenticate = function() {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  // Check the collection's auth token
  if (collectionOptions && collectionOptions.authToken) {
    return self._requestAuthToken === collectionOptions.authToken;
  }

  // Check the global auth token
  if (self._server.options.authToken) {
    return self._requestAuthToken === self._server.options.authToken;
  }

  return true;
};

CollectionAPI._requestListener.prototype._handleRequest = function() {
  var self = this;

  if (!self._requestMethodAllowed(self._request.method)) {
    return self._notSupportedResponse();
  }

  switch (self._request.method) {
  case 'GET':
    return self._getRequest();
  case 'POST':
    return self._postRequest();
  case 'PUT':
    return self._putRequest();
  case 'DELETE':
    return self._deleteRequest();
  default:
    return self._notSupportedResponse();
  }
};

CollectionAPI._requestListener.prototype._requestMethodAllowed = function (method) {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  if (collectionOptions && collectionOptions.methods) {
    return _.indexOf(collectionOptions.methods, method) >= 0;
  }

  return true;
};

CollectionAPI._requestListener.prototype._beforeHandling = function (method) {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  if (collectionOptions && collectionOptions.before && collectionOptions.before[method] &&  _.isFunction(collectionOptions.before[method])) {
    return collectionOptions.before[method].apply(self, _.rest(arguments));
  }

  return true;
}

CollectionAPI._requestListener.prototype._getRequest = function(fromPutRequest) {
  var self = this;

  self._server._fiber(function() {

    try {
      // TODO: A better way to do this?
      var collection_result = self._requestPath.collectionId !== undefined ? self._requestCollection.find(self._requestPath.collectionId) : self._requestCollection.find();

      var records = [];
      collection_result.forEach(function(record) {
        records.push(record);
      });

      if(!self._beforeHandling('GET',  self._requestPath.collectionId, records, self._requestPath.fields, self._requestPath.query)) {
        if (fromPutRequest) {
          return records.length ? self._noContentResponse() : self._notFoundResponse('No Record(s) Found');
        }
        return self._rejectedResponse("Could not get that collection/object.");
      }

      records = _.compact(records);

      if (records.length === 0) {
        return self._notFoundResponse('No Record(s) Found');
      }

      return self._okResponse(JSON.stringify(records));

    } catch (e) {
      return self._internalServerErrorResponse(e);
    }
  }).run();
};

CollectionAPI._requestListener.prototype._putRequest = function() {
  var self = this;

  if (! self._requestPath.collectionId) {
    return self._notFoundResponse('Missing _id');
  }

  var requestData = '';

  self._request.on('data', function(chunk) {
    requestData += chunk.toString();
  });

  self._request.on('end', function() {
    self._server._fiber(function() {
      try {
        var obj = JSON.parse(requestData);

        if(!self._beforeHandling('PUT',
                                 self._requestPath.collectionId,
                                 self._requestCollection.findOne(self._requestPath.collectionId),
                                 obj,
                                 self._requestPath.fields,
                                 self._requestPath.query)) {
          return self._rejectedResponse("Could not put that object.");
        }
        self._requestCollection.update(self._requestPath.collectionId, obj);
      } catch (e) {
        return self._internalServerErrorResponse(e);
      }
      if (self._requestPath.query.callback === "0") {
        return self._createdResponse(JSON.stringify({'status': 'success'}));
      } else {
        return self._getRequest('fromPutRequest');
      }
    }).run();
  });
};

CollectionAPI._requestListener.prototype._deleteRequest = function() {
  var self = this;

  if (! self._requestPath.collectionId) {
    return self._notFoundResponse('Missing _id');
  }

  self._server._fiber(function() {
    try {
      if(!self._beforeHandling('DELETE', self._requestPath.collectionId, self._requestCollection.findOne(self._requestPath.collectionId))) {
        return self._rejectedResponse("Could not delete that object.");
      }
      self._requestCollection.remove(self._requestPath.collectionId);
    } catch (e) {
      return self._internalServerErrorResponse(e);
    }
    return self._okResponse('');
  }).run();
};

CollectionAPI._requestListener.prototype._postRequest = function() {
  var self = this;
  var requestData = '';

  self._request.on('data', function(chunk) {
    requestData += chunk.toString();
  });

  self._request.on('end', function() {
    self._server._fiber(function() {
      try {
        var obj = JSON.parse(requestData);

        if(!self._beforeHandling('POST', obj)) {
          return self._rejectedResponse("Could not post that object.");
        }
        self._requestPath.collectionId = self._requestCollection.insert(obj);
      } catch (e) {
        return self._internalServerErrorResponse(e);
      }
      return self._createdResponse(JSON.stringify({_id: self._requestPath.collectionId}));
    }).run();
  });
};

CollectionAPI._requestListener.prototype._okResponse = function(body) {
  var self = this;
  self._sendResponse(200, body);
};

CollectionAPI._requestListener.prototype._createdResponse = function(body) {
  var self = this;
  self._sendResponse(201, body);
};

CollectionAPI._requestListener.prototype._noContentResponse = function() {
  var self = this;
  self._sendResponse(204, '');
};

CollectionAPI._requestListener.prototype._notSupportedResponse = function() {
  var self = this;
  self._sendResponse(501, '');
};

CollectionAPI._requestListener.prototype._unauthorizedResponse = function(body) {
  var self = this;
  self._sendResponse(401, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._notFoundResponse = function(body) {
  var self = this;
  self._sendResponse(404, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._rejectedResponse= function(body) {
  var self = this;
  self._sendResponse(409, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._internalServerErrorResponse = function(body) {
  var self = this;
  self._sendResponse(500, JSON.stringify({error: body.toString()}));
};

CollectionAPI._requestListener.prototype._sendResponse = function(statusCode, body) {
  var self = this;
  self._response.statusCode = statusCode;
  self._response.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'));
  self._response.setHeader('Content-Type', 'application/json');
  self._response.write(body);
  self._response.end();
};
<commit_msg>let POST request return a full object by passing to GET.<commit_after>CollectionAPI = function(options) {
  var self = this;

  self.version = '0.1.17';
  self._url = Npm.require('url');
  self._querystring = Npm.require('querystring');
  self._fiber = Npm.require('fibers');
  self._collections = {};
  self.options = {
    apiPath: 'collectionapi',
    standAlone: false,
    sslEnabled: false,
    listenPort: 3005,
    listenHost: undefined,
    authToken: undefined,
    privateKeyFile: 'privatekey.pem',
    certificateFile: 'certificate.pem'
  };
  _.extend(self.options, options || {});
};

CollectionAPI.prototype.addCollection = function(collection, path, options) {
  var self = this;

  var collectionOptions = {};
  collectionOptions[path] = {
    collection: collection,
    options: options || {}
  };
  _.extend(self._collections, collectionOptions);
};

CollectionAPI.prototype.start = function() {
  var self = this;
  var httpServer, httpOptions, scheme;

  var startupMessage = 'Collection API v' + self.version;

  if (self.options.standAlone === true) {
    if (self.options.sslEnabled === true) {
      scheme = 'https://';
      httpServer = Npm.require('https');
      var fs = Npm.require('fs');

      httpOptions = {
        key: fs.readFileSync(self.options.privateKeyFile),
        cert: fs.readFileSync(self.options.certificateFile)
      };
    } else {
      scheme = 'http://';
      httpServer = Npm.require('http');
    }

    self._httpServer = httpServer.createServer(httpOptions);
    self._httpServer.addListener('request', function(request, response) { new CollectionAPI._requestListener(self, request, response); });
    self._httpServer.listen(self.options.listenPort, self.options.listenHost);
    console.log(startupMessage + ' running as a stand-alone server on ' +  scheme + (self.options.listenHost || 'localhost') + ':' + self.options.listenPort + '/' + (self.options.apiPath || ''));
  } else {

    RoutePolicy.declare('/' + this.options.apiPath + '/', 'network');

    WebApp.connectHandlers.use(function(req, res, next) {
      if (req.url.split('/')[1] !== self.options.apiPath) {
        next();
        return;
      }
      self._fiber(function () {
        new CollectionAPI._requestListener(self, req, res);
      }).run();
    });

    console.log(startupMessage + ' running at /' + this.options.apiPath);
  }
};

CollectionAPI.prototype._collectionOptions = function(requestPath) {
  var self = this;
  return self._collections[requestPath.collectionPath] ? self._collections[requestPath.collectionPath].options : undefined;
};

CollectionAPI._requestListener = function (server, request, response) {
  var self = this;

  self._server = server;
  self._request = request;
  self._response = response;

  self._requestUrl = self._server._url.parse(self._request.url, true);

  // Check for the X-Auth-Token header or auth-token in the query string
  self._requestAuthToken = self._request.headers['x-auth-token'] ? self._request.headers['x-auth-token'] : self._server._querystring.parse(self._requestUrl.query)['auth-token'];

  var requestPath;
  var pathName = self._requestUrl.pathname;
  if (pathName.charAt(pathName.length - 1) === '/') {
    pathName = pathName.substring(0, pathName.length - 1);
  }

  if (self._server.options.standAlone === true && ! self._server.options.apiPath) {
    requestPath = pathName.split('/').slice(1);
  } else {
    requestPath = pathName.split('/').slice(2);
  }

  self._requestPath = {
    collectionPath: requestPath[0],
    collectionId: requestPath[1],
    fields: requestPath.slice(2),
    query: self._requestUrl.query
  };

  self._requestCollection = self._server._collections[self._requestPath.collectionPath] ? self._server._collections[self._requestPath.collectionPath].collection : undefined;

  if (!self._authenticate()) {
    return self._unauthorizedResponse('Invalid/Missing Auth Token');
  }

  if (!self._requestCollection) {
    return self._notFoundResponse('Collection Object Not Found');
  }

  return self._handleRequest();
};

CollectionAPI._requestListener.prototype._authenticate = function() {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  // Check the collection's auth token
  if (collectionOptions && collectionOptions.authToken) {
    return self._requestAuthToken === collectionOptions.authToken;
  }

  // Check the global auth token
  if (self._server.options.authToken) {
    return self._requestAuthToken === self._server.options.authToken;
  }

  return true;
};

CollectionAPI._requestListener.prototype._handleRequest = function() {
  var self = this;

  if (!self._requestMethodAllowed(self._request.method)) {
    return self._notSupportedResponse();
  }

  switch (self._request.method) {
  case 'GET':
    return self._getRequest();
  case 'POST':
    return self._postRequest();
  case 'PUT':
    return self._putRequest();
  case 'DELETE':
    return self._deleteRequest();
  default:
    return self._notSupportedResponse();
  }
};

CollectionAPI._requestListener.prototype._requestMethodAllowed = function (method) {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  if (collectionOptions && collectionOptions.methods) {
    return _.indexOf(collectionOptions.methods, method) >= 0;
  }

  return true;
};

CollectionAPI._requestListener.prototype._beforeHandling = function (method) {
  var self = this;
  var collectionOptions = self._server._collectionOptions(self._requestPath);

  if (collectionOptions && collectionOptions.before && collectionOptions.before[method] &&  _.isFunction(collectionOptions.before[method])) {
    return collectionOptions.before[method].apply(self, _.rest(arguments));
  }

  return true;
}

CollectionAPI._requestListener.prototype._getRequest = function(fromRequest) {
  var self = this;

  self._server._fiber(function() {

    try {
      // TODO: A better way to do this?
      var collection_result = self._requestPath.collectionId !== undefined ? self._requestCollection.find(self._requestPath.collectionId) : self._requestCollection.find();

      var records = [];
      collection_result.forEach(function(record) {
        records.push(record);
      });

      if(!self._beforeHandling('GET',  self._requestPath.collectionId, records, self._requestPath.fields, self._requestPath.query)) {
        if (fromRequest) {
          return records.length ? self._noContentResponse() : self._notFoundResponse('No Record(s) Found');
        }
        return self._rejectedResponse("Could not get that collection/object.");
      }

      records = _.compact(records);

      if (records.length === 0) {
        return self._notFoundResponse('No Record(s) Found');
      }

      if (fromRequest === "POST") {
        return self._createdResponse(JSON.stringify(records));
      }

      return self._okResponse(JSON.stringify(records));

    } catch (e) {
      return self._internalServerErrorResponse(e);
    }
  }).run();
};

CollectionAPI._requestListener.prototype._putRequest = function() {
  var self = this;

  if (! self._requestPath.collectionId) {
    return self._notFoundResponse('Missing _id');
  }

  var requestData = '';

  self._request.on('data', function(chunk) {
    requestData += chunk.toString();
  });

  self._request.on('end', function() {
    self._server._fiber(function() {
      try {
        var obj = JSON.parse(requestData);

        if(!self._beforeHandling('PUT',
                                 self._requestPath.collectionId,
                                 self._requestCollection.findOne(self._requestPath.collectionId),
                                 obj,
                                 self._requestPath.fields,
                                 self._requestPath.query)) {
          return self._rejectedResponse("Could not put that object.");
        }
        self._requestCollection.update(self._requestPath.collectionId, obj);
      } catch (e) {
        return self._internalServerErrorResponse(e);
      }
      if (self._requestPath.query.callback === "0") {
        return self._createdResponse(JSON.stringify({'status': 'success'}));
      } else {
        return self._getRequest('PUT');
      }
    }).run();
  });
};

CollectionAPI._requestListener.prototype._deleteRequest = function() {
  var self = this;

  if (! self._requestPath.collectionId) {
    return self._notFoundResponse('Missing _id');
  }

  self._server._fiber(function() {
    try {
      if(!self._beforeHandling('DELETE', self._requestPath.collectionId, self._requestCollection.findOne(self._requestPath.collectionId))) {
        return self._rejectedResponse("Could not delete that object.");
      }
      self._requestCollection.remove(self._requestPath.collectionId);
    } catch (e) {
      return self._internalServerErrorResponse(e);
    }
    return self._okResponse('');
  }).run();
};

CollectionAPI._requestListener.prototype._postRequest = function() {
  var self = this;
  var requestData = '';

  self._request.on('data', function(chunk) {
    requestData += chunk.toString();
  });

  self._request.on('end', function() {
    self._server._fiber(function() {
      try {
        var obj = JSON.parse(requestData);

        if(!self._beforeHandling('POST', obj)) {
          return self._rejectedResponse("Could not post that object.");
        }
        self._requestPath.collectionId = self._requestCollection.insert(obj);
      } catch (e) {
        return self._internalServerErrorResponse(e);
      }
      return self._getRequest('POST');
    }).run();
  });
};

CollectionAPI._requestListener.prototype._okResponse = function(body) {
  var self = this;
  self._sendResponse(200, body);
};

CollectionAPI._requestListener.prototype._createdResponse = function(body) {
  var self = this;
  self._sendResponse(201, body);
};

CollectionAPI._requestListener.prototype._noContentResponse = function() {
  var self = this;
  self._sendResponse(204, '');
};

CollectionAPI._requestListener.prototype._notSupportedResponse = function() {
  var self = this;
  self._sendResponse(501, '');
};

CollectionAPI._requestListener.prototype._unauthorizedResponse = function(body) {
  var self = this;
  self._sendResponse(401, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._notFoundResponse = function(body) {
  var self = this;
  self._sendResponse(404, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._rejectedResponse= function(body) {
  var self = this;
  self._sendResponse(409, JSON.stringify({message: body.toString()}));
};

CollectionAPI._requestListener.prototype._internalServerErrorResponse = function(body) {
  var self = this;
  self._sendResponse(500, JSON.stringify({error: body.toString()}));
};

CollectionAPI._requestListener.prototype._sendResponse = function(statusCode, body) {
  var self = this;
  self._response.statusCode = statusCode;
  self._response.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'));
  self._response.setHeader('Content-Type', 'application/json');
  self._response.write(body);
  self._response.end();
};
<|endoftext|>


<commit_before>/*
 * Artefact Web Extensions
 *
 * Copyright 2012, Artefact Group LLC
 * Licensed under MIT.
 */

(function(Awe, global, document, undefined) {

  // = Awe.StateMachine =
  //
  // A general state machine. More documentation to come...
  //
  // State contains any of:
  //  {{{allowOnly}}} - array with 0 or more states (or null)
  //  {{{doNotAllow}}} - array with 1 or more states (or null)
  //  {{{start}}} function (or null)
  //  {{{update}}} function (or null)
  //  {{{end}}} function (or null)

  var StateMachine = Awe.StateMachine = function(name, stateMap, initialStateId) {
    var _i = this;

    function _sm_trace(str) { console.log("[awe.sm] " + str); }
    function _sm_trace_na(from,to) { _sm_trace(from + " -> " + to + " not allowed"); }
    function _sm_trace_e(s) { _sm_trace("enter : " + s); }
    function _sm_trace_x(s) { _sm_trace("exit  : " + s); }

    StateMachine.stateMachines.push(_i);

    _i.name = name;
    _i.states = stateMap || [];
    _i.currentStateId = null;

    _i.tracing = false;

    _i.addState = function(id, state) {
      if (_i.states[id]) {
        throw "Duplicate state added!";
      }
      _i.states[id] = state;
    }

    _i.getCurrentStateId = function() {
      return _i.currentStateId;
    }

    // Request a state that is assumed to be a transitional state and call the supplied callback on completion.
    // The callback will not be called if the transition state is interrupted, but you can prevent this by making
    // the transition state uninterruptable: pass an empty array in the transition states allowOnly field, which will
    // disallow any transitions while the state is active. The transitionCompleteCallback should then change state
    // once the transition is complete.
    _i.requestTransitionState = function(id) {

      if (typeof(arguments[arguments.length - 1]) != "function") {
        throw "Last argument of StateMachine.requestTransitionState must be an on-complete callback"
      }

      if (_i.requestState.apply(_i, arguments)) {
        _i.transitionCompleteCallback = arguments[arguments.length - 1];

        if (!_i.states[_i.currentStateId].update) {
          throw "ERROR: Transition states must have an update method, otherwise they will never be able to complete.";
        }
        return true;
      }

      // Remove the transition complete callback if the switch to the transition state was refused.
      _i.transitionCompleteCallback = null;
      return false;
    }

    // Request a change to the supplied state. If a current state exists that will be checked for any conditions that
    // disallow transition to the new state.
    _i.requestState = function(id) {

      if(!id) throw 'Cannot request a null state';

      var nextState = _i.states[id];

      if (!nextState) throw 'Specified state does not exist';

      if (_i.currentStateId) {
        var currentState = _i.states[_i.currentStateId];

        if (!_i.currentStateDone && currentState.allowOnly && currentState.allowOnly.indexOf(id) < 0) {
          if(_i.tracing) _sm_trace_na(_i.currentStateId, id);
          return false;
        }
        if (!_i.currentStateDone && currentState.doNotAllow && currentState.doNotAllow.indexOf(id) >= 0) {
          if(_i.tracing) _sm_trace_na(_i.currentStateId, id)
          return false;
        }

        if(_i.tracing) _sm_trace_x(_i.currentStateId);
        if (currentState.end) {
          currentState.end.call(currentState, id);
        }
      }

      if (nextState) {
        nextState._startTime = Date.now();
        nextState.runTime = 0;
        if(_i.tracing) _sm_trace_e(id);
        if (nextState.start) {
          nextState.start.apply(nextState, [_i.currentState && _i.currentState.id].concat(Array.prototype.slice.call(arguments,1)));
        }
      }

      _i.currentStateId = id;

      return true;
    }

    _i.update = function() {
      var currentState = _i.states[_i.currentStateId];
      currentState.runTime = (Date.now() - currentState._startTime) * 0.001;
      if (currentState && currentState.update) {
        // Update the current state and if it's a transition state, call the transition state callback once the
        // transition state's update returns true to signify completion
        if (currentState.update.call(currentState) && _i.transitionCompleteCallback) {
          var callback = _i.transitionCompleteCallback;
          _i.transitionCompleteCallback = null;
          _i.currentStateDone = true;
          callback();
          _i.currentStateDone = false;
        }
      }
    }

    if (initialStateId) {
      _i.requestState(initialStateId);
    }

    return _i;
  }

  StateMachine.stateMachines = []
  StateMachine.update = function() {
    for (var i = 0; i < StateMachine.stateMachines.length; ++i) {
      StateMachine.stateMachines[i].update();
    }
  }
})(Awe, this, document)
<commit_msg>Fix another edge case in awe-sm<commit_after>/*
 * Artefact Web Extensions
 *
 * Copyright 2012, Artefact Group LLC
 * Licensed under MIT.
 */

(function(Awe, global, document, undefined) {

  // = Awe.StateMachine =
  //
  // A general state machine. More documentation to come...
  //
  // State contains any of:
  //  {{{allowOnly}}} - array with 0 or more states (or null)
  //  {{{doNotAllow}}} - array with 1 or more states (or null)
  //  {{{start}}} function (or null)
  //  {{{update}}} function (or null)
  //  {{{end}}} function (or null)

  var StateMachine = Awe.StateMachine = function(name, stateMap, initialStateId) {
    var _i = this;

    function _sm_trace(str) { console.log("[awe.sm] " + str); }
    function _sm_trace_na(from,to) { _sm_trace(from + " -> " + to + " not allowed"); }
    function _sm_trace_e(s) { _sm_trace("enter : " + s); }
    function _sm_trace_x(s) { _sm_trace("exit  : " + s); }

    StateMachine.stateMachines.push(_i);

    _i.name = name;
    _i.states = stateMap || [];
    _i.currentStateId = null;

    _i.tracing = false;

    _i.addState = function(id, state) {
      if (_i.states[id]) {
        throw "Duplicate state added!";
      }
      _i.states[id] = state;
    }

    _i.getCurrentStateId = function() {
      return _i.currentStateId;
    }

    // Request a state that is assumed to be a transitional state and call the supplied callback on completion.
    // The callback will not be called if the transition state is interrupted, but you can prevent this by making
    // the transition state uninterruptable: pass an empty array in the transition states allowOnly field, which will
    // disallow any transitions while the state is active. The transitionCompleteCallback should then change state
    // once the transition is complete.
    _i.requestTransitionState = function(id) {

      if (typeof(arguments[arguments.length - 1]) != "function") {
        throw "Last argument of StateMachine.requestTransitionState must be an on-complete callback"
      }

      if (_i.requestState.apply(_i, arguments)) {
        _i.transitionCompleteCallback = arguments[arguments.length - 1];

        if (!_i.states[_i.currentStateId].update) {
          throw "ERROR: Transition states must have an update method, otherwise they will never be able to complete.";
        }
        return true;
      }

      // Remove the transition complete callback if the switch to the transition state was refused.
      _i.transitionCompleteCallback = null;
      return false;
    }

    // Request a change to the supplied state. If a current state exists that will be checked for any conditions that
    // disallow transition to the new state.
    _i.requestState = function(id) {

      if(!id) throw 'Cannot request a null state';

      var nextState = _i.states[id];

      if (!nextState) throw 'Specified state does not exist';

      if (_i.currentStateId) {

        if(_i.currentStateId == id) {
          if(_i.tracing) _sm_trace_e(id + " (no change)");
          return true;
        }

        var currentState = _i.states[_i.currentStateId];

        if (!_i.currentStateDone && currentState.allowOnly && currentState.allowOnly.indexOf(id) < 0) {
          if(_i.tracing) _sm_trace_na(_i.currentStateId, id);
          return false;
        }
        if (!_i.currentStateDone && currentState.doNotAllow && currentState.doNotAllow.indexOf(id) >= 0) {
          if(_i.tracing) _sm_trace_na(_i.currentStateId, id)
          return false;
        }

        if(_i.tracing) _sm_trace_x(_i.currentStateId);
        if (currentState.end) {
          currentState.end.call(currentState, id);
        }
      }

      if (nextState) {
        nextState._startTime = Date.now();
        nextState.runTime = 0;
        if(_i.tracing) _sm_trace_e(id);
        if (nextState.start) {
          nextState.start.apply(nextState, [_i.currentState && _i.currentState.id].concat(Array.prototype.slice.call(arguments,1)));
        }
      }

      _i.currentStateId = id;

      return true;
    }

    _i.update = function() {
      var currentState = _i.states[_i.currentStateId];
      currentState.runTime = (Date.now() - currentState._startTime) * 0.001;
      if (currentState && currentState.update) {
        // Update the current state and if it's a transition state, call the transition state callback once the
        // transition state's update returns true to signify completion
        if (currentState.update.call(currentState) && _i.transitionCompleteCallback) {
          var callback = _i.transitionCompleteCallback;
          _i.transitionCompleteCallback = null;
          _i.currentStateDone = true;
          callback();
          _i.currentStateDone = false;
        }
      }
    }

    if (initialStateId) {
      _i.requestState(initialStateId);
    }

    return _i;
  }

  StateMachine.stateMachines = []
  StateMachine.update = function() {
    for (var i = 0; i < StateMachine.stateMachines.length; ++i) {
      StateMachine.stateMachines[i].update();
    }
  }
})(Awe, this, document)
<|endoftext|>


<commit_before>'use strict'

const {TEXT, FLOAT} = require('sequelize')

module.exports = db => db.define('reviews', {
  rating: {
    type: FLOAT,
    allowNull: false,
    validate: {
      isNumeric: true
    }
  },
  comment: {
    type: TEXT,
    allowNull: true
  }
})

module.exports.associations = (Review, {Magnet, User}) => {
  Review.hasOne(User)
  Review.hasOne(Magnet)
}
<commit_msg>fix: review associations<commit_after>'use strict'

const {TEXT, FLOAT} = require('sequelize')

module.exports = db => db.define('reviews', {
  rating: {
    type: FLOAT,
    allowNull: false,
    validate: {
      isNumeric: true
    }
  },
  comment: {
    type: TEXT,
    allowNull: true
  }
})

module.exports.associations = (Review, {Magnet, User}) => {
  Review.belongsTo(User)
  Review.belongsTo(Magnet)
}
<|endoftext|>


<commit_before><commit_msg>Update fill.js<commit_after><|endoftext|>