<commit_before>import messages from 'shared/messages';
import * as consoleActions from 'console/actions/console';

const inputShownMode = (state) => {
  return ['command', 'find'].includes(state.mode);
};

export default class ConsoleComponent {
  constructor(wrapper, store) {
    this.wrapper = wrapper;
    this.store = store;
    this.prevMode = '';

    let doc = this.wrapper.ownerDocument;
    let input = doc.querySelector('#vimvixen-console-command-input');

    input.addEventListener('blur', this.onBlur.bind(this));
    input.addEventListener('keydown', this.onKeyDown.bind(this));
    input.addEventListener('input', this.onInput.bind(this));

    store.subscribe(() => {
      this.update();
    });
    this.update();
  }

  onBlur() {
    let state = this.store.getState();
    if (state.mode === 'command') {
      this.hideCommand();
    }
  }

  doEnter(e) {
    e.stopPropagation();
    e.preventDefault();
    return this.onEntered(e.target.value);
  }

  selectNext(e) {
    this.store.dispatch(consoleActions.completionNext());
    e.stopPropagation();
    e.preventDefault();
  }

  selectPrev(e) {
    this.store.dispatch(consoleActions.completionPrev());
    e.stopPropagation();
    e.preventDefault();
  }

  onKeyDown(e) {
    switch (e.keyCode) {
    case KeyboardEvent.DOM_VK_ESCAPE:
      return this.hideCommand();
    case KeyboardEvent.DOM_VK_RETURN:
      return this.doEnter(e);
    case KeyboardEvent.DOM_VK_TAB:
      if (e.shiftKey) {
        this.store.dispatch(consoleActions.completionPrev());
      } else {
        this.store.dispatch(consoleActions.completionNext());
      }
      e.stopPropagation();
      e.preventDefault();
      break;
    case KeyboardEvent.DOM_VK_OPEN_BRACKET:
      if (e.ctrlKey) {
        return this.hideCommand();
      }
      break;
    case KeyboardEvent.DOM_VK_M:
      if (e.ctrlKey) {
        return this.doEnter(e);
      }
      break;
    case KeyboardEvent.DOM_VK_DOWN:
      this.selectNext(e);
      break;
    case KeyboardEvent.DOM_VK_N:
    case KeyboardEvent.DOM_VK_J:
      if (e.ctrlKey) {
        this.selectNext(e);
      }
      break;
    case KeyboardEvent.DOM_VK_UP:
      this.selectPrev(e);
      break;
    case KeyboardEvent.DOM_VK_P:
    case KeyboardEvent.DOM_VK_K:
      if (e.ctrlKey) {
        this.selectPrev(e);
      }
      break;
    }
  }

  onEntered(value) {
    let state = this.store.getState();
    if (state.mode === 'command') {
      browser.runtime.sendMessage({
        type: messages.CONSOLE_ENTER_COMMAND,
        text: value,
      });
      this.hideCommand();
    } else if (state.mode === 'find') {
      this.hideCommand();
      window.top.postMessage(JSON.stringify({
        type: messages.CONSOLE_ENTER_FIND,
        text: value,
      }), '*');
    }
  }

  onInput(e) {
    this.store.dispatch(consoleActions.setConsoleText(e.target.value));

    let source = e.target.value;
    return browser.runtime.sendMessage({
      type: messages.CONSOLE_QUERY_COMPLETIONS,
      text: source,
    }).then((completions) => {
      this.store.dispatch(consoleActions.setCompletions(source, completions));
    });
  }

  onInputShown(state) {
    let doc = this.wrapper.ownerDocument;
    let input = doc.querySelector('#vimvixen-console-command-input');

    input.focus();
    window.focus();

    if (state.mode === 'command') {
      this.onInput({ target: input });
    }
  }

  hideCommand() {
    this.store.dispatch(consoleActions.hideCommand());
    window.top.postMessage(JSON.stringify({
      type: messages.CONSOLE_UNFOCUS,
    }), '*');
  }

  update() {
    let state = this.store.getState();

    this.updateMessage(state);
    this.updateCommand(state);
    this.updatePrompt(state);

    if (this.prevMode !== state.mode && inputShownMode(state)) {
      this.onInputShown(state);
    }
    this.prevMode = state.mode;
  }

  updateMessage(state) {
    let doc = this.wrapper.ownerDocument;
    let box = doc.querySelector('.vimvixen-console-message');
    let display = 'none';
    let classList = ['vimvixen-console-message'];

    if (state.mode === 'error' || state.mode === 'info') {
      display = 'block';
      classList.push('vimvixen-console-' + state.mode);
    }

    box.className = classList.join(' ');
    box.style.display = display;
    box.textContent = state.messageText;
  }

  updateCommand(state) {
    let doc = this.wrapper.ownerDocument;
    let command = doc.querySelector('#vimvixen-console-command');
    let input = doc.querySelector('#vimvixen-console-command-input');

    let display = 'none';
    if (inputShownMode(state)) {
      display = 'block';
    }

    command.style.display = display;
    input.value = state.consoleText;
  }

  updatePrompt(state) {
    let classList = ['vimvixen-console-command-prompt'];
    if (inputShownMode(state)) {
      classList.push('prompt-' + state.mode);
    }

    let doc = this.wrapper.ownerDocument;
    let ele = doc.querySelector('.vimvixen-console-command-prompt');
    ele.className = classList.join(' ');
  }
}
<commit_msg>Fix drop <C-j>, <C-k> from cursor selector<commit_after>import messages from 'shared/messages';
import * as consoleActions from 'console/actions/console';

const inputShownMode = (state) => {
  return ['command', 'find'].includes(state.mode);
};

export default class ConsoleComponent {
  constructor(wrapper, store) {
    this.wrapper = wrapper;
    this.store = store;
    this.prevMode = '';

    let doc = this.wrapper.ownerDocument;
    let input = doc.querySelector('#vimvixen-console-command-input');

    input.addEventListener('blur', this.onBlur.bind(this));
    input.addEventListener('keydown', this.onKeyDown.bind(this));
    input.addEventListener('input', this.onInput.bind(this));

    store.subscribe(() => {
      this.update();
    });
    this.update();
  }

  onBlur() {
    let state = this.store.getState();
    if (state.mode === 'command') {
      this.hideCommand();
    }
  }

  doEnter(e) {
    e.stopPropagation();
    e.preventDefault();
    return this.onEntered(e.target.value);
  }

  selectNext(e) {
    this.store.dispatch(consoleActions.completionNext());
    e.stopPropagation();
    e.preventDefault();
  }

  selectPrev(e) {
    this.store.dispatch(consoleActions.completionPrev());
    e.stopPropagation();
    e.preventDefault();
  }

  onKeyDown(e) {
    switch (e.keyCode) {
    case KeyboardEvent.DOM_VK_ESCAPE:
      return this.hideCommand();
    case KeyboardEvent.DOM_VK_RETURN:
      return this.doEnter(e);
    case KeyboardEvent.DOM_VK_TAB:
      if (e.shiftKey) {
        this.store.dispatch(consoleActions.completionPrev());
      } else {
        this.store.dispatch(consoleActions.completionNext());
      }
      e.stopPropagation();
      e.preventDefault();
      break;
    case KeyboardEvent.DOM_VK_OPEN_BRACKET:
      if (e.ctrlKey) {
        return this.hideCommand();
      }
      break;
    case KeyboardEvent.DOM_VK_M:
      if (e.ctrlKey) {
        return this.doEnter(e);
      }
      break;
    case KeyboardEvent.DOM_VK_DOWN:
      this.selectNext(e);
      break;
    case KeyboardEvent.DOM_VK_N:
      if (e.ctrlKey) {
        this.selectNext(e);
      }
      break;
    case KeyboardEvent.DOM_VK_UP:
      this.selectPrev(e);
      break;
    case KeyboardEvent.DOM_VK_P:
      if (e.ctrlKey) {
        this.selectPrev(e);
      }
      break;
    }
  }

  onEntered(value) {
    let state = this.store.getState();
    if (state.mode === 'command') {
      browser.runtime.sendMessage({
        type: messages.CONSOLE_ENTER_COMMAND,
        text: value,
      });
      this.hideCommand();
    } else if (state.mode === 'find') {
      this.hideCommand();
      window.top.postMessage(JSON.stringify({
        type: messages.CONSOLE_ENTER_FIND,
        text: value,
      }), '*');
    }
  }

  onInput(e) {
    this.store.dispatch(consoleActions.setConsoleText(e.target.value));

    let source = e.target.value;
    return browser.runtime.sendMessage({
      type: messages.CONSOLE_QUERY_COMPLETIONS,
      text: source,
    }).then((completions) => {
      this.store.dispatch(consoleActions.setCompletions(source, completions));
    });
  }

  onInputShown(state) {
    let doc = this.wrapper.ownerDocument;
    let input = doc.querySelector('#vimvixen-console-command-input');

    input.focus();
    window.focus();

    if (state.mode === 'command') {
      this.onInput({ target: input });
    }
  }

  hideCommand() {
    this.store.dispatch(consoleActions.hideCommand());
    window.top.postMessage(JSON.stringify({
      type: messages.CONSOLE_UNFOCUS,
    }), '*');
  }

  update() {
    let state = this.store.getState();

    this.updateMessage(state);
    this.updateCommand(state);
    this.updatePrompt(state);

    if (this.prevMode !== state.mode && inputShownMode(state)) {
      this.onInputShown(state);
    }
    this.prevMode = state.mode;
  }

  updateMessage(state) {
    let doc = this.wrapper.ownerDocument;
    let box = doc.querySelector('.vimvixen-console-message');
    let display = 'none';
    let classList = ['vimvixen-console-message'];

    if (state.mode === 'error' || state.mode === 'info') {
      display = 'block';
      classList.push('vimvixen-console-' + state.mode);
    }

    box.className = classList.join(' ');
    box.style.display = display;
    box.textContent = state.messageText;
  }

  updateCommand(state) {
    let doc = this.wrapper.ownerDocument;
    let command = doc.querySelector('#vimvixen-console-command');
    let input = doc.querySelector('#vimvixen-console-command-input');

    let display = 'none';
    if (inputShownMode(state)) {
      display = 'block';
    }

    command.style.display = display;
    input.value = state.consoleText;
  }

  updatePrompt(state) {
    let classList = ['vimvixen-console-command-prompt'];
    if (inputShownMode(state)) {
      classList.push('prompt-' + state.mode);
    }

    let doc = this.wrapper.ownerDocument;
    let ele = doc.querySelector('.vimvixen-console-command-prompt');
    ele.className = classList.join(' ');
  }
}
<|endoftext|>


<commit_before>const createState = topic => ({
  topic,
  paused: new Set(),
  pauseAll: false,
  resumed: new Set(),
})

module.exports = class SubscriptionState {
  constructor() {
    this.assignedPartitionsByTopic = {}
    this.subscriptionStatesByTopic = {}
  }

  /**
   * Replace the current assignment with a new set of assignments
   *
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  assign(topicPartitions = []) {
    this.assignedPartitionsByTopic = topicPartitions.reduce(
      (assigned, { topic, partitions = [] }) => {
        return { ...assigned, [topic]: { topic, partitions } }
      },
      {}
    )
  }

  /**
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  pause(topicPartitions = []) {
    topicPartitions.forEach(({ topic, partitions }) => {
      const state = this.subscriptionStatesByTopic[topic] || createState(topic)

      if (typeof partitions === 'undefined') {
        state.paused.clear()
        state.resumed.clear()
        state.pauseAll = true
      } else if (Array.isArray(partitions)) {
        partitions.forEach(partition => {
          state.paused.add(partition)
          state.resumed.delete(partition)
        })
        state.pauseAll = false
      }

      this.subscriptionStatesByTopic[topic] = state
    })
  }

  /**
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  resume(topicPartitions = []) {
    topicPartitions.forEach(({ topic, partitions }) => {
      const state = this.subscriptionStatesByTopic[topic] || createState(topic)

      if (typeof partitions === 'undefined') {
        state.paused.clear()
        state.resumed.clear()
        state.pauseAll = false
      } else if (Array.isArray(partitions)) {
        partitions.forEach(partition => {
          state.paused.delete(partition)

          if (state.pauseAll) {
            state.resumed.add(partition)
          }
        })
      }

      this.subscriptionStatesByTopic[topic] = state
    })
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  assigned() {
    return Object.values(this.assignedPartitionsByTopic).map(({ topic, partitions }) => ({
      topic,
      partitions,
    }))
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  active() {
    return Object.values(this.assignedPartitionsByTopic).map(({ topic, partitions }) => ({
      topic,
      partitions: partitions.filter(partition => !this.isPaused(topic, partition)),
    }))
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  paused() {
    return Object.values(this.assignedPartitionsByTopic).map(({ topic, partitions }) => ({
      topic,
      partitions: partitions.filter(partition => this.isPaused(topic, partition)),
    }))
  }

  isPaused(topic, partition) {
    const state = this.subscriptionStatesByTopic[topic]

    if (!state) {
      return false
    }

    const partitionResumed = state.resumed.has(partition)
    const partitionPaused = state.paused.has(partition)

    return (state.pauseAll && !partitionResumed) || partitionPaused
  }
}
<commit_msg>SubscriptionState#paused only returns topics with paused partitions<commit_after>const createState = topic => ({
  topic,
  paused: new Set(),
  pauseAll: false,
  resumed: new Set(),
})

module.exports = class SubscriptionState {
  constructor() {
    this.assignedPartitionsByTopic = {}
    this.subscriptionStatesByTopic = {}
  }

  /**
   * Replace the current assignment with a new set of assignments
   *
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  assign(topicPartitions = []) {
    this.assignedPartitionsByTopic = topicPartitions.reduce(
      (assigned, { topic, partitions = [] }) => {
        return { ...assigned, [topic]: { topic, partitions } }
      },
      {}
    )
  }

  /**
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  pause(topicPartitions = []) {
    topicPartitions.forEach(({ topic, partitions }) => {
      const state = this.subscriptionStatesByTopic[topic] || createState(topic)

      if (typeof partitions === 'undefined') {
        state.paused.clear()
        state.resumed.clear()
        state.pauseAll = true
      } else if (Array.isArray(partitions)) {
        partitions.forEach(partition => {
          state.paused.add(partition)
          state.resumed.delete(partition)
        })
        state.pauseAll = false
      }

      this.subscriptionStatesByTopic[topic] = state
    })
  }

  /**
   * @param {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  resume(topicPartitions = []) {
    topicPartitions.forEach(({ topic, partitions }) => {
      const state = this.subscriptionStatesByTopic[topic] || createState(topic)

      if (typeof partitions === 'undefined') {
        state.paused.clear()
        state.resumed.clear()
        state.pauseAll = false
      } else if (Array.isArray(partitions)) {
        partitions.forEach(partition => {
          state.paused.delete(partition)

          if (state.pauseAll) {
            state.resumed.add(partition)
          }
        })
      }

      this.subscriptionStatesByTopic[topic] = state
    })
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  assigned() {
    return Object.values(this.assignedPartitionsByTopic).map(({ topic, partitions }) => ({
      topic,
      partitions,
    }))
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  active() {
    return Object.values(this.assignedPartitionsByTopic).map(({ topic, partitions }) => ({
      topic,
      partitions: partitions.filter(partition => !this.isPaused(topic, partition)),
    }))
  }

  /**
   * @returns {Array<TopicPartitions>} topicPartitions
   * Example: [{ topic: 'topic-name', partitions: [1, 2] }]
   */
  paused() {
    return Object.values(this.assignedPartitionsByTopic)
      .map(({ topic, partitions }) => ({
        topic,
        partitions: partitions.filter(partition => this.isPaused(topic, partition)).sort(),
      }))
      .filter(({ partitions }) => partitions.length !== 0)
  }

  isPaused(topic, partition) {
    const state = this.subscriptionStatesByTopic[topic]

    if (!state) {
      return false
    }

    const partitionResumed = state.resumed.has(partition)
    const partitionPaused = state.paused.has(partition)

    return (state.pauseAll && !partitionResumed) || partitionPaused
  }
}
<|endoftext|>


<commit_before>// @flow
import React, { PropTypes, PureComponent } from 'react'
import { connect } from 'react-redux'
import { isIOS, scrollToPosition } from '../lib/jello'
import { FOOTER_LINKS as links } from '../constants/locales/en'
import { FORM_CONTROL_STATUS as STATUS } from '../constants/status_types'
import { LOAD_NEXT_CONTENT_REQUEST, SET_LAYOUT_MODE } from '../constants/action_types'
import { selectIsLoggedIn } from '../selectors/authentication'
import { selectIsGridMode, selectIsLayoutToolHidden, selectIsMobile } from '../selectors/gui'
import { selectAvailability } from '../selectors/profile'
import { selectStreamType } from '../selectors/stream'
import { checkAvailability } from '../actions/profile'
import { getEmailStateFromClient, getEmailStateFromServer } from '../components/forms/Validators'
import { Footer } from '../components/footer/FooterRenderables'
import type { Availability } from '../types/flowtypes'

let emailValue = ''

type Props = {
  availability: Availability,
  dispatch: () => void,
  formActionPath: string,
  isGridMode: boolean,
  isLayoutToolHidden: boolean,
  isLoggedIn: boolean,
  isMobile: boolean,
  isPaginatoring: boolean,
}

type State = {
  emailStatus: string,
  formMessage: string,
  formStatus: string,
  isFormDisabled: boolean,
  isFormFocused: boolean,
}

function mapStateToProps(state, props) {
  const streamType = selectStreamType(state)
  return {
    availability: selectAvailability(state),
    formActionPath: checkAvailability().payload.endpoint.path,
    isGridMode: selectIsGridMode(state),
    isLayoutToolHidden: selectIsLayoutToolHidden(state, props),
    isLoggedIn: selectIsLoggedIn(state),
    isMobile: selectIsMobile(state),
    isPaginatoring: streamType === LOAD_NEXT_CONTENT_REQUEST,
  }
}

class FooterContainer extends PureComponent {
  props: Props
  state: State

  static childContextTypes = {
    onClickScrollToTop: PropTypes.func.isRequired,
    onClickToggleLayoutMode: PropTypes.func.isRequired,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    onSubmit: PropTypes.func,
  }

  getChildContext() {
    const { isLoggedIn } = this.props
    return {
      onClickScrollToTop: this.onClickScrollToTop,
      onClickToggleLayoutMode: this.onClickToggleLayoutMode,
      onBlur: !isLoggedIn ? this.onBlur : null,
      onChange: !isLoggedIn ? this.onChange : null,
      onFocus: !isLoggedIn ? this.onFocus : null,
      onSubmit: !isLoggedIn ? this.onSubmit : null,
    }
  }

  componentWillMount() {
    this.state = {
      emailStatus: STATUS.INDETERMINATE,
      formMessage: '',
      formStatus: STATUS.INDETERMINATE,
      isFormDisabled: true,
      isFormFocused: false,
    }
    emailValue = ''
  }

  componentWillReceiveProps(nextProps) {
    const { availability } = nextProps
    if (!availability) { return }
    if (availability.has('email')) {
      this.validateEmailResponse(availability)
    }
  }

  onChange = ({ email }) => {
    emailValue = email
    const { emailStatus, isFormDisabled, formStatus } = this.state
    const currentStatus = emailStatus
    const clientState = getEmailStateFromClient({ value: email, currentStatus })
    if (formStatus !== STATUS.INDETERMINATE) {
      this.setState({ formStatus: STATUS.INDETERMINATE, formMessage: '' })
    }
    if (clientState.status === STATUS.SUCCESS && isFormDisabled) {
      this.setState({ emailStatus: STATUS.SUCCESS, isFormDisabled: false })
      return
    }
    if (currentStatus !== clientState.status) {
      this.setState({
        emailStatus: clientState.status,
        isFormDisabled: true,
      })
    }
  }

  // Only happens on iOS
  onBlur = () => {
    this.setState({ isFormFocused: false })
    if (isIOS() && document && document.body) {
      document.body.classList.remove('isModalActive')
    }
  }

  // Only happens on iOS
  onFocus = () => {
    if (isIOS() && document && document.body) {
      document.body.classList.add('isModalActive')
    }
    this.setState({ isFormFocused: true })
  }

  onClickScrollToTop = () => {
    scrollToPosition(0, 0)
  }

  onClickToggleLayoutMode = () => {
    const { dispatch, isGridMode } = this.props
    const newMode = isGridMode ? 'list' : 'grid'
    dispatch({ type: SET_LAYOUT_MODE, payload: { mode: newMode } })
  }

  onSubmit = (e: Event) => {
    const { dispatch } = this.props
    e.preventDefault()
    dispatch(checkAvailability({ email: emailValue, is_signup: true }))
  }

  validateEmailResponse(availability) {
    const { formStatus } = this.state
    const newState = getEmailStateFromServer({ availability, currentStatus: formStatus })
    if (newState.status === STATUS.SUCCESS) {
      this.setState({ formStatus: STATUS.SUCCESS, formMessage: 'Subscribed. See you tomorrow' })
      setTimeout(() => {
        const el = document.getElementById('FooterEmailInput')
        // $FlowFixMe
        if (el) { el.value = '' }
        this.setState({ formStatus: STATUS.INDETERMINATE, formMessage: '' })
      }, 1666)
    } else {
      this.setState({ formStatus: newState.status, formMessage: newState.message })
    }
  }

  render() {
    const props = {
      formActionPath: this.props.formActionPath,
      formMessage: this.state.formMessage,
      formStatus: this.state.formStatus,
      isFormDisabled: this.state.isFormDisabled,
      isFormFocused: this.state.isFormFocused,
      isGridMode: this.props.isGridMode,
      isLayoutToolHidden: this.props.isLayoutToolHidden,
      isLoggedIn: this.props.isLoggedIn,
      isMobile: this.props.isMobile,
      isPaginatoring: this.props.isPaginatoring,
      links,
    }
    return <Footer {...props} />
  }
}

export default connect(mapStateToProps)(FooterContainer)
<commit_msg>Use the correct id destructuring the email input<commit_after>// @flow
import React, { PropTypes, PureComponent } from 'react'
import { connect } from 'react-redux'
import { isIOS, scrollToPosition } from '../lib/jello'
import { FOOTER_LINKS as links } from '../constants/locales/en'
import { FORM_CONTROL_STATUS as STATUS } from '../constants/status_types'
import { LOAD_NEXT_CONTENT_REQUEST, SET_LAYOUT_MODE } from '../constants/action_types'
import { selectIsLoggedIn } from '../selectors/authentication'
import { selectIsGridMode, selectIsLayoutToolHidden, selectIsMobile } from '../selectors/gui'
import { selectAvailability } from '../selectors/profile'
import { selectStreamType } from '../selectors/stream'
import { checkAvailability } from '../actions/profile'
import { getEmailStateFromClient, getEmailStateFromServer } from '../components/forms/Validators'
import { Footer } from '../components/footer/FooterRenderables'
import type { Availability } from '../types/flowtypes'

let emailValue = ''

type Props = {
  availability: Availability,
  dispatch: () => void,
  formActionPath: string,
  isGridMode: boolean,
  isLayoutToolHidden: boolean,
  isLoggedIn: boolean,
  isMobile: boolean,
  isPaginatoring: boolean,
}

type State = {
  emailStatus: string,
  formMessage: string,
  formStatus: string,
  isFormDisabled: boolean,
  isFormFocused: boolean,
}

function mapStateToProps(state, props) {
  const streamType = selectStreamType(state)
  return {
    availability: selectAvailability(state),
    formActionPath: checkAvailability().payload.endpoint.path,
    isGridMode: selectIsGridMode(state),
    isLayoutToolHidden: selectIsLayoutToolHidden(state, props),
    isLoggedIn: selectIsLoggedIn(state),
    isMobile: selectIsMobile(state),
    isPaginatoring: streamType === LOAD_NEXT_CONTENT_REQUEST,
  }
}

class FooterContainer extends PureComponent {
  props: Props
  state: State

  static childContextTypes = {
    onClickScrollToTop: PropTypes.func.isRequired,
    onClickToggleLayoutMode: PropTypes.func.isRequired,
    onBlur: PropTypes.func,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    onSubmit: PropTypes.func,
  }

  getChildContext() {
    const { isLoggedIn } = this.props
    return {
      onClickScrollToTop: this.onClickScrollToTop,
      onClickToggleLayoutMode: this.onClickToggleLayoutMode,
      onBlur: !isLoggedIn ? this.onBlur : null,
      onChange: !isLoggedIn ? this.onChange : null,
      onFocus: !isLoggedIn ? this.onFocus : null,
      onSubmit: !isLoggedIn ? this.onSubmit : null,
    }
  }

  componentWillMount() {
    this.state = {
      emailStatus: STATUS.INDETERMINATE,
      formMessage: '',
      formStatus: STATUS.INDETERMINATE,
      isFormDisabled: true,
      isFormFocused: false,
    }
    emailValue = ''
  }

  componentWillReceiveProps(nextProps) {
    const { availability } = nextProps
    if (!availability) { return }
    if (availability.has('email')) {
      this.validateEmailResponse(availability)
    }
  }

  onChange = ({ FooterEmailInput: email }) => {
    emailValue = email
    const { emailStatus, isFormDisabled, formStatus } = this.state
    const currentStatus = emailStatus
    const clientState = getEmailStateFromClient({ value: email, currentStatus })
    if (formStatus !== STATUS.INDETERMINATE) {
      this.setState({ formStatus: STATUS.INDETERMINATE, formMessage: '' })
    }
    if (clientState.status === STATUS.SUCCESS && isFormDisabled) {
      this.setState({ emailStatus: STATUS.SUCCESS, isFormDisabled: false })
      return
    }
    if (currentStatus !== clientState.status) {
      this.setState({
        emailStatus: clientState.status,
        isFormDisabled: true,
      })
    }
  }

  // Only happens on iOS
  onBlur = () => {
    this.setState({ isFormFocused: false })
    if (isIOS() && document && document.body) {
      document.body.classList.remove('isModalActive')
    }
  }

  // Only happens on iOS
  onFocus = () => {
    if (isIOS() && document && document.body) {
      document.body.classList.add('isModalActive')
    }
    this.setState({ isFormFocused: true })
  }

  onClickScrollToTop = () => {
    scrollToPosition(0, 0)
  }

  onClickToggleLayoutMode = () => {
    const { dispatch, isGridMode } = this.props
    const newMode = isGridMode ? 'list' : 'grid'
    dispatch({ type: SET_LAYOUT_MODE, payload: { mode: newMode } })
  }

  onSubmit = (e: Event) => {
    const { dispatch } = this.props
    e.preventDefault()
    dispatch(checkAvailability({ email: emailValue, is_signup: true }))
  }

  validateEmailResponse(availability) {
    const { formStatus } = this.state
    const newState = getEmailStateFromServer({ availability, currentStatus: formStatus })
    if (newState.status === STATUS.SUCCESS) {
      this.setState({ formStatus: STATUS.SUCCESS, formMessage: 'Subscribed. See you tomorrow' })
      setTimeout(() => {
        const el = document.getElementById('FooterEmailInput')
        // $FlowFixMe
        if (el) { el.value = '' }
        this.setState({ formStatus: STATUS.INDETERMINATE, formMessage: '' })
      }, 1666)
    } else {
      this.setState({ formStatus: newState.status, formMessage: newState.message })
    }
  }

  render() {
    const props = {
      formActionPath: this.props.formActionPath,
      formMessage: this.state.formMessage,
      formStatus: this.state.formStatus,
      isFormDisabled: this.state.isFormDisabled,
      isFormFocused: this.state.isFormFocused,
      isGridMode: this.props.isGridMode,
      isLayoutToolHidden: this.props.isLayoutToolHidden,
      isLoggedIn: this.props.isLoggedIn,
      isMobile: this.props.isMobile,
      isPaginatoring: this.props.isPaginatoring,
      links,
    }
    return <Footer {...props} />
  }
}

export default connect(mapStateToProps)(FooterContainer)
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { getMessage } from '../../actions/index';

/* component styles */
import { styles } from './styles.scss';

export class MessageDisplay extends Component {

  componentWillMount() {
    this.socket = io();
    this.socket.on('message', data => {
      this.props.getMessage(data);
    });
  }

  parseMessage(msg, emotes) {
    let splitText;

    if (emotes) {
      splitText = msg.split('');
      for (let i in emotes) {
        let e = emotes[i];
        for (let j in e) {
          let mote = e[j];
          if (typeof mote === 'string') {
            mote = mote.split('-');
            mote = [parseInt(mote[0]), parseInt(mote[1])];
            let length =  mote[1] - mote[0];
            let empty = Array.apply(null, new Array(length + 1)).map(function() { return '' });
            splitText = splitText.slice(0, mote[0]).concat(empty).concat(splitText.slice(mote[1] + 1, splitText.length));
            splitText.splice(mote[0], 1, parseInt(i));
          }
        }
      }
      msg = splitText.reduce((msgArr, char, i) => {
        if (typeof char == 'number') {
          msgArr[msgArr.length] = parseInt(char);
        } else {
          if (typeof msgArr[msgArr.length - 1] == 'number') {
            msgArr[msgArr.length] = char;
          } else {
            msgArr[msgArr.length - 1] += char;
          }
        }
        return msgArr;
      }, ['']);
    } else {
      msg = [msg];
    }
    return msg;
  }

  renderMessage(msg) {
    return msg.map((chunk) => {
      if (typeof chunk === 'number') {
        return (
          <img className="emoticon" src={`http://static-cdn.jtvnw.net/emoticons/v1/${chunk}/3.0`} />
        );
      } else {
        return (
          {chunk}
        );
      }
    });
  }

  render() {
    return (
      <section className={`${styles}`}>

                <ReactCSSTransitionGroup className="ticker-user" transitionName="fade" transitionEnterTimeout={300} transitionLeaveTimeout={0}>

                    {this.props.user}

                </ReactCSSTransitionGroup>

                <ReactCSSTransitionGroup className="ticker-message" transitionName="carousel" transitionEnterTimeout={300} transitionLeaveTimeout={200}>

                    {this.renderMessage(this.parseMessage(this.props.message, this.props.emotes))}

                </ReactCSSTransitionGroup>

Insert Graph Here

    );
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ getMessage }, dispatch);
}

function mapStateToProps({ message }) {
  if (message.user) {
    return {
      message: message.msg,
      user: message.user.username,
      emotes: message.user.emotes,
    };
  }
  return { noMessage: message };
}

export default connect(mapStateToProps, mapDispatchToProps)(MessageDisplay);
<commit_msg>Include channel in component state, emit socket message when the selectedChannel changes<commit_after>import React, { Component } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { getMessage } from '../../actions/index';

/* component styles */
import { styles } from './styles.scss';

export class MessageDisplay extends Component {

  componentWillMount() {
    this.socket = io();
    this.socket.on('message', data => {
      this.props.getMessage(data);
    });
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedChannel === this.props.selectedChannel) return;
    // emit unsubscribe message
    if (this.props.selectedChannel) {
      this.socket.emit('channel:unsubscribe', this.props.selectedChannel);
    }
    // emit subscribe message
    this.socket.emit('channel:subscribe', nextProps.selectedChannel);
  }

  parseMessage(msg, emotes) {
    let splitText;

    if (emotes) {
      splitText = msg.split('');
      for (let i in emotes) {
        let e = emotes[i];
        for (let j in e) {
          let mote = e[j];
          if (typeof mote === 'string') {
            mote = mote.split('-');
            mote = [parseInt(mote[0]), parseInt(mote[1])];
            let length =  mote[1] - mote[0];
            let empty = Array.apply(null, new Array(length + 1)).map(function() { return '' });
            splitText = splitText.slice(0, mote[0]).concat(empty).concat(splitText.slice(mote[1] + 1, splitText.length));
            splitText.splice(mote[0], 1, parseInt(i));
          }
        }
      }
      msg = splitText.reduce((msgArr, char, i) => {
        if (typeof char == 'number') {
          msgArr[msgArr.length] = parseInt(char);
        } else {
          if (typeof msgArr[msgArr.length - 1] == 'number') {
            msgArr[msgArr.length] = char;
          } else {
            msgArr[msgArr.length - 1] += char;
          }
        }
        return msgArr;
      }, ['']);
    } else {
      msg = [msg];
    }
    return msg;
  }

  renderMessage(msg) {
    return msg.map((chunk) => {
      if (typeof chunk === 'number') {
        return (
          <img className="emoticon" src={`http://static-cdn.jtvnw.net/emoticons/v1/${chunk}/3.0`} />
        );
      } else {
        return (
          {chunk}
        );
      }
    });
  }

  render() {
    return (
      <section className={`${styles}`}>

          {this.props.selectedChannel}

                <ReactCSSTransitionGroup className="ticker-user" transitionName="fade" transitionEnterTimeout={300} transitionLeaveTimeout={0}>

                    {this.props.user}

                </ReactCSSTransitionGroup>

                <ReactCSSTransitionGroup className="ticker-message" transitionName="carousel" transitionEnterTimeout={300} transitionLeaveTimeout={200}>

                    {this.renderMessage(this.parseMessage(this.props.message, this.props.emotes))}

                </ReactCSSTransitionGroup>

Insert Graph Here

    );
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ getMessage }, dispatch);
}

function mapStateToProps({ message, channels }) {
  if (message.user) {
    return {
      message: message.msg,
      user: message.user.username,
      emotes: message.user.emotes,
      selectedChannel: channels.selected
    };
  }
  return { noMessage: message, selectedChannel: channels.selected };
}

export default connect(mapStateToProps, mapDispatchToProps)(MessageDisplay);
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { login } from '../actions/index'
import { browserHistory, Link } from 'react-router'

class Login extends Component {

	constructor(props) {
		super(props)
		this.state = {
			existingUser: null,
			user: {
				username: '',
				password: ''
			}
		}
		this.onFormSubmit = this.onFormSubmit.bind(this)
		this.onFieldChanged = this.onFieldChanged.bind(this)
	}

	onFieldChanged (event) {
    this.setState({
    	...this.state,
    	user: {
    		...this.state.user,
    		[event.target.name]: event.target.value
    	}
    })
  }

	onFormSubmit(event) {
		event.preventDefault()
		this.props.login(this.state.user)
		.then(({payload}) => {
			if(payload.data.msg) {
				this.setState({
					...this.state,
					existingUser: payload.data.msg
				})
			} else {
				browserHistory.push('/home')
			}
		})
		this.setState({
			user: {
				username: '',
				password: ''
			}
		})
	}

	render() {
		return(

Log In To Reading Buddy

							{ this.state.existingUser ?
{this.state.existingUser}
 : ''}

			    		<form
			    			role="form"
			    			onSubmit={this.onFormSubmit}>

					    				<input
					    					type="username"
					    					name="username"
					    					id="username"
					    					className="form-control input-sm"
					    					placeholder="Username"
					    					value={this.state.user.username}
					    					onChange={this.onFieldChanged}/>

			    						<input
			    							type="password"
			    							name="password"
			    							id="password"
			    							className="form-control input-sm"
			    							placeholder="Password"
			    							value={this.state.user.password}
			    							onChange={this.onFieldChanged}/>

			    			<input type="submit" value="Login" className="btn btn-info btn-block" />

			    			<Link to="/register" className="btn btn-info btn-block">
									Register
								</Link>

			    		</form>

		)
	}
}

function mapDispatchToProps(dispatch) {
	return bindActionCreators({ login }, dispatch)
}

export default connect(null, mapDispatchToProps)(Login)<commit_msg>put set state in then statement of dispatch call<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { login } from '../actions/index'
import { browserHistory, Link } from 'react-router'

class Login extends Component {

	constructor(props) {
		super(props)
		this.state = {
			existingUser: null,
			user: {
				username: '',
				password: ''
			}
		}
		this.onFormSubmit = this.onFormSubmit.bind(this)
		this.onFieldChanged = this.onFieldChanged.bind(this)
	}

	onFieldChanged (event) {
    this.setState({
    	...this.state,
    	user: {
    		...this.state.user,
    		[event.target.name]: event.target.value
    	}
    })
  }

	onFormSubmit(event) {
		event.preventDefault()
		this.props.login(this.state.user)
		.then(({payload}) => {
			if(payload.data.msg) {
				this.setState({
					...this.state,
					existingUser: payload.data.msg
				})
			} else {
				this.setState({
					user: {
						username: '',
						password: ''
					}
				})
				browserHistory.push('/home')
			}
		})
	}

	render() {
		return(

Log In To Reading Buddy

							{ this.state.existingUser ?
{this.state.existingUser}
 : ''}

			    		<form
			    			role="form"
			    			onSubmit={this.onFormSubmit}>

					    				<input
					    					type="username"
					    					name="username"
					    					id="username"
					    					className="form-control input-sm"
					    					placeholder="Username"
					    					value={this.state.user.username}
					    					onChange={this.onFieldChanged}/>

			    						<input
			    							type="password"
			    							name="password"
			    							id="password"
			    							className="form-control input-sm"
			    							placeholder="Password"
			    							value={this.state.user.password}
			    							onChange={this.onFieldChanged}/>

			    			<input type="submit" value="Login" className="btn btn-info btn-block" />

			    			<Link to="/register" className="btn btn-info btn-block">
									Register
								</Link>

			    		</form>

		)
	}
}

function mapDispatchToProps(dispatch) {
	return bindActionCreators({ login }, dispatch)
}

export default connect(null, mapDispatchToProps)(Login)<|endoftext|>


<commit_before>
"use strict";

let params = undefined;
let route = undefined;
let modal = undefined;
let _page = 1;
let filter = {};

/**
 * Default "list" controller. Usually good enough if all you need is a
 * clickable list of items in a Component.
 */
export default class ListController {

    /**
     * Class constructor.
     *
     * @return void
     */
    constructor() {
        this.page = _page;
    }

    /**
     * Reset ("reload") the current page.
     *
     * @return void
     */
    reset() {
        route.reset();
        this.page = 1;
    }

    /**
     * Getter for the current page number.
     *
     * @return integer Current page number.
     */
    get page() {
        return _page;
    }

    /**
     * Setter for the current page. Automatically refreshes the current
     * Collection via the registered Manager.
     *
     * @return void
     */
    set page(page) {
        _page = page;
        this.items = this.resource.query({filter: this.filter, limit: 10, offset: (page - 1) * 10});
    }

    get filter() {
        return filter;
    }

    set filter(f) {
        filter = f;
        this.page = 1;
    }

};

ListController.$inject = ['$scope', '$route', '$uibModal'];

<commit_msg>only set this if items were not filled via bindings<commit_after>
"use strict";

let params = undefined;
let route = undefined;
let modal = undefined;
let _page = 1;
let filter = {};

/**
 * Default "list" controller. Usually good enough if all you need is a
 * clickable list of items in a Component.
 */
export default class ListController {

    /**
     * Class constructor.
     *
     * @return void
     */
    constructor() {
        if (!this.items) {
            this.page = _page;
        }
    }

    /**
     * Reset ("reload") the current page.
     *
     * @return void
     */
    reset() {
        route.reset();
        this.page = 1;
    }

    /**
     * Getter for the current page number.
     *
     * @return integer Current page number.
     */
    get page() {
        return _page;
    }

    /**
     * Setter for the current page. Automatically refreshes the current
     * Collection via the registered Manager.
     *
     * @return void
     */
    set page(page) {
        _page = page;
        this.items = this.resource.query({filter: this.filter, limit: 10, offset: (page - 1) * 10});
    }

    get filter() {
        return filter;
    }

    set filter(f) {
        filter = f;
        this.page = 1;
    }

};

ListController.$inject = ['$scope', '$route', '$uibModal'];

<|endoftext|>


<commit_before>/**
 * 3dof (Gear VR, Daydream) controls for mobile.
 */
module.exports = AFRAME.registerComponent('trackpad-controls', {
  schema: {
    enabled: { default: true }
  },

  init: function () {
    this.dVelocity = new THREE.Vector3();
    this.zVel      = 0;
    this.bindMethods();
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
    this.dVelocity.set(0, 0, 0);
  },

  remove: function () {
    this.pause();
  },

  addEventListeners: function () {
    const sceneEl = this.el.sceneEl;

    sceneEl.addEventListener('axismove', this.onAxisMove);
    sceneEl.addEventListener('trackpadtouchstart', this.onTouchStart);
    sceneEl.addEventListener('trackpadtouchend', this.onTouchEnd);

  },

  removeEventListeners: function () {
    const sceneEl = this.el.sceneEl;

    sceneEl.removeEventListener('axismove', this.onAxisMove);
    sceneEl.removeEventListener('trackpadtouchstart', this.onTouchStart);
    sceneEl.removeEventListener('trackpadtouchend', this.onTouchEnd);

  },

  isVelocityActive: function () {
    return this.data.enabled && this.isMoving;
  },

  getVelocityDelta: function () {
    this.dVelocity.z = this.isMoving ? -this.zVel : 1;
    return this.dVelocity.clone();
  },

  bindMethods: function () {
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
    this.onAxisMove = this.onAxisMove.bind(this);
  },

  onTouchStart: function (e) {
    this.isMoving = true;
    e.preventDefault();
  },

  onTouchEnd: function (e) {
    this.isMoving = false;
    e.preventDefault();
  },

  onAxisMove: function(e){
    var axis_data = e.detail.axis;

    if(axis_data[1] < 0){
      this.zVel = 1;
    }

    if(axis_data[1] > 0){
      this.zVel = -1;
    }

  }

});
<commit_msg>Swipe-based rather than touch based trackpad movement<commit_after>/**
 * 3dof (Gear VR, Daydream) controls for mobile.
 */
module.exports = AFRAME.registerComponent('trackpad-controls', {
  schema: {
    enabled: { default: true }
  },

  init: function () {
    this.dVelocity = new THREE.Vector3();
    this.zVel      = 0;
    this.bindMethods();
  },

  play: function () {
    this.addEventListeners();
  },

  pause: function () {
    this.removeEventListeners();
    this.dVelocity.set(0, 0, 0);
  },

  remove: function () {
    this.pause();
  },

  addEventListeners: function () {
    const sceneEl = this.el.sceneEl;

    sceneEl.addEventListener('axismove', this.onAxisMove);
    sceneEl.addEventListener('trackpadtouchstart', this.onTouchStart);
    sceneEl.addEventListener('trackpadtouchend', this.onTouchEnd);

  },

  removeEventListeners: function () {
    const sceneEl = this.el.sceneEl;

    sceneEl.removeEventListener('axismove', this.onAxisMove);
    sceneEl.removeEventListener('trackpadtouchstart', this.onTouchStart);
    sceneEl.removeEventListener('trackpadtouchend', this.onTouchEnd);

  },

  isVelocityActive: function () {
    return this.data.enabled && this.isMoving;
  },

  getVelocityDelta: function () {
    this.dVelocity.z = this.isMoving ? -this.zVel : 1;
    this.dVelocity.x = this.isMoving ? this.xVel : 1;
    return this.dVelocity.clone();
  },

  bindMethods: function () {
    this.onTouchStart = this.onTouchStart.bind(this);
    this.onTouchEnd = this.onTouchEnd.bind(this);
    this.onAxisMove = this.onAxisMove.bind(this);
  },

  onTouchStart: function (e) {
    this.startingAxisData = [];
    this.canRecordAxis = true;
    e.preventDefault();
  },

  onTouchEnd: function (e) {
    this.startingAxisData = [];
    this.isMoving = false;
    e.preventDefault();
  },

  onAxisMove: function(e){
    var axis_data = e.detail.axis;

    if(this.startingAxisData.length === 0){
      this.startingAxisData[0] = axis_data[0]
      this.startingAxisData[1] = axis_data[1]
    }

    if(this.startingAxisData.length > 0){
      var movingLeft     = this.startingAxisData[0] > 0 && axis_data[0] < 0
      var movingRight    = this.startingAxisData[0] < 0 && axis_data[0] > 0
      var movingForward  = this.startingAxisData[1] > 0 && axis_data[1] < 0
      var movingBackward = this.startingAxisData[1] < 0 && axis_data[1] > 0

      var velX = axis_data[0] < this.startingAxisData[0] ? -1 : 1
      var velZ = axis_data[1] < this.startingAxisData[1] ? 1 : -1

      var absChangeZ = Math.abs(this.startingAxisData[1] - axis_data[1])
      var absChangeX = Math.abs(this.startingAxisData[0] - axis_data[0])

      if(absChangeZ > absChangeX) {
        this.xVel = 0;
        this.zVel = velZ;
        this.isMoving = true;

      }else{
        this.zVel = 0;
        this.xVel = velX
        this.isMoving = true;
      }
    }
  }
});

<|endoftext|>


<commit_before>import $ from 'dom7';
import { window /* , document */ } from 'ssr-window';
// import Device from '../../utils/device';
// import Support from '../../utils/support';
import ViewClass from '../../components/view/view-class';

function initClicks(app) {
  function handleClicks(e) {
    const $clickedEl = $(e.target);
    const $clickedLinkEl = $clickedEl.closest('a');
    const isLink = $clickedLinkEl.length > 0;
    const url = isLink && $clickedLinkEl.attr('href');
    // const isTabLink = isLink && $clickedLinkEl.hasClass('tab-link') && ($clickedLinkEl.attr('data-tab') || (url && url.indexOf('#') === 0));

    // Check if link is external
    if (isLink) {
      // eslint-disable-next-line
      if ($clickedLinkEl.is(app.params.clicks.externalLinks) || (url && url.indexOf('javascript:') >= 0)) {
        const target = $clickedLinkEl.attr('target');
        if (
          url
          && window.cordova
          && window.cordova.InAppBrowser
          && (target === '_system' || target === '_blank')
        ) {
          e.preventDefault();
          window.cordova.InAppBrowser.open(url, target);
        }
        return;
      }
    }

    // Modules Clicks
    Object.keys(app.modules).forEach((moduleName) => {
      const moduleClicks = app.modules[moduleName].clicks;
      if (!moduleClicks) return;
      if (e.preventF7Router) return;
      Object.keys(moduleClicks).forEach((clickSelector) => {
        const matchingClickedElement = $clickedEl.closest(clickSelector).eq(0);
        if (matchingClickedElement.length > 0) {
          moduleClicks[clickSelector].call(app, matchingClickedElement, matchingClickedElement.dataset(), e);
        }
      });
    });

    // Load Page
    let clickedLinkData = {};
    if (isLink) {
      e.preventDefault();
      clickedLinkData = $clickedLinkEl.dataset();
    }

    // Prevent Router
    if (e.preventF7Router) return;
    if ($clickedLinkEl.hasClass('prevent-router') || $clickedLinkEl.hasClass('router-prevent')) return;

    const validUrl = url && url.length > 0 && url[0] !== '#';
    if (validUrl || $clickedLinkEl.hasClass('back')) {
      let view;
      if (clickedLinkData.view) {
        view = $(clickedLinkData.view)[0].f7View;
      } else {
        view = $clickedEl.parents('.view')[0] && $clickedEl.parents('.view')[0].f7View;
        if (!$clickedLinkEl.hasClass('back') && view && view.params.linksView) {
          if (typeof view.params.linksView === 'string') view = $(view.params.linksView)[0].f7View;
          else if (view.params.linksView instanceof ViewClass) view = view.params.linksView;
        }
      }
      if (!view) {
        if (app.views.main) view = app.views.main;
      }
      if (!view || !view.router) return;
      if (clickedLinkData.context && typeof clickedLinkData.context === 'string') {
        try {
          clickedLinkData.context = JSON.parse(clickedLinkData.context);
        } catch (err) {
          // something wrong there
        }
      }
      if ($clickedLinkEl[0].f7RouteProps) {
        clickedLinkData.props = $clickedLinkEl[0].f7RouteProps;
      }
      if ($clickedLinkEl.hasClass('back')) view.router.back(url, clickedLinkData);
      else view.router.navigate(url, clickedLinkData);
    }
  }

  app.on('click', handleClicks);

  // TODO: check if need this in iOS
  // Prevent scrolling on overlays
  // function preventScrolling(e) {
  //   e.preventDefault();
  // }
  // if (Support.touch && !Device.android) {
  //   const activeListener = Support.passiveListener ? { passive: false, capture: false } : false;
  //   $(document).on((app.params.touch.fastClicks ? 'touchstart' : 'touchmove'), '.panel-backdrop, .dialog-backdrop, .preloader-backdrop, .popup-backdrop, .searchbar-backdrop', preventScrolling, activeListener);
  // }
}
export default {
  name: 'clicks',
  params: {
    clicks: {
      // External Links
      externalLinks: '.external',
    },
  },
  on: {
    init() {
      const app = this;
      initClicks(app);
    },
  },
};
<commit_msg>Core: Clicks - add support for data-view="current" on links<commit_after>import $ from 'dom7';
import { window /* , document */ } from 'ssr-window';
// import Device from '../../utils/device';
// import Support from '../../utils/support';
import ViewClass from '../../components/view/view-class';

function initClicks(app) {
  function handleClicks(e) {
    const $clickedEl = $(e.target);
    const $clickedLinkEl = $clickedEl.closest('a');
    const isLink = $clickedLinkEl.length > 0;
    const url = isLink && $clickedLinkEl.attr('href');
    // const isTabLink = isLink && $clickedLinkEl.hasClass('tab-link') && ($clickedLinkEl.attr('data-tab') || (url && url.indexOf('#') === 0));

    // Check if link is external
    if (isLink) {
      // eslint-disable-next-line
      if ($clickedLinkEl.is(app.params.clicks.externalLinks) || (url && url.indexOf('javascript:') >= 0)) {
        const target = $clickedLinkEl.attr('target');
        if (
          url
          && window.cordova
          && window.cordova.InAppBrowser
          && (target === '_system' || target === '_blank')
        ) {
          e.preventDefault();
          window.cordova.InAppBrowser.open(url, target);
        }
        return;
      }
    }

    // Modules Clicks
    Object.keys(app.modules).forEach((moduleName) => {
      const moduleClicks = app.modules[moduleName].clicks;
      if (!moduleClicks) return;
      if (e.preventF7Router) return;
      Object.keys(moduleClicks).forEach((clickSelector) => {
        const matchingClickedElement = $clickedEl.closest(clickSelector).eq(0);
        if (matchingClickedElement.length > 0) {
          moduleClicks[clickSelector].call(app, matchingClickedElement, matchingClickedElement.dataset(), e);
        }
      });
    });

    // Load Page
    let clickedLinkData = {};
    if (isLink) {
      e.preventDefault();
      clickedLinkData = $clickedLinkEl.dataset();
    }

    // Prevent Router
    if (e.preventF7Router) return;
    if ($clickedLinkEl.hasClass('prevent-router') || $clickedLinkEl.hasClass('router-prevent')) return;

    const validUrl = url && url.length > 0 && url[0] !== '#';
    if (validUrl || $clickedLinkEl.hasClass('back')) {
      let view;
      if (clickedLinkData.view && clickedLinkData.view === 'current') {
        view = app.views.current;
      } else if (clickedLinkData.view) {
        view = $(clickedLinkData.view)[0].f7View;
      } else {
        view = $clickedEl.parents('.view')[0] && $clickedEl.parents('.view')[0].f7View;
        if (!$clickedLinkEl.hasClass('back') && view && view.params.linksView) {
          if (typeof view.params.linksView === 'string') view = $(view.params.linksView)[0].f7View;
          else if (view.params.linksView instanceof ViewClass) view = view.params.linksView;
        }
      }
      if (!view) {
        if (app.views.main) view = app.views.main;
      }
      if (!view || !view.router) return;
      if (clickedLinkData.context && typeof clickedLinkData.context === 'string') {
        try {
          clickedLinkData.context = JSON.parse(clickedLinkData.context);
        } catch (err) {
          // something wrong there
        }
      }
      if ($clickedLinkEl[0].f7RouteProps) {
        clickedLinkData.props = $clickedLinkEl[0].f7RouteProps;
      }
      if ($clickedLinkEl.hasClass('back')) view.router.back(url, clickedLinkData);
      else view.router.navigate(url, clickedLinkData);
    }
  }

  app.on('click', handleClicks);

  // TODO: check if need this in iOS
  // Prevent scrolling on overlays
  // function preventScrolling(e) {
  //   e.preventDefault();
  // }
  // if (Support.touch && !Device.android) {
  //   const activeListener = Support.passiveListener ? { passive: false, capture: false } : false;
  //   $(document).on((app.params.touch.fastClicks ? 'touchstart' : 'touchmove'), '.panel-backdrop, .dialog-backdrop, .preloader-backdrop, .popup-backdrop, .searchbar-backdrop', preventScrolling, activeListener);
  // }
}
export default {
  name: 'clicks',
  params: {
    clicks: {
      // External Links
      externalLinks: '.external',
    },
  },
  on: {
    init() {
      const app = this;
      initClicks(app);
    },
  },
};
<|endoftext|>


<commit_before>avalon.scan();

//路由定义
require(["mmRouter", "mmRequest", "nav"], function () {

    //根据UI表创建对应的路由和模块配置
        function newRouter(n){
            avalon.router.get('/'+ n, function () {
                modal.getOutByRouter()
                layout.url = "./ui/"+ n+"/doc.html"
                getDoc("./ui/"+ n+"/README.md")
                require(["../../ui/"+n+"/"+n+'.js'],function(){
                    console.log(n+"加载成功")
                })
            });
        }

    //监听路由
    avalon.router.get('/', function () {
        //调用门禁
        modal.getOutByRouter()
        layout.url = ""
        //加载所依赖的VM
        require(["home"],function(){
            getDoc("./README.md")
            avalon.scan()
        })
    });

    //组件列表
    avalon.router.get('/list', function () {
        //调用门禁
        modal.getIn(960)
        modal.url = "./body/list.html"
        require(["ui"], function () {
            ui.ready()
            avalon.scan()
        })
        //加载所依赖的VM
    });

    //获取ui列表
    avalon.ajax({
        type: "get",
        url: "./ui.json",
        success: function (res) {
            cache.go(res)

            //生成路由
            for(var i=0;i< res.uiList.length;i++){
                var n= res.uiList[i].name
                //构建路由
                newRouter(n)
                console.log(n)
            }
            //开始监听路由
            avalon.history.start();
        }
    })

    //开始监听

    //扫描
    avalon.scan();
});<commit_msg>组件文档库构建完毕，准备开始写组件了<commit_after>avalon.scan();

//路由定义
require(["mmRouter", "mmRequest", "nav"], function () {

    //根据UI表创建对应的路由和模块配置
        function newRouter(n){
            avalon.router.get('/'+ n, function () {
                modal.getOutByRouter()
                var uiURL='./ui/'+n+"/";
                layout.url = uiURL+"doc.html"
                getDoc(uiURL+"README.md")
                require(["../../ui/"+n+"/"+n+'.js'],function(){
                    console.log(n+"加载成功")
                })
            });
        }

    //监听路由
    avalon.router.get('/', function () {
        //调用门禁
        modal.getOutByRouter()
        layout.url = ""
        //加载所依赖的VM
        require(["home"],function(){
            getDoc("./README.md")
            avalon.scan()
        })
    });

    //组件列表
    avalon.router.get('/list', function () {
        //调用门禁
        modal.getIn(960)
        modal.url = "./body/list.html"
        require(["ui"], function () {
            ui.ready()
            avalon.scan()
        })
        //加载所依赖的VM
    });

    //获取ui列表
    avalon.ajax({
        type: "get",
        url: "./ui.json",
        success: function (res) {
            cache.go(res)

            //生成路由
            for(var i=0;i< res.uiList.length;i++){
                var n= res.uiList[i].name
                //构建路由
                newRouter(n)
                console.log(n)
            }
            //开始监听路由
            avalon.history.start();
        }
    })

    //开始监听

    //扫描
    avalon.scan();
});<|endoftext|>


<commit_before>'use strict'

var ipc = null;
const VERBS  = ['GET', 'POST', 'UPDATE', 'DELETE'];
var CACHED = { std: [], http: {} };

const PROC_SIDES = { 'RENDERER': 0, 'MAIN': 1, TR: { 0: 'RENDERER', 1: 'MAIN' } };
const SIDE_EVTS = {
	0: { GET: 'ROUTER_REND_SET_ROUTES', SET: 'ROUTER_REND_GET_ROUTES' },
	1: { GET: 'ROUTER_MAIN_SET_ROUTES', SET: 'ROUTER_MAIN_GET_ROUTES' }
}
var PROC_SIDE = 'MAIN';

function _wrap( route ){
	var ret = {};
	Object.keys( route ).forEach( r => {
		ret[r] = {
			http: Object.keys( route[r].http ).map( h => {
				return h;
			})
		};
		if( route[r].std.length ){
			ret[r].std = route[r].std.length
		}
	})
	return JSON.stringify( ret );
}

function _storeOnCache( route ){
	route = JSON.parse( route );
	Object.keys( route ).forEach( r => {
		if( route[r].std && (-1 === CACHED.std.indexOf( r )) ){
			CACHED.std.push( r );
		}
		route[r].http.forEach( h => {
			if( (-1 === CACHED.http[h].indexOf( r )) ){
				CACHED.http[h].push( r );
			}
		})
	})
}

function _hashRouteListen( verb, route, back ){
	verb  = verb.trim().toUpperCase();
	route = route.trim()//;.toUpperCase();
	back = (back || false);
	if( verb === 'STD' || (3 > arguments.length)){
		return verb + '::' + route + '::' + PROC_SIDES.TR[PROC_SIDE];
	}else if( !back ){
		return verb + '::' + route + '::' + PROC_SIDES.TR[PROC_SIDE] + '::' + 'FORW';
	}else{
		return verb + '::' + route + '::' + PROC_SIDES.TR[1-PROC_SIDE] + '::' + 'BACK';
	}
}

function _hashRouteSend( verb, route, back ){
	verb  = verb.trim().toUpperCase();
	route = route.trim()//;.toUpperCase();
	back = (back || false);
	if( verb === 'STD' || (3 > arguments.length)){
		return verb + '::' + route + '::' + PROC_SIDES.TR[1-PROC_SIDE];
	}else if( !back ){
		return verb + '::' + route + '::' + PROC_SIDES.TR[1-PROC_SIDE] + '::' + 'FORW';
	}else{
		return verb + '::' + route + '::' + PROC_SIDES.TR[PROC_SIDE] + '::' + 'BACK';
	}
}

class Router {

	constructor( name, window ) {

		this._name = name;
		this._routes = {};
		this._window = window;

		// Prepare CACHE
		VERBS.forEach( verb => {
			CACHED.http[ verb ] = [];
		})

		this._setup();
	}

	_setup(){
		// Here we set the necessary callbacks for retrieving
		// other side events
		if( ipc ){
			var that = this;
			//console.log('[SETUP]', PROC_SIDE, 'LISTENING FOR', SIDE_EVTS[PROC_SIDE].GET);
			// Register a handler for retrieving other's side route
			ipc.on(SIDE_EVTS[PROC_SIDE].GET, function( event, routes ){
				_storeOnCache( routes );
				//console.log('RESTORE CACHE', CACHED);
			})
			//console.log('[SETUP]', PROC_SIDE, 'RETRIEVING FOR', SIDE_EVTS[1-PROC_SIDE].GET);
			ipc.on(SIDE_EVTS[1-PROC_SIDE].SET, function( event, routes ){
				that._sendCache();
			})
			this._requestCache();
		}
	}

	_requestCache(){

		// If we are on a different process "routes" is going to be empty
		// so no route is going to be fired, as every method registers on
		// ipc too, we can reconstruct the routes based on ipc channel
		// Reconstruct all routes
		//console.log('[SETUP]', PROC_SIDES.TR[PROC_SIDE], 'SENDING FOR', SIDE_EVTS[PROC_SIDE].SET);
		if( this._window ){
			this._window.send(SIDE_EVTS[PROC_SIDE].SET);
		}else{
			ipc.send(SIDE_EVTS[PROC_SIDE].SET);
		}

	}

	_sendCache(){

		if( this._window ){
			this._window.send( SIDE_EVTS[1-PROC_SIDE].GET, _wrap(this._routes) );
		}else{
			ipc.send( SIDE_EVTS[1-PROC_SIDE].GET, _wrap(this._routes) );
		}

	}

	_common( argss, verb ){

		var args = Array.prototype.slice.call( argss, 0 );
		if( 1 === args.length ){
			throw new Error('Bad arguments, MUST provide a route and a callback')
		}

		var route = args.shift();
		var cb = args.pop();
		verb = verb.toUpperCase();

		if( typeof cb === 'function' ){

			this._setupRoute( route );
			this._routes[ route ][ 'http' ][ verb ] = cb;

		}

		//console.log('Registering route over http', verb, route, _hashRouteListen( verb, route ));

		var that = this;

		if( ipc ){

			ipc.on( _hashRouteListen( verb, route ), function( event, req ){

				req = JSON.parse( req );
				//console.log('Inside receiver cb', req);
				req.push({
					json: function( err, obj ){
						if( arguments.length === 1 ){
							obj = err;
							err = null;
						}
						//console.log('sending to', this._window?'window':'ipc', _hashRouteSend( verb, route, true ));
						if( this._window ){
							this._window.send.apply(this._window, [_hashRouteSend( verb, route, true ), err].concat(JSON.parse(JSON.stringify( obj ))));
						}else{
							ipc.send.apply(ipc, [_hashRouteSend( verb, route, true ), err].concat(JSON.parse(JSON.stringify( obj ))));
						}
					}.bind(that)
				});
				cb.apply( that, req );

			});

		}

		this._sendCache();

	}

	_setupRoute( route ){

		if( this._routes.hasOwnProperty( route ) ){
			return;
		}

		this._routes[ route ] = {
			std: [],
			http: {}
		};

	}

	// SENDER
	send(){

		// Re-Cache, just in case...
		this._requestCache();

		var that = this;
		var fnArgs = arguments;

		setTimeout(function() {

			var args = Array.prototype.slice.call(fnArgs, 0);

			if( that._window ){
				that._window.send.apply( that._window, args || [] );
			}

			var routes = [args.shift()];
			if( routes[0] === '*' ){
				routes = routes.concat( Object.keys( that._routes ) );
			}else{
				routes = ['*'].concat( routes );
			}

			routes.forEach(function( route ){

				if( ipc && (-1 !== CACHED.std.indexOf(route) )){

					if( that._window ){
						that._window.send.apply(that._window, [_hashRouteSend( 'STD', route )].concat( JSON.stringify(args || []) ));
					}else{
						ipc.send.apply(ipc, [_hashRouteSend( 'STD', route )].concat( JSON.stringify(args || []) ));
					}

				}
				// Not registered
				if( that._routes.hasOwnProperty( route ) ){

					that._routes[ route ][ 'std' ].forEach(function( cb ){

						cb.apply( null, args || [] );

					})

				}
			}, that)

		}, 10);

	}

	// RECEIVER
	on(){

		var args = Array.prototype.slice.call(arguments, 0);
		if( 1 === args.length ){
			throw new Error('Bad arguments, MUST provide a route and a callback')
		}

		var route = args.shift();
		var endCb = args.pop();

		if( typeof endCb === 'function' ){

			this._setupRoute( route );
			this._routes[ route ][ 'std' ].push( endCb );

			if( ipc ){

				ipc.on( _hashRouteListen( 'STD', route ), function(){

					args = Array.prototype.slice.call(arguments, 1);
					endCb.apply( null, JSON.parse(args) );

				})

			}

		}

		this._sendCache();

	}

	// SENDER
	route(){

		var args = Array.prototype.slice.call(arguments, 0);
		if( 3 >  args.length ){
			throw new Error('Bad arguments, MUST provide a route, a method and a callback')
		}
		var route = args.shift();
		var verbs = [args.shift().toUpperCase()];
		var params= [];

		var len = args.length-1;
		var i 	= 0;
		while( i++ < len ){
			params.push( args.shift() );
		}

		var cb = args.pop();

		// Re-Cache, just in case...
		this._requestCache();

		var that = this;
		setTimeout(function() {

			if( verbs[0] === '*' ){
				verbs = VERBS;
			}

			verbs.forEach(function( verb ){

				var fnArgs = [{
					method: verb,
					params: JSON.parse( JSON.stringify(params) )
				}];

				//console.log('Routing', verb, route, _hashRouteSend( verb, route ), fnArgs);

				if( ipc && (-1 !== CACHED.http[verb].indexOf(route) )){

					ipc.once(_hashRouteListen( verb, route, true ), function(){
						var args = Array.prototype.slice.call( arguments, 1 );
						cb( args.splice(0, 1)[0], JSON.parse( JSON.stringify( args ) ) );
					})
					//console.log('send', 'registered back on', _hashRouteListen( verb, route, true ));
					if( that._window ){
						that._window.send.apply(that._window, [_hashRouteSend( verb, route )].concat( JSON.stringify(fnArgs || []) ));
					}else{
						ipc.send.apply(ipc, [_hashRouteSend( verb, route )].concat( JSON.stringify(fnArgs || []) ));
					}

				}
				if(
					that._routes.hasOwnProperty( route ) &&
					that._routes[ route ][ 'http' ][verb] &&
					typeof that._routes[ route ][ 'http' ][verb] === 'function'
				){

					fnArgs = fnArgs.concat({
						json: function( err, obj ){
							console.log('inner json route', err, obj, arguments);
							if( arguments.length === 1 ){
								obj = err;
								err = null;
							}
							cb( err, JSON.parse( JSON.stringify( obj ) ) );
						}
					})
					that._routes[ route ][ 'http' ][ verb ].apply( null, fnArgs );

				}// Not registered, skip on std router

			}, that);

		}, 10);

	}

	// RECEIVERS
	post(){

		this._common( arguments, 'POST' );

	}

	get(){

		this._common( arguments, 'GET' );

	}

	update(){

		this._common( arguments, 'UPDATE' );
	}

	delete(){

		this._common( arguments, 'DELETE' );

	}

}

//module.exports = Router;
var _router = null;
module.exports = function( name, window ){
	try{

		ipc = require('electron').ipcMain;

		if( ipc !== undefined ){
			// We are on main process
			PROC_SIDE = PROC_SIDES.MAIN;
		}else{
			ipc = require('electron').ipcRenderer;
			PROC_SIDE = PROC_SIDES.RENDERER;
		}

	}catch( e ){
		// Non electron environment
	}
	if( null === _router ){
		_router = new Router( name, window );
	}
	return _router;
}
<commit_msg>Stability and Fixes<commit_after>'use strict'

var ipc = null;
const VERBS  = ['GET', 'POST', 'UPDATE', 'DELETE'];
var CACHED = { std: [], http: {} };

const PROC_SIDES = { 'RENDERER': 0, 'MAIN': 1, TR: { 0: 'RENDERER', 1: 'MAIN' } };
const SIDE_EVTS = {
	0: { GET: 'ROUTER_REND_SET_ROUTES', SET: 'ROUTER_REND_GET_ROUTES' },
	1: { GET: 'ROUTER_MAIN_SET_ROUTES', SET: 'ROUTER_MAIN_GET_ROUTES' }
}
var PROC_SIDE = 'MAIN';

function _wrap( route ){
	var ret = {};
	Object.keys( route ).forEach( r => {
		ret[r] = {
			http: Object.keys( route[r].http ).map( h => {
				return h;
			})
		};
		if( route[r].std.length ){
			ret[r].std = route[r].std.length
		}
	})
	return JSON.stringify( ret );
}

function _storeOnCache( route ){
	route = JSON.parse( route );
	Object.keys( route ).forEach( r => {
		if( route[r].std && (-1 === CACHED.std.indexOf( r )) ){
			CACHED.std.push( r );
		}
		route[r].http.forEach( h => {
			if( (-1 === CACHED.http[h].indexOf( r )) ){
				CACHED.http[h].push( r );
			}
		})
	})
}

function _hashRoute( verb, route, back, sendOrListen){
	verb  = verb.trim().toUpperCase();
	route = route.trim();
	var base = verb + '::' + route;
	var proc = (sendOrListen === 'LISTEN')?PROC_SIDES.TR[PROC_SIDE]:PROC_SIDES.TR[1-PROC_SIDE];
	var no_p = (sendOrListen === 'LISTEN')?PROC_SIDES.TR[1-PROC_SIDE]:PROC_SIDES.TR[PROC_SIDE];
	var dir  = (sendOrListen === 'LISTEN')?'FORW':'BACK';
	var separator = '::';
	if( verb === 'STD' || undefined === back ){
		base += ( separator + proc );
	}else if( !back ){
		base += ( separator + proc + separator + dir );
	}else{
		base += ( separator + no_p + separator + 'BACK' );
	}
	return base;
}

function _hashRouteListen( verb, route, back ){
	return _hashRoute( verb, route, back, 'LISTEN');
}

function _hashRouteSend( verb, route, back ){
	return _hashRoute( verb, route, back, 'SEND');
}

class Router {

	constructor( name, window ) {

		this._name = name;
		this._routes = {};
		this._windows = [];

		// Prepare CACHE
		VERBS.forEach( verb => {
			CACHED.http[ verb ] = [];
		})

		this._setup( window );
	}

	_setup( window ){
		// Here we set the necessary callbacks for retrieving
		// other side events
		if( ipc ){
			var that = this;
			// Register a handler for retrieving other's side route
			ipc.on(SIDE_EVTS[PROC_SIDE].GET, function( event, routes ){
				_storeOnCache( routes );
			})
			ipc.on(SIDE_EVTS[1-PROC_SIDE].SET, function( event, routes ){
				that._sendCache();
			})
			this._requestCache();
		}
		if( window ){
			this._windows.push( window );
			var idx = this._windows.length -1;
			window.on('closed', function(){ this._cleanWindow( idx ) }.bind(this))
		}
	}

	_requestCache(){

		// If we are on a different process "routes" is going to be empty
		// so no route is going to be fired, as every method registers on
		// ipc too, we can reconstruct the routes based on ipc channel
		// Reconstruct all routes
		if( this._windows.length ){
			this._windows.forEach( w => { w.send(SIDE_EVTS[PROC_SIDE].SET) }, this);
		}else if( ipc && ipc.send ){
			ipc.send(SIDE_EVTS[PROC_SIDE].SET);
		}

	}

	_sendCache(){

		if( this._windows.length ){
			this._windows.forEach(w => { w.send( SIDE_EVTS[1-PROC_SIDE].GET, _wrap(this._routes) ) }, this);
		}else if( ipc && ipc.send ){
			ipc.send( SIDE_EVTS[1-PROC_SIDE].GET, _wrap(this._routes) );
		}

	}

	_cleanWindow( idx ){

		this._windows.splice( idx, 1 );

	}

	_common( argss, verb ){

		var args = Array.prototype.slice.call( argss, 0 );
		if( 1 === args.length ){
			throw new Error('Bad arguments, MUST provide a route and a callback')
		}

		var route = args.shift();
		var cb = args.pop();
		verb = verb.toUpperCase();

		if( typeof cb === 'function' ){

			this._setupRoute( route );
			this._routes[ route ][ 'http' ][ verb ] = cb;

		}

		//console.log('Registering route over http', verb, route, _hashRouteListen( verb, route ));

		var that = this;

		if( ipc ){

			ipc.on( _hashRouteListen( verb, route ), function( event, req ){

				req = JSON.parse( req );
				//console.log('Inside receiver cb', req);
				req.push({
					json: function( err, obj ){
						if( arguments.length === 1 ){
							obj = err;
							err = null;
						}
						//console.log('sending to', this._window?'window':'ipc', _hashRouteSend( verb, route, true ));
						if( this._windows.length ){
							this._windows.forEach( w => { w.send.apply(w, [_hashRouteSend( verb, route, true ), err].concat(JSON.parse(JSON.stringify( obj )))) }, this );
						}else if( ipc && ipc.send ){
							ipc.send.apply(ipc, [_hashRouteSend( verb, route, true ), err].concat(JSON.parse(JSON.stringify( obj ))));
						}
					}.bind(that)
				});
				cb.apply( that, req );

			});

		}

		this._sendCache();

	}

	_setupRoute( route ){

		if( this._routes.hasOwnProperty( route ) ){
			return;
		}

		this._routes[ route ] = {
			std: [],
			http: {}
		};

	}

	// SENDER - One direction
	send(){

		var that = this;
		var fnArgs = arguments;

		// Ensure
		process.nextTick(function(){

			var args = Array.prototype.slice.call(fnArgs, 0);
			if( that._windows.length ){
				that._windows.forEach( w => { w.send.apply( w, args || [] ) }, that );
			}

			var routes = [args.shift()];
			if( routes[0] === '*' ){
				routes = routes.concat( Object.keys( that._routes ) );
			}else{
				routes = ['*'].concat( routes );
			}
			routes.forEach(function( route ){

				if( ipc && (-1 !== CACHED.std.indexOf(route) )){

					if( that._windows.length ){
						that._windows.forEach( w => { w.send.apply(w, [_hashRouteSend( 'STD', route )].concat( JSON.stringify(args || []) )) }, that );
					}else if( ipc && ipc.send ){
						ipc.send.apply(ipc, [_hashRouteSend( 'STD', route )].concat( JSON.stringify(args || []) ));
					}

				}
				// Not registered
				if( that._routes.hasOwnProperty( route ) ){

					that._routes[ route ][ 'std' ].forEach(function( cb ){

						cb.apply( null, args || [] );

					})

				}

			}, that)

		});

	}

	// RECEIVER
	on(){

		var args = Array.prototype.slice.call(arguments, 0);
		if( 1 === args.length ){
			throw new Error('Bad arguments, MUST provide a route and a callback')
		}

		var route = args.shift();
		var endCb = args.pop();

		if( typeof endCb === 'function' ){

			this._setupRoute( route );
			this._routes[ route ][ 'std' ].push( endCb );

			if( ipc ){

				ipc.on( _hashRouteListen( 'STD', route ), function(){

					args = Array.prototype.slice.call(arguments, 1);
					endCb.apply( null, JSON.parse(args) );

				})

			}

		}

		this._sendCache();

	}

	// SENDER - Duplex
	route(){

		var args = Array.prototype.slice.call(arguments, 0);
		if( 3 >  args.length ){
			throw new Error('Bad arguments, MUST provide a route, a method and a callback')
		}
		var route = args.shift();
		var verbs = [args.shift().toUpperCase()];
		var params= [];

		var len = args.length-1;
		var i 	= 0;
		while( i++ < len ){
			params.push( args.shift() );
		}

		var cb = args.pop();

		// Re-Cache, just in case...
		this._requestCache();

		var that = this;
		setTimeout(function() {

			if( verbs[0] === '*' ){
				verbs = VERBS;
			}

			verbs.forEach(function( verb ){

				var fnArgs = [{
					method: verb,
					params: JSON.parse( JSON.stringify(params) )
				}];

				//console.log('Routing', verb, route, _hashRouteSend( verb, route ), fnArgs);

				if( ipc && (-1 !== CACHED.http[verb].indexOf(route) )){

					ipc.once(_hashRouteListen( verb, route, true ), function(){
						var args = Array.prototype.slice.call( arguments, 1 );
						cb( args.splice(0, 1)[0], JSON.parse( JSON.stringify( args ) ) );
					})
					//console.log('send', 'registered back on', _hashRouteListen( verb, route, true ));
					if( that._windows.length ){
						that._windows.forEach( w => { w.send.apply(w, [_hashRouteSend( verb, route )].concat( JSON.stringify(fnArgs || []) )) }, this );
					}else if( ipc && ipc.send ){
						ipc.send.apply(ipc, [_hashRouteSend( verb, route )].concat( JSON.stringify(fnArgs || []) ));
					}

				}
				if(
					that._routes.hasOwnProperty( route ) &&
					that._routes[ route ][ 'http' ][verb] &&
					typeof that._routes[ route ][ 'http' ][verb] === 'function'
				){

					fnArgs = fnArgs.concat({
						json: function( err, obj ){
							console.log('inner json route', err, obj, arguments);
							if( arguments.length === 1 ){
								obj = err;
								err = null;
							}
							cb( err, JSON.parse( JSON.stringify( obj ) ) );
						}
					})
					that._routes[ route ][ 'http' ][ verb ].apply( null, fnArgs );

				}// Not registered, skip on std router

			}, that);

		}, 10);

	}

	// RECEIVERS
	post(){

		this._common( arguments, 'POST' );

	}

	get(){

		this._common( arguments, 'GET' );

	}

	update(){

		this._common( arguments, 'UPDATE' );
	}

	delete(){

		this._common( arguments, 'DELETE' );

	}

}

//module.exports = Router;
var _router = null;
module.exports = function( name, window ){

	if( null === ipc ){
		try{

			ipc = require('electron').ipcMain;

			if( ipc !== undefined ){
				// We are on main process
				PROC_SIDE = PROC_SIDES.MAIN;
			}else{
				ipc = require('electron').ipcRenderer;
				PROC_SIDE = PROC_SIDES.RENDERER;
			}

		}catch( e ){
			// Non electron environment
		}
	}

	if( null === _router ){
		_router = new Router( name, window );
	}else if( undefined !== name || undefined !== window ){
		_router._name = _router._name || name;
		_router._setup( window );
	}
	return _router;
}
<|endoftext|>


<commit_before>/**
 * 前端路由
 *
 * @author 老雷<leizongmin@gmail.com>
 */

(function (window) {

  // 使用TomatoRouter命名空间
  var me = window.TomatoRouter = {};

  me.debug = function (msg) {
    console.log('[' + new Date().toLocaleTimeString() + '] ' + msg);
  };

  //----------------------------------------------------------------------------
  /**
   * 路由对象
   */
  function Router () {
    // 存储路由表
    this.staticTable = {};       // 静态转换表
    this.regexpTable = [];       // 正则转换表
  }

  /**
   * 注册路由
   *
   * @param {string|RegExp} path 路径
   * @param {function|object} handle 处理函数
   * @param {object} data 附加信息
   * @return {bool}
   */
  Router.prototype.add = function (path, handle, data) {
    data = data || {};
    var p = this.parse(path);
    if (p === null) return false;
    if (p.path instanceof RegExp) {
      this.regexpTable.push({
        path:   p.path,
        handle: handle,
        names:  p.names,
        info:   data
      });
    } else {
      this.staticTable[p.path] = {
        handle: handle,
        info:   data
      };
    }
    return true;
  };

  /**
   * 查询路由
   *
   * @param {string} url 请求的路径
   * @param {int} index 开始位置（仅对regexpTable有效）
   * @return {object} 包含 index, handle, value, info 失败返回null
   */
  Router.prototype.query = function (url, index) {
    // 先检查是否在 staticTable 中，如果没有在，再逐个判断 regexpTable
    var _static_url = this.staticTable[url];
    if (_static_url) {
      return {
        index:  -1,                    // 索引位置
        handle: _static_url.handle,    // 处理句柄
        value:  null,                  // PATH参数值
        info:   _static_url.info       // 附件信息
      };
    }

    if (isNaN(index)) index = 0;
    for (var i = index, n = this.regexpTable.length; i < n; i++) {
      // 查找符合的处理函数
      var r = this.regexpTable[i];
      // 清除lastIndex信息
      r.lastIndex = 0;
      // 测试正则
      var pv = r.path.exec(url);
      if (pv === null) continue;

      // 填充匹配的PATH值
      var ret = {
        index:  i,           // 索引位置
        handle: r.handle,    // 处理句柄
        value:  {},          // PATH参数值
        info:   r.info       // 附加信息
      };
      // 填充value
      if (r.names !== null) {
        var rnames = r.names;
        for (var j = 0, nlen = rnames.length; j < nlen; j++) {
          ret.value[rnames[j]] = pv[j + 1];
        }
      } else {
        // 如果是自定义的RegExp，则使用数字索引
        ret.value = pv.slice(1);
      }

      return ret;
    }

    // 没找到则返回null
    return null;
  };

  /**
   * 解析路径
   *
   * @param {string|RegExp} path 路径
   * @return {object} 包含 path, names
   */
  Router.prototype.parse = function (path) {
    // 如果是RegExp类型，则直接返回
    if (path instanceof RegExp) return {path: path, names: null};

    // 如果不是string类型，返回null
    if (typeof path != 'string') return null;

    // 如果没有包含:name类型的路径，则直接返回string路径，否则将其编译成RegExp
    path = path.trim();
    var names = path.match(/:[\w\d_$]+/g);
    if (names !== null) {
      // 编译path路径
      for (var i = 0; i < names.length; i++) {
        names[i] = names[i].substr(1);
      }
    }
    // 替换正则表达式
    var path = '^' + path.replace(/:[\w\d_$]+/g, '([^/]+)') + '$';

    return {
      path:  new RegExp(path),
      names: names
    };
  };

  /**
   * 删除路由
   *
   * @param {string|RegExp} path 注册时填写的路径
   * @return {bool}
   */
  Router.prototype.remove = function (path) {
    var p = this.parse(path);

    if (p === null) return false;

    var isReomve = false;
    // 从 regexpTable 表中查找
    if (p.path instanceof RegExp) {
      path = p.path.toString();
      for (var i = 0; i < this.regexpTable.length; i++) {
        if (this.regexpTable[i].path.toString() === path) {
          this.regexpTable.splice(i, 1);
          isReomve = true;
          i--;
        }
      }
    } else if (p.path in this.staticTable) {
      // 从 staticTable 表中查找
      delete this.staticTable[p.path];
      isReomve = true;
    }

    return isReomve;
  };

  //----------------------------------------------------------------------------
  // 解析URL参数
  function parseQueryString (qs) {
    var sep = '&';
    var eq = '=';
    var obj = {};
    if (typeof qs !== 'string' || qs.length === 0) return obj;
    var regexp = /\+/g;
    qs = qs.split(sep);
    for (var i = 0, len = qs.length; i < len; ++i) {
      var x = qs[i].replace(regexp, '%20');
      var idx = x.indexOf(eq);
      var kstr = x.substring(0, idx);
      var vstr = x.substring(idx + 1);
      try {
        var k = decodeURIComponent(kstr);
        var v = decodeURIComponent(vstr);
      } catch (e) {
        var k = unescape(kstr, true);
        var v = unescape(vstr, true);
      }
      obj[k] = v;
    }
    return obj;
  };

  // 解析URL
  function parseUrl (url) {
    var ret = {};
    var qm = url.indexOf('?');
    if (qm === -1) {
      ret.path = url;
      ret.query = {};
    } else {
      ret.path = url.substr(0, qm);
      ret.query = parseQueryString(url.substr(qm + 1));
    }
    return ret;
  };

  //----------------------------------------------------------------------------
  me.router = new Router();

  function startWithSlash (url) {
    return (url[0] === '/');
  }

  function fixUrl (url) {
    return startWithSlash(url) ? url : '/' + url;
  }

  // 注册全局控制器
  me.on = function (url, handle) {
    url = fixUrl(url);
    me.router.add(url, handle);
    me.debug('监听: ' + url);
  };

  // 开始检查
  me.check = function (url) {
    url = url || location.hash.substr(1);
    if (!startWithSlash(url)) {
      me.debug('不符合要求的hash: ' + url);
      return;
    }
    url = parseUrl(url);
    var ret = null;
    var i = 0;
    var c = 0;
    while (ret = me.router.query(url.path, i)) {
      c++;
      me.debug('@' + c + '转到: ' + url.path);
      var obj = new HashEventObject(url, ret);
      try {
        ret.handle(obj);
      } catch (err) {
        console.error(err.stack);
      }
      if (ret.index < 0) break;
      i = ret.index + 1;
    }
    if (c < 1) {
      me.debug('没有注册的路由: ' + url.path);
      // 自动跳转到首页
      if (url.path !== '/') me.redirect('/');
    }
  };

  // 跳转
  me.redirect = function (url) {
    me.debug('跳转: ' + url);
    location.hash = fixUrl(url);
  };

  // 初始化
  me.init = function () {
    me.debug('初始化');
    function onHashChange () {
      me.check();
    };
    // 监听hash变化
    if (typeof window.addEventListener === 'function') {
      window.addEventListener('hashchange', onHashChange);
    } else if ('onhashchange' in window) {
      me.debug('on IE8');
      window.onhashchange = onHashChange;
    } else {
      me.debug('浏览器不支持hashchange事件');
    }
    // 首次打开页面
    if (startWithSlash(location.hash)) {
      onHashChange();
    } else {
      me.debug('首次打开页面');
      me.redirect('/');
    }
  };

  //----------------------------------------------------------------------------
  // 输出操作对象
  function HashEventObject (url, info) {
    this.path = url.path;
    this.query = url.query;
    this.params = info.value;
  };
  HashEventObject.prototype.redirect = me.redirect;
  me.HashEventObject = HashEventObject;

})(window);<commit_msg>修正没有注册 / 时自动调整混乱<commit_after>/**
 * 前端路由
 *
 * @author 老雷<leizongmin@gmail.com>
 */

(function (window) {

  // 使用TomatoRouter命名空间
  var me = window.TomatoRouter = {};

  me.debug = function (msg) {
    console.log('[' + new Date().toLocaleTimeString() + '] ' + msg);
  };

  //----------------------------------------------------------------------------
  /**
   * 路由对象
   */
  function Router () {
    // 存储路由表
    this.staticTable = {};       // 静态转换表
    this.regexpTable = [];       // 正则转换表
  }

  /**
   * 注册路由
   *
   * @param {string|RegExp} path 路径
   * @param {function|object} handle 处理函数
   * @param {object} data 附加信息
   * @return {bool}
   */
  Router.prototype.add = function (path, handle, data) {
    data = data || {};
    var p = this.parse(path);
    if (p === null) return false;
    if (p.path instanceof RegExp) {
      this.regexpTable.push({
        path:   p.path,
        handle: handle,
        names:  p.names,
        info:   data
      });
    } else {
      this.staticTable[p.path] = {
        handle: handle,
        info:   data
      };
    }
    return true;
  };

  /**
   * 查询路由
   *
   * @param {string} url 请求的路径
   * @param {int} index 开始位置（仅对regexpTable有效）
   * @return {object} 包含 index, handle, value, info 失败返回null
   */
  Router.prototype.query = function (url, index) {
    // 先检查是否在 staticTable 中，如果没有在，再逐个判断 regexpTable
    var _static_url = this.staticTable[url];
    if (_static_url) {
      return {
        index:  -1,                    // 索引位置
        handle: _static_url.handle,    // 处理句柄
        value:  null,                  // PATH参数值
        info:   _static_url.info       // 附件信息
      };
    }

    if (isNaN(index)) index = 0;
    for (var i = index, n = this.regexpTable.length; i < n; i++) {
      // 查找符合的处理函数
      var r = this.regexpTable[i];
      // 清除lastIndex信息
      r.lastIndex = 0;
      // 测试正则
      var pv = r.path.exec(url);
      if (pv === null) continue;

      // 填充匹配的PATH值
      var ret = {
        index:  i,           // 索引位置
        handle: r.handle,    // 处理句柄
        value:  {},          // PATH参数值
        info:   r.info       // 附加信息
      };
      // 填充value
      if (r.names !== null) {
        var rnames = r.names;
        for (var j = 0, nlen = rnames.length; j < nlen; j++) {
          ret.value[rnames[j]] = pv[j + 1];
        }
      } else {
        // 如果是自定义的RegExp，则使用数字索引
        ret.value = pv.slice(1);
      }

      return ret;
    }

    // 没找到则返回null
    return null;
  };

  /**
   * 解析路径
   *
   * @param {string|RegExp} path 路径
   * @return {object} 包含 path, names
   */
  Router.prototype.parse = function (path) {
    // 如果是RegExp类型，则直接返回
    if (path instanceof RegExp) return {path: path, names: null};

    // 如果不是string类型，返回null
    if (typeof path != 'string') return null;

    // 如果没有包含:name类型的路径，则直接返回string路径，否则将其编译成RegExp
    path = path.trim();
    var names = path.match(/:[\w\d_$]+/g);
    if (names !== null) {
      // 编译path路径
      for (var i = 0; i < names.length; i++) {
        names[i] = names[i].substr(1);
      }
    }
    // 替换正则表达式
    var path = '^' + path.replace(/:[\w\d_$]+/g, '([^/]+)') + '$';

    return {
      path:  new RegExp(path),
      names: names
    };
  };

  /**
   * 删除路由
   *
   * @param {string|RegExp} path 注册时填写的路径
   * @return {bool}
   */
  Router.prototype.remove = function (path) {
    var p = this.parse(path);

    if (p === null) return false;

    var isReomve = false;
    // 从 regexpTable 表中查找
    if (p.path instanceof RegExp) {
      path = p.path.toString();
      for (var i = 0; i < this.regexpTable.length; i++) {
        if (this.regexpTable[i].path.toString() === path) {
          this.regexpTable.splice(i, 1);
          isReomve = true;
          i--;
        }
      }
    } else if (p.path in this.staticTable) {
      // 从 staticTable 表中查找
      delete this.staticTable[p.path];
      isReomve = true;
    }

    return isReomve;
  };

  //----------------------------------------------------------------------------
  // 解析URL参数
  function parseQueryString (qs) {
    var sep = '&';
    var eq = '=';
    var obj = {};
    if (typeof qs !== 'string' || qs.length === 0) return obj;
    var regexp = /\+/g;
    qs = qs.split(sep);
    for (var i = 0, len = qs.length; i < len; ++i) {
      var x = qs[i].replace(regexp, '%20');
      var idx = x.indexOf(eq);
      var kstr = x.substring(0, idx);
      var vstr = x.substring(idx + 1);
      try {
        var k = decodeURIComponent(kstr);
        var v = decodeURIComponent(vstr);
      } catch (e) {
        var k = unescape(kstr, true);
        var v = unescape(vstr, true);
      }
      obj[k] = v;
    }
    return obj;
  };

  // 解析URL
  function parseUrl (url) {
    var ret = {};
    var qm = url.indexOf('?');
    if (qm === -1) {
      ret.path = url;
      ret.query = {};
    } else {
      ret.path = url.substr(0, qm);
      ret.query = parseQueryString(url.substr(qm + 1));
    }
    return ret;
  };

  //----------------------------------------------------------------------------
  me.router = new Router();

  function startWithSlash (url) {
    return (url[0] === '/');
  }

  function fixUrl (url) {
    return startWithSlash(url) ? url : '/' + url;
  }

  // 注册全局控制器
  me.on = function (url, handle) {
    url = fixUrl(url);
    me.router.add(url, handle);
    me.debug('监听: ' + url);
  };

  // 开始检查
  me.check = function (url) {
    url = url || location.hash.substr(1);
    if (!startWithSlash(url)) {
      me.debug('不符合要求的hash: ' + url);
      return;
    }
    url = parseUrl(url);
    var ret = null;
    var i = 0;
    var c = 0;
    while (ret = me.router.query(url.path, i)) {
      c++;
      me.debug('@' + c + '转到: ' + url.path);
      var obj = new HashEventObject(url, ret);
      try {
        ret.handle(obj);
      } catch (err) {
        console.error(err.stack);
      }
      if (ret.index < 0) break;
      i = ret.index + 1;
    }
    if (c < 1) {
      me.debug('没有注册的路由: ' + url.path);
      // 如果是首次打开页面，则自动跳转到首页
      if (!me._hasRedirectHome) {
        me._hasRedirectHome = true;
        me.debug('首次打开页面，自动跳转到 /');
        if (url.path !== '/') me.redirect('/');
      }
    }
  };

  // 跳转
  me.redirect = function (url) {
    me.debug('跳转: ' + url);
    location.hash = fixUrl(url);
  };

  // 初始化
  me.init = function () {
    me.debug('初始化');
    function onHashChange () {
      me.check();
    };
    // 监听hash变化
    if (typeof window.addEventListener === 'function') {
      window.addEventListener('hashchange', onHashChange);
    } else if ('onhashchange' in window) {
      me.debug('on IE8');
      window.onhashchange = onHashChange;
    } else {
      me.debug('浏览器不支持hashchange事件');
    }
    // 首次打开页面
    if (startWithSlash(location.hash)) {
      onHashChange();
    } else {
      me.debug('首次打开页面');
      me.redirect('/');
      me._hasRedirectHome = true;
    }
    // 设置已初始化
    me.initialized = true;
  };

  //----------------------------------------------------------------------------
  // 输出操作对象
  function HashEventObject (url, info) {
    this.path = url.path;
    this.query = url.query;
    this.params = info.value;
  };
  HashEventObject.prototype.redirect = me.redirect;
  me.HashEventObject = HashEventObject;

})(window);<|endoftext|>


<commit_before>module.exports = function(app) {
  var GHProjects = require('./models/ghprojects')(app.get('bookshelf'));
  app.get('/', function (req, res) {
    new GHProjects().fetchAll().then(function (projects) {
        return res.render('index.hjs', { projects: projects.toJSON() });
      }
    );
	});
};
<commit_msg>Remove bookshelf from routes and add a post filter<commit_after>module.exports = function(app) {
  var knex = app.get('knex');
  app.get('/', function (req, res) {
    knex.select('*').from('ghprojects').then(function(projects) {
      return res.render('index.hjs', { projects: projects });
    });
  });
  app.post('/', function (req, res) {
    knex.select('*')
      .from('ghprojects')
      .where({ gh_language: req.body.language })
      .then(function (projects) {
        return res.render('index.hjs', {
          filter_lang: req.body.language,
          projects: projects
        });
      });
  });
};
<|endoftext|>