<commit_before>import { Util } from './util';
import { DateTime } from '../datetime';
import { English } from './english';

function stringifyTokens(splits, tokenToString) {
  let s = '';
  for (const token of splits) {
    if (token.literal) {
      s += token.val;
    } else {
      s += tokenToString(token.val);
    }
  }
  return s;
}

/**
 * @private
 */

export class Formatter {
  static create(locale, opts = {}) {
    const formatOpts = Object.assign({}, { round: true }, opts);
    return new Formatter(locale, formatOpts);
  }

  static parseFormat(fmt) {
    let current = null,
      currentFull = '',
      bracketed = false;
    const splits = [];
    for (let i = 0; i < fmt.length; i++) {
      const c = fmt.charAt(i);
      if (c === "'") {
        if (currentFull.length > 0) {
          splits.push({ literal: bracketed, val: currentFull });
        }
        current = null;
        currentFull = '';
        bracketed = !bracketed;
      } else if (bracketed) {
        currentFull += c;
      } else if (c === current) {
        currentFull += c;
      } else {
        if (currentFull.length > 0) {
          splits.push({ literal: false, val: currentFull });
        }
        currentFull = c;
        current = c;
      }
    }

    if (currentFull.length > 0) {
      splits.push({ literal: bracketed, val: currentFull });
    }

    return splits;
  }

  constructor(locale, formatOpts) {
    this.opts = formatOpts;
    this.loc = locale;
    this.systemLoc = null;
  }

  formatWithSystemDefault(dt, opts) {
    if (this.systemLoc === null) {
      this.systemLoc = this.loc.redefaultToSystem();
    }
    const df = this.systemLoc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.format();
  }

  formatDateTime(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.format();
  }

  formatDateTimeParts(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.formatToParts();
  }

  resolvedOptions(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.resolvedOptions();
  }

  num(n, p = 0) {
    const opts = Object.assign({}, this.opts);

    if (p > 0) {
      opts.padTo = p;
    }

    return this.loc.numberFormatter(opts).format(n);
  }

  formatDateTimeFromString(dt, fmt) {
    const knownEnglish = this.loc.listingMode() === 'en';
    const string = (opts, extract) => this.loc.extract(dt, opts, extract),
      formatOffset = opts => {
        if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
          return 'Z';
        }

        const hours = Math.trunc(dt.offset / 60),
          minutes = Math.abs(dt.offset % 60),
          sign = hours >= 0 ? '+' : '-',
          base = `${sign}${Math.abs(hours)}`;

        switch (opts.format) {
          case 'short':
            return `${sign}${this.num(Math.abs(hours), 2)}:${this.num(minutes, 2)}`;
          case 'narrow':
            return minutes > 0 ? `${base}:${minutes}` : base;
          case 'techie':
            return `${sign}${this.num(Math.abs(hours), 2)}${this.num(minutes, 2)}`;
          default:
            throw new RangeError(`Value format ${opts.format} is out of range for property format`);
        }
      },
      meridiem = () =>
        knownEnglish
          ? English.meridiemForDateTime(dt)
          : string({ hour: 'numeric', hour12: true }, 'dayperiod'),
      month = (length, standalone) =>
        knownEnglish
          ? English.monthForDateTime(dt, length)
          : string(standalone ? { month: length } : { month: length, day: 'numeric' }, 'month'),
      weekday = (length, standalone) =>
        knownEnglish
          ? English.weekdayForDateTime(dt, length)
          : string(
              standalone ? { weekday: length } : { weekday: length, month: 'long', day: 'numeric' },
              'weekday'
            ),
      era = length =>
        knownEnglish ? English.eraForDateTime(dt, length) : string({ era: length }, 'era'),
      tokenToString = token => {
        const outputCal = this.loc.outputCalendar;

        // Where possible: http://cldr.unicode.org/translation/date-time#TOC-Stand-Alone-vs.-Format-Styles
        switch (token) {
          // ms
          case 'S':
            return this.num(dt.millisecond);
          case 'u':
          // falls through
          case 'SSS':
            return this.num(dt.millisecond, 3);
          // seconds
          case 's':
            return this.num(dt.second);
          case 'ss':
            return this.num(dt.second, 2);
          // minutes
          case 'm':
            return this.num(dt.minute);
          case 'mm':
            return this.num(dt.minute, 2);
          // hours
          case 'h':
            return this.num(dt.hour === 12 ? 12 : dt.hour % 12);
          case 'hh':
            return this.num(dt.hour === 12 ? 12 : dt.hour % 12, 2);
          case 'H':
            return this.num(dt.hour);
          case 'HH':
            return this.num(dt.hour, 2);
          // offset
          case 'Z':
            // like +6
            return formatOffset({ format: 'narrow', allowZ: true });
          case 'ZZ':
            // like +06:00
            return formatOffset({ format: 'short', allowZ: true });
          case 'ZZZ':
            // like +0600
            return formatOffset({ format: 'techie', allowZ: false });
          case 'ZZZZ':
            // like EST
            return dt.offsetNameShort;
          case 'ZZZZZ':
            // like Eastern Standard Time
            return dt.offsetNameLong;
          // zone
          case 'z':
            return dt.zoneName;
          // like America/New_York
          // meridiems
          case 'a':
            return meridiem();
          // dates
          case 'd':
            return outputCal ? string({ day: 'numeric' }, 'day') : this.num(dt.day);
          case 'dd':
            return outputCal ? string({ day: '2-digit' }, 'day') : this.num(dt.day, 2);
          // weekdays - standalone
          case 'c':
            // like 1
            return this.num(dt.weekday);
          case 'ccc':
            // like 'Tues'
            return weekday('short', true);
          case 'cccc':
            // like 'Tuesday'
            return weekday('long', true);
          case 'ccccc':
            // like 'T'
            return weekday('narrow', true);
          // weekdays - format
          case 'E':
            // like 1
            return this.num(dt.weekday);
          case 'EEE':
            // like 'Tues'
            return weekday('short', false);
          case 'EEEE':
            // like 'Tuesday'
            return weekday('long', false);
          case 'EEEEE':
            // like 'T'
            return weekday('narrow', false);
          // months - standalone
          case 'L':
            // like 1
            return outputCal
              ? string({ month: 'numeric', day: 'numeric' }, 'month')
              : this.num(dt.month);
          case 'LL':
            // like 01, doesn't seem to work
            return outputCal
              ? string({ month: '2-digit', day: 'numeric' }, 'month')
              : this.num(dt.month, 2);
          case 'LLL':
            // like Jan
            return month('short', true);
          case 'LLLL':
            // like January
            return month('long', true);
          case 'LLLLL':
            // like J
            return month('narrow', true);
          // months - format
          case 'M':
            // like 1
            return outputCal ? string({ month: 'numeric' }, 'month') : this.num(dt.month);
          case 'MM':
            // like 01
            return outputCal ? string({ month: '2-digit' }, 'month') : this.num(dt.month, 2);
          case 'MMM':
            // like Jan
            return month('short', false);
          case 'MMMM':
            // like January
            return month('long', false);
          case 'MMMMM':
            // like J
            return month('narrow', false);
          // years
          case 'y':
            // like 2014
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year);
          case 'yy':
            // like 14
            return outputCal
              ? string({ year: '2-digit' }, 'year')
              : this.num(dt.year.toString().slice(-2), 2);
          case 'yyyy':
            // like 0012
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year, 4);
          case 'yyyyyy':
            // like 000012
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year, 6);
          // eras
          case 'G':
            // like AD
            return era('short');
          case 'GG':
            // like Anno Domini
            return era('long');
          case 'GGGGG':
            return era('narrow');
          case 'kk':
            return this.num(dt.weekYear.toString().slice(-2), 2);
          case 'kkkk':
            return this.num(dt.weekYear, 4);
          case 'W':
            return this.num(dt.weekNumber);
          case 'WW':
            return this.num(dt.weekNumber, 2);
          case 'o':
            return this.num(dt.ordinal);
          case 'ooo':
            return this.num(dt.ordinal, 3);
          // macros
          case 'D':
            return this.formatWithSystemDefault(dt, DateTime.DATE_SHORT);
          case 'DD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_MED);
          case 'DDD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_FULL);
          case 'DDDD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_HUGE);
          case 't':
            return this.formatWithSystemDefault(dt, DateTime.TIME_SIMPLE);
          case 'tt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_SECONDS);
          case 'ttt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_SHORT_OFFSET);
          case 'tttt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_LONG_OFFSET);
          case 'T':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_SIMPLE);
          case 'TT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_SECONDS);
          case 'TTT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_SHORT_OFFSET);
          case 'TTTT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_LONG_OFFSET);
          case 'f':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_SHORT);
          case 'ff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_MED);
          case 'fff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_FULL);
          case 'ffff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_HUGE);
          case 'F':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_SHORT_WITH_SECONDS);
          case 'FF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_MED_WITH_SECONDS);
          case 'FFF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_FULL_WITH_SECONDS);
          case 'FFFF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_HUGE_WITH_SECONDS);

          default:
            return token;
        }
      };

    return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
  }

  formatDurationFromString(dur, fmt) {
    const tokenToField = token => {
        switch (token[0]) {
          case 'S':
            return 'millisecond';
          case 's':
            return 'second';
          case 'm':
            return 'minute';
          case 'h':
            return 'hour';
          case 'd':
            return 'day';
          case 'M':
            return 'month';
          case 'y':
            return 'year';
          default:
            return null;
        }
      },
      tokenToString = lildur => token => {
        const mapped = tokenToField(token);
        if (mapped) {
          return this.num(lildur.get(mapped), token.length);
        } else {
          return token;
        }
      },
      tokens = Formatter.parseFormat(fmt),
      realTokens = tokens.reduce(
        (found, { literal, val }) => (literal ? found : found.concat(val)),
        []
      ),
      collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t));
    return stringifyTokens(tokens, tokenToString(collapsed));
  }
}
<commit_msg>remove unused import<commit_after>import { DateTime } from '../datetime';
import { English } from './english';

function stringifyTokens(splits, tokenToString) {
  let s = '';
  for (const token of splits) {
    if (token.literal) {
      s += token.val;
    } else {
      s += tokenToString(token.val);
    }
  }
  return s;
}

/**
 * @private
 */

export class Formatter {
  static create(locale, opts = {}) {
    const formatOpts = Object.assign({}, { round: true }, opts);
    return new Formatter(locale, formatOpts);
  }

  static parseFormat(fmt) {
    let current = null,
      currentFull = '',
      bracketed = false;
    const splits = [];
    for (let i = 0; i < fmt.length; i++) {
      const c = fmt.charAt(i);
      if (c === "'") {
        if (currentFull.length > 0) {
          splits.push({ literal: bracketed, val: currentFull });
        }
        current = null;
        currentFull = '';
        bracketed = !bracketed;
      } else if (bracketed) {
        currentFull += c;
      } else if (c === current) {
        currentFull += c;
      } else {
        if (currentFull.length > 0) {
          splits.push({ literal: false, val: currentFull });
        }
        currentFull = c;
        current = c;
      }
    }

    if (currentFull.length > 0) {
      splits.push({ literal: bracketed, val: currentFull });
    }

    return splits;
  }

  constructor(locale, formatOpts) {
    this.opts = formatOpts;
    this.loc = locale;
    this.systemLoc = null;
  }

  formatWithSystemDefault(dt, opts) {
    if (this.systemLoc === null) {
      this.systemLoc = this.loc.redefaultToSystem();
    }
    const df = this.systemLoc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.format();
  }

  formatDateTime(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.format();
  }

  formatDateTimeParts(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.formatToParts();
  }

  resolvedOptions(dt, opts = {}) {
    const df = this.loc.dtFormatter(dt, Object.assign({}, this.opts, opts));
    return df.resolvedOptions();
  }

  num(n, p = 0) {
    const opts = Object.assign({}, this.opts);

    if (p > 0) {
      opts.padTo = p;
    }

    return this.loc.numberFormatter(opts).format(n);
  }

  formatDateTimeFromString(dt, fmt) {
    const knownEnglish = this.loc.listingMode() === 'en';
    const string = (opts, extract) => this.loc.extract(dt, opts, extract),
      formatOffset = opts => {
        if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
          return 'Z';
        }

        const hours = Math.trunc(dt.offset / 60),
          minutes = Math.abs(dt.offset % 60),
          sign = hours >= 0 ? '+' : '-',
          base = `${sign}${Math.abs(hours)}`;

        switch (opts.format) {
          case 'short':
            return `${sign}${this.num(Math.abs(hours), 2)}:${this.num(minutes, 2)}`;
          case 'narrow':
            return minutes > 0 ? `${base}:${minutes}` : base;
          case 'techie':
            return `${sign}${this.num(Math.abs(hours), 2)}${this.num(minutes, 2)}`;
          default:
            throw new RangeError(`Value format ${opts.format} is out of range for property format`);
        }
      },
      meridiem = () =>
        knownEnglish
          ? English.meridiemForDateTime(dt)
          : string({ hour: 'numeric', hour12: true }, 'dayperiod'),
      month = (length, standalone) =>
        knownEnglish
          ? English.monthForDateTime(dt, length)
          : string(standalone ? { month: length } : { month: length, day: 'numeric' }, 'month'),
      weekday = (length, standalone) =>
        knownEnglish
          ? English.weekdayForDateTime(dt, length)
          : string(
              standalone ? { weekday: length } : { weekday: length, month: 'long', day: 'numeric' },
              'weekday'
            ),
      era = length =>
        knownEnglish ? English.eraForDateTime(dt, length) : string({ era: length }, 'era'),
      tokenToString = token => {
        const outputCal = this.loc.outputCalendar;

        // Where possible: http://cldr.unicode.org/translation/date-time#TOC-Stand-Alone-vs.-Format-Styles
        switch (token) {
          // ms
          case 'S':
            return this.num(dt.millisecond);
          case 'u':
          // falls through
          case 'SSS':
            return this.num(dt.millisecond, 3);
          // seconds
          case 's':
            return this.num(dt.second);
          case 'ss':
            return this.num(dt.second, 2);
          // minutes
          case 'm':
            return this.num(dt.minute);
          case 'mm':
            return this.num(dt.minute, 2);
          // hours
          case 'h':
            return this.num(dt.hour === 12 ? 12 : dt.hour % 12);
          case 'hh':
            return this.num(dt.hour === 12 ? 12 : dt.hour % 12, 2);
          case 'H':
            return this.num(dt.hour);
          case 'HH':
            return this.num(dt.hour, 2);
          // offset
          case 'Z':
            // like +6
            return formatOffset({ format: 'narrow', allowZ: true });
          case 'ZZ':
            // like +06:00
            return formatOffset({ format: 'short', allowZ: true });
          case 'ZZZ':
            // like +0600
            return formatOffset({ format: 'techie', allowZ: false });
          case 'ZZZZ':
            // like EST
            return dt.offsetNameShort;
          case 'ZZZZZ':
            // like Eastern Standard Time
            return dt.offsetNameLong;
          // zone
          case 'z':
            return dt.zoneName;
          // like America/New_York
          // meridiems
          case 'a':
            return meridiem();
          // dates
          case 'd':
            return outputCal ? string({ day: 'numeric' }, 'day') : this.num(dt.day);
          case 'dd':
            return outputCal ? string({ day: '2-digit' }, 'day') : this.num(dt.day, 2);
          // weekdays - standalone
          case 'c':
            // like 1
            return this.num(dt.weekday);
          case 'ccc':
            // like 'Tues'
            return weekday('short', true);
          case 'cccc':
            // like 'Tuesday'
            return weekday('long', true);
          case 'ccccc':
            // like 'T'
            return weekday('narrow', true);
          // weekdays - format
          case 'E':
            // like 1
            return this.num(dt.weekday);
          case 'EEE':
            // like 'Tues'
            return weekday('short', false);
          case 'EEEE':
            // like 'Tuesday'
            return weekday('long', false);
          case 'EEEEE':
            // like 'T'
            return weekday('narrow', false);
          // months - standalone
          case 'L':
            // like 1
            return outputCal
              ? string({ month: 'numeric', day: 'numeric' }, 'month')
              : this.num(dt.month);
          case 'LL':
            // like 01, doesn't seem to work
            return outputCal
              ? string({ month: '2-digit', day: 'numeric' }, 'month')
              : this.num(dt.month, 2);
          case 'LLL':
            // like Jan
            return month('short', true);
          case 'LLLL':
            // like January
            return month('long', true);
          case 'LLLLL':
            // like J
            return month('narrow', true);
          // months - format
          case 'M':
            // like 1
            return outputCal ? string({ month: 'numeric' }, 'month') : this.num(dt.month);
          case 'MM':
            // like 01
            return outputCal ? string({ month: '2-digit' }, 'month') : this.num(dt.month, 2);
          case 'MMM':
            // like Jan
            return month('short', false);
          case 'MMMM':
            // like January
            return month('long', false);
          case 'MMMMM':
            // like J
            return month('narrow', false);
          // years
          case 'y':
            // like 2014
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year);
          case 'yy':
            // like 14
            return outputCal
              ? string({ year: '2-digit' }, 'year')
              : this.num(dt.year.toString().slice(-2), 2);
          case 'yyyy':
            // like 0012
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year, 4);
          case 'yyyyyy':
            // like 000012
            return outputCal ? string({ year: 'numeric' }, 'year') : this.num(dt.year, 6);
          // eras
          case 'G':
            // like AD
            return era('short');
          case 'GG':
            // like Anno Domini
            return era('long');
          case 'GGGGG':
            return era('narrow');
          case 'kk':
            return this.num(dt.weekYear.toString().slice(-2), 2);
          case 'kkkk':
            return this.num(dt.weekYear, 4);
          case 'W':
            return this.num(dt.weekNumber);
          case 'WW':
            return this.num(dt.weekNumber, 2);
          case 'o':
            return this.num(dt.ordinal);
          case 'ooo':
            return this.num(dt.ordinal, 3);
          // macros
          case 'D':
            return this.formatWithSystemDefault(dt, DateTime.DATE_SHORT);
          case 'DD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_MED);
          case 'DDD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_FULL);
          case 'DDDD':
            return this.formatWithSystemDefault(dt, DateTime.DATE_HUGE);
          case 't':
            return this.formatWithSystemDefault(dt, DateTime.TIME_SIMPLE);
          case 'tt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_SECONDS);
          case 'ttt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_SHORT_OFFSET);
          case 'tttt':
            return this.formatWithSystemDefault(dt, DateTime.TIME_WITH_LONG_OFFSET);
          case 'T':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_SIMPLE);
          case 'TT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_SECONDS);
          case 'TTT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_SHORT_OFFSET);
          case 'TTTT':
            return this.formatWithSystemDefault(dt, DateTime.TIME_24_WITH_LONG_OFFSET);
          case 'f':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_SHORT);
          case 'ff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_MED);
          case 'fff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_FULL);
          case 'ffff':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_HUGE);
          case 'F':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_SHORT_WITH_SECONDS);
          case 'FF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_MED_WITH_SECONDS);
          case 'FFF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_FULL_WITH_SECONDS);
          case 'FFFF':
            return this.formatWithSystemDefault(dt, DateTime.DATETIME_HUGE_WITH_SECONDS);

          default:
            return token;
        }
      };

    return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
  }

  formatDurationFromString(dur, fmt) {
    const tokenToField = token => {
        switch (token[0]) {
          case 'S':
            return 'millisecond';
          case 's':
            return 'second';
          case 'm':
            return 'minute';
          case 'h':
            return 'hour';
          case 'd':
            return 'day';
          case 'M':
            return 'month';
          case 'y':
            return 'year';
          default:
            return null;
        }
      },
      tokenToString = lildur => token => {
        const mapped = tokenToField(token);
        if (mapped) {
          return this.num(lildur.get(mapped), token.length);
        } else {
          return token;
        }
      },
      tokens = Formatter.parseFormat(fmt),
      realTokens = tokens.reduce(
        (found, { literal, val }) => (literal ? found : found.concat(val)),
        []
      ),
      collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter(t => t));
    return stringifyTokens(tokens, tokenToString(collapsed));
  }
}
<|endoftext|>


<commit_before>
/**
 * @param container
 * @constructor
 */
var Vector = function (container) {

};

/**
 * This method copies all own properties(enumerable and non-enumerable)
 * carefully with descriptors from source objects to target and merges them.
 * It does not make deep copy of properties.
 *
 * @param target object which will be merged by sources
 * @return target object
 */
Vector.merge = function (target) {
    if (isNullOrUndefined(target))
        throw new TypeError("Target object can't be null or undefined");
    target = Object(target);
    var i,
        source,
        descriptors;

    for(i = 1; i<arguments.length; ++i) {
        source = arguments[i];
        if (isNullOrUndefined(source))
            continue;
        source = Object(source);
        descriptors = Object.getOwnPropertyNames(source).reduce(function (descriptors, nextKey) {
            descriptors[nextKey] = Object.getOwnPropertyDescriptor(source, nextKey);
            return descriptors;
        }, {});
        defineProperties(target, descriptors);
    }

    return target;
};

Vector.merge(Vector, {

    ns: {

        svg: svgNS,

        xlink: xlinkNS,

        ev: evNS,

        xhtml: xhtmlNS,

        xml: xmlNS
    },

    /**
     * Creates a SVGElement and returns actual DOM Node, not the wrapper one.
     * @param tagName
     * @returns {SVGElement}
     */
    createElement: function (tagName) {
        return document.createElementNS(Vector.ns.svg, tagName);
    },

    /**
     * Set attribute to SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param value
     * @param namespace
     * @returns {Vector}
     */
    setAttribute: function (svgDomNode, name, value, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        svgDomNode.setAttributeNS(namespace, name, value);
        return this;
    },

    /**
     * Set attributes to SVG DOM Element
     * @param svgDomNode
     * @param attrs
     * @param namespace
     * @return {Vector}
     */
    setAttributes: function (svgDomNode, attrs, namespace) {
        Object.keys(attrs).forEach(function (attr) {
            Vector.setAttribute(svgDomNode, attr, attrs[attr], namespace);
        });
        return this;
    },

    /**
     * Check if the attribute exists in SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {boolean}
     */
    hasAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        return svgDomNode.hasAttributeNS(namespace, name);
    },

    /**
     * Returns attribute value from SVG DOM element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {string}
     */
    getAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        return svgDomNode.getAttributeNS(namespace, name);
    },

    /**
     * Deletes a attribute from SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {Vector}
     */
    removeAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        svgDomNode.removeAttributeNS(namespace, name);
        return this;
    },

    /**
     * Generates RFC4122 version 4 compliant UUID.
     * @returns {string}
     */
    uuid: (function() {
        var table = [],
            i = 0;

        for (; i < 256; i++) {
            table[i] = (i < 16 ? '0' : '') + (i).toString(16);
        }

        return function() {
            var d0 = Math.random() * 0xffffffff | 0,
                d1 = Math.random() * 0xffffffff | 0,
                d2 = Math.random() * 0xffffffff | 0,
                d3 = Math.random() * 0xffffffff | 0;

            return table[d0 & 0xff] + table[d0 >> 8 & 0xff] + table[d0 >> 16 & 0xff] + table[d0 >> 24 & 0xff] + '-' +
                table[d1 & 0xff] + table[d1 >> 8 & 0xff] + '-' + table[d1 >> 16 & 0x0f | 0x40] + table[d1 >> 24 & 0xff] + '-' +
                table[d2 & 0x3f | 0x80] + table[d2 >> 8 & 0xff] + '-' + table[d2 >> 16 & 0xff] + table[d2 >> 24 & 0xff] +
                table[d3 & 0xff] + table[d3 >> 8 & 0xff] + table[d3 >> 16 & 0xff] + table[d3 >> 24 & 0xff];
        };
    })(),

    /**
     * Returns an array of unique values,
     * does not alter main array
     *
     * Time Complexity: O(n)
     *
     * @param arr Array or Array like object
     * @returns {Array}
     */
    unique: function (arr) {
        if (isNullOrUndefined(arr))
            return [];

        var cArr = Object(arr),
            out = [],
            primHashSet = {},
            objSet = [],
            extIndex = [],
            randomProp = Vector.uuid(),
            anyValue = true, //any value without 'undefined'
            ln = Vector.toIntLength(cArr.length),
            val,
            prop,
            i = 0;

        for(; i<ln; ++i) {
            // If i property does not exist in cArr then it will be treated as undefined
            // if we check every tme then running speed will be less.
            val = cArr[i];

            if (isObject(val)) {
                // Check val is extensible or not
                if (Object.isExtensible(val)) {
                    // Make a link in val to get maximum running speed
                    if (val[randomProp] === undefined) {
                        out.push(val);
                        val[randomProp] = anyValue;
                        extIndex.push(out.length - 1);
                    }

                } else {
                    // Otherwise use linear search,
                    // it is very very rare case
                    if (objSet.indexOf(val) === -1) {
                        out.push(val);
                        objSet.push(val);
                    }
                }

            } else {
                if (val === undefined)
                    prop = "U";
                else if (val === null)
                    prop = "I";
                else if (typeof val === "string")
                    prop = "S_" + val;
                else if (typeof val === "number")
                    prop = "N_" + val;
                else if (typeof val === "boolean")
                    prop = "B_" + val;
                else
                    prop = val; // Support for 'symbol' type in ES6

                if (primHashSet[prop] === undefined) {
                    out.push(val);
                    primHashSet[prop] = anyValue;
                }
            }
        }

        // Delete the randomProp from extensible objects
        extIndex.forEach(function (index) {
            delete out[index][randomProp];
        });

        return out;
    },

    /**
     * If svgDOMNode is not SVGElement then it returns null,
     * and if svgDOMNode is already wrapped then previous wrapper will be return
     * otherwise a new wrapper object will be returned.
     *
     * @param svgDOMNode
     * @returns {Vector.Element}
     */
    wrap: function (svgDOMNode) {
        if (!(svgDOMNode instanceof window.SVGElement))
            return null;

        if (Vector.isWrapped(svgDOMNode))
            return svgDOMNode["_wrappingElement"];

        switch (svgDOMNode.constructor) {
            case window.SVGRectElement:
                return new Rect(undefined, undefined, undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGCircleElement:
                return new Circle(undefined, undefined, undefined, svgDOMNode);
            case window.SVGPolylineElement:
                return new Polyline(undefined, svgDOMNode);
            case window.SVGPolygonElement:
                return new Polygon(undefined, svgDOMNode);
            case window.SVGLineElement:
                return new Line(undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGEllipseElement:
                return new Ellipse(undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGPathElement:
                return new Path(undefined, svgDOMNode);
            case window.SVGSVGElement:
                return new SVG(undefined, undefined, undefined, undefined, svgDOMNode);
            default:
                return new Element(svgDOMNode);
        }
    },

    /**
     * Check if a DOM element object is wrapped or not
     * @param svgDOMNode
     * @returns {boolean}
     */
    isWrapped: function (svgDOMNode) {
        if(!isObject(svgDOMNode))
            return false;
        var wrapper = svgDOMNode["_wrappingElement"];
        return wrapper instanceof Element && svgDOMNode instanceof wrapper.constructor.domInterface && wrapper["_domElement"] === svgDOMNode;
    },

    /**
     * It converts any value to Integer length [Positive Integer value]
     * @param value
     * @returns {number}
     */
    toIntLength: function (value) {
        value = Number(value);
        value = isNaN(value) ? 0 : value;
        value = max(0, value);
        value = floor(value);
        return value;
    },

    /**
     * It returns values without holes in array and array-like objects
     * @param arr
     */
    unHoles: function (arr) {
        if (isNullOrUndefined(arr))
            return [];

        arr = Object(arr);
        return reduce.call(arr, function (acc, next) {
            acc.push(next);
            return acc;
        }, []);
    },

    /**
     * It formats pointString as a array of points.
     * every point is a object containing two x and y properties.
     *
     * @param pointsString
     * @returns {Array}
     */
    points: function (pointsString) {
        if (isNullOrUndefined(pointsString))
            return [];
        pointsString = String(pointsString);
        var nums,
            pointsCount,
            coords,
            x,
            y,
            outs = [],
            i = 0;

        pointsString = pointsString.replace(regex.trimStartPointString, "").replace(regex.trimEndPointString, "");
        nums = pointsString.split(regex.pointSeparator);
        pointsCount = floor(nums.length / 2);
        coords = pointsCount * 2;

        for(; i < coords; i+=2) {
            x = nums[i];
            y = nums[i + 1];
            x = +x;
            y = +y;
            if (isFinite(x) && isFinite(y)) {
                outs.push({
                    x: x,
                    y: y
                });
            }
        }

        return outs;
    },

    /**
     * If pointList is formatted like: pointList = [{x: 12, y: 11}, {x: 11, y: 33}, {x: 111, y: 999}]
     * then it will be converted to point string like "12,11 11,33 111,999",
     * otherwise if pointList is primitive(i.e. string) then it is passed to
     * @method Vector.points() and then its output value is interpreted.
     *
     * @param pointList string or array or array-like object
     * @returns {string}
     */
    pointString: function (pointList) {
        if(!isObject(pointList))
            pointList = Vector.points(pointList);
        return reduce.call(pointList, function (outs, point) {
            if (!isObject(point))
                return outs;

            var x = point["x"],
                y = point["y"];

            x = +String(x);
            y = +String(y);

            if (isFinite(x) && isFinite(y)) {
                outs.push(x + "," + y);
                return outs;
            } else
                return outs;

        }, []).join(" ");
    },

    isIEOrEdgeBrowser: isIEOrEdgeBrowser,

    isFirefox: isFirefox,

    distance: function (x1, y1, x2, y2) {
        x1 = +x1;
        y1 = +y1;
        x2 = +x2;
        y2 = +y2;

        if (!isFinite(x1))
            x1 = 0;
        if (!isFinite(y1))
            y1 = 0;
        if (!isFinite(x2))
            x2 = 0;
        if (!isFinite(y2))
            y2 = 0;

        return Math.pow(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2), 0.5);
    },

    svgSupported: isSVGSupported()

});

<commit_msg>Update Vector interface<commit_after>/**
 * It returns a SVGDoc instance and provides a
 * drawing paper to draw on it.
 *
 * @param container its value can be SVGDoc, window.SVGSVGElement, window.HTMLElement or string id.
 * @param width
 * @param height
 * @returns {Vector.SVGDoc}
 */
var Vector = function Vector(container, width, height) {
    width = isNullOrUndefined(width) ? "100%" : width;
    height = isNullOrUndefined(height) ? "100%" : height;
    var temp;

    if (container instanceof SVGDoc)
        return container.size(width, height);
    else if (container instanceof window.SVGSVGElement)
        return new SVGDoc(width, height, container);
    else if (container instanceof window.HTMLElement) {
        temp = new SVGDoc(width, height); // Automatically creates a new svg element
        container.appendChild(temp._domElement);
        return temp;
    } else if(typeof container === "string") {
        return Vector(document.getElementById(container), width, height);
    } else {
        // Otherwise returns a SVGDoc
        // which is not attached to the document DOM tree initially.
        // And it can be attached by calling SVGDoc.container() method
        return new SVGDoc(width, height);
    }
};

/**
 * This method copies all own properties(enumerable and non-enumerable)
 * carefully with descriptors from source objects to target and merges them.
 * It does not make deep copy of properties.
 *
 * @param target object which will be merged by sources
 * @return target object
 */
Vector.merge = function (target) {
    if (isNullOrUndefined(target))
        throw new TypeError("Target object can't be null or undefined");
    target = Object(target);
    var i,
        source,
        descriptors;

    for(i = 1; i<arguments.length; ++i) {
        source = arguments[i];
        if (isNullOrUndefined(source))
            continue;
        source = Object(source);
        descriptors = Object.getOwnPropertyNames(source).reduce(function (descriptors, nextKey) {
            descriptors[nextKey] = Object.getOwnPropertyDescriptor(source, nextKey);
            return descriptors;
        }, {});
        defineProperties(target, descriptors);
    }

    return target;
};

Vector.merge(Vector, {

    ns: {

        svg: svgNS,

        xlink: xlinkNS,

        ev: evNS,

        xhtml: xhtmlNS,

        xml: xmlNS
    },

    /**
     * Creates a SVGElement and returns actual DOM Node, not the wrapper one.
     * @param tagName
     * @returns {SVGElement}
     */
    createElement: function (tagName) {
        return document.createElementNS(Vector.ns.svg, tagName);
    },

    /**
     * Set attribute to SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param value
     * @param namespace
     * @returns {Vector}
     */
    setAttribute: function (svgDomNode, name, value, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        svgDomNode.setAttributeNS(namespace, name, value);
        return this;
    },

    /**
     * Set attributes to SVG DOM Element
     * @param svgDomNode
     * @param attrs
     * @param namespace
     * @return {Vector}
     */
    setAttributes: function (svgDomNode, attrs, namespace) {
        Object.keys(attrs).forEach(function (attr) {
            Vector.setAttribute(svgDomNode, attr, attrs[attr], namespace);
        });
        return this;
    },

    /**
     * Check if the attribute exists in SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {boolean}
     */
    hasAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        return svgDomNode.hasAttributeNS(namespace, name);
    },

    /**
     * Returns attribute value from SVG DOM element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {string}
     */
    getAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        return svgDomNode.getAttributeNS(namespace, name);
    },

    /**
     * Deletes a attribute from SVG DOM Element
     * @param svgDomNode
     * @param name
     * @param namespace
     * @returns {Vector}
     */
    removeAttribute: function (svgDomNode, name, namespace) {
        namespace = isNullOrUndefined(namespace) ? null : namespace;
        svgDomNode.removeAttributeNS(namespace, name);
        return this;
    },

    /**
     * Generates RFC4122 version 4 compliant UUID.
     * @returns {string}
     */
    uuid: (function() {
        var table = [],
            i = 0;

        for (; i < 256; i++) {
            table[i] = (i < 16 ? '0' : '') + (i).toString(16);
        }

        return function() {
            var d0 = Math.random() * 0xffffffff | 0,
                d1 = Math.random() * 0xffffffff | 0,
                d2 = Math.random() * 0xffffffff | 0,
                d3 = Math.random() * 0xffffffff | 0;

            return table[d0 & 0xff] + table[d0 >> 8 & 0xff] + table[d0 >> 16 & 0xff] + table[d0 >> 24 & 0xff] + '-' +
                table[d1 & 0xff] + table[d1 >> 8 & 0xff] + '-' + table[d1 >> 16 & 0x0f | 0x40] + table[d1 >> 24 & 0xff] + '-' +
                table[d2 & 0x3f | 0x80] + table[d2 >> 8 & 0xff] + '-' + table[d2 >> 16 & 0xff] + table[d2 >> 24 & 0xff] +
                table[d3 & 0xff] + table[d3 >> 8 & 0xff] + table[d3 >> 16 & 0xff] + table[d3 >> 24 & 0xff];
        };
    })(),

    /**
     * Returns an array of unique values,
     * does not alter main array
     *
     * Time Complexity: O(n)
     *
     * @param arr Array or Array like object
     * @returns {Array}
     */
    unique: function (arr) {
        if (isNullOrUndefined(arr))
            return [];

        var cArr = Object(arr),
            out = [],
            primHashSet = {},
            objSet = [],
            extIndex = [],
            randomProp = Vector.uuid(),
            anyValue = true, //any value without 'undefined'
            ln = Vector.toIntLength(cArr.length),
            val,
            prop,
            i = 0;

        for(; i<ln; ++i) {
            // If i property does not exist in cArr then it will be treated as undefined
            // if we check every tme then running speed will be less.
            val = cArr[i];

            if (isObject(val)) {
                // Check val is extensible or not
                if (Object.isExtensible(val)) {
                    // Make a link in val to get maximum running speed
                    if (val[randomProp] === undefined) {
                        out.push(val);
                        val[randomProp] = anyValue;
                        extIndex.push(out.length - 1);
                    }

                } else {
                    // Otherwise use linear search,
                    // it is very very rare case
                    if (objSet.indexOf(val) === -1) {
                        out.push(val);
                        objSet.push(val);
                    }
                }

            } else {
                if (val === undefined)
                    prop = "U";
                else if (val === null)
                    prop = "I";
                else if (typeof val === "string")
                    prop = "S_" + val;
                else if (typeof val === "number")
                    prop = "N_" + val;
                else if (typeof val === "boolean")
                    prop = "B_" + val;
                else
                    prop = val; // Support for 'symbol' type in ES6

                if (primHashSet[prop] === undefined) {
                    out.push(val);
                    primHashSet[prop] = anyValue;
                }
            }
        }

        // Delete the randomProp from extensible objects
        extIndex.forEach(function (index) {
            delete out[index][randomProp];
        });

        return out;
    },

    /**
     * If svgDOMNode is not SVGElement then it returns null,
     * and if svgDOMNode is already wrapped then previous wrapper will be return
     * otherwise a new wrapper object will be returned.
     *
     * @param svgDOMNode
     * @returns {Vector.Element}
     */
    wrap: function (svgDOMNode) {
        if (!(svgDOMNode instanceof window.SVGElement))
            return null;

        if (Vector.isWrapped(svgDOMNode))
            return svgDOMNode["_wrappingElement"];

        switch (svgDOMNode.constructor) {
            case window.SVGRectElement:
                return new Rect(undefined, undefined, undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGCircleElement:
                return new Circle(undefined, undefined, undefined, svgDOMNode);
            case window.SVGPolylineElement:
                return new Polyline(undefined, svgDOMNode);
            case window.SVGPolygonElement:
                return new Polygon(undefined, svgDOMNode);
            case window.SVGLineElement:
                return new Line(undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGEllipseElement:
                return new Ellipse(undefined, undefined, undefined, undefined, svgDOMNode);
            case window.SVGPathElement:
                return new Path(undefined, svgDOMNode);
            case window.SVGSVGElement:
                return new SVG(undefined, undefined, undefined, undefined, svgDOMNode);
            default:
                return new Element(svgDOMNode);
        }
    },

    /**
     * Check if a DOM element object is wrapped or not
     * @param svgDOMNode
     * @returns {boolean}
     */
    isWrapped: function (svgDOMNode) {
        if(!isObject(svgDOMNode))
            return false;
        var wrapper = svgDOMNode["_wrappingElement"];
        return wrapper instanceof Element && svgDOMNode instanceof wrapper.constructor.domInterface && wrapper["_domElement"] === svgDOMNode;
    },

    /**
     * It converts any value to Integer length [Positive Integer value]
     * @param value
     * @returns {number}
     */
    toIntLength: function (value) {
        value = Number(value);
        value = isNaN(value) ? 0 : value;
        value = max(0, value);
        value = floor(value);
        return value;
    },

    /**
     * It returns values without holes in array and array-like objects
     * @param arr
     */
    unHoles: function (arr) {
        if (isNullOrUndefined(arr))
            return [];

        arr = Object(arr);
        return reduce.call(arr, function (acc, next) {
            acc.push(next);
            return acc;
        }, []);
    },

    /**
     * It formats pointString as a array of points.
     * every point is a object containing two x and y properties.
     *
     * @param pointsString
     * @returns {Array}
     */
    points: function (pointsString) {
        if (isNullOrUndefined(pointsString))
            return [];
        pointsString = String(pointsString);
        var nums,
            pointsCount,
            coords,
            x,
            y,
            outs = [],
            i = 0;

        pointsString = pointsString.replace(regex.trimStartPointString, "").replace(regex.trimEndPointString, "");
        nums = pointsString.split(regex.pointSeparator);
        pointsCount = floor(nums.length / 2);
        coords = pointsCount * 2;

        for(; i < coords; i+=2) {
            x = nums[i];
            y = nums[i + 1];
            x = +x;
            y = +y;
            if (isFinite(x) && isFinite(y)) {
                outs.push({
                    x: x,
                    y: y
                });
            }
        }

        return outs;
    },

    /**
     * If pointList is formatted like: pointList = [{x: 12, y: 11}, {x: 11, y: 33}, {x: 111, y: 999}]
     * then it will be converted to point string like "12,11 11,33 111,999",
     * otherwise if pointList is primitive(i.e. string) then it is passed to
     * @method Vector.points() and then its output value is interpreted.
     *
     * @param pointList string or array or array-like object
     * @returns {string}
     */
    pointString: function (pointList) {
        if(!isObject(pointList))
            pointList = Vector.points(pointList);
        return reduce.call(pointList, function (outs, point) {
            if (!isObject(point))
                return outs;

            var x = point["x"],
                y = point["y"];

            x = +String(x);
            y = +String(y);

            if (isFinite(x) && isFinite(y)) {
                outs.push(x + "," + y);
                return outs;
            } else
                return outs;

        }, []).join(" ");
    },

    isIEOrEdgeBrowser: isIEOrEdgeBrowser,

    isFirefox: isFirefox,

    distance: function (x1, y1, x2, y2) {
        x1 = +x1;
        y1 = +y1;
        x2 = +x2;
        y2 = +y2;

        if (!isFinite(x1))
            x1 = 0;
        if (!isFinite(y1))
            y1 = 0;
        if (!isFinite(x2))
            x2 = 0;
        if (!isFinite(y2))
            y2 = 0;

        return Math.pow(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2), 0.5);
    },

    svgSupported: isSVGSupported()

});

<|endoftext|>


<commit_before>export default function Vector(x, y, immutable) {
    this.immutable = immutable || false;
    this.x = x || 0;
    this.y = y || 0;
}

Vector.prototype = {
    clone: function () {
        return new Vector(this.x, this.y);
    },

    set: function (value) {
        vectorArg(value, (x, y) => {
            this.x = x;
            this.y = y;
        });
        return this;
    },

    add: function (value) {
        vectorArg(value, (x, y) => {
            this.x += x;
            this.y += y;
        });
        return this;
    },

    subtract: function (value) {
        vectorArg(value, (x, y) => {
            this.x -= x;
            this.y -= y;
        });
        return this;
    },

    multiply: function (value) {
        vectorArg(value, (x, y) => {
            this.x *= x;
            this.y *= y;
        });
        return this;
    },

    divide: function (value) {
        vectorArg(value, (x, y) => {
            if (x !== 0) this.x /= x;
            if (y !== 0) this.y /= y;
        });
        return this;
    },

    inverse: function () {
        this.x *= -1;
        this.y *= -1;
        return this;
    },

    normalize: function (value = 1) {
        return this.multiply(value / this.length);
    },

    dot: function (vector) {
        return this.x * vector.x + this.y * vector.y;
    },

    cross: function (vector) {
        return this.x * vector.y - this.y * vector.x
    },

    distance: function (vector) {
        const x = this.x - vector.x;
        const y = this.y - vector.y;
        return Math.sqrt((x * x) + (y * y));
    },

    equals: function (vector) {
        return this.x === vector.x && this.y === vector.y;
    },

    magnitude: function (value) {
        this.length = value;
        return this;
    },

    limit: function (value) {
        if (this.length > value) {
            this.normalize(value);
        }
        return this;
    },

    radiansTo: function (vector) {
        const length = this.length * vector.length;
        return length === 0 ? NaN : Math.acos(this.dot(vector) / length);
    },

    angleTo: function (vector) {
        return this.radiansTo(vector) * 180 / Math.PI;
    },

    rotate: function (angle) {
        if (angle !== 0) {
            const rad = (this.angle + angle) * Math.PI / 180;
            this.x = Math.cos(rad) * this.length;
            this.y = Math.sin(rad) * this.length;
        }
        return this;
    },

    get length() {
        return Math.sqrt(this.dot(this));
    },

    set length(value) {
        this.normalize().multiply(value);
    },

    get radians() {
        return Math.atan2(this.y, this.x);
    },

    set radians(value) {
        this.set(
            Math.cos(value) * this.length,
            Math.sin(value) * this.length
        );
    },

    get angle() {
        return this.radians * 180 / Math.PI;
    },

    set angle(value) {
        this.radians(value * Math.PI / 180);
    },
}

Vector.random = function (a = 1, b = 1) {
    return new Vector(a * Math.random(), b * Math.random());
}

function vectorArg(value, callback) {
    if (value instanceof Vector) {
        callback(value.x, value.y);
    } else if (typeof value === 'number') {
        callback(value, value);
    }
}

function isZero(vector) {
    return vector.x === 0 && vector.y === 0;
}<commit_msg>sort methods into groups, vectorArg handles multiple arguments<commit_after>export default function Vector(x, y, immutable) {
    this.immutable = immutable || false;
    this.x = x || 0;
    this.y = y || 0;
}

Vector.prototype = {
    /**
     *  Utility
     */
    clone: function () {
        return new Vector(this.x, this.y);
    },

    equals: function (vector) {
        return this.x === vector.x && this.y === vector.y;
    },

    /**
     *  Properties
     */
    get length() {
        return Math.sqrt(this.dot(this));
    },

    set length(value) {
        this.normalize().multiply(value);
    },

    get angle() {
        return this.radians * 180 / Math.PI;
    },

    set angle(value) {
        this.radians(value * Math.PI / 180);
    },

    get radians() {
        return Math.atan2(this.y, this.x);
    },

    set radians(value) {
        this.set(
            Math.cos(value) * this.length,
            Math.sin(value) * this.length
        );
    },

    /**
     *  Manipulation
     */
    set: function () {
        vectorArg(arguments, (x, y) => {
            this.x = x;
            this.y = y;
        });
        return this;
    },

    add: function (value) {
        vectorArg(value, (x, y) => {
            this.x += x;
            this.y += y;
        });
        return this;
    },

    subtract: function (value) {
        vectorArg(value, (x, y) => {
            this.x -= x;
            this.y -= y;
        });
        return this;
    },

    multiply: function (value) {
        vectorArg(value, (x, y) => {
            this.x *= x;
            this.y *= y;
        });
        return this;
    },

    divide: function (value) {
        vectorArg(value, (x, y) => {
            if (x !== 0) this.x /= x;
            if (y !== 0) this.y /= y;
        });
        return this;
    },

    negate: function () {
        this.x *= -1;
        this.y *= -1;
        return this;
    },

    normalize: function (value = 1) {
        this.multiply(value / this.length);
        return this;
    },

    limit: function (value) {
        if (this.length > value) {
            this.normalize(value);
        }
        return this;
    },

    magnitude: function (value) {
        this.length = value;
        return this;
    },

    rotate: function (angle) {
        if (angle) {
            const rad = (this.angle + angle) * Math.PI / 180;
            const length = this.length;
            this.x = Math.cos(rad) * length;
            this.y = Math.sin(rad) * length;
        }
        return this;
    },

    /**
     *  Products
     */
    dot: function (vector) {
        return this.x * vector.x + this.y * vector.y;
    },

    cross: function (vector) {
        return this.x * vector.y - this.y * vector.x
    },

    distance: function (vector) {
        const x = this.x - vector.x;
        const y = this.y - vector.y;
        return Math.sqrt((x * x) + (y * y));
    },

    angleTo: function (vector) {
        return this.radiansTo(vector) * 180 / Math.PI;
    },

    radiansTo: function (vector) {
        const length = this.length * vector.length;
        return length === 0 ? NaN : Math.acos(this.dot(vector) / length);
    }
}

Vector.random = function (a = 1, b = 1) {
    return new Vector(a * Math.random(), b * Math.random());
}

function vectorArg(value, callback) {
    if (typeof value === 'number') {
        callback(value, value);

    } else if (value instanceof Vector) {
        callback(value.x, value.y);

    } else if (isArguments(value)) {
        const x = value[0];
        if (x !== undefined) {
            const y = value[1] !== undefined
                ? value[1]
                : value[0];
            callback(x, y);
        }
    }
}

function isZero(vector) {
    return vector.x === 0 && vector.y === 0;
}

function isArguments(value) {
    return value && value.hasOwnProperty('callee') || false;
}<|endoftext|>


<commit_before>// @flow
/**
 * Utils: provides helper functions for other modules.
 *
 * @module internal/utils
 */

export const BETWEEN_PAREN_REGEX: RegExp = /\(([^)]+)\)/;
export const ALL_COMMAS_REGEX: RegExp = /, /g;
export const NUMBER_REGEX: RegExp = /(-)?\d+(\.\d+)?/;
export const NON_NUMER_REGEX: RegExp = /\D+/;

export const noop = (): void => {};

export const isStatelessComponent = (Component: Object): boolean => !Component.prototype.render;

export const isReactComponent = (MaybeComponent: Object): boolean => (
  MaybeComponent != null
  && (
    typeof MaybeComponent === 'function'
    || !!(MaybeComponent.prototype && MaybeComponent.prototype.render)
  )
);

export const makeError = (...messages: string[]): Error => {
  const err = new Error(messages.reduce((result, segment) => (
    result === '' ?
      segment
    : result.charAt(result.length - 1) === '\n' ?
      `${ result }${ segment }`
    : segment === '\n' ?
      `${ result }${ segment }`
    :
      `${ result } ${ segment }`
  ), ''));
  const actualStack = err.stack.split('\n');
  const poppedStack = actualStack
    .slice(0, messages.length)
    .concat(
      actualStack.slice(messages.length + 1)
    );
  err.stack = poppedStack.join('\n');
  return err;
}

export const flatten = <T>(arrays: Array<Array<T>>): Array<T> =>
  arrays.reduce((flat, arr) => flat.concat(arr), []);

export const stringify = (obj: Object): string => JSON.stringify(obj, null, 2);

export const isUsingTime = (animation: Object): boolean =>
  animation.duration != null;

export const isUsingSpring = (animation: Object): boolean =>
  animation.stiffness != null && animation.damping != null;

<commit_msg>:sparkles: [utils] Added getDisplayName helper fn<commit_after>// @flow
/**
 * Utils: provides helper functions for other modules.
 *
 * @module internal/utils
 */

export const BETWEEN_PAREN_REGEX: RegExp = /\(([^)]+)\)/;
export const ALL_COMMAS_REGEX: RegExp = /, /g;
export const NUMBER_REGEX: RegExp = /(-)?\d+(\.\d+)?/;
export const NON_NUMER_REGEX: RegExp = /\D+/;

export const noop = (): void => {};

export const isStatelessComponent = (Component: Object): boolean => !Component.prototype.render;

export const isReactComponent = (MaybeComponent: Object): boolean => (
  MaybeComponent != null
  && (
    typeof MaybeComponent === 'function'
    || !!(MaybeComponent.prototype && MaybeComponent.prototype.render)
  )
);

export const makeError = (...messages: string[]): Error => {
  const err = new Error(messages.reduce((result, segment) => (
    result === '' ?
      segment
    : result.charAt(result.length - 1) === '\n' ?
      `${ result }${ segment }`
    : segment === '\n' ?
      `${ result }${ segment }`
    :
      `${ result } ${ segment }`
  ), ''));
  const actualStack = err.stack.split('\n');
  const poppedStack = actualStack
    .slice(0, messages.length)
    .concat(
      actualStack.slice(messages.length + 1)
    );
  err.stack = poppedStack.join('\n');
  return err;
}

export const flatten = <T>(arrays: Array<Array<T>>): Array<T> =>
  arrays.reduce((flat, arr) => flat.concat(arr), []);

export const stringify = (obj: Object): string => JSON.stringify(obj, null, 2);

export const isUsingTime = (animation: Object): boolean =>
  animation.duration != null;

export const isUsingSpring = (animation: Object): boolean =>
  animation.stiffness != null && animation.damping != null;

export const getDisplayName = (Component: Object|string): string =>
  typeof Component === 'string' ?
    Component
  : !Component ?
    'undefined'
  :
    Component.displayName || Component.name || 'Unknown'
<|endoftext|>


<commit_before>/**
 * @class InvertedIndex
 */
class InvertedIndex {
/**
 * @constructor
 */
  constructor() {
    this.mappedIndex = {};
    this.content = '';
  }

  /**
   *
   * @param {string}filename of file
   * @param {Object}fileContent content of file to be indexed
   * @return {Object}indexed filename and index(indicies)
   */
  createIndex(fileContent, filename) {
    this.content = fileContent;
    const mappedIndex = {};
    fileContent.forEach((file) => {
      const mergeFile = file.text;
      const removeSymbols = mergeFile.replace(/[-.,;:#*!@%&+={}?|_~\\()]/g, ' ');
      const changeToLowerCase = removeSymbols.toLowerCase();
      const singleWord = changeToLowerCase.split(' ');

      singleWord.forEach((word) => {
        if (word in mappedIndex){
          mappedIndex[word] = Array.from(new Set(mappedIndex[word].concat([fileContent.indexOf(file)])));
        }
       if (!(word in mappedIndex)) {
          mappedIndex[word] = [fileContent.indexOf(file)];

     } else if (!(singleWord.indexOf(file))) {
       mappedIndex[word] = fileContent.indexOf(file, mappedIndex[word] + 1)

             }
      });
    });
    this.mappedIndex = mappedIndex;
    return mappedIndex;
  }
}<commit_msg>Refactor class methods<commit_after>/**
 * @class InvertedIndex
 */
class InvertedIndex {
/**
 * @constructor
 */
  constructor() {
    this.mappedIndex = {};
    this.content = '';
    this.documents = {};
  }
/**
 * @return {Error | boolean} returns true or an error message
 * @param {any } file any file
 */
  isJson(file) {
    this.content = file;
    const stringFile = JSON.stringify(file);
    const isArray = JSON.parse(stringFile);
    if (isArray.length > 0 && isArray.some(arrayObject => arrayObject.title === undefined && arrayObject.text === undefined)) {
      throw new Error('malformed file');
    } else {
      return true;
    }
  }

 /**
   *@description {takes in a file and returns an object containing each word and index(ces)}
   * @param {String }fileName of file
   * @param {Object}fileContent content of file to be indexed
   * @return {Object}indexed filename and index(indicies)
   */
  createIndex(fileName, fileContent) {
    if (this.isJson(fileName) === true && fileName.length > 0) {
      fileContent = fileName;
      this.content = fileContent;
      const mappedIndex = {};
      fileContent.forEach((file) => {
        const doc = file.text;
        const removeSymbols = doc.replace(/[-.,;:#*!@%&+={}?|_~\\()]/g, ' ');
        const changeToLowerCase = removeSymbols.toLowerCase();
        const singleWord = changeToLowerCase.split(' ');

        singleWord.forEach((word) => {
          if (word in mappedIndex) {
            const joinedIndex = (mappedIndex[word].concat([fileContent.indexOf(file)]));
            mappedIndex[word] = Array.from(new Set(joinedIndex));
          }
          if (!(word in mappedIndex)) {
            mappedIndex[word] = [fileContent.indexOf(file)];
          } else if (!(singleWord.indexOf(file))) {
            mappedIndex[word] = fileContent.indexOf(file, mappedIndex[word] + 1);
          }
        });
      });
      this.documents = mappedIndex;
      return mappedIndex;
    } else {
      this.documents = null;
      return 'Invalid file or Empty file';
    }
  }
  /**
   *@returns { object} created index
   * @param {String } filename
   */
  getIndex(filename) {
    return this.documents[filename];
  }

  /**
   * @return {Object}returns an Object containing search results
   * @param {Objects } index
   * @param {String } filename
   * @param {Array } terms
   */
  searchIndex(index, filename, ...terms) {
    index = this.documents;
    const words = terms.toString().split(',');
    const searchWords = {};
    const results = {};
    if (words.length > 1) {
      words.forEach((word) => {
        if (word in index) {
          results[word] = index[word];
          searchWords[filename] = results;
        } else {
          results[word] = ['Term not found'];
          searchWords[filename] = results;
        }
      });
    } else if (words in index) {
      results[words] = index[words];
      searchWords[filename] = results;
    } else {
      results[words] = ['Term not found'];
      searchWords[filename] = results;
    }
    return searchWords;
  }
  /**
   * @return {boolean } returns true or false values.
   * @param {Object } file
   */
}
exports.InvertedIndex = InvertedIndex;
<|endoftext|>


<commit_before>class Index {
	/**
	 * {
	 * 	file: {
	 * 		name: 'name of file',
	 * 		books: <all books for this file>,
	 * 		index: <index based on the books in this file>
	 * 	},
	 * 	allBooks: [],
	 * 	index: {}
	 * }
	 */

  constructor() {
    this.files = {};
  }

	/** Create Index
	 * Creates an index from file(s) uploaded
	 *
	 * @param {String} filename
	 * @param {Object} files
	 */
  static removePunctuation(data) {
    return data.replace(new RegExp("\\s+|[`~!@#$%^&*()_|+-=?;:'\",\\.<>{}\\[\\]\\/\\\\]", 'g'), " ").toLowerCase();
  }
  static deleteDuplicate(file) {
    let bookString = "";
    let books = file.allBooks ? file.allBooks : file.books;
    for (let i = 0; i < books.length; i++) {
      bookString += ` ${books[i].text}`;
    }
    bookString = Index.removePunctuation(bookString).split(" ");

    return bookString.filter((item, index, arr) => arr.indexOf(item) === index
			// test to check for duplicate. If Index of current object is equals to index
  );
  }

  createIndex(filename, files) {
    let file = filename ? this.files[filename] : files;
    let books = filename ? this.files[filename].books : files.allBooks;
    const indexObject = {};
    let wordList = Index.deleteDuplicate(file).sort().join(' ').toLowerCase()
		.split(' ');
    wordList.shift();
    for (let i in wordList) {
      for (let j = 0; j < books.length; j++) {
        let re = new RegExp(`\\b${wordList[i]}\\b`, 'i');
        if (re.test(books[j].text)) {
          if (indexObject[wordList[i]]) {
            indexObject[wordList[i]].push(j);
          } else {
            indexObject[wordList[i]] = [j];
          }
        }
      }
    }
    file['index'] = indexObject;
  }

	/** Search Index
	 *
	 *  takes a string and returns an object with the book position of the string
	 *
	 * @param {String} terms
	 * @param {String} filepath
	 * @returns {Object}
	 *
	 *
	 */

  searchIndex(terms, filepath) {
    let termsArr = Index.removePunctuation(terms).split(' ');
    let subResult = {};
    if (filepath) {
      for (let index in termsArr) {
        if (this.files[filepath].index[termsArr[index]]) {
          subResult[termsArr[index]] = this.files[filepath].index[termsArr[index]];
        }
        else {
          subResult[termsArr[index]] = [];
        }
      }
      return subResult;
    }
    return this.searchAll(terms, this.files);
  }

	/**
	 * Search all
	 *
	 * If particular file is not selected, it returns search results from all
	 * files
	 *
	 * @param {String} terms
	 * @returns {Object}
	 *
	 *
	 */

  searchAll(terms) {
    let termsArr = Index.removePunctuation(terms).split(" ");
    !this.files.index ? this.collateBooks() : null;
    let allIndex = this.files.index;
    let subResult = {};
    for (let index in termsArr) {
      for (let indexedWord in allIndex) {
        if (termsArr[index] === indexedWord) {
          subResult[termsArr[index]] = allIndex[indexedWord];
        }
      }
      if (!subResult[termsArr[index]]) {
        subResult[termsArr[index]] = [];
      }
    }
    return subResult;
  }

	/** Get index
	 *
	 * takes filename and returns index for that particular file
	 *
	 * @param {String} filename
	 * @returns {Object}
	 *
	 *
	 */
  getIndex(filename) {
    filename = filename || false;
    if (filename) {
      return this.files[filename].index;
    }
  }

/** Get all Books
	 *
	 * sets key with value containing all the books
	 * @returns {Object}
	 *
	 *
	 */
  getAllBooks() {
    let booksall = [];
    for (let filename in this.files) {
      if (filename !== 'allBooks' && filename !== 'index') {
        booksall = booksall.concat(this.files[filename].books);
      }
    }
    this.files['allBooks'] = booksall;
  }

  collateBooks() {
    this.getAllBooks();
    this.createIndex(null, this.files);
  }

/** Get all Index
	 *
	 * returns index of all books in all files
	 * @returns {Object}
	 *
	 *
	 */
  getAllIndex() {
    return this.files.index;
  }

/** Get all Index
	 *
	 * Checks if contents of file uploaded is in the correct JSON
	 * format with a title and text property returns true or false
	 *
	 * @param uploadedFile
	 * @returns {bool}
	 *
	 *
	 */
  isValidJSON(uploadedFile) {
    let isValid = true;
    let file;
    try {
      file = JSON.parse(uploadedFile);
      if (Array.isArray(file)) {
        file.forEach((item) => {
          if (!item.hasOwnProperty('title') || !item.hasOwnProperty('text')) {
            isValid = false;
          }
        });
      } else if (!file.hasOwnProperty('title') || !file.hasOwnProperty('text')) {
        isValid = false;
      }
    } catch (error) {
      throw new Error("JSON file invalid");
    }
    return isValid ? file : isValid;
  }
}

module.exports = Index;
<commit_msg>update inverted-index file<commit_after>class Index {
	/**
	 * {
	 * 	file: {
	 * 		name: 'name of file',
	 * 		books: <all books for this file>,
	 * 		index: <index based on the books in this file>
	 * 	},
	 * 	allBooks: [],
	 * 	index: {}
	 * }
	 */

  constructor() {
    this.files = {};
  }

	/** Remove Punctuation
	 * removes punctuation from string and converts to lowercase
	 *
	 * @param {String} data
	 * @returns {String}
	 */

  static removePunctuation(data) {
    return data.replace(new RegExp("\\s+|[`~!@#$%^&*()_|+-=?;:'\",\\.<>{}\\[\\]\\/\\\\]", 'g'), " ").toLowerCase();
  }

	/** Delete Dupicate
	 * joins all the strings in all books in a particular file,
	 * splits into array and removes duplicates
	 *
	 * @param {Object} file
	 * @returns {Array} bookString
	 */

  static deleteDuplicate(file) {
    let bookString = "";
    let books = file.allBooks ? file.allBooks : file.books;
    for (let i = 0; i < books.length; i++) {
      bookString += ` ${books[i].text}`;
    }
    bookString = Index.removePunctuation(bookString).split(" ");

    return bookString.filter((item, index, arr) => arr.indexOf(item) === index
			// test to check for duplicate. If Index of current object is equals to index
  );
  }

	/** Create Index
	 * Creates an index from file(s) uploaded
	 *
	 * @param {String} filename
	 * @param {Object} files
	 */

  createIndex(filename, files) {
    let file = filename ? this.files[filename] : files;
    let books = filename ? this.files[filename].books : files.allBooks;
    const indexObject = {};
    let wordList = Index.deleteDuplicate(file).sort().join(' ').toLowerCase()
		.split(' ');
    wordList.shift();
    for (let i in wordList) {
      for (let j = 0; j < books.length; j++) {
        let re = new RegExp(`\\b${wordList[i]}\\b`, 'i');
        if (re.test(books[j].text)) {
          if (indexObject[wordList[i]]) {
            indexObject[wordList[i]].push(j);
          } else {
            indexObject[wordList[i]] = [j];
          }
        }
      }
    }
    file['index'] = indexObject;
  }

	/** Search Index
	 *
	 *  takes a string and returns an object with the book position of the string
	 *
	 * @param {String} terms
	 * @param {String} filepath
	 * @returns {Object}
	 *
	 *
	 */

  searchIndex(terms, filepath) {
    let termsArr = Index.removePunctuation(terms).split(' ');
    let subResult = {};
    if (filepath) {
      for (let index in termsArr) {
        if (this.files[filepath].index[termsArr[index]]) {
          subResult[termsArr[index]] = this.files[filepath].index[termsArr[index]];
        }
        else {
          subResult[termsArr[index]] = [];
        }
      }
      return subResult;
    }
    return this.searchAll(terms, this.files);
  }

	/**
	 * Search all
	 *
	 * If particular file is not selected, it returns search results from all
	 * files
	 *
	 * @param {String} terms
	 * @returns {Object}
	 *
	 *
	 */

  searchAll(terms) {
    let termsArr = Index.removePunctuation(terms).split(" ");
    !this.files.index ? this.collateBooks() : null;
    let allIndex = this.files.index;
    let subResult = {};
    for (let index in termsArr) {
      for (let indexedWord in allIndex) {
        if (termsArr[index] === indexedWord) {
          subResult[termsArr[index]] = allIndex[indexedWord];
        }
      }
      if (!subResult[termsArr[index]]) {
        subResult[termsArr[index]] = [];
      }
    }
    return subResult;
  }

	/** Get index
	 *
	 * takes filename and returns index for that particular file
	 *
	 * @param {String} filename
	 * @returns {Object}
	 *
	 *
	 */
  getIndex(filename) {
    filename = filename || false;
    if (filename) {
      return this.files[filename].index;
    }
  }

/** Get all Books
	 *
	 * sets key with value containing all the books
	 * @returns {Object}
	 *
	 *
	 */
  getAllBooks() {
    let booksall = [];
    for (let filename in this.files) {
      if (filename !== 'allBooks' && filename !== 'index') {
        booksall = booksall.concat(this.files[filename].books);
      }
    }
    this.files['allBooks'] = booksall;
  }

  collateBooks() {
    this.getAllBooks();
    this.createIndex(null, this.files);
  }

/** Get all Index
	 *
	 * returns index of all books in all files
	 * @returns {Object}
	 *
	 *
	 */
  getAllIndex() {
    return this.files.index;
  }

/** Get all Index
	 *
	 * Checks if contents of file uploaded is in the correct JSON
	 * format with a title and text property returns true or false
	 *
	 * @param uploadedFile
	 * @returns {bool}
	 *
	 *
	 */
  isValidJSON(uploadedFile) {
    let isValid = true;
    let file;
    try {
      file = JSON.parse(uploadedFile);
      if (Array.isArray(file)) {
        file.forEach((item) => {
          if (!item.hasOwnProperty('title') || !item.hasOwnProperty('text')) {
            isValid = false;
          }
        });
      } else if (!file.hasOwnProperty('title') || !file.hasOwnProperty('text')) {
        isValid = false;
      }
    } catch (error) {
      throw new Error("JSON file invalid");
    }
    return isValid ? file : isValid;
  }
}

module.exports = Index;
<|endoftext|>


<commit_before>var books = [{
    "title": "Alice in Wonderland",
    "text": "Alice falls into elf a rabbit, hole. and enters a world full of imagination.",
  },

  {
    "title": "The Lord of the Rings: The Fellowship of the Ring.",
    "text": "An alice unusual alliance of man, elf, dwarf, wizard and hobbit seek to destroy a powerful ring. imagination"
  }
];
var bookTwo = [{
    "title": "Alice in Wonderland",
    "text": "and enters a world full of imagination.",
  },

  {
    "title": "Hunger games The Fellowship of the Ring.",
    "text": "A game of life an death"
  }
];
class invertedIndex {

  constructor() {
    this.indices = [];
  }

  tokenizer(string) {
    return string.replace(/[^a-z\d\s]/ig, '')
      .toLowerCase()
      .split(/\s+/);
  }

  createIndex(fileName, content) {
    const result = {};
    let doc = 1;
    content.forEach(book => {
      for (let key in book) {
        // console.log(this.tokenizer(book[key]))
        this.tokenizer(book[key]).forEach(word => {
          if (!result.hasOwnProperty(word)) {
            result[word] = [];
          }
          if (result[word].indexOf(doc) > -1) {
            return;
          }
          result[word].push(doc);
        });
      }
      doc++;
    });
    this.indices[fileName] = result;
  }

  getIndex(fileName) {
    return this.indices[fileName];
  }

  searchIndex(fileName, query) {
    const result = {};
    const indexedFile = this.getIndex(fileName);
    if (!query || !indexedFile) {
      return "file dose not exist";
    }
    this.tokenizer(query).forEach(word => {
      if (indexedFile.hasOwnProperty(word)) {
        result[word] = indexedFile[word];
      }
    });
    return result;
  }
}
const indexer = new invertedIndex();
indexer.createIndex('book', books);
indexer.createIndex('bookTwo', bookTwo);
indexer.getIndex('book', books);
console.log(indexer.searchIndex('book', "Alice"));
<commit_msg>update the file for export<commit_after>const books = require('./books.js').books;

class InvertedIndex {

  constructor() {
    this.indices = [];
  }

  tokenizer(string) {
    return string.replace(/[^a-z\d\s]/ig, '')
      .toLowerCase()
      .split(/\s+/);
  }

  createIndex(filePath) {
    let content = books;
    const result = {};
    let doc = 1;
    content.forEach(book => {
      for (let key in book) {
        // console.log(this.tokenizer(book[key]))
        this.tokenizer(book[key]).forEach(word => {
          if (!result.hasOwnProperty(word)) {
            result[word] = [];
          }
          if (result[word].indexOf(doc) > -1) {
            return;
          }
          result[word].push(doc);
        });
      }
      doc++;

    });
    this.indices[filePath] = result;
  }

  getIndex(filePath) {
    return this.indices[filePath];
  }

  searchIndex(filePath, query) {
    const result = {};
    const indexedFile = this.getIndex(filePath);
    if (!query || !indexedFile) {
      return "file dose not exist";
    }
    this.tokenizer(query).forEach(word => {
      if (indexedFile.hasOwnProperty(word)) {
        result[word] = indexedFile[word];
      }
    });
    return result;
  }
}

module.exports = new InvertedIndex();
<|endoftext|>


<commit_before>var path = require('path'),
    glob = require('glob'),
    log = require('./logger'),
    helper = require('./util');

// Coffee is required here to enable config files written in coffee-script.
try {
  require('coffee-script').register();
} catch (e) {
  // Intentionally blank - ignore if coffee-script is not available.
}

// LiveScript is required here to enable config files written in LiveScript.
try {
  require('LiveScript');
} catch (e) {
  // Intentionally blank - ignore if LiveScript is not available.
}

var ConfigParser = function() {
  // Default configuration.
  this.config_ = {
    specs: [],
    multiCapabilities: [],
    rootElement: 'body',
    allScriptsTimeout: 11000,
    getPageTimeout: 10000,
    params: {},
    framework: 'jasmine',
    jasmineNodeOpts: {
      showColors: true,
      stackFilter: helper.filterStackTrace,
      defaultTimeoutInterval: (30 * 1000)
    },
    seleniumArgs: [],
    mochaOpts: {
      ui: 'bdd',
      reporter: 'list'
    },
    chromeDriver: null,
    configDir: './',
    plugins: [],
    skipSourceMapSupport: false
  };
};

/**
 * Merge config objects together.
 *
 * @private
 * @param {Object} into
 * @param {Object} from
 *
 * @return {Object} The 'into' config.
 */
var merge_ = function(into, from) {
  for (var key in from) {
    if (into[key] instanceof Object &&
        !(into[key] instanceof Array) &&
        !(into[key] instanceof Function)) {
      merge_(into[key], from[key]);
    } else {
      into[key] = from[key];
    }
  }
  return into;
};

/**
 * Returns the item if it's an array or puts the item in an array
 * if it was not one already.
 */
var makeArray = function(item) {
  return Array.isArray(item) ? item : [item];
};

/**
 * Adds to an array all the elements in another array without adding any
 * duplicates
 *
 * @param {Array<string>} dest The array to add to
 * @param {Array<string>} src The array to copy from
 */
var union = function(dest, src) {
  var elems = {};
  for (var key in dest) {
    elems[key] = true;
  }
  for (key in src) {
    if (!elems[key]) {
      dest.push(key);
      elems[key] = true;
    }
  }
};

/**
 * Resolve a list of file patterns into a list of individual file paths.
 *
 * @param {Array.<string> | string} patterns
 * @param {=boolean} opt_omitWarnings Whether to omit did not match warnings
 * @param {=string} opt_relativeTo Path to resolve patterns against
 *
 * @return {Array} The resolved file paths.
 */
ConfigParser.resolveFilePatterns =
    function(patterns, opt_omitWarnings, opt_relativeTo) {
  var resolvedFiles = [];
  var cwd = opt_relativeTo || process.cwd();

  patterns = (typeof patterns === 'string') ?
      [patterns] : patterns;

  if (patterns) {
    for (var i = 0; i < patterns.length; ++i) {
      var fileName = patterns[i];
      var matches = glob.sync(fileName, {cwd: cwd});

      if (!matches.length && !opt_omitWarnings) {
        log.warn('pattern ' + patterns[i] + ' did not match any files.');
      }
      for (var j = 0; j < matches.length; ++j) {
        var resolvedPath = path.resolve(cwd, matches[j]);
        resolvedFiles.push(resolvedPath);
      }
    }
  }
  return resolvedFiles;
};

/**
 * Returns only the specs that should run currently based on `config.suite`
 *
 * @return {Array} An array of globs locating the spec files
 */
ConfigParser.getSpecs = function(config) {
  var specs = [];
  if (config.suite) {
    config.suite.split(',').forEach(function(suite) {
      var suiteList = config.suites[suite];
      if (suiteList == null) {
        throw new Error('Unknown test suite: ' + suite);
      }
      union(specs, makeArray(suiteList));
    });
    return specs;
  }

  if (config.specs.length > 0) {
    return config.specs;
  }

  config.suites.forEach(function(suite) {
    union(specs, makeArray(suite));
  });
  return specs;
};

/**
 * Add the options in the parameter config to this runner instance.
 *
 * @private
 * @param {Object} additionalConfig
 * @param {string} relativeTo the file path to resolve paths against
 */
ConfigParser.prototype.addConfig_ = function(additionalConfig, relativeTo) {
  // All filepaths should be kept relative to the current config location.
  // This will not affect absolute paths.
  ['seleniumServerJar', 'chromeDriver', 'onPrepare', 'firefoxPath',
   'frameworkPath'].
      forEach(function(name) {
        if (additionalConfig[name] &&
            typeof additionalConfig[name] === 'string') {
          additionalConfig[name] =
              path.resolve(relativeTo, additionalConfig[name]);
        }
  });

  merge_(this.config_, additionalConfig);
};

/**
 * Public function specialized towards merging in a file's config
 *
 * @public
 * @param {String} filename
 */
ConfigParser.prototype.addFileConfig = function(filename) {
  try {
    if (!filename) {
      return this;
    }
    var filePath = path.resolve(process.cwd(), filename);
    var fileConfig = require(filePath).config;
    if (!fileConfig) {
      log.error('configuration file ' + filename + ' did not export a config ' +
          'object');
    }
    fileConfig.configDir = path.dirname(filePath);
    this.addConfig_(fileConfig, fileConfig.configDir);
  } catch (e) {
    log.error('failed loading configuration file ' + filename);
    throw e;
  }
  return this;
};

/**
 * Public function specialized towards merging in config from argv
 *
 * @public
 * @param {Object} argv
 */
ConfigParser.prototype.addConfig = function(argv) {
  this.addConfig_(argv, process.cwd());
  return this;
};

/**
 * Public getter for the final, computed config object
 *
 * @public
 * @return {Object} config
 */
ConfigParser.prototype.getConfig = function() {
  return this.config_;
};

module.exports = ConfigParser;
<commit_msg>fix(config parser): lodash removal had mistakes which broke the suite feature<commit_after>var path = require('path'),
    glob = require('glob'),
    log = require('./logger'),
    helper = require('./util');

// Coffee is required here to enable config files written in coffee-script.
try {
  require('coffee-script').register();
} catch (e) {
  // Intentionally blank - ignore if coffee-script is not available.
}

// LiveScript is required here to enable config files written in LiveScript.
try {
  require('LiveScript');
} catch (e) {
  // Intentionally blank - ignore if LiveScript is not available.
}

var ConfigParser = function() {
  // Default configuration.
  this.config_ = {
    specs: [],
    multiCapabilities: [],
    rootElement: 'body',
    allScriptsTimeout: 11000,
    getPageTimeout: 10000,
    params: {},
    framework: 'jasmine',
    jasmineNodeOpts: {
      showColors: true,
      stackFilter: helper.filterStackTrace,
      defaultTimeoutInterval: (30 * 1000)
    },
    seleniumArgs: [],
    mochaOpts: {
      ui: 'bdd',
      reporter: 'list'
    },
    chromeDriver: null,
    configDir: './',
    plugins: [],
    skipSourceMapSupport: false
  };
};

/**
 * Merge config objects together.
 *
 * @private
 * @param {Object} into
 * @param {Object} from
 *
 * @return {Object} The 'into' config.
 */
var merge_ = function(into, from) {
  for (var key in from) {
    if (into[key] instanceof Object &&
        !(into[key] instanceof Array) &&
        !(into[key] instanceof Function)) {
      merge_(into[key], from[key]);
    } else {
      into[key] = from[key];
    }
  }
  return into;
};

/**
 * Returns the item if it's an array or puts the item in an array
 * if it was not one already.
 */
var makeArray = function(item) {
  return Array.isArray(item) ? item : [item];
};

/**
 * Adds to an array all the elements in another array without adding any
 * duplicates
 *
 * @param {Array<string>} dest The array to add to
 * @param {Array<string>} src The array to copy from
 */
var union = function(dest, src) {
  var elems = {};
  for (var key in dest) {
    elems[dest[key]] = true;
  }
  for (key in src) {
    if (!elems[src[key]]) {
      dest.push(src[key]);
      elems[src[key]] = true;
    }
  }
};

/**
 * Resolve a list of file patterns into a list of individual file paths.
 *
 * @param {Array.<string> | string} patterns
 * @param {=boolean} opt_omitWarnings Whether to omit did not match warnings
 * @param {=string} opt_relativeTo Path to resolve patterns against
 *
 * @return {Array} The resolved file paths.
 */
ConfigParser.resolveFilePatterns =
    function(patterns, opt_omitWarnings, opt_relativeTo) {
  var resolvedFiles = [];
  var cwd = opt_relativeTo || process.cwd();

  patterns = (typeof patterns === 'string') ?
      [patterns] : patterns;

  if (patterns) {
    for (var i = 0; i < patterns.length; ++i) {
      var fileName = patterns[i];
      var matches = glob.sync(fileName, {cwd: cwd});

      if (!matches.length && !opt_omitWarnings) {
        log.warn('pattern ' + patterns[i] + ' did not match any files.');
      }
      for (var j = 0; j < matches.length; ++j) {
        var resolvedPath = path.resolve(cwd, matches[j]);
        resolvedFiles.push(resolvedPath);
      }
    }
  }
  return resolvedFiles;
};

/**
 * Returns only the specs that should run currently based on `config.suite`
 *
 * @return {Array} An array of globs locating the spec files
 */
ConfigParser.getSpecs = function(config) {
  var specs = [];
  if (config.suite) {
    config.suite.split(',').forEach(function(suite) {
      var suiteList = config.suites[suite];
      if (suiteList == null) {
        throw new Error('Unknown test suite: ' + suite);
      }
      union(specs, makeArray(suiteList));
    });
    return specs;
  }

  if (config.specs.length > 0) {
    return config.specs;
  }

  Array.prototype.forEach.call(config.suites, function(suite) {
    union(specs, makeArray(suite));
  });
  return specs;
};

/**
 * Add the options in the parameter config to this runner instance.
 *
 * @private
 * @param {Object} additionalConfig
 * @param {string} relativeTo the file path to resolve paths against
 */
ConfigParser.prototype.addConfig_ = function(additionalConfig, relativeTo) {
  // All filepaths should be kept relative to the current config location.
  // This will not affect absolute paths.
  ['seleniumServerJar', 'chromeDriver', 'onPrepare', 'firefoxPath',
   'frameworkPath'].
      forEach(function(name) {
        if (additionalConfig[name] &&
            typeof additionalConfig[name] === 'string') {
          additionalConfig[name] =
              path.resolve(relativeTo, additionalConfig[name]);
        }
  });

  merge_(this.config_, additionalConfig);
};

/**
 * Public function specialized towards merging in a file's config
 *
 * @public
 * @param {String} filename
 */
ConfigParser.prototype.addFileConfig = function(filename) {
  try {
    if (!filename) {
      return this;
    }
    var filePath = path.resolve(process.cwd(), filename);
    var fileConfig = require(filePath).config;
    if (!fileConfig) {
      log.error('configuration file ' + filename + ' did not export a config ' +
          'object');
    }
    fileConfig.configDir = path.dirname(filePath);
    this.addConfig_(fileConfig, fileConfig.configDir);
  } catch (e) {
    log.error('failed loading configuration file ' + filename);
    throw e;
  }
  return this;
};

/**
 * Public function specialized towards merging in config from argv
 *
 * @public
 * @param {Object} argv
 */
ConfigParser.prototype.addConfig = function(argv) {
  this.addConfig_(argv, process.cwd());
  return this;
};

/**
 * Public getter for the final, computed config object
 *
 * @public
 * @return {Object} config
 */
ConfigParser.prototype.getConfig = function() {
  return this.config_;
};

module.exports = ConfigParser;
<|endoftext|>