<commit_before>/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/* Dev tools handlers.                                                                            */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

'use strict';

const nodeinfo   = require('nodejs-info'); // node info
const dateFormat = require('dateformat');  // Steven Levithan's dateFormat()

class Dev {

    /**
     * Information about current Node versions.
     */
    static nodeinfo(ctx) {
        ctx.body = nodeinfo(ctx.req);
    }

    /**
     * Show access log.
     */
    static async logAccess(ctx) {
        // access logging uses capped collection log-access (size: 1000×1e3, max: 1000)
        const log = global.mongoDb.collection('log-access');

        const entriesAll = (await log.find({}).sort({ $natural: -1 }).toArray());

        // 'from' defaults to later of first entry or 1 month ago
        const oldest = entriesAll.reduce((old, e) => e._id.getTimestamp()<old ? e._id.getTimestamp() : old, new Date());
        const monthAgo = new Date(); monthAgo.setMonth(new Date().getMonth() - 1);
        ctx.query.from = ctx.query.from || dateFormat(oldest<monthAgo ? monthAgo : oldest, 'yyyy-mm-dd');

        // 'to' defaults to today
        ctx.query.to = ctx.query.to || dateFormat('yyyy-mm-dd');
        // filter needs 1 day added to 'to' to made it end of the day
        const toFilter = new Date(ctx.query.to); toFilter.setDate(toFilter.getDate() + 1);

        // filter results according to query string
        const entriesFiltered = entriesAll
            .filter(e => ctx.query.from ? e._id.getTimestamp() >= new Date(ctx.query.from) : true)
            .filter(e => ctx.query.to ? e._id.getTimestamp() <= toFilter : true)
            .filter(e => ctx.query.app ? RegExp('^'+ctx.query.app).test(e.host) : true)
            .filter(e => ctx.query.time ? e.ms > ctx.query.time : true);

        // add in extra fields to each entry
        const entries = entriesFiltered.map(e => {
            return Object.assign(e, {
                time:  dateFormat(e._id.getTimestamp(), 'yyyy-mm-dd HH:MM:ss'),
                path:  e.url.split('?')[0] + (e.url.split('?').length>1 ? '?…' : ''),
                qs:    e.url.split('?')[1],
                os:    Number(e.ua.os.major) ? `${e.ua.os.family} ${e.ua.os.major}` : e.ua.os.family,
                ua:    Number(e.ua.major) ? e.ua.family+'-'+ e.ua.major : e.ua.family,
                ip:    e.ip,
                speed: e.ms>500 ? 'slow' : e.ms>100 ? 'medium' : '',
            });
        });

        // for display, time defaults to 0
        ctx.query.time = ctx.query.time || '0';

        const context = {
            entries:   entries,
            filter:    ctx.query,
            filterMin: dateFormat(oldest, 'yyyy-mm-dd'),
            filterMax: dateFormat('yyyy-mm-dd'),
        };

        await ctx.render('dev-logs-access', context);
    }

    /**
     * Show error log.
     */
    static async logError(ctx) {
        // error logging uses capped collection log-error (size: 1000×4e3, max: 1000)
        const log = global.mongoDb.collection('log-error');

        const entriesAll = (await log.find({}).sort({ $natural: -1 }).toArray());

        // 'from' defaults to later of first entry or 1 month ago
        const oldest = entriesAll.reduce((old, e) => e._id.getTimestamp()<old ? e._id.getTimestamp() : old, new Date());
        const monthAgo = new Date(); monthAgo.setMonth(new Date().getMonth() - 1);
        ctx.query.from = ctx.query.from || dateFormat(oldest<monthAgo ? monthAgo : oldest, 'yyyy-mm-dd');

        // 'to' defaults to today
        ctx.query.to = ctx.query.to || dateFormat('yyyy-mm-dd');
        // filter needs 1 day added to 'to' to made it end of the day
        const toFilter = new Date(ctx.query.to); toFilter.setDate(toFilter.getDate() + 1);

        // filter results according to query string
        const entriesFiltered = entriesAll
            .filter(e => ctx.query.from ? e._id.getTimestamp() >= new Date(ctx.query.from) : true)
            .filter(e => ctx.query.to ? e._id.getTimestamp() <= toFilter : true);

        // add in extra fields to each entry
        const entries = entriesFiltered.map(e => {
            return Object.assign(e, {
                time:      dateFormat(e._id.getTimestamp(), 'yyyy-mm-dd HH:MM:ss'),
                path:      e.url.split('?')[0] + (e.url.split('?').length>1 ? '?…' : ''),
                qs:        e.url.split('?')[1],
                os:        Number(e.ua.os.major) ? `${e.ua.os.family} ${e.ua.os.major}` : e.ua.os.family,
                ua:        Number(e.ua.major) ? e.ua.family+'-'+ e.ua.major : e.ua.family,
                ip:        e.ip,
                showstack: e.stack ? 'show' : 'hide',
            });
        });

        // for display, time defaults to 0
        ctx.query.time = ctx.query.time || '0';

        const context = {
            entries:   entries,
            filter:    ctx.query,
            filterMin: dateFormat(oldest, 'yyyy-mm-dd'),
            filterMax: dateFormat('yyyy-mm-dd'),
        };

        await ctx.render('dev-logs-error', context);
    }

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

module.exports = Dev;
<commit_msg>Formatting<commit_after>/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/* Dev tools handlers.                                                                            */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

'use strict';

const nodeinfo   = require('nodejs-info'); // node info
const dateFormat = require('dateformat');  // Steven Levithan's dateFormat()

class Dev {

    /**
     * Information about current Node versions.
     */
    static nodeinfo(ctx) {
        ctx.body = nodeinfo(ctx.req);
    }

    /**
     * Show access log.
     */
    static async logAccess(ctx) {
        // access logging uses capped collection log-access (size: 1000×1e3, max: 1000)
        const log = global.mongoDb.collection('log-access');

        const entriesAll = (await log.find({}).sort({ $natural: -1 }).toArray());

        // 'from' defaults to later of first entry or 1 month ago
        const oldest = entriesAll.reduce((old, e) => e._id.getTimestamp()<old ? e._id.getTimestamp() : old, new Date());
        const monthAgo = new Date(); monthAgo.setMonth(new Date().getMonth() - 1);
        ctx.query.from = ctx.query.from || dateFormat(oldest<monthAgo ? monthAgo : oldest, 'yyyy-mm-dd');

        // 'to' defaults to today
        ctx.query.to = ctx.query.to || dateFormat('yyyy-mm-dd');
        // filter needs 1 day added to 'to' to made it end of the day
        const toFilter = new Date(ctx.query.to); toFilter.setDate(toFilter.getDate() + 1);

        // filter results according to query string
        const entriesFiltered = entriesAll
            .filter(e => ctx.query.from ? e._id.getTimestamp() >= new Date(ctx.query.from) : true)
            .filter(e => ctx.query.to ? e._id.getTimestamp() <= toFilter : true)
            .filter(e => ctx.query.app ? RegExp('^'+ctx.query.app).test(e.host) : true)
            .filter(e => ctx.query.time ? e.ms > ctx.query.time : true);

        // add in extra fields to each entry
        const entries = entriesFiltered.map(e => {
            return Object.assign(e, {
                time:  dateFormat(e._id.getTimestamp(), 'yyyy-mm-dd HH:MM:ss'),
                host:  e.host.replace('koa-sample-app.movable-type.co.uk', ''),
                path:  e.url.split('?')[0] + (e.url.split('?').length>1 ? '?…' : ''),
                qs:    e.url.split('?')[1],
                os:    Number(e.ua.os.major) ? `${e.ua.os.family} ${e.ua.os.major}` : e.ua.os.family,
                ua:    Number(e.ua.major) ? e.ua.family+'-'+ e.ua.major : e.ua.family,
                ip:    e.ip,
                speed: e.ms>500 ? 'slow' : e.ms>100 ? 'medium' : '',
            });
        });

        // for display, time defaults to 0
        ctx.query.time = ctx.query.time || '0';

        const context = {
            entries:   entries,
            filter:    ctx.query,
            filterMin: dateFormat(oldest, 'yyyy-mm-dd'),
            filterMax: dateFormat('yyyy-mm-dd'),
        };

        await ctx.render('dev-logs-access', context);
    }

    /**
     * Show error log.
     */
    static async logError(ctx) {
        // error logging uses capped collection log-error (size: 1000×4e3, max: 1000)
        const log = global.mongoDb.collection('log-error');

        const entriesAll = (await log.find({}).sort({ $natural: -1 }).toArray());

        // 'from' defaults to later of first entry or 1 month ago
        const oldest = entriesAll.reduce((old, e) => e._id.getTimestamp()<old ? e._id.getTimestamp() : old, new Date());
        const monthAgo = new Date(); monthAgo.setMonth(new Date().getMonth() - 1);
        ctx.query.from = ctx.query.from || dateFormat(oldest<monthAgo ? monthAgo : oldest, 'yyyy-mm-dd');

        // 'to' defaults to today
        ctx.query.to = ctx.query.to || dateFormat('yyyy-mm-dd');
        // filter needs 1 day added to 'to' to made it end of the day
        const toFilter = new Date(ctx.query.to); toFilter.setDate(toFilter.getDate() + 1);

        // filter results according to query string
        const entriesFiltered = entriesAll
            .filter(e => ctx.query.from ? e._id.getTimestamp() >= new Date(ctx.query.from) : true)
            .filter(e => ctx.query.to ? e._id.getTimestamp() <= toFilter : true);

        // add in extra fields to each entry
        const entries = entriesFiltered.map(e => {
            return Object.assign(e, {
                time:      dateFormat(e._id.getTimestamp(), 'yyyy-mm-dd HH:MM:ss'),
                host:      e.host.replace('koa-sample-app.movable-type.co.uk', ''),
                path:      e.url.split('?')[0] + (e.url.split('?').length>1 ? '?…' : ''),
                qs:        e.url.split('?')[1],
                os:        Number(e.ua.os.major) ? `${e.ua.os.family} ${e.ua.os.major}` : e.ua.os.family,
                ua:        Number(e.ua.major) ? e.ua.family+'-'+ e.ua.major : e.ua.family,
                ip:        e.ip,
                showstack: e.stack ? 'show' : 'hide',
            });
        });

        // for display, time defaults to 0
        ctx.query.time = ctx.query.time || '0';

        const context = {
            entries:   entries,
            filter:    ctx.query,
            filterMin: dateFormat(oldest, 'yyyy-mm-dd'),
            filterMax: dateFormat('yyyy-mm-dd'),
        };

        await ctx.render('dev-logs-error', context);
    }

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

module.exports = Dev;
<|endoftext|>


<commit_before>"use strict";

/** @private */
const Utils = require("./color-utils").ColorUtils;
/** @private */
const Checker = require("./contrast-checker").ContrastChecker;

/** @private */
class SearchCriteria {
  static shouldScanDarkerSide(fixedRgb, otherRgb) {
    const fixedLuminance = Checker.relativeLuminance(fixedRgb);
    const otherLuminance = Checker.relativeLuminance(otherRgb);
    return fixedLuminance > otherLuminance ||
      fixedLuminance === otherLuminance && Checker.isLightColor(fixedRgb);
  }

  static define(fixedRgb, otherRgb, level) {
    const targetContrast = Checker.levelToRatio(level);

    if (this.shouldScanDarkerSide(fixedRgb, otherRgb)) {
      return new ToDarkerSide(targetContrast, fixedRgb);
    } else {
      return new ToBrighterSide(targetContrast, fixedRgb);
    }
  }

  constructor(targetContrast, fixedRgb) {
    this.targetContrast = targetContrast;
    this.fixedLuminance = Checker.relativeLuminance(fixedRgb);
  }

  hasSufficientContrast(rgb) {
    return this.contrastRatio(rgb) >= this.targetContrast;
  }

  contrastRatio(rgb) {
    const luminance = Checker.relativeLuminance(rgb);
    return Checker.luminanceToContrastRatio(this.fixedLuminance,
                                            luminance);
  }
}

/** @private */
class ToDarkerSide extends SearchCriteria {
  round(r) {
    return Math.floor(r * 10 ) / 10;
  }

  incrementCondition(contrastRatio) {
    return contrastRatio > this.targetContrast;
  }
}

/** @private */
class ToBrighterSide extends SearchCriteria {
  round(r) {
    return Math.ceil(r * 10) / 10;
  }

  incrementCondition(contrastRatio) {
    return this.targetContrast > contrastRatio;
  }
}

/** @private */
class ThresholdFinder {
  /** @private */
  static * binarySearchWidth(initWidth, min) {
    let i = 1;
    let d = initWidth / Math.pow(2, i);

    while (d > min) {
      yield d;
      i++;
      d = initWidth / Math.pow(2, i);
    }
  }

  /**
   * @private
   */
  static findRatio(otherColor, criteria, initRatio, initWidth) {
    let r = initRatio;
    let passingRatio = null;

    for (let d of this.binarySearchWidth(initWidth, 0.01)) {
      const newRgb = this.rgbWithRatio(otherColor, r);
      const contrast = criteria.contrastRatio(newRgb);

      if (criteria.hasSufficientContrast(newRgb)) { passingRatio = r; }
      if (contrast === criteria.targetContrast) { break; }
      r += criteria.incrementCondition(contrast) ? d : -d;
    }

    return [r, passingRatio];
  }

  /**
   * @private
   */
  static rgbWithBetterRatio(color, criteria, lastRatio, passingRatio) {
    const closestRgb = this.rgbWithRatio(color, lastRatio);

    if (passingRatio && ! criteria.hasSufficientContrast(closestRgb)) {
      return this.rgbWithRatio(color, passingRatio);
    }

    return closestRgb;
  }
}

/** @private */
class LightnessFinder extends ThresholdFinder {
  /**
   * Tries to find a color whose contrast against the base color is close to
   * a given level.
   *
   * The returned color is gained by modifying the lightness of otherRgb.
   * Even when a color that satisfies the level is not found, it returns
   * a new color anyway.
   * @param {Array<number, number, number>} fixedRgb - RGB value which remains
   *     unchanged
   * @param {Array<number, number, number>} otherRgb - RGB value before the
   *     modification of lightness
   * @param {string} [level="AA"] - A, AA or AAA
   * @returns {Array<number, number, number>} RGB value of a new color whose
   *     contrast ratio against fixedRgb is close to a specified level
   */
  static find(fixedRgb, otherRgb, level = "AA") {
    const criteria = SearchCriteria.define(fixedRgb, otherRgb, level);
    const otherHsl = Utils.rgbToHsl(otherRgb);
    const [max, min] = this.determineMinmax(fixedRgb, otherRgb, otherHsl[2]);

    const boundaryRgb = this.boundaryColor(fixedRgb, max, min, criteria);

    if (boundaryRgb) { return boundaryRgb; }

    const [r, passingRatio] = this.findRatio(otherHsl, criteria,
                                             (max + min) / 2, max - min);

    return this.rgbWithBetterRatio(otherHsl, criteria, r, passingRatio);
  }

  /**
   * @private
   */
  static rgbWithRatio(hsl, ratio) {
    if (ratio !== undefined && hsl[2] !== ratio) {
      hsl = hsl.slice(0);
      hsl[2] = ratio;
    }

    return Utils.hslToRgb(hsl);
  }

  /**
   * @private
   */
  static determineMinmax(fixedRgb, otherRgb, initL) {
    if (SearchCriteria.shouldScanDarkerSide(fixedRgb, otherRgb)) {
      return [initL, 0];
    } else {
      return [100, initL];
    }
  }

  /**
   * @private
   */
  static boundaryColor(rgb, max, min, criteria) {
    const black = Checker.LUMINANCE.BLACK;
    const white = Checker.LUMINANCE.WHITE;

    if (min === 0 && ! this.hasSufficientContrast(black, rgb, criteria)) {
      return Utils.RGB.BLACK;
    }

    if (max === 100 && ! this.hasSufficientContrast(white, rgb, criteria)) {
      return Utils.RGB.WHITE;
    }

    return null;
  }

  /**
   * @private
   */
  static hasSufficientContrast(fixedLuminance, rgb, criteria) {
    const luminance = Checker.relativeLuminance(rgb);
    const ratio = Checker.luminanceToContrastRatio(fixedLuminance, luminance);
    return ratio >= criteria.targetContrast;
  }
}

/** @private */
class BrightnessFinder extends ThresholdFinder {
  /**
   * Tries to find a color whose contrast against the base color is close
   *  to a given level.
   *
   * The returned color is gained by modifying the brightness of otherRgb.
   * Even when a color that satisfies the level is not found, it returns
   * a new color anyway.
   * @param {Array<number, number, number>} fixedRgb - RGB value which remains
   *     unchanged
   * @param {Array<number, number, number>} otherRgb - RGB value before the
   *     modification of brightness
   * @param {string} [level="AA"] - A, AA or AAA
   * @returns {Array<number, number, number>} RGB value of a new color whose
   *     contrast ratio against fixedRgb is close to a specified level
   */
  static find(fixedRgb, otherRgb, level = "AA") {
    const criteria = SearchCriteria.define(fixedRgb, otherRgb, level);
    const w = this.calcUpperRatioLimit(otherRgb) / 2;

    const upperRgb = this.rgbWithRatio(otherRgb, w * 2);

    if (this.exceedUpperLimit(criteria, otherRgb, upperRgb)) {
      return upperRgb;
    }

    const ratios = this.findRatio(otherRgb, criteria, w, w).map(criteria.round);

    return this.rgbWithBetterRatio(otherRgb, criteria, ...ratios);
  }

  /**
   * @private
   */
  static rgbWithRatio(rgb, ratio) {
    return Utils.BrightnessCalc.calcRgb(rgb, ratio);
  }

  /**
   * @private
   */
  static exceedUpperLimit(criteria, otherRgb, upperRgb) {
    const otherLuminance = Checker.relativeLuminance(otherRgb);
    return otherLuminance > criteria.fixedLuminance &&
      ! criteria.hasSufficientContrast(upperRgb);
  }

  /**
   * @private
   */
  static calcUpperRatioLimit(rgb) {
    if (Utils.isSameRgbColor(Utils.RGB.BLACK, rgb)) {
      return 100;
    }

    const darkest = rgb
          .filter(c => c !== 0)
          .reduce((a, b) => Math.min(a, b));
    return Math.ceil((255 / darkest) * 100);
  }
}

module.exports.LightnessFinder = LightnessFinder;
module.exports.BrightnessFinder = BrightnessFinder;
<commit_msg>rename a local variable<commit_after>"use strict";

/** @private */
const Utils = require("./color-utils").ColorUtils;
/** @private */
const Checker = require("./contrast-checker").ContrastChecker;

/** @private */
class SearchCriteria {
  static shouldScanDarkerSide(fixedRgb, otherRgb) {
    const fixedLuminance = Checker.relativeLuminance(fixedRgb);
    const otherLuminance = Checker.relativeLuminance(otherRgb);
    return fixedLuminance > otherLuminance ||
      fixedLuminance === otherLuminance && Checker.isLightColor(fixedRgb);
  }

  static define(fixedRgb, otherRgb, level) {
    const targetContrast = Checker.levelToRatio(level);

    if (this.shouldScanDarkerSide(fixedRgb, otherRgb)) {
      return new ToDarkerSide(targetContrast, fixedRgb);
    } else {
      return new ToBrighterSide(targetContrast, fixedRgb);
    }
  }

  constructor(targetContrast, fixedRgb) {
    this.targetContrast = targetContrast;
    this.fixedLuminance = Checker.relativeLuminance(fixedRgb);
  }

  hasSufficientContrast(rgb) {
    return this.contrastRatio(rgb) >= this.targetContrast;
  }

  contrastRatio(rgb) {
    const luminance = Checker.relativeLuminance(rgb);
    return Checker.luminanceToContrastRatio(this.fixedLuminance,
                                            luminance);
  }
}

/** @private */
class ToDarkerSide extends SearchCriteria {
  round(ratio) {
    return Math.floor(ratio * 10 ) / 10;
  }

  incrementCondition(contrastRatio) {
    return contrastRatio > this.targetContrast;
  }
}

/** @private */
class ToBrighterSide extends SearchCriteria {
  round(ratio) {
    return Math.ceil(ratio * 10) / 10;
  }

  incrementCondition(contrastRatio) {
    return this.targetContrast > contrastRatio;
  }
}

/** @private */
class ThresholdFinder {
  /** @private */
  static * binarySearchWidth(initWidth, min) {
    let i = 1;
    let d = initWidth / Math.pow(2, i);

    while (d > min) {
      yield d;
      i++;
      d = initWidth / Math.pow(2, i);
    }
  }

  /**
   * @private
   */
  static findRatio(otherColor, criteria, initRatio, initWidth) {
    let r = initRatio;
    let passingRatio = null;

    for (let d of this.binarySearchWidth(initWidth, 0.01)) {
      const newRgb = this.rgbWithRatio(otherColor, r);
      const contrast = criteria.contrastRatio(newRgb);

      if (criteria.hasSufficientContrast(newRgb)) { passingRatio = r; }
      if (contrast === criteria.targetContrast) { break; }
      r += criteria.incrementCondition(contrast) ? d : -d;
    }

    return [r, passingRatio];
  }

  /**
   * @private
   */
  static rgbWithBetterRatio(color, criteria, lastRatio, passingRatio) {
    const closestRgb = this.rgbWithRatio(color, lastRatio);

    if (passingRatio && ! criteria.hasSufficientContrast(closestRgb)) {
      return this.rgbWithRatio(color, passingRatio);
    }

    return closestRgb;
  }
}

/** @private */
class LightnessFinder extends ThresholdFinder {
  /**
   * Tries to find a color whose contrast against the base color is close to
   * a given level.
   *
   * The returned color is gained by modifying the lightness of otherRgb.
   * Even when a color that satisfies the level is not found, it returns
   * a new color anyway.
   * @param {Array<number, number, number>} fixedRgb - RGB value which remains
   *     unchanged
   * @param {Array<number, number, number>} otherRgb - RGB value before the
   *     modification of lightness
   * @param {string} [level="AA"] - A, AA or AAA
   * @returns {Array<number, number, number>} RGB value of a new color whose
   *     contrast ratio against fixedRgb is close to a specified level
   */
  static find(fixedRgb, otherRgb, level = "AA") {
    const criteria = SearchCriteria.define(fixedRgb, otherRgb, level);
    const otherHsl = Utils.rgbToHsl(otherRgb);
    const [max, min] = this.determineMinmax(fixedRgb, otherRgb, otherHsl[2]);

    const boundaryRgb = this.boundaryColor(fixedRgb, max, min, criteria);

    if (boundaryRgb) { return boundaryRgb; }

    const [r, passingRatio] = this.findRatio(otherHsl, criteria,
                                             (max + min) / 2, max - min);

    return this.rgbWithBetterRatio(otherHsl, criteria, r, passingRatio);
  }

  /**
   * @private
   */
  static rgbWithRatio(hsl, ratio) {
    if (ratio !== undefined && hsl[2] !== ratio) {
      hsl = hsl.slice(0);
      hsl[2] = ratio;
    }

    return Utils.hslToRgb(hsl);
  }

  /**
   * @private
   */
  static determineMinmax(fixedRgb, otherRgb, initL) {
    if (SearchCriteria.shouldScanDarkerSide(fixedRgb, otherRgb)) {
      return [initL, 0];
    } else {
      return [100, initL];
    }
  }

  /**
   * @private
   */
  static boundaryColor(rgb, max, min, criteria) {
    const black = Checker.LUMINANCE.BLACK;
    const white = Checker.LUMINANCE.WHITE;

    if (min === 0 && ! this.hasSufficientContrast(black, rgb, criteria)) {
      return Utils.RGB.BLACK;
    }

    if (max === 100 && ! this.hasSufficientContrast(white, rgb, criteria)) {
      return Utils.RGB.WHITE;
    }

    return null;
  }

  /**
   * @private
   */
  static hasSufficientContrast(fixedLuminance, rgb, criteria) {
    const luminance = Checker.relativeLuminance(rgb);
    const ratio = Checker.luminanceToContrastRatio(fixedLuminance, luminance);
    return ratio >= criteria.targetContrast;
  }
}

/** @private */
class BrightnessFinder extends ThresholdFinder {
  /**
   * Tries to find a color whose contrast against the base color is close
   *  to a given level.
   *
   * The returned color is gained by modifying the brightness of otherRgb.
   * Even when a color that satisfies the level is not found, it returns
   * a new color anyway.
   * @param {Array<number, number, number>} fixedRgb - RGB value which remains
   *     unchanged
   * @param {Array<number, number, number>} otherRgb - RGB value before the
   *     modification of brightness
   * @param {string} [level="AA"] - A, AA or AAA
   * @returns {Array<number, number, number>} RGB value of a new color whose
   *     contrast ratio against fixedRgb is close to a specified level
   */
  static find(fixedRgb, otherRgb, level = "AA") {
    const criteria = SearchCriteria.define(fixedRgb, otherRgb, level);
    const w = this.calcUpperRatioLimit(otherRgb) / 2;

    const upperRgb = this.rgbWithRatio(otherRgb, w * 2);

    if (this.exceedUpperLimit(criteria, otherRgb, upperRgb)) {
      return upperRgb;
    }

    const ratios = this.findRatio(otherRgb, criteria, w, w).map(criteria.round);

    return this.rgbWithBetterRatio(otherRgb, criteria, ...ratios);
  }

  /**
   * @private
   */
  static rgbWithRatio(rgb, ratio) {
    return Utils.BrightnessCalc.calcRgb(rgb, ratio);
  }

  /**
   * @private
   */
  static exceedUpperLimit(criteria, otherRgb, upperRgb) {
    const otherLuminance = Checker.relativeLuminance(otherRgb);
    return otherLuminance > criteria.fixedLuminance &&
      ! criteria.hasSufficientContrast(upperRgb);
  }

  /**
   * @private
   */
  static calcUpperRatioLimit(rgb) {
    if (Utils.isSameRgbColor(Utils.RGB.BLACK, rgb)) {
      return 100;
    }

    const darkest = rgb
          .filter(c => c !== 0)
          .reduce((a, b) => Math.min(a, b));
    return Math.ceil((255 / darkest) * 100);
  }
}

module.exports.LightnessFinder = LightnessFinder;
module.exports.BrightnessFinder = BrightnessFinder;
<|endoftext|>


<commit_before>describe('Parse', function() {

    var list;

    before(function() {
        $('body').append($('
\

\

Jonny1986
\

Jocke1985
\

\

'));

        list = new List('parse-list', {
            valueNames: ['name', 'born']
        });
    });

    after(function() {
        $('#parse-list').remove();
    });

    describe('Parse', function() {
        it('should have two items', function() {
            expect(list.items.length).to.equal(2);
            expect(list.items[0].values().name).to.equal("Jonny");
            expect(list.items[1].values().name).to.equal("Jocke");
        });
        it('should add item to parsed list', function() {
            list.add({ name: "Sven", born: 1950 });
            expect(list.items.length).to.equal(3);
            expect(list.items[0].values().name).to.equal("Jonny");
            expect(list.items[1].values().name).to.equal("Jocke");
            expect(list.items[2].values().name).to.equal("Sven");
            expect(list.items[0].values().born).to.equal("1986");
            expect(list.items[2].values().born).to.equal(1950);
            expect($('#parse-list').find('.list div')[2].innerHTML).to.equal('Sven1950');
        });
        it('should parsed value always be string while added could be number', function() {
            expect(list.items[0].values().born).to.equal("1986");
            expect(list.items[0].values().born).not.to.equal(1986);
            expect(list.items[2].values().born).not.to.equal("1950");
            expect(list.items[2].values().born).to.equal(1950);
        });
    });
});<commit_msg>Better parse tests for IE<commit_after>describe('Parse', function() {

    var list;

    before(function() {
        $('body').append($('
\

\

Jonny1986
\

Jocke1985
\

\

'));

        list = new List('parse-list', {
            valueNames: ['name', 'born']
        });
    });

    after(function() {
        $('#parse-list').remove();
    });

    describe('Parse', function() {
        it('should have two items', function() {
            expect(list.items.length).to.equal(2);
            expect(list.items[0].values().name).to.equal("Jonny");
            expect(list.items[1].values().name).to.equal("Jocke");
        });
        it('should add item to parsed list', function() {
            list.add({ name: "Sven", born: 1950 });
            expect(list.items.length).to.equal(3);
            expect(list.items[0].values().name).to.equal("Jonny");
            expect(list.items[1].values().name).to.equal("Jocke");
            expect(list.items[2].values().name).to.equal("Sven");
            expect(list.items[0].values().born).to.equal("1986");
            expect(list.items[2].values().born).to.equal(1950);
            var el = $($('#parse-list').find('.list div')[2]);
            expect(el.find('span').size()).to.equal(2);
            expect(el.find('span.name').text()).to.equal('Sven');
            expect(el.find('span.born').text()).to.equal('1950');
        });
        it('should parsed value always be string while added could be number', function() {
            expect(list.items[0].values().born).to.equal("1986");
            expect(list.items[0].values().born).not.to.equal(1986);
            expect(list.items[2].values().born).not.to.equal("1950");
            expect(list.items[2].values().born).to.equal(1950);
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

var getFileUrl = require('../getfileurl');
var assert = require('assert');

describe('Slurper - inputs', function(){

  it('Should throw if opts.url is not specified', function(done){

    var opts = {};

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Missing required param: url/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

  it('Should throw if opts.url is not a valid url', function(done){

    var opts = {
      url:'pysje',
      id:'#megaman'
    };

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Invalid url/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

  it('Should throw if opts.id is not specified', function(done){

    var opts = {
      url:'http://www.google.com'
    };

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Missing required param: id/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

});<commit_msg>Changes code style<commit_after>'use strict';

var getFileUrl = require('../getfileurl');
var assert = require('assert');

describe('Slurper - inputs', function(){

  it('Should throw if opts.url is not specified', function(done){

    var opts = {};

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Missing required param: url/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

  it('Should throw if opts.url is not a valid url', function(done){

    var opts = {
      url:'pysje',
      id:'#megaman'
    };

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Invalid url/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

  it('Should throw if opts.id is not specified', function(done){

    var opts = {
      url:'http://www.google.com',
      id: false
    };

    getFileUrl(opts, function(err, data){
      assert.throws(function(){
          if(err) {
            throw err;
          } else {
            console.log(data);
          }
        }, function(err){
          if((err instanceof Error) && /Missing required param: id/.test(err)){
            return true;
          }
        },
        'Unexpected error'
      );
      done();
    });

  });

});<|endoftext|>


<commit_before>"use strict";

var fn;

function LocalTransportServer(_fn) {
    fn = _fn;
}

LocalTransportServer.prototype.listen = function(done) {
    done();
};

LocalTransportServer.prototype.stop = function(done) {
    done();
};

function LocalTransportClient() {

}

LocalTransportClient.prototype.connect = function(done) {
    done(); //noop
};

LocalTransportClient.prototype.disconnect = function(done) {
    done(); //noop
};

LocalTransportClient.prototype.call = function(method, data, callback) {
    // force data to be passed through as valid json
    // both the input, and the response from the service
    forceJSON(data, function(err, data) {
        if(err) return callback(err);
        fn(method, data, function(err, response) {
            if(err) {
                return forceJSON(err, function(err) {
                    callback(err);
                });
            }
            forceJSON(response, function(response) {
                callback(null, response);
            });
        });
    });
};

function LocalTransport() {
    this.Server = LocalTransportServer;
    this.Client = LocalTransportClient;
}

module.exports = LocalTransport;

function forceJSON(input, callback) {
    // JSON.stringify can throw on circular structures
    try {
        input = JSON.parse(JSON.stringify(input));
    } catch(e) {
        return callback(e);
    }
    return callback(null, input);
}<commit_msg>fix local transport not returning responses properly<commit_after>"use strict";

var fn;

function LocalTransportServer(_fn) {
    fn = _fn;
}

LocalTransportServer.prototype.listen = function(done) {
    done();
};

LocalTransportServer.prototype.stop = function(done) {
    done();
};

function LocalTransportClient() {

}

LocalTransportClient.prototype.connect = function(done) {
    done(); //noop
};

LocalTransportClient.prototype.disconnect = function(done) {
    done(); //noop
};

LocalTransportClient.prototype.call = function(method, data, callback) {
    // force data to be passed through as valid json
    // both the input, and the response from the service
    forceJSON(data, function(err, data) {
        if(err) return callback(err);
        fn(method, data, function(err, response) {
            if(err) {
                return forceJSON(err, callback);
            }
            forceJSON(response, callback);
        });
    });
};

function LocalTransport() {
    this.Server = LocalTransportServer;
    this.Client = LocalTransportClient;
}

module.exports = LocalTransport;

function forceJSON(input, callback) {
    // JSON.stringify can throw on circular structures
    try {
        input = JSON.parse(JSON.stringify(input));
    } catch(e) {
        return callback(e);
    }
    return callback(null, input);
}<|endoftext|>


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

'use strict';

function test_event_basic() {
  let codepages = {
    Default: {
      Tags: {
        ROOT: 0x05,
        CARD: 0x06,
        FIELD: 0x07,
      },
      Attrs: {
        TYPE: { value: 0x05 },
      },
    }
  };
  WBXML.CompileCodepages(codepages);
  let cp = codepages.Default.Tags;
  let cpa = codepages.Default.Attrs;

  let a = WBXML.Writer.a;

  let w1 = new WBXML.Writer('1.1', 1, 'US-ASCII');
  w1.stag(cp.ROOT)
      .tag(cp.CARD)
    .etag();

  let expectedSubdoc1 = { type: 'TAG', tag: cp.CARD, localTagName: 'CARD' };

  let e1 = new WBXML.EventParser();
  e1.addEventListener([cp.ROOT, cp.CARD], function(subdoc) {
    verify_subdocument(subdoc, expectedSubdoc1);
  });
  e1.run(new WBXML.Reader(w1, codepages));

  let w2 = new WBXML.Writer('1.1', 1, 'US-ASCII');
  w2.stag(cp.ROOT)
      .stag(cp.CARD)
        .tag(cp.FIELD, a(cpa.TYPE, 'NAME'), 'Anne')
        .text('anne@anne.com')
      .etag()
    .etag();

  let expectedSubdoc2 =
    { type: 'TAG', tag: cp.CARD, localTagName: 'CARD', children: [
      { type: 'TAG', tag: cp.FIELD, localTagName: 'FIELD',
        attributes: { TYPE: 'NAME' }, children: [
          { type: 'TEXT', textContent: 'Anne' },
        ] },
      { type: 'TEXT', textContent: 'anne@anne.com' },
    ]};

  let e2 = new WBXML.EventParser();
  e2.addEventListener([cp.ROOT, cp.CARD], function(subdoc) {
    verify_subdocument(subdoc, expectedSubdoc2);
  });
  e2.run(new WBXML.Reader(w2, codepages));
}
<commit_msg>Add more tests for the event-based parser<commit_after>/* Copyright 2012 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

function test_event_basic() {
  let codepages = {
    Default: {
      Tags: {
        ROOT: 0x05,
        CARD: 0x06,
        FIELD: 0x07,
      },
      Attrs: {
        TYPE: { value: 0x05 },
      },
    }
  };
  WBXML.CompileCodepages(codepages);
  let cp = codepages.Default.Tags;
  let cpa = codepages.Default.Attrs;

  let a = WBXML.Writer.a;

  let w1 = new WBXML.Writer('1.1', 1, 'US-ASCII');
  w1.stag(cp.ROOT)
      .tag(cp.CARD)
    .etag();

  let expectedSubdoc1 = { type: 'TAG', tag: cp.CARD, localTagName: 'CARD' };

  let e1 = new WBXML.EventParser();
  e1.addEventListener([cp.ROOT, cp.CARD], function(subdoc) {
    verify_subdocument(subdoc, expectedSubdoc1);
  });
  e1.run(new WBXML.Reader(w1, codepages));

  let w2 = new WBXML.Writer('1.1', 1, 'US-ASCII');
  w2.stag(cp.ROOT)
      .stag(cp.CARD)
        .tag(cp.FIELD, a(cpa.TYPE, 'NAME'), 'Anne')
        .text('anne@anne.com')
      .etag()
    .etag();

  let expectedSubdoc2 =
    { type: 'TAG', tag: cp.CARD, localTagName: 'CARD', children: [
      { type: 'TAG', tag: cp.FIELD, localTagName: 'FIELD',
        attributes: { TYPE: 'NAME' }, children: [
          { type: 'TEXT', textContent: 'Anne' },
        ] },
      { type: 'TEXT', textContent: 'anne@anne.com' },
    ]};

  let e2 = new WBXML.EventParser();
  e2.addEventListener([cp.ROOT, cp.CARD], function(subdoc) {
    verify_subdocument(subdoc, expectedSubdoc2);
  });
  e2.run(new WBXML.Reader(w2, codepages));
}

function test_event_overlapped() {
  let codepages = {
    Default: {
      Tags: {
        ROOT: 0x05,
        CARD: 0x06,
        FIELD: 0x07,
      },
      Attrs: {
        TYPE: { value: 0x05 },
      },
    }
  };
  WBXML.CompileCodepages(codepages);
  let cp = codepages.Default.Tags;
  let cpa = codepages.Default.Attrs;

  let a = WBXML.Writer.a;

  let w = new WBXML.Writer('1.1', 1, 'US-ASCII');
  w.stag(cp.ROOT)
     .stag(cp.CARD)
       .tag(cp.FIELD, a(cpa.TYPE, 'NAME'), 'Anne')
       .text('anne@anne.com')
     .etag()
   .etag();

  let e = new WBXML.EventParser();

  e.addEventListener([cp.ROOT, cp.CARD], function(subdoc) {
    let expectedSubdoc =
      { type: 'TAG', tag: cp.CARD, localTagName: 'CARD', children: [
        { type: 'TAG', tag: cp.FIELD, localTagName: 'FIELD',
          attributes: { TYPE: 'NAME' }, children: [
            { type: 'TEXT', textContent: 'Anne' },
          ] },
        { type: 'TEXT', textContent: 'anne@anne.com' },
      ]};
    verify_subdocument(subdoc, expectedSubdoc);
  });

  e.addEventListener([cp.ROOT, cp.CARD, cp.FIELD], function(subdoc) {
    let expectedSubdoc =
      { type: 'TAG', tag: cp.FIELD, localTagName: 'FIELD',
        attributes: { TYPE: 'NAME' }, children: [
          { type: 'TEXT', textContent: 'Anne' },
        ] };
    verify_subdocument(subdoc, expectedSubdoc);
  });

  e.run(new WBXML.Reader(w, codepages));
}
<|endoftext|>


<commit_before>module.exports = {
  name: 'unexpected-stream',
  version: require('../package.json').version,
  installInto: function(expect) {
    expect.addType({
      name: 'Stream',
      base: 'object',
      inspect: function(obj, depth, output, inspect) {
        output.text(
          (obj.constructor && obj.constructor.name) || 'Stream',
          'jsFunctionName'
        );
      },
      identify: function(obj) {
        return (
          obj &&
          typeof obj.on === 'function' &&
          (obj._readableState ||
            typeof obj.readable === 'boolean' ||
            typeof obj.writable === 'boolean')
        );
      }
    });

    expect.addAssertion(
      '<Stream|string|Buffer|array> [when] piped through <array|Stream> <assertion?>',
      function(expect, subject, value) {
        expect.errorMode = 'nested';

        return expect.promise(function(resolve, reject) {
          var pipeThroughStreams = Array.isArray(value) ? value : [value];
          expect(pipeThroughStreams, 'not to be empty');
          var subjectType = expect.findTypeOf(subject);
          var currentStream;
          if (subjectType.is('Stream')) {
            currentStream = subject;
            subject.on('error', reject);
          }

          var returnValue = expect.shift(
            pipeThroughStreams[pipeThroughStreams.length - 1]
          );

          pipeThroughStreams.forEach(function(pipeThroughStream) {
            pipeThroughStream.on('error', reject);
            currentStream = currentStream
              ? currentStream.pipe(pipeThroughStream)
              : pipeThroughStream;
          });

          if (!subjectType.is('Stream')) {
            // string, Buffer, or array
            try {
              (Array.isArray(subject) ? subject : [subject]).forEach(function(
                chunk
              ) {
                pipeThroughStreams[0].write(chunk);
              });
              pipeThroughStreams[0].end();
            } catch (e) {
              return reject(e);
            }
          }
          return resolve(returnValue);
        });
      }
    );

    function ensureUnexpectedStreamChunksPromise(stream) {
      if (!stream._unexpectedStreamChunksPromise) {
        stream._unexpectedStreamChunksPromise = expect.promise(function(
          resolve,
          reject
        ) {
          expect(stream, 'to have property', 'readable', true);
          var chunks = [];
          stream
            .on('data', function(chunk) {
              chunks.push(chunk);
            })
            .on('end', function() {
              resolve(chunks);
            })
            .on('error', reject);
        });
      }
    }

    expect.addAssertion(
      [
        '<Stream> to yield (output|chunks|objects) satisfying <any+>',
        '<Stream> to yield (output|chunks|objects) satisfying <assertion>'
      ],
      function(expect, subject, assertionOrSatisfySpec) {
        expect.errorMode = 'nested';

        var extraArgs = Array.prototype.slice.call(arguments, 2);

        var mustConcat = expect.alternations[0] === 'output';

        return expect
          .promise(function(resolve, reject) {
            ensureUnexpectedStreamChunksPromise(subject);
            subject._unexpectedStreamChunksPromise.then(function(chunks) {
              var result;
              if (mustConcat) {
                if (
                  chunks.length > 0 &&
                  chunks.every(function(chunk) {
                    return typeof chunk === 'string';
                  })
                ) {
                  result = chunks.join('');
                } else {
                  // chunks or objects (object mode)
                  result = Buffer.concat(
                    chunks.map(function(chunk) {
                      if (typeof chunk === 'string') {
                        if (Buffer.from) {
                          return Buffer.from(chunk, 'utf-8');
                        } else {
                          return new Buffer(chunk, 'utf-8');
                        }
                      } else {
                        return chunk;
                      }
                    })
                  );
                }
              } else {
                result = chunks;
              }
              resolve(result);
            }, reject);
          })
          .then(function(result) {
            return expect.apply(
              expect,
              [result, 'to satisfy assertion'].concat(extraArgs)
            );
          });
      }
    );

    expect.addAssertion('<Stream> to error', function(expect, subject) {
      ensureUnexpectedStreamChunksPromise(subject);
      return subject._unexpectedStreamChunksPromise.then(
        function() {
          expect.fail(
            new Error('Stream was supposed to fail, but ended correctly')
          );
        },
        function(err) {
          return err;
        }
      );
    });

    expect.addAssertion('<Stream> to error with <any>', function(
      expect,
      subject,
      value
    ) {
      expect.errorMode = 'nested';
      return expect(subject, 'to error').then(function(err) {
        return expect(err, 'to satisfy', value);
      });
    });
  }
};
<commit_msg>Use a WeakMap for storing the chunks from already consumed streams<commit_after>module.exports = {
  name: 'unexpected-stream',
  version: require('../package.json').version,
  installInto: function(expect) {
    expect.addType({
      name: 'Stream',
      base: 'object',
      inspect: function(obj, depth, output, inspect) {
        output.text(
          (obj.constructor && obj.constructor.name) || 'Stream',
          'jsFunctionName'
        );
      },
      identify: function(obj) {
        return (
          obj &&
          typeof obj.on === 'function' &&
          (obj._readableState ||
            typeof obj.readable === 'boolean' ||
            typeof obj.writable === 'boolean')
        );
      }
    });

    expect.addAssertion(
      '<Stream|string|Buffer|array> [when] piped through <array|Stream> <assertion?>',
      function(expect, subject, value) {
        expect.errorMode = 'nested';

        return expect.promise(function(resolve, reject) {
          var pipeThroughStreams = Array.isArray(value) ? value : [value];
          expect(pipeThroughStreams, 'not to be empty');
          var subjectType = expect.findTypeOf(subject);
          var currentStream;
          if (subjectType.is('Stream')) {
            currentStream = subject;
            subject.on('error', reject);
          }

          var returnValue = expect.shift(
            pipeThroughStreams[pipeThroughStreams.length - 1]
          );

          pipeThroughStreams.forEach(function(pipeThroughStream) {
            pipeThroughStream.on('error', reject);
            currentStream = currentStream
              ? currentStream.pipe(pipeThroughStream)
              : pipeThroughStream;
          });

          if (!subjectType.is('Stream')) {
            // string, Buffer, or array
            try {
              (Array.isArray(subject) ? subject : [subject]).forEach(function(
                chunk
              ) {
                pipeThroughStreams[0].write(chunk);
              });
              pipeThroughStreams[0].end();
            } catch (e) {
              return reject(e);
            }
          }
          return resolve(returnValue);
        });
      }
    );

    const chunkPromiseByStream = new WeakMap();
    function getChunkPromise(stream) {
      let chunkPromise = chunkPromiseByStream.get(stream);
      if (!chunkPromise) {
        chunkPromise = expect.promise(function(resolve, reject) {
          expect(stream, 'to have property', 'readable', true);
          const chunks = [];
          stream
            .on('data', function(chunk) {
              chunks.push(chunk);
            })
            .on('end', function() {
              resolve(chunks);
            })
            .on('error', reject);
        });
        chunkPromiseByStream.set(stream, chunkPromise);
      }
      return chunkPromise;
    }

    expect.addAssertion(
      [
        '<Stream> to yield (output|chunks|objects) satisfying <any+>',
        '<Stream> to yield (output|chunks|objects) satisfying <assertion>'
      ],
      function(expect, subject, assertionOrSatisfySpec) {
        expect.errorMode = 'nested';

        var extraArgs = Array.prototype.slice.call(arguments, 2);

        var mustConcat = expect.alternations[0] === 'output';

        return expect
          .promise(function(resolve, reject) {
            getChunkPromise(subject).then(function(chunks) {
              var result;
              if (mustConcat) {
                if (
                  chunks.length > 0 &&
                  chunks.every(function(chunk) {
                    return typeof chunk === 'string';
                  })
                ) {
                  result = chunks.join('');
                } else {
                  // chunks or objects (object mode)
                  result = Buffer.concat(
                    chunks.map(function(chunk) {
                      if (typeof chunk === 'string') {
                        if (Buffer.from) {
                          return Buffer.from(chunk, 'utf-8');
                        } else {
                          return new Buffer(chunk, 'utf-8');
                        }
                      } else {
                        return chunk;
                      }
                    })
                  );
                }
              } else {
                result = chunks;
              }
              resolve(result);
            }, reject);
          })
          .then(function(result) {
            return expect.apply(
              expect,
              [result, 'to satisfy assertion'].concat(extraArgs)
            );
          });
      }
    );

    expect.addAssertion('<Stream> to error', function(expect, subject) {
      return getChunkPromise(subject).then(
        function() {
          expect.fail(
            new Error('Stream was supposed to fail, but ended correctly')
          );
        },
        function(err) {
          return err;
        }
      );
    });

    expect.addAssertion('<Stream> to error with <any>', function(
      expect,
      subject,
      value
    ) {
      expect.errorMode = 'nested';
      return expect(subject, 'to error').then(function(err) {
        return expect(err, 'to satisfy', value);
      });
    });
  }
};
<|endoftext|>


<commit_before>/*
  p.js
*/

var ip_is_in_range = function(val, range){
  if( range[0] <= val && range[1] >= val )
    return true;
  else
    return false;
}

var ip_lighten = function(dst, val){
  var roi = ip_get_roi( dst );
  var ch_mask = [];

  for(i=0;i<4;i++){
    ch_mask[i] = ip_get_channel_mask( dst, i );
  }

  for(i=roi[0];i<roi[2];i++){
    for(j=roi[1];j<roi[3];j++){
      var idx = (j * dst.width + i) * 4;

      for(k=0;k<IP_A;k++){
        if( ip_is_in_range( dst.data[idx+k], ch_mask[k] ) ){
          dst.data[idx+k] = Math.min( dst.data[idx+k]+val, 255 );
        }
      }
    }
  }
}
var ip_darken = function(dst, val){
  ip_lighten( dst, -val );
}
<commit_msg>Update ip_p.js<commit_after>/*
  p.js
*/

var ip_is_in_range = function(val, range){
  if( range[0] <= val && range[1] >= val )
    return true;
  else
    return false;
}

var ip_lighten = function(dst, val){
  var roi = ip_get_roi( dst );
  var ch_mask = [];

  for(i=0;i<4;i++){
    ch_mask[i] = ip_get_channel_mask( dst, i );
  }

  for(i=roi[0];i<roi[2];i++){
    for(j=roi[1];j<roi[3];j++){
      var idx = (j * dst.width + i) * 4;

      for(k=0;k<IP_A;k++){
        if( ip_is_in_range( dst.data[idx+k], ch_mask[k] ) ){
          dst.data[idx+k] = Math.min( dst.data[idx+k]+val, 255 );
        }
      }
    }
  }
}
var ip_darken = function(dst, val){
var roi = ip_get_roi( dst );
  var ch_mask = [];

  for(i=0;i<4;i++){
    ch_mask[i] = ip_get_channel_mask( dst, i );
  }

  for(i=roi[0];i<roi[2];i++){
    for(j=roi[1];j<roi[3];j++){
      var idx = (j * dst.width + i) * 4;

      for(k=0;k<IP_A;k++){
        if( ip_is_in_range( dst.data[idx+k], ch_mask[k] ) ){
          dst.data[idx+k] = Math.max( dst.data[idx+k]-val, 0 );
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/*jslint es5: true, indent: 2, nomen: true, maxlen: 120 */
/*global module, require */

////////////////////////////////////////////////////////////////////////////////
/// @brief Foxx queues
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Alan Plum
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var QUEUE_MANAGER_PERIOD = 1000, // in ms
  _ = require('underscore'),
  tasks = require('org/arangodb/tasks'),
  arangodb = require('org/arangodb'),
  db = arangodb.db,
  queueMap,
  queues,
  Queue,
  worker;

queueMap = Object.create(null);

queues = {
  _jobTypes: Object.create(null),
  get: function (key) {
    'use strict';
    if (!db._queues.exists(key)) {
      throw new Error('Queue does not exist: ' + key);
    }
    if (!queueMap[key]) {
      queueMap[key] = new Queue(key);
    }
    return queueMap[key];
  },
  create: function (key, maxWorkers) {
    'use strict';
    try {
      db._queues.save({_key: key, maxWorkers: maxWorkers || 1});
    } catch (err) {
      if (!err instanceof arangodb.ArangoError ||
          err.errorNum !== arangodb.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED) {
        throw err;
      }
      if (maxWorkers) {
        db._queues.update(key, {maxWorkers: maxWorkers});
      }
    }
    if (!queueMap[key]) {
      queueMap[key] = new Queue(key);
    }
    return queueMap[key];
  },
  destroy: function (key) {
    'use strict';
    var result = false;
    db._executeTransaction({
      collections: {
        read: ['_queues'],
        write: ['_queues']
      },
      action: function () {
        if (db._queues.exists(key)) {
          db._queues.delete(key);
          result = true;
        }
      }
    });
    return result;
  },
  registerJobType: function (type, opts) {
    'use strict';
    if (typeof opts === 'function') {
      opts = {execute: opts};
    }
    if (typeof opts.execute !== 'function') {
      throw new Error('Must provide a function to execute!');
    }
    var cfg = _.extend({maxFailures: 0}, opts);
    if (cfg.maxFailures < 0) {
      cfg.maxFailures = Infinity;
    }
    queues._jobTypes[type] = cfg;
  }
};

Queue = function Queue(name) {
  'use strict';
  Object.defineProperty(this, 'name', {
    get: function () {
      return name;
    },
    set: function (value) {
      throw new Error('name is not mutable');
    },
    configurable: false,
    enumerable: true
  });
};

_.extend(Queue.prototype, {
  push: function (type, data) {
    'use strict';
    if (typeof type !== 'string') {
      throw new Error('Must pass a job type!');
    }
    db._jobs.save({
      status: 'pending',
      queue: this.name,
      type: type,
      failures: 0,
      data: data
    });
    return db._jobs.byExample({
      status: 'pending',
      queue: this.name
    }).count();
  }
});

queues._worker = {
  work: function (job) {
    'use strict';
    var db = require('org/arangodb').db,
      queues = require('org/arangodb/foxx').queues,
      console = require('console'),
      cfg = queues._jobTypes[job.type],
      failed = false;

    if (!cfg) {
      console.warn('Unknown job type for job ' + job._key + ':', job.type);
      db._jobs.update(job._key, {status: 'pending'});
      return;
    }

    try {
      cfg.execute(job.data);
    } catch (err) {
      console.error('Job ' + job._key + ' failed:', err);
      failed = true;
    }
    db._jobs.update(job._key, failed ? {
      failures: job.failures + 1,
      status: (job.failures + 1 > cfg.maxFailures) ? 'failed' : 'pending'
    } : {status: 'complete'});
  }
};

queues._manager = {
  manage: function () {
    'use strict';
    var _ = require('underscore'),
      db = require('org/arangodb').db,
      queues = require('org/arangodb/foxx').queues,
      tasks = require('org/arangodb/tasks');

    db._executeTransaction({
      collections: {
        read: ['_queues', '_jobs'],
        write: ['_jobs']
      },
      action: function () {
        db._queues.all().toArray().forEach(function (queue) {
          var numBusy = db._jobs.byExample({
            queue: queue._key,
            status: 'progress'
          }).count();
          if (numBusy >= queue.maxWorkers) {
            return;
          }
          db._jobs.byExample({
            queue: queue._key,
            status: 'pending'
          }).limit(queue.maxWorkers - numBusy).toArray().forEach(function (doc) {
            db._jobs.update(doc, {status: 'progress'});
            tasks.register({
              command: queues._worker.work,
              params: _.extend({}, doc, {status: 'progress'}),
              offset: 0
            });
          });
        });
      }
    });
  },
  run: function () {
    'use strict';
    db._jobs.updateByExample({status: 'progress'}, {status: 'pending'});
    tasks.register({command: queues._manager.manage, period: QUEUE_MANAGER_PERIOD / 1000});
  }
};

queues.create('default', 1);

module.exports = queues;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
// End:
<commit_msg>Renamed queues.destroy => queues.delete.<commit_after>/*jslint es5: true, indent: 2, nomen: true, maxlen: 120 */
/*global module, require */

////////////////////////////////////////////////////////////////////////////////
/// @brief Foxx queues
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Alan Plum
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var QUEUE_MANAGER_PERIOD = 1000, // in ms
  _ = require('underscore'),
  tasks = require('org/arangodb/tasks'),
  arangodb = require('org/arangodb'),
  db = arangodb.db,
  queueMap,
  queues,
  Queue,
  worker;

queueMap = Object.create(null);

queues = {
  _jobTypes: Object.create(null),
  get: function (key) {
    'use strict';
    if (!db._queues.exists(key)) {
      throw new Error('Queue does not exist: ' + key);
    }
    if (!queueMap[key]) {
      queueMap[key] = new Queue(key);
    }
    return queueMap[key];
  },
  create: function (key, maxWorkers) {
    'use strict';
    try {
      db._queues.save({_key: key, maxWorkers: maxWorkers || 1});
    } catch (err) {
      if (!err instanceof arangodb.ArangoError ||
          err.errorNum !== arangodb.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED) {
        throw err;
      }
      if (maxWorkers) {
        db._queues.update(key, {maxWorkers: maxWorkers});
      }
    }
    if (!queueMap[key]) {
      queueMap[key] = new Queue(key);
    }
    return queueMap[key];
  },
  delete: function (key) {
    'use strict';
    var result = false;
    db._executeTransaction({
      collections: {
        read: ['_queues'],
        write: ['_queues']
      },
      action: function () {
        if (db._queues.exists(key)) {
          db._queues.delete(key);
          result = true;
        }
      }
    });
    return result;
  },
  registerJobType: function (type, opts) {
    'use strict';
    if (typeof opts === 'function') {
      opts = {execute: opts};
    }
    if (typeof opts.execute !== 'function') {
      throw new Error('Must provide a function to execute!');
    }
    var cfg = _.extend({maxFailures: 0}, opts);
    if (cfg.maxFailures < 0) {
      cfg.maxFailures = Infinity;
    }
    queues._jobTypes[type] = cfg;
  }
};

Queue = function Queue(name) {
  'use strict';
  Object.defineProperty(this, 'name', {
    get: function () {
      return name;
    },
    set: function (value) {
      throw new Error('name is not mutable');
    },
    configurable: false,
    enumerable: true
  });
};

_.extend(Queue.prototype, {
  push: function (type, data) {
    'use strict';
    if (typeof type !== 'string') {
      throw new Error('Must pass a job type!');
    }
    db._jobs.save({
      status: 'pending',
      queue: this.name,
      type: type,
      failures: 0,
      data: data
    });
    return db._jobs.byExample({
      status: 'pending',
      queue: this.name
    }).count();
  }
});

queues._worker = {
  work: function (job) {
    'use strict';
    var db = require('org/arangodb').db,
      queues = require('org/arangodb/foxx').queues,
      console = require('console'),
      cfg = queues._jobTypes[job.type],
      failed = false;

    if (!cfg) {
      console.warn('Unknown job type for job ' + job._key + ':', job.type);
      db._jobs.update(job._key, {status: 'pending'});
      return;
    }

    try {
      cfg.execute(job.data);
    } catch (err) {
      console.error('Job ' + job._key + ' failed:', err);
      failed = true;
    }
    db._jobs.update(job._key, failed ? {
      failures: job.failures + 1,
      status: (job.failures + 1 > cfg.maxFailures) ? 'failed' : 'pending'
    } : {status: 'complete'});
  }
};

queues._manager = {
  manage: function () {
    'use strict';
    var _ = require('underscore'),
      db = require('org/arangodb').db,
      queues = require('org/arangodb/foxx').queues,
      tasks = require('org/arangodb/tasks');

    db._executeTransaction({
      collections: {
        read: ['_queues', '_jobs'],
        write: ['_jobs']
      },
      action: function () {
        db._queues.all().toArray().forEach(function (queue) {
          var numBusy = db._jobs.byExample({
            queue: queue._key,
            status: 'progress'
          }).count();
          if (numBusy >= queue.maxWorkers) {
            return;
          }
          db._jobs.byExample({
            queue: queue._key,
            status: 'pending'
          }).limit(queue.maxWorkers - numBusy).toArray().forEach(function (doc) {
            db._jobs.update(doc, {status: 'progress'});
            tasks.register({
              command: queues._worker.work,
              params: _.extend({}, doc, {status: 'progress'}),
              offset: 0
            });
          });
        });
      }
    });
  },
  run: function () {
    'use strict';
    db._jobs.updateByExample({status: 'progress'}, {status: 'pending'});
    tasks.register({command: queues._manager.manage, period: QUEUE_MANAGER_PERIOD / 1000});
  }
};

queues.create('default', 1);

module.exports = queues;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
// End:
<|endoftext|>


<commit_before>
module.exports = writeShim
writeShim.ifExists = shimIfExists

var fs = require("./graceful-fs")
  , path = require("path")
  , relativize = require("./relativize")
  , mkdir = require("./mkdir-p")

function shimIfExists (from, to, dep, cb) {
  if (!cb) cb = dep, dep = false
  fs.stat(from, function (er) {
    if (er) return cb()
    writeShim(from, to, dep, cb)
  })
}

function writeShim (from, to, dep, cb) {
  if (!cb) cb = dep, dep = false
  if (dep) dep = JSON.stringify(relativize(dep, to))
  from = JSON.stringify(relativize(from, to).replace(/\.(js|node)$/, ''))
  var nodePath = process.execPath
               || path.join(process.installPrefix, "bin", "node")
    , code = "#!"+nodePath+"\n"
           + "// generated by npm, please don't touch!\n"
           + (dep ? "var depMet = require.paths.indexOf("+dep+") !== -1\n"
                  + "if (!depMet) require.paths.unshift("+dep+")\n"
             : ""
             )
           + "module.exports = require(" + from + ")\n"
           + ( dep ? "if (!depMet) {\n" // get the possibly-changed location
                   + "  var i = require.paths.indexOf("+dep+")\n"
                   + "  if (i !== -1) require.paths.splice(i, 1)\n"
                   + "}"
             : "\n"
             )
  mkdir(path.dirname(to), function (er) {
    if (er) return cb(er)
    fs.writeFile(to, code, "ascii", function (er, ok) {
      if (er) return cb(er)
      fs.chmod(to, 0755, cb)
    })
  })
}
<commit_msg>Make the dep path absolute at run-time<commit_after>
module.exports = writeShim
writeShim.ifExists = shimIfExists

var fs = require("./graceful-fs")
  , path = require("path")
  , relativize = require("./relativize")
  , mkdir = require("./mkdir-p")
  , log = require("./log")

function shimIfExists (from, to, dep, cb) {
  if (!cb) cb = dep, dep = false
  fs.stat(from, function (er) {
    if (er) return cb()
    writeShim(from, to, dep, cb)
  })
}

function writeShim (from, to, dep, cb) {
  if (!cb) cb = dep, dep = false
  log.silly([from,to], "writeShim")
  if (dep) dep = relativize(dep, to)
  from = relativize(from, to).replace(/\.(js|node)$/, '')
  var nodePath = process.execPath
               || path.join(process.installPrefix, "bin", "node")
    , code = "#!"+nodePath+"\n"
           + "// generated by npm, please don't touch!\n"
           + (dep ? "var dep = require('path').join(__dirname, "
                  + JSON.stringify(dep || null) + ")\n"
                  + "var depMet = require.paths.indexOf(dep) !== -1\n"
                  : "")
           + "var from = "+JSON.stringify(from)+"\n"
           + "\n"
           + (dep ? "if (!depMet) require.paths.unshift(dep)\n" : "")
           + "module.exports = require(from)\n"
           + "\n"
           + (dep ? "if (!depMet) {\n"
                  + "  var i = require.paths.indexOf(dep)\n"
                  + "  if (i !== -1) require.paths.splice(i, 1)\n"
                  + "}\n"
                  : "")

  mkdir(path.dirname(to), function (er) {
    if (er) return cb(er)
    fs.writeFile(to, code, "ascii", function (er, ok) {
      if (er) return cb(er)
      fs.chmod(to, 0755, cb)
    })
  })
}
<|endoftext|>


<commit_before>'use strict';

/*
 * Module dependencies
 */

var $ = require('jbone');
var _ = require('underscore');
var Mei = require('./mei');
var Goban = require('../model/goban');
var GobanView = require('./goban');

/*
 * GobanOption : Private View
 */

var GobanOption = GobanView.extend();

/* Bind select event */

GobanOption.prototype.events = {
  'mousedown': 'select'
};

/* Listen to form change event */

GobanOption.prototype.initialize = function (opts) {
  _.bindAll(this, 'render', 'select');
  GobanView.prototype.initialize.apply(this, [
    _.extend({geom: {width: 250}}, opts)
  ]);
  window.theEl = this.$el;
  this.el.classList.add('small');
  this.form = opts.form;
  this.form.on('change', this.render);
};

/* Update form on select */

GobanOption.prototype.select = function () {
  this.form.set('size', this.model.get('size'));
};

/* Set element as selected if size of form is current */

GobanOption.prototype.render = function () {
  if (this.form.get('size') === this.model.get('size')) {
    this.el.classList.add('selected');
  } else {
    this.el.classList.remove('selected');
  }
  return this;
};

/*
 * GobanChoiceView
 */

var GobanChoiceView = module.exports = Mei.View.extend();

GobanChoiceView.prototype.className = 'mei-goban-choice';

/* Instanciate the 3 options, forwarding the form model */

GobanChoiceView.prototype.initialize = function () {
  _.bindAll(this, 'render');
  this.model.on('change', this.render);
  this.options = {
    19: new GobanOption({model: new Goban({size: 19}), form: this.model}),
    13: new GobanOption({model: new Goban({size: 13}), form: this.model}),
    9: new GobanOption({model: new Goban({size: 9}), form: this.model})
  };

  _
    .chain(this.options)
    .keys()
    .reverse()
    .each(function (key) {
      this.$el.append(this.options[key].render().el);
    }, this);
};
<commit_msg>fix goban option size in creation board<commit_after>'use strict';

/*
 * Module dependencies
 */

var $ = require('jbone');
var _ = require('underscore');
var Mei = require('./mei');
var Goban = require('../model/goban');
var GobanView = require('./goban');

/*
 * GobanOption : Private View
 */

var GobanOption = GobanView.extend();

/* Bind select event */

GobanOption.prototype.events = {
  'mousedown': 'select'
};

/* Listen to form change event */

GobanOption.prototype.initialize = function (opts) {
  _.bindAll(this, 'render', 'select');
  GobanView.prototype.initialize.apply(this, [
    _.extend({geom: {width: 250, height: 250}}, opts)
  ]);
  this.meiGoban.set('width', 250);
  window.theEl = this.$el;
  this.el.classList.add('small');
  this.form = opts.form;
  this.form.on('change', this.render);
};

/* Update form on select */

GobanOption.prototype.select = function () {
  this.form.set('size', this.model.get('size'));
};

/* Set element as selected if size of form is current */

GobanOption.prototype.render = function () {
  if (this.form.get('size') === this.model.get('size')) {
    this.el.classList.add('selected');
  } else {
    this.el.classList.remove('selected');
  }
  return this;
};

/*
 * GobanChoiceView
 */

var GobanChoiceView = module.exports = Mei.View.extend();

GobanChoiceView.prototype.className = 'mei-goban-choice';

/* Instanciate the 3 options, forwarding the form model */

GobanChoiceView.prototype.initialize = function () {
  _.bindAll(this, 'render');
  this.model.on('change', this.render);
  this.options = {
    19: new GobanOption({model: new Goban({size: 19}), form: this.model}),
    13: new GobanOption({model: new Goban({size: 13}), form: this.model}),
    9: new GobanOption({model: new Goban({size: 9}), form: this.model})
  };

  _
    .chain(this.options)
    .keys()
    .reverse()
    .each(function (key) {
      this.$el.append(this.options[key].render().el);
    }, this);
};
<|endoftext|>


<commit_before>import Events from '../events/Events';

class GuideLayer extends Events {
    constructor() {
        super();
    }

    getLabelBBox(node, selector) {
        selector = selector || '.guide_label';

        var parentNode = d3.select(node.parentNode);
        var textNode = parentNode.select(selector);
        var bbox = textNode.node().getBBox();
        return bbox;
    }

    drawXLineGuide(dataSelection, enterSelection, chartOpts, guideOpts) {
        var self = this;
        var opts = this._createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts);
        guideOpts = guideOpts || {};

        opts.enterSelection.append('line')
            .attr('class', 'guide_line guide_line_x');

        opts.enterSelection.append('rect')
            .attr('class', 'guide_label_bg guide_label_x_bg');

        opts.enterSelection.append('text')
            .attr('class', 'guide_label guide_label_x')
            .attr('font-size', opts.fontSize)
            .attr('height', opts.fontSize)
            .attr('text-anchor', 'middle');

        var createXLabelText = opts.createLabelText || function(d) { return opts.xLabelFormat(d[opts.xProp]); };

        opts.dataSelection.selectAll('.guide_label_x')
            .text(createXLabelText)
            .attr('x', function(d) {
                var x              = opts.xRange(d[opts.xProp]);
                var labelWidth     = this.getBBox().width;
                var halfLabelWidth = labelWidth / 2;
                var labelLeft      = x - halfLabelWidth;
                var labelRight     = x + halfLabelWidth;
                var xMax           = opts.width - halfLabelWidth - opts.margins.right;

                if (labelRight > xMax) {
                    x = xMax;
                } else if (labelLeft < opts.xMin) {
                    x = opts.xMin + halfLabelWidth;
                }

                return x;
            })
            .attr('y', function() {
                return guideOpts.labelY || opts.bottom + this.getBBox().height;
            });

        opts.dataSelection.selectAll('.guide_label_x_bg').each(function() {
            var labelBBox = self.getLabelBBox(this, '.guide_label_x');
            // + 1's are for centering
            d3.select(this)
                .attr('x', labelBBox.x - (opts.labelPadding / 2))
                .attr('y', labelBBox.y + 1)
                .attr('width', labelBBox.width + opts.labelPadding)
                .attr('height', opts.fontSize + 1);
        });

        var line = opts.dataSelection
                        .selectAll('.guide_line_x')
                        // For the line to update, data must be returned as an array
                        .data(function(d, i) { return [d]; });

        if (opts.onLineEnter) {
            opts.onLineEnter(line);
        } else {
            line
                .attr('x1', opts.constrainX)
                .attr('y1', opts.constrainY)
                .attr('x2', opts.constrainX)
                .attr('y2', opts.bottom);
        }
    }

    drawYLineGuide(dataSelection, enterSelection, chartOpts, guideOpts) {
        var self = this;
        var opts = this._createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts);

        opts.enterSelection.append('line')
            .attr('class', 'guide_line guide_line_y');

        opts.enterSelection.append('rect')
            .attr('class', 'guide_label_bg guide_label_y_bg');

        opts.enterSelection.append('text')
            .attr('class', 'guide_label guide_label_y')
            .attr('font-size', opts.fontSize)
            .attr('height', opts.fontSize)
            .attr('text-anchor', 'end');

        var createYLabelText = opts.createLabelText || function(d) { return opts.yLabelFormat(d[opts.yProp]); };
        opts.dataSelection.selectAll('.guide_label_y')
            .text(createYLabelText)
            .attr('x', opts.margins.left - opts.labelMargin - opts.labelPadding)
            .attr('y', d => opts.yRange(d[opts.yProp]) + (opts.fontSize / 2));

        opts.dataSelection.selectAll('.guide_label_y_bg').each(function() {
            var labelBBox = self.getLabelBBox(this, '.guide_label_y');
            // + 1's are for centering
            d3.select(this)
                .attr('x', labelBBox.x - (opts.labelPadding / 2))
                .attr('y', labelBBox.y + 1)
                .attr('width', labelBBox.width + opts.labelPadding)
                .attr('height', opts.fontSize + 1);
        });

        var line = opts.dataSelection
                    .selectAll('.guide_line_y')
                    // For the line to update, data must be returned as an array
                    .data(function(d, i) { return [d]; });

        if (opts.onLineEnter) {
            opts.onLineEnter(line);
        } else {
            line
                .attr('x1', opts.xMin)
                .attr('y1', opts.constrainY)
                .attr('x2', opts.constrainX)
                .attr('y2', opts.constrainY);
        }
    }

    getPathY(length, pathNode, accuracyPx) {
        var pathLength = pathNode.getTotalLength();

        var y = 0;

        if (length <= 0) {
            y = pathNode.getPointAtLength(0).y;
            return y;
        }

        if (length >= pathLength) {
            y = pathNode.getPointAtLength(pathLength).y;
            return y;
        }

        // Increment to crawl the path at. Smaller is more accurate, but more intensive. Usually 1 is
        // fine (and extremely accurate), but you might want to increase this if you notice performance issues.
        accuracyPx = accuracyPx || 1;

        for (var i = 0; i < pathLength; i = i + accuracyPx) {
            var pos = pathNode.getPointAtLength(i);
            if (pos.x >= length) {
                y = pos.y;
                break;
            }
        }

        return y;
    }

    _createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts) {
        guideOpts = guideOpts || {};

        var width   = chartOpts.width;
        var height  = chartOpts.height;
        var margins = chartOpts.margins;
        var bottom  = height - margins.bottom;

        var opts = {
            dataSelection   : dataSelection,
            enterSelection  : enterSelection,
            xRange          : chartOpts.xRange,
            yRange          : chartOpts.yRange,
            fontSize        : 14,
            labelMargin     : 2,
            labelPadding    : 5,
            width           : width,
            height          : height,
            margins         : margins,
            bottom          : bottom,
            xMin            : margins.left,
            xMax            : width - margins.right,
            yMin            : margins.top,
            yMax            : bottom,
            xLabelFormat    : chartOpts.xLabelFormat,
            yLabelFormat    : chartOpts.yLabelFormat,
            xProp           : guideOpts.xProp || 'x',
            yProp           : guideOpts.yProp || 'y',
            createLabelText : guideOpts.createLabelText,
            onLineEnter     : guideOpts.onLineEnter
        };

        opts.constrainX = function(d) {
            var x = opts.xRange(d[opts.xProp]);
            if (x > opts.xMax) {
                x = opts.xMax;
            } else if (x < opts.xMin) {
                x = opts.xMin;
            }
            return x;
        };

        opts.constrainY = function(d) {
            var y = opts.yRange(d[opts.yProp]);
            if (y > opts.yMax) {
                y = opts.yMax;
            } else if (y < opts.yMin) {
                y = opts.yMin;
            }
            return y;
        };

        return opts;
    }
}

export default GuideLayer;
<commit_msg>keep guide data locked and optimize the hell out of getPathY<commit_after>import Events from '../events/Events';

class GuideLayer extends Events {
    constructor() {
        super();
    }

    getLabelBBox(node, selector) {
        selector = selector || '.guide_label';

        var parentNode = d3.select(node.parentNode);
        var textNode = parentNode.select(selector);
        var bbox = textNode.node().getBBox();
        return bbox;
    }

    drawXLineGuide(dataSelection, enterSelection, chartOpts, guideOpts) {
        var self = this;
        var opts = this._createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts);
        guideOpts = guideOpts || {};

        opts.enterSelection.append('line')
            .attr('class', 'guide_line guide_line_x');

        opts.enterSelection.append('rect')
            .attr('class', 'guide_label_bg guide_label_x_bg');

        opts.enterSelection.append('text')
            .attr('class', 'guide_label guide_label_x')
            .attr('font-size', opts.fontSize)
            .attr('height', opts.fontSize)
            .attr('text-anchor', 'middle');

        var guideData = opts.dataSelection.data();
        var createXLabelText = opts.createLabelText || function(d) { return opts.xLabelFormat(d[opts.xProp]); };

        opts.dataSelection.selectAll('.guide_label_x')
            .text(createXLabelText)
            .data(function(d, i) { return [guideData[i]]; })
            .attr('x', function(d) {
                var x              = opts.xRange(d[opts.xProp]);
                var labelWidth     = this.getBBox().width;
                var halfLabelWidth = labelWidth / 2;
                var labelLeft      = x - halfLabelWidth;
                var labelRight     = x + halfLabelWidth;
                var xMax           = opts.width - halfLabelWidth - opts.margins.right;

                if (labelRight > xMax) {
                    x = xMax;
                } else if (labelLeft < opts.xMin) {
                    x = opts.xMin + halfLabelWidth;
                }

                return x;
            })
            .attr('y', function() {
                return guideOpts.labelY || opts.bottom + this.getBBox().height;
            });

        opts.dataSelection.selectAll('.guide_label_x_bg').each(function() {
            var labelBBox = self.getLabelBBox(this, '.guide_label_x');
            // + 1's are for centering
            d3.select(this)
                .attr('x', labelBBox.x - (opts.labelPadding / 2))
                .attr('y', labelBBox.y + 1)
                .attr('width', labelBBox.width + opts.labelPadding)
                .attr('height', opts.fontSize + 1);
        });

        var line = opts.dataSelection
                        .selectAll('.guide_line_x')
                        .data(function(d, i) { return [guideData[i]]; });

        if (opts.onLineEnter) {
            opts.onLineEnter(line);
        } else {
            line
                .attr('x1', opts.constrainX)
                .attr('y1', opts.constrainY)
                .attr('x2', opts.constrainX)
                .attr('y2', opts.bottom);
        }
    }

    drawYLineGuide(dataSelection, enterSelection, chartOpts, guideOpts) {
        var self = this;
        var opts = this._createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts);

        opts.enterSelection.append('line')
            .attr('class', 'guide_line guide_line_y');

        opts.enterSelection.append('rect')
            .attr('class', 'guide_label_bg guide_label_y_bg');

        opts.enterSelection.append('text')
            .attr('class', 'guide_label guide_label_y')
            .attr('font-size', opts.fontSize)
            .attr('height', opts.fontSize)
            .attr('text-anchor', 'end');

        var guideData = opts.dataSelection.data();
        var createYLabelText = opts.createLabelText || function(d) { return opts.yLabelFormat(d[opts.yProp]); };

        opts.dataSelection.selectAll('.guide_label_y')
            .data(function(d, i) { return [guideData[i]]; })
            .text(createYLabelText)
            .attr('x', opts.margins.left - opts.labelMargin - opts.labelPadding)
            .attr('y', d => opts.yRange(d[opts.yProp]) + (opts.fontSize / 2));

        opts.dataSelection.selectAll('.guide_label_y_bg').each(function() {
            var labelBBox = self.getLabelBBox(this, '.guide_label_y');
            // + 1's are for centering
            d3.select(this)
                .attr('x', labelBBox.x - (opts.labelPadding / 2))
                .attr('y', labelBBox.y + 1)
                .attr('width', labelBBox.width + opts.labelPadding)
                .attr('height', opts.fontSize + 1);
        });

        var line = opts.dataSelection
                    .selectAll('.guide_line_y')
                    .data(function(d, i) { return [guideData[i]]; });

        if (opts.onLineEnter) {
            opts.onLineEnter(line);
        } else {
            line
                .attr('x1', opts.xMin)
                .attr('y1', opts.constrainY)
                .attr('x2', opts.constrainX)
                .attr('y2', opts.constrainY);
        }
    }

    getPathY(length, pathNode, accuracyPx, numTestChunks) {
        var pathLength = pathNode.getTotalLength();

        var y = 0;

        if (length <= 0) {
            y = pathNode.getPointAtLength(0).y;
            return y;
        }

        if (length >= pathLength) {
            y = pathNode.getPointAtLength(pathLength).y;
            return y;
        }

        var startLength = 0;

        // First, make some very broad checks to figure out which "chunk" to start searching from...
        // Here, we split the path into sections and test to see if the requested length is within
        // them. This gives us a decent starting point so we're not iterating over the entire path
        // unnecessarily.
        numTestChunks = numTestChunks || 8;
        var testChunkSize = pathLength / numTestChunks;

        for (var i = 1; i <= numTestChunks; i++){
            var testChunkStart = testChunkSize * i;
            if (pathNode.getPointAtLength(testChunkStart).x > length) {
                startLength = testChunkStart - testChunkSize;
                break;
            }
        }

        // Increment to crawl the path at. Smaller is more accurate, but more intensive. Usually 1 is
        // fine (and extremely accurate), but you might want to increase this if you notice performance issues.
        accuracyPx = accuracyPx || 1;

        for (var j = startLength; j < pathLength; j = j + accuracyPx) {
            var pos = pathNode.getPointAtLength(j);
            if (pos.x >= length) {
                y = pos.y;
                break;
            }
        }

        return y;
    }

    _createCommonGuideOpts(dataSelection, enterSelection, chartOpts, guideOpts) {
        guideOpts = guideOpts || {};

        var width   = chartOpts.width;
        var height  = chartOpts.height;
        var margins = chartOpts.margins;
        var bottom  = height - margins.bottom;

        var opts = {
            dataSelection   : dataSelection,
            enterSelection  : enterSelection,
            xRange          : chartOpts.xRange,
            yRange          : chartOpts.yRange,
            fontSize        : 14,
            labelMargin     : 2,
            labelPadding    : 5,
            width           : width,
            height          : height,
            margins         : margins,
            bottom          : bottom,
            xMin            : margins.left,
            xMax            : width - margins.right,
            yMin            : margins.top,
            yMax            : bottom,
            xLabelFormat    : chartOpts.xLabelFormat,
            yLabelFormat    : chartOpts.yLabelFormat,
            xProp           : guideOpts.xProp || 'x',
            yProp           : guideOpts.yProp || 'y',
            createLabelText : guideOpts.createLabelText,
            onLineEnter     : guideOpts.onLineEnter
        };

        opts.constrainX = function(d) {
            var x = opts.xRange(d[opts.xProp]);
            if (x > opts.xMax) {
                x = opts.xMax;
            } else if (x < opts.xMin) {
                x = opts.xMin;
            }
            return x;
        };

        opts.constrainY = function(d) {
            var y = opts.yRange(d[opts.yProp]);
            if (y > opts.yMax) {
                y = opts.yMax;
            } else if (y < opts.yMin) {
                y = opts.yMin;
            }
            return y;
        };

        return opts;
    }
}

export default GuideLayer;
<|endoftext|>


<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 */

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(["require", "../../deep", "../utils", "./dom-sheeter","../errors"], function(require, deep, utils, DomSheeter, errors){

/*

*/
	deep.ui.directives = {
		from: function(node, context, path) {
			var $ = deep.context.$;
			var controller = this;
			return deep.getAll(path.split(","))
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					if (typeof result === 'function')
						$(node).html(result({
							_ctrl: controller
						}));
					else
						$(node).html(result);
					return deep.ui.enhance(controller, node, context);
				});
		},
		sheets: function(node, context, from) {
			var $ = deep.context.$;
			var controller = this;
			return deep.getAll(from.split(","))
				.done(function(results) {
					return deep.sheet(deep.utils.compile(results), node);
				});
		},
		control: function(node, context, from) {
			// console.log("directives control : ", node, context, from);
			var $ = deep.context.$,
				controller = this;
			var d = deep({});
			return d.up.apply(d, from.split(","))
			.done(function(ctrl) {
				if (!ctrl)
					return deep.errors.Internal("dp-control has retrieved nothing");
				var config = ctrl.config || {};
				if (config.scope && config.scope != "both")
					if (!deep.isBrowser) {
						if (config.scope != "server")
							return;
					}
					else if (config.scope != "browser")
						return;
				if(controller && controller.addSubControlled)
					controller.addSubControlled(ctrl);
				ctrl.placed = function() {
					return node;
				};
				return ctrl.refresh();
			});
		},
		click: function(node, context, action, args) {
			if (!deep.isBrowser)
				return;
			var controller = this;
			if(!controller)
				return;
			if (args)
				args = args.split(",");
			else
				args = []
			var $ = deep.context.$;
			$(node).click(function(e) {
				e.preventDefault();
				if(!controller[action])
					throw new Error("no method found in control with : "+action);
				controller[action].apply(controller, args);
				return false;
			});
		}
	}

	var cache = {};

	deep.ui.parseDirectives = function(directives){
		if(cache[directives])
			return cache[directives];
		var splitted = directives.split(/[\s]+/);
		var sheeter = DomSheeter.create();
		for(var i = 0, len = splitted.length; i < len; ++i)
		{
			var d = splitted[i],
				pI = d.indexOf("("),
				args = [],
				enhancer = d;
			if(pI > -1)
			{
				enhancer = d.substring(0,pI);
				args = d.substring(pI+1, d.length-1).split(",");
			}
			if(!deep.ui.directives[enhancer])
			{
				deep.warn("missing ui enhancer : ",enhancer);
				continue;
			}
			args.unshift(enhancer);
			sheeter.enhance.apply(sheeter, args);
		}
		cache[directives] = sheeter;
		return sheeter;
	};
	deep.ui.enhance = function(ctrl, selector, context) {
		var promises = [];
		ctrl = ctrl || {};
		deep.context.$(selector)
		.find(".dp-enhance")
		.each(function(index, element) {
			$(this).removeClass("dp-enhance");
			var scope = $(this).attr("dp-scope");
			if (scope && scope != 'both' && scope != 'browser' && deep.isBrowser)
				return;
			var enhancements = $(this).attr("dp-enhancements");
			if(enhancements)
			{
				$(this).removeAttr("dp-enhancements");
				var r = deep.ui.parseDirectives(enhancements).call(ctrl, this, context);
				if(r && r.then)
					promises.push(r);
			}
		});
		if (promises.length === 0)
			return null;
		return deep.all(promises);
	}
});<commit_msg>work on remove dp-* tags<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 */

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}
define(["require", "../../deep", "../utils", "./dom-sheeter","../errors"], function(require, deep, utils, DomSheeter, errors){

/*

*/
	deep.ui.directives = {
		from: function(node, context, path) {
			var $ = deep.context.$;
			var controller = this;
			return deep.getAll(path.split(","))
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					if (typeof result === 'function')
						$(node).html(result({
							_ctrl: controller
						}));
					else
						$(node).html(result);
					return deep.ui.enhance(controller, node, context);
				});
		},
		sheets: function(node, context, from) {
			var $ = deep.context.$;
			var controller = this;
			return deep.getAll(from.split(","))
				.done(function(results) {
					return deep.sheet(deep.utils.compile(results), node);
				});
		},
		control: function(node, context, from) {
			// console.log("directives control : ", node, context, from);
			var $ = deep.context.$,
				controller = this;
			var d = deep({});
			return d.up.apply(d, from.split(","))
			.done(function(ctrl) {
				if (!ctrl)
					return deep.errors.Internal("dp-control has retrieved nothing");
				var config = ctrl.config || {};
				if (config.scope && config.scope != "both")
					if (!deep.isBrowser) {
						if (config.scope != "server")
							return;
					}
					else if (config.scope != "browser")
						return;
				if(controller && controller.addSubControlled)
					controller.addSubControlled(ctrl);
				ctrl.placed = function() {
					return node;
				};
				return ctrl.refresh();
			});
		},
		click: function(node, context, action, args) {
			if (!deep.isBrowser)
				return;
			var controller = this;
			if(!controller)
				return;
			if (args)
				args = args.split(",");
			else
				args = []
			var $ = deep.context.$;
			$(node).click(function(e) {
				e.preventDefault();
				if(!controller[action])
					throw new Error("no method found in control with : "+action);
				controller[action].apply(controller, args);
				return false;
			});
		}
	}

	var cache = {};

	deep.ui.parseDirectives = function(directives){
		if(cache[directives])
			return cache[directives];
		var splitted = directives.split(/[\s]+/);
		var sheeter = DomSheeter.create();
		for(var i = 0, len = splitted.length; i < len; ++i)
		{
			var d = splitted[i],
				pI = d.indexOf("("),
				args = [],
				enhancer = d;
			if(pI > -1)
			{
				enhancer = d.substring(0,pI);
				args = d.substring(pI+1, d.length-1).split(",");
			}
			if(!deep.ui.directives[enhancer])
			{
				deep.warn("missing ui enhancer : ",enhancer);
				continue;
			}
			args.unshift(enhancer);
			sheeter.enhance.apply(sheeter, args);
		}
		cache[directives] = sheeter;
		return sheeter;
	};
	deep.ui.enhance = function(ctrl, selector, context) {
		var promises = [];
		ctrl = ctrl || {};
		deep.context.$(selector)
		.find(".dp-enhance")
		.each(function(index, element) {

			var scope = $(this).attr("dp-scope");
			if (scope && scope != 'both' && (scope == 'browser' && !deep.isBrowser) || (scope == 'server' && deep.isBrowser))
				return;
			var enhancements = $(this).attr("dp-enhancements");
			if(deep.isBrowser || (scope == "server" && !deep.isBrowser))
				$(this).removeClass("dp-enhance").removeAttr("dp-enhancements");
			if(enhancements)
			{
				var r = deep.ui.parseDirectives(enhancements).call(ctrl, this, context);
				if(r && r.then)
					promises.push(r);
			}
		});
		if (promises.length === 0)
			return null;
		return deep.all(promises);
	}
});<|endoftext|>