<commit_before>iD.modes.DragNode = function(context) {
    var mode = {
        id: 'drag-node',
        button: 'browse'
    };

    var nudgeInterval,
        activeIDs,
        wasMidpoint,
        cancelled,
        hover = iD.behavior.Hover().altDisables(true);

    function edge(point, size) {
        var pad = [30, 100, 30, 100];
        if (point[0] > size[0] - pad[0]) return [-10, 0];
        else if (point[0] < pad[2]) return [10, 0];
        else if (point[1] > size[1] - pad[1]) return [0, -10];
        else if (point[1] < pad[3]) return [0, 10];
        return null;
    }

    function startNudge(nudge) {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = window.setInterval(function() {
            context.pan(nudge);
        }, 50);
    }

    function stopNudge() {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = null;
    }

    function moveAnnotation(entity) {
        return t('operations.move.annotation.' + entity.geometry(context.graph()));
    }

    function connectAnnotation(datum) {
        return t('operations.connect.annotation.' + datum.geometry(context.graph()));
    }

    function origin(entity) {
        return context.projection(entity.loc);
    }

    function start(entity) {
        cancelled = d3.event.sourceEvent.shiftKey;
        if (cancelled) return behavior.cancel();

        wasMidpoint = entity.type === 'midpoint';
        if (wasMidpoint) {
            var midpoint = entity;
            entity = iD.Node();
            context.perform(iD.actions.AddMidpoint(midpoint, entity));

             var vertex = context.surface()
                .selectAll('.vertex')
                .filter(function(d) { return d.id === entity.id; });
             behavior.target(vertex.node(), entity);

        } else {
            context.perform(
                iD.actions.Noop());
        }

        activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
        activeIDs.push(entity.id);

        context.enter(mode);
    }

    function datum() {
        if (d3.event.sourceEvent.altKey) {
            return {};
        }

        return d3.event.sourceEvent.target.__data__ || {};
    }

    function move(entity) {
        if (cancelled) return;
        d3.event.sourceEvent.stopPropagation();

        var nudge = edge(d3.event.point, context.map().size());
        if (nudge) startNudge(nudge);
        else stopNudge();

        var loc = context.map().mouseCoordinates();

        var d = datum();
        if (d.type === 'node' && d.id !== entity.id) {
            loc = d.loc;
        } else if (d.type === 'way') {
            loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
        }

        context.replace(
            iD.actions.MoveNode(entity.id, loc),
            t('operations.move.annotation.' + entity.geometry(context.graph())));
    }

    function end(entity) {
        if (cancelled) return;

        var d = datum();

        if (d.type === 'way') {
            var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
            context.replace(
                iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
                connectAnnotation(d));

        } else if (d.type === 'node' && d.id !== entity.id) {
            context.replace(
                iD.actions.Connect([entity.id, d.id]),
                connectAnnotation(d));

        } else if (wasMidpoint) {
            context.replace(
                iD.actions.Noop(),
                t('operations.add.annotation.vertex'));

        } else {
            context.replace(
                iD.actions.Noop(),
                moveAnnotation(entity));
        }

        context.enter(iD.modes.Browse(context));
    }

    function cancel() {
        behavior.cancel();
        context.enter(iD.modes.Browse(context));
    }

    var behavior = iD.behavior.drag()
        .delegate("g.node, g.point, g.midpoint")
        .surface(context.surface().node())
        .origin(origin)
        .on('start', start)
        .on('move', move)
        .on('end', end);

    mode.enter = function() {
        context.install(hover);

        context.history()
            .on('undone.drag-node', cancel);

        context.surface()
            .selectAll('.node, .way')
            .filter(function(d) { return activeIDs.indexOf(d.id) >= 0; })
            .classed('active', true);
    };

    mode.exit = function() {
        context.uninstall(hover);

        context.history()
            .on('undone.drag-node', null);

        context.surface()
            .selectAll('.active')
            .classed('active', false);

        stopNudge();
    };

    mode.behavior = behavior;

    return mode;
};
<commit_msg>Stop nudging when users drag outside of the window. Fixes #1326<commit_after>iD.modes.DragNode = function(context) {
    var mode = {
        id: 'drag-node',
        button: 'browse'
    };

    var nudgeInterval,
        activeIDs,
        wasMidpoint,
        cancelled,
        hover = iD.behavior.Hover().altDisables(true);

    function edge(point, size) {
        var pad = [30, 100, 30, 100];
        if (point[0] > size[0] - pad[0]) return [-10, 0];
        else if (point[0] < pad[2]) return [10, 0];
        else if (point[1] > size[1] - pad[1]) return [0, -10];
        else if (point[1] < pad[3]) return [0, 10];
        return null;
    }

    function startNudge(nudge) {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = window.setInterval(function() {
            context.pan(nudge);
        }, 50);
    }

    function stopNudge() {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = null;
    }

    function moveAnnotation(entity) {
        return t('operations.move.annotation.' + entity.geometry(context.graph()));
    }

    function connectAnnotation(datum) {
        return t('operations.connect.annotation.' + datum.geometry(context.graph()));
    }

    function origin(entity) {
        return context.projection(entity.loc);
    }

    function start(entity) {
        cancelled = d3.event.sourceEvent.shiftKey;
        if (cancelled) return behavior.cancel();

        wasMidpoint = entity.type === 'midpoint';
        if (wasMidpoint) {
            var midpoint = entity;
            entity = iD.Node();
            context.perform(iD.actions.AddMidpoint(midpoint, entity));

             var vertex = context.surface()
                .selectAll('.vertex')
                .filter(function(d) { return d.id === entity.id; });
             behavior.target(vertex.node(), entity);

        } else {
            context.perform(
                iD.actions.Noop());
        }

        activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
        activeIDs.push(entity.id);

        context.enter(mode);
    }

    function datum() {
        if (d3.event.sourceEvent.altKey) {
            return {};
        }

        return d3.event.sourceEvent.target.__data__ || {};
    }

    // via https://gist.github.com/shawnbot/4166283
    function childOf(p, c) {
        if (p === c) return false;
        while (c && c !== p) c = c.parentNode;
        return c === p;
    }

    function move(entity) {
        if (cancelled) return;
        d3.event.sourceEvent.stopPropagation();

        var nudge = childOf(context.container().node(),
            d3.event.sourceEvent.toElement) &&
            edge(d3.event.point, context.map().size());

        if (nudge) startNudge(nudge);
        else stopNudge();

        var loc = context.map().mouseCoordinates();

        var d = datum();
        if (d.type === 'node' && d.id !== entity.id) {
            loc = d.loc;
        } else if (d.type === 'way') {
            loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
        }

        context.replace(
            iD.actions.MoveNode(entity.id, loc),
            t('operations.move.annotation.' + entity.geometry(context.graph())));
    }

    function end(entity) {
        if (cancelled) return;

        var d = datum();

        if (d.type === 'way') {
            var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
            context.replace(
                iD.actions.AddMidpoint({ loc: choice.loc, edge: [d.nodes[choice.index - 1], d.nodes[choice.index]] }, entity),
                connectAnnotation(d));

        } else if (d.type === 'node' && d.id !== entity.id) {
            context.replace(
                iD.actions.Connect([entity.id, d.id]),
                connectAnnotation(d));

        } else if (wasMidpoint) {
            context.replace(
                iD.actions.Noop(),
                t('operations.add.annotation.vertex'));

        } else {
            context.replace(
                iD.actions.Noop(),
                moveAnnotation(entity));
        }

        context.enter(iD.modes.Browse(context));
    }

    function cancel() {
        behavior.cancel();
        context.enter(iD.modes.Browse(context));
    }

    var behavior = iD.behavior.drag()
        .delegate("g.node, g.point, g.midpoint")
        .surface(context.surface().node())
        .origin(origin)
        .on('start', start)
        .on('move', move)
        .on('end', end);

    mode.enter = function() {
        context.install(hover);

        context.history()
            .on('undone.drag-node', cancel);

        context.surface()
            .selectAll('.node, .way')
            .filter(function(d) { return activeIDs.indexOf(d.id) >= 0; })
            .classed('active', true);
    };

    mode.exit = function() {
        context.uninstall(hover);

        context.history()
            .on('undone.drag-node', null);

        context.surface()
            .selectAll('.active')
            .classed('active', false);

        stopNudge();
    };

    mode.behavior = behavior;

    return mode;
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import uniqueId from 'lodash/uniqueId';
import classNames from 'classnames';
import { DefaultAccordionHeader } from './headers';
import css from './Accordion.css';
import { HotKeys } from '../HotKeys';

const propTypes = {
  open: PropTypes.bool,
  id: PropTypes.string,
  contentId: PropTypes.string,
  label: PropTypes.oneOfType([PropTypes.element, PropTypes.string]).isRequired, // eslint-disable-line react/no-unused-prop-types
  displayWhenOpen: PropTypes.element, // eslint-disable-line react/no-unused-prop-types
  displayWhenClosed: PropTypes.element, // eslint-disable-line react/no-unused-prop-types
  onToggle: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  header: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.func]),
  contentRef: PropTypes.func,
  separator: PropTypes.bool,
  toggleKeyHandlers: PropTypes.object,
  toggleKeyMap: PropTypes.object,
  children: PropTypes.oneOfType([
    PropTypes.node,
  ]).isRequired,
  closedByDefault: PropTypes.bool,
};

class Accordion extends React.Component {
  static defaultProps = {
    header: DefaultAccordionHeader,
    separator: true,
  }

  constructor(props) {
    super(props);

    this.uncontrolledToggle = this.uncontrolledToggle.bind(this);
    this.initializeAccordion = this.initializeAccordion.bind(this);
    this.setContentRef = this.setContentRef.bind(this);
    this.tearDownHandlers = this.tearDownHandlers.bind(this);
    this.syncMaxHeight = this.syncMaxHeight.bind(this);
    this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
    this.collapseRaf = this.collapseRaf.bind(this);

    // ref to content div.
    this.content = null;
    // ref to toggle.
    this.toggle = null;

    // tracked scrollHeight of content div
    this.contentHeight = 0;
    // whether this.contentHeight should be updated (false indicates difference.)
    this.syncedHeight = true;

    // animation callback for expansion requestAnimationFrame (RAF)
    this.expandRAFCallback = null;
    // animation callback for collapsing requestAnimationFrame (RAF)
    this.collapseRAFCallback = null;

    // callback for syncing
    this.syncHeightCallback = null;

    // status indicators for transition - true = mid transition.
    this.transitioningOpen = false;
    this.transitioningClosed = false;

    // id for content div needed for proper area support - either supplied or auto-generated.
    this.contentId = this.initializeContentId();

    // local state may or may not be set up if open prop is passed.
    this.state = this.initializeAccordion();
  }

  componentDidMount() {
    this.syncMaxHeight();
    // fix issue with accordion not animating open if closed on mount.
    if ((Object.prototype.hasOwnProperty.call(this.props, 'open') && !this.props.open) ||
        (Object.prototype.hasOwnProperty.call(this.state, 'isOpen') && !this.state.isOpen)) {
      this.content.style.maxHeight = '0';
    }
  }

  componentWillUpdate(nextProps, nextState) {
    // prop/state changes to open status set off the animation callbacks.
    let shouldAdjust = false;
    // if using state...
    if (typeof nextState.isOpen === 'boolean') {
      if (nextState.isOpen !== this.state.isOpen) {
        if (nextState.isOpen) {
          this.transitioningOpen = true;
          this.syncedHeight = false;
          this.expandRaf();
        } else {
          shouldAdjust = true;
          this.transitioningClosed = true;
          this.collapseRaf();
        }
      }
    } else if (nextProps.open !== this.props.open) {
      if (nextProps.open) {
        this.transitioningOpen = true;
        this.syncedHeight = false;
        this.expandRaf();
      } else {
        shouldAdjust = true;
        this.transitioningClosed = true;
        this.collapseRaf();
      }
    }

    if (shouldAdjust) {
      this.syncMaxHeight();
    }
  }

  componentWillUnmount() {
    this.tearDownHandlers();
  }

  syncMaxHeight() {
    this.syncHeightCallback = window.requestAnimationFrame(() => {
      this.contentHeight = this.content.scrollHeight;
      this.syncedHeight = true;
      this.forceUpdate();
    });
  }

  collapseRaf() {
    if (this.collapseCallback) { // if there's a collapse in progress, let it finish...
      this.transitioningClosed = false;
      return;
    }
    this.transitioningOpen = false;
    this.collapseCallback = window.requestAnimationFrame(() => {
      this.content.style.maxHeight = `${this.contentHeight}px`;
      window.cancelAnimationFrame(this.collapseCallback);
      this.collapseCallback = window.requestAnimationFrame(() => {
        this.content.style.maxHeight = '0';
        window.cancelAnimationFrame(this.collapseCallback);
        this.collapseCallback = null;
      });
    });
  }

  expandRaf() {
    if (this.expandCallback) {
      this.transitioningOpen = false;
      return;
    }
    this.transitioningClosed = false;
    this.expandCallback = window.requestAnimationFrame(() => {
      // update contentHeight...
      this.contentHeight = this.content.scrollHeight;
      this.content.style.maxHeight = `${this.contentHeight}px`;
      window.cancelAnimationFrame(this.expandCallback);
      this.expandCallback = null;
    });
  }

  initializeAccordion() {
    // if no 'open' boolean or 'id' prop is provided, set up our own state...
    if (this.props.open === undefined && this.props.id === undefined) {
      if (this.props.closedByDefault) {
        return {};
      }
      return { isOpen: true };
    }
    return {};
  }

  initializeContentId() {
    // generate unique id if no id is provided.
    if (this.props.contentId === undefined) {
      this.contentId = uniqueId('accordion');
    } else {
      this.contentId = this.props.contentId;
    }
  }

  tearDownHandlers() {
    window.cancelAnimationFrame(this.syncHeightCallback);
    window.cancelAnimationFrame(this.expandCallback);
    window.cancelAnimationFrame(this.collapseCallback);
  }

  uncontrolledToggle() {
    this.setState((curState) => {
      const newState = { ...curState };
      newState.isOpen = !curState.isOpen;
      return newState;
    });
  }

  setContentRef(ref) {
    this.content = ref;
    if (this.props.contentRef) {
      this.props.contentRef(ref);
    }
  }

  handleTransitionEnd() {
    if (this.transitioningOpen) {
      this.transitioningOpen = false;
      this.content.style.maxHeight = 'none';
    }
    if (this.transitioningClosed) {
      this.transitioningClosed = false;
    }
  }

  // eslint-disable-next-line class-methods-use-this
  getContentClass(open) {
    return classNames(
      css.content,
      { [`${css.expanded}`]: open },
    );
  }

  // eslint-disable-next-line class-methods-use-this
  getRootClasses() {
    return classNames(
      css.accordion,
      { [css.hasSeparator]: this.props.separator },
    );
  }

  render() {
    let open;
    if (this.props.open !== undefined) {
      open = this.props.open;
    } else {
      open = this.state.isOpen;
    }

    let onToggle;
    if (this.props.onToggle && this.props.open !== undefined) {
      onToggle = this.props.onToggle;
    } else {
      onToggle = this.uncontrolledToggle;
    }

    const headerProps = Object.assign({}, this.props, { contentId: this.contentId, toggleRef: (ref) => { this.toggle = ref; }, open, onToggle });
    const headerElement = React.createElement(this.props.header, headerProps);

    return (

        <HotKeys keyMap={this.props.toggleKeyMap} handlers={this.props.toggleKeyHandlers} noWrapper >
          {headerElement}
        </HotKeys>

          {this.props.children}

    );
  }
}
// {this.props.separator &&
}

Accordion.propTypes = propTypes;

export default Accordion;
<commit_msg>Set max height on mount with edge case when open prop is undefined<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import uniqueId from 'lodash/uniqueId';
import classNames from 'classnames';
import { DefaultAccordionHeader } from './headers';
import css from './Accordion.css';
import { HotKeys } from '../HotKeys';

const propTypes = {
  open: PropTypes.bool,
  id: PropTypes.string,
  contentId: PropTypes.string,
  label: PropTypes.oneOfType([PropTypes.element, PropTypes.string]).isRequired, // eslint-disable-line react/no-unused-prop-types
  displayWhenOpen: PropTypes.element, // eslint-disable-line react/no-unused-prop-types
  displayWhenClosed: PropTypes.element, // eslint-disable-line react/no-unused-prop-types
  onToggle: PropTypes.func, // eslint-disable-line react/no-unused-prop-types
  header: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.func]),
  contentRef: PropTypes.func,
  separator: PropTypes.bool,
  toggleKeyHandlers: PropTypes.object,
  toggleKeyMap: PropTypes.object,
  children: PropTypes.oneOfType([
    PropTypes.node,
  ]).isRequired,
  closedByDefault: PropTypes.bool,
};

class Accordion extends React.Component {
  static defaultProps = {
    header: DefaultAccordionHeader,
    separator: true,
  }

  constructor(props) {
    super(props);

    this.uncontrolledToggle = this.uncontrolledToggle.bind(this);
    this.initializeAccordion = this.initializeAccordion.bind(this);
    this.setContentRef = this.setContentRef.bind(this);
    this.tearDownHandlers = this.tearDownHandlers.bind(this);
    this.syncMaxHeight = this.syncMaxHeight.bind(this);
    this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
    this.collapseRaf = this.collapseRaf.bind(this);

    // ref to content div.
    this.content = null;
    // ref to toggle.
    this.toggle = null;

    // tracked scrollHeight of content div
    this.contentHeight = 0;
    // whether this.contentHeight should be updated (false indicates difference.)
    this.syncedHeight = true;

    // animation callback for expansion requestAnimationFrame (RAF)
    this.expandRAFCallback = null;
    // animation callback for collapsing requestAnimationFrame (RAF)
    this.collapseRAFCallback = null;

    // callback for syncing
    this.syncHeightCallback = null;

    // status indicators for transition - true = mid transition.
    this.transitioningOpen = false;
    this.transitioningClosed = false;

    // id for content div needed for proper area support - either supplied or auto-generated.
    this.contentId = this.initializeContentId();

    // local state may or may not be set up if open prop is passed.
    this.state = this.initializeAccordion();
  }

  componentDidMount() {
    this.syncMaxHeight();
    // fix issue with accordion not animating open if closed on mount.
    if ((Object.prototype.hasOwnProperty.call(this.props, 'open') && this.props.open === false) ||
        (Object.prototype.hasOwnProperty.call(this.state, 'isOpen') && !this.state.isOpen)) {
      this.content.style.maxHeight = '0';
    }
  }

  componentWillUpdate(nextProps, nextState) {
    // prop/state changes to open status set off the animation callbacks.
    let shouldAdjust = false;
    // if using state...
    if (typeof nextState.isOpen === 'boolean') {
      if (nextState.isOpen !== this.state.isOpen) {
        if (nextState.isOpen) {
          this.transitioningOpen = true;
          this.syncedHeight = false;
          this.expandRaf();
        } else {
          shouldAdjust = true;
          this.transitioningClosed = true;
          this.collapseRaf();
        }
      }
    } else if (nextProps.open !== this.props.open) {
      if (nextProps.open) {
        this.transitioningOpen = true;
        this.syncedHeight = false;
        this.expandRaf();
      } else {
        shouldAdjust = true;
        this.transitioningClosed = true;
        this.collapseRaf();
      }
    }

    if (shouldAdjust) {
      this.syncMaxHeight();
    }
  }

  componentWillUnmount() {
    this.tearDownHandlers();
  }

  syncMaxHeight() {
    this.syncHeightCallback = window.requestAnimationFrame(() => {
      this.contentHeight = this.content.scrollHeight;
      this.syncedHeight = true;
      this.forceUpdate();
    });
  }

  collapseRaf() {
    if (this.collapseCallback) { // if there's a collapse in progress, let it finish...
      this.transitioningClosed = false;
      return;
    }
    this.transitioningOpen = false;
    this.collapseCallback = window.requestAnimationFrame(() => {
      this.content.style.maxHeight = `${this.contentHeight}px`;
      window.cancelAnimationFrame(this.collapseCallback);
      this.collapseCallback = window.requestAnimationFrame(() => {
        this.content.style.maxHeight = '0';
        window.cancelAnimationFrame(this.collapseCallback);
        this.collapseCallback = null;
      });
    });
  }

  expandRaf() {
    if (this.expandCallback) {
      this.transitioningOpen = false;
      return;
    }
    this.transitioningClosed = false;
    this.expandCallback = window.requestAnimationFrame(() => {
      // update contentHeight...
      this.contentHeight = this.content.scrollHeight;
      this.content.style.maxHeight = `${this.contentHeight}px`;
      window.cancelAnimationFrame(this.expandCallback);
      this.expandCallback = null;
    });
  }

  initializeAccordion() {
    // if no 'open' boolean or 'id' prop is provided, set up our own state...
    if (this.props.open === undefined && this.props.id === undefined) {
      if (this.props.closedByDefault) {
        return {};
      }
      return { isOpen: true };
    }
    return {};
  }

  initializeContentId() {
    // generate unique id if no id is provided.
    if (this.props.contentId === undefined) {
      this.contentId = uniqueId('accordion');
    } else {
      this.contentId = this.props.contentId;
    }
  }

  tearDownHandlers() {
    window.cancelAnimationFrame(this.syncHeightCallback);
    window.cancelAnimationFrame(this.expandCallback);
    window.cancelAnimationFrame(this.collapseCallback);
  }

  uncontrolledToggle() {
    this.setState((curState) => {
      const newState = { ...curState };
      newState.isOpen = !curState.isOpen;
      return newState;
    });
  }

  setContentRef(ref) {
    this.content = ref;
    if (this.props.contentRef) {
      this.props.contentRef(ref);
    }
  }

  handleTransitionEnd() {
    if (this.transitioningOpen) {
      this.transitioningOpen = false;
      this.content.style.maxHeight = 'none';
    }
    if (this.transitioningClosed) {
      this.transitioningClosed = false;
    }
  }

  // eslint-disable-next-line class-methods-use-this
  getContentClass(open) {
    return classNames(
      css.content,
      { [`${css.expanded}`]: open },
    );
  }

  // eslint-disable-next-line class-methods-use-this
  getRootClasses() {
    return classNames(
      css.accordion,
      { [css.hasSeparator]: this.props.separator },
    );
  }

  render() {
    let open;
    if (this.props.open !== undefined) {
      open = this.props.open;
    } else {
      open = this.state.isOpen;
    }

    let onToggle;
    if (this.props.onToggle && this.props.open !== undefined) {
      onToggle = this.props.onToggle;
    } else {
      onToggle = this.uncontrolledToggle;
    }

    const headerProps = Object.assign({}, this.props, { contentId: this.contentId, toggleRef: (ref) => { this.toggle = ref; }, open, onToggle });
    const headerElement = React.createElement(this.props.header, headerProps);

    return (

        <HotKeys keyMap={this.props.toggleKeyMap} handlers={this.props.toggleKeyHandlers} noWrapper >
          {headerElement}
        </HotKeys>

          {this.props.children}

    );
  }
}
// {this.props.separator &&
}

Accordion.propTypes = propTypes;

export default Accordion;
<|endoftext|>


<commit_before>/**
 * v1.2
 */
;(function($, doc, win) {
    "use strict";

    var name = 'js-asfar';

    function Asfar(el, opts, target) {
        this.$el  = $(el);
        this.pagecount = 0;
        this.currentHash = null;
        this.historyBackward = [];

        this.default = {
            'selector':        'a[href*="' + document.location.host + '"], a[href^="/"], a:not([href^="http://"])',
            'target':           target,
            'sheBangFallback': true,
            'sheBangInterval': 150,
            'html5Support':     true,
            'before':           function (urlFragment, target) {},
            'success':          function (urlFragment, target) {},
            'after':            function (urlFragment, target) {},
            'error':            function (jqXHR, textStatus, errorThrown, urlFragment, target) {},
            'insert':           function (urlFragment, target, data) {
			    $(target).html(data);
		    },
            'historyNext':      function(urlFragment, target, data) {
                this.opts.insert(urlFragment, target, data);
            },
            'historyPrev':      function(urlFragment, target, data) {
                this.opts.insert(urlFragment, target, data);
            }
        };

        this.opts = $.extend(this.default, opts);

        this.$el.data(name, this);

        this.$selector = $(this.opts.selector);

        this.init();
    }

    Asfar.prototype.pushState = function (urlFragment, first) {
        if (this.opts.html5Support) {
            history.pushState({type: 'ajax', stamp: (Math.random(0, 9999) * 100000) + (new Date().getTime())}, "title " + this.pagecount,urlFragment);
            this.pagecount++;
        }else{
            location.hash = '#!' + urlFragment;
            this.currentHash = location.hash;
        }
    }

    Asfar.prototype.call = function(urlFragment, target, isHistory) {
        var self = this;

        self.opts.before(urlFragment, target);

        $.ajax({
            cache: false,
            async: true,
            type: 'GET',
            dataType: "html",
            url: urlFragment,
            success: function(data) {
                // we need to wrap it, otherwise we can't select it if body is the parent
                var responseHtml = '
' + data + '
';

                if (0 !== $(responseHtml).find(target).parent().size()) {
                    data = $(responseHtml).find(target).html();
                }

                self.opts.insert(urlFragment, target, data);

                if (true !== isHistory) {
                    self.pushState(urlFragment);
                }

                self.opts.success(urlFragment, target);

                $(target).trigger('asfar', [urlFragment]);
            },
            error: function(jqXHR, textStatus, errorThrown){
                self.opts.error(jqXHR, textStatus, errorThrown, urlFragment, target);
            }
        });

        // is that really usefull?
        self.opts.after(urlFragment, target);
    }

    /**
     * @todo check viability of delegation
     */
    Asfar.prototype.init = function() {
        var self = this;

        // no matter if you want html5 support or not, if it ain't there it ain't gonna happen
        if (typeof history.pushState === 'undefined') {
            this.opts.html5Support = false;
        }

        // no html5 , no she bang fallback, no asfar *sadface*
        if (!this.opts.html5Support && !this.opts.sheBangFallback) {
            return false;
        }

        if (!this.opts.html5Support) {
            setInterval(function () {
                if ('' === location.hash) {
                    // start
                    if (null !== self.currentHash){
                        self.call(location.hash.substring(2), self.opts.target, true);
                        self.currentHash = null;
                    }
                } else {
                    if (location.hash !== self.currentHash) {
                        self.call(location.hash.substring(2), self.opts.target, false);
                    }

                }
            }, self.opts.sheBangInterval);
        } else {
            window.onpopstate = function (event) {

                if (event.state !== null) {
                    if ("ajax" === event.state.type) {
                        console.log(event.state);

                        var i, direction = 'next';

                        console.log(self.historyBackward.length);

                        if (0 < self.historyBackward.length) {
                            self.historyBackward.foreach( function( k, v ) {
                                console.log(k);
                                if (k === event.state.stamp) {
                                    self.historyBackward.splice(i, 9999999);
                                    direction = 'prev';
                                }
                                i++;
                            });
                        }

                        console.log(direction);

                        if ('next' === direction) {
                            self.historyBackward[event.state.stamp] = 'x';
                            console.log('2 ' + self.historyBackward.length);
                        }

                        self.call(document.location.pathname, self.opts.target, true);

                    }
                }
            };
        }

        // this can be copied in to the header of the html document to increase speed
        if (!this.opts.html5Support && '#!' === location.hash.substring(0,2)) {
			location.href = location.hash.substring(2);
		}

        this.$selector.on('click.' + name, function(e) {
            e.preventDefault();

            self.call($(this).attr('href'), self.opts.target, false);
        });

        if (this.opts.html5Support) {
            self.pushState(document.location.pathname);
        }
    };

    Asfar.prototype.destroy = function() {
        this.$selector.off('.' + name);

        this.$el.removeData(name);
        this.$el = null;
    };

    $.fn.asfar = function(opts) {
        var target = $(this).selector;

        // you can not initiate more then one asfar instance at a time because you would need a unique selector (a elements for each asfar content area)
        if (1 === $(this).size()) {
            return new Asfar(this, opts, target);
        }
    };

})(jQuery, document, window);<commit_msg>unstable update for version 2 #2<commit_after>/**
 * v1.2
 */
;(function($, doc, win) {
    "use strict";

    var name = 'js-asfar';

    function Asfar(el, opts, target) {
        this.$el  = $(el);
        this.pagecount = 0;
        this.currentHash = null;
        this.historyBackward = new Array();

        this.default = {
            'selector':        'a[href*="' + document.location.host + '"], a[href^="/"], a:not([href^="http://"])',
            'target':           target,
            'sheBangFallback': true,
            'sheBangInterval': 150,
            'html5Support':     true,
            'before':           function (urlFragment, target) {},
            'success':          function (urlFragment, target) {},
            'after':            function (urlFragment, target) {},
            'error':            function (jqXHR, textStatus, errorThrown, urlFragment, target) {},
            'insert':           function (urlFragment, target, data) {
			    $(target).html(data);
		    },
            'historyNext':      function(urlFragment, target, data) {
                this.opts.insert(urlFragment, target, data);
            },
            'historyPrev':      function(urlFragment, target, data) {
                this.opts.insert(urlFragment, target, data);
            }
        };

        this.opts = $.extend(this.default, opts);

        this.$el.data(name, this);

        this.$selector = $(this.opts.selector);

        this.init();
    }

    Asfar.prototype.pushState = function (urlFragment, first) {
        if (this.opts.html5Support) {
            history.pushState({type: 'ajax', stamp: 'x' + (Math.floor((Math.random()*9999)+1))}, "title " + this.pagecount,urlFragment);
            this.pagecount++;
        }else{
            location.hash = '#!' + urlFragment;
            this.currentHash = location.hash;
        }
    }

    Asfar.prototype.call = function(urlFragment, target, isHistory) {
        var self = this;

        self.opts.before(urlFragment, target);

        $.ajax({
            cache: false,
            async: true,
            type: 'GET',
            dataType: "html",
            url: urlFragment,
            success: function(data) {
                // we need to wrap it, otherwise we can't select it if body is the parent
                var responseHtml = '
' + data + '
';

                if (0 !== $(responseHtml).find(target).parent().size()) {
                    data = $(responseHtml).find(target).html();
                }

                self.opts.insert(urlFragment, target, data);

                if (true !== isHistory) {
                    self.pushState(urlFragment);
                }

                self.opts.success(urlFragment, target);

                $(target).trigger('asfar', [urlFragment]);
            },
            error: function(jqXHR, textStatus, errorThrown){
                self.opts.error(jqXHR, textStatus, errorThrown, urlFragment, target);
            }
        });

        // is that really usefull?
        self.opts.after(urlFragment, target);
    }

    /**
     * @todo check viability of delegation
     */
    Asfar.prototype.init = function() {
        var self = this;

        // no matter if you want html5 support or not, if it ain't there it ain't gonna happen
        if (typeof history.pushState === 'undefined') {
            this.opts.html5Support = false;
        }

        // no html5 , no she bang fallback, no asfar *sadface*
        if (!this.opts.html5Support && !this.opts.sheBangFallback) {
            return false;
        }

        if (!this.opts.html5Support) {
            setInterval(function () {
                if ('' === location.hash) {
                    // start
                    if (null !== self.currentHash){
                        self.call(location.hash.substring(2), self.opts.target, true);
                        self.currentHash = null;
                    }
                } else {
                    if (location.hash !== self.currentHash) {
                        self.call(location.hash.substring(2), self.opts.target, false);
                    }

                }
            }, self.opts.sheBangInterval);
        } else {
            window.onpopstate = function (event) {

                if (event.state !== null) {
                    if ("ajax" === event.state.type) {
                        console.log(event.state);

                        var i, direction = 'next', stamp;

                        stamp = event.state.stamp;

                        console.log(self.historyBackward.length);

                        if (0 < self.historyBackward.length) {
                            self.historyBackward.foreach( function( k, v ) {
                                console.log(k);
                                if (k === stamp) {
                                    self.historyBackward.splice(i, 9999999);
                                    direction = 'prev';
                                }
                                i++;
                            });
                        }

                        console.log(direction);

                        if ('next' === direction) {
                            self.historyBackward[stamp] = 'x';
                            console.log(self.historyBackward, self.historyBackward.length);
                        }

                        self.call(document.location.pathname, self.opts.target, true);

                    }
                }
            };
        }

        // this can be copied in to the header of the html document to increase speed
        if (!this.opts.html5Support && '#!' === location.hash.substring(0,2)) {
			location.href = location.hash.substring(2);
		}

        this.$selector.on('click.' + name, function(e) {
            e.preventDefault();

            self.call($(this).attr('href'), self.opts.target, false);
        });

        if (this.opts.html5Support) {
            self.pushState(document.location.pathname);
        }
    };

    Asfar.prototype.destroy = function() {
        this.$selector.off('.' + name);

        this.$el.removeData(name);
        this.$el = null;
    };

    $.fn.asfar = function(opts) {
        var target = $(this).selector;

        // you can not initiate more then one asfar instance at a time because you would need a unique selector (a elements for each asfar content area)
        if (1 === $(this).size()) {
            return new Asfar(this, opts, target);
        }
    };

})(jQuery, document, window);<|endoftext|>


<commit_before>var iostream = require('socket.io');

var Statics = {
    io_ref: null,
    routers: {}
};

var func_params = function (func) {
    var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
        params = /([^\s,]+)/g,
        func_str = func.toString().replace(comments, ''),
        start = func_str.indexOf('(') + 1,
        end = func_str.indexOf(')'),
        head = func_str.slice(start, end),
        result = head.match(params);
    return (result) ? result : [];
};

var power_set = function (list) {
    var set = [],
        listSize = list.length,
        combinationsCount = (1 << listSize);
    for (var i = 1; i < combinationsCount; i++, set.push(combination))
        for (var j = 0, combination = []; j < listSize; j++)
            if ((i & (1 << j)))
                combination.push(list[j]);
    return set;
};

var Router = function () {
    var Handler = function (func) {
        var params = func_params(func),
            p = params.filter(function (v) { return (v[0] !== '$'); }),
            o = params.filter(function (v) { return (v[0] === '$'); }),
            o = (function (a) {
                for (var i in a) a[i] = a[i].substr(1); return a;
            }(o)),
            pset = power_set(o),
            possible = (p.length > 0) ? [p.sort().join(' ')] : [];
        for (var i in pset) {
            possible.push(p.concat(pset[i]).sort().join(' '));
        }
        return {
            params: params,
            keys: possible,
            func: func,
            execute: function (socket, data) {
                var k, p = [];
                if (data) {
                    for (var i in this.params) {
                        k = this.params[i];
                        if (k[0] === '$') p.push(data[k.substr(1)]);
                        else p.push(data[k]);
                    }
                }
                this.func.apply(socket, p);
            }
        };
    };
    return {
        _handlers: {},
        getIO: function () { return Statics.io_ref; },
        add: function (func) {
            var h = new Handler(func);
            if (!this._add_handler(h)) {
                throw new Error('Cannot add socket handler for path' +
                                ' that already exists.');
            }
        },
        addException: function (func) {
            if (this._handlers.except) {
                throw new Error('Exception handler already added for event.');
            }
            this._handlers.except = new Handler(func);
        },
        run: function (socket, data) {
            var h, k;
            if (!data || typeof(data) !== 'object' ||
                    Object.keys(data).length < 1) {
                h = this._handlers.do;
            }
            else {
                h = this._handlers[Object.keys(data.sort()).join(' ')];
            }
            if (!h) {
                h = this._handlers.except;
            }
            if (!this._run_handler(h, socket, data)) {
                throw new Error('Uncaught event passed: ' + JSON.stringify(data));
            }
        },
        _run_handler: function (h, socket, data) {
            if (!h) return false;
            h.execute(socket, data);
            return true;
        },
        _add_handler: function (h) {
            if (h.params.length === 0) {
                if (this._handlers.do) return false;
                this._handlers.do = h;
            }
            else {
                for (var i in h.keys) {
                    if (h.keys[i]) {
                        if (this._handlers[h.keys[i]]) return false;
                        this._handlers[h.keys[i]] = h;
                    }
                }
            }
            return true;
        }
    };
};

var init = function (server, middleware) {
    Statics.io_ref = iostream(server);
    Statics.io_ref.use(middleware);
    Statics.io_ref.on('connection', function (socket) {
        var r;
        if (Statics.routers['connection']) {
            // Connection Handler
            Statics.routers['connection'].run(socket);
        }
        // All other handlers
        console.log(Statics.routers);
        for (var event in Statics.routers) {
            if (event !== 'connection') {
                console.log('Adding listener for ' + event);
                r = Statics.routers[event];
                socket.on(event, function(data) {
                    r.run(socket, data);
                });
            }
        }
    });
};

var use = function (event, router) {
    Statics.routers[event] = router;
};

module.exports = {
    Router: Router,
    initialize: init,
    use: use
};<commit_msg>[Debug] Wrong event listeners firing.<commit_after>var iostream = require('socket.io');

var Statics = {
    io_ref: null,
    routers: {}
};

var func_params = function (func) {
    var comments = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
        params = /([^\s,]+)/g,
        func_str = func.toString().replace(comments, ''),
        start = func_str.indexOf('(') + 1,
        end = func_str.indexOf(')'),
        head = func_str.slice(start, end),
        result = head.match(params);
    return (result) ? result : [];
};

var power_set = function (list) {
    var set = [],
        listSize = list.length,
        combinationsCount = (1 << listSize);
    for (var i = 1; i < combinationsCount; i++, set.push(combination))
        for (var j = 0, combination = []; j < listSize; j++)
            if ((i & (1 << j)))
                combination.push(list[j]);
    return set;
};

var Router = function () {
    var Handler = function (func) {
        var params = func_params(func),
            p = params.filter(function (v) { return (v[0] !== '$'); }),
            o = params.filter(function (v) { return (v[0] === '$'); }),
            o = (function (a) {
                for (var i in a) a[i] = a[i].substr(1); return a;
            }(o)),
            pset = power_set(o),
            possible = (p.length > 0) ? [p.sort().join(' ')] : [];
        for (var i in pset) {
            possible.push(p.concat(pset[i]).sort().join(' '));
        }
        return {
            params: params,
            keys: possible,
            func: func,
            execute: function (socket, data) {
                var k, p = [];
                if (data) {
                    for (var i in this.params) {
                        k = this.params[i];
                        if (k[0] === '$') p.push(data[k.substr(1)]);
                        else p.push(data[k]);
                    }
                }
                this.func.apply(socket, p);
            }
        };
    };
    return {
        _handlers: {},
        getIO: function () { return Statics.io_ref; },
        add: function (func) {
            var h = new Handler(func);
            if (!this._add_handler(h)) {
                throw new Error('Cannot add socket handler for path' +
                                ' that already exists.');
            }
        },
        addException: function (func) {
            if (this._handlers.except) {
                throw new Error('Exception handler already added for event.');
            }
            this._handlers.except = new Handler(func);
        },
        run: function (socket, data) {
            var h, k;
            if (!data || typeof(data) !== 'object' ||
                    Object.keys(data).length < 1) {
                h = this._handlers.do;
            }
            else {
                h = this._handlers[Object.keys(data.sort()).join(' ')];
            }
            if (!h) {
                h = this._handlers.except;
            }
            if (!this._run_handler(h, socket, data)) {
                throw new Error('Uncaught event passed: ' + JSON.stringify(data));
            }
        },
        _run_handler: function (h, socket, data) {
            if (!h) return false;
            h.execute(socket, data);
            return true;
        },
        _add_handler: function (h) {
            if (h.params.length === 0) {
                if (this._handlers.do) return false;
                this._handlers.do = h;
            }
            else {
                for (var i in h.keys) {
                    if (h.keys[i]) {
                        if (this._handlers[h.keys[i]]) return false;
                        this._handlers[h.keys[i]] = h;
                    }
                }
            }
            return true;
        }
    };
};

var init = function (server, middleware) {
    Statics.io_ref = iostream(server);
    Statics.io_ref.use(middleware);
    Statics.io_ref.on('connection', function (socket) {
        var f = function (event) {
                return function (data) {
                    console.log("Firing event: " + event);
                    Statics.routers[event].run(socket, data);
                };
            };
        if (Statics.routers['connection']) {
            // Connection Handler
            Statics.routers['connection'].run(socket);
        }
        // All other handlers
        console.log(Statics.routers);
        for (var event in Statics.routers) {
            if (event !== 'connection') {
                console.log('Adding listener for ' + event);
                socket.on(event, f(event));
            }
        }
    });
};

var use = function (event, router) {
    Statics.routers[event] = router;
};

module.exports = {
    Router: Router,
    initialize: init,
    use: use
};<|endoftext|>


<commit_before>(function() {
  var commentPattern = /(\/\*)[\s\S]*?(\*\/)/g;
  var cssRulePattern = /\s*([^{]+)\{([^}]*)\}\s*/g;
  var queryPattern = /:media\s*\(([^)]*)\)/g;
  var selectorPattern = /(?:^|\})\s*([^{]+)\s*/g;
  var queryRulesPattern = /\(?([^\s:]+):\s*(\d+(?:\.\d+)?)(px|em|rem|vw|vh|vmin|vmax)\)?/g;
  var mediaPattern = /(@media[^{]*)\{((?:\s*([^{]+)\{([^}]*)\}\s*)*)\}/g

  var queryMatchers = {
    "max-available-width": function(element, value, units) {
      var parent = element.parentNode;
      var px = convertToPx(parent, value, units);
      return value && parent && parent.offsetWidth <= px;
    },

    "min-available-width": function(element, value, units) {
      var parent = element.parentNode;
      var px = convertToPx(parent, value, units);
      return value && parent && parent.offsetWidth >= px;
    },
  };

  var queries = [];

  var query = {
    selector: "string",
    className: "string",
    match: function() {}
  };

  var ElementQuery = function() {};

  var queriesForSelector = function(selectorString) {
    var selectors = selectorString.split(",");
    var selectorResults = [];
    var queryResults = [];
    for (var i = 0, len = selectors.length; i < len; i++) {
      var result = queriesForSingleSelector(selectors[i]);
      selectorResults.push(result.selector);
      queryResults = queryResults.concat(result.queries);
    }
    return {
      selector: selectorResults.join(","),
      queries: queryResults
    };
  };

  var queriesForSingleSelector = function(selectorString) {
    var queries = [];
    var newSelector = "";
    var lastIndex = 0;
    var queryMatch;
    while (queryMatch = queryPattern.exec(selectorString)) {
      var querySelector = selectorString.slice(0, queryMatch.index);
      var queryRules = parseQuery(queryMatch[1]);
      var className = classNameForRules(queryRules);
      newSelector += selectorString.slice(lastIndex, queryMatch.index);
      lastIndex = queryMatch.index + queryMatch[0].length;
      queries.push({
        selector: newSelector,
        className: className,
        rules: queryRules
      });
      newSelector += "." + className;
    }
    newSelector += selectorString.slice(lastIndex);
    return {
      selector: newSelector,
      queries: queries
    };
  };

  var parseQuery = function(queryString) {
    var rules = [];
    var ruleMatch;
    while (ruleMatch = queryRulesPattern.exec(queryString)) {
      rules.push({
        property: ruleMatch[1],
        value: parseFloat(ruleMatch[2]),
        units: ruleMatch[3]
      });
    }
    return rules;
  };

  var classNameForRules = function(rules) {
    var name = "query";
    for (var i = 0, len = rules.length; i < len; i++) {
      name += "_" + rules[i].property + "_" + rules[i].value + rules[i].units;
    }
    return name;
  };

  var convertToPx = function(element, value, units) {
    switch (units) {
      case "px": return value;
      case "em": return value * getEmSize(element);
      case "rem": return value * getEmSize();
      case "vw": return value * document.documentElement.clientWidth / 100;
      case "vh": return value * document.documentElement.clientHeight / 100;
      case "vmin":
      case "vmax":
        var vw = document.documentElement.clientWidth / 100;
        var vh = document.documentElement.clientHeight / 100;
        var chooser = Math[units === "vmin" ? "min" : "max"];
        return value * chooser(vw, vh);
      default: return value;
      // for now, not supporting physical units (since they are just a set number of px)
      // or ex/ch (getting accurate measurements is hard)
    }
  };

  var getEmSize = function(element) {
    if (!element) {
      element = document.documentElement;
    }
    if (window.getComputedStyle) {
      return parseFloat(getComputedStyle(element).fontSize) || 16;
    }
    // TODO: support IE?
    return 16;
  };

  var scanStyleSheets = function(sheets, callback) {
    var completed = 0;
    for (var i = 0, len = sheets.length; i < len; i++) {
      scanStyleSheet(sheets[i], function() {
        completed += 1;
        if (completed === len) {
          callback && callback();
        }
      });
    }
  };

  var scanStyleSheet = function(sheet, callback) {
    if (sheet.ownerNode.nodeName === "STYLE") {
      var newStyles = scanStyleString(sheet.ownerNode.innerHTML);
      sheet.ownerNode.innerHTML += newStyles;
      callback && callback();
    }
    else if (sheet.href) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", sheet.href, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            var newStyles = scanStyleString(xhr.responseText);
            var style = document.createElement("style");
            style.innerHTML = newStyles;
            document.body.appendChild(style);
          }
          else if (window.console) {
            console.log("Could not load stylesheet at " + sheet.href);
          }
          callback && callback();
        }
      }
      xhr.send(null);
    }
  };

  var scanStyleString = function(sheet) {
    // new stylesheet content to add (replacing rules with `:media()` in them)
    var newRules = "";

    // remove comments
    sheet = sheet.replace(commentPattern, "");

    // manage vanilla media queries
    var mediaRules = "";
    sheet = sheet.replace(mediaPattern, function(mediaString, query, content) {
      var newMediaRules = scanStyleString(content);
      if (!/^\s*$/.test(newMediaRules)) {
        mediaRules += query + "{\n" + newMediaRules + "\n}\n";
      }
      return "";
    });

    var ruleMatch;
    while (ruleMatch = cssRulePattern.exec(sheet)) {
      var results = queriesForSelector(ruleMatch[1]);
      if (results.queries.length) {
        newRules += results.selector + "{" + ruleMatch[2] + "}\n";
        queries.push.apply(queries, results.queries);
      }
    }
    return newRules + "\n" + mediaRules;
  };

  var elementMatchesRules = function(element, rules) {
    for (var i = rules.length - 1; i > -1; i--) {
      var rule = rules[i];
      var matcher = queryMatchers[rule.property];
      if (matcher && !matcher(element, rule.value, rule.units)) {
        return false;
      }
    }
    return true;
  };

  var evaluateQueries = function() {
    for (var i = 0, len = queries.length; i < len; i++) {
      var elements = document.querySelectorAll(queries[i].selector);
      for (var j = 0; j < elements.length; j++) {
        var element = elements[j];
        if (elementMatchesRules(element, queries[i].rules)) {
          element.classList.add(queries[i].className);
        }
        else {
          element.classList.remove(queries[i].className);
        }
      }
    }
  };

  // re-run all queries on resize
  window.addEventListener("resize", function() {
    evaluateQueries();
  }, false);

  // TODO: re-run all queries... on an interval?
  // override setTimeout, addEventListener, etc to hit every possible JS entry
  // point? Not really an ideal solution to this.
  // Repaint events in Mozilla?

  // automatically look for things on window load
  window.addEventListener("load", function() {
    var evaluated = false;
    scanStyleSheets(document.styleSheets, function() {
      evaluated = true;
      evaluateQueries();
    });

    // if we are still waiting for some asynchronous ones, go ahead and evaluate
    // any found queries now for minimum latency
    if (!evaluated) {
      evaluateQueries();
    }
  }, false);
}());<commit_msg>Note about methodology for viewport units<commit_after>(function() {
  var commentPattern = /(\/\*)[\s\S]*?(\*\/)/g;
  var cssRulePattern = /\s*([^{]+)\{([^}]*)\}\s*/g;
  var queryPattern = /:media\s*\(([^)]*)\)/g;
  var selectorPattern = /(?:^|\})\s*([^{]+)\s*/g;
  var queryRulesPattern = /\(?([^\s:]+):\s*(\d+(?:\.\d+)?)(px|em|rem|vw|vh|vmin|vmax)\)?/g;
  var mediaPattern = /(@media[^{]*)\{((?:\s*([^{]+)\{([^}]*)\}\s*)*)\}/g

  var queryMatchers = {
    "max-available-width": function(element, value, units) {
      var parent = element.parentNode;
      var px = convertToPx(parent, value, units);
      return value && parent && parent.offsetWidth <= px;
    },

    "min-available-width": function(element, value, units) {
      var parent = element.parentNode;
      var px = convertToPx(parent, value, units);
      return value && parent && parent.offsetWidth >= px;
    },
  };

  var queries = [];

  var query = {
    selector: "string",
    className: "string",
    match: function() {}
  };

  var ElementQuery = function() {};

  var queriesForSelector = function(selectorString) {
    var selectors = selectorString.split(",");
    var selectorResults = [];
    var queryResults = [];
    for (var i = 0, len = selectors.length; i < len; i++) {
      var result = queriesForSingleSelector(selectors[i]);
      selectorResults.push(result.selector);
      queryResults = queryResults.concat(result.queries);
    }
    return {
      selector: selectorResults.join(","),
      queries: queryResults
    };
  };

  var queriesForSingleSelector = function(selectorString) {
    var queries = [];
    var newSelector = "";
    var lastIndex = 0;
    var queryMatch;
    while (queryMatch = queryPattern.exec(selectorString)) {
      var querySelector = selectorString.slice(0, queryMatch.index);
      var queryRules = parseQuery(queryMatch[1]);
      var className = classNameForRules(queryRules);
      newSelector += selectorString.slice(lastIndex, queryMatch.index);
      lastIndex = queryMatch.index + queryMatch[0].length;
      queries.push({
        selector: newSelector,
        className: className,
        rules: queryRules
      });
      newSelector += "." + className;
    }
    newSelector += selectorString.slice(lastIndex);
    return {
      selector: newSelector,
      queries: queries
    };
  };

  var parseQuery = function(queryString) {
    var rules = [];
    var ruleMatch;
    while (ruleMatch = queryRulesPattern.exec(queryString)) {
      rules.push({
        property: ruleMatch[1],
        value: parseFloat(ruleMatch[2]),
        units: ruleMatch[3]
      });
    }
    return rules;
  };

  var classNameForRules = function(rules) {
    var name = "query";
    for (var i = 0, len = rules.length; i < len; i++) {
      name += "_" + rules[i].property + "_" + rules[i].value + rules[i].units;
    }
    return name;
  };

  var convertToPx = function(element, value, units) {
    switch (units) {
      case "px": return value;
      case "em": return value * getEmSize(element);
      case "rem": return value * getEmSize();
      // Viewport units!
      // According to http://quirksmode.org/mobile/tableViewport.html
      // documentElement.clientWidth/Height gets us the most reliable info
      case "vw": return value * document.documentElement.clientWidth / 100;
      case "vh": return value * document.documentElement.clientHeight / 100;
      case "vmin":
      case "vmax":
        var vw = document.documentElement.clientWidth / 100;
        var vh = document.documentElement.clientHeight / 100;
        var chooser = Math[units === "vmin" ? "min" : "max"];
        return value * chooser(vw, vh);
      default: return value;
      // for now, not supporting physical units (since they are just a set number of px)
      // or ex/ch (getting accurate measurements is hard)
    }
  };

  var getEmSize = function(element) {
    if (!element) {
      element = document.documentElement;
    }
    if (window.getComputedStyle) {
      return parseFloat(getComputedStyle(element).fontSize) || 16;
    }
    // TODO: support IE?
    return 16;
  };

  var scanStyleSheets = function(sheets, callback) {
    var completed = 0;
    for (var i = 0, len = sheets.length; i < len; i++) {
      scanStyleSheet(sheets[i], function() {
        completed += 1;
        if (completed === len) {
          callback && callback();
        }
      });
    }
  };

  var scanStyleSheet = function(sheet, callback) {
    if (sheet.ownerNode.nodeName === "STYLE") {
      var newStyles = scanStyleString(sheet.ownerNode.innerHTML);
      sheet.ownerNode.innerHTML += newStyles;
      callback && callback();
    }
    else if (sheet.href) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", sheet.href, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if (xhr.status === 200) {
            var newStyles = scanStyleString(xhr.responseText);
            var style = document.createElement("style");
            style.innerHTML = newStyles;
            document.body.appendChild(style);
          }
          else if (window.console) {
            console.log("Could not load stylesheet at " + sheet.href);
          }
          callback && callback();
        }
      }
      xhr.send(null);
    }
  };

  var scanStyleString = function(sheet) {
    // new stylesheet content to add (replacing rules with `:media()` in them)
    var newRules = "";

    // remove comments
    sheet = sheet.replace(commentPattern, "");

    // manage vanilla media queries
    var mediaRules = "";
    sheet = sheet.replace(mediaPattern, function(mediaString, query, content) {
      var newMediaRules = scanStyleString(content);
      if (!/^\s*$/.test(newMediaRules)) {
        mediaRules += query + "{\n" + newMediaRules + "\n}\n";
      }
      return "";
    });

    var ruleMatch;
    while (ruleMatch = cssRulePattern.exec(sheet)) {
      var results = queriesForSelector(ruleMatch[1]);
      if (results.queries.length) {
        newRules += results.selector + "{" + ruleMatch[2] + "}\n";
        queries.push.apply(queries, results.queries);
      }
    }
    return newRules + "\n" + mediaRules;
  };

  var elementMatchesRules = function(element, rules) {
    for (var i = rules.length - 1; i > -1; i--) {
      var rule = rules[i];
      var matcher = queryMatchers[rule.property];
      if (matcher && !matcher(element, rule.value, rule.units)) {
        return false;
      }
    }
    return true;
  };

  var evaluateQueries = function() {
    for (var i = 0, len = queries.length; i < len; i++) {
      var elements = document.querySelectorAll(queries[i].selector);
      for (var j = 0; j < elements.length; j++) {
        var element = elements[j];
        if (elementMatchesRules(element, queries[i].rules)) {
          element.classList.add(queries[i].className);
        }
        else {
          element.classList.remove(queries[i].className);
        }
      }
    }
  };

  // re-run all queries on resize
  window.addEventListener("resize", function() {
    evaluateQueries();
  }, false);

  // TODO: re-run all queries... on an interval?
  // override setTimeout, addEventListener, etc to hit every possible JS entry
  // point? Not really an ideal solution to this.
  // Repaint events in Mozilla?

  // automatically look for things on window load
  window.addEventListener("load", function() {
    var evaluated = false;
    scanStyleSheets(document.styleSheets, function() {
      evaluated = true;
      evaluateQueries();
    });

    // if we are still waiting for some asynchronous ones, go ahead and evaluate
    // any found queries now for minimum latency
    if (!evaluated) {
      evaluateQueries();
    }
  }, false);
}());<|endoftext|>


<commit_before>// super, super simple
Gravatar = {
  getGravatar: function(user, options) {
    if(user.email_hash){
      var options = options || {};

      var protocol = options.secure ? 'https' : 'http';
      delete options.secure;
      var hash = user.email_hash;
      var url = protocol + '://www.gravatar.com/avatar/' + hash;

      var params = _.map(options, function(val, key) { return key + "=" + val}).join('&');
      if (params !== '')
      url += '?' + params;

      return url;

    }else if(user.services && user.services.twitter){
      return user.services.twitter.profile_image_url; //for the oauth-login avatar, diff for diff oauth, maybe it could be better
    }
  }
}<commit_msg>사용자 이미지 표시<commit_after>// super, super simple
Gravatar = {
  getGravatar: function(user, options) {
    if(user.email_hash){
      var options = options || {};

      var protocol = options.secure ? 'https' : 'http';
      delete options.secure;
      var hash = user.email_hash;
      var url = protocol + '://www.gravatar.com/avatar/' + hash;

      var params = _.map(options, function(val, key) { return key + "=" + val}).join('&');
      if (params !== '')
      url += '?' + params;

      return url;

    }else if(user.services && user.services.twitter){
      return user.services.twitter.profile_image_url; //for the oauth-login avatar, diff for diff oauth, maybe it could be better
    }else if(user.services && user.services.facebook){
      return "http://graph.facebook.com/"+user.services.facebook.username+"/picture";
    }else if(user.services && user.services.google){
      return user.services.google.picture;
    }
  }
}<|endoftext|>


<commit_before>iD.modes.DragNode = function(context) {
    var mode = {
        id: 'drag-node',
        button: 'browse'
    };

    var nudgeInterval,
        activeIDs,
        wasMidpoint,
        cancelled,
        hover = iD.behavior.Hover().altDisables(true);

    function edge(point, size) {
        var pad = [30, 100, 30, 100];
        if (point[0] > size[0] - pad[0]) return [-10, 0];
        else if (point[0] < pad[2]) return [10, 0];
        else if (point[1] > size[1] - pad[1]) return [0, -10];
        else if (point[1] < pad[3]) return [0, 10];
        return null;
    }

    function startNudge(nudge) {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = window.setInterval(function() {
            context.pan(nudge);
        }, 50);
    }

    function stopNudge() {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = null;
    }

    function moveAnnotation(entity) {
        return t('operations.move.annotation.' + entity.geometry(context.graph()));
    }

    function connectAnnotation(datum) {
        return t('operations.connect.annotation.' + datum.geometry(context.graph()));
    }

    function origin(entity) {
        return context.projection(entity.loc);
    }

    function start(entity) {
        cancelled = d3.event.sourceEvent.shiftKey;
        if (cancelled) return behavior.cancel();

        wasMidpoint = entity.type === 'midpoint';
        if (wasMidpoint) {
            var midpoint = entity;
            entity = iD.Node();
            context.perform(iD.actions.AddMidpoint(midpoint, entity));

             var vertex = context.surface()
                .selectAll('.vertex')
                .filter(function(d) { return d.id === entity.id; });
             behavior.target(vertex.node(), entity);

        } else {
            context.perform(
                iD.actions.Noop());
        }

        activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
        activeIDs.push(entity.id);

        context.enter(mode);
    }

    function datum() {
        if (d3.event.sourceEvent.altKey) {
            return {};
        }

        return d3.event.sourceEvent.target.__data__ || {};
    }

    function move(entity) {
        if (cancelled) return;
        d3.event.sourceEvent.stopPropagation();

        var nudge = edge(d3.event.point, context.map().size());
        if (nudge) startNudge(nudge);
        else stopNudge();

        var loc = context.map().mouseCoordinates();

        var d = datum();
        if (d.type === 'node' && d.id !== entity.id) {
            loc = d.loc;
        } else if (d.type === 'way') {
            loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
        }

        context.replace(
            iD.actions.MoveNode(entity.id, loc),
            t('operations.move.annotation.' + entity.geometry(context.graph())));
    }

    function end(entity) {
        if (cancelled) return;

        function adjacent(d) {
            return _.any(context.graph().parentWays(entity).map(function(w) {
                return w.areAdjacent(d.id, entity.id);
            }));
        }

        var d = datum();

        if (d.type === 'way') {
            var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
            context.replace(
                iD.actions.MoveNode(entity.id, choice.loc),
                iD.actions.AddVertex(d.id, entity.id, choice.index),
                connectAnnotation(d));

        } else if (d.type === 'node' && adjacent(d)) {
            context.replace(
                    iD.actions.DeleteNode(entity.id),
                    t('operations.delete.annotation.vertex'));

        } else if (d.type === 'node' && d.id !== entity.id) {
            context.replace(
                iD.actions.Connect([entity.id, d.id]),
                connectAnnotation(d));

        } else if (wasMidpoint) {
            context.replace(
                iD.actions.Noop(),
                t('operations.add.annotation.vertex'));

        } else {
            context.replace(
                iD.actions.Noop(),
                moveAnnotation(entity));
        }

        context.enter(iD.modes.Browse(context));
    }

    function cancel() {
        behavior.cancel();
        context.enter(iD.modes.Browse(context));
    }

    var behavior = iD.behavior.drag()
        .delegate("g.node, g.point, g.midpoint")
        .surface(context.surface().node())
        .origin(origin)
        .on('start', start)
        .on('move', move)
        .on('end', end);

    mode.enter = function() {
        context.install(hover);

        context.history()
            .on('undone.drag-node', cancel);

        context.surface()
            .selectAll('.node, .way')
            .filter(function(d) { return activeIDs.indexOf(d.id) >= 0; })
            .classed('active', true);
    };

    mode.exit = function() {
        context.uninstall(hover);

        context.history()
            .on('undone.drag_node', null);

        context.surface()
            .selectAll('.active')
            .classed('active', false);

        stopNudge();
    };

    mode.behavior = behavior;

    return mode;
};
<commit_msg>avoid history change when dragging midpoint<commit_after>iD.modes.DragNode = function(context) {
    var mode = {
        id: 'drag-node',
        button: 'browse'
    };

    var nudgeInterval,
        activeIDs,
        wasMidpoint,
        cancelled,
        hover = iD.behavior.Hover().altDisables(true);

    function edge(point, size) {
        var pad = [30, 100, 30, 100];
        if (point[0] > size[0] - pad[0]) return [-10, 0];
        else if (point[0] < pad[2]) return [10, 0];
        else if (point[1] > size[1] - pad[1]) return [0, -10];
        else if (point[1] < pad[3]) return [0, 10];
        return null;
    }

    function startNudge(nudge) {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = window.setInterval(function() {
            context.pan(nudge);
        }, 50);
    }

    function stopNudge() {
        if (nudgeInterval) window.clearInterval(nudgeInterval);
        nudgeInterval = null;
    }

    function moveAnnotation(entity) {
        return t('operations.move.annotation.' + entity.geometry(context.graph()));
    }

    function connectAnnotation(datum) {
        return t('operations.connect.annotation.' + datum.geometry(context.graph()));
    }

    function origin(entity) {
        return context.projection(entity.loc);
    }

    function start(entity) {
        cancelled = d3.event.sourceEvent.shiftKey;
        if (cancelled) return behavior.cancel();

        wasMidpoint = entity.type === 'midpoint';
        if (wasMidpoint) {
            var midpoint = entity;
            entity = iD.Node();
            context.perform(iD.actions.AddMidpoint(midpoint, entity));

             var vertex = context.surface()
                .selectAll('.vertex')
                .filter(function(d) { return d.id === entity.id; });
             behavior.target(vertex.node(), entity);

        } else {
            context.perform(
                iD.actions.Noop());
        }

        activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
        activeIDs.push(entity.id);

        context.enter(mode);
    }

    function datum() {
        if (d3.event.sourceEvent.altKey) {
            return {};
        }

        return d3.event.sourceEvent.target.__data__ || {};
    }

    function move(entity) {
        if (cancelled) return;
        d3.event.sourceEvent.stopPropagation();

        var nudge = edge(d3.event.point, context.map().size());
        if (nudge) startNudge(nudge);
        else stopNudge();

        var loc = context.map().mouseCoordinates();

        var d = datum();
        if (d.type === 'node' && d.id !== entity.id) {
            loc = d.loc;
        } else if (d.type === 'way') {
            loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
        }

        context.replace(
            iD.actions.MoveNode(entity.id, loc),
            t('operations.move.annotation.' + entity.geometry(context.graph())));
    }

    function end(entity) {
        if (cancelled) return;

        function adjacent(d) {
            return _.any(context.graph().parentWays(entity).map(function(w) {
                return w.areAdjacent(d.id, entity.id);
            }));
        }

        var d = datum();

        if (d.type === 'way') {
            var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
            context.replace(
                iD.actions.MoveNode(entity.id, choice.loc),
                iD.actions.AddVertex(d.id, entity.id, choice.index),
                connectAnnotation(d));

        } else if (d.type === 'node' && adjacent(d)) {
            if (wasMidpoint) {
                context.history().pop();
            } else {
                context.replace(
                    iD.actions.DeleteNode(entity.id),
                    t('operations.delete.annotation.vertex'));
            }

        } else if (d.type === 'node' && d.id !== entity.id) {
            context.replace(
                iD.actions.Connect([entity.id, d.id]),
                connectAnnotation(d));

        } else if (wasMidpoint) {
            context.replace(
                iD.actions.Noop(),
                t('operations.add.annotation.vertex'));

        } else {
            context.replace(
                iD.actions.Noop(),
                moveAnnotation(entity));
        }

        context.enter(iD.modes.Browse(context));
    }

    function cancel() {
        behavior.cancel();
        context.enter(iD.modes.Browse(context));
    }

    var behavior = iD.behavior.drag()
        .delegate("g.node, g.point, g.midpoint")
        .surface(context.surface().node())
        .origin(origin)
        .on('start', start)
        .on('move', move)
        .on('end', end);

    mode.enter = function() {
        context.install(hover);

        context.history()
            .on('undone.drag-node', cancel);

        context.surface()
            .selectAll('.node, .way')
            .filter(function(d) { return activeIDs.indexOf(d.id) >= 0; })
            .classed('active', true);
    };

    mode.exit = function() {
        context.uninstall(hover);

        context.history()
            .on('undone.drag_node', null);

        context.surface()
            .selectAll('.active')
            .classed('active', false);

        stopNudge();
    };

    mode.behavior = behavior;

    return mode;
};
<|endoftext|>


<commit_before>/**
# Object Registry

The object registry is an observable object.

    set('root', object_value); // registers the object with the name
    set('root.path.to.value', new_value); // sets the value

You can set the registry's properties by path. Root level properties must
themselves be objects, and are referred to as _registry__.

    get('root.path.to.value'); // gets the value

You can get a property by path.

    call('root.path.to.method', ...args); // returns value as appropriate

You can call a method by path.
*/
/* global module, require, console */
'use strict';

const {getByPath, setByPath} = require('./b8r.byPath.js');
const registry = {};
let listeners = [];  // { path_string_or_test, callback }

class Listener {
  constructor(test, callback) {
    if (typeof test === 'string') {
      this.test = t =>
          t.length >= test.length && test === t.substr(0, test.length);
    } else if (test instanceof RegExp) {
      this.test = test.test;
    } else if (test instanceof Function) {
      this.test = test;
    } else {
      throw 'expect listener test to be a string, RegExp, or test function';
    }
    this.callback = callback;
    listeners.push(this);
  }
}

const get = path => getByPath(registry, path);

const set = (path, value, source_element) => {
  const path_parts = path.split('.');
  const model = path_parts[0];
  if (path_parts.length > 1 && !registry[model]) {
    console.error(`cannot set ${path} to ${value}, ${model} does not exist`);
  } else if (path_parts.length === 1 && typeof value !== 'object') {
    throw 'cannot set ${path}; you can only register objects at root-level';
  } else if (setByPath(registry, path, value)) {
    touch(path, source_element);
  }
};

const call = (path, ...args) => {
  const method = get(path);
  if (method instanceof Function) {
    return method(...args);
  } else {
    throw `cannot call ${path}; not a method`;
  }
};

/**
    touch(path [, source_element]); //

Triggers all observers as though the value at path has changed. Useful
if you change a property independently of the registry.
*/
const touch = (path, source_element) => {
  listeners.filter(listener => listener.test(path))
      .forEach(listener => listener.callback(path, source_element));
};

/**
    observe('root.path.to', callback); // returns a Listener instance

You can observe a path (string). The callback will fire whenever any path
matching or starting
with the string changes. The callback will be passed the exact path that
changed.

    observe(/root.path.[^\.]+.value/, callback);

Instead of a constant path, you can pass a RegExp which will fire the callback
when a path
matching the test changes. The callback will be passed the exact path that
changed.

    observe(path => path.split('.').length === 1, callback);

Finally you can observe a path test function.

    unobserve(listener);

You can remove a listener (if you kept the reference handy).

    unobserve(test);

You can remove a listener by test, but it will remove _all_ listeners which use
that test.
*/

const observe = (test, callback) => {
  return new Listener(test, callback);
};

const unobserve = test => {
  let index;
  let found = false;
  if (test instanceof Listener) {
    index = listeners.indexOf(test);
    if (index > -1) {
      listeners.splice(index, 1);
    } else {
      console.error('unobserve failed, listener not found');
    }
  } else {
    for (let i = listeners.length - 1; i >= 0; i--) {
      if (listeners[i].test === test) {
        listeners.splice(i, 1);
        found = true;
      }
    }
  }
  return found;
};

/**
    registered('root-name'); // => true | false

You can get a list of root level objects:

    models();

You can obtain a value using a path.
*/

const models = () => Object.keys(registry);

const registered = path => !!registry[path.split('.')[0]];

/**
    remove('root');

You can remove a root-level object ("model-name").
*/
const remove = name => {
  if (registry[name]) {
    delete registry[name];
  } else {
    console.error(`remove model ${name} failed; does not exist`);
  }
};

module.exports = {
  get,
  set,
  call,
  touch,
  observe,
  unobserve,
  models,
  registered,
  remove
};
<commit_msg>added touch and push to registry<commit_after>/**
# Object Registry

The object registry is an observable object.

    set('root', object_value); // registers the object with the name
    set('root.path.to.value', new_value); // sets the value

You can set the registry's properties by path. Root level properties must
themselves be objects, and are referred to as _registry__.

    get('root.path.to.value'); // gets the value

You can get a property by path.

    call('root.path.to.method', ...args); // returns value as appropriate

You can call a method by path.

    touch(path [, source_element]); //

Triggers all observers as though the value at path has changed. Useful
if you change a property independently of the registry.
*/
/* global module, require, console */
'use strict';

const {getByPath, setByPath} = require('./b8r.byPath.js');
const registry = {};
let listeners = [];  // { path_string_or_test, callback }

class Listener {
  constructor(test, callback) {
    if (typeof test === 'string') {
      this.test = t =>
          t.length >= test.length && test === t.substr(0, test.length);
    } else if (test instanceof RegExp) {
      this.test = test.test;
    } else if (test instanceof Function) {
      this.test = test;
    } else {
      throw 'expect listener test to be a string, RegExp, or test function';
    }
    this.callback = callback;
    listeners.push(this);
  }
}

const get = path => getByPath(registry, path);

const touch = (path, source_element) => {
  listeners.filter(listener => listener.test(path))
      .forEach(listener => listener.callback(path, source_element));
};

const set = (path, value, source_element) => {
  const path_parts = path.split('.');
  const model = path_parts[0];
  if (path_parts.length > 1 && !registry[model]) {
    console.error(`cannot set ${path} to ${value}, ${model} does not exist`);
  } else if (path_parts.length === 1 && typeof value !== 'object') {
    throw 'cannot set ${path}; you can only register objects at root-level';
  } else if (setByPath(registry, path, value)) {
    touch(path, source_element);
  }
};

const push = (path, value) => {
  const list = get(path);
  if(Array.isArray(list)) {
    list.push(value);
    touch(path);
  }
};

const call = (path, ...args) => {
  const method = get(path);
  if (method instanceof Function) {
    return method(...args);
  } else {
    throw `cannot call ${path}; not a method`;
  }
};

/**
    observe('root.path.to', callback); // returns a Listener instance

You can observe a path (string). The callback will fire whenever any path
matching or starting
with the string changes. The callback will be passed the exact path that
changed.

    observe(/root.path.[^\.]+.value/, callback);

Instead of a constant path, you can pass a RegExp which will fire the callback
when a path
matching the test changes. The callback will be passed the exact path that
changed.

    observe(path => path.split('.').length === 1, callback);

Finally you can observe a path test function.

    unobserve(listener);

You can remove a listener (if you kept the reference handy).

    unobserve(test);

You can remove a listener by test, but it will remove _all_ listeners which use
that test.
*/

const observe = (test, callback) => {
  return new Listener(test, callback);
};

const unobserve = test => {
  let index;
  let found = false;
  if (test instanceof Listener) {
    index = listeners.indexOf(test);
    if (index > -1) {
      listeners.splice(index, 1);
    } else {
      console.error('unobserve failed, listener not found');
    }
  } else {
    for (let i = listeners.length - 1; i >= 0; i--) {
      if (listeners[i].test === test) {
        listeners.splice(i, 1);
        found = true;
      }
    }
  }
  return found;
};

/**
    registered('root-name'); // => true | false

You can get a list of root level objects:

    models();

You can obtain a value using a path.
*/

const models = () => Object.keys(registry);

const registered = path => !!registry[path.split('.')[0]];

/**
    remove('root');

You can remove a root-level object ("model-name").
*/
const remove = name => {
  if (registry[name]) {
    delete registry[name];
  } else {
    console.error(`remove model ${name} failed; does not exist`);
  }
};

module.exports = {
  get,
  set,
  push,
  call,
  touch,
  observe,
  unobserve,
  models,
  registered,
  remove,
};
<|endoftext|>