<commit_before>import moment from 'moment/src/moment';
import React from 'react';
import {ActivityIndicator, Image, StyleSheet, Text, ScrollView, View, WebView} from 'react-native';
import {Card, Divider, Subheader} from 'react-native-material-design';
import {StackNavigator} from "react-navigation";

export class MainScreen extends React.Component {
  static navigationOptions = {
    title: 'Events',
    headerTintColor: 'grey',
  };

  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      events: undefined,
    };
  }

  componentDidMount() {
    this.setState({ isLoading: true });
    this._scrapeEvents();
  }

  render() {
    if (this.state.isLoading) {
      return (
          <View style={{flex: 1, paddingTop: 50}}>
            <ActivityIndicator color="mediumslateblue" size="large"/>
          </View>
        );
    }

    return (
      <View style={styles.container}>
        <ScrollView>
          {this.renderDays()}
        </ScrollView>
      </View>
    );
  }

  renderDays() {
    return this.state.events.map(this.renderSingleDay);
  }

  renderSingleDay = (day) => {
    const renderedEvents = day.events.map(this.renderEvent);
    const humanReadableDate = moment(day.date).calendar(null, {
      lastDay: '[Yesterday]',
      sameDay: '[Today]',
      nextDay: '[Tomorrow]',
      lastWeek: '[last] dddd',
      nextWeek: 'dddd',
      sameElse: 'dddd - LL'
    });

    return (
      <View key={day.date}>
        <Subheader text={humanReadableDate} />
        {renderedEvents}
        <Divider />
      </View>
    );
  };

  renderEvent = (event) => {
    const { navigate } = this.props.navigation;
    return (
      <Card
        key={event.ticketLink}
        onPress={() => navigate('Event', { event: event })}>
        <Card.Media
          image={<Image source={{uri: event.image}}/>}
        />
        <Card.Body>
          <Text>{event.title}</Text>
          <Text>{event.startTime}</Text>
        </Card.Body>
      </Card>
    );
  };

  _scrapeEvents = () => {
    return fetch('http://165.227.75.208:9081/api/events')
      .then((response) => response.json())
      .then((response) => {
        this.setState({
          isLoading: false,
          events: response,
        });
      })
      .catch((error) => {
        console.error(error);
      });
  }
}

class EventDetailScreen extends React.Component {
  static navigationOptions = ({ navigation }) => ({
    title: `${navigation.state.params.event.title}`,
  });

  render() {
    const { event } = this.props.navigation.state.params;
    return (
      <WebView
        source={{uri: event.eventLink}}
      />
    );
  }
}

const MainScreenNavigator = StackNavigator({
  Main: { screen: MainScreen },
  Event: { screen: EventDetailScreen },
});

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <MainScreenNavigator />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
  },
});
<commit_msg>Added Refresh on pull down<commit_after>import moment from 'moment/src/moment';
import React from 'react';
import {ActivityIndicator, Image, StyleSheet, Text, ScrollView, View, WebView, RefreshControl} from 'react-native';
import {Card, Divider, Subheader} from 'react-native-material-design';
import {StackNavigator} from "react-navigation";

export class MainScreen extends React.Component {
  static navigationOptions = {
    title: 'Events',
    headerTintColor: 'grey',
  };

  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      events: undefined,
    };
  }

  componentWillMount() {
    this._refresh();
  }

  render() {
    if (this.state.isLoading) {
      return (
          <View style={{flex: 1, paddingTop: 50}}>
            <ActivityIndicator color="mediumslateblue" size="large"/>
          </View>
        );
    }

    return (
      <View style={styles.container}>
        <ScrollView
          refreshControl={
            <RefreshControl
              refreshing={this.state.isLoading}
              onRefresh={this._refresh}
            />
          }
        >
          {this.renderDays()}
        </ScrollView>
      </View>
    );
  }

  renderDays() {
    return this.state.events.map(this.renderSingleDay);
  }

  renderSingleDay = (day) => {
    const renderedEvents = day.events.map(this.renderEvent);
    const humanReadableDate = moment(day.date).calendar(null, {
      lastDay: '[Yesterday]',
      sameDay: '[Today]',
      nextDay: '[Tomorrow]',
      lastWeek: '[last] dddd',
      nextWeek: 'dddd',
      sameElse: 'dddd - LL'
    });

    return (
      <View key={day.date}>
        <Subheader text={humanReadableDate} />
        {renderedEvents}
        <Divider />
      </View>
    );
  };

  renderEvent = (event) => {
    const { navigate } = this.props.navigation;
    return (
      <Card
        key={event.ticketLink}
        onPress={() => navigate('Event', { event: event })}>
        <Card.Media
          image={<Image source={{uri: event.image}}/>}
        />
        <Card.Body>
          <Text>{event.title}</Text>
          <Text>{event.startTime}</Text>
        </Card.Body>
      </Card>
    );
  };

  _refresh = () => {
    this.setState({isLoading: true});
    this._scrapeEvents();
  };

  _scrapeEvents = () => {
    return fetch('http://165.227.75.208:9081/api/events')
      .then((response) => response.json())
      .then((response) => {
        this.setState({
          isLoading: false,
          events: response,
        });
      })
      .catch((error) => {
        console.error(error);
      });
  }
}

class EventDetailScreen extends React.Component {
  static navigationOptions = ({ navigation }) => ({
    title: `${navigation.state.params.event.title}`,
  });

  render() {
    const { event } = this.props.navigation.state.params;
    return (
      <WebView
        source={{uri: event.eventLink}}
      />
    );
  }
}

const MainScreenNavigator = StackNavigator({
  Main: { screen: MainScreen },
  Event: { screen: EventDetailScreen },
});

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <MainScreenNavigator />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "white",
  },
});
<|endoftext|>


<commit_before>var color = require('../config/color');

exports.parseEmoticons = parseEmoticons;

var emotes = {
	':absol:': 'http://cbc.pokecommunity.com/config/emoticons/absol.png',
	':arceus:': 'http://cbc.pokecommunity.com/config/emoticons/arceus.png',
	':armycat:': 'http://cbc.pokecommunity.com/config/emoticons/armycat.png',
	':azelf:': 'http://cbc.pokecommunity.com/config/emoticons/azelf.png',
	':bidoof:': 'http://cbc.pokecommunity.com/config/emoticons/bidoof.png',
	':bowie:': 'http://cbc.pokecommunity.com/config/emoticons/bowie.png',
	':bye:': 'http://cbc.pokecommunity.com/config/emoticons/bye.gif',
	':castform:': 'http://cbc.pokecommunity.com/config/emoticons/castform.png',
	':catflip:': 'http://cbc.pokecommunity.com/config/emoticons/catflip.png',
	':charizard:': 'http://cbc.pokecommunity.com/config/emoticons/charizard.png',
	':clown:': 'http://cbc.pokecommunity.com/config/emoticons/clown.png',
	':cookie:': 'http://cbc.pokecommunity.com/config/emoticons/cookie.png',
	':dk:': 'http://cbc.pokecommunity.com/config/emoticons/dk.png',
	':electrode:': 'http://cbc.pokecommunity.com/config/emoticons/electrode.png',
	':espurr:': 'http://cbc.pokecommunity.com/config/emoticons/espurr.png',
	':flirt:': 'http://cbc.pokecommunity.com/config/emoticons/flirt.png',
	':gav:': 'http://cbc.pokecommunity.com/config/emoticons/gav.png',
	':gloom:': 'http://cbc.pokecommunity.com/config/emoticons/gloom.png',
	':growlithe:': 'http://cbc.pokecommunity.com/config/emoticons/growlithe.png',
	':hamster:': 'http://cbc.pokecommunity.com/config/emoticons/hamster.png',
	':helix:': 'http://cbc.pokecommunity.com/config/emoticons/helix.png',
	':houndoom:': 'http://cbc.pokecommunity.com/config/emoticons/houndoom.png',
	':jigglypuff:': 'http://cbc.pokecommunity.com/config/emoticons/jigglypuff.png',
	':jynx:': 'http://cbc.pokecommunity.com/config/emoticons/jynx.png',
	':kappa:': 'http://cbc.pokecommunity.com/config/emoticons/kappa.png',
	':keepo:': 'http://cbc.pokecommunity.com/config/emoticons/keepo.png',
	':kermit:': 'http://cbc.pokecommunity.com/config/emoticons/kermit.png',
	':kreygasm:': 'http://cbc.pokecommunity.com/config/emoticons/kreygasm.png',
	':lapras:': 'http://cbc.pokecommunity.com/config/emoticons/lapras.png',
	':lileep:': 'http://cbc.pokecommunity.com/config/emoticons/lileep.png',
	':ludicolo:': 'http://cbc.pokecommunity.com/config/emoticons/ludicolo.png',
	':luvdisc:': 'http://cbc.pokecommunity.com/config/emoticons/luvdisc.png',
	':magikarp:': 'http://cbc.pokecommunity.com/config/emoticons/magikarp.png',
	':meganium:': 'http://cbc.pokecommunity.com/config/emoticons/meganium.png',
	':meowstic:': 'http://cbc.pokecommunity.com/config/emoticons/meowstic.png',
	':meowsticf:': 'http://cbc.pokecommunity.com/config/emoticons/meowsticf.png',
	':metagross:': 'http://cbc.pokecommunity.com/config/emoticons/metagross.png',
	':moo:': 'http://cbc.pokecommunity.com/config/emoticons/moo.gif',
	':nw:': 'http://cbc.pokecommunity.com/config/emoticons/nw.gif',
	':oddish:': 'http://cbc.pokecommunity.com/config/emoticons/oddish.png',
	':pear:': 'http://cbc.pokecommunity.com/config/emoticons/pear.png',
	':pjsalt:': 'http://cbc.pokecommunity.com/config/emoticons/pjsalt.png',
	':pogchamp:': 'http://cbc.pokecommunity.com/config/emoticons/pogchamp.png',
	':potato:': 'http://cbc.pokecommunity.com/config/emoticons/potato.png',
	':psyduck:': 'http://cbc.pokecommunity.com/config/emoticons/psyduck.png',
	':pyoshi:': 'http://cbc.pokecommunity.com/config/emoticons/pyoshi.png',
	':seduce:': 'http://cbc.pokecommunity.com/config/emoticons/seduce.png',
	':senpai:': 'http://cbc.pokecommunity.com/config/emoticons/senpai.png',
	':sims:': 'http://cbc.pokecommunity.com/config/emoticons/sims.png',
	':slowpoke:': 'http://cbc.pokecommunity.com/config/emoticons/slowpoke.png',
	':snorlax:': 'http://cbc.pokecommunity.com/config/emoticons/snorlax.png',
	':spheal:': 'http://cbc.pokecommunity.com/config/emoticons/spheal.png',
	':sri:': 'http://cbc.pokecommunity.com/config/emoticons/sri.png',
	':strut:': 'http://cbc.pokecommunity.com/config/emoticons/strut.png',
	':suicune:': 'http://cbc.pokecommunity.com/config/emoticons/suicune.png',
	':superman:': 'http://cbc.pokecommunity.com/config/emoticons/superman.png',
	':sweep:': 'http://cbc.pokecommunity.com/config/emoticons/sweep.gif',
	':taco:': 'http://cbc.pokecommunity.com/config/emoticons/taco.png',
	':vulpix:': 'http://cbc.pokecommunity.com/config/emoticons/vulpix.png',
	':weezing:': 'http://cbc.pokecommunity.com/config/emoticons/weezing.png',
	':why:': 'http://cbc.pokecommunity.com/config/emoticons/why.png',
	':wobbuffet:': 'http://cbc.pokecommunity.com/config/emoticons/wobbuffet.png',
	':wooper:': 'http://cbc.pokecommunity.com/config/emoticons/wooper.png',
	':wynaut:': 'http://cbc.pokecommunity.com/config/emoticons/wynaut.png',
	':y:': 'http://cbc.pokecommunity.com/config/emoticons/y.png',
	':yoshi:': 'http://cbc.pokecommunity.com/config/emoticons/yoshi.png'
};

var emotesKeys = Object.keys(emotes);
var patterns = [];
var metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g;

for (var i in emotes) {
	if (emotes.hasOwnProperty(i)) {
		patterns.push('(' + i.replace(metachars, '\\$&') + ')');
	}
}
var patternRegex = new RegExp(patterns.join('|'), 'g');

/**
 * Parse emoticons in message.
 *
 * @param {String} message
 * @param {Object} room
 * @param {Object} user
 * @param {Boolean} pm - returns a string if it is in private messages
 * @returns {Boolean|String}
 */
function parseEmoticons(message, room, user, pm) {
	if (typeof message !== 'string' || (!pm && room.disableEmoticons)) return false;

	var match = false;
	var len = emotesKeys.length;

	while (len--) {
		if (message && message.indexOf(emotesKeys[len]) >= 0) {
			match = true;
			break;
		}
	}

	if (!match) return false;

	// escape HTML
	message = Tools.escapeHTML(message);

	// add emotes
	message = message.replace(patternRegex, function (match) {
		var emote = emotes[match];
		if (match === ':gav:' || match === ':kermit:' || match === ':nw:' || match === ':superman:' || match === ':sweep:' || match === ':yoshi:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':bowie:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':catflip:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':strut:') return typeof emote != 'undefined' ?
			'' :
			match;
		return typeof emote != 'undefined' ?
			'' :
			match;
	});

	// __italics__
	message = message.replace(/\_\_([^< ](?:[^<]*?[^< ])?)\_\_(?![^<]*?<\/a)/g, '$1');

	// **bold**
	message = message.replace(/\*\*([^< ](?:[^<]*?[^< ])?)\*\*/g, '$1');

	var group = user.getIdentity().charAt(0);
	if (room.auth) group = room.auth[user.userid] || group;

	var style = "background:none;border:0;padding:0 5px 0 0;font-family:Verdana,Helvetica,Arial,sans-serif;font-size:9pt;cursor:pointer";

	message = "
" + "" + group + "" + "<button name='parseCommand' value='/user " + user.name + "' style='" + style + "'>" + "" + user.name + ":" + "</button>" + message + "
";
	if (pm) return message;

	room.addRaw(message);

	return true;
}

/**
 * Create a two column table listing emoticons.
 *
 * @return {String} emotes table
 */
function create_table() {
	var emotes_name = Object.keys(emotes);
	var emotes_list = [];
	var emotes_group_list = [];
	var len = emotes_name.length;

	for (var i = 0; i < len; i++) {
		emotes_list.push("" +
			"" +
			emotes_name[i] + "	");
	}

	var emotes_list_right = emotes_list.splice(len / 2, len / 2);

	for (var i = 0; i < len / 2; i++) {
		var emote1 = emotes_list[i],
			emote2 = emotes_list_right[i];
		if (emote2) {
			emotes_group_list.push("
" + emote1 + emote2 + "
");
		} else {
			emotes_group_list.push("
" + emote1 + "
");
		}
	}

	return "List of Emoticons" + "
" + "
" + emotes_group_list.join("") + "
" + "
";
}

var emotes_table = create_table();

exports.commands = {
	blockemote: 'blockemoticons',
	blockemotes: 'blockemoticons',
	blockemoticon: 'blockemoticons',
	blockemoticons: function (target, room, user) {
		if (user.blockEmoticons === (target || true)) return this.sendReply("You are already blocking emoticons in private messages! To unblock, use /unblockemoticons");
		user.blockEmoticons = true;
		return this.sendReply("You are now blocking emoticons in private messages.");
	},
	blockemoticonshelp: ["/blockemoticons - Blocks emoticons in private messages. Unblock them with /unblockemoticons."],

	unblockemote: 'unblockemoticons',
	unblockemotes: 'unblockemoticons',
	unblockemoticon: 'unblockemoticons',
	unblockemoticons: function (target, room, user) {
		if (!user.blockEmoticons) return this.sendReply("You are not blocking emoticons in private messages! To block, use /blockemoticons");
		user.blockEmoticons = false;
		return this.sendReply("You are no longer blocking emoticons in private messages.");
	},
	unblockemoticonshelp: ["/unblockemoticons - Unblocks emoticons in private messages. Block them with /blockemoticons."],

	emotes: 'emoticons',
	emoticons: function (target, room, user) {
		if (!this.canBroadcast()) return;
		this.sendReplyBox("- List of Emoticons");
	},
	emoticonshelp: ["/emoticons - Get a list of emoticons."],

	toggleemote: 'toggleemoticons',
	toggleemotes: 'toggleemoticons',
	toggleemoticons: function (target, room, user) {
		if (!this.can('declare', null, room)) return false;
		room.disableEmoticons = !room.disableEmoticons;
		this.sendReply("Disallowing emoticons is set to " + room.disableEmoticons + " in this room.");
		if (room.disableEmoticons) {
			this.add("|raw|
Emoticons are disabled!
Emoticons will not work.
");
		} else {
			this.add("|raw|
Emoticons are enabled!
Emoticons will work now.
");
		}
	},
	toggleemoticonshelp: ["/toggleemoticons - Toggle emoticons on or off."]
};
<commit_msg>Add /emoticon alias<commit_after>var color = require('../config/color');

exports.parseEmoticons = parseEmoticons;

var emotes = {
	':absol:': 'http://cbc.pokecommunity.com/config/emoticons/absol.png',
	':arceus:': 'http://cbc.pokecommunity.com/config/emoticons/arceus.png',
	':armycat:': 'http://cbc.pokecommunity.com/config/emoticons/armycat.png',
	':azelf:': 'http://cbc.pokecommunity.com/config/emoticons/azelf.png',
	':bidoof:': 'http://cbc.pokecommunity.com/config/emoticons/bidoof.png',
	':bowie:': 'http://cbc.pokecommunity.com/config/emoticons/bowie.png',
	':bye:': 'http://cbc.pokecommunity.com/config/emoticons/bye.gif',
	':castform:': 'http://cbc.pokecommunity.com/config/emoticons/castform.png',
	':catflip:': 'http://cbc.pokecommunity.com/config/emoticons/catflip.png',
	':charizard:': 'http://cbc.pokecommunity.com/config/emoticons/charizard.png',
	':clown:': 'http://cbc.pokecommunity.com/config/emoticons/clown.png',
	':cookie:': 'http://cbc.pokecommunity.com/config/emoticons/cookie.png',
	':dk:': 'http://cbc.pokecommunity.com/config/emoticons/dk.png',
	':electrode:': 'http://cbc.pokecommunity.com/config/emoticons/electrode.png',
	':espurr:': 'http://cbc.pokecommunity.com/config/emoticons/espurr.png',
	':flirt:': 'http://cbc.pokecommunity.com/config/emoticons/flirt.png',
	':gav:': 'http://cbc.pokecommunity.com/config/emoticons/gav.png',
	':gloom:': 'http://cbc.pokecommunity.com/config/emoticons/gloom.png',
	':growlithe:': 'http://cbc.pokecommunity.com/config/emoticons/growlithe.png',
	':hamster:': 'http://cbc.pokecommunity.com/config/emoticons/hamster.png',
	':helix:': 'http://cbc.pokecommunity.com/config/emoticons/helix.png',
	':houndoom:': 'http://cbc.pokecommunity.com/config/emoticons/houndoom.png',
	':jigglypuff:': 'http://cbc.pokecommunity.com/config/emoticons/jigglypuff.png',
	':jynx:': 'http://cbc.pokecommunity.com/config/emoticons/jynx.png',
	':kappa:': 'http://cbc.pokecommunity.com/config/emoticons/kappa.png',
	':keepo:': 'http://cbc.pokecommunity.com/config/emoticons/keepo.png',
	':kermit:': 'http://cbc.pokecommunity.com/config/emoticons/kermit.png',
	':kreygasm:': 'http://cbc.pokecommunity.com/config/emoticons/kreygasm.png',
	':lapras:': 'http://cbc.pokecommunity.com/config/emoticons/lapras.png',
	':lileep:': 'http://cbc.pokecommunity.com/config/emoticons/lileep.png',
	':ludicolo:': 'http://cbc.pokecommunity.com/config/emoticons/ludicolo.png',
	':luvdisc:': 'http://cbc.pokecommunity.com/config/emoticons/luvdisc.png',
	':magikarp:': 'http://cbc.pokecommunity.com/config/emoticons/magikarp.png',
	':meganium:': 'http://cbc.pokecommunity.com/config/emoticons/meganium.png',
	':meowstic:': 'http://cbc.pokecommunity.com/config/emoticons/meowstic.png',
	':meowsticf:': 'http://cbc.pokecommunity.com/config/emoticons/meowsticf.png',
	':metagross:': 'http://cbc.pokecommunity.com/config/emoticons/metagross.png',
	':moo:': 'http://cbc.pokecommunity.com/config/emoticons/moo.gif',
	':nw:': 'http://cbc.pokecommunity.com/config/emoticons/nw.gif',
	':oddish:': 'http://cbc.pokecommunity.com/config/emoticons/oddish.png',
	':pear:': 'http://cbc.pokecommunity.com/config/emoticons/pear.png',
	':pjsalt:': 'http://cbc.pokecommunity.com/config/emoticons/pjsalt.png',
	':pogchamp:': 'http://cbc.pokecommunity.com/config/emoticons/pogchamp.png',
	':potato:': 'http://cbc.pokecommunity.com/config/emoticons/potato.png',
	':psyduck:': 'http://cbc.pokecommunity.com/config/emoticons/psyduck.png',
	':pyoshi:': 'http://cbc.pokecommunity.com/config/emoticons/pyoshi.png',
	':seduce:': 'http://cbc.pokecommunity.com/config/emoticons/seduce.png',
	':senpai:': 'http://cbc.pokecommunity.com/config/emoticons/senpai.png',
	':sims:': 'http://cbc.pokecommunity.com/config/emoticons/sims.png',
	':slowpoke:': 'http://cbc.pokecommunity.com/config/emoticons/slowpoke.png',
	':snorlax:': 'http://cbc.pokecommunity.com/config/emoticons/snorlax.png',
	':spheal:': 'http://cbc.pokecommunity.com/config/emoticons/spheal.png',
	':sri:': 'http://cbc.pokecommunity.com/config/emoticons/sri.png',
	':strut:': 'http://cbc.pokecommunity.com/config/emoticons/strut.png',
	':suicune:': 'http://cbc.pokecommunity.com/config/emoticons/suicune.png',
	':superman:': 'http://cbc.pokecommunity.com/config/emoticons/superman.png',
	':sweep:': 'http://cbc.pokecommunity.com/config/emoticons/sweep.gif',
	':taco:': 'http://cbc.pokecommunity.com/config/emoticons/taco.png',
	':vulpix:': 'http://cbc.pokecommunity.com/config/emoticons/vulpix.png',
	':weezing:': 'http://cbc.pokecommunity.com/config/emoticons/weezing.png',
	':why:': 'http://cbc.pokecommunity.com/config/emoticons/why.png',
	':wobbuffet:': 'http://cbc.pokecommunity.com/config/emoticons/wobbuffet.png',
	':wooper:': 'http://cbc.pokecommunity.com/config/emoticons/wooper.png',
	':wynaut:': 'http://cbc.pokecommunity.com/config/emoticons/wynaut.png',
	':y:': 'http://cbc.pokecommunity.com/config/emoticons/y.png',
	':yoshi:': 'http://cbc.pokecommunity.com/config/emoticons/yoshi.png'
};

var emotesKeys = Object.keys(emotes);
var patterns = [];
var metachars = /[[\]{}()*+?.\\|^$\-,&#\s]/g;

for (var i in emotes) {
	if (emotes.hasOwnProperty(i)) {
		patterns.push('(' + i.replace(metachars, '\\$&') + ')');
	}
}
var patternRegex = new RegExp(patterns.join('|'), 'g');

/**
 * Parse emoticons in message.
 *
 * @param {String} message
 * @param {Object} room
 * @param {Object} user
 * @param {Boolean} pm - returns a string if it is in private messages
 * @returns {Boolean|String}
 */
function parseEmoticons(message, room, user, pm) {
	if (typeof message !== 'string' || (!pm && room.disableEmoticons)) return false;

	var match = false;
	var len = emotesKeys.length;

	while (len--) {
		if (message && message.indexOf(emotesKeys[len]) >= 0) {
			match = true;
			break;
		}
	}

	if (!match) return false;

	// escape HTML
	message = Tools.escapeHTML(message);

	// add emotes
	message = message.replace(patternRegex, function (match) {
		var emote = emotes[match];
		if (match === ':gav:' || match === ':kermit:' || match === ':nw:' || match === ':superman:' || match === ':sweep:' || match === ':yoshi:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':bowie:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':catflip:') return typeof emote != 'undefined' ?
			'' :
			match;
		if (match === ':strut:') return typeof emote != 'undefined' ?
			'' :
			match;
		return typeof emote != 'undefined' ?
			'' :
			match;
	});

	// __italics__
	message = message.replace(/\_\_([^< ](?:[^<]*?[^< ])?)\_\_(?![^<]*?<\/a)/g, '$1');

	// **bold**
	message = message.replace(/\*\*([^< ](?:[^<]*?[^< ])?)\*\*/g, '$1');

	var group = user.getIdentity().charAt(0);
	if (room.auth) group = room.auth[user.userid] || group;

	var style = "background:none;border:0;padding:0 5px 0 0;font-family:Verdana,Helvetica,Arial,sans-serif;font-size:9pt;cursor:pointer";

	message = "
" + "" + group + "" + "<button name='parseCommand' value='/user " + user.name + "' style='" + style + "'>" + "" + user.name + ":" + "</button>" + message + "
";
	if (pm) return message;

	room.addRaw(message);

	return true;
}

/**
 * Create a two column table listing emoticons.
 *
 * @return {String} emotes table
 */
function create_table() {
	var emotes_name = Object.keys(emotes);
	var emotes_list = [];
	var emotes_group_list = [];
	var len = emotes_name.length;

	for (var i = 0; i < len; i++) {
		emotes_list.push("" +
			"" +
			emotes_name[i] + "	");
	}

	var emotes_list_right = emotes_list.splice(len / 2, len / 2);

	for (var i = 0; i < len / 2; i++) {
		var emote1 = emotes_list[i],
			emote2 = emotes_list_right[i];
		if (emote2) {
			emotes_group_list.push("
" + emote1 + emote2 + "
");
		} else {
			emotes_group_list.push("
" + emote1 + "
");
		}
	}

	return "List of Emoticons" + "
" + "
" + emotes_group_list.join("") + "
" + "
";
}

var emotes_table = create_table();

exports.commands = {
	blockemote: 'blockemoticons',
	blockemotes: 'blockemoticons',
	blockemoticon: 'blockemoticons',
	blockemoticons: function (target, room, user) {
		if (user.blockEmoticons === (target || true)) return this.sendReply("You are already blocking emoticons in private messages! To unblock, use /unblockemoticons");
		user.blockEmoticons = true;
		return this.sendReply("You are now blocking emoticons in private messages.");
	},
	blockemoticonshelp: ["/blockemoticons - Blocks emoticons in private messages. Unblock them with /unblockemoticons."],

	unblockemote: 'unblockemoticons',
	unblockemotes: 'unblockemoticons',
	unblockemoticon: 'unblockemoticons',
	unblockemoticons: function (target, room, user) {
		if (!user.blockEmoticons) return this.sendReply("You are not blocking emoticons in private messages! To block, use /blockemoticons");
		user.blockEmoticons = false;
		return this.sendReply("You are no longer blocking emoticons in private messages.");
	},
	unblockemoticonshelp: ["/unblockemoticons - Unblocks emoticons in private messages. Block them with /blockemoticons."],

	emotes: 'emoticons',
	emoticon: 'emoticons',
	emoticons: function (target, room, user) {
		if (!this.canBroadcast()) return;
		this.sendReplyBox("- List of Emoticons");
	},
	emoticonshelp: ["/emoticons - Get a list of emoticons."],

	toggleemote: 'toggleemoticons',
	toggleemotes: 'toggleemoticons',
	toggleemoticons: function (target, room, user) {
		if (!this.can('declare', null, room)) return false;
		room.disableEmoticons = !room.disableEmoticons;
		this.sendReply("Disallowing emoticons is set to " + room.disableEmoticons + " in this room.");
		if (room.disableEmoticons) {
			this.add("|raw|
Emoticons are disabled!
Emoticons will not work.
");
		} else {
			this.add("|raw|
Emoticons are enabled!
Emoticons will work now.
");
		}
	},
	toggleemoticonshelp: ["/toggleemoticons - Toggle emoticons on or off."]
};
<|endoftext|>


<commit_before>var C = require("../constants").default
import rootRef from '../libs/firebase';
const sessionsRef = rootRef.child('savedSessions');

export default {
  get: function(sessionID, cb){
    sessionsRef.child(sessionID).once("value", (snapshot) => {
      if (snapshot.exists()) {
        const session = snapshot.val();
        if (session.currentQuestion.question) {
          session.currentQuestion.question.attempts = [];
        } else {
          session.currentQuestion.data.attempts = [];
        }
        cb(session);
      }
    })
  },

  update: function(sessionID, session){
    sessionsRef.child(sessionID).set(session)
  },

  delete: function(sessionID){
    sessionsRef.child(sessionID).remove(session)
  },

}
<commit_msg>fix bugs with sessions<commit_after>var C = require("../constants").default
import rootRef from '../libs/firebase';
import _ from 'lodash';
const sessionsRef = rootRef.child('savedSessions');

export default {
  get: function(sessionID, cb){
    sessionsRef.child(sessionID).once("value", (snapshot) => {
      if (snapshot.exists()) {
        const session = snapshot.val();
        if (session.currentQuestion) {
          if (session.currentQuestion.question) {
            session.currentQuestion.question.attempts = [];
          } else {
            session.currentQuestion.data.attempts = [];
          }
        }
        cb(session);
      }
    })
  },

  update: function(sessionID, session){
    // delete_null_properties(session, true)
    // console.log(session);
    console.log(_.pickBy(session))
    sessionsRef.child(sessionID).set(_.pickBy(session))
  },

  delete: function(sessionID){
    sessionsRef.child(sessionID).remove()
  },

}

function delete_null_properties(test, recurse) {
    for (var i in test) {
        if (test[i] === null) {
            delete test[i];
        } else if (recurse && typeof test[i] === 'object') {
            delete_null_properties(test[i], recurse);
        }
    }
    return
}
<|endoftext|>


<commit_before>import React               from 'react';
import SearchAppDispatcher from '../dispatcher/SearchAppDispatcher';
import ActionTypes         from '../constants/ActionTypes';
import TranscriptSearchAPI from '../utils/TranscriptSearchAPI';
import TranscriptStore     from '../stores/TranscriptStore';

var Header        = React.createFactory(require('./Header'));
var SearchForm    = React.createFactory(require('./SearchForm'));
var Timeline      = React.createFactory(require('./Timeline'));
var ResultDetails = React.createFactory(require('./ResultDetails'));
var SpeechModal   = React.createFactory(require('./SpeechModal'));
var DevPanel      = React.createFactory(require('./DevPanel'));

var {div} = React.DOM;

// really not sure if this belongs here
SearchAppDispatcher.register(function (payload) {
    switch (payload.action.type) {
        case ActionTypes.SEARCH:
            TranscriptSearchAPI.search(payload.action.query, payload.action.interval);
            var searchPath = `/search/${encodeURIComponent(payload.action.query)}`;

            if (window.location.pathname !== searchPath) {
                window.history.pushState(
                    payload.action,
                    payload.action.query,
                    searchPath
                );
            }

            break;
        case ActionTypes.SPEECH_CONTEXT:
            TranscriptSearchAPI.speechContext(payload.action.transcript, payload.action.start, payload.action.end);
            break;
        case ActionTypes.RESET:
            window.history.pushState(null, null, '/');
            break;
        default:
        // nothing
    }
});

class SearchApp extends React.Component {
    constructor(props) {
        super(props);

        this.state             = this.fetchStateFromStore();
        this.state.unit        = 'pct';
        this.state.devPanel    = { visible: true };
        this.state.orientation = 'horizontal';
        this.state.interval    = '24w';
    }

    componentDidMount() {
        TranscriptStore.addChangeListener(this.handleChange.bind(this));
        window.addEventListener('popstate', this.handleStateChange.bind(this));

        // TODO: use keymaster to provide some instructions on '?'
        // TODO: use keymaster to toggle dev panel

        SearchAppDispatcher.handleViewAction({
            type: ActionTypes.SEARCH,
            query: this.initialQuery(),
            interval: this.state.interval
        });
    }

    componentWillUnmount() {
        TranscriptStore.removeChangeListener(this.handleChange.bind(this));
    }

    handleChange() {
        this.setState(this.fetchStateFromStore());
    }

    handleUnitChange(event) {
        this.setState({
            unit: event.target.value === '%' ? 'pct' : 'count'
        });
    }

    handleStateChange(event) {
        if (event.state) {
            SearchAppDispatcher.handleViewAction({
                type: ActionTypes.SEARCH,
                query: event.state.query,
                interval: event.state.interval
            });
        }
    }

    fetchStateFromStore() {
        return {
            query: TranscriptStore.getQuery(),
            result: TranscriptStore.getResult()
        };
    }

    initialQuery() {
        let match = window.location.pathname.match(/search\/(.+?)(\/|$)/);
        return match ? decodeURIComponent(match[1]) : 'skatt';
    }

    render() {
        return div({},
            Header(),
            div({className: 'container'},
                SearchForm({
                    interval: this.state.interval
                }),
                Timeline({
                    unit: this.state.unit,
                    query: this.state.query,
                    result: this.state.result,
                    onUnitChange: this.handleUnitChange.bind(this)
                }),
                ResultDetails({
                    unit: this.state.unit,
                    query: this.state.query,
                    result: this.state.result,
                    orientation: this.state.orientation
                }),
                SpeechModal(),
                DevPanel({
                    visible: this.state.devPanel.visible,
                    orientation: this.state.orientation,
                    interval: this.state.interval,
                    onOrientationChange: this.handleOrientationChange.bind(this),
                    onIntervalChange: this.handleIntervalChange.bind(this)
                })
            )
        );
    }

    handleOrientationChange(event) {
        this.setState({
            orientation: event.target.value
        });
    }

    handleIntervalChange(event) {
        this.setState({
            interval: event.target.value
        });
    }
}

module.exports = SearchApp;

<commit_msg>Toggle dev panel.<commit_after>import React               from 'react';
import SearchAppDispatcher from '../dispatcher/SearchAppDispatcher';
import ActionTypes         from '../constants/ActionTypes';
import TranscriptSearchAPI from '../utils/TranscriptSearchAPI';
import TranscriptStore     from '../stores/TranscriptStore';
import keymaster           from 'keymaster';

var Header        = React.createFactory(require('./Header'));
var SearchForm    = React.createFactory(require('./SearchForm'));
var Timeline      = React.createFactory(require('./Timeline'));
var ResultDetails = React.createFactory(require('./ResultDetails'));
var SpeechModal   = React.createFactory(require('./SpeechModal'));
var DevPanel      = React.createFactory(require('./DevPanel'));

var {div} = React.DOM;

// really not sure if this belongs here
SearchAppDispatcher.register(function (payload) {
    switch (payload.action.type) {
        case ActionTypes.SEARCH:
            TranscriptSearchAPI.search(payload.action.query, payload.action.interval);
            var searchPath = `/search/${encodeURIComponent(payload.action.query)}`;

            if (window.location.pathname !== searchPath) {
                window.history.pushState(
                    payload.action,
                    payload.action.query,
                    searchPath
                );
            }

            break;
        case ActionTypes.SPEECH_CONTEXT:
            TranscriptSearchAPI.speechContext(payload.action.transcript, payload.action.start, payload.action.end);
            break;
        case ActionTypes.RESET:
            window.history.pushState(null, null, '/');
            break;
        default:
        // nothing
    }
});

class SearchApp extends React.Component {
    constructor(props) {
        super(props);

        this.state             = this.fetchStateFromStore();
        this.state.unit        = 'pct';
        this.state.devPanel    = { visible: true };
        this.state.orientation = 'horizontal';
        this.state.interval    = '24w';
    }

    componentDidMount() {
        TranscriptStore.addChangeListener(this.handleChange.bind(this));
        window.addEventListener('popstate', this.handleStateChange.bind(this));

        // TODO: use keymaster to provide some instructions on '?'

        keymaster('ctrl+`', () => {
            this.setState({devPanel: {visible: !this.state.devPanel.visible}});
        });

        SearchAppDispatcher.handleViewAction({
            type: ActionTypes.SEARCH,
            query: this.initialQuery(),
            interval: this.state.interval
        });
    }

    componentWillUnmount() {
        TranscriptStore.removeChangeListener(this.handleChange.bind(this));
    }

    handleChange() {
        this.setState(this.fetchStateFromStore());
    }

    handleUnitChange(event) {
        this.setState({
            unit: event.target.value === '%' ? 'pct' : 'count'
        });
    }

    handleStateChange(event) {
        if (event.state) {
            SearchAppDispatcher.handleViewAction({
                type: ActionTypes.SEARCH,
                query: event.state.query,
                interval: event.state.interval
            });
        }
    }

    fetchStateFromStore() {
        return {
            query: TranscriptStore.getQuery(),
            result: TranscriptStore.getResult()
        };
    }

    initialQuery() {
        let match = window.location.pathname.match(/search\/(.+?)(\/|$)/);
        return match ? decodeURIComponent(match[1]) : 'skatt';
    }

    render() {
        return div({},
            Header(),
            div({className: 'container'},
                SearchForm({
                    interval: this.state.interval
                }),
                Timeline({
                    unit: this.state.unit,
                    query: this.state.query,
                    result: this.state.result,
                    onUnitChange: this.handleUnitChange.bind(this)
                }),
                ResultDetails({
                    unit: this.state.unit,
                    query: this.state.query,
                    result: this.state.result,
                    orientation: this.state.orientation
                }),
                SpeechModal(),
                DevPanel({
                    visible: this.state.devPanel.visible,
                    orientation: this.state.orientation,
                    interval: this.state.interval,
                    onOrientationChange: this.handleOrientationChange.bind(this),
                    onIntervalChange: this.handleIntervalChange.bind(this)
                })
            )
        );
    }

    handleOrientationChange(event) {
        this.setState({
            orientation: event.target.value
        });
    }

    handleIntervalChange(event) {
        this.setState({
            interval: event.target.value
        });
    }
}

module.exports = SearchApp;

<|endoftext|>


<commit_before>			function clean_values( to_clean )
			{
				$('#form-error-message').hide();
				to_clean = ( typeof to_clean != 'undefined' && to_clean instanceof Array ) ? to_clean : [];
				$.each(to_clean, function(i, obj)
				{
					$(obj).val('');
					$(obj+"_div").text('');
				});
			}

			function validate_form()
			{
			  var trueCount=-1;
        var falseCount=-1;
        var what_to_test = window.to_validate;
     		$.each(what_to_test, function (i,v)
     		  {
   		  	var saveval = $(v+'_div').text();
   		  	$(v+'_div').text($(v).val());
          if (valid_value.call(this,v)) {
            trueCount++;
          }
          else {
            falseCount++;
          }
   		  	$(this+'_div').text(saveval);
   		  	saveval='';
          });

          if (falseCount >= 0)
          {
				    return false;
          }
          if (trueCount >= 0)
          {
				    return true;
          }
          return false;
			}

      var _onPaste_StripFormatting_IEPaste = false;
      function OnPaste_StripFormatting(elem, e) {

          if (e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
              e.preventDefault();
              var text = e.originalEvent.clipboardData.getData('text/plain');
              window.document.execCommand('insertText', false, text);
          }
          else if (e.clipboardData && e.clipboardData.getData) {
              e.preventDefault();
              var text = e.clipboardData.getData('text/plain');
              window.document.execCommand('insertText', false, text);
          }
          else if (window.clipboardData && window.clipboardData.getData) {
              // Stop stack overflow
              if (!_onPaste_StripFormatting_IEPaste) {
                  _onPaste_StripFormatting_IEPaste = true;
                  e.preventDefault();
                  window.document.execCommand('ms-pasteTextOnly', false);
              }
              _onPaste_StripFormatting_IEPaste = false;
          }

      }
			function enable_validation( object )
			{
				window.to_validate = ( typeof window.to_validate != 'undefined' && window.to_validate instanceof Array ) ? window.to_validate : [];
				$( $(object).closest('form') ).submit(function(e)
				{
					if (!validate_form())
					{
						e.preventDefault();
					}
				});

				$(object+'_div').on('paste', function(e)
				{
					OnPaste_StripFormatting(this, event);
				});

				$(object+'_div').on('blur keyup input focusin', function(e)
				{
					$(object).attr('value',$(this).text());
					if (e.type == "blur" )
					{
						$(this).removeClass("ui-focus").addClass("ui-shadow-inset");
					}
					else
					{
						$(this).removeClass("ui-shadow-inset").addClass("ui-focus");
					}
					if ( $(this).hasClass('param_ok') )
					{
						$(this).removeClass('param_ok').addClass('param_ok');
					}
					else
					{
						$(this).removeClass('param_error').addClass('param_error');
					}

					valid_value( object,e );
				});
			}
			function valid_value( object,evt,rule )
			{
				evt = evt || 0;
				var rule = rule || $(object).attr("data-validation-rule");
				rule = rule || "(?=a)b";
				rule = new RegExp(rule);
				if( !rule.test( $(object+'_div').text() )  )
				{
					if (evt.type == "blur"  || evt === 0 )
					{
						$('#form-error-message').html( $(object).attr('data-validation-error-msg'));
						var offset = $(object+'_div').offset();
						$('#form-error-message').fadeIn(400);
						$('#form-error-message').css({'padding': '5px', 'border': '1px solid #FF0000', 'border-radius': '5px', 'color': '#FF0000', 'background-color': '#FFFFC0', 'z-index': 1000, 'top': offset.top - 35, 'left': offset.left, 'position':'absolute'});
						setTimeout( function() { $('#form-error-message').fadeOut(400); }, 3000);
					}
						$(object+'_div').removeClass('param_ok').addClass('param_error');
						$(object).val('');
					return false
				}
				else
				{
					$(object+'_div').removeClass('param_error').addClass('param_ok');
					$(object).val($(object+'_div').text());
					return true
				}
			}
<commit_msg>Validate: Init value on focus<commit_after>			function clean_values( to_clean )
			{
				$('#form-error-message').hide();
				to_clean = ( typeof to_clean != 'undefined' && to_clean instanceof Array ) ? to_clean : [];
				$.each(to_clean, function(i, obj)
				{
					$(obj).val('');
					$(obj+"_div").text('');
				});
			}

			function validate_form()
			{
			  var trueCount=-1;
        var falseCount=-1;
        var what_to_test = window.to_validate;
     		$.each(what_to_test, function (i,v)
     		  {
   		  	var saveval = $(v+'_div').text();
   		  	$(v+'_div').text($(v).val());
          if (valid_value.call(this,v)) {
            trueCount++;
          }
          else {
            falseCount++;
          }
   		  	$(this+'_div').text(saveval);
   		  	saveval='';
          });

          if (falseCount >= 0)
          {
				    return false;
          }
          if (trueCount >= 0)
          {
				    return true;
          }
          return false;
			}

      var _onPaste_StripFormatting_IEPaste = false;
      function OnPaste_StripFormatting(elem, e) {

          if (e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.getData) {
              e.preventDefault();
              var text = e.originalEvent.clipboardData.getData('text/plain');
              window.document.execCommand('insertText', false, text);
          }
          else if (e.clipboardData && e.clipboardData.getData) {
              e.preventDefault();
              var text = e.clipboardData.getData('text/plain');
              window.document.execCommand('insertText', false, text);
          }
          else if (window.clipboardData && window.clipboardData.getData) {
              // Stop stack overflow
              if (!_onPaste_StripFormatting_IEPaste) {
                  _onPaste_StripFormatting_IEPaste = true;
                  e.preventDefault();
                  window.document.execCommand('ms-pasteTextOnly', false);
              }
              _onPaste_StripFormatting_IEPaste = false;
          }

      }
			function enable_validation( object )
			{
				window.to_validate = ( typeof window.to_validate != 'undefined' && window.to_validate instanceof Array ) ? window.to_validate : [];
				$( $(object).closest('form') ).submit(function(e)
				{
					if (!validate_form())
					{
						e.preventDefault();
					}
				});
				$(object+'_div').on('paste', function(e)
				{
					OnPaste_StripFormatting(this, event);
				});

				$(object+'_div').on('blur keyup input focusin', function(e)
				{
					$(object).attr('value',$(this).text());
					if (e.type == "focusin" )
					{
						this.text($(object+'_div').text());
					}
					if (e.type == "blur" )
					{
						$(this).removeClass("ui-focus").addClass("ui-shadow-inset");
					}
					else
					{
						$(this).removeClass("ui-shadow-inset").addClass("ui-focus");
					}
					if ( $(this).hasClass('param_ok') )
					{
						$(this).removeClass('param_ok').addClass('param_ok');
					}
					else
					{
						$(this).removeClass('param_error').addClass('param_error');
					}

					valid_value( object,e );
				});
			}
			function valid_value( object,evt,rule )
			{
				evt = evt || 0;
				var rule = rule || $(object).attr("data-validation-rule");
				rule = rule || "(?=a)b";
				rule = new RegExp(rule);
				if( !rule.test( $(object+'_div').text() )  )
				{
					if (evt.type == "blur"  || evt === 0 )
					{
						$('#form-error-message').html( $(object).attr('data-validation-error-msg'));
						var offset = $(object+'_div').offset();
						$('#form-error-message').fadeIn(400);
						$('#form-error-message').css({'padding': '5px', 'border': '1px solid #FF0000', 'border-radius': '5px', 'color': '#FF0000', 'background-color': '#FFFFC0', 'z-index': 1000, 'top': offset.top - 35, 'left': offset.left, 'position':'absolute'});
						setTimeout( function() { $('#form-error-message').fadeOut(400); }, 3000);
					}
						$(object+'_div').removeClass('param_ok').addClass('param_error');
						$(object).val('');
					return false
				}
				else
				{
					$(object+'_div').removeClass('param_error').addClass('param_ok');
					$(object).val($(object+'_div').text());
					return true
				}
			}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import Message from '../../../models/messaging-message';
import Group from '../../../models/messaging-group';
import History from '../../../models/messaging-history';
import User from '../../../models/user';
import DriveFile from '../../../models/drive-file';
import serialize from '../../../serializers/messaging-message';
import publishUserStream from '../../../event';
import { publishMessagingStream } from '../../../event';
import config from '../../../config';

/**
 * 最大文字数
 */
const maxTextLength = 500;

/**
 * Create a message
 *
 * @param {Object} params
 * @param {Object} user
 * @return {Promise<object>}
 */
module.exports = (params, user) =>
	new Promise(async (res, rej) =>
{
	// Get 'user_id' parameter
	let recipient = params.user_id;
	if (recipient !== undefined && recipient !== null) {
		recipient = await User.findOne({
			_id: new mongo.ObjectID(recipient)
		});

		if (recipient === null) {
			return rej('user not found');
		}
	} else {
		recipient = null;
	}

	// Get 'group' parameter
	let group = params.group;
	if (group !== undefined && group !== null) {
		group = await Group.findOne({
			_id: new mongo.ObjectID(group)
		});

		if (group === null) {
			return rej('group not found');
		}

		// このグループのメンバーじゃなかったらreject
		if (group.members
				.map(member => member.toString())
				.indexOf(user._id.toString()) === -1) {
			return rej('access denied');
		}
	} else {
		group = null;
	}

	// ユーザーの指定がないかつグループの指定もなかったらエラー
	if (recipient === null && group === null) {
		return rej('user or group is required');
	}

	// ユーザーとグループ両方指定してたらエラー
	if (recipient !== null && group !== null) {
		return rej('need translate');
	}

	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'file_id' parameter
	let file = params.file_id;
	if (file !== undefined && file !== null) {
		file = await DriveFile.findOne({
			_id: new mongo.ObjectID(file),
			user_id: user._id
		}, {
			data: false
		});

		if (file === null) {
			return rej('file not found');
		}
	} else {
		file = null;
	}

	// テキストが無いかつ添付ファイルも無かったらエラー
	if (text === null && file === null) {
		return rej('text or file is required');
	}

	// メッセージを作成
	const inserted = await Message.insert({
		created_at: new Date(),
		file_id: file ? file._id : undefined,
		recipient_id: recipient ? recipient._id : undefined,
		group_id: group ? group._id : undefined,
		text: text ? text : undefined,
		user_id: user._id,
		is_read: recipient ? false : undefined,
		read: group ? false : undefined
	});

	const message = inserted.ops[0];

	// Serialize
	const messageObj = await serialize(message);

	// Reponse
	res(messageObj);

	// 自分のストリーム
	publishMessagingStream(message.user, message.recipient, 'message', messageObj);
	publishUserStream(message.user, 'messaging_message', messageObj);

	if (message.recipient) {
		// 相手のストリーム
		publishMessagingStream(message.recipient, message.user, 'message', messageObj);
		publishUserStream(message.recipient, 'messaging_message', messageObj);
	} else if (message.group) {
		// グループのストリーム
		publishMessagingStream(message.recipient, message.user, 'message', messageObj);

		const group = await Group.findOne({
			_id: message.group
		});

		// メンバーのストリーム
		group.members.forEach(member => {
			publishUserStream(member, 'messaging_message', messageObj);
		});
	}

	// 5秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
	setTimeout(async () => {
		if (message.recipient) {
			const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true });
			if (!freshMessage.is_read) {
				publishUserStream(message.recipient, 'unread_messaging_message', messageObj);
			}
		} else if (message.group) {
			// TODO
		}
	}, 5000);

	// Register to search database
	if (message.text && config.elasticsearch.enable) {
		const es = require('../../../db/elasticsearch');

		es.index({
			index: 'misskey',
			type: 'messaging_message',
			id: message._id.toString(),
			body: {
				text: message.text
			}
		});
	}

	// 履歴を作成しておく(対人)
	if (recipient) {
		// 自分
		History.updateOne({
			user_id: user._id,
			partner: recipient._id
		}, {
			updated_at: new Date(),
			user_id: user._id,
			partner: recipient._id,
			message: message._id
		}, {
			upsert: true
		});

		// 相手
		History.updateOne({
			user_id: recipient._id,
			partner: user._id
		}, {
			updated_at: new Date(),
			user_id: recipient._id,
			partner: user._id,
			message: message._id
		}, {
			upsert: true
		});
	}

	// 履歴を作成しておく(グループ)
	if (group) {
		group.members.forEach(member => {
			History.updateOne({
				user_id: member,
				group_id: group._id
			}, {
				updated_at: new Date(),
				user_id: member._id,
				group_id: group._id,
				message: message._id
			}, {
				upsert: true
			});
		});
	}
});
<commit_msg>Fix bug<commit_after>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import Message from '../../../models/messaging-message';
import Group from '../../../models/messaging-group';
import History from '../../../models/messaging-history';
import User from '../../../models/user';
import DriveFile from '../../../models/drive-file';
import serialize from '../../../serializers/messaging-message';
import publishUserStream from '../../../event';
import { publishMessagingStream } from '../../../event';
import config from '../../../../config';

/**
 * 最大文字数
 */
const maxTextLength = 500;

/**
 * Create a message
 *
 * @param {Object} params
 * @param {Object} user
 * @return {Promise<object>}
 */
module.exports = (params, user) =>
	new Promise(async (res, rej) =>
{
	// Get 'user_id' parameter
	let recipient = params.user_id;
	if (recipient !== undefined && recipient !== null) {
		recipient = await User.findOne({
			_id: new mongo.ObjectID(recipient)
		});

		if (recipient === null) {
			return rej('user not found');
		}
	} else {
		recipient = null;
	}

	// Get 'group' parameter
	let group = params.group;
	if (group !== undefined && group !== null) {
		group = await Group.findOne({
			_id: new mongo.ObjectID(group)
		});

		if (group === null) {
			return rej('group not found');
		}

		// このグループのメンバーじゃなかったらreject
		if (group.members
				.map(member => member.toString())
				.indexOf(user._id.toString()) === -1) {
			return rej('access denied');
		}
	} else {
		group = null;
	}

	// ユーザーの指定がないかつグループの指定もなかったらエラー
	if (recipient === null && group === null) {
		return rej('user or group is required');
	}

	// ユーザーとグループ両方指定してたらエラー
	if (recipient !== null && group !== null) {
		return rej('need translate');
	}

	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'file_id' parameter
	let file = params.file_id;
	if (file !== undefined && file !== null) {
		file = await DriveFile.findOne({
			_id: new mongo.ObjectID(file),
			user_id: user._id
		}, {
			data: false
		});

		if (file === null) {
			return rej('file not found');
		}
	} else {
		file = null;
	}

	// テキストが無いかつ添付ファイルも無かったらエラー
	if (text === null && file === null) {
		return rej('text or file is required');
	}

	// メッセージを作成
	const inserted = await Message.insert({
		created_at: new Date(),
		file_id: file ? file._id : undefined,
		recipient_id: recipient ? recipient._id : undefined,
		group_id: group ? group._id : undefined,
		text: text ? text : undefined,
		user_id: user._id,
		is_read: recipient ? false : undefined,
		read: group ? false : undefined
	});

	const message = inserted.ops[0];

	// Serialize
	const messageObj = await serialize(message);

	// Reponse
	res(messageObj);

	// 自分のストリーム
	publishMessagingStream(message.user, message.recipient, 'message', messageObj);
	publishUserStream(message.user, 'messaging_message', messageObj);

	if (message.recipient) {
		// 相手のストリーム
		publishMessagingStream(message.recipient, message.user, 'message', messageObj);
		publishUserStream(message.recipient, 'messaging_message', messageObj);
	} else if (message.group) {
		// グループのストリーム
		publishMessagingStream(message.recipient, message.user, 'message', messageObj);

		const group = await Group.findOne({
			_id: message.group
		});

		// メンバーのストリーム
		group.members.forEach(member => {
			publishUserStream(member, 'messaging_message', messageObj);
		});
	}

	// 5秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する
	setTimeout(async () => {
		if (message.recipient) {
			const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true });
			if (!freshMessage.is_read) {
				publishUserStream(message.recipient, 'unread_messaging_message', messageObj);
			}
		} else if (message.group) {
			// TODO
		}
	}, 5000);

	// Register to search database
	if (message.text && config.elasticsearch.enable) {
		const es = require('../../../db/elasticsearch');

		es.index({
			index: 'misskey',
			type: 'messaging_message',
			id: message._id.toString(),
			body: {
				text: message.text
			}
		});
	}

	// 履歴を作成しておく(対人)
	if (recipient) {
		// 自分
		History.updateOne({
			user_id: user._id,
			partner: recipient._id
		}, {
			updated_at: new Date(),
			user_id: user._id,
			partner: recipient._id,
			message: message._id
		}, {
			upsert: true
		});

		// 相手
		History.updateOne({
			user_id: recipient._id,
			partner: user._id
		}, {
			updated_at: new Date(),
			user_id: recipient._id,
			partner: user._id,
			message: message._id
		}, {
			upsert: true
		});
	}

	// 履歴を作成しておく(グループ)
	if (group) {
		group.members.forEach(member => {
			History.updateOne({
				user_id: member,
				group_id: group._id
			}, {
				updated_at: new Date(),
				user_id: member._id,
				group_id: group._id,
				message: message._id
			}, {
				upsert: true
			});
		});
	}
});
<|endoftext|>


<commit_before>(function () {
    'use strict';

    var app = angular.module('OrganizationModule');

    function OrganizationController($scope, $mdDialog, TreeFactory, $state, hardCoded) {
        var vm = this;

        vm.orgName = hardCoded.org;

        vm.missionStatement =
            'Monterey Bay Aquarium: The mission of the non-profit Monterey Bay Aquarium is to inspire conservation of the oceans.';

        vm.orgMembers = [{
            userName: "slacker",
            userId: "fsdfdsfd8fds9f8ds8f7",
            role: "boss"
        }, {
            userName: "pdiddy",
            userId: "fsdfdsasdasd9f8ds8f7",
            role: "denizen"
        }];

        vm.linkedNodeIds = ['organizationNode', 'objectiveNode', 'keyResultNode', 'taskNode'];

        //Waiting to fix issue with backend get request CORS
        // TreeFactory.getTrees('someorg').then(function (reponse) {
        //     console.log(response);
        // });

        vm.trees = [{
            "Name": "Monthly 12/14",
            "Id": "549dcb2befb6f7203e000001",
            "Active": true
        }, {
            "Name": "yearly 12/14",
            "Id": "549dcbe9efb6f7204b000001",
            "Active": true
        }, {
            "Name": "Make bread",
            "Id": "549dcbf2efb6f7204b000002",
            "Active": true
        }, {
            "Name": "Monthly 12/14",
            "Id": "549dcb2befb6f7203e000001",
            "Active": true
        }, {
            "Name": "yearly 12/14",
            "Id": "549dcbe9efb6f7204b000001",
            "Active": true
        }];

        vm.formattedTrees = TreeFactory.formatTrees(vm.trees);

        vm.openOrganizationMembersModal = function ($event) {
            $mdDialog.show({
                targetEvent: $event,
                templateUrl: 'app/organization/organization-members-modal.tpl.html',
                controller: 'OrganizationMembersModalController',
                controllerAs: 'modal',
                locals: {
                    members: vm.orgMembers
                }
            }).then(function (response) {
                vm.orgMembers = response;
            });
        };

        vm.openAddTreeModal = function ($event) {
            $mdDialog.show({
                targetEvent: $event,
                templateUrl: 'app/organization/add-tree-modal.tpl.html',
                controller: 'AddTreeModalController',
                controllerAs: 'modal'
            }).then(function (response) {
                if (response) {
                    vm.trees.push(response);
                    vm.formattedTrees = TreeFactory.formatTrees(vm.trees);
                }
            });
        };
    }

    OrganizationController.$inject = ['$scope', '$mdDialog', 'TreeFactory', '$state', 'hardCoded'];

    app.controller('OrganizationController', OrganizationController);

})();
<commit_msg>uncommented get request<commit_after>(function () {
    'use strict';

    var app = angular.module('OrganizationModule');

    function OrganizationController($scope, $mdDialog, TreeFactory, $state, hardCoded) {
        var vm = this;

        vm.orgName = hardCoded.org;

        vm.missionStatement =
            'Monterey Bay Aquarium: The mission of the non-profit Monterey Bay Aquarium is to inspire conservation of the oceans.';

        vm.orgMembers = [{
            userName: "slacker",
            userId: "fsdfdsfd8fds9f8ds8f7",
            role: "boss"
        }, {
            userName: "pdiddy",
            userId: "fsdfdsasdasd9f8ds8f7",
            role: "denizen"
        }];

        vm.linkedNodeIds = ['organizationNode', 'objectiveNode', 'keyResultNode', 'taskNode'];

        TreeFactory.getTrees('someorg').then(function (reponse) {
            console.log(response);
        });

        vm.trees = [{
            "Name": "Monthly 12/14",
            "Id": "549dcb2befb6f7203e000001",
            "Active": true
        }, {
            "Name": "yearly 12/14",
            "Id": "549dcbe9efb6f7204b000001",
            "Active": true
        }, {
            "Name": "Make bread",
            "Id": "549dcbf2efb6f7204b000002",
            "Active": true
        }, {
            "Name": "Monthly 12/14",
            "Id": "549dcb2befb6f7203e000001",
            "Active": true
        }, {
            "Name": "yearly 12/14",
            "Id": "549dcbe9efb6f7204b000001",
            "Active": true
        }];

        vm.formattedTrees = TreeFactory.formatTrees(vm.trees);

        vm.openOrganizationMembersModal = function ($event) {
            $mdDialog.show({
                targetEvent: $event,
                templateUrl: 'app/organization/organization-members-modal.tpl.html',
                controller: 'OrganizationMembersModalController',
                controllerAs: 'modal',
                locals: {
                    members: vm.orgMembers
                }
            }).then(function (response) {
                vm.orgMembers = response;
            });
        };

        vm.openAddTreeModal = function ($event) {
            $mdDialog.show({
                targetEvent: $event,
                templateUrl: 'app/organization/add-tree-modal.tpl.html',
                controller: 'AddTreeModalController',
                controllerAs: 'modal'
            }).then(function (response) {
                if (response) {
                    vm.trees.push(response);
                    vm.formattedTrees = TreeFactory.formatTrees(vm.trees);
                }
            });
        };
    }

    OrganizationController.$inject = ['$scope', '$mdDialog', 'TreeFactory', '$state', 'hardCoded'];

    app.controller('OrganizationController', OrganizationController);

})();
<|endoftext|>


<commit_before>/*
Copyright (c) 2011, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var WordInflector = function () {
};

WordInflector.prototype.addSingular = function(pattern, replacement) {
    this.customSingularForms.push([pattern, replacement]);
};

WordInflector.prototype.addPlural = function(pattern, replacement) {
    this.customPluralForms.push([pattern, replacement]);
};

WordInflector.prototype.ize = function (token, formSet, customForms) {
    return this.izeRegExps(token, customForms) || this.izeAbiguous(token) ||
        this.izeRegulars(token, formSet) || this.izeRegExps(token, formSet.regularForms) ||
        token;
}

WordInflector.prototype.izeAbiguous = function (token) {
    if(this.ambiguous.indexOf(token.toLowerCase()) > -1)
        return token.toLowerCase();

    return false;
}

WordInflector.prototype.pluralize = function (token) {
    return this.ize(token, this.pluralForms, this.customPluralForms);
};

WordInflector.prototype.singularize = function(token) {
    return this.ize(token, this.singularForms, this.customSingularForms);
};

WordInflector.prototype.izeRegulars = function(token, formSet) {
    token = token.toLowerCase();

    if(formSet.irregularForms[token])
        return formSet.irregularForms[token];

    return false;
}

WordInflector.prototype.addForm = function(singularTable, pluralTable, singular, plural) {
    singular = singular.toLowerCase();
    plural = plural.toLowerCase();
    pluralTable[singular] = plural;
    singularTable[plural] = singular;
};

WordInflector.prototype.addIrregular = function(singular, plural) {
    this.addForm(this.singularForms.irregularForms, this.pluralForms.irregularForms, singular, plural);
};

WordInflector.prototype.izeRegExps = function(token, forms) {
        for(i = 0; i < forms.length; i++) {
            form = forms[i];

            if(token.match(form[0]))
                return token.replace(form[0], form[1]);
        }

        return false;
    }

module.exports = WordInflector;<commit_msg>corrected naming of module in veb_inflector<commit_after>/*
Copyright (c) 2011, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var TenseInflector = function () {
};

TenseInflector.prototype.addSingular = function(pattern, replacement) {
    this.customSingularForms.push([pattern, replacement]);
};

TenseInflector.prototype.addPlural = function(pattern, replacement) {
    this.customPluralForms.push([pattern, replacement]);
};

TenseInflector.prototype.ize = function (token, formSet, customForms) {
    return this.izeRegExps(token, customForms) || this.izeAbiguous(token) ||
        this.izeRegulars(token, formSet) || this.izeRegExps(token, formSet.regularForms) ||
        token;
}

TenseInflector.prototype.izeAbiguous = function (token) {
    if(this.ambiguous.indexOf(token.toLowerCase()) > -1)
        return token.toLowerCase();

    return false;
}

TenseInflector.prototype.pluralize = function (token) {
    return this.ize(token, this.pluralForms, this.customPluralForms);
};

TenseInflector.prototype.singularize = function(token) {
    return this.ize(token, this.singularForms, this.customSingularForms);
};

TenseInflector.prototype.izeRegulars = function(token, formSet) {
    token = token.toLowerCase();

    if(formSet.irregularForms[token])
        return formSet.irregularForms[token];

    return false;
}

TenseInflector.prototype.addForm = function(singularTable, pluralTable, singular, plural) {
    singular = singular.toLowerCase();
    plural = plural.toLowerCase();
    pluralTable[singular] = plural;
    singularTable[plural] = singular;
};

TenseInflector.prototype.addIrregular = function(singular, plural) {
    this.addForm(this.singularForms.irregularForms, this.pluralForms.irregularForms, singular, plural);
};

TenseInflector.prototype.izeRegExps = function(token, forms) {
        for(i = 0; i < forms.length; i++) {
            form = forms[i];

            if(token.match(form[0]))
                return token.replace(form[0], form[1]);
        }

        return false;
    }

module.exports = TenseInflector;<|endoftext|>


<commit_before>"use strict";

var utils = require("../utils");
var log = require("npmlog");

var msgsRecv = 0;
var identity = function() {};

module.exports = function(defaultFuncs, api, ctx) {
  var currentlyRunning = null;
  var globalCallback = identity;

  var stopListening = function() {
    globalCallback = identity;
    if(currentlyRunning) {
      clearTimeout(currentlyRunning);
      currentlyRunning = null;
    }
  };

  var prev = Date.now();
  var tmpPrev = Date.now();
  var lastSync = Date.now();

  var form = {
    'channel' : 'p_' + ctx.userID,
    'seq' : '0',
    'partition' : '-2',
    'clientid' : ctx.clientID,
    'viewer_uid' : ctx.userID,
    'uid' : ctx.userID,
    'state' : 'active',
    'idle' : 0,
    'cap' : '8',
    'msgs_recv':msgsRecv
  };

  /**
   * Get an object maybe representing an event. Handles events it wants to handle
   * and returns true if it did handle an event (and called the globalCallback).
   * Returns false otherwise.
   */
  function handleMessagingEvents(event) {
    switch (event.event) {
      // "read_receipt" event triggers when other people read the user's messages.
      case 'read_receipt':
        globalCallback(null, utils.formatReadReceipt(event));
        return true;
      // "read event" triggers when the user read other people's messages.
      case 'read':
        globalCallback(null, utils.formatRead(event));
        return true;
      default:
        return false;
    }
  }

  function listen() {
    if(currentlyRunning == null || !ctx.loggedIn) {
      return;
    }

    form.idle = ~~(Date.now() / 1000) - prev;
    prev = ~~(Date.now() / 1000);
    var presence = utils.generatePresence(ctx.userID);
    ctx.jar.setCookie("presence=" + presence + "; path=/; domain=.facebook.com; secure", "https://www.facebook.com");
    utils.get("https://0-edge-chat.facebook.com/pull", ctx.jar, form)
    .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
    .then(function(resData) {
      var now = Date.now();
      log.info("listen", "Got answer in " + now - tmpPrev);
      tmpPrev = now;

      if(resData && resData.t === "lb") {
        form.sticky_token = resData.lb_info.sticky;
        form.sticky_pool = resData.lb_info.pool;
      }

      if(resData && resData.t === 'fullReload') {
        form.seq = resData.seq;
        delete form.sticky_pool;
        delete form.sticky_token;
        var form4 = {
          'lastSync' : ~~(lastSync/1000),
        };
        defaultFuncs.get("https://www.facebook.com/notifications/sync/", ctx.jar, form4)
        .then(utils.saveCookies(ctx.jar))
        .then(function() {
          lastSync = Date.now();
          var form = {
            'client' : 'mercury',
            'folders[0]': 'inbox',
            'last_action_timestamp' : ~~(Date.now() - 60)
          };
          defaultFuncs.post("https://www.facebook.com/ajax/mercury/thread_sync.php", ctx.jar, form)
          .then(function() {
            currentlyRunning = setTimeout(listen, 1000);
          });
        });
        return;
      }

      if(resData.ms) {
        msgsRecv += resData.ms.length;
        var atLeastOne = false;
        resData.ms.sort(function(a, b) {
          return a.timestamp - b.timestamp;
        }).forEach(function parsePackets(v) {
          switch (v.type) {
            // TODO: 'ttyp' was used before. It changed to 'typ'. We're keeping
            // both for now but we should remove 'ttyp' at some point.
            case 'ttyp':
            case 'typ':
              if(!ctx.globalOptions.listenEvents ||
                (!ctx.globalOptions.selfListen && v.from.toString() === ctx.userID)) {
                return;
              }

              return globalCallback(null, utils.formatTyp(v));
              break;
            case 'buddylist_overlay':
              // TODO: what happens when you're logged in as a page?
              if(!ctx.globalOptions.updatePresence) {
                return;
              }

              // There should be only one key inside overlay
              Object.keys(v.overlay).map(function(userID) {
                var formattedPresence = utils.formatPresence(v.overlay[userID], userID);
                if(ctx.loggedIn) {
                  return globalCallback(null, formattedPresence);
                }
              });
              break;
            case 'delta':
              if (ctx.globalOptions.pageID || (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents)) return

              if (v.delta.class == "NewMessage") {
                (function resolveAttachmentUrl(i) {
                  if (i == v.delta.attachments.length) {
                    var fmtMsg = utils.formatDeltaMessage(v);
                    return (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) ? undefined : globalCallback(null, fmtMsg);
                  } else {
                    if (v.delta.attachments[i].mercury.attach_type == 'photo') {
                      defaultFuncs
                        .get("https://www.facebook.com/mercury/attachments/photo/?photo_id=" + v.delta.attachments[i].fbid, ctx.jar, {})
                        .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
                        .then(function (resData) {
                          if (!resData.error) v.delta.attachments[i].mercury.url = resData.jsmods.require[0][3][0]
                          return resolveAttachmentUrl(i + 1);
                        })
                    }
                  }
                })(0)
                break;
              }

              switch (v.delta.class) {
                case 'AdminTextMessage':
                  switch (v.delta.type) {
                    case 'change_thread_theme':
                    case 'change_thread_nickname':
                    case 'change_thread_icon':
                      break;
                    default:
                      return;
                  }
                case 'ThreadName':
                case 'ParticipantsAddedToGroupThread':
                case 'ParticipantLeftGroupThread':
                  var formattedEvent = utils.formatEvent(v.delta);
                  return (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID || !ctx.loggedIn)
                    ? undefined
                    : globalCallback(null, formattedEvent);
              }

              break;
            case 'messaging':
              if (handleMessagingEvents(v)) {
                return;
              }
              break;
            case 'pages_messaging':
              if(!ctx.globalOptions.pageID ||
                v.event !== "deliver" ||
                (!ctx.globalOptions.selfListen && (v.message.sender_fbid.toString() === ctx.userID ||
                                                   v.message.sender_fbid.toString() === ctx.globalOptions.pageID)) ||
                v.realtime_viewer_fbid.toString() !== ctx.globalOptions.pageID) {
                return;
              }

              atLeastOne = true;
              if (ctx.loggedIn) {
                return globalCallback(null, utils.formatMessage(v));
              }
              break;
          }
        });

        if(atLeastOne) {
          // Send deliveryReceipt notification to the server
          var formDeliveryReceipt = {};

          resData.ms.filter(function(v) {
            return v.message && v.message.mid && v.message.sender_fbid.toString() !== ctx.userID;
          }).forEach(function(val, i) {
            formDeliveryReceipt["[" + i + "]"] = val.message.mid;
          });

          // If there's at least one, we do the post request
          if(formDeliveryReceipt["[0]"]) {
            defaultFuncs.post("https://www.facebook.com/ajax/mercury/delivery_receipts.php", ctx.jar, formDeliveryReceipt);
          }
        }
      }

      if(resData.seq) {
        form.seq = resData.seq;
      }
      if(resData.tr) {
        form.traceid = resData.tr;
      }
      if (currentlyRunning) {
        currentlyRunning = setTimeout(listen, Math.random() * 200 + 50);
      }
      return;
    })
    .catch(function(err) {
      if (err.code === 'ETIMEDOUT') {
        log.info("listen", "Suppressed timeout error.");
      } else {
        log.error("listen", err);
        globalCallback(err);
      }
      if (currentlyRunning) {
        currentlyRunning = setTimeout(listen, Math.random() * 200 + 50);
      }
    });
  }

  return function(callback) {
    globalCallback = callback;

    if (!currentlyRunning) {
      currentlyRunning = setTimeout(listen, Math.random() * 200 + 50, callback);
    }

    return stopListening;
  };
};
<commit_msg>Fix NaN error in listen<commit_after>"use strict";

var utils = require("../utils");
var log = require("npmlog");

var msgsRecv = 0;
var identity = function() {};

module.exports = function(defaultFuncs, api, ctx) {
  var currentlyRunning = null;
  var globalCallback = identity;

  var stopListening = function() {
    globalCallback = identity;
    if(currentlyRunning) {
      clearTimeout(currentlyRunning);
      currentlyRunning = null;
    }
  };

  var prev = Date.now();
  var tmpPrev = Date.now();
  var lastSync = Date.now();

  var form = {
    'channel' : 'p_' + ctx.userID,
    'seq' : '0',
    'partition' : '-2',
    'clientid' : ctx.clientID,
    'viewer_uid' : ctx.userID,
    'uid' : ctx.userID,
    'state' : 'active',
    'idle' : 0,
    'cap' : '8',
    'msgs_recv':msgsRecv
  };

  /**
   * Get an object maybe representing an event. Handles events it wants to handle
   * and returns true if it did handle an event (and called the globalCallback).
   * Returns false otherwise.
   */
  function handleMessagingEvents(event) {
    switch (event.event) {
      // "read_receipt" event triggers when other people read the user's messages.
      case 'read_receipt':
        globalCallback(null, utils.formatReadReceipt(event));
        return true;
      // "read event" triggers when the user read other people's messages.
      case 'read':
        globalCallback(null, utils.formatRead(event));
        return true;
      default:
        return false;
    }
  }

  function listen() {
    if(currentlyRunning == null || !ctx.loggedIn) {
      return;
    }

    form.idle = ~~(Date.now() / 1000) - prev;
    prev = ~~(Date.now() / 1000);
    var presence = utils.generatePresence(ctx.userID);
    ctx.jar.setCookie("presence=" + presence + "; path=/; domain=.facebook.com; secure", "https://www.facebook.com");
    utils.get("https://0-edge-chat.facebook.com/pull", ctx.jar, form)
    .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
    .then(function(resData) {
      var now = Date.now();
      log.info("listen", "Got answer in " + (now - tmpPrev));
      tmpPrev = now;

      if(resData && resData.t === "lb") {
        form.sticky_token = resData.lb_info.sticky;
        form.sticky_pool = resData.lb_info.pool;
      }

      if(resData && resData.t === 'fullReload') {
        form.seq = resData.seq;
        delete form.sticky_pool;
        delete form.sticky_token;
        var form4 = {
          'lastSync' : ~~(lastSync/1000),
        };
        defaultFuncs.get("https://www.facebook.com/notifications/sync/", ctx.jar, form4)
        .then(utils.saveCookies(ctx.jar))
        .then(function() {
          lastSync = Date.now();
          var form = {
            'client' : 'mercury',
            'folders[0]': 'inbox',
            'last_action_timestamp' : ~~(Date.now() - 60)
          };
          defaultFuncs.post("https://www.facebook.com/ajax/mercury/thread_sync.php", ctx.jar, form)
          .then(function() {
            currentlyRunning = setTimeout(listen, 1000);
          });
        });
        return;
      }

      if(resData.ms) {
        msgsRecv += resData.ms.length;
        var atLeastOne = false;
        resData.ms.sort(function(a, b) {
          return a.timestamp - b.timestamp;
        }).forEach(function parsePackets(v) {
          switch (v.type) {
            // TODO: 'ttyp' was used before. It changed to 'typ'. We're keeping
            // both for now but we should remove 'ttyp' at some point.
            case 'ttyp':
            case 'typ':
              if(!ctx.globalOptions.listenEvents ||
                (!ctx.globalOptions.selfListen && v.from.toString() === ctx.userID)) {
                return;
              }

              return globalCallback(null, utils.formatTyp(v));
              break;
            case 'buddylist_overlay':
              // TODO: what happens when you're logged in as a page?
              if(!ctx.globalOptions.updatePresence) {
                return;
              }

              // There should be only one key inside overlay
              Object.keys(v.overlay).map(function(userID) {
                var formattedPresence = utils.formatPresence(v.overlay[userID], userID);
                if(ctx.loggedIn) {
                  return globalCallback(null, formattedPresence);
                }
              });
              break;
            case 'delta':
              if (ctx.globalOptions.pageID || (v.delta.class !== "NewMessage" && !ctx.globalOptions.listenEvents)) return

              if (v.delta.class == "NewMessage") {
                (function resolveAttachmentUrl(i) {
                  if (i == v.delta.attachments.length) {
                    var fmtMsg = utils.formatDeltaMessage(v);
                    return (!ctx.globalOptions.selfListen && fmtMsg.senderID === ctx.userID) ? undefined : globalCallback(null, fmtMsg);
                  } else {
                    if (v.delta.attachments[i].mercury.attach_type == 'photo') {
                      defaultFuncs
                        .get("https://www.facebook.com/mercury/attachments/photo/?photo_id=" + v.delta.attachments[i].fbid, ctx.jar, {})
                        .then(utils.parseAndCheckLogin(ctx.jar, defaultFuncs))
                        .then(function (resData) {
                          if (!resData.error) v.delta.attachments[i].mercury.url = resData.jsmods.require[0][3][0]
                          return resolveAttachmentUrl(i + 1);
                        })
                    }
                  }
                })(0)
                break;
              }

              switch (v.delta.class) {
                case 'AdminTextMessage':
                  switch (v.delta.type) {
                    case 'change_thread_theme':
                    case 'change_thread_nickname':
                    case 'change_thread_icon':
                      break;
                    default:
                      return;
                  }
                case 'ThreadName':
                case 'ParticipantsAddedToGroupThread':
                case 'ParticipantLeftGroupThread':
                  var formattedEvent = utils.formatEvent(v.delta);
                  return (!ctx.globalOptions.selfListen && formattedEvent.author.toString() === ctx.userID || !ctx.loggedIn)
                    ? undefined
                    : globalCallback(null, formattedEvent);
              }

              break;
            case 'messaging':
              if (handleMessagingEvents(v)) {
                return;
              }
              break;
            case 'pages_messaging':
              if(!ctx.globalOptions.pageID ||
                v.event !== "deliver" ||
                (!ctx.globalOptions.selfListen && (v.message.sender_fbid.toString() === ctx.userID ||
                                                   v.message.sender_fbid.toString() === ctx.globalOptions.pageID)) ||
                v.realtime_viewer_fbid.toString() !== ctx.globalOptions.pageID) {
                return;
              }

              atLeastOne = true;
              if (ctx.loggedIn) {
                return globalCallback(null, utils.formatMessage(v));
              }
              break;
          }
        });

        if(atLeastOne) {
          // Send deliveryReceipt notification to the server
          var formDeliveryReceipt = {};

          resData.ms.filter(function(v) {
            return v.message && v.message.mid && v.message.sender_fbid.toString() !== ctx.userID;
          }).forEach(function(val, i) {
            formDeliveryReceipt["[" + i + "]"] = val.message.mid;
          });

          // If there's at least one, we do the post request
          if(formDeliveryReceipt["[0]"]) {
            defaultFuncs.post("https://www.facebook.com/ajax/mercury/delivery_receipts.php", ctx.jar, formDeliveryReceipt);
          }
        }
      }

      if(resData.seq) {
        form.seq = resData.seq;
      }
      if(resData.tr) {
        form.traceid = resData.tr;
      }
      if (currentlyRunning) {
        currentlyRunning = setTimeout(listen, Math.random() * 200 + 50);
      }
      return;
    })
    .catch(function(err) {
      if (err.code === 'ETIMEDOUT') {
        log.info("listen", "Suppressed timeout error.");
      } else {
        log.error("listen", err);
        globalCallback(err);
      }
      if (currentlyRunning) {
        currentlyRunning = setTimeout(listen, Math.random() * 200 + 50);
      }
    });
  }

  return function(callback) {
    globalCallback = callback;

    if (!currentlyRunning) {
      currentlyRunning = setTimeout(listen, Math.random() * 200 + 50, callback);
    }

    return stopListening;
  };
};
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import * as c from '@frogpond/colors'
import {data as credits} from '../../../docs/credits.json'
import glamorous from 'glamorous-native'
import {Platform, StyleSheet, ScrollView} from 'react-native'
import {iOSUIKit, material} from 'react-native-typography'
import {AppLogo} from './components/logo'

const styles = StyleSheet.create({
	container: {
		backgroundColor: c.white,
	},
	contentContainer: {
		paddingHorizontal: 5,
		paddingVertical: 10,
	},
})

const Title = glamorous.text({
	textAlign: 'center',
	marginTop: 10,
	marginBottom: 5,
	...Platform.select({
		ios: iOSUIKit.largeTitleEmphasizedObject,
		android: material.headlineObject,
	}),
})

const Heading = glamorous(Title)({
	...Platform.select({
		ios: iOSUIKit.subheadEmphasizedObject,
		android: material.titleObject,
	}),
})

const About = glamorous.text({
	...Platform.select({
		ios: iOSUIKit.bodyObject,
		android: material.body1Object,
	}),
	paddingHorizontal: 25,
	paddingTop: 10,
})

const Contributors = glamorous(About)({
	...Platform.select({
		ios: iOSUIKit.footnoteEmphasizedObject,
		android: material.body1Object,
	}),
	textAlign: 'center',
})

const formatPeopleList = arr => arr.map(w => w.replace(' ', ' ')).join(' • ')

export default function CreditsView() {
	return (
		<ScrollView
			contentContainerStyle={styles.contentContainer}
			contentInsetAdjustmentBehavior="automatic"
			style={styles.container}
		>
			<AppLogo />

			<Title>{credits.name}</Title>
			<About>{credits.content}</About>

			<Heading>Contributors</Heading>
			<Contributors>{formatPeopleList(credits.contributors)}</Contributors>

			<Heading>Acknowledgements</Heading>
			<Contributors>{formatPeopleList(credits.acknowledgements)}</Contributors>
		</ScrollView>
	)
}
CreditsView.navigationOptions = {
	title: 'Credits',
}
<commit_msg>fix up remaining import paths<commit_after>// @flow
import * as React from 'react'
import * as c from '@frogpond/colors'
import {data as credits} from '../../../../docs/credits.json'
import glamorous from 'glamorous-native'
import {Platform, StyleSheet, ScrollView} from 'react-native'
import {iOSUIKit, material} from 'react-native-typography'
import {AppLogo} from '../components/logo'

const styles = StyleSheet.create({
	container: {
		backgroundColor: c.white,
	},
	contentContainer: {
		paddingHorizontal: 5,
		paddingVertical: 10,
	},
})

const Title = glamorous.text({
	textAlign: 'center',
	marginTop: 10,
	marginBottom: 5,
	...Platform.select({
		ios: iOSUIKit.largeTitleEmphasizedObject,
		android: material.headlineObject,
	}),
})

const Heading = glamorous(Title)({
	...Platform.select({
		ios: iOSUIKit.subheadEmphasizedObject,
		android: material.titleObject,
	}),
})

const About = glamorous.text({
	...Platform.select({
		ios: iOSUIKit.bodyObject,
		android: material.body1Object,
	}),
	paddingHorizontal: 25,
	paddingTop: 10,
})

const Contributors = glamorous(About)({
	...Platform.select({
		ios: iOSUIKit.footnoteEmphasizedObject,
		android: material.body1Object,
	}),
	textAlign: 'center',
})

const formatPeopleList = arr => arr.map(w => w.replace(' ', ' ')).join(' • ')

export default function CreditsView() {
	return (
		<ScrollView
			contentContainerStyle={styles.contentContainer}
			contentInsetAdjustmentBehavior="automatic"
			style={styles.container}
		>
			<AppLogo />

			<Title>{credits.name}</Title>
			<About>{credits.content}</About>

			<Heading>Contributors</Heading>
			<Contributors>{formatPeopleList(credits.contributors)}</Contributors>

			<Heading>Acknowledgements</Heading>
			<Contributors>{formatPeopleList(credits.acknowledgements)}</Contributors>
		</ScrollView>
	)
}
CreditsView.navigationOptions = {
	title: 'Credits',
}
<|endoftext|>