<commit_before>/**
 * API for Windows
 *
 * @requires [ CommandCam ]
 *
 * @param Object options
 *
 */
"use strict";

var CHILD_PROCESS = require('child_process');

var EXEC = CHILD_PROCESS.exec;

var Webcam = require( "./../Webcam.js" );

var Utils = require( "./../utils/Utils.js" );

var Path = require( "path" );

//Main class

function WindowsWebcam( options ) {

    var scope = this;

    scope.opts = Utils.setDefaults( options, WindowsWebcam.Defaults );

    //Construct

    Webcam.call( scope, scope.opts );

}

WindowsWebcam.prototype = Object.create( Webcam.prototype );

WindowsWebcam.prototype.constructor = WindowsWebcam;

WindowsWebcam.prototype.bin = Path.resolve(
    __dirname, "..", "bindings",
    "CommandCam", "Commandcam.exe"
);

/**
 * @override
 *
 * Generate shell statement
 *
 * @param String location
 *
 */
WindowsWebcam.prototype.generateSh = function( location ) {

    var scope = this;

    var device = scope.opts.device
        ? "/devnum " + scope.opts.device
        : "";

    var delay = scope.opts.delay
        ? "/delay " + scope.opts.delay
        : "";

    var sh = scope.bin + " "
        + delay + " "
        + device + " "
        + "/filename " + location;

    return sh;

};

/**
 * List webcam devices using bin
 *
 * @param Function callback
 *
 */

WindowsWebcam.prototype.list = function( callback ) {

    var scope = this;

    var sh = scope.bin + " /devlist";

    var cams = [];

    EXEC( sh, function( err, data, out ) {

        if( err ) { throw err; }

        var lines = out.split( "\n" );

        var ll = lines.length;

        var camNum = 1;

        for( var i = 0; i < ll; i ++ ) {

            var line = lines[ i ];
            line = line.replace( "\r", "" );

            if(
                !! line
                && line !== "Available capture devices:"
                && "No video devices found"
            ) {

                cams.push( camNum.toString() );
                camNum++;

            }

        }

        callback && callback( cams );

    });

};

//Defaults

WindowsWebcam.Defaults = {

    output: "bmp"

};

//Export

module.exports = WindowsWebcam;
<commit_msg>Update WindowsWebcam.js<commit_after>/**
 * API for Windows
 *
 * @requires [ CommandCam ]
 *
 * @param Object options
 *
 */
"use strict";

var CHILD_PROCESS = require('child_process');

var EXEC = CHILD_PROCESS.exec;

var Webcam = require( "./../Webcam.js" );

var Utils = require( "./../utils/Utils.js" );

var Path = require( "path" );

//Main class

function WindowsWebcam( options ) {

    var scope = this;

    scope.opts = Utils.setDefaults( options, WindowsWebcam.Defaults );

    //Construct

    Webcam.call( scope, scope.opts );

}

WindowsWebcam.prototype = Object.create( Webcam.prototype );

WindowsWebcam.prototype.constructor = WindowsWebcam;

WindowsWebcam.prototype.bin = "\"" + Path.resolve(
    __dirname, "..", "bindings",
    "CommandCam", "Commandcam.exe"
) + "\"";

/**
 * @override
 *
 * Generate shell statement
 *
 * @param String location
 *
 */
WindowsWebcam.prototype.generateSh = function( location ) {

    var scope = this;

    var device = scope.opts.device
        ? "/devnum " + scope.opts.device
        : "";

    var delay = scope.opts.delay
        ? "/delay " + scope.opts.delay
        : "";

    var sh = scope.bin + " "
        + delay + " "
        + device + " "
        + "/filename " + location;

    return sh;

};

/**
 * List webcam devices using bin
 *
 * @param Function callback
 *
 */

WindowsWebcam.prototype.list = function( callback ) {

    var scope = this;

    var sh = scope.bin + " /devlist";

    var cams = [];

    EXEC( sh, function( err, data, out ) {

        if( err ) { throw err; }

        var lines = out.split( "\n" );

        var ll = lines.length;

        var camNum = 1;

        for( var i = 0; i < ll; i ++ ) {

            var line = lines[ i ];
            line = line.replace( "\r", "" );

            if(
                !! line
                && line !== "Available capture devices:"
                && "No video devices found"
            ) {

                cams.push( camNum.toString() );
                camNum++;

            }

        }

        callback && callback( cams );

    });

};

//Defaults

WindowsWebcam.Defaults = {

    output: "bmp"

};

//Export

module.exports = WindowsWebcam;
<|endoftext|>


<commit_before>
/* =========================================================================
 * Svelto - Widgets - Ripple
 * =========================================================================
 * Copyright (c) 2015-2016 Fabio Spampinato
 * Licensed under MIT (https://github.com/svelto/svelto/blob/master/LICENSE)
 * =========================================================================
 * @require core/animations/animations.js
 * @require core/browser/browser.js
 * @require core/mouse/mouse.js
 * @require core/widget/widget.js
 * ========================================================================= */

(function ( $, _, Svelto, Widgets, Factory, Browser, Pointer, Mouse, Animations ) {

  'use strict';

  /* CONFIG */

  let config = {
    name: 'ripple',
    plugin: true,
    selector: '.ripple',
    templates: {
      circle: '

'
    },
    options: {
      classes: {
        circle: {
          show: 'ripple-circle-show',
          hide: 'ripple-circle-hide'
        },
        centered: 'ripple-centered'
      },
      animations: {
        show: Animations.xslow,
        hide: Animations.xslow,
        overlap: Animations.xslow / 100 * 58 // Used for triggering the hide animation while still opening, for a better visual effect
      },
      callbacks: {
        show: _.noop,
        hide: _.noop
      }
    }
  };

  /* RIPPLE */

  class Ripple extends Widgets.Widget {

    /* SPECIAL */

    _variables () {

      this.$ripple = this.$element;

      this.circles = [];

    }

    _events () {

      this.___downTap ();

    }

    /* DOWN / TAP */

    ___downTap () {

      // Touch devices triggers a `Pointer.down` event, but maybe they will just scroll the page, more appropriate to bind on `Pointer.tap`

      this._on ( Browser.is.touchDevice ? Pointer.tap : Pointer.down, this.__downTap );

    }

    __downTap ( event ) {

      if ( !_.isUndefined ( event.button ) && event.button !== Mouse.buttons.LEFT ) return;

      if ( this.$ripple.hasClass ( this.options.classes.centered ) ) {

        let offset = this.$ripple.offset ();

        this._show ({
          X: offset.left + ( this.$ripple.outerWidth () / 2 ),
          Y: offset.top + ( this.$ripple.outerHeight () / 2 )
        });

      } else {

        this._show ( $.eventXY ( event ) );

      }

      this._one ( true, this.$document, Pointer.up, this.__up );

    }

    /* UP */

    __up () {

      for ( let [$circle, timestamp] of this.circles ) {

        this._hide ( $circle, timestamp );

      }

      this.circles = [];

    }

    /* SHOW */

    _show ( point ) {

      let $circle = $(this._tmpl ( 'circle' ));

      /* SIZE */

      let offset = this.$ripple.offset (),
          insetX = point.X - offset.left,
          insetY = point.Y - offset.top,
          sideX = Math.max ( insetX, this.$ripple.outerWidth () - insetX ),
          sideY = Math.max ( insetY, this.$ripple.outerHeight () - insetY ),
          radius = Math.sqrt ( Math.pow ( sideX, 2 ) + Math.pow ( sideY, 2 ) ), // Basically the max the distances from the point to the corners
          diameter = radius * 2;

      /* ADDING */

      this.circles.push ( [$circle, _.now ()] );

      /* SHOW */

      this._frame ( function () {

        /* PREPEND */

        $circle.css ({
          width: diameter,
          height: diameter,
          top: insetY,
          left: insetX,
        }).prependTo ( this.$ripple );

        /* SHOW */

        this._frame ( function () {

          $circle.addClass ( this.options.classes.circle.show );

          this._trigger ( 'show' );

        });

      });

    }

    /* HIDE */

    _hide ( $circle, timestamp ) {

      let remaining = Math.max ( 0, this.options.animations.show - this.options.animations.overlap + timestamp - _.now () );

      this._delay ( function () {

        $circle.addClass ( this.options.classes.circle.hide );

        this._delay ( function () {

          $circle.remove ();

          this._trigger ( 'hide' );

        }, this.options.animations.hide );

      }, remaining );

    }

  }

  /* FACTORY */

  Factory.init ( Ripple, config, Widgets );

}( Svelto.$, Svelto._, Svelto, Svelto.Widgets, Svelto.Factory, Svelto.Browser, Svelto.Pointer, Svelto.Mouse, Svelto.Animations ));
<commit_msg>Using `.ripple-center` instead of `.ripple-centered`<commit_after>
/* =========================================================================
 * Svelto - Widgets - Ripple
 * =========================================================================
 * Copyright (c) 2015-2016 Fabio Spampinato
 * Licensed under MIT (https://github.com/svelto/svelto/blob/master/LICENSE)
 * =========================================================================
 * @require core/animations/animations.js
 * @require core/browser/browser.js
 * @require core/mouse/mouse.js
 * @require core/widget/widget.js
 * ========================================================================= */

(function ( $, _, Svelto, Widgets, Factory, Browser, Pointer, Mouse, Animations ) {

  'use strict';

  /* CONFIG */

  let config = {
    name: 'ripple',
    plugin: true,
    selector: '.ripple',
    templates: {
      circle: '

'
    },
    options: {
      classes: {
        circle: {
          show: 'ripple-circle-show',
          hide: 'ripple-circle-hide'
        },
        center: 'ripple-center'
      },
      animations: {
        show: Animations.xslow,
        hide: Animations.xslow,
        overlap: Animations.xslow / 100 * 58 // Used for triggering the hide animation while still opening, for a better visual effect
      },
      callbacks: {
        show: _.noop,
        hide: _.noop
      }
    }
  };

  /* RIPPLE */

  class Ripple extends Widgets.Widget {

    /* SPECIAL */

    _variables () {

      this.$ripple = this.$element;

      this.circles = [];

    }

    _events () {

      this.___downTap ();

    }

    /* DOWN / TAP */

    ___downTap () {

      // Touch devices triggers a `Pointer.down` event, but maybe they will just scroll the page, more appropriate to bind on `Pointer.tap`

      this._on ( Browser.is.touchDevice ? Pointer.tap : Pointer.down, this.__downTap );

    }

    __downTap ( event ) {

      if ( !_.isUndefined ( event.button ) && event.button !== Mouse.buttons.LEFT ) return;

      if ( this.$ripple.hasClass ( this.options.classes.center ) ) {

        let offset = this.$ripple.offset ();

        this._show ({
          X: offset.left + ( this.$ripple.outerWidth () / 2 ),
          Y: offset.top + ( this.$ripple.outerHeight () / 2 )
        });

      } else {

        this._show ( $.eventXY ( event ) );

      }

      this._one ( true, this.$document, Pointer.up, this.__up );

    }

    /* UP */

    __up () {

      for ( let [$circle, timestamp] of this.circles ) {

        this._hide ( $circle, timestamp );

      }

      this.circles = [];

    }

    /* SHOW */

    _show ( point ) {

      let $circle = $(this._tmpl ( 'circle' ));

      /* SIZE */

      let offset = this.$ripple.offset (),
          insetX = point.X - offset.left,
          insetY = point.Y - offset.top,
          sideX = Math.max ( insetX, this.$ripple.outerWidth () - insetX ),
          sideY = Math.max ( insetY, this.$ripple.outerHeight () - insetY ),
          radius = Math.sqrt ( Math.pow ( sideX, 2 ) + Math.pow ( sideY, 2 ) ), // Basically the max the distances from the point to the corners
          diameter = radius * 2;

      /* ADDING */

      this.circles.push ( [$circle, _.now ()] );

      /* SHOW */

      this._frame ( function () {

        /* PREPEND */

        $circle.css ({
          width: diameter,
          height: diameter,
          top: insetY,
          left: insetX,
        }).prependTo ( this.$ripple );

        /* SHOW */

        this._frame ( function () {

          $circle.addClass ( this.options.classes.circle.show );

          this._trigger ( 'show' );

        });

      });

    }

    /* HIDE */

    _hide ( $circle, timestamp ) {

      let remaining = Math.max ( 0, this.options.animations.show - this.options.animations.overlap + timestamp - _.now () );

      this._delay ( function () {

        $circle.addClass ( this.options.classes.circle.hide );

        this._delay ( function () {

          $circle.remove ();

          this._trigger ( 'hide' );

        }, this.options.animations.hide );

      }, remaining );

    }

  }

  /* FACTORY */

  Factory.init ( Ripple, config, Widgets );

}( Svelto.$, Svelto._, Svelto, Svelto.Widgets, Svelto.Factory, Svelto.Browser, Svelto.Pointer, Svelto.Mouse, Svelto.Animations ));
<|endoftext|>


<commit_before>vg.parse.scales = (function() {
  var LINEAR = "linear",
      ORDINAL = "ordinal",
      LOG = "log",
      POWER = "pow",
      TIME = "time",
      QUANTILE = "quantile",
      GROUP_PROPERTY = {width: 1, height: 1};

  function scales(spec, scales, db, group) {
    return (spec || []).reduce(function(o, def) {
      var name = def.name, prev = name + ":prev";
      o[name] = scale(def, o[name], db, group);
      o[prev] = o[prev] || o[name];
      return o;
    }, scales || {});
  }

  function scale(def, scale, db, group) {
    var s = instance(def, scale),
        m = s.type===ORDINAL ? ordinal : quantitative,
        rng = range(def, group),
        data = vg.values(group.datum);

    m(def, s, rng, db, data);
    return s;
  }

  function instance(def, scale) {
    var type = def.type || LINEAR;
    if (!scale || type !== scale.type) {
      var ctor = vg.config.scale[type] || d3.scale[type];
      if (!ctor) vg.error("Unrecognized scale type: " + type);
      (scale = ctor()).type = scale.type || type;
      scale.scaleName = def.name;
    }
    return scale;
  }

  function ordinal(def, scale, rng, db, data) {
    var dataDrivenRange = false,
        pad = def.padding || 0,
        outer = def.outerPadding || 0,
        domain, sort, str, refs;

    // range pre-processing for data-driven ranges
    if (vg.isObject(def.range) && !vg.isArray(def.range)) {
      dataDrivenRange = true;
      refs = def.range.fields || vg.array(def.range);
      rng = extract(refs, db, data);
    }

    // domain
    sort = def.sort && !dataDrivenRange;
    domain = domainValues(def, db, data, sort);
    if (domain) scale.domain(domain);

    // width-defined range
    if (def.bandWidth) {
      var bw = def.bandWidth,
          len = domain.length,
          start = rng[0] || 0,
          space = def.points ? (pad*bw) : (pad*bw*(len-1) + 2*outer);
      rng = [start, start + (bw * len + space)];
    }

    // range
    str = typeof rng[0] === 'string';
    if (str || rng.length > 2 || rng.length===1 || dataDrivenRange) {
      scale.range(rng); // color or shape values
    } else if (def.points) {
      scale.rangePoints(rng, pad);
    } else if (def.round || def.round===undefined) {
      scale.rangeRoundBands(rng, pad, outer);
    } else {
      scale.rangeBands(rng, pad, outer);
    }
  }

  function quantitative(def, scale, rng, db, data) {
    var domain, interval;

    // domain
    domain = (def.type === QUANTILE)
      ? domainValues(def, db, data, false)
      : domainMinMax(def, db, data);
    scale.domain(domain);

    // range
    // vertical scales should flip by default, so use XOR here
    if (def.range === "height") rng = rng.reverse();
    scale[def.round && scale.rangeRound ? "rangeRound" : "range"](rng);

    if (def.exponent && def.type===POWER) scale.exponent(def.exponent);
    if (def.clamp) scale.clamp(true);
    if (def.nice) {
      if (def.type === TIME) {
        interval = d3.time[def.nice];
        if (!interval) vg.error("Unrecognized interval: " + interval);
        scale.nice(interval);
      } else {
        scale.nice();
      }
    }
  }

  function extract(refs, db, data) {
    return refs.reduce(function(values, r) {
      var dat = vg.values(db[r.data] || data),
          get = vg.accessor(vg.isString(r.field)
              ? r.field : "data." + vg.accessor(r.field.group)(data));
      return vg.unique(dat, get, values);
    }, []);
  }

  function domainValues(def, db, data, sort) {
    var domain = def.domain, values, refs;
    if (vg.isArray(domain)) {
      values = sort ? domain.slice() : domain;
    } else if (vg.isObject(domain)) {
      refs = domain.fields || vg.array(domain);
      values = extract(refs, db, data);
    }
    if (values && sort) values.sort(vg.cmp);
    return values;
  }

  function domainMinMax(def, db, data) {
    var domain = [null, null], refs, z;

    function extract(ref, min, max, z) {
      var dat = vg.values(db[ref.data] || data);
      var fields = vg.array(ref.field).map(function(f) {
        return vg.isString(f) ? f
          : "data." + vg.accessor(f.group)(data);
      });

      fields.forEach(function(f,i) {
        f = vg.accessor(f);
        if (min) domain[0] = d3.min([domain[0], d3.min(dat, f)]);
        if (max) domain[z] = d3.max([domain[z], d3.max(dat, f)]);
      });
    }

    if (def.domain !== undefined) {
      if (vg.isArray(def.domain)) {
        domain = def.domain.slice();
      } else if (vg.isObject(def.domain)) {
        refs = def.domain.fields || vg.array(def.domain);
        refs.forEach(function(r) { extract(r,1,1,1); });
      } else {
        domain = def.domain;
      }
    }
    z = domain.length - 1;
    if (def.domainMin !== undefined) {
      if (vg.isObject(def.domainMin)) {
        domain[0] = null;
        refs = def.domainMin.fields || vg.array(def.domainMin);
        refs.forEach(function(r) { extract(r,1,0,z); });
      } else {
        domain[0] = def.domainMin;
      }
    }
    if (def.domainMax !== undefined) {
      if (vg.isObject(def.domainMax)) {
        domain[z] = null;
        refs = def.domainMax.fields || vg.array(def.domainMax);
        refs.forEach(function(r) { extract(r,0,1,z); });
      } else {
        domain[z] = def.domainMax;
      }
    }
    if (def.type !== LOG && def.type !== TIME && (def.zero || def.zero===undefined)) {
      domain[0] = Math.min(0, domain[0]);
      domain[z] = Math.max(0, domain[z]);
    }
    return domain;
  }

  function range(def, group) {
    var rng = [null, null];

    if (def.range !== undefined) {
      if (typeof def.range === 'string') {
        if (GROUP_PROPERTY[def.range]) {
          rng = [0, group[def.range]];
        } else if (vg.config.range[def.range]) {
          rng = vg.config.range[def.range];
        } else {
          vg.error("Unrecogized range: "+def.range);
          return rng;
        }
      } else if (vg.isArray(def.range)) {
        rng = def.range;
      } else if (vg.isObject(def.range)) {
        return null; // early exit
      } else {
        rng = [0, def.range];
      }
    }
    if (def.rangeMin !== undefined) {
      rng[0] = def.rangeMin;
    }
    if (def.rangeMax !== undefined) {
      rng[rng.length-1] = def.rangeMax;
    }

    if (def.reverse !== undefined) {
      var rev = def.reverse;
      if (vg.isObject(rev)) {
        rev = vg.accessor(rev.field)(group.datum);
      }
      if (rev) rng = rng.reverse();
    }

    return rng;
  }

  return scales;
})();
<commit_msg>Copy the range because reverse is in place and calling domainMinMax multiple times undoes reverse. <commit_after>vg.parse.scales = (function() {
  var LINEAR = "linear",
      ORDINAL = "ordinal",
      LOG = "log",
      POWER = "pow",
      TIME = "time",
      QUANTILE = "quantile",
      GROUP_PROPERTY = {width: 1, height: 1};

  function scales(spec, scales, db, group) {
    return (spec || []).reduce(function(o, def) {
      var name = def.name, prev = name + ":prev";
      o[name] = scale(def, o[name], db, group);
      o[prev] = o[prev] || o[name];
      return o;
    }, scales || {});
  }

  function scale(def, scale, db, group) {
    var s = instance(def, scale),
        m = s.type===ORDINAL ? ordinal : quantitative,
        rng = range(def, group),
        data = vg.values(group.datum);

    m(def, s, rng, db, data);
    return s;
  }

  function instance(def, scale) {
    var type = def.type || LINEAR;
    if (!scale || type !== scale.type) {
      var ctor = vg.config.scale[type] || d3.scale[type];
      if (!ctor) vg.error("Unrecognized scale type: " + type);
      (scale = ctor()).type = scale.type || type;
      scale.scaleName = def.name;
    }
    return scale;
  }

  function ordinal(def, scale, rng, db, data) {
    var dataDrivenRange = false,
        pad = def.padding || 0,
        outer = def.outerPadding || 0,
        domain, sort, str, refs;

    // range pre-processing for data-driven ranges
    if (vg.isObject(def.range) && !vg.isArray(def.range)) {
      dataDrivenRange = true;
      refs = def.range.fields || vg.array(def.range);
      rng = extract(refs, db, data);
    }

    // domain
    sort = def.sort && !dataDrivenRange;
    domain = domainValues(def, db, data, sort);
    if (domain) scale.domain(domain);

    // width-defined range
    if (def.bandWidth) {
      var bw = def.bandWidth,
          len = domain.length,
          start = rng[0] || 0,
          space = def.points ? (pad*bw) : (pad*bw*(len-1) + 2*outer);
      rng = [start, start + (bw * len + space)];
    }

    // range
    str = typeof rng[0] === 'string';
    if (str || rng.length > 2 || rng.length===1 || dataDrivenRange) {
      scale.range(rng); // color or shape values
    } else if (def.points) {
      scale.rangePoints(rng, pad);
    } else if (def.round || def.round===undefined) {
      scale.rangeRoundBands(rng, pad, outer);
    } else {
      scale.rangeBands(rng, pad, outer);
    }
  }

  function quantitative(def, scale, rng, db, data) {
    var domain, interval;

    // domain
    domain = (def.type === QUANTILE)
      ? domainValues(def, db, data, false)
      : domainMinMax(def, db, data);
    scale.domain(domain);

    // range
    // vertical scales should flip by default, so use XOR here
    if (def.range === "height") rng = rng.reverse();
    scale[def.round && scale.rangeRound ? "rangeRound" : "range"](rng);

    if (def.exponent && def.type===POWER) scale.exponent(def.exponent);
    if (def.clamp) scale.clamp(true);
    if (def.nice) {
      if (def.type === TIME) {
        interval = d3.time[def.nice];
        if (!interval) vg.error("Unrecognized interval: " + interval);
        scale.nice(interval);
      } else {
        scale.nice();
      }
    }
  }

  function extract(refs, db, data) {
    return refs.reduce(function(values, r) {
      var dat = vg.values(db[r.data] || data),
          get = vg.accessor(vg.isString(r.field)
              ? r.field : "data." + vg.accessor(r.field.group)(data));
      return vg.unique(dat, get, values);
    }, []);
  }

  function domainValues(def, db, data, sort) {
    var domain = def.domain, values, refs;
    if (vg.isArray(domain)) {
      values = sort ? domain.slice() : domain;
    } else if (vg.isObject(domain)) {
      refs = domain.fields || vg.array(domain);
      values = extract(refs, db, data);
    }
    if (values && sort) values.sort(vg.cmp);
    return values;
  }

  function domainMinMax(def, db, data) {
    var domain = [null, null], refs, z;

    function extract(ref, min, max, z) {
      var dat = vg.values(db[ref.data] || data);
      var fields = vg.array(ref.field).map(function(f) {
        return vg.isString(f) ? f
          : "data." + vg.accessor(f.group)(data);
      });

      fields.forEach(function(f,i) {
        f = vg.accessor(f);
        if (min) domain[0] = d3.min([domain[0], d3.min(dat, f)]);
        if (max) domain[z] = d3.max([domain[z], d3.max(dat, f)]);
      });
    }

    if (def.domain !== undefined) {
      if (vg.isArray(def.domain)) {
        domain = def.domain.slice();
      } else if (vg.isObject(def.domain)) {
        refs = def.domain.fields || vg.array(def.domain);
        refs.forEach(function(r) { extract(r,1,1,1); });
      } else {
        domain = def.domain;
      }
    }
    z = domain.length - 1;
    if (def.domainMin !== undefined) {
      if (vg.isObject(def.domainMin)) {
        domain[0] = null;
        refs = def.domainMin.fields || vg.array(def.domainMin);
        refs.forEach(function(r) { extract(r,1,0,z); });
      } else {
        domain[0] = def.domainMin;
      }
    }
    if (def.domainMax !== undefined) {
      if (vg.isObject(def.domainMax)) {
        domain[z] = null;
        refs = def.domainMax.fields || vg.array(def.domainMax);
        refs.forEach(function(r) { extract(r,0,1,z); });
      } else {
        domain[z] = def.domainMax;
      }
    }
    if (def.type !== LOG && def.type !== TIME && (def.zero || def.zero===undefined)) {
      domain[0] = Math.min(0, domain[0]);
      domain[z] = Math.max(0, domain[z]);
    }
    return domain;
  }

  function range(def, group) {
    var rng = [null, null];

    if (def.range !== undefined) {
      if (typeof def.range === 'string') {
        if (GROUP_PROPERTY[def.range]) {
          rng = [0, group[def.range]];
        } else if (vg.config.range[def.range]) {
          rng = vg.config.range[def.range];
        } else {
          vg.error("Unrecogized range: "+def.range);
          return rng;
        }
      } else if (vg.isArray(def.range)) {
        rng = vg.duplicate(def.range);
      } else if (vg.isObject(def.range)) {
        return null; // early exit
      } else {
        rng = [0, def.range];
      }
    }
    if (def.rangeMin !== undefined) {
      rng[0] = def.rangeMin;
    }
    if (def.rangeMax !== undefined) {
      rng[rng.length-1] = def.rangeMax;
    }

    if (def.reverse !== undefined) {
      var rev = def.reverse;
      if (vg.isObject(rev)) {
        rev = vg.accessor(rev.field)(group.datum);
      }
      if (rev) rng = rng.reverse();
    }

    return rng;
  }

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


<commit_before>/**
 * Patterns validate - Form vlidation
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../registry",
    "../utils",
    "parsley"
], function($, patterns, utils) {
    var validate = {
        name: "validate",
        trigger: "form.pat-validate",

        init: function($el) {
            return $el.each(function() {
                this.noValidate=true;
                var parsley_form, field, i;

                parsley_form=$(this).parsley({
                    trigger: "change keyup",
                    successClass: "valid",
                    errorClass: "warning",
                    errors: {
                        classHandler: validate._classHandler,
                        container: validate._container
                    }
                });
                for (i=0; i<parsley_form.items.length; i++) {
                    field=parsley_form.items[i];
                    field.addError=validate._addFieldError;
                    field.removeError=validate._removeFieldError;
                }
            });
        },

        // Parsley error class handler, used to determine which element will
        // receive the status class.
        _classHandler: function(elem/*, isRadioOrCheckbox */) {
            var $result = elem;
            for (var i=0; i<elem.length; i++) {
                $result=$result.add(utils.findLabel(elem[i]));
                $result=$result.add(elem.eq(i).closest("fieldset"));
            }
            return $result;
        },

        // Parsley hook to determine where error messages are inserted.
        _container: function(/* element, isRadioOrCheckbox */) {
            return $();
        },

        _findErrorMessages: function($el, constraintName) {
            var selector = "em.validation.message[data-validate-constraint="+constraintName+"]",
                $messages = $el.siblings(selector);
            if ($el.is("[type=radio],[type=checkbox]")) {
                var $fieldset = $el.closest("fieldset.checklist");
                if ($fieldset.length)
                    $messages=$fieldset.find(selector);
            }
            return $messages;
        },

        // Parsley method to add an error to a field
        _addFieldError: function(error) {
            var $position = this.element,
                strategy="after";

            if (this.element.is("[type=radio],[type=checkbox]")) {
                var $fieldset = this.element.closest("fieldset.checklist");
                if ($fieldset.length) {
                    $position=$fieldset;
                    strategy="append";
                }
            }

            for (var constraintName in error) {
                if (validate._findErrorMessages(this.element, constraintName).length)
                    return;
                var $message = $("<em/>", {"class": "validation warning message"});
                $message.attr("data-validate-constraint", constraintName);
                $message.text(error[constraintName]);
                switch (strategy) {
                    case "append":
                        $message.appendTo($position);
                        break;
                    case "after":
                        $message.insertAfter($position);
                        break;
                }
            }
            $position.trigger("pat-update", {pattern: "validate"});
        },

        // Parsley method to remove all error messages for a field
        _removeFieldError: function(constraintName) {
            var $messages = validate._findErrorMessages(this.element, constraintName);
            $messages.parent().trigger("pat-update", {pattern: "validate"});
            $messages.remove();
        }
    };

    patterns.register(validate);
    return validate;
});
<commit_msg>Invoke validation on ajax/rest submit of forms.<commit_after>/**
 * Patterns validate - Form vlidation
 *
 * Copyright 2013 Simplon B.V. - Wichert Akkerman
 */
define([
    "jquery",
    "../registry",
    "../utils",
    "parsley"
], function($, patterns, utils) {
    var validate = {
        name: "validate",
        trigger: "form.pat-validate",

        init: function($el) {
            return $el.each(function() {
                this.noValidate=true;
                var parsley_form, field, i;

                parsley_form=$(this).parsley({
                    trigger: "change keyup",
                    successClass: "valid",
                    errorClass: "warning",
                    errors: {
                        classHandler: validate._classHandler,
                        container: validate._container
                    }
                });
                for (i=0; i<parsley_form.items.length; i++) {
                    field=parsley_form.items[i];
                    field.addError=validate._addFieldError;
                    field.removeError=validate._removeFieldError;
                }
                $(this).on('pat-ajax-before.pat-validate',
                           validate.onPreSubmit);
            });
        },

        // Parsley error class handler, used to determine which element will
        // receive the status class.
        _classHandler: function(elem/*, isRadioOrCheckbox */) {
            var $result = elem;
            for (var i=0; i<elem.length; i++) {
                $result=$result.add(utils.findLabel(elem[i]));
                $result=$result.add(elem.eq(i).closest("fieldset"));
            }
            return $result;
        },

        // Parsley hook to determine where error messages are inserted.
        _container: function(/* element, isRadioOrCheckbox */) {
            return $();
        },

        _findErrorMessages: function($el, constraintName) {
            var selector = "em.validation.message[data-validate-constraint="+constraintName+"]",
                $messages = $el.siblings(selector);
            if ($el.is("[type=radio],[type=checkbox]")) {
                var $fieldset = $el.closest("fieldset.checklist");
                if ($fieldset.length)
                    $messages=$fieldset.find(selector);
            }
            return $messages;
        },

        // Parsley method to add an error to a field
        _addFieldError: function(error) {
            var $position = this.element,
                strategy="after";

            if (this.element.is("[type=radio],[type=checkbox]")) {
                var $fieldset = this.element.closest("fieldset.checklist");
                if ($fieldset.length) {
                    $position=$fieldset;
                    strategy="append";
                }
            }

            for (var constraintName in error) {
                if (validate._findErrorMessages(this.element, constraintName).length)
                    return;
                var $message = $("<em/>", {"class": "validation warning message"});
                $message.attr("data-validate-constraint", constraintName);
                $message.text(error[constraintName]);
                switch (strategy) {
                    case "append":
                        $message.appendTo($position);
                        break;
                    case "after":
                        $message.insertAfter($position);
                        break;
                }
            }
            $position.trigger("pat-update", {pattern: "validate"});
        },

        // Parsley method to remove all error messages for a field
        _removeFieldError: function(constraintName) {
            var $messages = validate._findErrorMessages(this.element, constraintName);
            $messages.parent().trigger("pat-update", {pattern: "validate"});
            $messages.remove();
        },

        onPreSubmit: function(event, veto) {
            veto.veto |= !$(event.target).parsley('isValid');
            $(event.target).parsley('validate');
        }
    };

    patterns.register(validate);
    return validate;
});
<|endoftext|>


<commit_before>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

import * as utils from './utils.js';
import * as nativeMethods from './native-methods.js';

/*
Make this name unique so it is unlikely to conflict with properties on objects passed to `addEventListener`
https://github.com/webcomponents/shadydom/issues/173
*/
const /** string */ eventWrappersName = `__eventWrappers${Date.now()}`;

// https://github.com/w3c/webcomponents/issues/513#issuecomment-224183937
let alwaysComposed = {
  'blur': true,
  'focus': true,
  'focusin': true,
  'focusout': true,
  'click': true,
  'dblclick': true,
  'mousedown': true,
  'mouseenter': true,
  'mouseleave': true,
  'mousemove': true,
  'mouseout': true,
  'mouseover': true,
  'mouseup': true,
  'wheel': true,
  'beforeinput': true,
  'input': true,
  'keydown': true,
  'keyup': true,
  'compositionstart': true,
  'compositionupdate': true,
  'compositionend': true,
  'touchstart': true,
  'touchend': true,
  'touchmove': true,
  'touchcancel': true,
  'pointerover': true,
  'pointerenter': true,
  'pointerdown': true,
  'pointermove': true,
  'pointerup': true,
  'pointercancel': true,
  'pointerout': true,
  'pointerleave': true,
  'gotpointercapture': true,
  'lostpointercapture': true,
  'dragstart': true,
  'drag': true,
  'dragenter': true,
  'dragleave': true,
  'dragover': true,
  'drop': true,
  'dragend': true,
  'DOMActivate': true,
  'DOMFocusIn': true,
  'DOMFocusOut': true,
  'keypress': true
};

function pathComposer(startNode, composed) {
  let composedPath = [];
  let current = startNode;
  let startRoot = startNode === window ? window : startNode.getRootNode();
  while (current) {
    composedPath.push(current);
    if (current.assignedSlot) {
      current = current.assignedSlot;
    } else if (current.nodeType === Node.DOCUMENT_FRAGMENT_NODE && current.host && (composed || current !== startRoot)) {
      current = current.host;
    } else {
      current = current.parentNode;
    }
  }
  // event composedPath includes window when startNode's ownerRoot is document
  if (composedPath[composedPath.length - 1] === document) {
    composedPath.push(window);
  }
  return composedPath;
}

function retarget(refNode, path) {
  if (!utils.isShadyRoot) {
    return refNode;
  }
  // If ANCESTOR's root is not a shadow root or ANCESTOR's root is BASE's
  // shadow-including inclusive ancestor, return ANCESTOR.
  let refNodePath = pathComposer(refNode, true);
  let p$ = path;
  for (let i=0, ancestor, lastRoot, root, rootIdx; i < p$.length; i++) {
    ancestor = p$[i];
    root = ancestor === window ? window : ancestor.getRootNode();
    if (root !== lastRoot) {
      rootIdx = refNodePath.indexOf(root);
      lastRoot = root;
    }
    if (!utils.isShadyRoot(root) || rootIdx > -1) {
      return ancestor;
    }
  }
}

let eventMixin = {

  /**
   * @this {Event}
   */
  get composed() {
    // isTrusted may not exist in this browser, so just check if isTrusted is explicitly false
    if (this.isTrusted !== false && this.__composed === undefined) {
      this.__composed = alwaysComposed[this.type];
    }
    return this.__composed || false;
  },

  /**
   * @this {Event}
   */
  composedPath() {
    if (!this.__composedPath) {
      this.__composedPath = pathComposer(this['__target'], this.composed);
    }
    return this.__composedPath;
  },

  /**
   * @this {Event}
   */
  get target() {
    return retarget(this.currentTarget, this.composedPath());
  },

  // http://w3c.github.io/webcomponents/spec/shadow/#event-relatedtarget-retargeting
  /**
   * @this {Event}
   */
  get relatedTarget() {
    if (!this.__relatedTarget) {
      return null;
    }
    if (!this.__relatedTargetComposedPath) {
      this.__relatedTargetComposedPath = pathComposer(this.__relatedTarget, true);
    }
    // find the deepest node in relatedTarget composed path that is in the same root with the currentTarget
    return retarget(this.currentTarget, this.__relatedTargetComposedPath);
  },
  /**
   * @this {Event}
   */
  stopPropagation() {
    Event.prototype.stopPropagation.call(this);
    this.__propagationStopped = true;
  },
  /**
   * @this {Event}
   */
  stopImmediatePropagation() {
    Event.prototype.stopImmediatePropagation.call(this);
    this.__immediatePropagationStopped = true;
    this.__propagationStopped = true;
  }

};

function mixinComposedFlag(Base) {
  // NOTE: avoiding use of `class` here so that transpiled output does not
  // try to do `Base.call` with a dom construtor.
  let klazz = function(type, options) {
    let event = new Base(type, options);
    event.__composed = options && Boolean(options['composed']);
    return event;
  }
  // put constructor properties on subclass
  utils.mixin(klazz, Base);
  klazz.prototype = Base.prototype;
  return klazz;
}

let nonBubblingEventsToRetarget = {
  'focus': true,
  'blur': true
};

function fireHandlers(event, node, phase) {
  let hs = node.__handlers && node.__handlers[event.type] &&
    node.__handlers[event.type][phase];
  if (hs) {
    for (let i = 0, fn; (fn = hs[i]); i++) {
      if (event.target === event.relatedTarget) {
        return;
      }
      fn.call(node, event);
      if (event.__immediatePropagationStopped) {
        return;
      }
    }
  }
}

function retargetNonBubblingEvent(e) {
  let path = e.composedPath();
  let node;
  // override `currentTarget` to let patched `target` calculate correctly
  Object.defineProperty(e, 'currentTarget', {
    get: function() {
      return node;
    },
    configurable: true
  });
  for (let i = path.length - 1; i >= 0; i--) {
    node = path[i];
    // capture phase fires all capture handlers
    fireHandlers(e, node, 'capture');
    if (e.__propagationStopped) {
      return;
    }
  }

  // set the event phase to `AT_TARGET` as in spec
  Object.defineProperty(e, 'eventPhase', {get() { return Event.AT_TARGET }});

  // the event only needs to be fired when owner roots change when iterating the event path
  // keep track of the last seen owner root
  let lastFiredRoot;
  for (let i = 0; i < path.length; i++) {
    node = path[i];
    const root = node.__shady && node.__shady.root;
    if (i === 0 || (root && root === lastFiredRoot)) {
      fireHandlers(e, node, 'bubble');
      // don't bother with window, it doesn't have `getRootNode` and will be last in the path anyway
      if (node !== window) {
        lastFiredRoot = node.getRootNode();
      }
      if (e.__propagationStopped) {
        return;
      }
    }
  }
}

function listenerSettingsEqual(savedListener, node, type, capture, once, passive) {
  let {
    node: savedNode,
    type: savedType,
    capture: savedCapture,
    once: savedOnce,
    passive: savedPassive
  } = savedListener;
  return node === savedNode &&
    type === savedType &&
    capture === savedCapture &&
    once === savedOnce &&
    passive === savedPassive;
}

export function findListener(wrappers, node, type, capture, once, passive) {
  for (let i = 0; i < wrappers.length; i++) {
    if (listenerSettingsEqual(wrappers[i], node, type, capture, once, passive)) {
      return i;
    }
  }
  return -1;
}

/**
 * Firefox can throw on accessing eventWrappers inside of `removeEventListener` during a selenium run
 * Try/Catch accessing eventWrappers to work around
 * https://bugzilla.mozilla.org/show_bug.cgi?id=1353074
 */
function getEventWrappers(eventLike) {
  let wrappers = null;
  try {
    wrappers = eventLike[eventWrappersName];
  } catch (e) {} // eslint-disable-line no-empty
  return wrappers;
}

/**
 * @this {Event}
 */
export function addEventListener(type, fnOrObj, optionsOrCapture) {
  if (!fnOrObj) {
    return;
  }

  // The callback `fn` might be used for multiple nodes/events. Since we generate
  // a wrapper function, we need to keep track of it when we remove the listener.
  // It's more efficient to store the node/type/options information as Array in
  // `fn` itself rather than the node (we assume that the same callback is used
  // for few nodes at most, whereas a node will likely have many event listeners).
  // NOTE(valdrin) invoking external functions is costly, inline has better perf.
  let capture, once, passive;
  if (optionsOrCapture && typeof optionsOrCapture === 'object') {
    capture = Boolean(optionsOrCapture.capture);
    once = Boolean(optionsOrCapture.once);
    passive = Boolean(optionsOrCapture.passive);
  } else {
    capture = Boolean(optionsOrCapture);
    once = false;
    passive = false;
  }
  // hack to let ShadyRoots have event listeners
  // event listener will be on host, but `currentTarget`
  // will be set to shadyroot for event listener
  let target = (optionsOrCapture && optionsOrCapture.__shadyTarget) || this;

  let wrappers = fnOrObj[eventWrappersName];
  if (wrappers) {
    // Stop if the wrapper function has already been created.
    if (findListener(wrappers, target, type, capture, once, passive) > -1) {
      return;
    }
  } else {
    fnOrObj[eventWrappersName] = [];
  }

  /**
   * @this {HTMLElement}
   */
  const wrapperFn = function(e) {
    // Support `once` option.
    if (once) {
      this.removeEventListener(type, fnOrObj, optionsOrCapture);
    }
    if (!e['__target']) {
      patchEvent(e);
    }
    let lastCurrentTargetDesc;
    if (target !== this) {
      // replace `currentTarget` to make `target` and `relatedTarget` correct for inside the shadowroot
      lastCurrentTargetDesc = Object.getOwnPropertyDescriptor(e, 'currentTarget');
      Object.defineProperty(e, 'currentTarget', {get() { return target }, configurable: true});
    }
    // There are two critera that should stop events from firing on this node
    // 1. the event is not composed and the current node is not in the same root as the target
    // 2. when bubbling, if after retargeting, relatedTarget and target point to the same node
    if (e.composed || e.composedPath().indexOf(target) > -1) {
      if (e.target === e.relatedTarget) {
        if (e.eventPhase === Event.BUBBLING_PHASE) {
          e.stopImmediatePropagation();
        }
        return;
      }
      // prevent non-bubbling events from triggering bubbling handlers on shadowroot, but only if not in capture phase
      if (e.eventPhase !== Event.CAPTURING_PHASE && !e.bubbles && e.target !== target) {
        return;
      }
      let ret = (typeof fnOrObj === 'object' && fnOrObj.handleEvent) ?
        fnOrObj.handleEvent(e) :
        fnOrObj.call(target, e);
      if (target !== this) {
        // replace the "correct" `currentTarget`
        if (lastCurrentTargetDesc) {
          Object.defineProperty(e, 'currentTarget', lastCurrentTargetDesc);
          lastCurrentTargetDesc = null;
        } else {
          delete e['currentTarget'];
        }
      }
      return ret;
    }
  };
  // Store the wrapper information.
  fnOrObj[eventWrappersName].push({
    node: this,
    type: type,
    capture: capture,
    once: once,
    passive: passive,
    wrapperFn: wrapperFn
  });

  if (nonBubblingEventsToRetarget[type]) {
    this.__handlers = this.__handlers || {};
    this.__handlers[type] = this.__handlers[type] ||
      {'capture': [], 'bubble': []};
    this.__handlers[type][capture ? 'capture' : 'bubble'].push(wrapperFn);
  } else {
    let ael = this instanceof Window ? nativeMethods.windowAddEventListener :
      nativeMethods.addEventListener;
    ael.call(this, type, wrapperFn, optionsOrCapture);
  }
}

/**
 * @this {Event}
 */
export function removeEventListener(type, fnOrObj, optionsOrCapture) {
  if (!fnOrObj) {
    return;
  }

  // NOTE(valdrin) invoking external functions is costly, inline has better perf.
  let capture, once, passive;
  if (typeof optionsOrCapture === 'object') {
    capture = Boolean(optionsOrCapture.capture);
    once = Boolean(optionsOrCapture.once);
    passive = Boolean(optionsOrCapture.passive);
  } else {
    capture = Boolean(optionsOrCapture);
    once = false;
    passive = false;
  }
  let target = (optionsOrCapture && optionsOrCapture.__shadyTarget) || this;
  // Search the wrapped function.
  let wrapperFn = undefined;
  let wrappers = getEventWrappers(fnOrObj);
  if (wrappers) {
    let idx = findListener(wrappers, target, type, capture, once, passive);
    if (idx > -1) {
      wrapperFn = wrappers.splice(idx, 1)[0].wrapperFn;
      // Cleanup.
      if (!wrappers.length) {
        fnOrObj[eventWrappersName] = undefined;
      }
    }
  }
  let rel = this instanceof Window ? nativeMethods.windowRemoveEventListener :
    nativeMethods.removeEventListener;
  rel.call(this, type, wrapperFn || fnOrObj, optionsOrCapture);
  if (wrapperFn && nonBubblingEventsToRetarget[type] &&
      this.__handlers && this.__handlers[type]) {
    const arr = this.__handlers[type][capture ? 'capture' : 'bubble'];
    const idx = arr.indexOf(wrapperFn);
    if (idx > -1) {
      arr.splice(idx, 1);
    }
  }
}

function activateFocusEventOverrides() {
  for (let ev in nonBubblingEventsToRetarget) {
    window.addEventListener(ev, function(e) {
      if (!e['__target']) {
        patchEvent(e);
        retargetNonBubblingEvent(e);
      }
    }, true);
  }
}

function patchEvent(event) {
  event['__target'] = event.target;
  event.__relatedTarget = event.relatedTarget;
  // patch event prototype if we can
  if (utils.settings.hasDescriptors) {
    utils.patchPrototype(event, eventMixin);
  // and fallback to patching instance
  } else {
    utils.extend(event, eventMixin);
  }
}

let PatchedEvent = mixinComposedFlag(window.Event);
let PatchedCustomEvent = mixinComposedFlag(window.CustomEvent);
let PatchedMouseEvent = mixinComposedFlag(window.MouseEvent);

export function patchEvents() {
  window.Event = PatchedEvent;
  window.CustomEvent = PatchedCustomEvent;
  window.MouseEvent = PatchedMouseEvent;
  activateFocusEventOverrides();
}
<commit_msg>Update patch-events.js<commit_after>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

import * as utils from './utils.js';
import * as nativeMethods from './native-methods.js';

/*
Make this name unique so it is unlikely to conflict with properties on objects passed to `addEventListener`
https://github.com/webcomponents/shadydom/issues/173
*/
const /** string */ eventWrappersName = `__eventWrappers${Date.now()}`;

// https://github.com/w3c/webcomponents/issues/513#issuecomment-224183937
let alwaysComposed = {
  'blur': true,
  'focus': true,
  'focusin': true,
  'focusout': true,
  'click': true,
  'dblclick': true,
  'mousedown': true,
  'mouseenter': true,
  'mouseleave': true,
  'mousemove': true,
  'mouseout': true,
  'mouseover': true,
  'mouseup': true,
  'wheel': true,
  'beforeinput': true,
  'input': true,
  'keydown': true,
  'keyup': true,
  'compositionstart': true,
  'compositionupdate': true,
  'compositionend': true,
  'touchstart': true,
  'touchend': true,
  'touchmove': true,
  'touchcancel': true,
  'pointerover': true,
  'pointerenter': true,
  'pointerdown': true,
  'pointermove': true,
  'pointerup': true,
  'pointercancel': true,
  'pointerout': true,
  'pointerleave': true,
  'gotpointercapture': true,
  'lostpointercapture': true,
  'dragstart': true,
  'drag': true,
  'dragenter': true,
  'dragleave': true,
  'dragover': true,
  'drop': true,
  'dragend': true,
  'DOMActivate': true,
  'DOMFocusIn': true,
  'DOMFocusOut': true,
  'keypress': true
};

function pathComposer(startNode, composed) {
  let composedPath = [];
  let current = startNode;
  let startRoot = startNode === window ? window : startNode.getRootNode();
  while (current) {
    composedPath.push(current);
    if (current.assignedSlot) {
      current = current.assignedSlot;
    } else if (current.nodeType === Node.DOCUMENT_FRAGMENT_NODE && current.host && (composed || current !== startRoot)) {
      current = current.host;
    } else {
      current = current.parentNode;
    }
  }
  // event composedPath includes window when startNode's ownerRoot is document
  if (composedPath[composedPath.length - 1] === document) {
    composedPath.push(window);
  }
  return composedPath;
}

function retarget(refNode, path) {
  if (!utils.isShadyRoot) {
    return refNode;
  }
  // If ANCESTOR's root is not a shadow root or ANCESTOR's root is BASE's
  // shadow-including inclusive ancestor, return ANCESTOR.
  let refNodePath = pathComposer(refNode, true);
  let p$ = path;
  for (let i=0, ancestor, lastRoot, root, rootIdx; i < p$.length; i++) {
    ancestor = p$[i];
    root = ancestor === window ? window : ancestor.getRootNode();
    if (root !== lastRoot) {
      rootIdx = refNodePath.indexOf(root);
      lastRoot = root;
    }
    if (!utils.isShadyRoot(root) || rootIdx > -1) {
      return ancestor;
    }
  }
}

let eventMixin = {

  /**
   * @this {Event}
   */
  get composed() {
    // isTrusted may not exist in this browser, so just check if isTrusted is explicitly false
    if (this.isTrusted !== false && this.__composed === undefined) {
      this.__composed = alwaysComposed[this.type];
    }
    return this.__composed || false;
  },

  /**
   * @this {Event}
   */
  composedPath() {
    if (!this.__composedPath) {
      this.__composedPath = pathComposer(this['__target'], this.composed);
    }
    return this.__composedPath;
  },

  /**
   * @this {Event}
   */
  get target() {
    return retarget(this.currentTarget, this.composedPath());
  },

  // http://w3c.github.io/webcomponents/spec/shadow/#event-relatedtarget-retargeting
  /**
   * @this {Event}
   */
  get relatedTarget() {
    if (!this.__relatedTarget) {
      return null;
    }
    if (!this.__relatedTargetComposedPath) {
      this.__relatedTargetComposedPath = pathComposer(this.__relatedTarget, true);
    }
    // find the deepest node in relatedTarget composed path that is in the same root with the currentTarget
    return retarget(this.currentTarget, this.__relatedTargetComposedPath);
  },
  /**
   * @this {Event}
   */
  stopPropagation() {
    Event.prototype.stopPropagation.call(this);
    this.__propagationStopped = true;
  },
  /**
   * @this {Event}
   */
  stopImmediatePropagation() {
    Event.prototype.stopImmediatePropagation.call(this);
    this.__immediatePropagationStopped = true;
    this.__propagationStopped = true;
  }

};

function mixinComposedFlag(Base) {
  // NOTE: avoiding use of `class` here so that transpiled output does not
  // try to do `Base.call` with a dom construtor.
  let klazz = function(type, options) {
    let event = new Base(type, options);
    event.__composed = options && Boolean(options['composed']);
    return event;
  }
  // put constructor properties on subclass
  utils.mixin(klazz, Base);
  klazz.prototype = Base.prototype;
  return klazz;
}

let nonBubblingEventsToRetarget = {
  'focus': true,
  'blur': true
};

function fireHandlers(event, node, phase) {
  let hs = node.__handlers && node.__handlers[event.type] &&
    node.__handlers[event.type][phase];
  if (hs) {
    for (let i = 0, fn; (fn = hs[i]); i++) {
      if (event.target === event.relatedTarget) {
        return;
      }
      fn.call(node, event);
      if (event.__immediatePropagationStopped) {
        return;
      }
    }
  }
}

function retargetNonBubblingEvent(e) {
  let path = e.composedPath();
  let node;
  // override `currentTarget` to let patched `target` calculate correctly
  Object.defineProperty(e, 'currentTarget', {
    get: function() {
      return node;
    },
    configurable: true
  });
  for (let i = path.length - 1; i >= 0; i--) {
    node = path[i];
    // capture phase fires all capture handlers
    fireHandlers(e, node, 'capture');
    if (e.__propagationStopped) {
      return;
    }
  }

  // set the event phase to `AT_TARGET` as in spec
  Object.defineProperty(e, 'eventPhase', {get() { return Event.AT_TARGET }});

  // the event only needs to be fired when owner roots change when iterating the event path
  // keep track of the last seen owner root
  let lastFiredRoot;
  for (let i = 0; i < path.length; i++) {
    node = path[i];
    const root = node.__shady && node.__shady.root;
    if (i === 0 || (root && root === lastFiredRoot)) {
      fireHandlers(e, node, 'bubble');
      // don't bother with window, it doesn't have `getRootNode` and will be last in the path anyway
      if (node !== window) {
        lastFiredRoot = node.getRootNode();
      }
      if (e.__propagationStopped) {
        return;
      }
    }
  }
}

function listenerSettingsEqual(savedListener, node, type, capture, once, passive) {
  let {
    node: savedNode,
    type: savedType,
    capture: savedCapture,
    once: savedOnce,
    passive: savedPassive
  } = savedListener;
  return node === savedNode &&
    type === savedType &&
    capture === savedCapture &&
    once === savedOnce &&
    passive === savedPassive;
}

export function findListener(wrappers, node, type, capture, once, passive) {
  for (let i = 0; i < wrappers.length; i++) {
    if (listenerSettingsEqual(wrappers[i], node, type, capture, once, passive)) {
      return i;
    }
  }
  return -1;
}

/**
 * Firefox can throw on accessing eventWrappers inside of `removeEventListener` during a selenium run
 * Try/Catch accessing eventWrappers to work around
 * https://bugzilla.mozilla.org/show_bug.cgi?id=1353074
 */
function getEventWrappers(eventLike) {
  let wrappers = null;
  try {
    wrappers = eventLike[eventWrappersName];
  } catch (e) {} // eslint-disable-line no-empty
  return wrappers;
}

/**
 * @this {Event}
 */
export function addEventListener(type, fnOrObj, optionsOrCapture) {
  if (!fnOrObj) {
    return;
  }

  // The callback `fn` might be used for multiple nodes/events. Since we generate
  // a wrapper function, we need to keep track of it when we remove the listener.
  // It's more efficient to store the node/type/options information as Array in
  // `fn` itself rather than the node (we assume that the same callback is used
  // for few nodes at most, whereas a node will likely have many event listeners).
  // NOTE(valdrin) invoking external functions is costly, inline has better perf.
  let capture, once, passive;
  if (optionsOrCapture && typeof optionsOrCapture === 'object') {
    capture = Boolean(optionsOrCapture.capture);
    once = Boolean(optionsOrCapture.once);
    passive = Boolean(optionsOrCapture.passive);
  } else {
    capture = Boolean(optionsOrCapture);
    once = false;
    passive = false;
  }
  // hack to let ShadyRoots have event listeners
  // event listener will be on host, but `currentTarget`
  // will be set to shadyroot for event listener
  let target = (optionsOrCapture && optionsOrCapture.__shadyTarget) || this;

  let wrappers = fnOrObj[eventWrappersName];
  if (wrappers) {
    // Stop if the wrapper function has already been created.
    if (findListener(wrappers, target, type, capture, once, passive) > -1) {
      return;
    }
  } else {
    fnOrObj[eventWrappersName] = [];
  }

  /**
   * @this {HTMLElement}
   */
  const wrapperFn = function(e) {
    // Support `once` option.
    if (once) {
      this.removeEventListener(type, fnOrObj, optionsOrCapture);
    }
    if (!e['__target']) {
      patchEvent(e);
    }
    let lastCurrentTargetDesc;
    if (target !== this) {
      // replace `currentTarget` to make `target` and `relatedTarget` correct for inside the shadowroot
      lastCurrentTargetDesc = Object.getOwnPropertyDescriptor(e, 'currentTarget');
      Object.defineProperty(e, 'currentTarget', {get() { return target }, configurable: true});
    }
    // There are two critera that should stop events from firing on this node
    // 1. the event is not composed and the current node is not in the same root as the target
    // 2. when bubbling, if after retargeting, relatedTarget and target point to the same node
    if (e.composed || e.composedPath().indexOf(target) > -1) {
      if (e.target === e.relatedTarget) {
        if (e.eventPhase === Event.BUBBLING_PHASE) {
          e.stopImmediatePropagation();
        }
        return;
      }
      // prevent non-bubbling events from triggering bubbling handlers on shadowroot, but only if not in capture phase
      if (e.eventPhase !== Event.CAPTURING_PHASE && !e.bubbles && e.target !== target) {
        return;
      }
      let ret = (typeof fnOrObj === 'object' && fnOrObj.handleEvent) ?
        fnOrObj.handleEvent(e) :
        fnOrObj.call(target, e);
      if (target !== this) {
        // replace the "correct" `currentTarget`
        if (lastCurrentTargetDesc) {
          Object.defineProperty(e, 'currentTarget', lastCurrentTargetDesc);
          lastCurrentTargetDesc = null;
        } else {
          delete e['currentTarget'];
        }
      }
      return ret;
    }
  };
  // Store the wrapper information.
  fnOrObj[eventWrappersName].push({
    node: this,
    type: type,
    capture: capture,
    once: once,
    passive: passive,
    wrapperFn: wrapperFn
  });

  if (nonBubblingEventsToRetarget[type]) {
    this.__handlers = this.__handlers || {};
    this.__handlers[type] = this.__handlers[type] ||
      {'capture': [], 'bubble': []};
    this.__handlers[type][capture ? 'capture' : 'bubble'].push(wrapperFn);
  } else {
    let ael = this instanceof Window ? nativeMethods.windowAddEventListener :
      nativeMethods.addEventListener;
    ael.call(this, type, wrapperFn, optionsOrCapture);
  }
}

/**
 * @this {Event}
 */
export function removeEventListener(type, fnOrObj, optionsOrCapture) {
  if (!fnOrObj) {
    return;
  }

  // NOTE(valdrin) invoking external functions is costly, inline has better perf.
  let capture, once, passive;
  if (optionsOrCapture && typeof optionsOrCapture === 'object') {
    capture = Boolean(optionsOrCapture.capture);
    once = Boolean(optionsOrCapture.once);
    passive = Boolean(optionsOrCapture.passive);
  } else {
    capture = Boolean(optionsOrCapture);
    once = false;
    passive = false;
  }
  let target = (optionsOrCapture && optionsOrCapture.__shadyTarget) || this;
  // Search the wrapped function.
  let wrapperFn = undefined;
  let wrappers = getEventWrappers(fnOrObj);
  if (wrappers) {
    let idx = findListener(wrappers, target, type, capture, once, passive);
    if (idx > -1) {
      wrapperFn = wrappers.splice(idx, 1)[0].wrapperFn;
      // Cleanup.
      if (!wrappers.length) {
        fnOrObj[eventWrappersName] = undefined;
      }
    }
  }
  let rel = this instanceof Window ? nativeMethods.windowRemoveEventListener :
    nativeMethods.removeEventListener;
  rel.call(this, type, wrapperFn || fnOrObj, optionsOrCapture);
  if (wrapperFn && nonBubblingEventsToRetarget[type] &&
      this.__handlers && this.__handlers[type]) {
    const arr = this.__handlers[type][capture ? 'capture' : 'bubble'];
    const idx = arr.indexOf(wrapperFn);
    if (idx > -1) {
      arr.splice(idx, 1);
    }
  }
}

function activateFocusEventOverrides() {
  for (let ev in nonBubblingEventsToRetarget) {
    window.addEventListener(ev, function(e) {
      if (!e['__target']) {
        patchEvent(e);
        retargetNonBubblingEvent(e);
      }
    }, true);
  }
}

function patchEvent(event) {
  event['__target'] = event.target;
  event.__relatedTarget = event.relatedTarget;
  // patch event prototype if we can
  if (utils.settings.hasDescriptors) {
    utils.patchPrototype(event, eventMixin);
  // and fallback to patching instance
  } else {
    utils.extend(event, eventMixin);
  }
}

let PatchedEvent = mixinComposedFlag(window.Event);
let PatchedCustomEvent = mixinComposedFlag(window.CustomEvent);
let PatchedMouseEvent = mixinComposedFlag(window.MouseEvent);

export function patchEvents() {
  window.Event = PatchedEvent;
  window.CustomEvent = PatchedCustomEvent;
  window.MouseEvent = PatchedMouseEvent;
  activateFocusEventOverrides();
}
<|endoftext|>


<commit_before>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
import * as utils from './utils.js';
import {patchProperties} from './utils.js';
import {getInnerHTML} from './innerHTML.js';

const hasDescriptors = utils.settings.hasDescriptors;
export const NATIVE_PREFIX = utils.NATIVE_PREFIX;

// Object on which raw native methods are stored.
// e.g. `nativeMethods.querySelector.call(node, selector)`
// same as `node.querySelector(selector)`
export const nativeMethods = {
  /** @this {Element} */
  querySelector(selector) {
    return this[NATIVE_PREFIX + 'querySelector'](selector);
  },
  /** @this {Element} */
  querySelectorAll(selector) {
    return this[NATIVE_PREFIX + 'querySelectorAll'](selector);
  }
};
// Object on which raw native accessors are available via `accessorName(node)`.
// e.g. `nativeTree.firstChild(node)`
// same as `node.firstChild`
export const nativeTree = {};

const installNativeAccessor = (name) => {
  nativeTree[name] = (node) => node[NATIVE_PREFIX + name];
}

const installNativeMethod = (name, fn) => {
  if (!nativeMethods[name]) {
    nativeMethods[name] = fn;
  }
}

const defineNativeAccessors = (proto, descriptors) => {
  patchProperties(proto, descriptors, NATIVE_PREFIX);
  // make native accessors available to users
  for (let prop in descriptors) {
    installNativeAccessor(prop);
  }
}

const copyProperties = (proto, list = []) => {
  for (let i = 0; i < list.length; i++) {
    const name = list[i];
    const descriptor = Object.getOwnPropertyDescriptor(proto, name);
    if (descriptor) {
      Object.defineProperty(proto, NATIVE_PREFIX + name, descriptor);
      // make native methods/accessors available to users
      if (descriptor.value) {
        installNativeMethod(name, descriptor.value);
      } else {
        installNativeAccessor(name);

      }
    }
  }
}

/** @type {!TreeWalker} */
const nodeWalker = document.createTreeWalker(document, NodeFilter.SHOW_ALL,
  null, false);

/** @type {!TreeWalker} */
const elementWalker = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT,
  null, false);

/** @type {!Document} */
const inertDoc = document.implementation.createHTMLDocument('inert');

const clearNode = node => {
  let firstChild;
  while ((firstChild = node[NATIVE_PREFIX + 'firstChild'])) {
    node[NATIVE_PREFIX + 'removeChild'](firstChild);
  }
}

const ParentNodeAccessors = [
  'firstElementChild',
  'lastElementChild',
  'children',
  'childElementCount',
];

const ParentNodeMethods = [
  'querySelector',
  'querySelectorAll'
  // 'append', 'prepend'
];

export const addNativePrefixedProperties = () => {

  // EventTarget
  const eventProps = [
    'dispatchEvent',
    'addEventListener',
    'removeEventListener'
  ];
  if (window.EventTarget) {
    copyProperties(window.EventTarget.prototype, eventProps);
  } else {
    copyProperties(Node.prototype, eventProps);
    copyProperties(Window.prototype, eventProps);
  }

  // Node
  if (hasDescriptors) {
    copyProperties(Node.prototype, [
      'parentNode',
      'firstChild',
      'lastChild',
      'previousSibling',
      'nextSibling',
      'childNodes',
      'parentElement',
      'textContent',
    ]);
  } else {
    defineNativeAccessors(Node.prototype, {
      parentNode: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.parentNode();
        }
      },
      firstChild: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.firstChild();
        }
      },
      lastChild: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.lastChild();
        }

      },
      previousSibling: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.previousSibling();
        }
      },
      nextSibling: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.nextSibling();
        }
      },
      // TODO(sorvell): make this a NodeList or whatever
      childNodes: {
        /** @this {Node} */
        get() {
          const nodes = [];
          nodeWalker.currentNode = this;
          let n = nodeWalker.firstChild();
          while (n) {
            nodes.push(n);
            n = nodeWalker.nextSibling();
          }
          return nodes;
        }
      },
      parentElement: {
        /** @this {Node} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.parentNode();
        }
      },
      textContent: {
        /** @this {Node} */
        get() {
          /* eslint-disable no-case-declarations */
          switch (this.nodeType) {
            case Node.ELEMENT_NODE:
            case Node.DOCUMENT_FRAGMENT_NODE:
              // TODO(sorvell): This cannot be a single TreeWalker that's reused
              // at least for Safari 9, but it's unclear why.
              const textWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT,
                null, false);
              let content = '', n;
              while ( (n = textWalker.nextNode()) ) {
                // TODO(sorvell): can't use textContent since we patch it on Node.prototype!
                // However, should probably patch it only on element.
                content += n.nodeValue;
              }
              return content;
            default:
              return this.nodeValue;
          }
        },
        // Needed on browsers that do not proper accessors (e.g. old versions of Chrome)
        /** @this {Node} */
        set(value) {
          if (typeof value === 'undefined' || value === null) {
            value = ''
          }
          switch (this.nodeType) {
            case Node.ELEMENT_NODE:
            case Node.DOCUMENT_FRAGMENT_NODE:
              clearNode(this);
              // Document fragments must have no childnodes if setting a blank string
              if (value.length > 0 || this.nodeType === Node.ELEMENT_NODE) {
                // Note: old Chrome versions require 2nd argument here
                this[NATIVE_PREFIX + 'insertBefore'](document.createTextNode(value), undefined);
              }
              break;
            default:
              // TODO(sorvell): can't do this if patch nodeValue.
              this.nodeValue = value;
              break;
          }
        }
      }
    });
  }

  copyProperties(Node.prototype, [
    'appendChild',
    'insertBefore',
    'removeChild',
    'replaceChild',
    'cloneNode',
    'contains'
  ]);

  const ParentNodeWalkerDescriptors = {
    firstElementChild: {
      /** @this {ParentNode} */
      get() {
        elementWalker.currentNode = this;
        return elementWalker.firstChild();
      }
    },
    lastElementChild: {
      /** @this {ParentNode} */
      get() {
        elementWalker.currentNode = this;
        return elementWalker.lastChild();
      }
    },
    children: {
      /** @this {ParentNode} */
      get() {
        let nodes = [];
        elementWalker.currentNode = this;
        let n = elementWalker.firstChild();
        while (n) {
          nodes.push(n);
          n = elementWalker.nextSibling();
        }
        return utils.createPolyfilledHTMLCollection(nodes);
      }
    },
    childElementCount: {
      /** @this {ParentNode} */
      get() {
        if (this.children) {
          return this.children.length;
        }
        return 0;
      }
    }
  };

  // Element
  if (hasDescriptors) {
    copyProperties(Element.prototype, ParentNodeAccessors);

    copyProperties(Element.prototype, [
      'previousElementSibling',
      'nextElementSibling',
      'innerHTML'
    ]);

    // NOTE, on IE 11 / Edge 15 children and/or innerHTML are on HTMLElement instead of Element
    if (Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'children')) {
      copyProperties(HTMLElement.prototype, [
        'children'
      ]);
    }
    if (Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'innerHTML')) {
      copyProperties(HTMLElement.prototype, [
        'innerHTML'
      ]);
    }
  } else {
    defineNativeAccessors(Element.prototype, ParentNodeWalkerDescriptors);
    defineNativeAccessors(Element.prototype, {
      previousElementSibling: {
        /** @this {Element} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.previousSibling();
        }
      },
      nextElementSibling: {
        /** @this {Element} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.nextSibling();
        }
      },
      innerHTML: {
        /** @this {Element} */
        get() {
          return getInnerHTML(this, utils.nativeChildNodesArray);
        },
        // Needed on browsers that do not proper accessors (e.g. old versions of Chrome)
        /** @this {Element} */
        set(value) {
          const content = this.localName === 'template' ?
          /** @type {HTMLTemplateElement} */(this).content : this;
          clearNode(content);
          const containerName = this.localName || 'div';
          let htmlContainer;
          if (!this.namespaceURI || this.namespaceURI === inertDoc.namespaceURI) {
            htmlContainer = inertDoc.createElement(containerName);
          } else {
            htmlContainer = inertDoc.createElementNS(this.namespaceURI, containerName);
          }
          htmlContainer.innerHTML = value;
          const newContent = this.localName === 'template' ?
            /** @type {HTMLTemplateElement} */(htmlContainer).content : htmlContainer;
          let firstChild;
          while ((firstChild = newContent[NATIVE_PREFIX + 'firstChild'])) {
            // Note: old Chrome versions require 2nd argument here
            content[NATIVE_PREFIX + 'insertBefore'](firstChild, undefined);
          }
        }
      }
    });
  }

  copyProperties(Element.prototype, [
    'setAttribute',
    'getAttribute',
    'hasAttribute',
    'removeAttribute',
    // on older Safari, these are on Element.
    'focus',
    'blur',
  ]);
  copyProperties(Element.prototype, ParentNodeMethods);

  // HTMLElement
  copyProperties(HTMLElement.prototype, [
    'focus',
    'blur',
    // On IE these are on HTMLElement
    'contains'
  ]);

  if (hasDescriptors) {
    copyProperties(HTMLElement.prototype, [
      'parentElement',
      'children',
      'innerHTML'
    ]);
  }

  // HTMLTemplateElement
  if (window.HTMLTemplateElement) {
    copyProperties(window.HTMLTemplateElement.prototype, ['innerHTML']);
  }

  // DocumentFragment
  if (hasDescriptors) {
    // NOTE, IE 11 does not have on DocumentFragment
    // firstElementChild
    // lastElementChild
    copyProperties(DocumentFragment.prototype, ParentNodeAccessors);
  } else {
    defineNativeAccessors(DocumentFragment.prototype, ParentNodeWalkerDescriptors);
  }

  copyProperties(DocumentFragment.prototype, ParentNodeMethods);

  // Document
  if (hasDescriptors) {
    copyProperties(Document.prototype, ParentNodeAccessors);
    copyProperties(Document.prototype, [
      'activeElement'
    ]);
  } else {
    defineNativeAccessors(Document.prototype, ParentNodeWalkerDescriptors);
  }

  copyProperties(Document.prototype, [
    'importNode',
    'getElementById'
  ]);
  copyProperties(Document.prototype, ParentNodeMethods);

};
<commit_msg>Simplify native patching slightly and add className<commit_after>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
import * as utils from './utils.js';
import {patchProperties} from './utils.js';
import {getInnerHTML} from './innerHTML.js';

const hasDescriptors = utils.settings.hasDescriptors;
export const NATIVE_PREFIX = utils.NATIVE_PREFIX;

// Object on which raw native methods are stored.
// e.g. `nativeMethods.querySelector.call(node, selector)`
// same as `node.querySelector(selector)`
export const nativeMethods = {
  /** @this {Element} */
  querySelector(selector) {
    return this[NATIVE_PREFIX + 'querySelector'](selector);
  },
  /** @this {Element} */
  querySelectorAll(selector) {
    return this[NATIVE_PREFIX + 'querySelectorAll'](selector);
  }
};
// Object on which raw native accessors are available via `accessorName(node)`.
// e.g. `nativeTree.firstChild(node)`
// same as `node.firstChild`
export const nativeTree = {};

const installNativeAccessor = (name) => {
  nativeTree[name] = (node) => node[NATIVE_PREFIX + name];
}

const installNativeMethod = (name, fn) => {
  if (!nativeMethods[name]) {
    nativeMethods[name] = fn;
  }
}

const defineNativeAccessors = (proto, descriptors) => {
  patchProperties(proto, descriptors, NATIVE_PREFIX);
  // make native accessors available to users
  for (let prop in descriptors) {
    installNativeAccessor(prop);
  }
}

const copyProperties = (proto, list = []) => {
  for (let i = 0; i < list.length; i++) {
    const name = list[i];
    const descriptor = Object.getOwnPropertyDescriptor(proto, name);
    if (descriptor) {
      Object.defineProperty(proto, NATIVE_PREFIX + name, descriptor);
      // make native methods/accessors available to users
      if (descriptor.value) {
        installNativeMethod(name, descriptor.value);
      } else {
        installNativeAccessor(name);
      }
    }
  }
}

/** @type {!TreeWalker} */
const nodeWalker = document.createTreeWalker(document, NodeFilter.SHOW_ALL,
  null, false);

/** @type {!TreeWalker} */
const elementWalker = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT,
  null, false);

/** @type {!Document} */
const inertDoc = document.implementation.createHTMLDocument('inert');

const clearNode = node => {
  let firstChild;
  while ((firstChild = node[NATIVE_PREFIX + 'firstChild'])) {
    node[NATIVE_PREFIX + 'removeChild'](firstChild);
  }
}

const ParentNodeAccessors = [
  'firstElementChild',
  'lastElementChild',
  'children',
  'childElementCount',
];

const ParentNodeMethods = [
  'querySelector',
  'querySelectorAll'
  // 'append', 'prepend'
];

export const addNativePrefixedProperties = () => {

  // EventTarget
  const eventProps = [
    'dispatchEvent',
    'addEventListener',
    'removeEventListener'
  ];
  if (window.EventTarget) {
    copyProperties(window.EventTarget.prototype, eventProps);
  } else {
    copyProperties(Node.prototype, eventProps);
    copyProperties(Window.prototype, eventProps);
  }

  // Node
  if (hasDescriptors) {
    copyProperties(Node.prototype, [
      'parentNode',
      'firstChild',
      'lastChild',
      'previousSibling',
      'nextSibling',
      'childNodes',
      'parentElement',
      'textContent',
    ]);
  } else {
    defineNativeAccessors(Node.prototype, {
      parentNode: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.parentNode();
        }
      },
      firstChild: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.firstChild();
        }
      },
      lastChild: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.lastChild();
        }

      },
      previousSibling: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.previousSibling();
        }
      },
      nextSibling: {
        /** @this {Node} */
        get() {
          nodeWalker.currentNode = this;
          return nodeWalker.nextSibling();
        }
      },
      // TODO(sorvell): make this a NodeList or whatever
      childNodes: {
        /** @this {Node} */
        get() {
          const nodes = [];
          nodeWalker.currentNode = this;
          let n = nodeWalker.firstChild();
          while (n) {
            nodes.push(n);
            n = nodeWalker.nextSibling();
          }
          return nodes;
        }
      },
      parentElement: {
        /** @this {Node} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.parentNode();
        }
      },
      textContent: {
        /** @this {Node} */
        get() {
          /* eslint-disable no-case-declarations */
          switch (this.nodeType) {
            case Node.ELEMENT_NODE:
            case Node.DOCUMENT_FRAGMENT_NODE:
              // TODO(sorvell): This cannot be a single TreeWalker that's reused
              // at least for Safari 9, but it's unclear why.
              const textWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT,
                null, false);
              let content = '', n;
              while ( (n = textWalker.nextNode()) ) {
                // TODO(sorvell): can't use textContent since we patch it on Node.prototype!
                // However, should probably patch it only on element.
                content += n.nodeValue;
              }
              return content;
            default:
              return this.nodeValue;
          }
        },
        // Needed on browsers that do not proper accessors (e.g. old versions of Chrome)
        /** @this {Node} */
        set(value) {
          if (typeof value === 'undefined' || value === null) {
            value = ''
          }
          switch (this.nodeType) {
            case Node.ELEMENT_NODE:
            case Node.DOCUMENT_FRAGMENT_NODE:
              clearNode(this);
              // Document fragments must have no childnodes if setting a blank string
              if (value.length > 0 || this.nodeType === Node.ELEMENT_NODE) {
                // Note: old Chrome versions require 2nd argument here
                this[NATIVE_PREFIX + 'insertBefore'](document.createTextNode(value), undefined);
              }
              break;
            default:
              // TODO(sorvell): can't do this if patch nodeValue.
              this.nodeValue = value;
              break;
          }
        }
      }
    });
  }

  copyProperties(Node.prototype, [
    'appendChild',
    'insertBefore',
    'removeChild',
    'replaceChild',
    'cloneNode',
    'contains'
  ]);

  // NOTE, on some browsers IE 11 / Edge 15 some properties are incorrectly on HTMLElement
  copyProperties(HTMLElement.prototype, [
    'parentElement',
    'contains'
  ]);

  const ParentNodeWalkerDescriptors = {
    firstElementChild: {
      /** @this {ParentNode} */
      get() {
        elementWalker.currentNode = this;
        return elementWalker.firstChild();
      }
    },
    lastElementChild: {
      /** @this {ParentNode} */
      get() {
        elementWalker.currentNode = this;
        return elementWalker.lastChild();
      }
    },
    children: {
      /** @this {ParentNode} */
      get() {
        let nodes = [];
        elementWalker.currentNode = this;
        let n = elementWalker.firstChild();
        while (n) {
          nodes.push(n);
          n = elementWalker.nextSibling();
        }
        return utils.createPolyfilledHTMLCollection(nodes);
      }
    },
    childElementCount: {
      /** @this {ParentNode} */
      get() {
        if (this.children) {
          return this.children.length;
        }
        return 0;
      }
    }
  };

  // Element
  if (hasDescriptors) {
    copyProperties(Element.prototype, ParentNodeAccessors);

    copyProperties(Element.prototype, [
      'previousElementSibling',
      'nextElementSibling',
      'innerHTML',
      'className'
    ]);

    // NOTE, on some browsers IE 11 / Edge 15 some properties are incorrectly on HTMLElement
    copyProperties(HTMLElement.prototype, [
      'children',
      'innerHTML',
      'className'
    ]);
  } else {
    defineNativeAccessors(Element.prototype, ParentNodeWalkerDescriptors);
    defineNativeAccessors(Element.prototype, {
      previousElementSibling: {
        /** @this {Element} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.previousSibling();
        }
      },
      nextElementSibling: {
        /** @this {Element} */
        get() {
          elementWalker.currentNode = this;
          return elementWalker.nextSibling();
        }
      },
      innerHTML: {
        /** @this {Element} */
        get() {
          return getInnerHTML(this, utils.nativeChildNodesArray);
        },
        // Needed on browsers that do not proper accessors (e.g. old versions of Chrome)
        /** @this {Element} */
        set(value) {
          const content = this.localName === 'template' ?
          /** @type {HTMLTemplateElement} */(this).content : this;
          clearNode(content);
          const containerName = this.localName || 'div';
          let htmlContainer;
          if (!this.namespaceURI || this.namespaceURI === inertDoc.namespaceURI) {
            htmlContainer = inertDoc.createElement(containerName);
          } else {
            htmlContainer = inertDoc.createElementNS(this.namespaceURI, containerName);
          }
          htmlContainer.innerHTML = value;
          const newContent = this.localName === 'template' ?
            /** @type {HTMLTemplateElement} */(htmlContainer).content : htmlContainer;
          let firstChild;
          while ((firstChild = newContent[NATIVE_PREFIX + 'firstChild'])) {
            // Note: old Chrome versions require 2nd argument here
            content[NATIVE_PREFIX + 'insertBefore'](firstChild, undefined);
          }
        }
      },
      className: {
        /** @this {Element} */
        get() {
          return this.getAttribute('class') || '';
        },
        /** @this {Element} */
        set(value) {
          this.setAttribute('class', value);
        }
      }
    });
  }

  copyProperties(Element.prototype, [
    'setAttribute',
    'getAttribute',
    'hasAttribute',
    'removeAttribute',
    // on older Safari, these are on Element.
    'focus',
    'blur',
  ]);
  copyProperties(Element.prototype, ParentNodeMethods);

  // HTMLElement
  copyProperties(HTMLElement.prototype, [
    'focus',
    'blur'
  ]);

  // HTMLTemplateElement
  if (window.HTMLTemplateElement) {
    copyProperties(window.HTMLTemplateElement.prototype, ['innerHTML']);
  }

  // DocumentFragment
  if (hasDescriptors) {
    // NOTE, IE 11 does not have on DocumentFragment
    // firstElementChild
    // lastElementChild
    copyProperties(DocumentFragment.prototype, ParentNodeAccessors);
  } else {
    defineNativeAccessors(DocumentFragment.prototype, ParentNodeWalkerDescriptors);
  }

  copyProperties(DocumentFragment.prototype, ParentNodeMethods);

  // Document
  if (hasDescriptors) {
    copyProperties(Document.prototype, ParentNodeAccessors);
    copyProperties(Document.prototype, [
      'activeElement'
    ]);
  } else {
    defineNativeAccessors(Document.prototype, ParentNodeWalkerDescriptors);
  }

  copyProperties(Document.prototype, [
    'importNode',
    'getElementById'
  ]);
  copyProperties(Document.prototype, ParentNodeMethods);

};
<|endoftext|>