<commit_before>'use strict';
/* global CodeMirror */
/*eslint no-script-url:0 */

var React = require('react');
var ReactDOM = require('react-dom');
var TagTranslator = require('./tag-translator');
var _ = require('../../undash');
var cx = require('classnames');

var { keyCodes } = require('../../utils');

var toString = function (value) {
  if (_.isUndefined(value) || _.isNull(value)) {
    return '';
  }
  return String(value);
};

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.
 */
module.exports = React.createClass({

  displayName: 'PrettyTextInput',

  mixins: [require('../../mixins/helper')],

  componentDidMount: function() {
    this.createEditor();
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }
    this.updateEditor();
  },

  componentWillUnmount: function() {
    if (this.state.codeMirrorMode) {
      this.removeCodeMirrorEditor();
    }
  },

  getInitialState: function() {
    var selectedChoices = this.props.selectedChoices;
    var replaceChoices = this.props.replaceChoices;
    var translator = TagTranslator(selectedChoices.concat(replaceChoices), this.props.config.humanize);

    return {
      // With number values, onFocus never fires, which means it stays read-only. So convert to string.
      value: toString(this.props.value),
      codeMirrorMode: false,
      isChoicesOpen: false,
      replaceChoices: replaceChoices,
      translator: translator,
      hasChanged: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    // If we're debouncing a change, then we should just ignore this props change,
    // because there will be another when we hit the trailing edge of the debounce.
    if (this.isDebouncingCodeMirrorChange) {
      return;
    }

    var selectedChoices = nextProps.selectedChoices;
    var replaceChoices = nextProps.replaceChoices;
    var nextState = {
      replaceChoices: replaceChoices,
      translator: TagTranslator(selectedChoices.concat(replaceChoices), this.props.config.humanize)
    };

    // Not sure what the null/undefined checks are here for, but changed from falsey which was breaking.
    if (this.state.value !== nextProps.value && !_.isUndefined(nextProps.value) && nextProps.value !== null) {
      nextState.value = toString(nextProps.value);
      if (this.state.hasChanged === false) {
        nextState.hasChanged = true;
      }
    }

    this.setState(nextState);
  },

  onChange: function (newValue) {
    this.props.onChange(newValue);
  },

  handleChoiceSelection: function (key, event) {
    const selectChoice = () => {
      var pos = this.state.selectedTagPos;
      var tag = '{{' + key + '}}';

      this.isInserting = true;
      if (pos) {
        this.codeMirror.replaceRange(tag, {line: pos.line, ch: pos.start}, {line: pos.line, ch: pos.stop});
      } else {
        this.codeMirror.replaceSelection(tag, 'end');
      }
      this.isInserting = false;
      this.codeMirror.focus();

      this.setState({ isChoicesOpen: false, selectedTagPos: null });
    };
    if (this.state.codeMirrorMode) {
      selectChoice();
    } else if (this.props.readOnly) {
      // hackety hack to stop dropdown choices from toggling
      event.stopPropagation();
      this.isInserting = true;
      this.onChange('{{' + key + '}}');
      this.isInserting = false;
      this.setState({ isChoicesOpen: false });
    } else {
      this.switchToCodeMirror(selectChoice);
    }
  },

  onFocusWrapper: function () {
    this.switchToCodeMirror(() => {
      this.codeMirror.focus();
      this.codeMirror.setCursor(this.codeMirror.lineCount(), 0);
    }, () => {
      setTimeout(() => {
        if (this.codeMirror && !this.codeMirror.state.focused) {
          this.codeMirror.focus();
          this.codeMirror.setCursor(this.codeMirror.lineCount(), 0);
        }
      }, 0);
    });
  },

  focus() {
    if (this.codeMirror) {
      this.focusCodeMirror();
    } else {
      this.switchToCodeMirror(() => {
        this.focusCodeMirror();
      });
    }
  },

  focusCodeMirror() {
    if (this.codeMirror) {
      this.codeMirror.focus();
    }
  },

  onFocusCodeMirror() {
    this.setState({ hasFocus: true });
    this.props.onFocus();
  },

  onBlur() {
    if (this.isDebouncingCodeMirrorChange) {
      this.onChangeAndTagCodeMirror();
      this.isDebouncingCodeMirrorChange = false;
      this.setState({ hasFocus: false }, this.props.onBlur);
    } else {
      this.setState({ hasFocus: false });
      this.props.onBlur();
    }
  },

  insertBtn: function () {
    if (this.isReadOnly() && !this.hasReadOnlyControls()) {
      return null;
    }

    var onInsertClick = function () {
      this.setState({selectedTagPos: null});
      this.onToggleChoices();
    };

    const props = {
      ref: 'toggle',
      onClick: onInsertClick.bind(this),
      readOnly: this.isReadOnly(),
      field: this.props.field
    };
    return this.props.config.createElement('insert-button', props, 'Insert...');
  },

  choices: function () {
    if (this.isReadOnly()) {
      return null;
    }

    return this.props.config.createElement('choices', {
      ref: 'choices',
      onFocusSelect: this.focusCodeMirror,
      choices: this.state.replaceChoices,
      open: this.state.isChoicesOpen,
      ignoreCloseNodes: this.getCloseIgnoreNodes,
      onSelect: this.handleChoiceSelection,
      onClose: this.onCloseChoices,
      isAccordion: this.props.isAccordion,
      field: this.props.field,
      onChoiceAction: this.onChoiceAction
    });
  },

  onChoiceAction: function (choice) {
    this.setState({
      isChoicesOpen: !!choice.isOpen
    });
    this.onStartAction(choice.action, choice);
  },

  wrapperTabIndex() {
    if (this.props.readOnly || this.state.codeMirrorMode) {
      return null;
    }
    return this.props.tabIndex || 0;
  },

  onKeyDown(event) {
    if (!this.isReadOnly()) {
      if (event.keyCode === keyCodes.ESC) {
        event.preventDefault();
        event.stopPropagation();
        if (this.state.isChoicesOpen) {
          this.onToggleChoices();
          this.focusCodeMirror();
        }
      } else if (!this.state.isChoicesOpen) {
        // TODO: sane shortcut for opening choices
        // Below does not work yet. Ends up dumping { into search input.
        // if (this.codeMirror) {
        //   if (event.keyCode === keyCodes['['] && event.shiftKey) {
        //     const cursor = this.codeMirror.getCursor();
        //     const value = this.codeMirror.getValue();
        //     const lines = value.split('\n');
        //     const line = lines[cursor.line];
        //     if (line) {
        //       const prevChar = line[cursor.ch - 1];
        //       if (prevChar === '{') {
        //         this.onToggleChoices();
        //       }
        //     }
        //   }
        // }
      } else {
        if (this.refs.choices && this.refs.choices.onKeyDown) {
          this.refs.choices.onKeyDown(event);
        }
      }
    }
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var textBoxClasses = cx(_.extend({}, this.props.classes, {
      'pretty-text-box': true,
      placeholder: this.hasPlaceholder(),
      'has-focus': this.state.hasFocus
    }));

    // Render read-only version.
    return (

        {this.insertBtn()}
        {this.choices()}

    );
  },

  getCloseIgnoreNodes: function () {
    return this.refs.toggle;
  },

  onToggleChoices: function () {
    this.setChoicesOpen(!this.state.isChoicesOpen);
  },

  setChoicesOpen: function (isOpen) {
    var action = isOpen ? 'open-replacements' : 'close-replacements';
    this.onStartAction(action);
    this.setState({ isChoicesOpen: isOpen });
  },

  onCloseChoices: function () {
    if (this.state.isChoicesOpen) {
      this.setChoicesOpen(false);
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  updateEditor: function () {
    if (this.state.codeMirrorMode) {
      var codeMirrorValue = this.codeMirror.getValue();
      if (!this.hasPlaceholder() && codeMirrorValue !== this.state.value) {
        // switch back to read-only mode to make it easier to render
        this.removeCodeMirrorEditor();
        this.createReadonlyEditor();
        this.setState({
          codeMirrorMode: false
        });
      }
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var options = {
      tabindex: this.props.tabIndex || 0,
      lineWrapping: true,
      placeholder: toString(this.props.config.fieldPlaceholder(this.props.field)),
      value: toString(this.state.value),
      readOnly: false,
      mode: null,
      extraKeys: {
        Tab: false,
        'Shift-Tab': false
      }
    };

    var textBox = this.refs.textBox;
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    this.codeMirror = CodeMirror(textBox, options);
    this.codeMirror.on('change', this.onCodeMirrorChange);
    this.codeMirror.on('focus', this.onFocusCodeMirror);

    this.tagCodeMirror();
  },

  tagCodeMirror: function () {
    var positions = this.state.translator.getTagPositions(this.codeMirror.getValue());
    var self = this;

    var tagOps = function () {
      positions.forEach(function (pos) {
        var node = self.createTagNode(pos);
        self.codeMirror.markText({line: pos.line, ch: pos.start},
                                 {line: pos.line, ch: pos.stop},
                                 {replacedWith: node, handleMouseEvents: true});
      });
    };

    this.codeMirror.operation(tagOps);
  },

  onChangeAndTagCodeMirror() {
    if (!this.codeMirror) {
      // We might have erased our codemirror instance before hitting the trailing
      // end of the debounce. If so, get the value out of state.
      if (this.props.value !== this.state.value) {
        this.onChange(this.state.value);
      }
      return;
    }
    this.onChange(this.codeMirror.getValue());
    this.tagCodeMirror();
  },

  onCodeMirrorChange: function () {
    var newValue = this.codeMirror.getValue();
    this.setState({value: newValue});

    // Immediately change and tag if inserting.
    if (this.isInserting) {
      this.onChangeAndTagCodeMirror();
      return;
    }

    // Otherwise, debounce so CodeMirror doesn't die.
    if (!this.debounceCodeMirrorChange) {
      this.debounceCodeMirrorChange = _.debounce(() => {
        if (this.isDebouncingCodeMirrorChange) {
          this.isDebouncingCodeMirrorChange = false;
          this.onChangeAndTagCodeMirror();
        }
      }, 200);
    }
    this.isDebouncingCodeMirrorChange = true;
    this.debounceCodeMirrorChange();
  },

  /* Return true if we should render the placeholder */
  hasPlaceholder: function () {
    return !this.state.value;
  },

  createReadonlyEditor: function () {
    var textBoxNode = this.refs.textBox;

    if (this.hasPlaceholder()) {
      return ReactDOM.render({this.props.field.placeholder}, textBoxNode);
    }

    var tokens = this.props.config.tokenize(this.state.value);
    var self = this;
    var nodes = tokens.map(function (part, i) {
      if (part.type === 'tag') {
        var label = self.state.translator.getLabel(part.value);
        var props = {key: i, tag: part.value, replaceChoices: self.state.replaceChoices, field: self.props.field};
        return self.props.config.createElement('pretty-tag', props, label);
      }
      return {part.value};
    });

    ReactDOM.render({nodes}, textBoxNode);
  },

  removeCodeMirrorEditor: function () {
    var textBoxNode = this.refs.textBox;
    var cmNode = textBoxNode.firstChild;
    textBoxNode.removeChild(cmNode);
    this.codeMirror = null;
  },

  switchToCodeMirror: function (cb, alreadySwitchedCb) {
    if (this.isReadOnly()) {
      return; // never render in code mirror if read-only
    }

    if (!this.props.readOnly) {
      if (!this.state.codeMirrorMode) {
        this.setState({codeMirrorMode: true}, () => {
          if (this.codeMirror && _.isFunction(cb)) {
            cb();
          }
        });
      } else {
        if (_.isFunction(alreadySwitchedCb)) {
          alreadySwitchedCb();
        }
      }
    }
  },

  onTagClick: function () {
    const cursor = this.codeMirror.getCursor();
    const pos = this.state.translator.getTrueTagPosition(this.state.value, cursor);

    this.setState({selectedTagPos: pos});
    this.onToggleChoices();
  },

  createTagNode: function (pos) {
    var node = document.createElement('span');
    var label = this.state.translator.getLabel(pos.tag);
    var config = this.props.config;

    var props = {onClick: this.onTagClick, field: this.props.field, tag: pos.tag};

    ReactDOM.render(
      config.createElement('pretty-tag', props, label),
      node
    );

    return node;
  }
});
<commit_msg>only force focus when click to focus<commit_after>'use strict';
/* global CodeMirror */
/*eslint no-script-url:0 */

var React = require('react');
var ReactDOM = require('react-dom');
var TagTranslator = require('./tag-translator');
var _ = require('../../undash');
var cx = require('classnames');

var { keyCodes } = require('../../utils');

var toString = function (value) {
  if (_.isUndefined(value) || _.isNull(value)) {
    return '';
  }
  return String(value);
};

/*
   Editor for tagged text. Renders text like "hello {{firstName}}"
   with replacement labels rendered in a pill box. Designed to load
   quickly when many separate instances of it are on the same
   page.

   Uses CodeMirror to edit text. To save memory the CodeMirror node is
   instantiated when the user moves the mouse into the edit area.
   Initially a read-only view using a simple div is shown.
 */
module.exports = React.createClass({

  displayName: 'PrettyTextInput',

  mixins: [require('../../mixins/helper')],

  componentDidMount: function() {
    this.createEditor();
  },

  componentDidUpdate: function(prevProps, prevState) {
    if (prevState.codeMirrorMode !== this.state.codeMirrorMode) {
      // Changed from code mirror mode to read only mode or vice versa,
      // so setup the other editor.
      this.createEditor();
    }
    this.updateEditor();
  },

  componentWillUnmount: function() {
    if (this.state.codeMirrorMode) {
      this.removeCodeMirrorEditor();
    }
  },

  getInitialState: function() {
    var selectedChoices = this.props.selectedChoices;
    var replaceChoices = this.props.replaceChoices;
    var translator = TagTranslator(selectedChoices.concat(replaceChoices), this.props.config.humanize);

    return {
      // With number values, onFocus never fires, which means it stays read-only. So convert to string.
      value: toString(this.props.value),
      codeMirrorMode: false,
      isChoicesOpen: false,
      replaceChoices: replaceChoices,
      translator: translator,
      hasChanged: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    // If we're debouncing a change, then we should just ignore this props change,
    // because there will be another when we hit the trailing edge of the debounce.
    if (this.isDebouncingCodeMirrorChange) {
      return;
    }

    var selectedChoices = nextProps.selectedChoices;
    var replaceChoices = nextProps.replaceChoices;
    var nextState = {
      replaceChoices: replaceChoices,
      translator: TagTranslator(selectedChoices.concat(replaceChoices), this.props.config.humanize)
    };

    // Not sure what the null/undefined checks are here for, but changed from falsey which was breaking.
    if (this.state.value !== nextProps.value && !_.isUndefined(nextProps.value) && nextProps.value !== null) {
      nextState.value = toString(nextProps.value);
      if (this.state.hasChanged === false) {
        nextState.hasChanged = true;
      }
    }

    this.setState(nextState);
  },

  onChange: function (newValue) {
    this.props.onChange(newValue);
  },

  handleChoiceSelection: function (key, event) {
    const selectChoice = () => {
      var pos = this.state.selectedTagPos;
      var tag = '{{' + key + '}}';

      this.isInserting = true;
      if (pos) {
        this.codeMirror.replaceRange(tag, {line: pos.line, ch: pos.start}, {line: pos.line, ch: pos.stop});
      } else {
        this.codeMirror.replaceSelection(tag, 'end');
      }
      this.isInserting = false;
      this.codeMirror.focus();

      this.setState({ isChoicesOpen: false, selectedTagPos: null });
    };
    if (this.state.codeMirrorMode) {
      selectChoice();
    } else if (this.props.readOnly) {
      // hackety hack to stop dropdown choices from toggling
      event.stopPropagation();
      this.isInserting = true;
      this.onChange('{{' + key + '}}');
      this.isInserting = false;
      this.setState({ isChoicesOpen: false });
    } else {
      this.switchToCodeMirror(selectChoice);
    }
  },

  onFocusClick: function () {
    this.onFocusWrapper(true);
  },

  onFocusWrapper: function (isFromClick) {
    this.switchToCodeMirror(() => {
      this.codeMirror.focus();
      this.codeMirror.setCursor(this.codeMirror.lineCount(), 0);
    }, () => {
      // If this is from a click, we lose focus, so force it focused!
      if (isFromClick) {
        setTimeout(() => {
          if (this.codeMirror && !this.codeMirror.state.focused) {
            this.codeMirror.focus();
            this.codeMirror.setCursor(this.codeMirror.lineCount(), 0);
          }
        }, 0);
      }
    });
  },

  focus() {
    if (this.codeMirror) {
      this.focusCodeMirror();
    } else {
      this.switchToCodeMirror(() => {
        this.focusCodeMirror();
      });
    }
  },

  focusCodeMirror() {
    if (this.codeMirror) {
      this.codeMirror.focus();
    }
  },

  onFocusCodeMirror() {
    this.setState({ hasFocus: true });
    this.props.onFocus();
  },

  onBlur() {
    if (this.isDebouncingCodeMirrorChange) {
      this.onChangeAndTagCodeMirror();
      this.isDebouncingCodeMirrorChange = false;
      this.setState({ hasFocus: false }, this.props.onBlur);
    } else {
      this.setState({ hasFocus: false });
      this.props.onBlur();
    }
  },

  insertBtn: function () {
    if (this.isReadOnly() && !this.hasReadOnlyControls()) {
      return null;
    }

    var onInsertClick = function () {
      this.setState({selectedTagPos: null});
      this.onToggleChoices();
    };

    const props = {
      ref: 'toggle',
      onClick: onInsertClick.bind(this),
      readOnly: this.isReadOnly(),
      field: this.props.field
    };
    return this.props.config.createElement('insert-button', props, 'Insert...');
  },

  choices: function () {
    if (this.isReadOnly()) {
      return null;
    }

    return this.props.config.createElement('choices', {
      ref: 'choices',
      onFocusSelect: this.focusCodeMirror,
      choices: this.state.replaceChoices,
      open: this.state.isChoicesOpen,
      ignoreCloseNodes: this.getCloseIgnoreNodes,
      onSelect: this.handleChoiceSelection,
      onClose: this.onCloseChoices,
      isAccordion: this.props.isAccordion,
      field: this.props.field,
      onChoiceAction: this.onChoiceAction
    });
  },

  onChoiceAction: function (choice) {
    this.setState({
      isChoicesOpen: !!choice.isOpen
    });
    this.onStartAction(choice.action, choice);
  },

  wrapperTabIndex() {
    if (this.props.readOnly || this.state.codeMirrorMode) {
      return null;
    }
    return this.props.tabIndex || 0;
  },

  onKeyDown(event) {
    if (!this.isReadOnly()) {
      if (event.keyCode === keyCodes.ESC) {
        event.preventDefault();
        event.stopPropagation();
        if (this.state.isChoicesOpen) {
          this.onToggleChoices();
          this.focusCodeMirror();
        }
      } else if (!this.state.isChoicesOpen) {
        // TODO: sane shortcut for opening choices
        // Below does not work yet. Ends up dumping { into search input.
        // if (this.codeMirror) {
        //   if (event.keyCode === keyCodes['['] && event.shiftKey) {
        //     const cursor = this.codeMirror.getCursor();
        //     const value = this.codeMirror.getValue();
        //     const lines = value.split('\n');
        //     const line = lines[cursor.line];
        //     if (line) {
        //       const prevChar = line[cursor.ch - 1];
        //       if (prevChar === '{') {
        //         this.onToggleChoices();
        //       }
        //     }
        //   }
        // }
      } else {
        if (this.refs.choices && this.refs.choices.onKeyDown) {
          this.refs.choices.onKeyDown(event);
        }
      }
    }
  },

  render: function() {
    return this.renderWithConfig();
  },

  renderDefault: function () {
    var textBoxClasses = cx(_.extend({}, this.props.classes, {
      'pretty-text-box': true,
      placeholder: this.hasPlaceholder(),
      'has-focus': this.state.hasFocus
    }));

    // Render read-only version.
    return (

        {this.insertBtn()}
        {this.choices()}

    );
  },

  getCloseIgnoreNodes: function () {
    return this.refs.toggle;
  },

  onToggleChoices: function () {
    this.setChoicesOpen(!this.state.isChoicesOpen);
  },

  setChoicesOpen: function (isOpen) {
    var action = isOpen ? 'open-replacements' : 'close-replacements';
    this.onStartAction(action);
    this.setState({ isChoicesOpen: isOpen });
  },

  onCloseChoices: function () {
    if (this.state.isChoicesOpen) {
      this.setChoicesOpen(false);
    }
  },

  createEditor: function () {
    if (this.state.codeMirrorMode) {
      this.createCodeMirrorEditor();
    } else {
      this.createReadonlyEditor();
    }
  },

  updateEditor: function () {
    if (this.state.codeMirrorMode) {
      var codeMirrorValue = this.codeMirror.getValue();
      if (!this.hasPlaceholder() && codeMirrorValue !== this.state.value) {
        // switch back to read-only mode to make it easier to render
        this.removeCodeMirrorEditor();
        this.createReadonlyEditor();
        this.setState({
          codeMirrorMode: false
        });
      }
    } else {
      this.createReadonlyEditor();
    }
  },

  createCodeMirrorEditor: function () {
    var options = {
      tabindex: this.props.tabIndex || 0,
      lineWrapping: true,
      placeholder: toString(this.props.config.fieldPlaceholder(this.props.field)),
      value: toString(this.state.value),
      readOnly: false,
      mode: null,
      extraKeys: {
        Tab: false,
        'Shift-Tab': false
      }
    };

    var textBox = this.refs.textBox;
    textBox.innerHTML = ''; // release any previous read-only content so it can be GC'ed

    this.codeMirror = CodeMirror(textBox, options);
    this.codeMirror.on('change', this.onCodeMirrorChange);
    this.codeMirror.on('focus', this.onFocusCodeMirror);

    this.tagCodeMirror();
  },

  tagCodeMirror: function () {
    var positions = this.state.translator.getTagPositions(this.codeMirror.getValue());
    var self = this;

    var tagOps = function () {
      positions.forEach(function (pos) {
        var node = self.createTagNode(pos);
        self.codeMirror.markText({line: pos.line, ch: pos.start},
                                 {line: pos.line, ch: pos.stop},
                                 {replacedWith: node, handleMouseEvents: true});
      });
    };

    this.codeMirror.operation(tagOps);
  },

  onChangeAndTagCodeMirror() {
    if (!this.codeMirror) {
      // We might have erased our codemirror instance before hitting the trailing
      // end of the debounce. If so, get the value out of state.
      if (this.props.value !== this.state.value) {
        this.onChange(this.state.value);
      }
      return;
    }
    this.onChange(this.codeMirror.getValue());
    this.tagCodeMirror();
  },

  onCodeMirrorChange: function () {
    var newValue = this.codeMirror.getValue();
    this.setState({value: newValue});

    // Immediately change and tag if inserting.
    if (this.isInserting) {
      this.onChangeAndTagCodeMirror();
      return;
    }

    // Otherwise, debounce so CodeMirror doesn't die.
    if (!this.debounceCodeMirrorChange) {
      this.debounceCodeMirrorChange = _.debounce(() => {
        if (this.isDebouncingCodeMirrorChange) {
          this.isDebouncingCodeMirrorChange = false;
          this.onChangeAndTagCodeMirror();
        }
      }, 200);
    }
    this.isDebouncingCodeMirrorChange = true;
    this.debounceCodeMirrorChange();
  },

  /* Return true if we should render the placeholder */
  hasPlaceholder: function () {
    return !this.state.value;
  },

  createReadonlyEditor: function () {
    var textBoxNode = this.refs.textBox;

    if (this.hasPlaceholder()) {
      return ReactDOM.render({this.props.field.placeholder}, textBoxNode);
    }

    var tokens = this.props.config.tokenize(this.state.value);
    var self = this;
    var nodes = tokens.map(function (part, i) {
      if (part.type === 'tag') {
        var label = self.state.translator.getLabel(part.value);
        var props = {key: i, tag: part.value, replaceChoices: self.state.replaceChoices, field: self.props.field};
        return self.props.config.createElement('pretty-tag', props, label);
      }
      return {part.value};
    });

    ReactDOM.render({nodes}, textBoxNode);
  },

  removeCodeMirrorEditor: function () {
    var textBoxNode = this.refs.textBox;
    var cmNode = textBoxNode.firstChild;
    textBoxNode.removeChild(cmNode);
    this.codeMirror = null;
  },

  switchToCodeMirror: function (cb, alreadySwitchedCb) {
    if (this.isReadOnly()) {
      return; // never render in code mirror if read-only
    }

    if (!this.props.readOnly) {
      if (!this.state.codeMirrorMode) {
        this.setState({codeMirrorMode: true}, () => {
          if (this.codeMirror && _.isFunction(cb)) {
            cb();
          }
        });
      } else {
        if (_.isFunction(alreadySwitchedCb)) {
          alreadySwitchedCb();
        }
      }
    }
  },

  onTagClick: function () {
    const cursor = this.codeMirror.getCursor();
    const pos = this.state.translator.getTrueTagPosition(this.state.value, cursor);

    this.setState({selectedTagPos: pos});
    this.onToggleChoices();
  },

  createTagNode: function (pos) {
    var node = document.createElement('span');
    var label = this.state.translator.getLabel(pos.tag);
    var config = this.props.config;

    var props = {onClick: this.onTagClick, field: this.props.field, tag: pos.tag};

    ReactDOM.render(
      config.createElement('pretty-tag', props, label),
      node
    );

    return node;
  }
});
<|endoftext|>


<commit_before>/**
 * mobileapp provides page content for the Mobile Apps.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easy to consume by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var mwapi = require('../lib/mwapi');
var mUtil = require('../lib/mobile-util');
var parse = require('../lib/parseProperty');
var parsoid = require('../lib/parsoid-access');
var sUtil = require('../lib/util');
var transforms = require('../lib/transforms');

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageContentForMainPagePromise(req) {
    return mwapi.getAllSections(app, req)
    .then(function (response) {
        var page = response.body.mobileview;
        var sections = page.sections;
        var section;

        // transform all sections
        for (var idx = 0; idx < sections.length; idx++) {
            section = sections[idx];
            section.text = transforms.runMainPageDomTransforms(section.text);
        }

        page.sections = sections;
        return page;
    });
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageMetadataPromise(req) {
    return mwapi.getMetadata(app, req)
    .then(function (response) {
        return response.body.mobileview;
    });
}

function buildLeadSections(sections) {
    var out = [],
        section,
        len = sections.length;

    out.push(sections[0]);
    for (var i = 1; i < len; i++) {
        section = sections[i];
        var item = {
            id: section.id,
            toclevel: section.toclevel,
            anchor: section.anchor,
            line: section.line
        };
        out.push(item);
    }
    return out;
}

function sanitizeEmptyProtection(protection) {
    if (Array.isArray(protection)) {
        return undefined; // MediaWiki API returns an empty array instead of an empty object, ouch!
    }
    return protection;
}

function buildLead(input) {
    var lead = domino.createDocument(input.page.sections[0].text);
    return {
        id: input.meta.id,
        revision: input.page.revision,
        lastmodified: input.page.lastmodified,
        displaytitle: input.meta.displaytitle,
        normalizedtitle: input.meta.normalizedtitle,
        redirected: input.meta.redirected,
        description: input.meta.description,
        protection: sanitizeEmptyProtection(input.meta.protection),
        editable: input.meta.editable,
        mainpage: input.meta.mainpage,
        languagecount: input.meta.languagecount,
        image: mUtil.defaultVal(mUtil.filterEmpty({
            file: input.meta.image && input.meta.image.file,
            urls: input.meta.thumb && mwapi.buildLeadImageUrls(input.meta.thumb.url)
        })),
        pronunciation: parse.parsePronunciation(lead, input.meta.displaytitle),
        spoken: input.page.spoken,
        geo: input.page.geo,
        sections: buildLeadSections(input.page.sections)
    };
}

function buildRemaining(input) {
    return {
        sections: input.page.sections.slice(1) // don't repeat the first section
    };
}

function buildAll(input) {
    return {
        lead: buildLead(input),
        remaining: buildRemaining(input)
    };
}

/**
 * For main page only, switch to mobileview content because Parsoid doesn't
 * provide a good mobile presentation of main pages.
 */
function mainPageFixPromise(req, response) {
    return pageContentForMainPagePromise(req)
    .then(function (mainPageContent) {
        return {
            page: mainPageContent,
            meta: response.meta,
            extract: response.extract
        };
    });
}

/**
 * GET {domain}/v1/page/mobile-sections/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/mobile-sections/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req),
        meta: pageMetadataPromise(req)
    }).then(function (response) {
        if (response.meta.mainpage) {
            return mainPageFixPromise(req, response);
        }
        return response;
    }).then(function (response) {
        response = buildAll(response);
        res.status(200);
        mUtil.setETag(req, res, response.lead.revision);
        res.json(response).end();
    });
});

/**
 * GET {domain}/v1/page/mobile-sections-lead/{title}
 * Gets the lead section for the mobile app version of a given wiki page.
 */
router.get('/mobile-sections-lead/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req),
        meta: pageMetadataPromise(req),
        extract: mwapi.requestExtract(app, req)
    }).then(function (response) {
        if (response.meta.mainpage) {
            return mainPageFixPromise(req, response);
        }
        return response;
    }).then(function (response) {
        response = buildLead(response);
        res.status(200);
        mUtil.setETag(req, res, response.revision);
        res.json(response).end();
    });
});

/**
 * GET {domain}/v1/page/mobile-sections-remaining/{title}
 * Gets the remaining sections for the mobile app version of a given wiki page.
 */
router.get('/mobile-sections-remaining/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req)
    }).then(function (response) {
        res.status(200);
        mUtil.setETag(req, res, response.page.revision);
        res.json(buildRemaining(response)).end();
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/page',
        api_version: 1,
        router: router
    };
};
<commit_msg>Remove sanitizeEmptyProtection<commit_after>/**
 * mobileapp provides page content for the Mobile Apps.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easy to consume by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var mwapi = require('../lib/mwapi');
var mUtil = require('../lib/mobile-util');
var parse = require('../lib/parseProperty');
var parsoid = require('../lib/parsoid-access');
var sUtil = require('../lib/util');
var transforms = require('../lib/transforms');

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageContentForMainPagePromise(req) {
    return mwapi.getAllSections(app, req)
    .then(function (response) {
        var page = response.body.mobileview;
        var sections = page.sections;
        var section;

        // transform all sections
        for (var idx = 0; idx < sections.length; idx++) {
            section = sections[idx];
            section.text = transforms.runMainPageDomTransforms(section.text);
        }

        page.sections = sections;
        return page;
    });
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageMetadataPromise(req) {
    return mwapi.getMetadata(app, req)
    .then(function (response) {
        return response.body.mobileview;
    });
}

function buildLeadSections(sections) {
    var out = [],
        section,
        len = sections.length;

    out.push(sections[0]);
    for (var i = 1; i < len; i++) {
        section = sections[i];
        var item = {
            id: section.id,
            toclevel: section.toclevel,
            anchor: section.anchor,
            line: section.line
        };
        out.push(item);
    }
    return out;
}

function buildLead(input) {
    var lead = domino.createDocument(input.page.sections[0].text);
    return {
        id: input.meta.id,
        revision: input.page.revision,
        lastmodified: input.page.lastmodified,
        displaytitle: input.meta.displaytitle,
        normalizedtitle: input.meta.normalizedtitle,
        redirected: input.meta.redirected,
        description: input.meta.description,
        protection: input.meta.protection,
        editable: input.meta.editable,
        mainpage: input.meta.mainpage,
        languagecount: input.meta.languagecount,
        image: mUtil.defaultVal(mUtil.filterEmpty({
            file: input.meta.image && input.meta.image.file,
            urls: input.meta.thumb && mwapi.buildLeadImageUrls(input.meta.thumb.url)
        })),
        pronunciation: parse.parsePronunciation(lead, input.meta.displaytitle),
        spoken: input.page.spoken,
        geo: input.page.geo,
        sections: buildLeadSections(input.page.sections)
    };
}

function buildRemaining(input) {
    return {
        sections: input.page.sections.slice(1) // don't repeat the first section
    };
}

function buildAll(input) {
    return {
        lead: buildLead(input),
        remaining: buildRemaining(input)
    };
}

/**
 * For main page only, switch to mobileview content because Parsoid doesn't
 * provide a good mobile presentation of main pages.
 */
function mainPageFixPromise(req, response) {
    return pageContentForMainPagePromise(req)
    .then(function (mainPageContent) {
        return {
            page: mainPageContent,
            meta: response.meta,
            extract: response.extract
        };
    });
}

/**
 * GET {domain}/v1/page/mobile-sections/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/mobile-sections/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req),
        meta: pageMetadataPromise(req)
    }).then(function (response) {
        if (response.meta.mainpage) {
            return mainPageFixPromise(req, response);
        }
        return response;
    }).then(function (response) {
        response = buildAll(response);
        res.status(200);
        mUtil.setETag(req, res, response.lead.revision);
        res.json(response).end();
    });
});

/**
 * GET {domain}/v1/page/mobile-sections-lead/{title}
 * Gets the lead section for the mobile app version of a given wiki page.
 */
router.get('/mobile-sections-lead/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req),
        meta: pageMetadataPromise(req),
        extract: mwapi.requestExtract(app, req)
    }).then(function (response) {
        if (response.meta.mainpage) {
            return mainPageFixPromise(req, response);
        }
        return response;
    }).then(function (response) {
        response = buildLead(response);
        res.status(200);
        mUtil.setETag(req, res, response.revision);
        res.json(response).end();
    });
});

/**
 * GET {domain}/v1/page/mobile-sections-remaining/{title}
 * Gets the remaining sections for the mobile app version of a given wiki page.
 */
router.get('/mobile-sections-remaining/:title/:revision?', function (req, res) {
    return BBPromise.props({
        page: parsoid.pageContentPromise(app, req)
    }).then(function (response) {
        res.status(200);
        mUtil.setETag(req, res, response.page.revision);
        res.json(buildRemaining(response)).end();
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/page',
        api_version: 1,
        router: router
    };
};
<|endoftext|>


<commit_before>class Animation {
    constructor (options) {
        if (!(this instanceof Animation)) {
            return new Animation(options);
        }

        // state
        this.canceled = false;
        this.completed = false;
        this.paused = false;
        this.autonomous = false;

        // timing
        this.startTime = null;

        for (let key of Object.keys(options)) {
            this[key] = options[key];
        }

        if (this.autonomous) {
            this.rafId = null;
        }

        this.start();
    }

    cancelRAF() {
        caf(this.rafId);
        this.rafId = null;
    }

    start () {
        if (this.completed || this.canceled) {
            return this;
        }

        if (this.autonomous) {
            // prevent launching multiple rafs
            this.cancelRAF();
            this.rafId = raf(this.cycle.bind(this));
        }

        this.paused = false;
        return this;
    }

    pause () {
        // prevent state change after animation completes
        if (this.completed || this.canceled) {
            return this;
        }

        if (this.autonomous) {
            this.cancelRAF();
        }

        this.paused = true;
        return this;
    }

    cancel () {
        this.canceled = true;
        this.complete();
        return this;
    }

    complete () {
        if (this.autonomous) {
            this.cancelRAF();
        }

        this.completed = true;
        this._oncomplete();
        return this;
    }

    updateState (now) {
        var previousState = this.previousState;

        // initialize state data and 'previous' state data
        if (!previousState) {
            this.startTime = now;

            previousState = {};
            previousState.now = now;
            previousState.lastNow = now;
            previousState.runningTime = 0;
            previousState.deltaT = 0;
            previousState.progress = 0;

            this.state = this.previousState = previousState;
            return;
        }

        // store previous state data
        previousState = this.previousState = this.state;

        // create new state data based on the previous state values
        this.state = {
            now,
            lastNow: previousState.now,
            deltaT: now - previousState.now,
            // use previous values for these two because if the animation is paused,
            // or there is too much of a delay between frames (this.pauseThreshold),
            // the elapsed time between frames should not accumulate here.
            runningTime: previousState.runningTime,
            progress: previousState.progress,
        };
    }

    cycle (now) {
        // make sure this method isn't accidentally called
        if (!this.autonomous) {
            return;
        }

        // must do this first
        this.updateState(now);

        var args = this.args || [];
        // cache for lookups
        var state = this.state;

        // set up another frame
        this.rafId = raf(this.cycle.bind(this));

        // only run animation when the time between frames is short enough.
        // the gap can be wide if the tab becomes inactive, app is switched, etc.
        // ref: hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
        //
        // if paused, we're now resuming since cycle() is called only by start() and itself.
        // we need to burn one cycle in order to get the deltaT's small enough to animate smoothly.
        if (this.paused || this.pauseThreshold && state.deltaT >= this.pauseThreshold) {
            this.paused = false;
            return;
        }

        // now that we know that the animation will run, update the runningTime and progress values
        state.runningTime += state.deltaT;
        if (this.duration) {
            state.progress = Math.max(0, Math.min(1, state.runningTime / this.duration));
        }

        if (is(this.before, 'Function')) {
            this.before(...args);
        }

        // do not call render() if the animation was paused or canceled in before()
        if (this.paused || this.canceled) {
            return;
        }

        this.render(...args);

        // do not call done() if the animation was paused or canceled in render()
        if (this.paused || this.canceled) {
            return;
        }

        if (state.progress === 1) {
            if (is(this.done, 'Function')) {
                this.done(...args);
            }

            this.complete();
        }
    }
}
<commit_msg>Use Animation#resume() to take care of smoothing deltaT after pause<commit_after>class Animation {
    constructor (options) {
        if (!(this instanceof Animation)) {
            return new Animation(options);
        }

        // state
        this.canceled = false;
        this.completed = false;
        this.paused = false;
        this.autonomous = false;

        // timing
        this.startTime = null;

        for (let key of Object.keys(options)) {
            this[key] = options[key];
        }

        if (this.autonomous) {
            this.rafId = null;
        }

        this.start();
    }

    cancelRAF() {
        caf(this.rafId);
        this.rafId = null;
    }

    start () {
        if (this.completed || this.canceled) {
            return this;
        }

        if (this.autonomous) {
            // prevent launching multiple rafs
            this.cancelRAF();
            this.rafId = raf(this.resume.bind(this));
        }

        this.paused = false;
        return this;
    }

    // use one frame to get deltaT's small enough to animate smoothly
    resume (now) {
        this.updateState(now);
        this.rafId = raf(this.cycle.bind(this));
    }

    pause () {
        // prevent state change after animation completes
        if (this.completed || this.canceled) {
            return this;
        }

        if (this.autonomous) {
            this.cancelRAF();
        }

        this.paused = true;
        return this;
    }

    cancel () {
        this.canceled = true;
        this.complete();
        return this;
    }

    complete () {
        if (this.autonomous) {
            this.cancelRAF();
        }

        this.completed = true;
        this._oncomplete();
        return this;
    }

    updateState (now) {
        var previousState = this.previousState;

        // initialize state data and 'previous' state data
        if (!previousState) {
            this.startTime = now;

            previousState = {};
            previousState.now = now;
            previousState.lastNow = now;
            previousState.runningTime = 0;
            previousState.deltaT = 0;
            previousState.progress = 0;

            this.state = this.previousState = previousState;
            return;
        }

        // store previous state data
        previousState = this.previousState = this.state;

        // create new state data based on the previous state values
        this.state = {
            now,
            lastNow: previousState.now,
            deltaT: now - previousState.now,
            // use previous values for these two because if the animation is paused,
            // or there is too much of a delay between frames (this.pauseThreshold),
            // the elapsed time between frames should not accumulate here.
            runningTime: previousState.runningTime,
            progress: previousState.progress,
        };
    }

    cycle (now) {
        // make sure this method isn't accidentally called
        if (!this.autonomous) {
            return;
        }

        // must do this first
        this.updateState(now);

        var args = this.args || [];
        // cache for lookups
        var state = this.state;

        // set up another frame
        this.rafId = raf(this.cycle.bind(this));

        // only run animation when the time between frames is short enough.
        // the gap can be wide if the tab becomes inactive, app is switched, etc.
        // ref: hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
        if (this.pauseThreshold && state.deltaT >= this.pauseThreshold) {
            return;
        }

        // now that we know that the animation will run, update the runningTime and progress values
        state.runningTime += state.deltaT;
        if (this.duration) {
            state.progress = Math.max(0, Math.min(1, state.runningTime / this.duration));
        }

        if (is(this.before, 'Function')) {
            this.before(...args);
        }

        // do not call render() if the animation was paused or canceled in before()
        if (this.paused || this.canceled) {
            return;
        }

        this.render(...args);

        // do not call done() if the animation was paused or canceled in render()
        if (this.paused || this.canceled) {
            return;
        }

        if (state.progress === 1) {
            if (is(this.done, 'Function')) {
                this.done(...args);
            }

            this.complete();
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Immediately send received items to target<commit_after><|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient,
    EventEmitter = new(require('events').EventEmitter)(),
    Logger = require('logb').getLogger(module.filename);

var DATABASE = {},
    database2IsInstantiatingAConnection = {},
    done = false

var _connect, _getConnectionUrl, closeDatabase;

var getUrlFromOptions = function(options) {
        var options = options || {};
        // console.log('options',options);
        if (typeof options.url !== 'undefined') {
            return options.url
        }
        return _getConnectionUrl(options)
    }
    /***
     */
module.exports.getDb = function(options, cb) {
    // console.log('options',options);
    var url = getUrlFromOptions(options);

    if (typeof DATABASE[url] !== 'undefined') {
        return cb(null, DATABASE[url])
    }
    if (typeof database2IsInstantiatingAConnection[url] !== 'undefined' && database2IsInstantiatingAConnection[url]) {
        EventEmitter.once('connected', function(err, databaseUrl) {
            if (err) {
                console.log('ERROR DURING INSTANTIATING CONNECTION TO DATABASE', databaseUrl, err)
                return;
            }
            if (databaseUrl == url) {
                return cb(null, DATABASE[url])
            }
        })
        return;
    }
    database2IsInstantiatingAConnection[url] = true;
    EventEmitter.once('connected', function(err, databaseUrl) {
        if (err) {
            console.log('ERROR DURING INSTANTIATING CONNECTION TO DATABASE', databaseUrl, err)
            return;
        }
        if (databaseUrl == url) {
            return cb(null, DATABASE[url])
        }
    })
    _connect(options, function(err, db) {
        DATABASE[url] = db
        EventEmitter.emit('connected', err, url);
    })

}
module.exports._connect = _connect = function(options, cb) {
    var url = getUrlFromOptions(options)
    MongoClient.connect(url, function(err, db) {
        cb(err, db)
    });
}

module.exports._getConnectionUrl = _getConnectionUrl = function(options) {
    // console.trace(options);

    var url = (typeof options.USERNAME !== 'undefined') ? ['mongodb://', options.USERNAME + ':' + options.PASSWORD + '@'] : ['mongodb://']

    if (typeof options.HOST !== 'undefined') {
        url.push(options.HOST + ':')
    } else {
        url.push('localhost:')
    }
    if (typeof options.PORT !== 'undefined') {
        url.push(options.PORT)
    } else {
        url.push('27017')
    }

    url.push('/' + options.DATABASE_NAME)
    url = url.join('');

    Logger.info('DB connection URL is', url);

    return url;
}

module.exports.closeDatabase = closeDatabase = function closeDatabase(db, url) {
    db.close(function() {
        console.log('MONGODB->CONNECTION_POOL:', url, 'closed')
    });
}
module.exports.closeAllDatabases = function() {

    Object.keys(DATABASE).forEach(function(k) {
        var db = DATABASE[k];
        closeDatabase(db, k);
    })
}<commit_msg>better logging<commit_after>var MongoClient = require('mongodb').MongoClient,
    EventEmitter = new(require('events').EventEmitter)(),
    Logger = require('logb').getLogger(module.filename);

var DATABASE = {},
    database2IsInstantiatingAConnection = {},
    done = false

var _connect, _getConnectionUrl, closeDatabase;

var getUrlFromOptions = function(options) {
        var options = options || {};
        // console.log('options',options);
        if (typeof options.url !== 'undefined') {
            return options.url
        }
        return _getConnectionUrl(options)
    }
    /***
     */
module.exports.getDb = function(options, cb) {
    // console.log('options',options);
    var url = getUrlFromOptions(options);

    if (typeof DATABASE[url] !== 'undefined') {
        return cb(null, DATABASE[url])
    }
    if (typeof database2IsInstantiatingAConnection[url] !== 'undefined' && database2IsInstantiatingAConnection[url]) {
        EventEmitter.once('connected', function(err, databaseUrl) {
            if (err) {
                console.log('ERROR DURING INSTANTIATING CONNECTION TO DATABASE', databaseUrl, err)
                return;
            }
            if (databaseUrl == url) {
                return cb(null, DATABASE[url])
            }
        })
        return;
    }
    database2IsInstantiatingAConnection[url] = true;
    EventEmitter.once('connected', function(err, databaseUrl) {
        if (err) {
            console.log('ERROR DURING INSTANTIATING CONNECTION TO DATABASE', databaseUrl, err)
            return;
        }
        if (databaseUrl == url) {
            return cb(null, DATABASE[url])
        }
    })
    _connect(options, function(err, db) {
        DATABASE[url] = db
        EventEmitter.emit('connected', err, url);
        Logger.info('Connected to url:', url);
    })

}
module.exports._connect = _connect = function(options, cb) {
    var url = getUrlFromOptions(options)
    MongoClient.connect(url, function(err, db) {
        cb(err, db)
    });
}

module.exports._getConnectionUrl = _getConnectionUrl = function(options) {
    // console.trace(options);

    var url = (typeof options.USERNAME !== 'undefined') ? ['mongodb://', options.USERNAME + ':' + options.PASSWORD + '@'] : ['mongodb://']

    if (typeof options.HOST !== 'undefined') {
        url.push(options.HOST + ':')
    } else {
        url.push('localhost:')
    }
    if (typeof options.PORT !== 'undefined') {
        url.push(options.PORT)
    } else {
        url.push('27017')
    }

    url.push('/' + options.DATABASE_NAME)
    url = url.join('');

    Logger.info('DB connection URL is', url);

    return url;
}

module.exports.closeDatabase = closeDatabase = function closeDatabase(db, url) {
    db.close(function() {
        console.log('MONGODB->CONNECTION_POOL:', url, 'closed')
    });
}
module.exports.closeAllDatabases = function() {

    Object.keys(DATABASE).forEach(function(k) {
        var db = DATABASE[k];
        closeDatabase(db, k);
    })
}<|endoftext|>


<commit_before>/*
 * Copyright 2014 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @constructor
 * @extends {Protocol.Agents}
 * @param {string} name
 * @param {!InspectorBackendClass.Connection} connection
 * @param {function(?WebInspector.Target)=} callback
 */
WebInspector.Target = function(name, connection, callback)
{
    Protocol.Agents.call(this, connection.agentsMap());
    this._name = name;
    this._connection = connection;
    connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this);
    this._id = WebInspector.Target._nextId++;

    /** @type {!Map.<!Function, !WebInspector.SDKModel>} */
    this._modelByConstructor = new Map();

    /** @type {!Object.<string, boolean>} */
    this._capabilities = {};
    this.pageAgent().canScreencast(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanScreencast, null));
    this.pageAgent().canEmulate(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanEmulate, null));
    if (Runtime.experiments.isEnabled("timelinePowerProfiler"))
        this.powerAgent().canProfilePower(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanProfilePower, null));
    this.workerAgent().canInspectWorkers(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanInspectWorkers, this._loadedWithCapabilities.bind(this, callback)));
    this.consoleAgent().setTracingBasedTimeline(true);
}

/**
 * @enum {string}
 */
WebInspector.Target.Capabilities = {
    CanScreencast: "CanScreencast",
    HasTouchInputs: "HasTouchInputs",
    CanProfilePower: "CanProfilePower",
    CanInspectWorkers: "CanInspectWorkers",
    CanEmulate: "CanEmulate"
}

WebInspector.Target._nextId = 1;

WebInspector.Target.prototype = {
    suspend: function()
    {
        if (!Runtime.experiments.isEnabled("disableAgentsWhenProfile")) {
            this.debuggerModel.asyncStackTracesStateChanged();
            return;
        }
        this.debuggerModel.suspendModel();
        this.cssModel.suspendModel();
        this.domModel.suspendModel();
    },

    resume: function()
    {
        if (Runtime.experiments.isEnabled("disableAgentsWhenProfile")) {
            this.domModel.resumeModel();
            this.cssModel.resumeModel();
            this.debuggerModel.resumeModel();
        } else {
            this.debuggerModel.asyncStackTracesStateChanged();
        }
    },

    /**
     * @return {number}
     */
    id: function()
    {
        return this._id;
    },

    /**
     *
     * @return {string}
     */
    name: function()
    {
        return this._name;
    },

    /**
     * @param {string} name
     * @param {function()|null} callback
     * @param {?Protocol.Error} error
     * @param {boolean} result
     */
    _initializeCapability: function(name, callback, error, result)
    {
        this._capabilities[name] = result;
        if (callback)
            callback();
    },

    /**
     * @param {string} capability
     * @return {boolean}
     */
    hasCapability: function(capability)
    {
        return !!this._capabilities[capability];
    },

    /**
     * @param {function(?WebInspector.Target)=} callback
     */
    _loadedWithCapabilities: function(callback)
    {
        if (this._connection.isClosed()) {
            callback(null);
            return;
        }

        /** @type {!WebInspector.ConsoleModel} */
        this.consoleModel = new WebInspector.ConsoleModel(this);

        /** @type {!WebInspector.NetworkManager} */
        this.networkManager = new WebInspector.NetworkManager(this);

        /** @type {!WebInspector.ResourceTreeModel} */
        this.resourceTreeModel = new WebInspector.ResourceTreeModel(this);
        if (!WebInspector.resourceTreeModel)
            WebInspector.resourceTreeModel = this.resourceTreeModel;

        /** @type {!WebInspector.NetworkLog} */
        this.networkLog = new WebInspector.NetworkLog(this);
        if (!WebInspector.networkLog)
            WebInspector.networkLog = this.networkLog;

        /** @type {!WebInspector.DebuggerModel} */
        this.debuggerModel = new WebInspector.DebuggerModel(this);
        if (!WebInspector.debuggerModel)
            WebInspector.debuggerModel = this.debuggerModel;

        /** @type {!WebInspector.RuntimeModel} */
        this.runtimeModel = new WebInspector.RuntimeModel(this);
        if (!WebInspector.runtimeModel)
            WebInspector.runtimeModel = this.runtimeModel;

        /** @type {!WebInspector.DOMModel} */
        this.domModel = new WebInspector.DOMModel(this);

        /** @type {!WebInspector.CSSStyleModel} */
        this.cssModel = new WebInspector.CSSStyleModel(this);
        if (!WebInspector.cssModel)
            WebInspector.cssModel = this.cssModel;

        /** @type {!WebInspector.WorkerManager} */
        this.workerManager = new WebInspector.WorkerManager(this, this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers));
        if (!WebInspector.workerManager)
            WebInspector.workerManager = this.workerManager;

        if (this.hasCapability(WebInspector.Target.Capabilities.CanProfilePower))
            WebInspector.powerProfiler = new WebInspector.PowerProfiler(this);

        /** @type {!WebInspector.DatabaseModel} */
        this.databaseModel = new WebInspector.DatabaseModel(this);
        if (!WebInspector.databaseModel)
            WebInspector.databaseModel = this.databaseModel;

        /** @type {!WebInspector.DOMStorageModel} */
        this.domStorageModel = new WebInspector.DOMStorageModel(this);
        if (!WebInspector.domStorageModel)
            WebInspector.domStorageModel = this.domStorageModel;

        /** @type {!WebInspector.CPUProfilerModel} */
        this.cpuProfilerModel = new WebInspector.CPUProfilerModel(this);
        if (!WebInspector.cpuProfilerModel)
            WebInspector.cpuProfilerModel = this.cpuProfilerModel;

        /** @type {!WebInspector.HeapProfilerModel} */
        this.heapProfilerModel = new WebInspector.HeapProfilerModel(this);

        /** @type {!WebInspector.IndexedDBModel} */
        this.indexedDBModel = new WebInspector.IndexedDBModel(this);

        /** @type {!WebInspector.LayerTreeModel} */
        this.layerTreeModel = new WebInspector.LayerTreeModel(this);

        /** @type {!WebInspector.AnimationModel} */
        this.animationModel = new WebInspector.AnimationModel(this);

        if (callback)
            callback(this);
    },

    /**
     * @override
     * @param {string} domain
     * @param {!Object} dispatcher
     */
    registerDispatcher: function(domain, dispatcher)
    {
        this._connection.registerDispatcher(domain, dispatcher);
    },

    /**
     * @return {boolean}
     */
    isWorkerTarget: function()
    {
        return !this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers);
    },

    /**
     * @return {boolean}
     */
    canEmulate: function()
    {
        return this.hasCapability(WebInspector.Target.Capabilities.CanEmulate);
    },

    _onDisconnect: function()
    {
        WebInspector.targetManager.removeTarget(this);
        this._dispose();
    },

    _dispose: function()
    {
        this.debuggerModel.dispose();
        this.networkManager.dispose();
        this.cpuProfilerModel.dispose();
    },

    /**
     * @return {boolean}
     */
    isDetached: function()
    {
        return this._connection.isClosed();
    },

    __proto__: Protocol.Agents.prototype
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 * @param {!WebInspector.Target} target
 */
WebInspector.SDKObject = function(target)
{
    WebInspector.Object.call(this);
    this._target = target;
}

WebInspector.SDKObject.prototype = {
    /**
     * @return {!WebInspector.Target}
     */
    target: function()
    {
        return this._target;
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @constructor
 * @extends {WebInspector.SDKObject}
 * @param {!Function} modelClass
 * @param {!WebInspector.Target} target
 */
WebInspector.SDKModel = function(modelClass, target)
{
    WebInspector.SDKObject.call(this, target);
    target._modelByConstructor.set(modelClass, this);
}

WebInspector.SDKModel.prototype = {
    __proto__: WebInspector.SDKObject.prototype
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 */
WebInspector.TargetManager = function()
{
    WebInspector.Object.call(this);
    /** @type {!Array.<!WebInspector.Target>} */
    this._targets = [];
    /** @type {!Array.<!WebInspector.TargetManager.Observer>} */
    this._observers = [];
    /** @type {!Object.<string, !Array.<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!WebInspector.Event)}>>} */
    this._modelListeners = {};
    /** @type {boolean} */
    this._allTargetsSuspended = false;
}

WebInspector.TargetManager.Events = {
    InspectedURLChanged: "InspectedURLChanged",
    MainFrameNavigated: "MainFrameNavigated",
    Load: "Load",
    WillReloadPage: "WillReloadPage",
    SuspendStateChanged: "SuspendStateChanged"
}

WebInspector.TargetManager.prototype = {
    suspendAllTargets: function()
    {
        console.assert(!this._allTargetsSuspended);
        if (this._allTargetsSuspended)
            return;
        this._allTargetsSuspended = true;
        this._targets.forEach(function(target)
        {
            target.suspend();
        });
        this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
    },

    resumeAllTargets: function()
    {
        console.assert(this._allTargetsSuspended);
        if (!this._allTargetsSuspended)
            return;
        this._allTargetsSuspended = false;
        this._targets.forEach(function(target)
        {
            target.resume();
        });
        this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
    },

    /**
     * @return {boolean}
     */
    allTargetsSuspended: function()
    {
        return this._allTargetsSuspended;
    },

    /**
     * @return {string}
     */
    inspectedPageURL: function()
    {
        if (!this._targets.length)
            return "";

        return this._targets[0].resourceTreeModel.inspectedPageURL();
    },

    /**
     * @return {string}
     */
    inspectedPageDomain: function()
    {
        if (!this._targets.length)
            return "";

        return this._targets[0].resourceTreeModel.inspectedPageDomain();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _redispatchEvent: function(event)
    {
        this.dispatchEventToListeners(event.type, event.data);
    },

    /**
     * @param {boolean=} ignoreCache
     */
    reloadPage: function(ignoreCache)
    {
        if (this._targets.length)
            this._targets[0].resourceTreeModel.reloadPage(ignoreCache);
    },

    /**
     * @param {!Function} modelClass
     * @param {string} eventType
     * @param {function(!WebInspector.Event)} listener
     * @param {!Object=} thisObject
     */
    addModelListener: function(modelClass, eventType, listener, thisObject)
    {
        for (var i = 0; i < this._targets.length; ++i) {
            var model = this._targets[i]._modelByConstructor.get(modelClass);
            model.addEventListener(eventType, listener, thisObject);
        }
        if (!this._modelListeners[eventType])
            this._modelListeners[eventType] = [];
        this._modelListeners[eventType].push({ modelClass: modelClass, thisObject: thisObject, listener: listener });
    },

    /**
     * @param {!Function} modelClass
     * @param {string} eventType
     * @param {function(!WebInspector.Event)} listener
     * @param {!Object=} thisObject
     */
    removeModelListener: function(modelClass, eventType, listener, thisObject)
    {
        if (!this._modelListeners[eventType])
            return;

        for (var i = 0; i < this._targets.length; ++i) {
            var model = this._targets[i]._modelByConstructor.get(modelClass);
            model.removeEventListener(eventType, listener, thisObject);
        }

        var listeners = this._modelListeners[eventType];
        for (var i = 0; i < listeners.length; ++i) {
            if (listeners[i].modelClass === modelClass && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
                listeners.splice(i--, 1);
        }
        if (!listeners.length)
            delete this._modelListeners[eventType];
    },

    /**
     * @param {!WebInspector.TargetManager.Observer} targetObserver
     */
    observeTargets: function(targetObserver)
    {
        this.targets().forEach(targetObserver.targetAdded.bind(targetObserver));
        this._observers.push(targetObserver);
    },

    /**
     * @param {!WebInspector.TargetManager.Observer} targetObserver
     */
    unobserveTargets: function(targetObserver)
    {
        this._observers.remove(targetObserver);
    },

    /**
     * @param {string} name
     * @param {!InspectorBackendClass.Connection} connection
     * @param {function(?WebInspector.Target)=} callback
     */
    createTarget: function(name, connection, callback)
    {
        var target = new WebInspector.Target(name, connection, callbackWrapper.bind(this));

        /**
         * @this {WebInspector.TargetManager}
         * @param {?WebInspector.Target} newTarget
         */
        function callbackWrapper(newTarget)
        {
            if (newTarget)
                this.addTarget(newTarget);
            if (callback)
                callback(newTarget);
        }
    },

    /**
     * @param {!WebInspector.Target} target
     */
    addTarget: function(target)
    {
        this._targets.push(target);
        if (this._targets.length === 1) {
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
        }
        var copy = this._observers.slice();
        for (var i = 0; i < copy.length; ++i)
            copy[i].targetAdded(target);

        for (var eventType in this._modelListeners) {
            var listeners = this._modelListeners[eventType];
            for (var i = 0; i < listeners.length; ++i) {
                var model = target._modelByConstructor.get(listeners[i].modelClass);
                model.addEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
            }
        }
    },

    /**
     * @param {!WebInspector.Target} target
     */
    removeTarget: function(target)
    {
        this._targets.remove(target);
        if (this._targets.length === 0) {
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
        }
        var copy = this._observers.slice();
        for (var i = 0; i < copy.length; ++i)
            copy[i].targetRemoved(target);

        for (var eventType in this._modelListeners) {
            var listeners = this._modelListeners[eventType];
            for (var i = 0; i < listeners.length; ++i) {
                var model = target._modelByConstructor.get(listeners[i].modelClass);
                model.removeEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
            }
        }
    },

    /**
     * @return {boolean}
     */
    hasTargets: function()
    {
        return !!this._targets.length;
    },

    /**
     * @return {!Array.<!WebInspector.Target>}
     */
    targets: function()
    {
        return this._targets.slice();
    },

    /**
     * @return {?WebInspector.Target}
     */
    mainTarget: function()
    {
        return this._targets[0] || null;
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @interface
 */
WebInspector.TargetManager.Observer = function()
{
}

WebInspector.TargetManager.Observer.prototype = {
    /**
     * @param {!WebInspector.Target} target
     */
    targetAdded: function(target) { },

    /**
     * @param {!WebInspector.Target} target
     */
    targetRemoved: function(target) { },
}

/**
 * @type {!WebInspector.TargetManager}
 */
WebInspector.targetManager = new WebInspector.TargetManager();
<commit_msg>Check existance of models on targets before event work<commit_after>/*
 * Copyright 2014 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @constructor
 * @extends {Protocol.Agents}
 * @param {string} name
 * @param {!InspectorBackendClass.Connection} connection
 * @param {function(?WebInspector.Target)=} callback
 */
WebInspector.Target = function(name, connection, callback)
{
    Protocol.Agents.call(this, connection.agentsMap());
    this._name = name;
    this._connection = connection;
    connection.addEventListener(InspectorBackendClass.Connection.Events.Disconnected, this._onDisconnect, this);
    this._id = WebInspector.Target._nextId++;

    /** @type {!Map.<!Function, !WebInspector.SDKModel>} */
    this._modelByConstructor = new Map();

    /** @type {!Object.<string, boolean>} */
    this._capabilities = {};
    this.pageAgent().canScreencast(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanScreencast, null));
    this.pageAgent().canEmulate(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanEmulate, null));
    if (Runtime.experiments.isEnabled("timelinePowerProfiler"))
        this.powerAgent().canProfilePower(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanProfilePower, null));
    this.workerAgent().canInspectWorkers(this._initializeCapability.bind(this, WebInspector.Target.Capabilities.CanInspectWorkers, this._loadedWithCapabilities.bind(this, callback)));
    this.consoleAgent().setTracingBasedTimeline(true);
}

/**
 * @enum {string}
 */
WebInspector.Target.Capabilities = {
    CanScreencast: "CanScreencast",
    HasTouchInputs: "HasTouchInputs",
    CanProfilePower: "CanProfilePower",
    CanInspectWorkers: "CanInspectWorkers",
    CanEmulate: "CanEmulate"
}

WebInspector.Target._nextId = 1;

WebInspector.Target.prototype = {
    suspend: function()
    {
        if (!Runtime.experiments.isEnabled("disableAgentsWhenProfile")) {
            this.debuggerModel.asyncStackTracesStateChanged();
            return;
        }
        this.debuggerModel.suspendModel();
        this.cssModel.suspendModel();
        this.domModel.suspendModel();
    },

    resume: function()
    {
        if (Runtime.experiments.isEnabled("disableAgentsWhenProfile")) {
            this.domModel.resumeModel();
            this.cssModel.resumeModel();
            this.debuggerModel.resumeModel();
        } else {
            this.debuggerModel.asyncStackTracesStateChanged();
        }
    },

    /**
     * @return {number}
     */
    id: function()
    {
        return this._id;
    },

    /**
     *
     * @return {string}
     */
    name: function()
    {
        return this._name;
    },

    /**
     * @param {string} name
     * @param {function()|null} callback
     * @param {?Protocol.Error} error
     * @param {boolean} result
     */
    _initializeCapability: function(name, callback, error, result)
    {
        this._capabilities[name] = result;
        if (callback)
            callback();
    },

    /**
     * @param {string} capability
     * @return {boolean}
     */
    hasCapability: function(capability)
    {
        return !!this._capabilities[capability];
    },

    /**
     * @param {function(?WebInspector.Target)=} callback
     */
    _loadedWithCapabilities: function(callback)
    {
        if (this._connection.isClosed()) {
            callback(null);
            return;
        }

        /** @type {!WebInspector.ConsoleModel} */
        this.consoleModel = new WebInspector.ConsoleModel(this);

        /** @type {!WebInspector.NetworkManager} */
        this.networkManager = new WebInspector.NetworkManager(this);

        /** @type {!WebInspector.ResourceTreeModel} */
        this.resourceTreeModel = new WebInspector.ResourceTreeModel(this);
        if (!WebInspector.resourceTreeModel)
            WebInspector.resourceTreeModel = this.resourceTreeModel;

        /** @type {!WebInspector.NetworkLog} */
        this.networkLog = new WebInspector.NetworkLog(this);
        if (!WebInspector.networkLog)
            WebInspector.networkLog = this.networkLog;

        /** @type {!WebInspector.DebuggerModel} */
        this.debuggerModel = new WebInspector.DebuggerModel(this);
        if (!WebInspector.debuggerModel)
            WebInspector.debuggerModel = this.debuggerModel;

        /** @type {!WebInspector.RuntimeModel} */
        this.runtimeModel = new WebInspector.RuntimeModel(this);
        if (!WebInspector.runtimeModel)
            WebInspector.runtimeModel = this.runtimeModel;

        /** @type {!WebInspector.DOMModel} */
        this.domModel = new WebInspector.DOMModel(this);

        /** @type {!WebInspector.CSSStyleModel} */
        this.cssModel = new WebInspector.CSSStyleModel(this);
        if (!WebInspector.cssModel)
            WebInspector.cssModel = this.cssModel;

        /** @type {!WebInspector.WorkerManager} */
        this.workerManager = new WebInspector.WorkerManager(this, this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers));
        if (!WebInspector.workerManager)
            WebInspector.workerManager = this.workerManager;

        if (this.hasCapability(WebInspector.Target.Capabilities.CanProfilePower))
            WebInspector.powerProfiler = new WebInspector.PowerProfiler(this);

        /** @type {!WebInspector.DatabaseModel} */
        this.databaseModel = new WebInspector.DatabaseModel(this);
        if (!WebInspector.databaseModel)
            WebInspector.databaseModel = this.databaseModel;

        /** @type {!WebInspector.DOMStorageModel} */
        this.domStorageModel = new WebInspector.DOMStorageModel(this);
        if (!WebInspector.domStorageModel)
            WebInspector.domStorageModel = this.domStorageModel;

        /** @type {!WebInspector.CPUProfilerModel} */
        this.cpuProfilerModel = new WebInspector.CPUProfilerModel(this);
        if (!WebInspector.cpuProfilerModel)
            WebInspector.cpuProfilerModel = this.cpuProfilerModel;

        /** @type {!WebInspector.HeapProfilerModel} */
        this.heapProfilerModel = new WebInspector.HeapProfilerModel(this);

        /** @type {!WebInspector.IndexedDBModel} */
        this.indexedDBModel = new WebInspector.IndexedDBModel(this);

        /** @type {!WebInspector.LayerTreeModel} */
        this.layerTreeModel = new WebInspector.LayerTreeModel(this);

        /** @type {!WebInspector.AnimationModel} */
        this.animationModel = new WebInspector.AnimationModel(this);

        if (callback)
            callback(this);
    },

    /**
     * @override
     * @param {string} domain
     * @param {!Object} dispatcher
     */
    registerDispatcher: function(domain, dispatcher)
    {
        this._connection.registerDispatcher(domain, dispatcher);
    },

    /**
     * @return {boolean}
     */
    isWorkerTarget: function()
    {
        return !this.hasCapability(WebInspector.Target.Capabilities.CanInspectWorkers);
    },

    /**
     * @return {boolean}
     */
    canEmulate: function()
    {
        return this.hasCapability(WebInspector.Target.Capabilities.CanEmulate);
    },

    _onDisconnect: function()
    {
        WebInspector.targetManager.removeTarget(this);
        this._dispose();
    },

    _dispose: function()
    {
        this.debuggerModel.dispose();
        this.networkManager.dispose();
        this.cpuProfilerModel.dispose();
    },

    /**
     * @return {boolean}
     */
    isDetached: function()
    {
        return this._connection.isClosed();
    },

    __proto__: Protocol.Agents.prototype
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 * @param {!WebInspector.Target} target
 */
WebInspector.SDKObject = function(target)
{
    WebInspector.Object.call(this);
    this._target = target;
}

WebInspector.SDKObject.prototype = {
    /**
     * @return {!WebInspector.Target}
     */
    target: function()
    {
        return this._target;
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @constructor
 * @extends {WebInspector.SDKObject}
 * @param {!Function} modelClass
 * @param {!WebInspector.Target} target
 */
WebInspector.SDKModel = function(modelClass, target)
{
    WebInspector.SDKObject.call(this, target);
    target._modelByConstructor.set(modelClass, this);
}

WebInspector.SDKModel.prototype = {
    __proto__: WebInspector.SDKObject.prototype
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 */
WebInspector.TargetManager = function()
{
    WebInspector.Object.call(this);
    /** @type {!Array.<!WebInspector.Target>} */
    this._targets = [];
    /** @type {!Array.<!WebInspector.TargetManager.Observer>} */
    this._observers = [];
    /** @type {!Object.<string, !Array.<{modelClass: !Function, thisObject: (!Object|undefined), listener: function(!WebInspector.Event)}>>} */
    this._modelListeners = {};
    /** @type {boolean} */
    this._allTargetsSuspended = false;
}

WebInspector.TargetManager.Events = {
    InspectedURLChanged: "InspectedURLChanged",
    MainFrameNavigated: "MainFrameNavigated",
    Load: "Load",
    WillReloadPage: "WillReloadPage",
    SuspendStateChanged: "SuspendStateChanged"
}

WebInspector.TargetManager.prototype = {
    suspendAllTargets: function()
    {
        console.assert(!this._allTargetsSuspended);
        if (this._allTargetsSuspended)
            return;
        this._allTargetsSuspended = true;
        this._targets.forEach(function(target)
        {
            target.suspend();
        });
        this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
    },

    resumeAllTargets: function()
    {
        console.assert(this._allTargetsSuspended);
        if (!this._allTargetsSuspended)
            return;
        this._allTargetsSuspended = false;
        this._targets.forEach(function(target)
        {
            target.resume();
        });
        this.dispatchEventToListeners(WebInspector.TargetManager.Events.SuspendStateChanged);
    },

    /**
     * @return {boolean}
     */
    allTargetsSuspended: function()
    {
        return this._allTargetsSuspended;
    },

    /**
     * @return {string}
     */
    inspectedPageURL: function()
    {
        if (!this._targets.length)
            return "";

        return this._targets[0].resourceTreeModel.inspectedPageURL();
    },

    /**
     * @return {string}
     */
    inspectedPageDomain: function()
    {
        if (!this._targets.length)
            return "";

        return this._targets[0].resourceTreeModel.inspectedPageDomain();
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _redispatchEvent: function(event)
    {
        this.dispatchEventToListeners(event.type, event.data);
    },

    /**
     * @param {boolean=} ignoreCache
     */
    reloadPage: function(ignoreCache)
    {
        if (this._targets.length)
            this._targets[0].resourceTreeModel.reloadPage(ignoreCache);
    },

    /**
     * @param {!Function} modelClass
     * @param {string} eventType
     * @param {function(!WebInspector.Event)} listener
     * @param {!Object=} thisObject
     */
    addModelListener: function(modelClass, eventType, listener, thisObject)
    {
        for (var i = 0; i < this._targets.length; ++i) {
            var model = this._targets[i]._modelByConstructor.get(modelClass);
            if (model)
                model.addEventListener(eventType, listener, thisObject);
        }
        if (!this._modelListeners[eventType])
            this._modelListeners[eventType] = [];
        this._modelListeners[eventType].push({ modelClass: modelClass, thisObject: thisObject, listener: listener });
    },

    /**
     * @param {!Function} modelClass
     * @param {string} eventType
     * @param {function(!WebInspector.Event)} listener
     * @param {!Object=} thisObject
     */
    removeModelListener: function(modelClass, eventType, listener, thisObject)
    {
        if (!this._modelListeners[eventType])
            return;

        for (var i = 0; i < this._targets.length; ++i) {
            var model = this._targets[i]._modelByConstructor.get(modelClass);
            if (model)
                model.removeEventListener(eventType, listener, thisObject);
        }

        var listeners = this._modelListeners[eventType];
        for (var i = 0; i < listeners.length; ++i) {
            if (listeners[i].modelClass === modelClass && listeners[i].listener === listener && listeners[i].thisObject === thisObject)
                listeners.splice(i--, 1);
        }
        if (!listeners.length)
            delete this._modelListeners[eventType];
    },

    /**
     * @param {!WebInspector.TargetManager.Observer} targetObserver
     */
    observeTargets: function(targetObserver)
    {
        this.targets().forEach(targetObserver.targetAdded.bind(targetObserver));
        this._observers.push(targetObserver);
    },

    /**
     * @param {!WebInspector.TargetManager.Observer} targetObserver
     */
    unobserveTargets: function(targetObserver)
    {
        this._observers.remove(targetObserver);
    },

    /**
     * @param {string} name
     * @param {!InspectorBackendClass.Connection} connection
     * @param {function(?WebInspector.Target)=} callback
     */
    createTarget: function(name, connection, callback)
    {
        var target = new WebInspector.Target(name, connection, callbackWrapper.bind(this));

        /**
         * @this {WebInspector.TargetManager}
         * @param {?WebInspector.Target} newTarget
         */
        function callbackWrapper(newTarget)
        {
            if (newTarget)
                this.addTarget(newTarget);
            if (callback)
                callback(newTarget);
        }
    },

    /**
     * @param {!WebInspector.Target} target
     */
    addTarget: function(target)
    {
        this._targets.push(target);
        if (this._targets.length === 1) {
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
            target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
        }
        var copy = this._observers.slice();
        for (var i = 0; i < copy.length; ++i)
            copy[i].targetAdded(target);

        for (var eventType in this._modelListeners) {
            var listeners = this._modelListeners[eventType];
            for (var i = 0; i < listeners.length; ++i) {
                var model = target._modelByConstructor.get(listeners[i].modelClass);
                if (model)
                    model.addEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
            }
        }
    },

    /**
     * @param {!WebInspector.Target} target
     */
    removeTarget: function(target)
    {
        this._targets.remove(target);
        if (this._targets.length === 0) {
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.Load, this._redispatchEvent, this);
            target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.WillReloadPage, this._redispatchEvent, this);
        }
        var copy = this._observers.slice();
        for (var i = 0; i < copy.length; ++i)
            copy[i].targetRemoved(target);

        for (var eventType in this._modelListeners) {
            var listeners = this._modelListeners[eventType];
            for (var i = 0; i < listeners.length; ++i) {
                var model = target._modelByConstructor.get(listeners[i].modelClass);
                if (model)
                    model.removeEventListener(eventType, listeners[i].listener, listeners[i].thisObject);
            }
        }
    },

    /**
     * @return {boolean}
     */
    hasTargets: function()
    {
        return !!this._targets.length;
    },

    /**
     * @return {!Array.<!WebInspector.Target>}
     */
    targets: function()
    {
        return this._targets.slice();
    },

    /**
     * @return {?WebInspector.Target}
     */
    mainTarget: function()
    {
        return this._targets[0] || null;
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @interface
 */
WebInspector.TargetManager.Observer = function()
{
}

WebInspector.TargetManager.Observer.prototype = {
    /**
     * @param {!WebInspector.Target} target
     */
    targetAdded: function(target) { },

    /**
     * @param {!WebInspector.Target} target
     */
    targetRemoved: function(target) { },
}

/**
 * @type {!WebInspector.TargetManager}
 */
WebInspector.targetManager = new WebInspector.TargetManager();
<|endoftext|>


<commit_before>/*
 * StromDAO Business Object: DSO
 * =========================================
 * Distribution Service Operator / logistical handling for StromDAO Energy Blockchain.
 *
 * @author Thorsten Zoerner thorsten.zoerner@stromdao.de
 *
 */

this.dso=function(obj_or_address) {
			if(typeof obj_or_address == "undefined") obj_or_address=parent.options.contracts["StromDAO-BO.sol:DSO"];
			var p1 = new Promise(function(resolve, reject) {

				var instance=parent._objInstance(obj_or_address,'StromDAO-BO.sol:DSO');
				instance.approveConnection=function(_address,_power_limit)  {
					var p2 = new Promise(function(resolve2, reject2) {
							instance.obj.approveConnection(_address,_power_limit).then(function(o) {
								parent._waitForTransactionKeepRef(o,resolve2);
							});
					});
					return p2;
				};
				resolve(instance);
			});
			return p1;
		};
<commit_msg>Added some Lookups<commit_after>/*
 * StromDAO Business Object: DSO
 * =========================================
 * Distribution Service Operator / logistical handling for StromDAO Energy Blockchain.
 *
 * @author Thorsten Zoerner thorsten.zoerner@stromdao.de
 *
 */

this.dso=function(obj_or_address) {
			if(typeof obj_or_address == "undefined") obj_or_address=parent.options.contracts["StromDAO-BO.sol:DSO"];
			var p1 = new Promise(function(resolve, reject) {

				var instance=parent._objInstance(obj_or_address,'StromDAO-BO.sol:DSO');
				instance.approveConnection=function(_address,_power_limit)  {
					var p2 = new Promise(function(resolve2, reject2) {
							instance.obj.approveConnection(_address,_power_limit).then(function(o) {
								parent._waitForTransactionKeepRef(o,resolve2);
							});
					});
					return p2;
				};
				instance.approvedConnections=function(_address) {
					var p2 = new Promise(function(resolve2, reject2) {
							instance.obj.approvedConnections(_address).then(function(o) {
								 resolve2(o);
							});
					});
					return p2;
				}
				resolve(instance);
			});
			return p1;
		};
<|endoftext|>


<commit_before>import url from 'url'

const currentHostname = () => window.location ? window.location.origin : 'http://happyjoyland'

export const signinRequestUrl = () => {
  const tenant = '575c5b98-c069-4475-99d9-cfb32e39d24e'
  const protocol = 'https'
  const host = `login.microsoftonline.com/${tenant}/oauth2/authorize`
  const query = {
    client_id: '123999c5-771d-4136-9d87-b5fc03f3266e',
    response_type: 'id_token',
    redirect_uri: `${currentHostname()}/openid-complete`,
    scope: 'openid',
    response_mode: 'query',
    nonce: '12345',
    prompt: 'login',
  }

  return url.format({
    protocol,
    host,
    query,
  })
}
<commit_msg>use real URL for fallback in case of missing window.location<commit_after>import url from 'url'

const currentHostname = () => window.location ? window.location.origin : 'http://localhost:3001'

export const signinRequestUrl = () => {
  const tenant = '575c5b98-c069-4475-99d9-cfb32e39d24e'
  const protocol = 'https'
  const host = `login.microsoftonline.com/${tenant}/oauth2/authorize`
  const query = {
    client_id: '123999c5-771d-4136-9d87-b5fc03f3266e',
    response_type: 'id_token',
    redirect_uri: `${currentHostname()}/openid-complete`,
    scope: 'openid',
    response_mode: 'query',
    nonce: '12345',
    prompt: 'login',
  }

  return url.format({
    protocol,
    host,
    query,
  })
}
<|endoftext|>


<commit_before>import {extend} from './util';
import {History} from './history';
import {Router} from './router';
import {PipelineProvider} from './pipelineProvider';
import {isNavigationCommand} from './navigationCommand';

export class AppRouter extends Router {
  constructor(history:History, pipelineProvider:PipelineProvider) {
    super(history);
    this.pipelineProvider = pipelineProvider;
    document.addEventListener('click', handleLinkClick.bind(this), true);
  }

  loadUrl(url) {
    return this.createNavigationInstruction(url)
      .then(instruction => {
        return this.queueInstruction(instruction);
      }).catch(error =>{
        console.error(error);

        if (this.history.previousFragment) {
          this.navigate(this.history.previousFragment, false);
        }
      });
  }

  queueInstruction(instruction) {
    return new Promise(resolve => {
      instruction.resolve = resolve;
      this.queue.unshift(instruction);
      this.dequeueInstruction();
    });
  }

  dequeueInstruction() {
    if (this.isNavigating) {
      return;
    }

    var instruction = this.queue.shift();
    this.queue = [];

    if (!instruction) {
      return;
    }

    this.isNavigating = true;

    var context = this.createNavigationContext(instruction);
    var pipeline = this.pipelineProvider.createPipeline(context);

    pipeline.run(context).then(result => {
      this.isNavigating = false;

      if (result.completed) {
        this.history.previousFragment = instruction.fragment;
      }

      if (result.output instanceof Error) {
        console.error(result.output);
      }

      if (isNavigationCommand(result.output)) {
        result.output.navigate(this);
      } else if (!result.completed && this.history.previousFragment) {
        this.navigate(this.history.previousFragment, false);
      }

      instruction.resolve(result);
      this.dequeueInstruction();
    });
  }

  registerViewPort(viewPort, name) {
    super.registerViewPort(viewPort, name);

    if (!this.isActive) {
      this.activate();
    } else {
      this.dequeueInstruction();
    }
  }

  activate(options) {
    if (this.isActive) {
      return;
    }

    this.isActive = true;
    this.options = extend({ routeHandler: this.loadUrl.bind(this) }, this.options, options);
    this.history.activate(this.options);
    this.dequeueInstruction();
  }

  deactivate() {
    this.isActive = false;
    this.history.deactivate();
  }

  reset() {
    super.reset();
    this.queue = [];
    delete this.options;
  };
}

function handleLinkClick(evt) {
  if (!this.isActive) {
    return;
  }

  var target = evt.target;
  if (target.tagName != 'A') {
    return;
  }

  if (this.history._hasPushState) {
    if (!evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && targetIsThisWindow(target)) {
      var href = target.getAttribute('href');

      // Ensure the protocol is not part of URL, meaning its relative.
      // Stop the event bubbling to ensure the link will not cause a page refresh.
      if (href != null && !(href.charAt(0) === "#" || (/^[a-z]+:/i).test(href))) {
        evt.preventDefault();
        this.history.navigate(href);
      }
    }
  }
}

function targetIsThisWindow(target) {
  var targetWindow = target.getAttribute('target');

  return !targetWindow ||
    targetWindow === window.name ||
    targetWindow === '_self' ||
    (targetWindow === 'top' && window === window.top);
}<commit_msg>style(appRouter): improve formatting<commit_after>import {extend} from './util';
import {History} from './history';
import {Router} from './router';
import {PipelineProvider} from './pipelineProvider';
import {isNavigationCommand} from './navigationCommand';

export class AppRouter extends Router {
  constructor(history:History, pipelineProvider:PipelineProvider) {
    super(history);
    this.pipelineProvider = pipelineProvider;
    document.addEventListener('click', handleLinkClick.bind(this), true);
  }

  loadUrl(url) {
    return this.createNavigationInstruction(url).
      then(instruction => this.queueInstruction(instruction)).
      catch(error => {
        console.error(error);

        if (this.history.previousFragment) {
          this.navigate(this.history.previousFragment, false);
        }
      });
  }

  queueInstruction(instruction) {
    return new Promise(resolve => {
      instruction.resolve = resolve;
      this.queue.unshift(instruction);
      this.dequeueInstruction();
    });
  }

  dequeueInstruction() {
    if (this.isNavigating) {
      return;
    }

    var instruction = this.queue.shift();
    this.queue = [];

    if (!instruction) {
      return;
    }

    this.isNavigating = true;

    var context = this.createNavigationContext(instruction);
    var pipeline = this.pipelineProvider.createPipeline(context);

    pipeline.run(context).then(result => {
      this.isNavigating = false;

      if (result.completed) {
        this.history.previousFragment = instruction.fragment;
      }

      if (result.output instanceof Error) {
        console.error(result.output);
      }

      if (isNavigationCommand(result.output)) {
        result.output.navigate(this);
      } else if (!result.completed && this.history.previousFragment) {
        this.navigate(this.history.previousFragment, false);
      }

      instruction.resolve(result);
      this.dequeueInstruction();
    });
  }

  registerViewPort(viewPort, name) {
    super.registerViewPort(viewPort, name);

    if (!this.isActive) {
      this.activate();
    } else {
      this.dequeueInstruction();
    }
  }

  activate(options) {
    if (this.isActive) {
      return;
    }

    this.isActive = true;
    this.options = extend({ routeHandler: this.loadUrl.bind(this) }, this.options, options);
    this.history.activate(this.options);
    this.dequeueInstruction();
  }

  deactivate() {
    this.isActive = false;
    this.history.deactivate();
  }

  reset() {
    super.reset();
    this.queue = [];
    delete this.options;
  };
}

function handleLinkClick(evt) {
  if (!this.isActive) {
    return;
  }

  var target = evt.target;
  if (target.tagName != 'A') {
    return;
  }

  if (this.history._hasPushState) {
    if (!evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && targetIsThisWindow(target)) {
      var href = target.getAttribute('href');

      // Ensure the protocol is not part of URL, meaning its relative.
      // Stop the event bubbling to ensure the link will not cause a page refresh.
      if (href != null && !(href.charAt(0) === "#" || (/^[a-z]+:/i).test(href))) {
        evt.preventDefault();
        this.history.navigate(href);
      }
    }
  }
}

function targetIsThisWindow(target) {
  var targetWindow = target.getAttribute('target');

  return !targetWindow ||
    targetWindow === window.name ||
    targetWindow === '_self' ||
    (targetWindow === 'top' && window === window.top);
}
<|endoftext|>