<commit_before>/**
 * @file message event
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

const CLEVERBOT = require('cleverbot-node');
const CREDENTIALS = require('../settings/credentials.json');
const BOT = new CLEVERBOT;
BOT.configure({
  botapi: CREDENTIALS.cleverbotAPIkey
});
const credentialsFilter = require('../utils/credentialsFilter');
const wordFilter = require('../utils/wordFilter');
const linkFilter = require('../utils/linkFilter');
const inviteFilter = require('../utils/inviteFilter');
const handleTrigger = require('../handlers/triggerHandler');
const handleUserLevel = require('../handlers/levelHandler');
const handleCommand = require('../handlers/commandHandler');

module.exports = message => {
  /**
   * Filter Bastion's credentials from the message
   */
  credentialsFilter(message);

  if (message.author.bot) return;

  if (message.guild) {
    /**
     * Filter specific words from the message
     */
    wordFilter(message);

    /**
     * Filter links from the message
     */
    linkFilter(message);

    /**
     * Filter Discord server invites from the message
     */
    inviteFilter(message);

    /**
     * Check if the message contains a trigger and respond to it
     */
    handleTrigger(message);

    message.client.db.all('SELECT userID FROM blacklistedUsers').then(users => {
      if (users.map(u => u.userID).includes(message.author.id)) return;

      /**
       * Increase experience and level up user
       */
      handleUserLevel(message);

      /**
       * Handles Bastion's commands
       */
      handleCommand(message);

      if (message.content.startsWith(`<@${message.client.credentials.botId}>`) || message.content.startsWith(`<@!${message.client.credentials.botId}>`)) {
        message.client.db.get(`SELECT chat FROM guildSettings WHERE guildID=${message.guild.id}`).then(guild => {
          if (guild.chat === 'false') return;

          let args = message.content.split(' ');
          if (args.length < 1) return;

          try {
            BOT.write(args.join(' '), function (response) {
              message.channel.startTyping();
              setTimeout(function () {
                message.channel.send(response.output).then(() => {
                  message.channel.stopTyping();
                }).catch(e => {
                  message.client.log.error(e.stack);
                });
              }, response.output.length * 100);
            });
          }
          catch (e) {
            message.client.log.error(e.stack);
          }
        }).catch(e => {
          message.client.log.error(e.stack);
        });
      }
    }).catch(e => {
      message.client.log.error(e.stack);
    });
  }
  else {
    if (message.content.startsWith(`${message.client.config.prefix}h`) || message.content.startsWith(`${message.client.config.prefix}help`)) {
      return message.channel.send({
        embed: {
          color: message.client.colors.blue,
          title: 'Bastion Discord BOT',
          url: 'https://bastion.js.org',
          description: 'Join [**Bastion Support Server**](https://discord.gg/fzx8fkt) for testing the commands or any help you need with the bot or maybe just for fun.',
          fields: [
            {
              name: 'Support Server Invite Link',
              value: 'https://discord.gg/fzx8fkt'
            },
            {
              name: 'BOT Invite Link',
              value: `https://discordapp.com/oauth2/authorize?client_id=${message.client.user.id}&scope=bot&permissions=2146958463`
            }
          ],
          thumbnail: {
            url: message.client.user.displayAvatarURL
          },
          footer: {
            text: 'Copyright © 2017 Sankarsan Kampa'
          }
        }
      }).catch(e => {
        message.client.log.error(e.stack);
      });
    }
  }
};
<commit_msg>Implemented conversation handler<commit_after>/**
 * @file message event
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

const credentialsFilter = require('../utils/credentialsFilter');
const wordFilter = require('../utils/wordFilter');
const linkFilter = require('../utils/linkFilter');
const inviteFilter = require('../utils/inviteFilter');
const handleTrigger = require('../handlers/triggerHandler');
const handleUserLevel = require('../handlers/levelHandler');
const handleCommand = require('../handlers/commandHandler');
const handleConversation = require('../handlers/conversationHandler');

module.exports = message => {
  /**
   * Filter Bastion's credentials from the message
   */
  credentialsFilter(message);

  if (message.author.bot) return;

  if (message.guild) {
    /**
     * Filter specific words from the message
     */
    wordFilter(message);

    /**
     * Filter links from the message
     */
    linkFilter(message);

    /**
     * Filter Discord server invites from the message
     */
    inviteFilter(message);

    /**
     * Check if the message contains a trigger and respond to it
     */
    handleTrigger(message);

    message.client.db.all('SELECT userID FROM blacklistedUsers').then(users => {
      if (users.map(u => u.userID).includes(message.author.id)) return;

      /**
       * Increase experience and level up user
       */
      handleUserLevel(message);

      /**
       * Handles Bastion's commands
       */
      handleCommand(message);

      if (message.content.startsWith(`<@${message.client.credentials.botId}>`) || message.content.startsWith(`<@!${message.client.credentials.botId}>`)) {
        /**
         * Handles conversations with Bastion
         */
        handleConversation(message);
      }
    }).catch(e => {
      message.client.log.error(e.stack);
    });
  }
  else {
    if (message.content.startsWith(`${message.client.config.prefix}h`) || message.content.startsWith(`${message.client.config.prefix}help`)) {
      return message.channel.send({
        embed: {
          color: message.client.colors.blue,
          title: 'Bastion Discord BOT',
          url: 'https://bastion.js.org',
          description: 'Join [**Bastion Support Server**](https://discord.gg/fzx8fkt) for testing the commands or any help you need with the bot or maybe just for fun.',
          fields: [
            {
              name: 'Support Server Invite Link',
              value: 'https://discord.gg/fzx8fkt'
            },
            {
              name: 'BOT Invite Link',
              value: `https://discordapp.com/oauth2/authorize?client_id=${message.client.user.id}&scope=bot&permissions=2146958463`
            }
          ],
          thumbnail: {
            url: message.client.user.displayAvatarURL
          },
          footer: {
            text: 'Copyright © 2017 Sankarsan Kampa'
          }
        }
      }).catch(e => {
        message.client.log.error(e.stack);
      });
    }
  }
};
<|endoftext|>


<commit_before>import Modal from 'flarum/components/Modal';
import DiscussionPage from 'flarum/components/DiscussionPage';
import Button from 'flarum/components/Button';
import highlight from 'flarum/helpers/highlight';
import classList from 'flarum/utils/classList';
import extractText from 'flarum/utils/extractText';
import KeyboardNavigatable from 'flarum/utils/KeyboardNavigatable';
import Stream from 'flarum/utils/Stream';

import tagLabel from '../../common/helpers/tagLabel';
import tagIcon from '../../common/helpers/tagIcon';
import sortTags from '../../common/utils/sortTags';

export default class TagDiscussionModal extends Modal {
  oninit(vnode) {
    super.oninit(vnode);

    this.tags = app.store.all('tags');

    if (this.attrs.discussion) {
      this.tags = this.tags.filter(tag => tag.canAddToDiscussion() || this.attrs.discussion.tags().indexOf(tag) !== -1);
    } else {
      this.tags = this.tags.filter(tag => tag.canStartDiscussion());
    }

    this.tags = sortTags(this.tags);

    this.selected = [];
    this.filter = Stream('');
    this.index = this.tags[0].id();
    this.focused = false;

    if (this.attrs.selectedTags) {
      this.attrs.selectedTags.map(this.addTag.bind(this));
    } else if (this.attrs.discussion) {
      this.attrs.discussion.tags().map(this.addTag.bind(this));
    }

    this.minPrimary = app.forum.attribute('minPrimaryTags');
    this.maxPrimary = app.forum.attribute('maxPrimaryTags');
    this.minSecondary = app.forum.attribute('minSecondaryTags');
    this.maxSecondary = app.forum.attribute('maxSecondaryTags');

    this.navigator = new KeyboardNavigatable();
    this.navigator
      .onUp(() => this.setIndex(this.getCurrentNumericIndex() - 1, true))
      .onDown(() => this.setIndex(this.getCurrentNumericIndex() + 1, true))
      .onSelect(this.select.bind(this))
      .onRemove(() => this.selected.splice(this.selected.length - 1, 1));
  }

  primaryCount() {
    return this.selected.filter(tag => tag.isPrimary()).length;
  }

  secondaryCount() {
    return this.selected.filter(tag => !tag.isPrimary()).length;
  }

  /**
   * Add the given tag to the list of selected tags.
   *
   * @param {Tag} tag
   */
  addTag(tag) {
    if (!tag.canStartDiscussion()) return;

    // If this tag has a parent, we'll also need to add the parent tag to the
    // selected list if it's not already in there.
    const parent = tag.parent();
    if (parent) {
      const index = this.selected.indexOf(parent);
      if (index === -1) {
        this.selected.push(parent);
      }
    }

    this.selected.push(tag);
  }

  /**
   * Remove the given tag from the list of selected tags.
   *
   * @param {Tag} tag
   */
  removeTag(tag) {
    const index = this.selected.indexOf(tag);
    if (index !== -1) {
      this.selected.splice(index, 1);

      // Look through the list of selected tags for any tags which have the tag
      // we just removed as their parent. We'll need to remove them too.
      this.selected
        .filter(selected => selected.parent() === tag)
        .forEach(this.removeTag.bind(this));
    }
  }

  className() {
    return 'TagDiscussionModal';
  }

  title() {
    return this.attrs.discussion
      ? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', {title: {this.attrs.discussion.title()}})
      : app.translator.trans('flarum-tags.forum.choose_tags.title');
  }

  getInstruction(primaryCount, secondaryCount) {
    if (primaryCount < this.minPrimary) {
      const remaining = this.minPrimary - primaryCount;
      return app.translator.transChoice('flarum-tags.forum.choose_tags.choose_primary_placeholder', remaining, {count: remaining});
    } else if (secondaryCount < this.minSecondary) {
      const remaining = this.minSecondary - secondaryCount;
      return app.translator.transChoice('flarum-tags.forum.choose_tags.choose_secondary_placeholder', remaining, {count: remaining});
    }

    return '';
  }

  content() {
    let tags = this.tags;
    const filter = this.filter().toLowerCase();
    const primaryCount = this.primaryCount();
    const secondaryCount = this.secondaryCount();

    // Filter out all child tags whose parents have not been selected. This
    // makes it impossible to select a child if its parent hasn't been selected.
    tags = tags.filter(tag => {
      const parent = tag.parent();
      return parent === false || this.selected.indexOf(parent) !== -1;
    });

    // If the number of selected primary/secondary tags is at the maximum, then
    // we'll filter out all other tags of that type.
    if (primaryCount >= this.maxPrimary) {
      tags = tags.filter(tag => !tag.isPrimary() || this.selected.indexOf(tag) !== -1);
    }

    if (secondaryCount >= this.maxSecondary) {
      tags = tags.filter(tag => tag.isPrimary() || this.selected.indexOf(tag) !== -1);
    }

    // If the user has entered text in the filter input, then filter by tags
    // whose name matches what they've entered.
    if (filter) {
      tags = tags.filter(tag => tag.name().substr(0, filter.length).toLowerCase() === filter);
    }

    if (tags.indexOf(this.index) === -1) this.index = tags[0];

    const inputWidth = Math.max(extractText(this.getInstruction(primaryCount, secondaryCount)).length, this.filter().length);

    return [

 this.$('.TagsInput input').focus()}
            >

                {this.selected.map(tag =>
                   {
                    this.removeTag(tag);
                    this.onready();
                  }}>
                    {tagLabel(tag)}

                )}

              <input className="FormControl"
                placeholder={extractText(this.getInstruction(primaryCount, secondaryCount))}
                bidi={this.filter}
                style={{ width: inputWidth + 'ch' }}
                onkeydown={this.navigator.navigate.bind(this.navigator)}
                onfocus={() => this.focused = true}
                onblur={() => this.focused = false}/>

            <Button type="submit" className="Button Button--primary" disabled={primaryCount < this.minPrimary || secondaryCount < this.minSecondary} icon="fas fa-check">
              {app.translator.trans('flarum-tags.forum.choose_tags.submit_button')}
            </Button>

,

          {tags
            .filter(tag => filter || !tag.parent() || this.selected.indexOf(tag.parent()) !== -1)
            .map(tag => (

 this.index = tag}
                onclick={this.toggleTag.bind(this, tag)}
              >
                {tagIcon(tag)}

                  {highlight(tag.name(), filter)}

                {tag.description()
                  ? (

                      {tag.description()}

                  ) : ''}

            ))}

    ];
  }

  toggleTag(tag) {
    const index = this.selected.indexOf(tag);

    if (index !== -1) {
      this.removeTag(tag);
    } else {
      this.addTag(tag);
    }

    if (this.filter()) {
      this.filter('');
      this.index = this.tags[0];
    }

    this.onready();
  }

  select(e) {
    // Ctrl + Enter submits the selection, just Enter completes the current entry
    if (e.metaKey || e.ctrlKey || this.selected.indexOf(this.index) !== -1) {
      if (this.selected.length) {
        this.$('form').submit();
      }
    } else {
      this.getItem(this.index)[0].dispatchEvent(new Event('click'));
    }
  }

  selectableItems() {
    return this.$('.TagDiscussionModal-list > li');
  }

  getCurrentNumericIndex() {
    return this.selectableItems().index(
      this.getItem(this.index)
    );
  }

  getItem(index) {
    return this.selectableItems().filter(`[data-index="${index.id()}"]`);
  }

  setIndex(index, scrollToItem) {
    const $items = this.selectableItems();
    const $dropdown = $items.parent();

    if (index < 0) {
      index = $items.length - 1;
    } else if (index >= $items.length) {
      index = 0;
    }

    const $item = $items.eq(index);

    this.index = app.store.getById('tags', $item.attr('data-index'));

    m.redraw();

    if (scrollToItem) {
      const dropdownScroll = $dropdown.scrollTop();
      const dropdownTop = $dropdown.offset().top;
      const dropdownBottom = dropdownTop + $dropdown.outerHeight();
      const itemTop = $item.offset().top;
      const itemBottom = itemTop + $item.outerHeight();

      let scrollTop;
      if (itemTop < dropdownTop) {
        scrollTop = dropdownScroll - dropdownTop + itemTop - parseInt($dropdown.css('padding-top'), 10);
      } else if (itemBottom > dropdownBottom) {
        scrollTop = dropdownScroll - dropdownBottom + itemBottom + parseInt($dropdown.css('padding-bottom'), 10);
      }

      if (typeof scrollTop !== 'undefined') {
        $dropdown.stop(true).animate({scrollTop}, 100);
      }
    }
  }

  onsubmit(e) {
    e.preventDefault();

    const discussion = this.attrs.discussion;
    const tags = this.selected;

    if (discussion) {
      discussion.save({relationships: {tags}})
        .then(() => {
          if (app.current.matches(DiscussionPage)) {
            app.current.get('stream').update();
          }
          m.redraw();
        });
    }

    if (this.attrs.onsubmit) this.attrs.onsubmit(tags);

    this.hide();
  }
}
<commit_msg>Fix duplicate tags in TagDiscussionModal<commit_after>import Modal from 'flarum/components/Modal';
import DiscussionPage from 'flarum/components/DiscussionPage';
import Button from 'flarum/components/Button';
import highlight from 'flarum/helpers/highlight';
import classList from 'flarum/utils/classList';
import extractText from 'flarum/utils/extractText';
import KeyboardNavigatable from 'flarum/utils/KeyboardNavigatable';
import Stream from 'flarum/utils/Stream';

import tagLabel from '../../common/helpers/tagLabel';
import tagIcon from '../../common/helpers/tagIcon';
import sortTags from '../../common/utils/sortTags';

export default class TagDiscussionModal extends Modal {
  oninit(vnode) {
    super.oninit(vnode);

    this.tags = app.store.all('tags');

    if (this.attrs.discussion) {
      this.tags = this.tags.filter(tag => tag.canAddToDiscussion() || this.attrs.discussion.tags().indexOf(tag) !== -1);
    } else {
      this.tags = this.tags.filter(tag => tag.canStartDiscussion());
    }

    this.tags = sortTags(this.tags);

    this.selected = [];
    this.filter = Stream('');
    this.index = this.tags[0].id();
    this.focused = false;

    if (this.attrs.selectedTags) {
      this.attrs.selectedTags.map(this.addTag.bind(this));
    } else if (this.attrs.discussion) {
      this.attrs.discussion.tags().map(this.addTag.bind(this));
    }

    this.minPrimary = app.forum.attribute('minPrimaryTags');
    this.maxPrimary = app.forum.attribute('maxPrimaryTags');
    this.minSecondary = app.forum.attribute('minSecondaryTags');
    this.maxSecondary = app.forum.attribute('maxSecondaryTags');

    this.navigator = new KeyboardNavigatable();
    this.navigator
      .onUp(() => this.setIndex(this.getCurrentNumericIndex() - 1, true))
      .onDown(() => this.setIndex(this.getCurrentNumericIndex() + 1, true))
      .onSelect(this.select.bind(this))
      .onRemove(() => this.selected.splice(this.selected.length - 1, 1));
  }

  primaryCount() {
    return this.selected.filter(tag => tag.isPrimary()).length;
  }

  secondaryCount() {
    return this.selected.filter(tag => !tag.isPrimary()).length;
  }

  /**
   * Add the given tag to the list of selected tags.
   *
   * @param {Tag} tag
   */
  addTag(tag) {
    if (!tag.canStartDiscussion()) return;

    // If this tag has a parent, we'll also need to add the parent tag to the
    // selected list if it's not already in there.
    const parent = tag.parent();
    if (parent && this.selected.indexOf(parent) === -1) {
      this.selected.push(parent);
    }

    if (this.selected.indexOf(tag) === -1) {
      this.selected.push(tag);
    }
  }

  /**
   * Remove the given tag from the list of selected tags.
   *
   * @param {Tag} tag
   */
  removeTag(tag) {
    const index = this.selected.indexOf(tag);
    if (index !== -1) {
      this.selected.splice(index, 1);

      // Look through the list of selected tags for any tags which have the tag
      // we just removed as their parent. We'll need to remove them too.
      this.selected
        .filter(selected => selected.parent() === tag)
        .forEach(this.removeTag.bind(this));
    }
  }

  className() {
    return 'TagDiscussionModal';
  }

  title() {
    return this.attrs.discussion
      ? app.translator.trans('flarum-tags.forum.choose_tags.edit_title', {title: {this.attrs.discussion.title()}})
      : app.translator.trans('flarum-tags.forum.choose_tags.title');
  }

  getInstruction(primaryCount, secondaryCount) {
    if (primaryCount < this.minPrimary) {
      const remaining = this.minPrimary - primaryCount;
      return app.translator.transChoice('flarum-tags.forum.choose_tags.choose_primary_placeholder', remaining, {count: remaining});
    } else if (secondaryCount < this.minSecondary) {
      const remaining = this.minSecondary - secondaryCount;
      return app.translator.transChoice('flarum-tags.forum.choose_tags.choose_secondary_placeholder', remaining, {count: remaining});
    }

    return '';
  }

  content() {
    let tags = this.tags;
    const filter = this.filter().toLowerCase();
    const primaryCount = this.primaryCount();
    const secondaryCount = this.secondaryCount();

    // Filter out all child tags whose parents have not been selected. This
    // makes it impossible to select a child if its parent hasn't been selected.
    tags = tags.filter(tag => {
      const parent = tag.parent();
      return parent === false || this.selected.indexOf(parent) !== -1;
    });

    // If the number of selected primary/secondary tags is at the maximum, then
    // we'll filter out all other tags of that type.
    if (primaryCount >= this.maxPrimary) {
      tags = tags.filter(tag => !tag.isPrimary() || this.selected.indexOf(tag) !== -1);
    }

    if (secondaryCount >= this.maxSecondary) {
      tags = tags.filter(tag => tag.isPrimary() || this.selected.indexOf(tag) !== -1);
    }

    // If the user has entered text in the filter input, then filter by tags
    // whose name matches what they've entered.
    if (filter) {
      tags = tags.filter(tag => tag.name().substr(0, filter.length).toLowerCase() === filter);
    }

    if (tags.indexOf(this.index) === -1) this.index = tags[0];

    const inputWidth = Math.max(extractText(this.getInstruction(primaryCount, secondaryCount)).length, this.filter().length);

    return [

 this.$('.TagsInput input').focus()}
            >

                {this.selected.map(tag =>
                   {
                    this.removeTag(tag);
                    this.onready();
                  }}>
                    {tagLabel(tag)}

                )}

              <input className="FormControl"
                placeholder={extractText(this.getInstruction(primaryCount, secondaryCount))}
                bidi={this.filter}
                style={{ width: inputWidth + 'ch' }}
                onkeydown={this.navigator.navigate.bind(this.navigator)}
                onfocus={() => this.focused = true}
                onblur={() => this.focused = false}/>

            <Button type="submit" className="Button Button--primary" disabled={primaryCount < this.minPrimary || secondaryCount < this.minSecondary} icon="fas fa-check">
              {app.translator.trans('flarum-tags.forum.choose_tags.submit_button')}
            </Button>

,

          {tags
            .filter(tag => filter || !tag.parent() || this.selected.indexOf(tag.parent()) !== -1)
            .map(tag => (

 this.index = tag}
                onclick={this.toggleTag.bind(this, tag)}
              >
                {tagIcon(tag)}

                  {highlight(tag.name(), filter)}

                {tag.description()
                  ? (

                      {tag.description()}

                  ) : ''}

            ))}

    ];
  }

  toggleTag(tag) {
    const index = this.selected.indexOf(tag);

    if (index !== -1) {
      this.removeTag(tag);
    } else {
      this.addTag(tag);
    }

    if (this.filter()) {
      this.filter('');
      this.index = this.tags[0];
    }

    this.onready();
  }

  select(e) {
    // Ctrl + Enter submits the selection, just Enter completes the current entry
    if (e.metaKey || e.ctrlKey || this.selected.indexOf(this.index) !== -1) {
      if (this.selected.length) {
        this.$('form').submit();
      }
    } else {
      this.getItem(this.index)[0].dispatchEvent(new Event('click'));
    }
  }

  selectableItems() {
    return this.$('.TagDiscussionModal-list > li');
  }

  getCurrentNumericIndex() {
    return this.selectableItems().index(
      this.getItem(this.index)
    );
  }

  getItem(index) {
    return this.selectableItems().filter(`[data-index="${index.id()}"]`);
  }

  setIndex(index, scrollToItem) {
    const $items = this.selectableItems();
    const $dropdown = $items.parent();

    if (index < 0) {
      index = $items.length - 1;
    } else if (index >= $items.length) {
      index = 0;
    }

    const $item = $items.eq(index);

    this.index = app.store.getById('tags', $item.attr('data-index'));

    m.redraw();

    if (scrollToItem) {
      const dropdownScroll = $dropdown.scrollTop();
      const dropdownTop = $dropdown.offset().top;
      const dropdownBottom = dropdownTop + $dropdown.outerHeight();
      const itemTop = $item.offset().top;
      const itemBottom = itemTop + $item.outerHeight();

      let scrollTop;
      if (itemTop < dropdownTop) {
        scrollTop = dropdownScroll - dropdownTop + itemTop - parseInt($dropdown.css('padding-top'), 10);
      } else if (itemBottom > dropdownBottom) {
        scrollTop = dropdownScroll - dropdownBottom + itemBottom + parseInt($dropdown.css('padding-bottom'), 10);
      }

      if (typeof scrollTop !== 'undefined') {
        $dropdown.stop(true).animate({scrollTop}, 100);
      }
    }
  }

  onsubmit(e) {
    e.preventDefault();

    const discussion = this.attrs.discussion;
    const tags = this.selected;

    if (discussion) {
      discussion.save({relationships: {tags}})
        .then(() => {
          if (app.current.matches(DiscussionPage)) {
            app.current.get('stream').update();
          }
          m.redraw();
        });
    }

    if (this.attrs.onsubmit) this.attrs.onsubmit(tags);

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


<commit_before>function compile(template_string) {
    var _container = div();
    html(_container, template_string);
    var children = _container.childNodes;
    var result;
    switch (children.length) {
        case 0:
            result = document.createTextNode(String(template_string || ""));
            break;
        case 1:
            result = _container.removeChild(children[0]);
            break;
        default:
            result = [].slice.call(children, 0).map(function (a) {
                _container.removeChild(a);
                return a;
            });
    }
    return result;
}<commit_msg>直接引用process中的逻辑<commit_after>"use /process/compile/compile.js"<|endoftext|>


<commit_before>/* globals k:false, jQuery:false, trackEvent:false */
(function($) {
  var searchTimeout;
  var locale = $('html').attr('lang');

  var search = new k.Search('/' + locale + '/search');
  var cxhr = new k.CachedXHR();

  function hideContent() {
    $('#main-content').hide();
    $('#main-content').siblings('aside').hide();
    $('body').addClass('search-results-visible');
    $('.home-search-section .mzp-l-content').removeClass('narrow');
    $('.home-search-section').removeClass('extra-pad-bottom');

    // clear sidebar form and focus if is's there.
    $('#support-search-sidebar').find('input[name=q]').val('');
  }

  function showContent() {
    $('body').removeClass('search-results-visible');
    $('.home-search-section').addClass('extra-pad-bottom');
    $('.support-search-main').hide();
    $('#main-content').show();
    $('#main-content').siblings('aside').show();
    $('#instant-search-content').remove();
    $('.search-form-large:visible').find('input[name=q]').focus().val('');
    $('#support-search').find('input[name=q]').val('');
    $(".home-search-section--content .search-results-heading").remove();
    $('.home-search-section .mzp-l-content').addClass('narrow');
    $('.hidden-search-masthead').hide();
  }

  function render(data) {
    var context = $.extend({}, data);
    var base_url = search.lastQueryUrl();
    var $searchContent;
    context.base_url = base_url;

    if ($('#instant-search-content').length) {
      $searchContent = $('#instant-search-content');
    } else {
      $searchContent = $('<div />').attr('id', 'instant-search-content');
      $('#main-content').after($searchContent);
    }

    $searchContent.html(k.nunjucksEnv.render('search-results.html', context));

    // These two functions are coming from the global scope, but should be proper
    // modules when we replace django-compressor with a FE build process.
    detailsInit(); // fold up sidebar on mobile.
    tabsInit();

    // remove and append search results heading
    $(".home-search-section--content .search-results-heading").remove();
    $(".search-results-heading").appendTo(".home-search-section--content");
  }

  window.k.InstantSearchSettings = {
    hideContent: hideContent,
    showContent: showContent,
    render: render,
    searchClient: search
  };

  $(document).on('submit', '[data-instant-search="form"]', function(ev) {
    ev.preventDefault();
  });

  $(document).on('keyup', '[data-instant-search="form"] input[type="search"]', function(ev) {
    var $this = $(this);
    var $form = $this.closest('form');
    var formId = $form.attr('id');
    var params = {
      format: 'json'
    };

    if ($this.val().length === 0) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      window.k.InstantSearchSettings.showContent();
    } else if ($this.val() !== search.lastQuery) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      $form.find('input').each(function () {
        if ($(this).attr('type') === 'submit') {
          return true;
        }
        if ($(this).attr('type') === 'button') {
          return true;
        }
        if ($(this).attr('name') === 'q') {
          var value = $(this).val();

          if (formId === 'support-search-masthead') {
            $('#support-search').find('input[name=q]').val(value);
          } else if (formId === 'support-search' || formId === 'mobile-search-results') {

            // If applicable, close the mobile search field and move the focus to the main field.
            $('.sumo-nav--mobile-search-form').removeClass('mzp-is-open').attr('aria-expanded', 'false');

            if ($('.hidden-search-masthead').length > 0) {
              $('.hidden-search-masthead').show();
              $('.hidden-search-masthead').find('input[name=q]').val(value).focus();
              window.scrollTo(0, 0);
            } else {
              window.scrollTo(0, 0);
              $('#support-search-masthead').find('input[name=q]').val(value).focus();
            }

          } else if (formId === 'support-search-sidebar') {
            $('.hidden-search-masthead').show();
            $('.hidden-search-masthead').find('input[name=q]').val(value).focus();

          } else if (formId === 'support-search-sidebar') {
            $('.hidden-search-masthead').show();
            $('.hidden-search-masthead').find('input[name=q]').val(value).focus();

          } else {
            $('#support-search').find('input[name=q]').val(value);
            $('#support-search-masthead').find('input[name=q]').val(value);
          }

          return true;
        }
        params[$(this).attr('name')] = $(this).val();
      });

      searchTimeout = setTimeout(function () {
        if (search.hasLastQuery) {
          trackEvent('Instant Search', 'Exit Search', search.lastQueryUrl());
        }
        search.setParams(params);
        search.query($this.val(), k.InstantSearchSettings.render);
        trackEvent('Instant Search', 'Search', search.lastQueryUrl());
      }, 200);

      k.InstantSearchSettings.hideContent();
    }
  });

  $(document).on('click', '[data-instant-search="link"]', function(ev) {
    ev.preventDefault();

    var $this = $(this);

    if (search.hasLastQuery) {
      trackEvent('Instant Search', 'Exit Search', search.queryUrl(search.lastQuery));
    }

    var setParams = $this.data('instant-search-set-params');
    if (setParams) {
      setParams = setParams.split('&');
      $(setParams).each(function() {
        var p = this.split('=');
        search.setParam(p.shift(), p.join('='));
      });
    }

    var unsetParams = $this.data('instant-search-unset-params');
    if (unsetParams) {
      unsetParams = unsetParams.split('&');
      $(unsetParams).each(function() {
        search.unsetParam(this);
      });
    }

    trackEvent('Instant Search', 'Search', $this.data('href'));

    cxhr.request($this.data('href'), {
      data: {format: 'json'},
      dataType: 'json',
      success: k.InstantSearchSettings.render
    });
  });

  // 'Popular searches' feature
  $(document).on('click', '[data-featured-search]', function(ev) {
    var $mainInput = $('#support-search-masthead input[name=q]');
    var thisLink = $(this).text();
    $('#support-search-masthead input[name=q]').focus().val(thisLink);
    $mainInput.trigger( "keyup" );
    ev.preventDefault();
  });

  $(document).on('click', '[data-mobile-nav-search-button]', function(ev) {
    // If we hijack the layout of the page and the user clicks the button again.
    // assume they want to get rid of the search.
    if ($('.hidden-search-masthead').is(':visible')) {
      window.k.InstantSearchSettings.showContent();
    } else if ($('.hidden-search-masthead').length > 0) {
      $('body').addClass('search-results-visible');
      $('.hidden-search-masthead').show();
      $('.hidden-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else {
      // catchall for pages with a searchbox in the masthead
      $('.simple-search-form .searchbox').trigger('keyup').focus();
    }

    ev.preventDefault();
  });
})(jQuery);
<commit_msg>search UI focus tweak, ref https://github.com/mozilla/sumo-project/issues/263#issuecomment-603073588<commit_after>/* globals k:false, jQuery:false, trackEvent:false */
(function($) {
  var searchTimeout;
  var locale = $('html').attr('lang');

  var search = new k.Search('/' + locale + '/search');
  var cxhr = new k.CachedXHR();

  function hideContent() {
    $('#main-content').hide();
    $('#main-content').siblings('aside').hide();
    $('body').addClass('search-results-visible');
    $('.home-search-section .mzp-l-content').removeClass('narrow');
    $('.home-search-section').removeClass('extra-pad-bottom');

    // clear sidebar form and focus if is's there.
    $('#support-search-sidebar').find('input[name=q]').val('');
  }

  function showContent() {
    $('body').removeClass('search-results-visible');
    $('.home-search-section').addClass('extra-pad-bottom');
    $('.support-search-main').hide();
    $('#main-content').show();
    $('#main-content').siblings('aside').show();
    $('#instant-search-content').remove();
    $('.search-form-large:visible').find('input[name=q]').focus().val('');
    $('#support-search').find('input[name=q]').val('');
    $(".home-search-section--content .search-results-heading").remove();
    $('.home-search-section .mzp-l-content').addClass('narrow');
    $('.hidden-search-masthead').hide();
  }

  function render(data) {
    var context = $.extend({}, data);
    var base_url = search.lastQueryUrl();
    var $searchContent;
    context.base_url = base_url;

    if ($('#instant-search-content').length) {
      $searchContent = $('#instant-search-content');
    } else {
      $searchContent = $('<div />').attr('id', 'instant-search-content');
      $('#main-content').after($searchContent);
    }

    $searchContent.html(k.nunjucksEnv.render('search-results.html', context));

    // These two functions are coming from the global scope, but should be proper
    // modules when we replace django-compressor with a FE build process.
    detailsInit(); // fold up sidebar on mobile.
    tabsInit();

    // remove and append search results heading
    $(".home-search-section--content .search-results-heading").remove();
    $(".search-results-heading").appendTo(".home-search-section--content");
  }

  window.k.InstantSearchSettings = {
    hideContent: hideContent,
    showContent: showContent,
    render: render,
    searchClient: search
  };

  $(document).on('submit', '[data-instant-search="form"]', function(ev) {
    if ( $(this).find('.searchbox').val('') ) {
      $(this).find('.searchbox').focus();
    }
    ev.preventDefault();
  });

  $(document).on('keyup', '[data-instant-search="form"] input[type="search"]', function(ev) {
    var $this = $(this);
    var $form = $this.closest('form');
    var formId = $form.attr('id');
    var params = {
      format: 'json'
    };

    if ($this.val().length === 0) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      window.k.InstantSearchSettings.showContent();
    } else if ($this.val() !== search.lastQuery) {
      if (searchTimeout) {
        window.clearTimeout(searchTimeout);
      }

      $form.find('input').each(function () {
        if ($(this).attr('type') === 'submit') {
          return true;
        }
        if ($(this).attr('type') === 'button') {
          return true;
        }
        if ($(this).attr('name') === 'q') {
          var value = $(this).val();

          if (formId === 'support-search-masthead') {
            $('#support-search').find('input[name=q]').val(value);
          } else if (formId === 'support-search' || formId === 'mobile-search-results') {

            // If applicable, close the mobile search field and move the focus to the main field.
            $('.sumo-nav--mobile-search-form').removeClass('mzp-is-open').attr('aria-expanded', 'false');

            if ($('.hidden-search-masthead').length > 0) {
              $('.hidden-search-masthead').show();
              $('.hidden-search-masthead').find('input[name=q]').val(value).focus();
              window.scrollTo(0, 0);
            } else {
              window.scrollTo(0, 0);
              $('#support-search-masthead').find('input[name=q]').val(value).focus();
            }

          } else if (formId === 'support-search-sidebar') {
            $('.hidden-search-masthead').show();
            $('.hidden-search-masthead').find('input[name=q]').val(value).focus();

          } else if (formId === 'support-search-sidebar') {
            $('.hidden-search-masthead').show();
            $('.hidden-search-masthead').find('input[name=q]').val(value).focus();

          } else {
            $('#support-search').find('input[name=q]').val(value);
            $('#support-search-masthead').find('input[name=q]').val(value);
          }

          return true;
        }
        params[$(this).attr('name')] = $(this).val();
      });

      searchTimeout = setTimeout(function () {
        if (search.hasLastQuery) {
          trackEvent('Instant Search', 'Exit Search', search.lastQueryUrl());
        }
        search.setParams(params);
        search.query($this.val(), k.InstantSearchSettings.render);
        trackEvent('Instant Search', 'Search', search.lastQueryUrl());
      }, 200);

      k.InstantSearchSettings.hideContent();
    }
  });

  $(document).on('click', '[data-instant-search="link"]', function(ev) {
    ev.preventDefault();

    var $this = $(this);

    if (search.hasLastQuery) {
      trackEvent('Instant Search', 'Exit Search', search.queryUrl(search.lastQuery));
    }

    var setParams = $this.data('instant-search-set-params');
    if (setParams) {
      setParams = setParams.split('&');
      $(setParams).each(function() {
        var p = this.split('=');
        search.setParam(p.shift(), p.join('='));
      });
    }

    var unsetParams = $this.data('instant-search-unset-params');
    if (unsetParams) {
      unsetParams = unsetParams.split('&');
      $(unsetParams).each(function() {
        search.unsetParam(this);
      });
    }

    trackEvent('Instant Search', 'Search', $this.data('href'));

    cxhr.request($this.data('href'), {
      data: {format: 'json'},
      dataType: 'json',
      success: k.InstantSearchSettings.render
    });
  });

  // 'Popular searches' feature
  $(document).on('click', '[data-featured-search]', function(ev) {
    var $mainInput = $('#support-search-masthead input[name=q]');
    var thisLink = $(this).text();
    $('#support-search-masthead input[name=q]').focus().val(thisLink);
    $mainInput.trigger( "keyup" );
    ev.preventDefault();
  });

  $(document).on('click', '[data-mobile-nav-search-button]', function(ev) {
    // If we hijack the layout of the page and the user clicks the button again.
    // assume they want to get rid of the search.
    if ($('.hidden-search-masthead').is(':visible')) {
      window.k.InstantSearchSettings.showContent();
    } else if ($('.hidden-search-masthead').length > 0) {
      $('body').addClass('search-results-visible');
      $('.hidden-search-masthead').show();
      $('.hidden-search-masthead').find('input[name=q]').focus();
      window.scrollTo(0, 0);
    } else {
      // catchall for pages with a searchbox in the masthead
      $('.simple-search-form .searchbox').trigger('keyup').focus();
    }

    ev.preventDefault();
  });
})(jQuery);
<|endoftext|>


<commit_before>/**
 * RedScript compiler
 * Copyright(c) 2013 Adam Brodzinski <adambrodzinski@gmail.com>
 * MIT Licensed
 */

module.exports = {
     _boilerplate_: function(line) {
         var regex = /foobar/;
         return line.replace(regex, function($0, $1) {
         });
     }
};

<commit_msg>adds boilerplate method<commit_after>/**
 * RedScript compiler
 * Copyright(c) 2013 Adam Brodzinski <adambrodzinski@gmail.com>
 * MIT Licensed
 */

module.exports = {
    /* Method_Description
     *
     * @param  {string} - accepts line as a string
     * @return {string  - transformed line
     * @api public
     */
    boilerplate: function(line) {
        var regex = /foobar/;
        return line.replace(regex, function($0, $1) {

        });
     }
};

<|endoftext|>


<commit_before>/**
 * socket.io-node-client
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

(function(){
  var io = this.io,

  /**
   * Message frame for encoding and decoding responses from the Socket.IO server.
   *
   * @const
   * @type {String}
   */
  frame = '~m~',

  /**
   * Transforms the message to a string. If the message is an {Object} we will convert it to
   * a string and prefix it with the `~j~` flag to indicate that message is JSON encoded.
   *
   * Example:
   *
   *     stringify({foo:"bar"});
   *     // => "~j~{"foo":"bar"}"
   *
   * @param {String|Array|Object} message The messages that needs to be transformed to a string.
   * @throws {Error} When the JSON.stringify implementation is missing in the browser.
   * @returns {String} Message.
   * @api private
   */
  stringify = function(message){
    if (Object.prototype.toString.call(message) == '[object Object]'){
      if (!('JSON' in window)){
        var error = 'Socket.IO Error: Trying to encode as JSON, but JSON.stringify is missing.';
        if ('console' in window && console.error){
          console.error(error);
        } else {
          throw new Error(error);
        }
        return '{ "$error": "'+ error +'" }';
      }
      return '~j~' + JSON.stringify(message);
    } else {
      return String(message);
    }
  },

  /**
   * This is the transport template for all supported transport methods. It provides the
   * basic functionality to create a working transport for Socket.IO.
   *
   * Options:
   *   - `timeout`  Transport shutdown timeout in milliseconds, based on the heartbeat interval.
   *
   * Example:
   *
   *     var transport = io.Transport.mytransport = function(){
   *       io.Transport.apply(this, arguments);
   *     };
   *     io.util.inherit(transport, io.Transport);
   *
   *     ... // more code here
   *
   *     // connect with your new transport
   *     var socket = new io.Socket(null,{transports:['mytransport']});
   *
   * @constructor
   * @param {Object} base The reference to io.Socket.
   * @param {Object} options The transport options.
   * @property {io.Socket|Object} base The reference to io.Socket.
   * @property {Object} options The transport options, these are used to overwrite the default options
   * @property {String} sessionid The sessionid of the established connection, this is only available a connection is established
   * @property {Boolean} connected The connection has been established.
   * @property {Boolean} connecting We are still connecting to the server.
   * @api public
   */
  Transport = io.Transport = function(base, options){
    this.base = base;
    this.options = {
      timeout: 15000 // based on heartbeat interval default
    };
    io.util.merge(this.options, options);
  };

  /**
   * Send the message to the connected Socket.IO server.
   *
   * @throws {Error} When the io.Transport is inherited, it should override this method.
   * @api public
   */
  Transport.prototype.send = function(){
    throw new Error('Missing send() implementation');
  };

  /**
   * Establish a connection with the Socket.IO server..
   *
   * @throws {Error} When the io.Transport is inherited, it should override this method.
   * @api public
   */
  Transport.prototype.connect = function(){
    throw new Error('Missing connect() implementation');
  };

  /**
   * Disconnect the established connection.
   *
   * @throws {Error} When the io.Transport is inherited, it should override this method.
   * @api private
   */
  Transport.prototype.disconnect = function(){
    throw new Error('Missing disconnect() implementation');
  };

  /**
   * Encode the message by adding the `frame` to each message. This allows
   * the client so send multiple messages with only one request.
   *
   * @param {String|Array} messages Messages that need to be encoded.
   * @returns {String} Encoded message.
   * @api private
   */
  Transport.prototype.encode = function(messages){
    var ret = '', message;
    messages = io.util.isArray(messages) ? messages : [messages];
    for (var i = 0, l = messages.length; i < l; i++){
      message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]);
      ret += frame + message.length + frame + message;
    }
    return ret;
  };

  /**
   * Decoded the response from the Socket.IO server, as the server could send multiple
   * messages in one response.
   *
   * @param (String} data The response from the server that requires decoding
   * @returns {Array} Decoded messages.
   * @api private
   */
  Transport.prototype.decode = function(data){
    var messages = [], number, n;
    do {
      if (data.substr(0, 3) !== frame) return messages;
      data = data.substr(3);
      number = '', n = '';
      for (var i = 0, l = data.length; i < l; i++){
        n = Number(data.substr(i, 1));
        if (data.substr(i, 1) == n){
          number += n;
        } else {
          data = data.substr(number.length + frame.length);
          number = Number(number);
          break;
        }
      }
      messages.push(data.substr(0, number)); // here
      data = data.substr(number);
    } while(data !== '');
    return messages;
  };

  /**
   * Handles the response from the server. When a new response is received
   * it will automatically update the timeout, decode the message and
   * forwards the response to the onMessage function for further processing.
   *
   * @param {String} data Response from the server.
   * @api private
   */
  Transport.prototype.onData = function(data){
    this.setTimeout();
    var msgs = this.decode(data);
    if (msgs && msgs.length){
      for (var i = 0, l = msgs.length; i < l; i++){
        this.onMessage(msgs[i]);
      }
    }
  };

  /**
   * All the transports have a dedicated timeout to detect if
   * the connection is still alive. We clear the existing timer
   * and set new one each time this function is called. When the
   * timeout does occur it will call the `onTimeout` method.
   *
   * @api private
   */
  Transport.prototype.setTimeout = function(){
    var self = this;
    if (this.timeout) clearTimeout(this.timeout);
    this.timeout = setTimeout(function(){
      self.onTimeout();
    }, this.options.timeout);
  };

  /**
   * Disconnect from the Socket.IO server when a timeout occurs.
   *
   * @api private
   */
  Transport.prototype.onTimeout = function(){
    this.onDisconnect();
  };

  /**
   * After the response from the server has been parsed to individual
   * messages we need to decode them using the the Socket.IO message
   * protocol: <https://github.com/learnboost/socket.io-node/>.
   *
   * When a message is received we check if a session id has been set,
   * if the session id is missing we can assume that the received message
   * contains the sessionid of the connection.

   * When a message is prefixed with `~h~` we dispatch it our heartbeat
   * processing method `onHeartbeat` with the content of the heartbeat.
   *
   * When the message is prefixed with `~j~` we can assume that the contents
   * of the message is JSON encoded, so we parse the message and notify
   * the base of the new message.
   *
   * If none of the above, we consider it just a plain text message and
   * notify the base of the new message.
   *
   * @param {String} message A decoded message from the server.
   * @api private
   */
  Transport.prototype.onMessage = function(message){
    if (!this.sessionid){
      this.sessionid = message;
      this.onConnect();
    } else if (message.substr(0, 3) == '~h~'){
      this.onHeartbeat(message.substr(3));
    } else if (message.substr(0, 3) == '~j~'){
      this.base.onMessage(JSON.parse(message.substr(3)));
    } else {
      this.base.onMessage(message);
    }
  },

  /**
   * Send the received heartbeat message back to server. So the server
   * knows we are still connected.
   *
   * @param {String} heartbeat Heartbeat response from the server.
   * @api private
   */
  Transport.prototype.onHeartbeat = function(heartbeat){
    this.send('~h~' + heartbeat); // echo
  };

  /**
   * Notifies the base when a connection to the Socket.IO server has
   * been established. And it starts the connection `timeout` timer.
   *
   * @api private
   */
  Transport.prototype.onConnect = function(){
    this.connected = true;
    this.connecting = false;
    this.base.onConnect();
    this.setTimeout();
  };

  /**
   * Notifies the base when the connection with the Socket.IO server
   * has been disconnected.
   *
   * @api private
   */
  Transport.prototype.onDisconnect = function(){
    this.connecting = false;
    this.connected = false;
    this.sessionid = null;
    this.base.onDisconnect();
  };

  /**
   * Generates a connection url based on the Socket.IO URL Protocol.
   * See <https://github.com/learnboost/socket.io-node/> for more details.
   *
   * @returns {String} Connection url
   * @api private
   */
  Transport.prototype.prepareUrl = function(){
    return (this.base.options.secure ? 'https' : 'http')
      + '://' + this.base.host
      + ':' + this.base.options.port
      + '/' + this.base.options.resource
      + '/' + this.type
      + (this.sessionid ? ('/' + this.sessionid) : '/');
  };

})();<commit_msg>Refactored Transport<commit_after>
/**
 * socket.io
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

(function (exports, io) {

  /**
   * Expose constructor.
   */

  exports.Transport = Transport;

  /**
   * This is the transport template for all supported transport methods.
   *
   * @constructor
   * @api public
   */

  function Transport (socket, sessid) {
    this.socket = socket;
    this.sessid = sessid;
  };

  /**
   * Apply EventEmitter mixin.
   */

  io.util.mixin(io.EventEmitter, Transport);

  /**
   * Handles the response from the server. When a new response is received
   * it will automatically update the timeout, decode the message and
   * forwards the response to the onMessage function for further processing.
   *
   * @param {String} data Response from the server.
   * @api private
   */

  Transport.prototype.onData = function(data){
    this.clearCloseTimeout();

    if (data !== '') {
      var msgs = parser.decodePayload(data);
      if (msgs && msgs.length){
        for (var i = 0, l = msgs.length; i < l; i++){
          this.onPacket(msgs[i]);
        }
      }
    }

    return this;
  };

  /**
   * Handles packets.
   *
   * @api private
   */

  Transport.prototype.onPacket = function (packet) {
    if (packet.type == 'heartbeat') {
      return this.onHeartbeat();
    }

    if (packet.type == 'disconnect' && packet.endpoint == '') {
      return this.onDisconnect();
    }

    this.socket.onPacket(packet);
    return this;
  };

  /**
   * Sets close timeout
   *
   * @api private
   */

  Transport.prototype.setCloseTimeout = function () {
    if (!this.closeTimeout) {
      var self = this;

      this.closeTimeout = setTimeout(function () {
        self.onDisconnect();
      }, this.socket.closeTimeout);
    }
  };

  /**
   * Called when transport disconnects.
   *
   * @api private
   */

  Transport.prototype.onDisconnect = function () {
    this.close();
    this.clearTimeouts();
    this.socket.onDisconnect();
    return this;
  };

  /**
   * Clears close timeout
   *
   * @api private
   */

  Transport.prototype.clearCloseTimeout = function () {
    if (this.closeTimeout) {
      clearTimeout(this.closeTimeout);
      this.closeTimeout = null;
    }
  };

  /**
   * Clear timeouts
   *
   * @api private
   */

  Transport.prototype.clearTimeouts = function () {
    this.clearCloseTimeout();

    if (this.reopenTimeout) {
      clearTimeout(this.reopenTimeout);
    }
  };

  /**
   * Send the received heartbeat message back to server. So the server
   * knows we are still connected.
   *
   * @param {String} heartbeat Heartbeat response from the server.
   * @api private
   */

  Transport.prototype.onHeartbeat = function (heartbeat) {
    this.packet({ type: 'heartbeat' });
  };

  /**
   * Called when the transport opens.
   *
   * @api private
   */

  Transport.prototype.onOpen = function () {
    this.clearCloseTimeout();
    this.socket.onOpen();
  };

  /**
   * Notifies the base when the connection with the Socket.IO server
   * has been disconnected.
   *
   * @api private
   */

  Transport.prototype.onClose = function () {
    this.reopenTimeout = setTimeout(function () {
      this.open();
    }, this.socket.options['reopen delay']);

    this.setCloseTimeout();
    this.socket.onClose();
  };

  /**
   * Generates a connection url based on the Socket.IO URL Protocol.
   * See <https://github.com/learnboost/socket.io-node/> for more details.
   *
   * @returns {String} Connection url
   * @api private
   */

  Transport.prototype.prepareUrl = function () {
    return this.scheme()
      + '://' + this.socket.host
      + ':' + this.socket.options.port
      + '/' + this.socket.options.resource
      + '/' +  '/' + this.name
      + (this.sessionid ? ('/' + this.sessionid) : '/');
  };

})(
    'undefined' != typeof io ? io : module.exports
  , 'undefined' != typeof io ? io : module.parent.exports
);
<|endoftext|>


<commit_before>/**
 * Created by nuintun on 2015/4/27.
 */

'use strict';

var hash = require('./hash');
var cache = require('./cache');
var extname = require('path').extname;
var util = require('./util');
var debug = util.debug;

function transport(vinyl, options){
  if (options.cache) {
    // set hash
    vinyl.hash = hash(vinyl.stat);
    // get cache
    var cached = cache.get(vinyl);

    // if not cached, parse vinyl
    if (cached === null) {
      vinyl = parse(vinyl, options);

      // cache vinyl
      cache.set(vinyl);
    } else {
      vinyl = cached;

      // debug
      debug('read file from cache: %s', colors.magenta(util.pathFromCwd(vinyl.path)));
    }
  } else {
    vinyl = parse(vinyl, options);
  }

  return vinyl;
}

function parse(vinyl, options){
  var ext = extname(vinyl.path);
  var plugins = options.plugins;
  var plugin = plugins[ext.substring(1)] || plugins.other;

  // parse vinyl
  return plugin(vinyl, options);
}

/**
 * exports module.
 */
module.exports = transport;
<commit_msg>add read file from cache debug log<commit_after>/**
 * Created by nuintun on 2015/4/27.
 */

'use strict';

var hash = require('./hash');
var cache = require('./cache');
var extname = require('path').extname;
var util = require('./util');
var debug = util.debug;

function transport(vinyl, options){
  if (options.cache) {
    // set hash
    vinyl.hash = hash(vinyl.stat);
    // get cache
    var cached = cache.get(vinyl);

    // if not cached, parse vinyl
    if (cached === null) {
      vinyl = parse(vinyl, options);

      // cache vinyl
      cache.set(vinyl);
    } else {
      vinyl = cached;

      // debug
      debug('read file: %s from cache', colors.magenta(util.pathFromCwd(vinyl.path)));
    }
  } else {
    vinyl = parse(vinyl, options);
  }

  return vinyl;
}

function parse(vinyl, options){
  var ext = extname(vinyl.path);
  var plugins = options.plugins;
  var plugin = plugins[ext.substring(1)] || plugins.other;

  // parse vinyl
  return plugin(vinyl, options);
}

/**
 * exports module.
 */
module.exports = transport;
<|endoftext|>


<commit_before>var _ = require('lodash');
var blessed = require('blessed');
var path = require('path');

var util = require('slap-util');

var BaseWidget = require('base-widget');
var Slap = require('./Slap');
var Button = require('./Button');

function Header (opts) {
  var self = this;

  if (!(self instanceof Header)) return new Header(opts);

  BaseWidget.call(self, _.merge(opts.headerPosition !== 'bottom'
    ? {top: 0}
    : {bottom: 0}, {
    left: 0,
    right: 0,
    height: 1
  }, Slap.global.options.header, opts));

  self.leftContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    left: 1,
    shrink: true,
    style: self.options.style
  }, self.options.leftContent));

  var helpBinding = Slap.global.options.bindings.help;
  helpBinding = Array.isArray(helpBinding) ? helpBinding[0] : helpBinding;
  self.helpButton = new Button(_.merge({
    parent: self,
    content: "Help" + (helpBinding ? ": " + helpBinding : "")
  }, self.options.helpButton));

  self.rightContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    shrink: true,
    style: self.options.style
  }, self.options.rightContent));

  self.messageContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    shrink: true,
    style: self.options.style
  }, self.options.messageContent));

  // self._blink(true);
}
Header.prototype.__proto__ = BaseWidget.prototype;

Header.prototype._blink = util.getterSetter('blink', null, function (blink) {
  var self = this;
  clearTimeout(self.data.blinkTimer);
  if (self.options.blinkRate) {
    self.data.blinkTimer = setTimeout(function () {
      self._blink(!blink);
    }, self.options.blinkRate);
  }
  return blink;
});
Header.prototype.message = util.getterSetter('message', null, function (message, styleName) {
  var self = this;

  clearTimeout(self.data.messageTimer);
  if (message) {
    self.data.messageTimer = setTimeout(function () {
      self.message(null);
    }, self.options.messageDuration);
  }

  // self._blink(false);
  return message !== null ? util.markup(' '+message+' ', self.options.style[styleName || 'info']) : null;
});

Header.prototype._initHandlers = function () {
  var self = this;
  ['message', 'blink'].forEach(function (evt) {
    self.on(evt, function () { self.parent.render(); });
  });
  self.helpButton.on('press', function () { self.screen.slap.help(); });
  return BaseWidget.prototype._initHandlers.apply(self, arguments);
};

Header.prototype.render = function () {
  var self = this;

  var left = ["\u270b"];
  var right = [];

  var style = self.options.style;
  var slap = self.screen.slap;
  var editor = (slap.panes[slap.data.currentPane] || {}).editor;
  if (editor) {
    var originalPath = editor.textBuf.getPath();
    var markupPath = originalPath
      ? blessed.escape(path.relative(slap.fileBrowser.cwd, originalPath))
      : "new file";
    if (editor.textBuf.isModified()) {
      markupPath = util.markup(markupPath+"*", style.changed);
    }
    left.push(markupPath);

    var cursor = editor.selection.getHeadPosition();
    var originalEncoding = editor.textBuf.getEncoding();
    right = [
      [cursor.row+1, cursor.column+1].join(","),
      "("+editor.textBuf.getLineCount()+")"
    ];
    if (originalEncoding) right.push(blessed.escape(originalEncoding));
    if (editor.readOnly()) right.push(util.markup("read-only", style.warning));
    if (!editor.insertMode()) right.unshift(util.markup("OVR", style.overwrite));
  }

  self.leftContent.setContent(left.join(" "));
  self.rightContent.setContent(right.join(" "));

  var message = self.message() || "";
  if (self._blink()) message = util.markup(message, style.blinkStyle);
  self.messageContent.setContent(message.toString());

  // float: right basically
  ['helpButton', 'rightContent', 'messageContent'].reduce(function (right, key) {
    self[key].right = right;
    return 2 + right + BaseWidget.prototype.shrinkWidth.call(self[key]);
  }, 1);

  return BaseWidget.prototype.render.apply(self, arguments);
};

module.exports = Header;
<commit_msg>fix(header): screen.render -> parent.render<commit_after>var _ = require('lodash');
var blessed = require('blessed');
var path = require('path');

var util = require('slap-util');

var BaseWidget = require('base-widget');
var Slap = require('./Slap');
var Button = require('./Button');

function Header (opts) {
  var self = this;

  if (!(self instanceof Header)) return new Header(opts);

  BaseWidget.call(self, _.merge(opts.headerPosition !== 'bottom'
    ? {top: 0}
    : {bottom: 0}, {
    left: 0,
    right: 0,
    height: 1
  }, Slap.global.options.header, opts));

  self.leftContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    left: 1,
    shrink: true,
    style: self.options.style
  }, self.options.leftContent));

  var helpBinding = Slap.global.options.bindings.help;
  helpBinding = Array.isArray(helpBinding) ? helpBinding[0] : helpBinding;
  self.helpButton = new Button(_.merge({
    parent: self,
    content: "Help" + (helpBinding ? ": " + helpBinding : "")
  }, self.options.helpButton));

  self.rightContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    shrink: true,
    style: self.options.style
  }, self.options.rightContent));

  self.messageContent = new BaseWidget(_.merge({
    parent: self,
    tags: true,
    shrink: true,
    style: self.options.style
  }, self.options.messageContent));

  // self._blink(true);
}
Header.prototype.__proto__ = BaseWidget.prototype;

Header.prototype._blink = util.getterSetter('blink', null, function (blink) {
  var self = this;
  clearTimeout(self.data.blinkTimer);
  if (self.options.blinkRate) {
    self.data.blinkTimer = setTimeout(function () {
      self._blink(!blink);
    }, self.options.blinkRate);
  }
  return blink;
});
Header.prototype.message = util.getterSetter('message', null, function (message, styleName) {
  var self = this;

  clearTimeout(self.data.messageTimer);
  if (message) {
    self.data.messageTimer = setTimeout(function () {
      self.message(null);
    }, self.options.messageDuration);
  }

  // self._blink(false);
  return message !== null ? util.markup(' '+message+' ', self.options.style[styleName || 'info']) : null;
});

Header.prototype._initHandlers = function () {
  var self = this;
  ['message', 'blink'].forEach(function (evt) {
    self.on(evt, function () { self.screen.render(); });
  });
  self.helpButton.on('press', function () { self.screen.slap.help(); });
  return BaseWidget.prototype._initHandlers.apply(self, arguments);
};

Header.prototype.render = function () {
  var self = this;

  var left = ["\u270b"];
  var right = [];

  var style = self.options.style;
  var slap = self.screen.slap;
  var editor = (slap.panes[slap.data.currentPane] || {}).editor;
  if (editor) {
    var originalPath = editor.textBuf.getPath();
    var markupPath = originalPath
      ? blessed.escape(path.relative(slap.fileBrowser.cwd, originalPath))
      : "new file";
    if (editor.textBuf.isModified()) {
      markupPath = util.markup(markupPath+"*", style.changed);
    }
    left.push(markupPath);

    var cursor = editor.selection.getHeadPosition();
    var originalEncoding = editor.textBuf.getEncoding();
    right = [
      [cursor.row+1, cursor.column+1].join(","),
      "("+editor.textBuf.getLineCount()+")"
    ];
    if (originalEncoding) right.push(blessed.escape(originalEncoding));
    if (editor.readOnly()) right.push(util.markup("read-only", style.warning));
    if (!editor.insertMode()) right.unshift(util.markup("OVR", style.overwrite));
  }

  self.leftContent.setContent(left.join(" "));
  self.rightContent.setContent(right.join(" "));

  var message = self.message() || "";
  if (self._blink()) message = util.markup(message, style.blinkStyle);
  self.messageContent.setContent(message.toString());

  // float: right basically
  ['helpButton', 'rightContent', 'messageContent'].reduce(function (right, key) {
    self[key].right = right;
    return 2 + right + BaseWidget.prototype.shrinkWidth.call(self[key]);
  }, 1);

  return BaseWidget.prototype.render.apply(self, arguments);
};

module.exports = Header;
<|endoftext|>


<commit_before>'use strict';

var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } };

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } };

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var PeerGroup = require('peergroup');
var Subtree = require('subtree');
var MapMapMap = require('./data_structures/map_map_map');
var MapMapSet = require('./data_structures/map_map_set');
var MapArray = require('./data_structures/map_array');
var VectorClock = require('./vector_clock');

var interval = 200;

module.exports = (function () {
  function Ultrawave(url) {
    var _this = this;

    _classCallCheck(this, Ultrawave);

    this.handles = new Map();
    this.clocks = new Map();
    this.changes = new MapArray(); // arrays of changes [data, clock, method, args]
    this.timeouts = new MapMapMap();
    this.peerGroup = new PeerGroup(url);

    // wait for id to be assigned by server before binding to other events

    this.peerGroup.ready.then(function (id) {

      _this.id = id;

      // add peers to clock immediately

      _this.peerGroup.on(_this.peerGroup.events.peer, function (group, id) {
        var clock = _this.clocks.get(group);
        if (clock != null) {
          clock.touch(id);
        }
      });

      // respond to requests from peers

      _this.peerGroup.on('request document', function (group, id) {
        var data = _this.handles.get(group).data();
        var clock = _this.clocks.get(group);
        _this.peerGroup.sendTo(group, id, 'document', { clock: clock, data: data });
      });

      _this.peerGroup.on('request changes', function (group, id, latest) {
        latest = new VectorClock(latest);
        var changes = _this.changes.get(group);
        for (var i = changes.length - 1; i >= 0; i -= 1) {
          var _changes$i = _slicedToArray(changes[i], 4);

          var clock = _changes$i[1];
          var method = _changes$i[2];
          var args = _changes$i[3];

          if (latest.laterThan(clock)) return;
          if (clock.id === _this.id) {
            _this.peerGroup.sendTo(group, id, method, { clock: clock, args: args });
          }
        }
      });

      _this.peerGroup.on('request sync', function (group, id, _ref) {
        var author = _ref.author;
        var tick = _ref.tick;

        for (var i = changes.length - 1; i >= 0; i -= 1) {
          var _changes$i2 = _slicedToArray(changes[i], 3);

          var clock = _changes$i2[0];
          var method = _changes$i2[1];
          var args = _changes$i2[2];

          if (clock.id === author && clock[author] == tick) {
            _this.peerGroup.sendTo(group, id, method, { clock: clock, args: args });
            break;
          }
        }
      });

      // apply changes from peers

      var methods = ['set', 'delete', 'merge', 'splice'];
      methods.forEach(function (method) {
        _this.peerGroup.on(method, function (group, id, _ref2) {
          var clock = _ref2.clock;
          var args = _ref2.args;

          clock = new VectorClock(clock);
          _this._clearTimeoutFor(group, clock);
          _this._syncMissingChangesFor(id, group, clock);
          _this._applyRemoteChange(group, clock, method, args);
          _this._updateClock(group, clock);
        });
      });
    });
  }

  _createClass(Ultrawave, [{
    key: '_clearTimeoutFor',
    value: function _clearTimeoutFor(group, clock) {
      var author = clock.id;
      var tick = clock[author];
      var pendingTimeout = this.timeouts.get(group, author, tick);
      if (pendingTimeout != null) {
        clearTimeout(pendingTimeout);
        this.timeouts['delete'](group, author, tick);
      }
    }
  }, {
    key: '_syncMissingChangesFor',
    value: function _syncMissingChangesFor(sender, group, clock) {
      var _this2 = this;

      // compare clock to the current clock to identify any missing messages

      var author = clock.id;

      var _loop = function (id) {
        var tick = clock[id];
        var latest = author[id];

        if (tick - (latest || 0) > 1) {
          var _loop2 = function (i) {

            var requestSync = function requestSync() {
              // if we are still connected to the author of the change, request
              // the change from them directly, otherwise request the change from
              // the sender
              if (_this2.peerGroup.peers(group).has(author)) {
                var _peer = author;
              } else {
                var _peer2 = sender;
              }

              _this2.peerGroup.sendTo(group, peer, 'request sync', {
                author: author,
                tick: i
              });

              setSyncTimeout();
            };

            var setSyncTimeout = function setSyncTimeout() {
              var timeout = setTimeout(requestSync, interval);
              _this2.timeouts.set(group, author, tick, timeout);
            };

            setSyncTimeout();
          };

          for (var i = latest + 1; i < tick; i++) {
            _loop2(i);
          }
        }
      };

      for (var id in clock) {
        _loop(id);
      }
    }
  }, {
    key: '_updateClock',
    value: function _updateClock(group, clock) {
      this.clocks.get(group).update(clock);
    }
  }, {
    key: '_applyRemoteChange',
    value: function _applyRemoteChange(group, clock, method, args) {
      var changes = this.changes.get(group);
      var handle = this.handles.get(group);

      // if the change is in order, apply it right away and return
      if (clock.laterThan(this.clocks.get(group))) {
        handle[method].apply(null, args);
        changes.push([handle.data(), clock, method, args]);
        return;
      }

      // find the most recent change earlier than than the incoming change,
      // unless we find that the incoming change has already been applied, in
      // which case we can return without bothering to apply it again
      var index = undefined;
      var author = clock.id;
      for (index = changes.length - 1; index >= 0; index -= 1) {
        var c = changes[index][1];
        if (c.id === author && clock[author] == c[author]) {
          return;
        }if (clock.laterThan(c)) break;
      }

      // rewind the data
      handle.set([], changes[index][0]);

      // apply the incoming change and splice it in place
      handle[method].apply(null, args);
      changes.splice(index + 1, 0, [handle.data(), clock, method, args]);

      // replay changes after the incoming change
      for (index = index + 2; index < changes.length; index += 1) {
        var _changes$index = _slicedToArray(changes[index], 4);

        var _method = _changes$index[2];
        var _args = _changes$index[3];

        handle[_method].apply(null, _args);
      }
    }
  }, {
    key: '_startCursor',
    value: function _startCursor(group, initialData, cb) {
      var _this3 = this;

      var clock = this.clocks.get(group);

      var changes = [];
      this.changes.map.set(group, changes);

      var handle = Subtree.create(initialData, function (root, newChanges) {
        var data = root.get();
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = newChanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var change = _step.value;

            var _change = _slicedToArray(change, 2);

            var method = _change[0];
            var args = _change[1];

            clock.increment();
            var newClock = clock.clone();
            changes.push([data, newClock, method, args]);
            _this3.peerGroup.send(group, method, { clock: newClock, args: args });
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator['return']) {
              _iterator['return']();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

        cb(root, changes);
      });
      this.handles.set(group, handle);

      return handle;
    }
  }, {
    key: 'create',
    value: function create(group, initialData, cb) {
      var _this4 = this;

      return new Promise(function (resolve, reject) {
        _this4.peerGroup.ready.then(function (id) {
          _this4.peerGroup.create(group).then(function () {
            _this4.clocks.set(group, new VectorClock({ id: id }));
            resolve(_this4._startCursor(group, initialData, cb));
          })['catch'](reject);
        });
      });
    }
  }, {
    key: 'join',
    value: function join(group, cb) {
      var _this5 = this;

      var events = this.peerGroup.events;

      return new Promise(function (resolve, reject) {
        _this5.peerGroup.ready.then(function (id) {
          _this5.peerGroup.join(group).then(function () {
            _this5.clocks.set(group, new VectorClock({ id: id }));

            // request the current document state from the first peer we form a
            // connection to, then send 'request changes' to each new peer
            // requesting changes they have authored after that clock.
            var docRequestCandidates = [];
            var docRequestTimeout = undefined;
            var changeRequestCandidates = undefined;
            var documentClock = undefined;

            var requestDocumentRetry = (function (_requestDocumentRetry) {
              function requestDocumentRetry() {
                return _requestDocumentRetry.apply(this, arguments);
              }

              requestDocumentRetry.toString = function () {
                return _requestDocumentRetry.toString();
              };

              return requestDocumentRetry;
            })(function () {
              if (docRequestCandidates.length > 0) {
                var _peer3 = docRequestCandidates.shift();
                _this5.peerGroup.sendTo(group, _peer3, 'request document');
              }

              docRequestTimeout = setTimeout(requestDocumentRetry, interval);
            });

            var onPeer = (function (_onPeer) {
              function onPeer(_x, _x2) {
                return _onPeer.apply(this, arguments);
              }

              onPeer.toString = function () {
                return _onPeer.toString();
              };

              return onPeer;
            })(function (subjectGroup, id) {
              if (subjectGroup !== group) return;

              if (docRequestTimeout == null) {

                // request the document immediately from the first peer

                _this5.peerGroup.sendTo(group, id, 'request document');
                docRequestTimeout = setTimeout(requestDocumentRetry, interval);
              } else if (documentClock == null) {

                // if the document has not been received, keep track of peers in
                // case we need to request the document from them

                docRequestCandidates.push(id);
              } else if (changeRequestCandidates.has(id)) {

                // once the document has been recevied, request changes from
                // peers as we connect to them

                _this5.peerGroup.send(group, id, 'request changes', documentClock);
                changeRequestCandidates['delete'](id);

                if (changeRequestCandidates.size === 0) {
                  _this5.peerGroup.off(events.peer, onPeer);
                }
              }
            });

            var onDocument = (function (_onDocument) {
              function onDocument(_x3, _x4, _x5) {
                return _onDocument.apply(this, arguments);
              }

              onDocument.toString = function () {
                return _onDocument.toString();
              };

              return onDocument;
            })(function (subjectGroup, id, _ref3) {
              var clock = _ref3.clock;
              var data = _ref3.data;

              if (subjectGroup !== group) return;
              _this5.peerGroup.off(events.document, onDocument);
              clearTimeout(docRequestTimeout);

              clock = new VectorClock(clock);
              _this5._updateClock(group, clock);

              // request changes from all group members
              changeRequestCandidates = new Set(clock.keys());
              changeRequestCandidates['delete'](clock.id);
              var _iteratorNormalCompletion2 = true;
              var _didIteratorError2 = false;
              var _iteratorError2 = undefined;

              try {
                for (var _iterator2 = _this5.peerGroup.peers(group)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                  var _peer4 = _step2.value;

                  if (changeRequestCandidates['delete'](_peer4)) {
                    _this5.peerGroup.send(group, 'request changes', clock);
                  }
                }
              } catch (err) {
                _didIteratorError2 = true;
                _iteratorError2 = err;
              } finally {
                try {
                  if (!_iteratorNormalCompletion2 && _iterator2['return']) {
                    _iterator2['return']();
                  }
                } finally {
                  if (_didIteratorError2) {
                    throw _iteratorError2;
                  }
                }
              }

              resolve(_this5._startCursor(group, data, cb));
            });

            _this5.peerGroup.on(events.peer, onPeer);
            _this5.peerGroup.on('document', onDocument);
          })['catch'](reject);
        });
      });
    }
  }, {
    key: 'leave',
    value: function leave(group) {
      this.peerGroup.leave(group);
      var _iteratorNormalCompletion3 = true;
      var _didIteratorError3 = false;
      var _iteratorError3 = undefined;

      try {
        for (var _iterator3 = this.timeouts.get(group).values()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
          var timeout = _step3.value;

          clearTimeout(timeout);
        }
      } catch (err) {
        _didIteratorError3 = true;
        _iteratorError3 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion3 && _iterator3['return']) {
            _iterator3['return']();
          }
        } finally {
          if (_didIteratorError3) {
            throw _iteratorError3;
          }
        }
      }

      this.handles['delete'](group);
      this.clocks['delete'](group);
      this.changes['delete'](group);
      this.timeouts['delete'](group);
    }
  }, {
    key: 'close',
    value: function close() {
      this.peerGroup.close();
    }
  }]);

  return Ultrawave;
})();

// export cursor superclass for type checking

Ultrawave.Cursor = Subtree.Cursor;<commit_msg>build<commit_after>'use strict';

var _slicedToArray = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } };

var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } };

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

var PeerGroup = require('peergroup');
var Subtree = require('subtree');
var MapMapMap = require('./data_structures/map_map_map');
var MapMapSet = require('./data_structures/map_map_set');
var MapArray = require('./data_structures/map_array');
var VectorClock = require('./vector_clock');

var interval = 200;

var Ultrawave = (function () {
  function Ultrawave(url) {
    var _this = this;

    _classCallCheck(this, Ultrawave);

    this.handles = new Map();
    this.clocks = new Map();
    this.changes = new MapArray(); // arrays of changes [data, clock, method, args]
    this.timeouts = new MapMapMap();
    this.peerGroup = new PeerGroup(url);

    // wait for id to be assigned by server before binding to other events

    this.peerGroup.ready.then(function (id) {

      _this.id = id;

      // add peers to clock immediately

      _this.peerGroup.on(_this.peerGroup.events.peer, function (group, id) {
        var clock = _this.clocks.get(group);
        if (clock != null) {
          clock.touch(id);
        }
      });

      // respond to requests from peers

      _this.peerGroup.on('request document', function (group, id) {
        var data = _this.handles.get(group).data();
        var clock = _this.clocks.get(group);
        _this.peerGroup.sendTo(group, id, 'document', { clock: clock, data: data });
      });

      _this.peerGroup.on('request changes', function (group, id, latest) {
        latest = new VectorClock(latest);
        var changes = _this.changes.get(group);
        for (var i = changes.length - 1; i >= 0; i -= 1) {
          var _changes$i = _slicedToArray(changes[i], 4);

          var clock = _changes$i[1];
          var method = _changes$i[2];
          var args = _changes$i[3];

          if (latest.laterThan(clock)) return;
          if (clock.id === _this.id) {
            _this.peerGroup.sendTo(group, id, method, { clock: clock, args: args });
          }
        }
      });

      _this.peerGroup.on('request sync', function (group, id, _ref) {
        var author = _ref.author;
        var tick = _ref.tick;

        for (var i = changes.length - 1; i >= 0; i -= 1) {
          var _changes$i2 = _slicedToArray(changes[i], 3);

          var clock = _changes$i2[0];
          var method = _changes$i2[1];
          var args = _changes$i2[2];

          if (clock.id === author && clock[author] == tick) {
            _this.peerGroup.sendTo(group, id, method, { clock: clock, args: args });
            break;
          }
        }
      });

      // apply changes from peers

      var methods = ['set', 'delete', 'merge', 'splice'];
      methods.forEach(function (method) {
        _this.peerGroup.on(method, function (group, id, _ref2) {
          var clock = _ref2.clock;
          var args = _ref2.args;

          clock = new VectorClock(clock);
          _this._clearTimeoutFor(group, clock);
          _this._syncMissingChangesFor(id, group, clock);
          _this._applyRemoteChange(group, clock, method, args);
          _this._updateClock(group, clock);
        });
      });
    });
  }

  _createClass(Ultrawave, [{
    key: '_clearTimeoutFor',
    value: function _clearTimeoutFor(group, clock) {
      var author = clock.id;
      var tick = clock[author];
      var pendingTimeout = this.timeouts.get(group, author, tick);
      if (pendingTimeout != null) {
        clearTimeout(pendingTimeout);
        this.timeouts['delete'](group, author, tick);
      }
    }
  }, {
    key: '_syncMissingChangesFor',
    value: function _syncMissingChangesFor(sender, group, clock) {
      var _this2 = this;

      // compare clock to the current clock to identify any missing messages

      var author = clock.id;

      var _loop = function (id) {
        var tick = clock[id];
        var latest = author[id];

        if (tick - (latest || 0) > 1) {
          var _loop2 = function (i) {

            var requestSync = function requestSync() {
              // if we are still connected to the author of the change, request
              // the change from them directly, otherwise request the change from
              // the sender
              if (_this2.peerGroup.peers(group).has(author)) {
                var _peer = author;
              } else {
                var _peer2 = sender;
              }

              _this2.peerGroup.sendTo(group, peer, 'request sync', {
                author: author,
                tick: i
              });

              setSyncTimeout();
            };

            var setSyncTimeout = function setSyncTimeout() {
              var timeout = setTimeout(requestSync, interval);
              _this2.timeouts.set(group, author, tick, timeout);
            };

            setSyncTimeout();
          };

          for (var i = latest + 1; i < tick; i++) {
            _loop2(i);
          }
        }
      };

      for (var id in clock) {
        _loop(id);
      }
    }
  }, {
    key: '_updateClock',
    value: function _updateClock(group, clock) {
      this.clocks.get(group).update(clock);
    }
  }, {
    key: '_applyRemoteChange',
    value: function _applyRemoteChange(group, clock, method, args) {
      var changes = this.changes.get(group);
      var handle = this.handles.get(group);

      // if the change is in order, apply it right away and return
      if (clock.laterThan(this.clocks.get(group))) {
        handle[method].apply(null, args);
        changes.push([handle.data(), clock, method, args]);
        return;
      }

      // find the most recent change earlier than than the incoming change,
      // unless we find that the incoming change has already been applied, in
      // which case we can return without bothering to apply it again
      var index = undefined;
      var author = clock.id;
      for (index = changes.length - 1; index >= 0; index -= 1) {
        var c = changes[index][1];
        if (c.id === author && clock[author] == c[author]) {
          return;
        }if (clock.laterThan(c)) break;
      }

      // rewind the data
      handle.set([], changes[index][0]);

      // apply the incoming change and splice it in place
      handle[method].apply(null, args);
      changes.splice(index + 1, 0, [handle.data(), clock, method, args]);

      // replay changes after the incoming change
      for (index = index + 2; index < changes.length; index += 1) {
        var _changes$index = _slicedToArray(changes[index], 4);

        var _method = _changes$index[2];
        var _args = _changes$index[3];

        handle[_method].apply(null, _args);
      }
    }
  }, {
    key: '_startCursor',
    value: function _startCursor(group, initialData, cb) {
      var _this3 = this;

      var clock = this.clocks.get(group);

      var changes = [];
      this.changes.map.set(group, changes);

      var handle = Subtree.create(initialData, function (root, newChanges) {
        var data = root.get();
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = newChanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var change = _step.value;

            var _change = _slicedToArray(change, 2);

            var method = _change[0];
            var args = _change[1];

            clock.increment();
            var newClock = clock.clone();
            changes.push([data, newClock, method, args]);
            _this3.peerGroup.send(group, method, { clock: newClock, args: args });
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator['return']) {
              _iterator['return']();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

        cb(root, changes);
      });
      this.handles.set(group, handle);

      return handle;
    }
  }, {
    key: 'create',
    value: function create(group, initialData, cb) {
      var _this4 = this;

      return new Promise(function (resolve, reject) {
        _this4.peerGroup.ready.then(function (id) {
          _this4.peerGroup.create(group).then(function () {
            _this4.clocks.set(group, new VectorClock({ id: id }));
            resolve(_this4._startCursor(group, initialData, cb));
          })['catch'](reject);
        });
      });
    }
  }, {
    key: 'join',
    value: function join(group, cb) {
      var _this5 = this;

      var events = this.peerGroup.events;

      return new Promise(function (resolve, reject) {
        _this5.peerGroup.ready.then(function (id) {
          _this5.peerGroup.join(group).then(function () {
            _this5.clocks.set(group, new VectorClock({ id: id }));

            // request the current document state from the first peer we form a
            // connection to, then send 'request changes' to each new peer
            // requesting changes they have authored after that clock.
            var docRequestCandidates = [];
            var docRequestTimeout = undefined;
            var changeRequestCandidates = undefined;
            var documentClock = undefined;

            var requestDocumentRetry = (function (_requestDocumentRetry) {
              function requestDocumentRetry() {
                return _requestDocumentRetry.apply(this, arguments);
              }

              requestDocumentRetry.toString = function () {
                return _requestDocumentRetry.toString();
              };

              return requestDocumentRetry;
            })(function () {
              if (docRequestCandidates.length > 0) {
                var _peer3 = docRequestCandidates.shift();
                _this5.peerGroup.sendTo(group, _peer3, 'request document');
              }

              docRequestTimeout = setTimeout(requestDocumentRetry, interval);
            });

            var onPeer = (function (_onPeer) {
              function onPeer(_x, _x2) {
                return _onPeer.apply(this, arguments);
              }

              onPeer.toString = function () {
                return _onPeer.toString();
              };

              return onPeer;
            })(function (subjectGroup, id) {
              if (subjectGroup !== group) return;

              if (docRequestTimeout == null) {

                // request the document immediately from the first peer

                _this5.peerGroup.sendTo(group, id, 'request document');
                docRequestTimeout = setTimeout(requestDocumentRetry, interval);
              } else if (documentClock == null) {

                // if the document has not been received, keep track of peers in
                // case we need to request the document from them

                docRequestCandidates.push(id);
              } else if (changeRequestCandidates.has(id)) {

                // once the document has been recevied, request changes from
                // peers as we connect to them

                _this5.peerGroup.send(group, id, 'request changes', documentClock);
                changeRequestCandidates['delete'](id);

                if (changeRequestCandidates.size === 0) {
                  _this5.peerGroup.off(events.peer, onPeer);
                }
              }
            });

            var onDocument = (function (_onDocument) {
              function onDocument(_x3, _x4, _x5) {
                return _onDocument.apply(this, arguments);
              }

              onDocument.toString = function () {
                return _onDocument.toString();
              };

              return onDocument;
            })(function (subjectGroup, id, _ref3) {
              var clock = _ref3.clock;
              var data = _ref3.data;

              if (subjectGroup !== group) return;
              _this5.peerGroup.off(events.document, onDocument);
              clearTimeout(docRequestTimeout);

              clock = new VectorClock(clock);
              _this5._updateClock(group, clock);

              // request changes from all group members
              changeRequestCandidates = new Set(clock.keys());
              changeRequestCandidates['delete'](clock.id);
              var _iteratorNormalCompletion2 = true;
              var _didIteratorError2 = false;
              var _iteratorError2 = undefined;

              try {
                for (var _iterator2 = _this5.peerGroup.peers(group)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
                  var _peer4 = _step2.value;

                  if (changeRequestCandidates['delete'](_peer4)) {
                    _this5.peerGroup.send(group, 'request changes', clock);
                  }
                }
              } catch (err) {
                _didIteratorError2 = true;
                _iteratorError2 = err;
              } finally {
                try {
                  if (!_iteratorNormalCompletion2 && _iterator2['return']) {
                    _iterator2['return']();
                  }
                } finally {
                  if (_didIteratorError2) {
                    throw _iteratorError2;
                  }
                }
              }

              resolve(_this5._startCursor(group, data, cb));
            });

            _this5.peerGroup.on(events.peer, onPeer);
            _this5.peerGroup.on('document', onDocument);
          })['catch'](reject);
        });
      });
    }
  }, {
    key: 'leave',
    value: function leave(group) {
      this.peerGroup.leave(group);
      var _iteratorNormalCompletion3 = true;
      var _didIteratorError3 = false;
      var _iteratorError3 = undefined;

      try {
        for (var _iterator3 = this.timeouts.get(group).values()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
          var timeout = _step3.value;

          clearTimeout(timeout);
        }
      } catch (err) {
        _didIteratorError3 = true;
        _iteratorError3 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion3 && _iterator3['return']) {
            _iterator3['return']();
          }
        } finally {
          if (_didIteratorError3) {
            throw _iteratorError3;
          }
        }
      }

      this.handles['delete'](group);
      this.clocks['delete'](group);
      this.changes['delete'](group);
      this.timeouts['delete'](group);
    }
  }, {
    key: 'close',
    value: function close() {
      this.peerGroup.close();
    }
  }]);

  return Ultrawave;
})();

// export cursor superclass for type checking

Ultrawave.Cursor = Subtree.Cursor;

module.exports = Ultrawave;<|endoftext|>


<commit_before>var http = require('http');

var utilities = {
	queryAPI: function (options, cb) {
		var request = http.request(options, function (response) {
			var body = '';
			response.on('data', function (data) {
				body += data;
			});
			response.on('end', function () {
				cb(null, body);
			});
		});

		request.on('error', function (err) {
			cb(err.message, null);
		});
		request.end();
	}
}

module.exports = utilities;<commit_msg>renamed exported object<commit_after>var http = require('http');

var functions = {
	queryAPI: function (options, cb) {
		var request = http.request(options, function (response) {
			var body = '';
			response.on('data', function (data) {
				body += data;
			});
			response.on('end', function () {
				return cb(null, body);
			});
		});

		request.on('error', function (err) {
			return cb(err.message, null);
		});
		request.end();
	}
}

module.exports = functions;
<|endoftext|>