<commit_before>/* global HTMLElement, MutationObserver */
var registerElement = require('./a-register-element').registerElement;
var utils = require('../utils/');
var bind = utils.bind;

/**
 * Base class for A-Frame that manages loading of objects.
 *
 * Nodes can be modified using mixins.
 * Nodes emit a `loaded` event when they and their children have initialized.
 */
module.exports = registerElement('a-node', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value: function () {
        this.hasLoaded = false;
        this.isNode = true;
        this.mixinEls = [];
        this.mixinObservers = {};
      },
      writable: window.debug
    },

    attachedCallback: {
      value: function () {
        var mixins = this.getAttribute('mixin');
        this.sceneEl = this.closestScene();
        this.emit('nodeready', {}, false);
        if (mixins) { this.updateMixins(mixins); }
      },
      writable: window.debug
    },

    attributeChangedCallback: {
      value: function (attr, oldVal, newVal) {
        if (attr === 'mixin') { this.updateMixins(newVal, oldVal); }
      }
    },

   /**
    * Returns the first scene by traversing up the tree starting from and
    * including receiver element.
    */
    closestScene: {
      value: function closest () {
        var element = this;
        while (element) {
          if (element.isScene) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    /**
     * Returns first element matching a selector by traversing up the tree starting
     * from and including receiver element.
     *
     * @param {string} selector - Selector of element to find.
     */
    closest: {
      value: function closest (selector) {
        var matches = this.matches || this.mozMatchesSelector ||
          this.msMatchesSelector || this.oMatchesSelector || this.webkitMatchesSelector;
        var element = this;
        while (element) {
          if (matches.call(element, selector)) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    detachedCallback: {
      value: function () {
        this.hasLoaded = false;
      }
    },

    /**
     * Wait for children to load, if any.
     * Then emit `loaded` event and set `hasLoaded`.
     */
    load: {
      value: function (cb, childFilter) {
        var children;
        var childrenLoaded;
        var self = this;

        if (this.hasLoaded) { return; }

        // Default to waiting for all nodes.
        childFilter = childFilter || function (el) { return el.isNode; };

        // Wait for children to load (if any), then load.
        children = this.getChildren();
        childrenLoaded = children.filter(childFilter).map(function (child) {
          return new Promise(function waitForLoaded (resolve) {
            if (child.hasLoaded) { return resolve(); }
            child.addEventListener('loaded', resolve);
          });
        });

        Promise.all(childrenLoaded).then(function emitLoaded () {
          self.hasLoaded = true;
          if (cb) { cb(); }
          self.emit('loaded', {}, false);
        });
      },
      writable: true
    },

    getChildren: {
      value: function () {
        return Array.prototype.slice.call(this.children, 0);
      }
    },

    updateMixins: {
      value: function (newMixins, oldMixins) {
        var newMixinsIds = newMixins.split(' ');
        var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
        // To determine what listeners will be removed
        var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
        this.mixinEls = [];
        diff.forEach(bind(this.unregisterMixin, this));
        newMixinsIds.forEach(bind(this.registerMixin, this));
      }
    },

    registerMixin: {
      value: function (mixinId) {
        if (!this.sceneEl) { return; }
        var mixinEl = this.sceneEl.querySelector('a-mixin#' + mixinId);
        if (!mixinEl) { return; }
        this.attachMixinListener(mixinEl);
        this.mixinEls.push(mixinEl);
      }
    },

    setAttribute: {
      value: function (attr, newValue) {
        if (attr === 'mixin') { this.updateMixins(newValue); }
        HTMLElement.prototype.setAttribute.call(this, attr, newValue);
      }
    },

    unregisterMixin: {
      value: function (mixinId) {
        var mixinEls = this.mixinEls;
        var mixinEl;
        var i;
        for (i = 0; i < mixinEls.length; ++i) {
          mixinEl = mixinEls[i];
          if (mixinId === mixinEl.id) {
            mixinEls.splice(i, 1);
            break;
          }
        }
        this.removeMixinListener(mixinId);
      }
    },

    removeMixinListener: {
      value: function (mixinId) {
        var observer = this.mixinObservers[mixinId];
        if (!observer) { return; }
        observer.disconnect();
        this.mixinObservers[mixinId] = null;
      }
    },

    attachMixinListener: {
      value: function (mixinEl) {
        var self = this;
        var mixinId = mixinEl.id;
        var currentObserver = this.mixinObservers[mixinId];
        if (!mixinEl) { return; }
        if (currentObserver) { return; }
        var observer = new MutationObserver(function (mutations) {
          var attr = mutations[0].attributeName;
          self.handleMixinUpdate(attr);
        });
        var config = { attributes: true };
        observer.observe(mixinEl, config);
        this.mixinObservers[mixinId] = observer;
      }
    },

    handleMixinUpdate: {
      value: function () { /* no-op */ }
    },

    /**
     * Emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object=} [detail={}]
     *   Custom data to pass as `detail` to the event.
     * @param {Boolean=} [bubbles=true]
     *   Whether the event should bubble.
     */
    emit: {
      value: function (name, detail, bubbles) {
        var self = this;
        detail = detail || {};
        if (bubbles === undefined) { bubbles = true; }
        var data = { bubbles: !!bubbles, detail: detail };
        return name.split(' ').map(function (eventName) {
          return utils.fireEvent(self, eventName, data);
        });
      }
    },

    /**
     * Returns a closure that emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object} detail
     *   Custom data (optional) to pass as `detail` if the event is to
     *   be a `CustomEvent`.
     * @param {Boolean} bubbles
     *   Whether the event should be bubble.
     */
    emitter: {
      value: function (name, detail, bubbles) {
        var self = this;
        return function () {
          self.emit(name, detail, bubbles);
        };
      }
    }
  })
});
<commit_msg>unset hasLoaded on attachedCallback<commit_after>/* global HTMLElement, MutationObserver */
var registerElement = require('./a-register-element').registerElement;
var utils = require('../utils/');
var bind = utils.bind;

/**
 * Base class for A-Frame that manages loading of objects.
 *
 * Nodes can be modified using mixins.
 * Nodes emit a `loaded` event when they and their children have initialized.
 */
module.exports = registerElement('a-node', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value: function () {
        this.hasLoaded = false;
        this.isNode = true;
        this.mixinEls = [];
        this.mixinObservers = {};
      },
      writable: window.debug
    },

    attachedCallback: {
      value: function () {
        var mixins;

        this.hasLoaded = false;
        this.sceneEl = this.closestScene();
        this.emit('nodeready', {}, false);

        mixins = this.getAttribute('mixin');
        if (mixins) { this.updateMixins(mixins); }
      },
      writable: window.debug
    },

    attributeChangedCallback: {
      value: function (attr, oldVal, newVal) {
        if (attr === 'mixin') { this.updateMixins(newVal, oldVal); }
      }
    },

   /**
    * Returns the first scene by traversing up the tree starting from and
    * including receiver element.
    */
    closestScene: {
      value: function closest () {
        var element = this;
        while (element) {
          if (element.isScene) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    /**
     * Returns first element matching a selector by traversing up the tree starting
     * from and including receiver element.
     *
     * @param {string} selector - Selector of element to find.
     */
    closest: {
      value: function closest (selector) {
        var matches = this.matches || this.mozMatchesSelector ||
          this.msMatchesSelector || this.oMatchesSelector || this.webkitMatchesSelector;
        var element = this;
        while (element) {
          if (matches.call(element, selector)) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    detachedCallback: {
      value: function () {
        this.hasLoaded = false;
      }
    },

    /**
     * Wait for children to load, if any.
     * Then emit `loaded` event and set `hasLoaded`.
     */
    load: {
      value: function (cb, childFilter) {
        var children;
        var childrenLoaded;
        var self = this;

        if (this.hasLoaded) { return; }

        // Default to waiting for all nodes.
        childFilter = childFilter || function (el) { return el.isNode; };

        // Wait for children to load (if any), then load.
        children = this.getChildren();
        childrenLoaded = children.filter(childFilter).map(function (child) {
          return new Promise(function waitForLoaded (resolve) {
            if (child.hasLoaded) { return resolve(); }
            child.addEventListener('loaded', resolve);
          });
        });

        Promise.all(childrenLoaded).then(function emitLoaded () {
          self.hasLoaded = true;
          if (cb) { cb(); }
          self.emit('loaded', {}, false);
        });
      },
      writable: true
    },

    getChildren: {
      value: function () {
        return Array.prototype.slice.call(this.children, 0);
      }
    },

    updateMixins: {
      value: function (newMixins, oldMixins) {
        var newMixinsIds = newMixins.split(' ');
        var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
        // To determine what listeners will be removed
        var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
        this.mixinEls = [];
        diff.forEach(bind(this.unregisterMixin, this));
        newMixinsIds.forEach(bind(this.registerMixin, this));
      }
    },

    registerMixin: {
      value: function (mixinId) {
        if (!this.sceneEl) { return; }
        var mixinEl = this.sceneEl.querySelector('a-mixin#' + mixinId);
        if (!mixinEl) { return; }
        this.attachMixinListener(mixinEl);
        this.mixinEls.push(mixinEl);
      }
    },

    setAttribute: {
      value: function (attr, newValue) {
        if (attr === 'mixin') { this.updateMixins(newValue); }
        HTMLElement.prototype.setAttribute.call(this, attr, newValue);
      }
    },

    unregisterMixin: {
      value: function (mixinId) {
        var mixinEls = this.mixinEls;
        var mixinEl;
        var i;
        for (i = 0; i < mixinEls.length; ++i) {
          mixinEl = mixinEls[i];
          if (mixinId === mixinEl.id) {
            mixinEls.splice(i, 1);
            break;
          }
        }
        this.removeMixinListener(mixinId);
      }
    },

    removeMixinListener: {
      value: function (mixinId) {
        var observer = this.mixinObservers[mixinId];
        if (!observer) { return; }
        observer.disconnect();
        this.mixinObservers[mixinId] = null;
      }
    },

    attachMixinListener: {
      value: function (mixinEl) {
        var self = this;
        var mixinId = mixinEl.id;
        var currentObserver = this.mixinObservers[mixinId];
        if (!mixinEl) { return; }
        if (currentObserver) { return; }
        var observer = new MutationObserver(function (mutations) {
          var attr = mutations[0].attributeName;
          self.handleMixinUpdate(attr);
        });
        var config = { attributes: true };
        observer.observe(mixinEl, config);
        this.mixinObservers[mixinId] = observer;
      }
    },

    handleMixinUpdate: {
      value: function () { /* no-op */ }
    },

    /**
     * Emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object=} [detail={}]
     *   Custom data to pass as `detail` to the event.
     * @param {Boolean=} [bubbles=true]
     *   Whether the event should bubble.
     */
    emit: {
      value: function (name, detail, bubbles) {
        var self = this;
        detail = detail || {};
        if (bubbles === undefined) { bubbles = true; }
        var data = { bubbles: !!bubbles, detail: detail };
        return name.split(' ').map(function (eventName) {
          return utils.fireEvent(self, eventName, data);
        });
      }
    },

    /**
     * Returns a closure that emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object} detail
     *   Custom data (optional) to pass as `detail` if the event is to
     *   be a `CustomEvent`.
     * @param {Boolean} bubbles
     *   Whether the event should be bubble.
     */
    emitter: {
      value: function (name, detail, bubbles) {
        var self = this;
        return function () {
          self.emit(name, detail, bubbles);
        };
      }
    }
  })
});
<|endoftext|>


<commit_before>import React, { PureComponent, PropTypes } from 'react';
import Path from './ReactMinimalPieChartPath';

const VIEWBOX_SIZE = 100;
const VIEWBOX_HALF_SIZE = VIEWBOX_SIZE / 2;

const sumValues = data => data.reduce((acc, dataEntry) => acc + dataEntry.value, 0);
const sumDegrees = data => data.reduce((acc, dataEntry) => acc + dataEntry.degrees, 0);

const evaluateViewBoxSize = (ratio, baseSize) => {
  // Wide ratio
  if (ratio > 1) {
    return `${baseSize} ${baseSize / ratio}`;
  }
  // Narrow/squared ratio
  return `${baseSize * ratio} ${baseSize}`;
}

// @TODO extract padding evaluation
const evaluateDegreesFromValues = (data, totalAngle, totalValue, paddingAngle) => {
  const total = totalValue || sumValues(data);

  // Substract form total available degrees the space taken by segments padding
  const degreesTakenByPadding = paddingAngle * (data.length);
  let totalDegrees = Math.abs(totalAngle) - degreesTakenByPadding;

  if (totalDegrees > 360) totalDegrees = 360;
  if (totalAngle < 0) totalDegrees = -totalDegrees;

  // Append "degrees" property into each data entry
  return data.map(dataEntry => Object.assign(
    { degrees: (dataEntry.value / total) * totalDegrees },
    dataEntry,
  ));
};

const makeSegmentTransitionStyle = (duration, easing) => ({
  transition: `stroke-dashoffset ${duration}ms ${easing}`,
});

const makeSegments = (data, props, hide) => {
  // Keep track of how many degrees have already been taken
  let lastSegmentAngle = props.startAngle;
  const segmentsPaddingAngle = props.paddingAngle * (props.lengthAngle / Math.abs(props.lengthAngle));
  let reveal;

  const style = props.animate
    ? makeSegmentTransitionStyle(props.animationDuration, props.animationEasing)
    : undefined;

  // Hide/reveal a segment?
  if (hide === true) {
    reveal = 0;
  } else if (typeof props.reveal === 'number') {
    reveal = props.reveal;
  } else if (hide === false) {
    reveal = 100;
  }

  return data.map((dataEntry, index) => {
    const startAngle = lastSegmentAngle;
    lastSegmentAngle += dataEntry.degrees + segmentsPaddingAngle;

    return (
      <Path
        key={dataEntry.key || index}
        cx={props.cx}
        cy={props.cy}
        startAngle={startAngle}
        lengthAngle={dataEntry.degrees}
        radius={props.radius}
        lineWidth={(props.radius / 100) * props.lineWidth}
        reveal={reveal}
        style={style}
        stroke={dataEntry.color}
        strokeLinecap={props.rounded ? 'round' : undefined}
        fill="none"
      />
    );
  });
};

export default class ReactMinimalPieChart extends PureComponent {
  constructor(props) {
    super(props);

    if (this.props.animate === true) {
      this.hideSegments = true;
    }
  }

  componentDidMount() {
    if (this.props.animate === true && requestAnimationFrame) {
      setTimeout(
        () => requestAnimationFrame(
          this.startAnimation.bind(this)
        )
      );
    }
  }

  startAnimation() {
    this.hideSegments = false;
    this.forceUpdate();
  }

  render() {
    if (this.props.data === undefined) {
      return null;
    }

    const normalizedData = evaluateDegreesFromValues(
      this.props.data,
      this.props.lengthAngle,
      this.props.totalValue,
      this.props.paddingAngle,
    );

    return (
      <div
        className={this.props.className}
        style={this.props.style}
      >
        <svg
          viewBox={`0 0 ${evaluateViewBoxSize(this.props.ratio, VIEWBOX_SIZE)}`}
          width="100%"
          height="100%"
        >
          {makeSegments(normalizedData, this.props, this.hideSegments)}
        </svg>
        {this.props.children}
      </div>
    );
  }
}

ReactMinimalPieChart.displayName = 'ReactMinimalPieChart';

ReactMinimalPieChart.propTypes = {
  data: PropTypes.arrayOf(
    PropTypes.shape({
      value: PropTypes.number.isRequired,
      key: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
      ]),
      color: PropTypes.string,
    }),
  ),
  cx: PropTypes.number,
  cy: PropTypes.number,
  ratio: PropTypes.number,
  totalValue: PropTypes.number,
  style: PropTypes.objectOf(
    PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),
  ),
  startAngle: PropTypes.number,
  lengthAngle: PropTypes.number,
  paddingAngle: PropTypes.number,
  lineWidth: PropTypes.number,
  radius: PropTypes.number,
  rounded: PropTypes.bool,
  animate: PropTypes.bool,
  animationDuration: PropTypes.number,
  animationEasing: PropTypes.string,
  reveal: PropTypes.number,
  children: PropTypes.node,
};

ReactMinimalPieChart.defaultProps = {
  cx: VIEWBOX_HALF_SIZE,
  cy: VIEWBOX_HALF_SIZE,
  ratio: 1,
  startAngle: 0,
  lengthAngle: 360,
  paddingAngle: 0,
  lineWidth: 100,
  radius: VIEWBOX_HALF_SIZE,
  rounded: false,
  animate: false,
  animationDuration: 500,
  animationEasing: 'ease-out',
};
<commit_msg>Remove obsolete sumDegrees function<commit_after>import React, { PureComponent, PropTypes } from 'react';
import Path from './ReactMinimalPieChartPath';

const VIEWBOX_SIZE = 100;
const VIEWBOX_HALF_SIZE = VIEWBOX_SIZE / 2;

const sumValues = data => data.reduce((acc, dataEntry) => acc + dataEntry.value, 0);

const evaluateViewBoxSize = (ratio, baseSize) => {
  // Wide ratio
  if (ratio > 1) {
    return `${baseSize} ${baseSize / ratio}`;
  }
  // Narrow/squared ratio
  return `${baseSize * ratio} ${baseSize}`;
}

// @TODO extract padding evaluation
const evaluateDegreesFromValues = (data, totalAngle, totalValue, paddingAngle) => {
  const total = totalValue || sumValues(data);

  // Substract form total available degrees the space taken by segments padding
  const degreesTakenByPadding = paddingAngle * (data.length);
  let totalDegrees = Math.abs(totalAngle) - degreesTakenByPadding;

  if (totalDegrees > 360) totalDegrees = 360;
  if (totalAngle < 0) totalDegrees = -totalDegrees;

  // Append "degrees" property into each data entry
  return data.map(dataEntry => Object.assign(
    { degrees: (dataEntry.value / total) * totalDegrees },
    dataEntry,
  ));
};

const makeSegmentTransitionStyle = (duration, easing) => ({
  transition: `stroke-dashoffset ${duration}ms ${easing}`,
});

const makeSegments = (data, props, hide) => {
  // Keep track of how many degrees have already been taken
  let lastSegmentAngle = props.startAngle;
  const segmentsPaddingAngle = props.paddingAngle * (props.lengthAngle / Math.abs(props.lengthAngle));
  let reveal;

  const style = props.animate
    ? makeSegmentTransitionStyle(props.animationDuration, props.animationEasing)
    : undefined;

  // Hide/reveal a segment?
  if (hide === true) {
    reveal = 0;
  } else if (typeof props.reveal === 'number') {
    reveal = props.reveal;
  } else if (hide === false) {
    reveal = 100;
  }

  return data.map((dataEntry, index) => {
    const startAngle = lastSegmentAngle;
    lastSegmentAngle += dataEntry.degrees + segmentsPaddingAngle;

    return (
      <Path
        key={dataEntry.key || index}
        cx={props.cx}
        cy={props.cy}
        startAngle={startAngle}
        lengthAngle={dataEntry.degrees}
        radius={props.radius}
        lineWidth={(props.radius / 100) * props.lineWidth}
        reveal={reveal}
        style={style}
        stroke={dataEntry.color}
        strokeLinecap={props.rounded ? 'round' : undefined}
        fill="none"
      />
    );
  });
};

export default class ReactMinimalPieChart extends PureComponent {
  constructor(props) {
    super(props);

    if (this.props.animate === true) {
      this.hideSegments = true;
    }
  }

  componentDidMount() {
    if (this.props.animate === true && requestAnimationFrame) {
      setTimeout(
        () => requestAnimationFrame(
          this.startAnimation.bind(this)
        )
      );
    }
  }

  startAnimation() {
    this.hideSegments = false;
    this.forceUpdate();
  }

  render() {
    if (this.props.data === undefined) {
      return null;
    }

    const normalizedData = evaluateDegreesFromValues(
      this.props.data,
      this.props.lengthAngle,
      this.props.totalValue,
      this.props.paddingAngle,
    );

    return (
      <div
        className={this.props.className}
        style={this.props.style}
      >
        <svg
          viewBox={`0 0 ${evaluateViewBoxSize(this.props.ratio, VIEWBOX_SIZE)}`}
          width="100%"
          height="100%"
        >
          {makeSegments(normalizedData, this.props, this.hideSegments)}
        </svg>
        {this.props.children}
      </div>
    );
  }
}

ReactMinimalPieChart.displayName = 'ReactMinimalPieChart';

ReactMinimalPieChart.propTypes = {
  data: PropTypes.arrayOf(
    PropTypes.shape({
      value: PropTypes.number.isRequired,
      key: PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.string,
      ]),
      color: PropTypes.string,
    }),
  ),
  cx: PropTypes.number,
  cy: PropTypes.number,
  ratio: PropTypes.number,
  totalValue: PropTypes.number,
  style: PropTypes.objectOf(
    PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),
  ),
  startAngle: PropTypes.number,
  lengthAngle: PropTypes.number,
  paddingAngle: PropTypes.number,
  lineWidth: PropTypes.number,
  radius: PropTypes.number,
  rounded: PropTypes.bool,
  animate: PropTypes.bool,
  animationDuration: PropTypes.number,
  animationEasing: PropTypes.string,
  reveal: PropTypes.number,
  children: PropTypes.node,
};

ReactMinimalPieChart.defaultProps = {
  cx: VIEWBOX_HALF_SIZE,
  cy: VIEWBOX_HALF_SIZE,
  ratio: 1,
  startAngle: 0,
  lengthAngle: 360,
  paddingAngle: 0,
  lineWidth: 100,
  radius: VIEWBOX_HALF_SIZE,
  rounded: false,
  animate: false,
  animationDuration: 500,
  animationEasing: 'ease-out',
};
<|endoftext|>


<commit_before>var
  path = require("path"),
  Surrogate = require(".."),
  surrogate = new Surrogate({
    port: 1337,
    ip: "0.0.0.0",
    verbose: false,
    initialState: {
      birthday: null
    }
  });

surrogate.jsonRoute({
  name: "set-info",
  method: "POST",
  path: "/api/person",
  handlers: {
    "set-person-success": {
      description: "Save the person's info.",
      handler: function (request) {
        this.state.birthday = request.payload.birthday;
        this.cookies.hasBirthday = true;
        return { status: "OK" };
      }
    }
  }
});

surrogate.htmlRoute({
  name: "get-info",
  method: "GET",
  path: "/api/person",
  handlers: {
    "get-person-success": {
      description: "Return info about the person.",
      handler: function (request, selectedOption) {
        var
          name = request.query.name || "Tommy Joe",
          birthday = this.state.birthday ?
            "has a birthday on " + this.state.birthday + "." :
            "has no birthday.";

        this.cookies.msg = "1";

        return selectedOption(name + " " + birthday);
      },
      optionsHelperText: "What should birthday info be wrapped in?",
      optionsType: "selectOne",
      options: {
        "wrap in body": function (str) {
          return "" + str + "";
        },
        "wrap in html": function (str) {
          return "" + str + "";
        }
      }
    },
    "get-person-error": {
      description: "Return an error instead of the person's info.",
      handler: function () {
        throw new Surrogate.HttpErr({
          code: 401,
          payload: "can't do that right now..."
        });
      }
    }
  }
});

surrogate.htmlRoute({
  name: "get-things",
  method: "GET",
  path: "/api/things",
  handlers: {
    "get-things-success": {
      description: "Return things.",
      handler: function (request, selectedOptions) {
        return selectedOptions.join(" ");
      },
      optionsHelperText: "What things should be returned?",
      optionsType: "selectMany",
      options: {
        "thing 1": "thing 1",
        "thing 2": "thing 2"
      }
    },
    "get-things-error": {
      description: "Return an error instead of the things.",
      handler: function () {
        throw new Surrogate.HttpErr({
          code: 401,
          payload: "can't do that right now..."
        });
      }
    }
  }
});

surrogate.staticDir({
  filePath: path.join(__dirname, "example-dir"),
  urlPath: "/static/"
});

surrogate.start();
<commit_msg>start surrogate instance if main module, otherwise export surrogate instance<commit_after>var
  path = require("path"),
  Surrogate = require(".."),
  surrogate = new Surrogate({
    port: 1337,
    ip: "0.0.0.0",
    verbose: false,
    initialState: {
      birthday: null
    }
  });

surrogate.jsonRoute({
  name: "set-info",
  method: "POST",
  path: "/api/person",
  handlers: {
    "set-person-success": {
      description: "Save the person's info.",
      handler: function (request) {
        this.state.birthday = request.payload.birthday;
        this.cookies.hasBirthday = true;
        return { status: "OK" };
      }
    }
  }
});

surrogate.htmlRoute({
  name: "get-info",
  method: "GET",
  path: "/api/person",
  handlers: {
    "get-person-success": {
      description: "Return info about the person.",
      handler: function (request, selectedOption) {
        var
          name = request.query.name || "Tommy Joe",
          birthday = this.state.birthday ?
            "has a birthday on " + this.state.birthday + "." :
            "has no birthday.";

        this.cookies.msg = "1";

        return selectedOption(name + " " + birthday);
      },
      optionsHelperText: "What should birthday info be wrapped in?",
      optionsType: "selectOne",
      options: {
        "wrap in body": function (str) {
          return "" + str + "";
        },
        "wrap in html": function (str) {
          return "" + str + "";
        }
      }
    },
    "get-person-error": {
      description: "Return an error instead of the person's info.",
      handler: function () {
        throw new Surrogate.HttpErr({
          code: 401,
          payload: "can't do that right now..."
        });
      }
    }
  }
});

surrogate.htmlRoute({
  name: "get-things",
  method: "GET",
  path: "/api/things",
  handlers: {
    "get-things-success": {
      description: "Return things.",
      handler: function (request, selectedOptions) {
        return selectedOptions.join(" ");
      },
      optionsHelperText: "What things should be returned?",
      optionsType: "selectMany",
      options: {
        "thing 1": "thing 1",
        "thing 2": "thing 2"
      }
    },
    "get-things-error": {
      description: "Return an error instead of the things.",
      handler: function () {
        throw new Surrogate.HttpErr({
          code: 401,
          payload: "can't do that right now..."
        });
      }
    }
  }
});

surrogate.staticDir({
  filePath: path.join(__dirname, "example-dir"),
  urlPath: "/static/"
});

if (require.main === module) {
  surrogate.start();
} else {
  module.exports = surrogate;
}
<|endoftext|>


<commit_before>// example
window.onload = function() {
  var routers = [
    [/^\/home$/, homeHandler],
    [/^\/topic\/(\d+)\/like$/, topicLikeHandler],
    ['^/string/(\\d+)', stringHandler]
  ];

  var area = document.getElementById('exp');
  router(routers);

  function homeHandler(request) {
    console.log('homeHandler');
    area.innerHTML = '
I am in homeHandler now!

params: ' + JSON.stringify(request.params) + '
';
  }

  function topicLikeHandler(request, id) {
    console.log('topicLikeHandler');
    console.log('id:', id);
    area.innerHTML = '
I am in topicLikeHandler now!

id: ' + id + '

params: ' + JSON.stringify(request.params) + '
'
  }

  function stringHandler(request, id) {
    console.log('stringHandler');
    console.log('id:', id);
    area.innerHTML = '
I am in stringHandler now!

id: ' + id + '

params: ' + JSON.stringify(request.params) + '
'
  }
};
<commit_msg>fix example<commit_after>// example
window.onload = function() {
  var routers = [
    [/^\/home$/, homeHandler],
    [/^\/topic\/(\d+)\/like$/, topicLikeHandler],
    ['^/string/(\\d+)', stringHandler]
  ];

  var area = document.getElementById('exp');
  (new LightHouse).route(routers);

  function homeHandler(request) {
    console.log('homeHandler');
    area.innerHTML = '
I am in homeHandler now!

params: ' + JSON.stringify(request.params) + '
';
  }

  function topicLikeHandler(request, id) {
    console.log('topicLikeHandler');
    console.log('id:', id);
    area.innerHTML = '
I am in topicLikeHandler now!

id: ' + id + '

params: ' + JSON.stringify(request.params) + '
'
  }

  function stringHandler(request, id) {
    console.log('stringHandler');
    console.log('id:', id);
    area.innerHTML = '
I am in stringHandler now!

id: ' + id + '

params: ' + JSON.stringify(request.params) + '
'
  }
};
<|endoftext|>


<commit_before>/**
 * argv[0] Access Key
 * argv[1] Secret Key
 * argv[2] Button id
 */
const args = require('yargs').argv._;
const usabilla = require('../dist');

const api = new usabilla.Usabilla(args[0], args[1]);

// Get all buttons for this account.
api.websites.buttons.get().then((response) => {
  const buttons = response.items;

  // Use the button id to get feedback for this button id.
  var buttonFeedbackQuery = {
    id: buttons[1].id,
    params: {
      limit: 1
    }
  };

  api.websites.buttons.feedback.get(buttonFeedbackQuery).then((feedback) => {
    console.log(feedback);
  });
});

// Get all campaigns for this account.
api.websites.campaigns.get().then((response) => {
  const campaigns = response.items;

  // Get the results for a campaign with id.
  var campaignQuery = {
    id: campaigns[0].id
  };

  api.websites.campaigns.resutls(campaignQuery).then((results) => {
    console.log(results);
  });

  api.websites.campaigns.stats(campaignQuery).then((results) => {
    console.log(results);
  });
});
<commit_msg>Removed comment for button id<commit_after>/**
 * argv[0] Access Key
 * argv[1] Secret Key
 */
const args = require('yargs').argv._;
const usabilla = require('../dist');

const api = new usabilla.Usabilla(args[0], args[1]);

// Get all buttons for this account.
api.websites.buttons.get().then((response) => {
  const buttons = response.items;

  // Use the button id to get feedback for this button id.
  var buttonFeedbackQuery = {
    id: buttons[1].id,
    params: {
      limit: 1
    }
  };

  api.websites.buttons.feedback.get(buttonFeedbackQuery).then((feedback) => {
    console.log(feedback);
  });
});

// Get all campaigns for this account.
api.websites.campaigns.get().then((response) => {
  const campaigns = response.items;

  // Get the results for a campaign with id.
  var campaignQuery = {
    id: campaigns[0].id
  };

  api.websites.campaigns.resutls(campaignQuery).then((results) => {
    console.log(results);
  });

  api.websites.campaigns.stats(campaignQuery).then((results) => {
    console.log(results);
  });
});
<|endoftext|>


<commit_before>/*jshint strict: false */
/*global request: true, Buffer: true, escape: true, $:true */
/*global extend: true, Crypto: true */

// Pretty dumb name for a function, just wraps callback calls so we dont
// to if (callback) callback() everywhere
var call = function(fun) {
  var args = Array.prototype.slice.call(arguments, 1);
  if (typeof fun === typeof Function) {
    fun.apply(this, args);
  }
};

// Wrapper for functions that call the bulkdocs api with a single doc,
// if the first result is an error, return an error
var yankError = function(callback) {
  return function(err, results) {
    if (err || results[0].error) {
      call(callback, err || results[0]);
    } else {
      call(callback, null, results[0]);
    }
  };
};

var isLocalId = function(id) {
  return (/^_local/).test(id);
};

var isAttachmentId = function(id) {
  return (/\//.test(id) && !isLocalId(id) && !/^_design/.test(id));
};

// Parse document ids: docid[/attachid]
//   - /attachid is optional, and can have slashes in it too
//   - int ids and strings beginning with _design or _local are not split
// returns an object: { docId: docid, attachmentId: attachid }
var parseDocId = function(id) {
  var ids = (typeof id === 'string') && !(/^_(design|local)\//.test(id)) ?
    id.split('/') : [id];
  return {
    docId: ids[0],
    attachmentId: ids.splice(1).join('/').replace(/^\/+/, '')
  };
};

// check if a specific revision of a doc has been deleted
//  - metadata: the metadata object from the doc store
//  - rev: (optional) the revision to check. defaults to metadata.rev
var isDeleted = function(metadata, rev) {
  if (!metadata || !metadata.deletions) {
    return false;
  }
  if (!rev) {
    rev = Pouch.merge.winningRev(metadata);
  }
  if (rev.indexOf('-') >= 0) {
    rev = rev.split('-')[1];
  }

  return metadata.deletions[rev] === true;
};

// Determine id an ID is valid
//   - invalid IDs begin with an underescore that does not begin '_design' or '_local'
//   - any other string value is a valid id
var isValidId = function(id) {
  if (/^_/.test(id)) {
    return (/^_(design|local)/).test(id);
  }
  return true;
};

// Preprocess documents, parse their revisions, assign an id and a
// revision for new writes that are missing them, etc
var parseDoc = function(doc, newEdits) {
  var error = null;

  // check for an attachment id and add attachments as needed
  if (doc._id) {
    var id = parseDocId(doc._id);
    if (id.attachmentId !== '') {
      var attachment = btoa(JSON.stringify(doc));
      doc = {_id: id.docId};
      if (!doc._attachments) {
        doc._attachments = {};
      }
      doc._attachments[id.attachmentId] = {
        content_type: 'application/json',
        data: attachment
      };
    }
  }

  var nRevNum;
  var newRevId;
  var revInfo;

  if (newEdits) {
    if (!doc._id) {
      doc._id = Math.uuid();
    }
    newRevId = Math.uuid(32, 16).toLowerCase();
    if (doc._rev) {
      revInfo = /^(\d+)-(.+)$/.exec(doc._rev);
      if (!revInfo) {
        throw "invalid value for property '_rev'";
      }
      doc._rev_tree = [{
        pos: parseInt(revInfo[1], 10),
        ids: [revInfo[2], [[newRevId, []]]]
      }];
      nRevNum = parseInt(revInfo[1], 10) + 1;
    } else {
      doc._rev_tree = [{
        pos: 1,
        ids : [newRevId, []]
      }];
      nRevNum = 1;
    }
  } else {
    if (doc._revisions) {
      doc._rev_tree = [{
        pos: doc._revisions.start - doc._revisions.ids.length + 1,
        ids: doc._revisions.ids.reduce(function(acc, x) {
          if (acc === null) {
            return [x, []];
          } else {
            return [x, [acc]];
          }
        }, null)
      }];
      nRevNum = doc._revisions.start;
      newRevId = doc._revisions.ids[0];
    }
    if (!doc._rev_tree) {
      revInfo = /^(\d+)-(.+)$/.exec(doc._rev);
      nRevNum = parseInt(revInfo[1], 10);
      newRevId = revInfo[2];
      doc._rev_tree = [{
        pos: parseInt(revInfo[1], 10),
        ids: [revInfo[2], []]
      }];
    }
  }

  if (typeof doc._id !== 'string') {
    error = Pouch.Errors.INVALID_ID;
  }
  else if (!isValidId(doc._id)) {
    error = Pouch.Errors.RESERVED_ID;
  }

  doc._id = decodeURIComponent(doc._id);
  doc._rev = [nRevNum, newRevId].join('-');

  if (error) {
    return error;
  }

  return Object.keys(doc).reduce(function(acc, key) {
    if (/^_/.test(key) && key !== '_attachments') {
      acc.metadata[key.slice(1)] = doc[key];
    } else {
      acc.data[key] = doc[key];
    }
    return acc;
  }, {metadata : {}, data : {}});
};

var compareRevs = function(a, b) {
  // Sort by id
  if (a.id !== b.id) {
    return (a.id < b.id ? -1 : 1);
  }
  // Then by deleted
  if (a.deleted ^ b.deleted) {
    return (a.deleted ? -1 : 1);
  }
  // Then by rev id
  if (a.rev_tree[0].pos === b.rev_tree[0].pos) {
    return (a.rev_tree[0].ids < b.rev_tree[0].ids ? -1 : 1);
  }
  // Then by depth of edits
  return (a.rev_tree[0].start < b.rev_tree[0].start ? -1 : 1);
};

// Pretty much all below can be combined into a higher order function to
// traverse revisions
// Callback has signature function(isLeaf, pos, id, [context])
// The return value from the callback will be passed as context to all children of that node
var traverseRevTree = function(revs, callback) {
  var toVisit = [];

  revs.forEach(function(tree) {
    toVisit.push({pos: tree.pos, ids: tree.ids});
  });

  while (toVisit.length > 0) {
    var node = toVisit.pop();
    var pos = node.pos;
    var tree = node.ids;
    var newCtx = callback(tree[1].length === 0, pos, tree[0], node.ctx);
    /*jshint loopfunc: true */
    tree[1].forEach(function(branch) {
      toVisit.push({pos: pos+1, ids: branch, ctx: newCtx});
    });
  }
};

var collectRevs = function(path) {
  var revs = [];

  traverseRevTree([path], function(isLeaf, pos, id) {
    revs.push({rev: pos + "-" + id, status: 'available'});
  });

  return revs;
};

var collectLeaves = function(revs) {
  var leaves = [];
  traverseRevTree(revs, function(isLeaf, pos, id) {
    if (isLeaf) {
      leaves.unshift({rev: pos + "-" + id, pos: pos});
    }
  });
  leaves.sort(function(a, b) {
    return b.pos - a.pos;
  });
  leaves.map(function(leaf) { delete leaf.pos; });
  return leaves;
};

var collectConflicts = function(revs, deletions) {
  // Remove all deleted leaves
  var leaves = collectLeaves(revs);
  for(var i = 0; i < leaves.length; i++){
    var leaf = leaves.shift();
    var rev = leaf.rev.split("-")[1];
    if(deletions && !deletions[rev]){
      leaves.push(leaf);
    }
  }

  // First is current rev
  leaves.shift();
  return leaves.map(function(x) { return x.rev; });
};

// returns first element of arr satisfying callback predicate
var arrayFirst = function(arr, callback) {
  for (var i = 0; i < arr.length; i++) {
    if (callback(arr[i], i) === true) {
      return arr[i];
    }
  }
  return false;
};

var filterChange = function(opts) {
  return function(change) {
    if (opts.filter && !opts.filter.call(this, change.doc)) {
      return;
    }
    if (!opts.include_docs) {
      delete change.doc;
    }
    call(opts.onChange, change);
  };
};

// returns array of all branches from root to leaf in the ids form:
// [[id, ...], ...]
var rootToLeaf = function(tree) {
  var paths = [];
  traverseRevTree(tree, function(isLeaf, pos, id, history) {
    history = history ? history.slice(0) : [];
    history.push(id);
    if (isLeaf) {
      var rootPos = pos + 1 - history.length;
      paths.unshift({pos: rootPos, ids: history});
    }
    return history;
  });
  return paths;
};

// Basic wrapper for localStorage
var win = this;
var localJSON = (function(){
  if (!win.localStorage) {
    return false;
  }
  return {
    set: function(prop, val) {
      localStorage.setItem(prop, JSON.stringify(val));
    },
    get: function(prop, def) {
      try {
        if (localStorage.getItem(prop) === null) {
          return def;
        }
        return JSON.parse((localStorage.getItem(prop) || 'false'));
      } catch(err) {
        return def;
      }
    },
    remove: function(prop) {
      localStorage.removeItem(prop);
    }
  };
})();

if (typeof module !== 'undefined' && module.exports) {
  // use node.js's crypto library instead of the Crypto object created by deps/uuid.js
  var crypto = require('crypto');
  var Crypto = {
    MD5: function(str) {
      return crypto.createHash('md5').update(str).digest('hex');
    }
  };
  var extend = require('./deps/extend');
  var ajax = require('./deps/ajax');

  request = require('request');
  _ = require('underscore');
  $ = _;

  module.exports = {
    Crypto: Crypto,
    call: call,
    yankError: yankError,
    isLocalId: isLocalId,
    isAttachmentId: isAttachmentId,
    parseDocId: parseDocId,
    parseDoc: parseDoc,
    isDeleted: isDeleted,
    compareRevs: compareRevs,
    collectRevs: collectRevs,
    collectLeaves: collectLeaves,
    collectConflicts: collectConflicts,
    arrayFirst: arrayFirst,
    filterChange: filterChange,
    atob: function(str) {
      return decodeURIComponent(escape(new Buffer(str, 'base64').toString('binary')));
    },
    btoa: function(str) {
      return new Buffer(unescape(encodeURIComponent(str)), 'binary').toString('base64');
    },
    extend: extend,
    ajax: ajax,
    traverseRevTree: traverseRevTree,
    rootToLeaf: rootToLeaf
  };
}

var Changes = function() {

  var api = {};
  var listeners = {};

  window.addEventListener("storage", function(e) {
    api.notify(e.key);
  });

  api.addListener = function(db_name, id, db, opts) {
    if (!listeners[db_name]) {
      listeners[db_name] = {};
    }
    listeners[db_name][id] = {
      db: db,
      opts: opts
    };
  };

  api.removeListener = function(db_name, id) {
    delete listeners[db_name][id];
  };

  api.clearListeners = function(db_name) {
    delete listeners[db_name];
  };

  api.notify = function(db_name) {
    if (!listeners[db_name]) { return; }
    for (var i in listeners[db_name]) {
      /*jshint loopfunc: true */
      var opts = listeners[db_name][i].opts;
      listeners[db_name][i].db.changes({
        include_docs: opts.include_docs,
        conflicts: opts.conflicts,
        continuous: false,
        descending: false,
        filter: opts.filter,
        since: opts.since,
        onChange: function(c) {
          if (c.seq > opts.since && !opts.cancelled) {
            opts.since = c.seq;
            call(opts.onChange, c);
          }
        }
      });
    }
  };

  return api;
};

<commit_msg>(#567) - fix isDeleted comment<commit_after>/*jshint strict: false */
/*global request: true, Buffer: true, escape: true, $:true */
/*global extend: true, Crypto: true */

// Pretty dumb name for a function, just wraps callback calls so we dont
// to if (callback) callback() everywhere
var call = function(fun) {
  var args = Array.prototype.slice.call(arguments, 1);
  if (typeof fun === typeof Function) {
    fun.apply(this, args);
  }
};

// Wrapper for functions that call the bulkdocs api with a single doc,
// if the first result is an error, return an error
var yankError = function(callback) {
  return function(err, results) {
    if (err || results[0].error) {
      call(callback, err || results[0]);
    } else {
      call(callback, null, results[0]);
    }
  };
};

var isLocalId = function(id) {
  return (/^_local/).test(id);
};

var isAttachmentId = function(id) {
  return (/\//.test(id) && !isLocalId(id) && !/^_design/.test(id));
};

// Parse document ids: docid[/attachid]
//   - /attachid is optional, and can have slashes in it too
//   - int ids and strings beginning with _design or _local are not split
// returns an object: { docId: docid, attachmentId: attachid }
var parseDocId = function(id) {
  var ids = (typeof id === 'string') && !(/^_(design|local)\//.test(id)) ?
    id.split('/') : [id];
  return {
    docId: ids[0],
    attachmentId: ids.splice(1).join('/').replace(/^\/+/, '')
  };
};

// check if a specific revision of a doc has been deleted
//  - metadata: the metadata object from the doc store
//  - rev: (optional) the revision to check. defaults to winning revision
var isDeleted = function(metadata, rev) {
  if (!metadata || !metadata.deletions) {
    return false;
  }
  if (!rev) {
    rev = Pouch.merge.winningRev(metadata);
  }
  if (rev.indexOf('-') >= 0) {
    rev = rev.split('-')[1];
  }

  return metadata.deletions[rev] === true;
};

// Determine id an ID is valid
//   - invalid IDs begin with an underescore that does not begin '_design' or '_local'
//   - any other string value is a valid id
var isValidId = function(id) {
  if (/^_/.test(id)) {
    return (/^_(design|local)/).test(id);
  }
  return true;
};

// Preprocess documents, parse their revisions, assign an id and a
// revision for new writes that are missing them, etc
var parseDoc = function(doc, newEdits) {
  var error = null;

  // check for an attachment id and add attachments as needed
  if (doc._id) {
    var id = parseDocId(doc._id);
    if (id.attachmentId !== '') {
      var attachment = btoa(JSON.stringify(doc));
      doc = {_id: id.docId};
      if (!doc._attachments) {
        doc._attachments = {};
      }
      doc._attachments[id.attachmentId] = {
        content_type: 'application/json',
        data: attachment
      };
    }
  }

  var nRevNum;
  var newRevId;
  var revInfo;

  if (newEdits) {
    if (!doc._id) {
      doc._id = Math.uuid();
    }
    newRevId = Math.uuid(32, 16).toLowerCase();
    if (doc._rev) {
      revInfo = /^(\d+)-(.+)$/.exec(doc._rev);
      if (!revInfo) {
        throw "invalid value for property '_rev'";
      }
      doc._rev_tree = [{
        pos: parseInt(revInfo[1], 10),
        ids: [revInfo[2], [[newRevId, []]]]
      }];
      nRevNum = parseInt(revInfo[1], 10) + 1;
    } else {
      doc._rev_tree = [{
        pos: 1,
        ids : [newRevId, []]
      }];
      nRevNum = 1;
    }
  } else {
    if (doc._revisions) {
      doc._rev_tree = [{
        pos: doc._revisions.start - doc._revisions.ids.length + 1,
        ids: doc._revisions.ids.reduce(function(acc, x) {
          if (acc === null) {
            return [x, []];
          } else {
            return [x, [acc]];
          }
        }, null)
      }];
      nRevNum = doc._revisions.start;
      newRevId = doc._revisions.ids[0];
    }
    if (!doc._rev_tree) {
      revInfo = /^(\d+)-(.+)$/.exec(doc._rev);
      nRevNum = parseInt(revInfo[1], 10);
      newRevId = revInfo[2];
      doc._rev_tree = [{
        pos: parseInt(revInfo[1], 10),
        ids: [revInfo[2], []]
      }];
    }
  }

  if (typeof doc._id !== 'string') {
    error = Pouch.Errors.INVALID_ID;
  }
  else if (!isValidId(doc._id)) {
    error = Pouch.Errors.RESERVED_ID;
  }

  doc._id = decodeURIComponent(doc._id);
  doc._rev = [nRevNum, newRevId].join('-');

  if (error) {
    return error;
  }

  return Object.keys(doc).reduce(function(acc, key) {
    if (/^_/.test(key) && key !== '_attachments') {
      acc.metadata[key.slice(1)] = doc[key];
    } else {
      acc.data[key] = doc[key];
    }
    return acc;
  }, {metadata : {}, data : {}});
};

var compareRevs = function(a, b) {
  // Sort by id
  if (a.id !== b.id) {
    return (a.id < b.id ? -1 : 1);
  }
  // Then by deleted
  if (a.deleted ^ b.deleted) {
    return (a.deleted ? -1 : 1);
  }
  // Then by rev id
  if (a.rev_tree[0].pos === b.rev_tree[0].pos) {
    return (a.rev_tree[0].ids < b.rev_tree[0].ids ? -1 : 1);
  }
  // Then by depth of edits
  return (a.rev_tree[0].start < b.rev_tree[0].start ? -1 : 1);
};

// Pretty much all below can be combined into a higher order function to
// traverse revisions
// Callback has signature function(isLeaf, pos, id, [context])
// The return value from the callback will be passed as context to all children of that node
var traverseRevTree = function(revs, callback) {
  var toVisit = [];

  revs.forEach(function(tree) {
    toVisit.push({pos: tree.pos, ids: tree.ids});
  });

  while (toVisit.length > 0) {
    var node = toVisit.pop();
    var pos = node.pos;
    var tree = node.ids;
    var newCtx = callback(tree[1].length === 0, pos, tree[0], node.ctx);
    /*jshint loopfunc: true */
    tree[1].forEach(function(branch) {
      toVisit.push({pos: pos+1, ids: branch, ctx: newCtx});
    });
  }
};

var collectRevs = function(path) {
  var revs = [];

  traverseRevTree([path], function(isLeaf, pos, id) {
    revs.push({rev: pos + "-" + id, status: 'available'});
  });

  return revs;
};

var collectLeaves = function(revs) {
  var leaves = [];
  traverseRevTree(revs, function(isLeaf, pos, id) {
    if (isLeaf) {
      leaves.unshift({rev: pos + "-" + id, pos: pos});
    }
  });
  leaves.sort(function(a, b) {
    return b.pos - a.pos;
  });
  leaves.map(function(leaf) { delete leaf.pos; });
  return leaves;
};

var collectConflicts = function(revs, deletions) {
  // Remove all deleted leaves
  var leaves = collectLeaves(revs);
  for(var i = 0; i < leaves.length; i++){
    var leaf = leaves.shift();
    var rev = leaf.rev.split("-")[1];
    if(deletions && !deletions[rev]){
      leaves.push(leaf);
    }
  }

  // First is current rev
  leaves.shift();
  return leaves.map(function(x) { return x.rev; });
};

// returns first element of arr satisfying callback predicate
var arrayFirst = function(arr, callback) {
  for (var i = 0; i < arr.length; i++) {
    if (callback(arr[i], i) === true) {
      return arr[i];
    }
  }
  return false;
};

var filterChange = function(opts) {
  return function(change) {
    if (opts.filter && !opts.filter.call(this, change.doc)) {
      return;
    }
    if (!opts.include_docs) {
      delete change.doc;
    }
    call(opts.onChange, change);
  };
};

// returns array of all branches from root to leaf in the ids form:
// [[id, ...], ...]
var rootToLeaf = function(tree) {
  var paths = [];
  traverseRevTree(tree, function(isLeaf, pos, id, history) {
    history = history ? history.slice(0) : [];
    history.push(id);
    if (isLeaf) {
      var rootPos = pos + 1 - history.length;
      paths.unshift({pos: rootPos, ids: history});
    }
    return history;
  });
  return paths;
};

// Basic wrapper for localStorage
var win = this;
var localJSON = (function(){
  if (!win.localStorage) {
    return false;
  }
  return {
    set: function(prop, val) {
      localStorage.setItem(prop, JSON.stringify(val));
    },
    get: function(prop, def) {
      try {
        if (localStorage.getItem(prop) === null) {
          return def;
        }
        return JSON.parse((localStorage.getItem(prop) || 'false'));
      } catch(err) {
        return def;
      }
    },
    remove: function(prop) {
      localStorage.removeItem(prop);
    }
  };
})();

if (typeof module !== 'undefined' && module.exports) {
  // use node.js's crypto library instead of the Crypto object created by deps/uuid.js
  var crypto = require('crypto');
  var Crypto = {
    MD5: function(str) {
      return crypto.createHash('md5').update(str).digest('hex');
    }
  };
  var extend = require('./deps/extend');
  var ajax = require('./deps/ajax');

  request = require('request');
  _ = require('underscore');
  $ = _;

  module.exports = {
    Crypto: Crypto,
    call: call,
    yankError: yankError,
    isLocalId: isLocalId,
    isAttachmentId: isAttachmentId,
    parseDocId: parseDocId,
    parseDoc: parseDoc,
    isDeleted: isDeleted,
    compareRevs: compareRevs,
    collectRevs: collectRevs,
    collectLeaves: collectLeaves,
    collectConflicts: collectConflicts,
    arrayFirst: arrayFirst,
    filterChange: filterChange,
    atob: function(str) {
      return decodeURIComponent(escape(new Buffer(str, 'base64').toString('binary')));
    },
    btoa: function(str) {
      return new Buffer(unescape(encodeURIComponent(str)), 'binary').toString('base64');
    },
    extend: extend,
    ajax: ajax,
    traverseRevTree: traverseRevTree,
    rootToLeaf: rootToLeaf
  };
}

var Changes = function() {

  var api = {};
  var listeners = {};

  window.addEventListener("storage", function(e) {
    api.notify(e.key);
  });

  api.addListener = function(db_name, id, db, opts) {
    if (!listeners[db_name]) {
      listeners[db_name] = {};
    }
    listeners[db_name][id] = {
      db: db,
      opts: opts
    };
  };

  api.removeListener = function(db_name, id) {
    delete listeners[db_name][id];
  };

  api.clearListeners = function(db_name) {
    delete listeners[db_name];
  };

  api.notify = function(db_name) {
    if (!listeners[db_name]) { return; }
    for (var i in listeners[db_name]) {
      /*jshint loopfunc: true */
      var opts = listeners[db_name][i].opts;
      listeners[db_name][i].db.changes({
        include_docs: opts.include_docs,
        conflicts: opts.conflicts,
        continuous: false,
        descending: false,
        filter: opts.filter,
        since: opts.since,
        onChange: function(c) {
          if (c.seq > opts.since && !opts.cancelled) {
            opts.since = c.seq;
            call(opts.onChange, c);
          }
        }
      });
    }
  };

  return api;
};

<|endoftext|>


<commit_before>/**
 * Copyright 2016 Netsend.
 *
 * This file is part of PersDB.
 *
 * PersDB is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * PersDB is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with PersDB. If not, see <https://www.gnu.org/licenses/>.
 */

'use strict';

var programName = require('path').basename(__filename, '.js');

if (process.getuid() !== 0) {
  console.error('%s: execute as root', programName);
  process.exit(1);
}

if (typeof process.send !== 'function') {
  console.error('%s: use child_process.spawn to invoke this module', programName);
  process.exit(2);
}

var dns = require('dns');
var fs = require('fs');

var chroot = require('chroot');
var posix = require('posix');
var ssh = require('ssh2');

var logger = require('./logger');

var log; // used after receiving the log configuration

/**
 * Instantiate an ssh tunnel.
 *
 * Full FSM: init --> listen
 *
 * The first message emitted is "init" which signals that this process is ready to
 * receive the configuration data. File desciptors for the log config should be
 * sent in subsequent messages.
 *
 * Only a kill message can be sent to signal the end of the process.
 *
 * As soon as the server is listening, a "listen" message is emitted.
 *
 * Expect one init request (request that contains tunnel config)
 * {
 *   log:            {Object}      // log configuration
 *   sshUser:        {String}      // username on the ssh server
 *   key:            {Object}      // path to private key to authenticate
 *   forward:        {String}      // ssh -L format [bind_address:]port:host:hostport
 *   host:           {String}      // ssh server to connect to
 *   fingerprint:    {String}      // sha256 hash of the host key in base64 or hex
 *   [port]:         {Number}      // ssh port, defaults to 22
 *   [name]:         {String}      // custom name for this tunnel
 *   [chroot]:       {String}      // defaults to /var/empty
 *   [user]:         {String}      // defaults to "_pdbnull"
 *   [group]:        {String}      // defaults to "_pdbnull"
 * }
 */
process.once('message', function(msg) {
  if (typeof msg !== 'object') { throw new TypeError('msg must be an object'); }

  if (typeof msg.log !== 'object') { throw new TypeError('msg.log must be an object'); }
  if (!msg.sshUser || typeof msg.sshUser !== 'string') { throw new TypeError('msg.sshUser must be a string'); }
  if (!msg.key || typeof msg.key !== 'string') { throw new TypeError('msg.key must be a string'); }
  if (!msg.forward || typeof msg.forward !== 'string') { throw new TypeError('msg.forward must be a string'); }
  if (!msg.host || typeof msg.host !== 'string') { throw new TypeError('msg.host must be a string'); }
  if (!msg.fingerprint || typeof msg.fingerprint !== 'string') { throw new TypeError('msg.fingerprint must be a string'); }

  if (msg.port != null && typeof msg.port !== 'number') { throw new TypeError('msg.port must be a number'); }
  if (msg.name != null && typeof msg.name !== 'string') { throw new TypeError('msg.name must be a string'); }
  if (msg.chroot != null && typeof msg.chroot !== 'string') { throw new TypeError('msg.chroot must be a string'); }
  if (msg.user != null && typeof msg.user !== 'string') { throw new TypeError('msg.user must be a string'); }
  if (msg.group != null && typeof msg.group !== 'string') { throw new TypeError('msg.group must be a string'); }

  if (msg.fingerprint.length !== 43 && msg.fingerprint.length !== 64) { throw new Error('msg.fingerprint must be a hex or base64 encoded sha256 hash'); }

  programName = 'tunnel ' + msg.forward;
  if (msg.name) {
    programName = 'tunnel ' + msg.name;
  }

  process.title = 'pdb/' + programName;

  var user = msg.user || '_pdbnull';
  var group = msg.group || '_pdbnull';

  var newRoot = msg.chroot || '/var/empty';

  msg.log.ident = programName;

  var forward = msg.forward.split(':');
  if (forward.length < 3 || forward.length > 4) {
    throw new Error('msg.forward format must follow [bind_address:]port:host:hostport');
  }

  // open log
  logger(msg.log, function(err, l) {
    if (err) { l.err(err); throw err; }

    log = l;

    // make sure the key is not world readable/writable
    var stats = fs.statSync(msg.key);
    if ((stats.mode & 0o066) !== 0) {
      log.err('msg.key should not be group or world readable or writable');
      process.exit(12);
    }

    var key = fs.readFileSync(msg.key);

    var uid, gid;
    try {
      uid = posix.getpwnam(user).uid;
      gid = posix.getgrnam(group).gid;
    } catch(err) {
      log.err('tunnel %s %s:%s', err, user, group);
      process.exit(3);
    }

    // create the tunnel or exit
    var tunnel = new ssh.Client();

    // start dns lookup of hostname before chrooting
    dns.lookup(msg.host, function(err, hostIp) {
      if (err) {
        log.err('tunnel could not lookup %s %s', msg.host, err);
        process.exit(4);
      }
      log.notice('resolved %s to %s', msg.host, hostIp);

      // ensure hex and base64 fingerprint
      var expectedFingerprintHex, expectedFingerprintBase64;
      if (msg.fingerprint.length === 64) {
        expectedFingerprintHex = msg.fingerprint;
        expectedFingerprintBase64 = new Buffer(msg.fingerprint, 'hex').toString('base64');
      } else {
        expectedFingerprintBase64 = msg.fingerprint;
        expectedFingerprintHex = new Buffer(msg.fingerprint, 'base64').toString('hex');
      }

      tunnel.connect({
        host: hostIp,
        port: msg.port || 22,
        username: msg.sshUser,
        privateKey: key,
        hostHash: 'sha256',
        debug: log.debug2,
        hostVerifier: function(hostFingerprintHex) {
          var hostFingerprintBase64 = new Buffer(hostFingerprintHex, 'hex').toString('base64');
          if (hostFingerprintHex === expectedFingerprintHex) {
            log.info('host key verified: %s', expectedFingerprintBase64);
            return true;
          } else {
            log.err('host key mismatch! expected %s got %s', expectedFingerprintBase64, hostFingerprintBase64);
            return false;
          }
        }
      });
    });

    // chroot or exit
    try {
      chroot(newRoot, uid, gid);
      log.info('changed root to %s and user:group to %s:%s', newRoot, user, group);
    } catch(err) {
      log.err('changing root or user failed %j %s', msg, err);
      process.exit(8);
    }

    var srcHost = '127.0.0.1';
    if (forward.length === 4) {
      srcHost = forward.shift();
    }

    var dstHost = forward[1];

    var srcPort = parseInt(forward[0]);
    var dstPort = parseInt(forward[2]);
    if (srcPort < 1024) {
      log.err('only source ports >1024 are supported');
      process.exit(5);
    }

    var gchannel;
    tunnel.on('ready', function() {
      log.notice('connected to %s', msg.host);
      tunnel.forwardOut(srcHost, srcPort, dstHost, dstPort, function(err, channel) {
        if (err) {
          log.err(err);
          process.exit(3);
        }
        channel.on('close', function() {
          log.notice('tunnel closed');
        });
        gchannel = channel;
        log.notice('tunnel ready %s', forward.join(':'));
        process.send('listen');
      });
    });

    tunnel.on('error', function(err) {
      log.err(err);
    });

    tunnel.on('close', function(hadError) {
      log.notice('closed');
      if (hadError) {
        log.err('closed with error');
      }
    });

    // handle shutdown
    function shutdown() {
      log.info('closing...');
      gchannel.end();
      tunnel.end();
    }

    // listen to kill signals
    process.once('SIGTERM', shutdown);
    process.once('SIGINT', shutdown);

    process.once('message', shutdown); // expect msg.type == kill
  });
});

process.send('init');
<commit_msg>lib/tunnel: start local server for forwarding<commit_after>/**
 * Copyright 2016 Netsend.
 *
 * This file is part of PersDB.
 *
 * PersDB is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * PersDB is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with PersDB. If not, see <https://www.gnu.org/licenses/>.
 */

'use strict';

var programName = require('path').basename(__filename, '.js');

if (process.getuid() !== 0) {
  console.error('%s: execute as root', programName);
  process.exit(1);
}

if (typeof process.send !== 'function') {
  console.error('%s: use child_process.spawn to invoke this module', programName);
  process.exit(2);
}

var dns = require('dns');
var fs = require('fs');
var net = require('net');

var chroot = require('chroot');
var posix = require('posix');
var ssh = require('ssh2');

var logger = require('./logger');

var log; // used after receiving the log configuration

/**
 * Instantiate an ssh tunnel.
 *
 * Full FSM: init --> listen
 *
 * The first message emitted is "init" which signals that this process is ready to
 * receive the configuration data. File desciptors for the log config should be
 * sent in subsequent messages.
 *
 * Only a kill message can be sent to signal the end of the process.
 *
 * As soon as the server is listening, a "listen" message is emitted.
 *
 * Expect one init request (request that contains tunnel config)
 * {
 *   log:            {Object}      // log configuration
 *   sshUser:        {String}      // username on the ssh server
 *   key:            {Object}      // path to private key to authenticate
 *   forward:        {String}      // ssh -L format [bind_address:]port:host:hostport
 *   host:           {String}      // ssh server to connect to
 *   fingerprint:    {String}      // sha256 hash of the host key in base64 or hex
 *   [port]:         {Number}      // ssh port, defaults to 22
 *   [name]:         {String}      // custom name for this tunnel
 *   [chroot]:       {String}      // defaults to /var/empty
 *   [user]:         {String}      // defaults to "_pdbnull"
 *   [group]:        {String}      // defaults to "_pdbnull"
 * }
 */
process.once('message', function(msg) {
  if (typeof msg !== 'object') { throw new TypeError('msg must be an object'); }

  if (typeof msg.log !== 'object') { throw new TypeError('msg.log must be an object'); }
  if (!msg.sshUser || typeof msg.sshUser !== 'string') { throw new TypeError('msg.sshUser must be a string'); }
  if (!msg.key || typeof msg.key !== 'string') { throw new TypeError('msg.key must be a string'); }
  if (!msg.forward || typeof msg.forward !== 'string') { throw new TypeError('msg.forward must be a string'); }
  if (!msg.host || typeof msg.host !== 'string') { throw new TypeError('msg.host must be a string'); }
  if (!msg.fingerprint || typeof msg.fingerprint !== 'string') { throw new TypeError('msg.fingerprint must be a string'); }

  if (msg.port != null && typeof msg.port !== 'number') { throw new TypeError('msg.port must be a number'); }
  if (msg.name != null && typeof msg.name !== 'string') { throw new TypeError('msg.name must be a string'); }
  if (msg.chroot != null && typeof msg.chroot !== 'string') { throw new TypeError('msg.chroot must be a string'); }
  if (msg.user != null && typeof msg.user !== 'string') { throw new TypeError('msg.user must be a string'); }
  if (msg.group != null && typeof msg.group !== 'string') { throw new TypeError('msg.group must be a string'); }

  if (msg.fingerprint.length !== 43 && msg.fingerprint.length !== 64) { throw new Error('msg.fingerprint must be a hex or base64 encoded sha256 hash'); }

  programName = 'tunnel ' + msg.forward;
  if (msg.name) {
    programName = 'tunnel ' + msg.name;
  }

  process.title = 'pdb/' + programName;

  var user = msg.user || '_pdbnull';
  var group = msg.group || '_pdbnull';

  var newRoot = msg.chroot || '/var/empty';

  msg.log.ident = programName;

  var forward = msg.forward.split(':');
  if (forward.length < 3 || forward.length > 4) {
    throw new Error('msg.forward format must follow [bind_address:]port:host:hostport');
  }

  // open log
  logger(msg.log, function(err, l) {
    if (err) { l.err(err); throw err; }

    log = l;

    // make sure the key is not world readable/writable
    var stats = fs.statSync(msg.key);
    if ((stats.mode & 0o066) !== 0) {
      log.err('msg.key should not be group or world readable or writable');
      process.exit(12);
    }

    var key = fs.readFileSync(msg.key);

    var uid, gid;
    try {
      uid = posix.getpwnam(user).uid;
      gid = posix.getgrnam(group).gid;
    } catch(err) {
      log.err('tunnel %s %s:%s', err, user, group);
      process.exit(3);
    }

    // create the tunnel or exit
    var tunnel = new ssh.Client();

    // start dns lookup of hostname before chrooting
    dns.lookup(msg.host, function(err, hostIp) {
      if (err) {
        log.err('tunnel could not lookup %s %s', msg.host, err);
        process.exit(4);
      }
      log.notice('resolved %s to %s', msg.host, hostIp);

      // ensure hex and base64 fingerprint
      var expectedFingerprintHex, expectedFingerprintBase64;
      if (msg.fingerprint.length === 64) {
        expectedFingerprintHex = msg.fingerprint;
        expectedFingerprintBase64 = new Buffer(msg.fingerprint, 'hex').toString('base64');
      } else {
        expectedFingerprintBase64 = msg.fingerprint;
        expectedFingerprintHex = new Buffer(msg.fingerprint, 'base64').toString('hex');
      }

      tunnel.connect({
        host: hostIp,
        port: msg.port || 22,
        username: msg.sshUser,
        privateKey: key,
        hostHash: 'sha256',
        debug: log.debug2,
        hostVerifier: function(hostFingerprintHex) {
          var hostFingerprintBase64 = new Buffer(hostFingerprintHex, 'hex').toString('base64');
          if (hostFingerprintHex === expectedFingerprintHex) {
            log.info('host key verified: %s', expectedFingerprintBase64);
            return true;
          } else {
            log.err('host key mismatch! expected %s got %s', expectedFingerprintBase64, hostFingerprintBase64);
            return false;
          }
        }
      });
    });

    // chroot or exit
    try {
      chroot(newRoot, uid, gid);
      log.info('changed root to %s and user:group to %s:%s', newRoot, user, group);
    } catch(err) {
      log.err('changing root or user failed %j %s', msg, err);
      process.exit(8);
    }

    var srcHost = '127.0.0.1';
    if (forward.length === 4) {
      srcHost = forward.shift();
    }

    var dstHost = forward[1];

    var srcPort = parseInt(forward[0]);
    var dstPort = parseInt(forward[2]);
    if (srcPort < 1024) {
      log.err('only source ports >1024 are supported');
      process.exit(5);
    }

    var gchannel, srv;
    tunnel.on('ready', function() {
      log.notice('connected to %s', msg.host);
      srv = net.createServer(function(sock) {
        tunnel.forwardOut(sock.remoteAddress, sock.remotePort, dstHost, dstPort, function(err, channel) {
          if (err) {
            log.err(err);
            sock.end();
          }
          channel.on('close', function() {
            log.notice('tunnel closed');
          });
          gchannel = channel;
          sock.pipe(channel).pipe(sock);
          log.notice('tunnel ready %s:%d:%s:%d', sock.remoteAddress, sock.remotePort, dstHost, dstPort);
        });
      });
      srv.listen(srcPort, srcHost, function() {
        process.send('listen');
      });
    });

    tunnel.on('error', function(err) {
      log.err(err);
    });

    tunnel.on('close', function(hadError) {
      log.notice('closed');
      srv && srv.close();
      if (hadError) {
        log.err('closed with error');
      }
    });

    // handle shutdown
    function shutdown() {
      log.info('closing...');
      gchannel.end();
      tunnel.end();
    }

    // listen to kill signals
    process.once('SIGTERM', shutdown);
    process.once('SIGINT', shutdown);

    process.once('message', shutdown); // expect msg.type == kill
  });
});

process.send('init');
<|endoftext|>


<commit_before>var _ = require('lodash');
var Scope = require('../scope');

module.exports = function() {
    this.type('array', {type: 'base'}, function() {
        this.typeAssertion(function(value) {
            return _.isArray(value);
        });

        this.onCreate(function() {
            this.scope = new Scope(this.scope.dsl, this.scope);
            this.indexes = {};
            this.optionals = {};
            this.extraIndexesAllowed = false;
        });

        this.onValidate(function(value, path, dataWrapper) {
            _.forOwn(this.indexes, function(validator, index) {
                if (value.hasOwnProperty(index)) {
                    validator.validate(value[index], dataWrapper.concatPaths(path, index), dataWrapper);
                } else if (!this.optionals[index]) {
                    this.scope.error(path, "Index " + index + " is missing");
                }
            }, this);

            var leftIndexes = _.difference(Object.keys(value), Object.keys(this.indexes));
            if (leftIndexes.length) {
                if (this.eachIndexScope) {
                    _.forOwn(leftIndexes, function(index) {
                        var subpath = dataWrapper.concatPaths(path, index);
                        if (this.eachIndexScope.restriction) {
                            this.eachIndexScope.restriction(path, index, dataWrapper);
                        }
                        this.eachIndexScope.validator.validate(value[index], subpath, dataWrapper);
                    }, this);
                } else if (this.extraIndexesAllowed) {
                    this.scope.error(path, "Unexpected indexes: " + leftIndexes.join(", "));
                }
            }

        });

        this.method('index', function(index, props, schema) {
            this.indexes[index] = this.scope.getValidator(props, schema);
        });

        this.method('optionalIndex', function(key, props, schema) {
            this.index(key, props, schema);
            this.optionals[key] = true;
        });

        this.method('allowExtraIndexes', function(value) {
            this.extraIndexesAllowed = (value === undefined) ? true : value;
        });

        this.method('eachIndex', function(restriction, props, schema) {
            this.eachIndexScope = {
                restriction: restriction,
                validator: this.scope.getValidator(props, schema)
            };
        });

        this.method('any', function() { return function() {}; });
        this.method('inSet', function(set) {
            var scope = this.scope;
            return function(path, index, dataWrapper) {
                if (set.indexOf(index) < 0) {
                    scope.error(path, "Index " + index + " is not in set " + JSON.stringify(set));
                }
            };
        });

    });
};
<commit_msg>Remove array null-support restriction<commit_after>var _ = require('lodash');
var Scope = require('../scope');

module.exports = function() {
    this.type('array', {type: 'base'}, function() {
        this.typeAssertion(function(value) {
            return _.isArray(value);
        });

        this.onCreate(function() {
            this.scope = new Scope(this.scope.dsl, this.scope);
            this.indexes = {};
            this.optionals = {};
            this.extraIndexesAllowed = false;
        });

        this.onValidate(function(value, path, dataWrapper) {
            _.forOwn(this.indexes, function(validator, index) {
                if (value.hasOwnProperty(index)) {
                    validator.validate(value[index], dataWrapper.concatPaths(path, index), dataWrapper);
                } else if (!this.optionals[index]) {
                    this.scope.error(path, "Index " + index + " is missing");
                }
            }, this);

            var leftIndexes = _.difference(Object.keys(value), Object.keys(this.indexes));
            if (leftIndexes.length) {
                if (this.eachIndexScope) {
                    _.forOwn(leftIndexes, function(index) {
                        var subpath = dataWrapper.concatPaths(path, index);
                        this.eachIndexScope.restriction(path, index, dataWrapper);
                        this.eachIndexScope.validator.validate(value[index], subpath, dataWrapper);
                    }, this);
                } else if (this.extraIndexesAllowed) {
                    this.scope.error(path, "Unexpected indexes: " + leftIndexes.join(", "));
                }
            }

        });

        this.method('index', function(index, props, schema) {
            this.indexes[index] = this.scope.getValidator(props, schema);
        });

        this.method('optionalIndex', function(key, props, schema) {
            this.index(key, props, schema);
            this.optionals[key] = true;
        });

        this.method('allowExtraIndexes', function(value) {
            this.extraIndexesAllowed = (value === undefined) ? true : value;
        });

        this.method('eachIndex', function(restriction, props, schema) {
            this.eachIndexScope = {
                restriction: restriction,
                validator: this.scope.getValidator(props, schema)
            };
        });

        this.method('any', function() { return function() {}; });
        this.method('inSet', function(set) {
            var scope = this.scope;
            return function(path, index, dataWrapper) {
                if (set.indexOf(index) < 0) {
                    scope.error(path, "Index " + index + " is not in set " + JSON.stringify(set));
                }
            };
        });

    });
};
<|endoftext|>


<commit_before>(function () {
    var moduleInjection = {};
    BI.module = function (xtype, cls) {
        if (moduleInjection[xtype] != null) {
            _global.console && console.error("module:[" + xtype + "] has been registed");
        }
        moduleInjection[xtype] = cls;
    };

    var constantInjection = {};
    BI.constant = function (xtype, cls) {
        if (constantInjection[xtype] != null) {
            _global.console && console.error("constant:[" + xtype + "] has been registed");
        }
        constantInjection[xtype] = cls;
    };

    var modelInjection = {};
    BI.model = function (xtype, cls) {
        if (modelInjection[xtype] != null) {
            _global.console && console.error("model:[" + xtype + "] has been registed");
        }
        modelInjection[xtype] = cls;
    };

    var storeInjection = {};
    BI.store = function (xtype, cls) {
        if (storeInjection[xtype] != null) {
            _global.console && console.error("store:[" + xtype + "] has been registed");
        }
        storeInjection[xtype] = cls;
    };

    var serviceInjection = {};
    BI.service = function (xtype, cls) {
        if (serviceInjection[xtype] != null) {
            _global.console && console.error("service:[" + xtype + "] has been registed");
        }
        serviceInjection[xtype] = cls;
    };

    var providerInjection = {};
    BI.provider = function (xtype, cls) {
        if (providerInjection[xtype] != null) {
            _global.console && console.error("provider:[" + xtype + "] has been registed");
        }
        providerInjection[xtype] = cls;
    };

    var configFunctions = {};
    BI.config = function (type, configFn) {
        if (BI.initialized) {
            if (constantInjection[type]) {
                return (constantInjection[type] = configFn(constantInjection[type]));
            }
            if (providerInjection[type]) {
                if (!providers[type]) {
                    providers[type] = new providerInjection[type]();
                }
                providerInstance[type] = null;
                return configFn(providers[type]);
            }
            return BI.Plugin.configWidget(type, configFn);
        }
        if (!configFunctions[type]) {
            configFunctions[type] = [];
            BI.prepares.push(function () {
                var queue = configFunctions[type];
                for (var i = 0; i < queue.length; i++) {
                    if (constantInjection[type]) {
                        constantInjection[type] = queue[i](constantInjection[type]);
                        continue;
                    }
                    if (providerInjection[type]) {
                        if (!providers[type]) {
                            providers[type] = new providerInjection[type]();
                        }
                        providerInstance[type] = null;
                        queue[i](providers[type]);
                        continue;
                    }
                    BI.Plugin.configWidget(type, queue[i]);
                }
                configFunctions[type] = null;
            });
        }
        configFunctions[type].push(configFn);
    };

    var actions = {};
    var globalAction = [];
    BI.action = function (type, actionFn) {
        if (BI.isFunction(type)) {
            globalAction.push(type);
            return function () {
                BI.remove(globalAction, function (idx) {
                    return globalAction.indexOf(actionFn) === idx;
                });
            };
        }
        if (!actions[type]) {
            actions[type] = [];
        }
        actions[type].push(actionFn);
        return function () {
            BI.remove(actions[type], function (idx) {
                return actions[type].indexOf(actionFn) === idx;
            });
            if (actions[type].length === 0) {
                delete actions[type];
            }
        };
    };

    var points = {};
    BI.point = function (type, action, pointFn, after) {
        if (!points[type]) {
            points[type] = {};
        }
        if (!points[type][action]) {
            points[type][action] = {};
        }
        if (!points[type][action][after ? "after" : "before"]) {
            points[type][action][after ? "after" : "before"] = [];
        }
        points[type][action][after ? "after" : "before"].push(pointFn);
    };

    BI.Modules = {
        getModule: function (type) {
            if (!moduleInjection[type]) {
                _global.console && console.error("module:[" + type + "] does not exists");
                return false;
            }
            return moduleInjection[type];
        },
        getAllModules: function () {
            return moduleInjection;
        }
    };

    BI.Constants = {
        getConstant: function (type) {
            return constantInjection[type];
        }
    };

    var callPoint = function (inst, types) {
        types = BI.isArray(types) ? types : [types];
        BI.each(types, function (idx, type) {
            if (points[type]) {
                for (var action in points[type]) {
                    var bfns = points[type][action].before;
                    if (bfns) {
                        BI.aspect.before(inst, action, function (bfns) {
                            return function () {
                                for (var i = 0, len = bfns.length; i < len; i++) {
                                    try {
                                        bfns[i].apply(inst, arguments);
                                    } catch (e) {
                                        _global.console && console.error(e);
                                    }
                                }
                            };
                        }(bfns));
                    }
                    var afns = points[type][action].after;
                    if (afns) {
                        BI.aspect.after(inst, action, function (afns) {
                            return function () {
                                for (var i = 0, len = afns.length; i < len; i++) {
                                    try {
                                        afns[i].apply(inst, arguments);
                                    } catch (e) {
                                        _global.console && console.error(e);
                                    }
                                }
                            };
                        }(afns));
                    }
                }
            }
        });
    };

    BI.Models = {
        getModel: function (type, config) {
            var inst = new modelInjection[type](config);
            inst._constructor && inst._constructor(config);
            inst.mixins && callPoint(inst, inst.mixins);
            callPoint(inst, type);
            return inst;
        }
    };

    var stores = {};

    BI.Stores = {
        getStore: function (type, config) {
            if (stores[type]) {
                return stores[type];
            }
            var inst = stores[type] = new storeInjection[type](config);
            inst._constructor && inst._constructor(config);
            callPoint(inst, type);
            return inst;
        }
    };

    var services = {};

    BI.Services = {
        getService: function (type, config) {
            if (services[type]) {
                return services[type];
            }
            services[type] = new serviceInjection[type](config);
            callPoint(services[type], type);
            return services[type];
        }
    };

    var providers = {},
        providerInstance = {};

    BI.Providers = {
        getProvider: function (type, config) {
            if (!providers[type]) {
                providers[type] = new providerInjection[type]();
            }
            if (!providerInstance[type]) {
                providerInstance[type] = new (providers[type].$get())(config);
            }
            return providerInstance[type];
        }
    };

    BI.Actions = {
        runAction: function (type, event, config) {
            BI.each(actions[type], function (i, act) {
                try {
                    act(event, config);
                } catch (e) {
                    _global.console && console.error(e);
                }
            });
        },
        runGlobalAction: function () {
            var args = [].slice.call(arguments);
            BI.each(globalAction, function (i, act) {
                try {
                    act.apply(null, args);
                } catch (e) {
                    _global.console && console.error(e);
                }
            });
        }
    };

    BI.getContext = function (type, config) {
        if (constantInjection[type]) {
            return BI.Constants.getConstant(type);
        }
        if (modelInjection[type]) {
            return BI.Models.getModel(type, config);
        }
        if (storeInjection[type]) {
            return BI.Stores.getStore(type, config);
        }
        if (serviceInjection[type]) {
            return BI.Services.getService(type, config);
        }
        if (providerInjection[type]) {
            return BI.Providers.getProvider(type, config);
        }
    };
})();<commit_msg>Pull request #1358: 无JIRA任务 bugfix<commit_after>(function () {
    var moduleInjection = {};
    BI.module = function (xtype, cls) {
        if (moduleInjection[xtype] != null) {
            _global.console && console.error("module:[" + xtype + "] has been registed");
        }
        moduleInjection[xtype] = cls;
    };

    var constantInjection = {};
    BI.constant = function (xtype, cls) {
        if (constantInjection[xtype] != null) {
            _global.console && console.error("constant:[" + xtype + "] has been registed");
        }
        constantInjection[xtype] = cls;
    };

    var modelInjection = {};
    BI.model = function (xtype, cls) {
        if (modelInjection[xtype] != null) {
            _global.console && console.error("model:[" + xtype + "] has been registed");
        }
        modelInjection[xtype] = cls;
    };

    var storeInjection = {};
    BI.store = function (xtype, cls) {
        if (storeInjection[xtype] != null) {
            _global.console && console.error("store:[" + xtype + "] has been registed");
        }
        storeInjection[xtype] = cls;
    };

    var serviceInjection = {};
    BI.service = function (xtype, cls) {
        if (serviceInjection[xtype] != null) {
            _global.console && console.error("service:[" + xtype + "] has been registed");
        }
        serviceInjection[xtype] = cls;
    };

    var providerInjection = {};
    BI.provider = function (xtype, cls) {
        if (providerInjection[xtype] != null) {
            _global.console && console.error("provider:[" + xtype + "] has been registed");
        }
        providerInjection[xtype] = cls;
    };

    var configFunctions = {};
    BI.config = function (type, configFn) {
        if (BI.initialized) {
            if (constantInjection[type]) {
                return (constantInjection[type] = configFn(constantInjection[type]));
            }
            if (providerInjection[type]) {
                if (!providers[type]) {
                    providers[type] = new providerInjection[type]();
                }
                // 如果config被重新配置的话，需要删除掉之前的实例
                if (providerInstance[type]) {
                    delete providerInstance[type];
                }
                return configFn(providers[type]);
            }
            return BI.Plugin.configWidget(type, configFn);
        }
        if (!configFunctions[type]) {
            configFunctions[type] = [];
            BI.prepares.push(function () {
                var queue = configFunctions[type];
                for (var i = 0; i < queue.length; i++) {
                    if (constantInjection[type]) {
                        constantInjection[type] = queue[i](constantInjection[type]);
                        continue;
                    }
                    if (providerInjection[type]) {
                        if (!providers[type]) {
                            providers[type] = new providerInjection[type]();
                        }
                        if (providerInstance[type]) {
                            delete providerInstance[type];
                        }
                        queue[i](providers[type]);
                        continue;
                    }
                    BI.Plugin.configWidget(type, queue[i]);
                }
                configFunctions[type] = null;
            });
        }
        configFunctions[type].push(configFn);
    };

    var actions = {};
    var globalAction = [];
    BI.action = function (type, actionFn) {
        if (BI.isFunction(type)) {
            globalAction.push(type);
            return function () {
                BI.remove(globalAction, function (idx) {
                    return globalAction.indexOf(actionFn) === idx;
                });
            };
        }
        if (!actions[type]) {
            actions[type] = [];
        }
        actions[type].push(actionFn);
        return function () {
            BI.remove(actions[type], function (idx) {
                return actions[type].indexOf(actionFn) === idx;
            });
            if (actions[type].length === 0) {
                delete actions[type];
            }
        };
    };

    var points = {};
    BI.point = function (type, action, pointFn, after) {
        if (!points[type]) {
            points[type] = {};
        }
        if (!points[type][action]) {
            points[type][action] = {};
        }
        if (!points[type][action][after ? "after" : "before"]) {
            points[type][action][after ? "after" : "before"] = [];
        }
        points[type][action][after ? "after" : "before"].push(pointFn);
    };

    BI.Modules = {
        getModule: function (type) {
            if (!moduleInjection[type]) {
                _global.console && console.error("module:[" + type + "] does not exists");
                return false;
            }
            return moduleInjection[type];
        },
        getAllModules: function () {
            return moduleInjection;
        }
    };

    BI.Constants = {
        getConstant: function (type) {
            return constantInjection[type];
        }
    };

    var callPoint = function (inst, types) {
        types = BI.isArray(types) ? types : [types];
        BI.each(types, function (idx, type) {
            if (points[type]) {
                for (var action in points[type]) {
                    var bfns = points[type][action].before;
                    if (bfns) {
                        BI.aspect.before(inst, action, function (bfns) {
                            return function () {
                                for (var i = 0, len = bfns.length; i < len; i++) {
                                    try {
                                        bfns[i].apply(inst, arguments);
                                    } catch (e) {
                                        _global.console && console.error(e);
                                    }
                                }
                            };
                        }(bfns));
                    }
                    var afns = points[type][action].after;
                    if (afns) {
                        BI.aspect.after(inst, action, function (afns) {
                            return function () {
                                for (var i = 0, len = afns.length; i < len; i++) {
                                    try {
                                        afns[i].apply(inst, arguments);
                                    } catch (e) {
                                        _global.console && console.error(e);
                                    }
                                }
                            };
                        }(afns));
                    }
                }
            }
        });
    };

    BI.Models = {
        getModel: function (type, config) {
            var inst = new modelInjection[type](config);
            inst._constructor && inst._constructor(config);
            inst.mixins && callPoint(inst, inst.mixins);
            callPoint(inst, type);
            return inst;
        }
    };

    var stores = {};

    BI.Stores = {
        getStore: function (type, config) {
            if (stores[type]) {
                return stores[type];
            }
            var inst = stores[type] = new storeInjection[type](config);
            inst._constructor && inst._constructor(config);
            callPoint(inst, type);
            return inst;
        }
    };

    var services = {};

    BI.Services = {
        getService: function (type, config) {
            if (services[type]) {
                return services[type];
            }
            services[type] = new serviceInjection[type](config);
            callPoint(services[type], type);
            return services[type];
        }
    };

    var providers = {},
        providerInstance = {};

    BI.Providers = {
        getProvider: function (type, config) {
            if (!providers[type]) {
                providers[type] = new providerInjection[type]();
            }
            if (!providerInstance[type]) {
                providerInstance[type] = new (providers[type].$get())(config);
            }
            return providerInstance[type];
        }
    };

    BI.Actions = {
        runAction: function (type, event, config) {
            BI.each(actions[type], function (i, act) {
                try {
                    act(event, config);
                } catch (e) {
                    _global.console && console.error(e);
                }
            });
        },
        runGlobalAction: function () {
            var args = [].slice.call(arguments);
            BI.each(globalAction, function (i, act) {
                try {
                    act.apply(null, args);
                } catch (e) {
                    _global.console && console.error(e);
                }
            });
        }
    };

    BI.getContext = function (type, config) {
        if (constantInjection[type]) {
            return BI.Constants.getConstant(type);
        }
        if (modelInjection[type]) {
            return BI.Models.getModel(type, config);
        }
        if (storeInjection[type]) {
            return BI.Stores.getStore(type, config);
        }
        if (serviceInjection[type]) {
            return BI.Services.getService(type, config);
        }
        if (providerInjection[type]) {
            return BI.Providers.getProvider(type, config);
        }
    };
})();<|endoftext|>


<commit_before>const nordpool = require('nordpool')
const prices = new nordpool.Prices()
const moment = require('moment-timezone')

async function run () {
  var opts = {
    currency: 'NOK',
    area: 'Bergen',
    from: '2016-01-01'
  }
  const results = await prices.weekly(opts)
  for (var i = 0; i < results.length; i++) {
    var date = results[i].date
    var price = results[i].value
    var weeklyPriceMessage = 'The price on week ' + moment(date).format('W/GGGG') +
      ' was ' + price + ' NOK/MWh'
    console.log(weeklyPriceMessage)
  }
}
run()
<commit_msg>Added Intl.NumberFormat example, renamed run() to getWeekly()<commit_after>const nordpool = require('nordpool')
const moment = require('moment-timezone')

const opts = {
  currency: 'NOK',
  area: 'Bergen',
  from: '2019-06-01'
}

getWeekly = async () => {
  const prices = await new nordpool.Prices().weekly(opts)
  for (const week of prices.reverse()) {
    const weeklyPriceMessage = 'The MWh price on week ' +
      moment(week.date).format('W/GGGG') +
      ' was ' + priceFormat.format(week.value)
    console.log(weeklyPriceMessage)
  }
}
getWeekly()

const priceFormat = new Intl.NumberFormat('no-NO', {
  style: 'currency',
  currency: 'NOK',
  minimumFractionDigits: 2
});
<|endoftext|>