<commit_before>import React, { Component } from 'react';
import Link from 'next/link';
import Head from 'next/head';
import randtoken from 'rand-token';
import Dropzone from 'react-dropzone';
import Footer from '../components/Footer';
import moment from 'moment';
import { size, map } from 'lodash';
import { nextConnect } from '../store';
import { dataURItoBlob } from '../helpers';
import { register, hideFlash, getEvents } from '../actions';

class Register extends Component {
  constructor() {
    super();
    this.state = {
      oldEnough: '',
      full_name: '',
      email: '',
      terms: false,
      referral: '',
      url: '',
      event: '',
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  componentDidMount() {
    const events = size(this.props.events);
    if (events === 0) {
      this.props.getEvents();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.flash.show) {
      this.props.hideFlash();
      this.setState({ processing: false });
    }
  }

  handleSubmit(e) {
    e.preventDefault();

    this.setState({ processing: true });

    const { email, terms, name, event } = this.state;
    if (email === '' || !terms || name === '' || event === '') {
      return false;
    }

    const keys = Object.keys(this.state);
    const formData = new FormData();
    formData.append('user_token', randtoken.generate(5));

    const files = {
      artwork_3: true,
      artwork_2: true,
      artwork_1: true,
      photo: true,
    };

    keys.forEach((key) => {
      if (files[key]) {
        const blob = dataURItoBlob(this.state[key]);
        const filename = `${randtoken.generate(20)}.jpg`;
        formData.append(key, blob, filename);
      } else {
        formData.append(key, this.state[key]);
      }
    });

    this.props.register(formData);

    return true;
  }

  handleImageChange(filesToUpload, name) {
    const reader = new FileReader();
    const file = filesToUpload[0];

    if (file.size > (1024 * 1024 * 5)) {
      alert('Holly molly! That photo is too large');

      return false;
    }
    const state = {};

    reader.onloadend = () => {
      state[name] = reader.result;
      this.setState(state);
    };

    reader.readAsDataURL(file);

    return true;
  }

  render() {
    return (

          <title>Submit your work - Conception Arts</title>
          <meta name="viewport" content="initial-scale=1.0, width=device-width" />
          <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" />

                <Link href="/index">

                </Link>

                      Artist Registration

                      Already a member of our team? Login.

                            <input
                              type="text"
                              placeholder="Name"
                              value={this.state.name}
                              onChange={evt => this.setState({ full_name: evt.target.value })}
                            />

                            <input
                              type="text"
                              placeholder="Email"
                              value={this.state.email}
                              onChange={evt => this.setState({ email: evt.target.value })}
                            />

                                  {this.state.photo && }
                                  Photo

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'photo')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                            Are you 21 years or older?

                                <button
                                  className={`check_button ${this.state.oldEnough && 'active'}`}
                                  onClick={() => this.setState({ oldEnough: true })}
                                >Yes</button>

                                <button
                                  className={`check_button ${!this.state.oldEnough && 'active'}`}
                                  onClick={() => this.setState({ oldEnough: false })}
                                >No</button>

                            <input
                              type="url"
                              required
                              pattern="https?://.+"
                              placeholder="Your Website"
                              value={this.state.url}
                              onChange={(evt) => this.setState({ url: evt.target.value })}
                            />

                            Upload your artwork (up to 3 pieces max)

                                  {this.state.artwork_1 &&

                                  }Artwork One

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_1')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                                  {this.state.artwork_2 &&

                                  }Artwork Two

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_2')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                                  {this.state.artwork_3 &&

                                  }Artwork Three

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_3')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                            <input
                              type="text"
                              placeholder="Where did you hear about Conception?"
                              value={this.state.referral}
                              onChange={(evt) => this.setState({ referral: evt.target.value })}
                            />

                            Which show are you submiting for?
                            <select
                              onChange={(evt) => this.setState({ event: Number(evt.target.value) })}
                              value={this.state.event}
                            >
                              <option value="">Pick event</option>
                              {map(this.props.events, (event) => (
                                <option value={event.id} key={event.id}>
                                  {event.name.text} - {moment(event.start.local).format('MMMM Do YYYY')}
                                </option>
                              ))}
                            </select>

                            <input
                              type="checkbox"
                              id="checkbox1"
                              checked={this.state.terms}
                              onChange={(evt) => this.setState({ terms: evt.target.checked })}
                            />

                              I agree to the terms and conditions.

                            <button
                              onClick={this.handleSubmit}
                            >{this.state.processing ? 'Submitting...' : 'Submit'}</button>

                          {this.props.flash.show &&

                              {this.props.flash.msg && this.props.flash.msg}
                              {!this.props.flash.msg &&

Registration successfully!

Check your email for your password.

                              }

}

    );
  }
}

Register.propTypes = {
  hideFlash: React.PropTypes.func,
  register: React.PropTypes.func,
  flash: React.PropTypes.object,
  events: React.PropTypes.object,
  getEvents: React.PropTypes.func,
};

function mapStateToProps(state) {
  console.log(state);
  return {
    flash: state.flash,
    events: state.events,
  };
}

export default nextConnect(mapStateToProps, { register, hideFlash, getEvents })(Register);
<commit_msg>updated<commit_after>import React, { Component } from 'react';
import Link from 'next/link';
import Head from 'next/head';
import randtoken from 'rand-token';
import Dropzone from 'react-dropzone';
import Footer from '../components/Footer';
import moment from 'moment';
import { size, map } from 'lodash';
import { nextConnect } from '../store';
import { dataURItoBlob } from '../helpers';
import { register, hideFlash, getEvents } from '../actions';

class Register extends Component {
  constructor() {
    super();
    this.state = {
      oldEnough: '',
      full_name: '',
      email: '',
      terms: false,
      referral: '',
      url: '',
      event: '',
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  componentDidMount() {
    const events = size(this.props.events);
    if (events === 0) {
      this.props.getEvents();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.flash.show) {
      this.props.hideFlash();
      this.setState({ processing: false });
    }
  }

  handleSubmit(e) {
    e.preventDefault();
    const { email, terms, name, event } = this.state;
    if (email === '' || !terms || name === '' || event === '') {
      alert('Please complete all fields');
      return false;
    }
    this.setState({ processing: true });
    const keys = Object.keys(this.state);
    const formData = new FormData();
    formData.append('user_token', randtoken.generate(5));

    const files = {
      artwork_3: true,
      artwork_2: true,
      artwork_1: true,
      photo: true,
    };

    keys.forEach((key) => {
      if (files[key]) {
        const blob = dataURItoBlob(this.state[key]);
        const filename = `${randtoken.generate(20)}.jpg`;
        formData.append(key, blob, filename);
      } else {
        formData.append(key, this.state[key]);
      }
    });

    this.props.register(formData);

    return true;
  }

  handleImageChange(filesToUpload, name) {
    const reader = new FileReader();
    const file = filesToUpload[0];

    if (file.size > (1024 * 1024 * 5)) {
      alert('Holly molly! That photo is too large');

      return false;
    }
    const state = {};

    reader.onloadend = () => {
      state[name] = reader.result;
      this.setState(state);
    };

    reader.readAsDataURL(file);

    return true;
  }

  render() {
    return (

          <title>Submit your work - Conception Arts</title>
          <meta name="viewport" content="initial-scale=1.0, width=device-width" />
          <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" />

                <Link href="/index">

                </Link>

                      Artist Registration

                      Already a member of our team? Login.

                            <input
                              type="text"
                              placeholder="Name"
                              value={this.state.name}
                              onChange={evt => this.setState({ full_name: evt.target.value })}
                            />

                            <input
                              type="text"
                              placeholder="Email"
                              value={this.state.email}
                              onChange={evt => this.setState({ email: evt.target.value })}
                            />

                                  {this.state.photo && }
                                  Photo

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'photo')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                            Are you 21 years or older?

                                <button
                                  className={`check_button ${this.state.oldEnough && 'active'}`}
                                  onClick={() => this.setState({ oldEnough: true })}
                                >Yes</button>

                                <button
                                  className={`check_button ${!this.state.oldEnough && 'active'}`}
                                  onClick={() => this.setState({ oldEnough: false })}
                                >No</button>

                            <input
                              type="url"
                              required
                              pattern="https?://.+"
                              placeholder="Your Website"
                              value={this.state.url}
                              onChange={(evt) => this.setState({ url: evt.target.value })}
                            />

                            Upload your artwork (up to 3 pieces max)

                                  {this.state.artwork_1 &&

                                  }Artwork One

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_1')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                                  {this.state.artwork_2 &&

                                  }Artwork Two

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_2')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                                  {this.state.artwork_3 &&

                                  }Artwork Three

                                  <Dropzone
                                    className="image-upload"
                                    multiple={false}
                                    onDrop={filesToUpload =>
                                      this.handleImageChange(filesToUpload, 'artwork_3')}
                                  >
                                    <button>Upload Photo</button>
                                  </Dropzone>

                            <input
                              type="text"
                              placeholder="Where did you hear about Conception?"
                              value={this.state.referral}
                              onChange={(evt) => this.setState({ referral: evt.target.value })}
                            />

                            Which show are you submiting for?
                            <select
                              onChange={(evt) => this.setState({ event: Number(evt.target.value) })}
                              value={this.state.event}
                            >
                              <option value="">Pick event</option>
                              {map(this.props.events, (event) => (
                                <option value={event.id} key={event.id}>
                                  {event.name.text} - {moment(event.start.local).format('MMMM Do YYYY')}
                                </option>
                              ))}
                            </select>

                            <input
                              type="checkbox"
                              id="checkbox1"
                              checked={this.state.terms}
                              onChange={(evt) => this.setState({ terms: evt.target.checked })}
                            />

                              I agree to the terms and conditions.

                            <button
                              onClick={this.handleSubmit}
                            >{this.state.processing ? 'Submitting...' : 'Submit'}</button>

                          {this.props.flash.show &&

                              {this.props.flash.msg && this.props.flash.msg}
                              {!this.props.flash.msg &&

Registration successfully!

Check your email for your password.

                              }

}

    );
  }
}

Register.propTypes = {
  hideFlash: React.PropTypes.func,
  register: React.PropTypes.func,
  flash: React.PropTypes.object,
  events: React.PropTypes.object,
  getEvents: React.PropTypes.func,
};

function mapStateToProps(state) {
  console.log(state);
  return {
    flash: state.flash,
    events: state.events,
  };
}

export default nextConnect(mapStateToProps, { register, hideFlash, getEvents })(Register);
<|endoftext|>


<commit_before>//define('parallelWorker',[],function(){

var parallelWorker = function(workerJobObject){
								if(typeof(Worker) != 'undefined'){
									var blob = new Blob([workerJobObject.textContent],{type:'text/javascript'});
									if(window.URL){
										this.worker = new Worker(window.URL.createObjectURL(blob));
										this.worker.listenerCallbackList = [];
									}
									this.status = true;
									//create a blob element from WSO text content
									//Create Object URL for the above created
									//blob with type as 'Application/java-script'
									//set Live status
								}else{
									//fallback to inline job execution
									eval(workerJobObject.textContent);
									this.listenerCallbackList = [];
									this.status = false;
									var self = this;
									this.postMessage = function(data){
										for(var i=0;i<this.listenerCallbackList.length;i++){
											var callbackMethod = self.listenerCallbackList[i];
											callbackMethod(data);
										}
									}
								}
							};
parallelWorker.prototype = {
		dispatchMessage 	: function(message){
			if(this.status===true){
				this.worker.postMessage(message);
			}else{
				if(this.onmessage){
					var eventDup = {};
					eventDup.data = message;
					this.onmessage(eventDup);
				}
			}
		},
		registerListener	: function(callback){
			if(this.status===true){
				this.worker.listenerCallbackList.push(callback);
				if(this.worker.listenerCallbackList.length==1){
					this.worker.onmessage = function(event){
						for(var i=0;i<this.listenerCallbackList.length;i++){
							var callbacktofire = this.listenerCallbackList[i];
							callbacktofire(event.data);
						}
					}
				}
			}else{
				this.listenerCallbackList.push(callback);
			}
		},
		removeAllListeners	: function(){
					if(this.status===true){
						this.worker.listenerCallbackList = [];
					}else{
						this.listenerCallbackList = [];
					}
		},
		destroyWorker	   	: function(){
			if(this.status===true){
				this.worker.terminate();
			}
		}
	}

//	return WorkerClass;
//})
<commit_msg>Delete parallelWorker.js<commit_after><|endoftext|>


<commit_before>import chokidar from 'chokidar'
import path from 'path'
import { includes } from 'lodash'

import config from '../../config/server'
import Links from './Links'
import Tags from './Tags'

const CHOKIDAR_OPTIONS = {
  ignoreInitial: true,
  persistent:    true,
}
const watchedExtensions = ['html', 'json']

export default class Watcher {
  static watch() {
    chokidar.watch(`${config.folders.articles}/*`, CHOKIDAR_OPTIONS)
      .on('raw', (event, filename) => {
        if (filename == null) { // On Windows, the filename is null. Fully reindex.
          Links.rebuild()
          Tags.rebuild()
          return
        }

        if (path.dirname(filename) !== '.' && // Windows = '.', and only events for files in the subdir
            path.dirname(filename) !== config.folders.articles) { // *nix = full path
          return
        }

        const ext = path.extname(filename)
        const action = event !== 'unlink' ? 'reindex' : 'unindex'
        const target = path.basename(filename, ext)

        if (!includes(watchedExtensions, ext)) return

        Links[`${action}${ext.toUpperCase()}`](target)
        if (ext === 'json') Tags[action](target)
      })
      .on('error', error => console.log(`Watcher error: ${error}`))
  }
}
<commit_msg>eslint<commit_after>import chokidar from 'chokidar'
import path from 'path'
import { includes } from 'lodash'

import config from '../../config/server'
import Links from './Links'
import Tags from './Tags'

const CHOKIDAR_OPTIONS = {
  ignoreInitial: true,
  persistent:    true,
}
const watchedExtensions = ['html', 'json']

export default class Watcher {
  static watch() {
    chokidar.watch(`${config.folders.articles}/*`, CHOKIDAR_OPTIONS)
      .on('raw', (event, filename) => {
        if (filename == null) { // On Windows, the filename is null. Fully reindex.
          Links.rebuild()
          Tags.rebuild()
          return
        }

        if (path.dirname(filename) !== '.' && // Windows = '.', and only events for files in the subdir
            path.dirname(filename) !== config.folders.articles) { // *nix = full path
          return
        }

        const ext = path.extname(filename)
        const action = event !== 'unlink' ? 'reindex' : 'unindex'
        const target = path.basename(filename, ext)

        if (!includes(watchedExtensions, ext)) return

        Links[`${action}${ext.toUpperCase()}`](target)
        if (ext === 'json') Tags[action](target)
      })
      .on('error', error =>
        console.log(`Watcher error: ${error}`) // eslint-disable-line no-console
      )
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var http = require('http');
var util = require('util');
var routing = require('./routing');
var staticServer = require('./staticServer');
var searchApi = require('./search');

function apiExample(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Api\n' + req.url + '\n');
}

routing.addRoute('^/app/', 'GET', staticServer.handleRequest);
routing.addRoute('^/api/', 'GET', searchApi.handleRequest);

var DEFAULT_PORT = 8000;

function main(argv) {
  var port = DEFAULT_PORT;
  if (argv[2]) {
    port = Number(argv[2]);
  }
  util.puts('argv:' + argv + ' port:' + port);
  if (!(port > 0 && port < 65536)) {
    util.puts('Invalid port');
    return 2;
  }

  util.puts('Starting server on port ' + port);
  var server = http.createServer(function(req, res) {
    var logEntry = req.method + ' ' + req.url;
    if (req.headers['user-agent']) {
      logEntry += ' ' + req.headers['user-agent'];
    }
    util.puts(logEntry);
    if (routing.process(req, res) === false) {
      res.writeHead(404, {'Content-Type': 'text/plain'});
      res.end('No matching route found');
    }
  }).listen(port);
}

// Must be last,
main(process.argv);

<commit_msg>Support /app without a trailing slash<commit_after>#!/usr/bin/env node

var http = require('http');
var util = require('util');
var routing = require('./routing');
var staticServer = require('./staticServer');
var searchApi = require('./search');

function apiExample(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Api\n' + req.url + '\n');
}

routing.addRoute('^/app/|^/app$', 'GET', staticServer.handleRequest);
routing.addRoute('^/api/', 'GET', searchApi.handleRequest);

var DEFAULT_PORT = 8000;

function main(argv) {
  var port = DEFAULT_PORT;
  if (argv[2]) {
    port = Number(argv[2]);
  }
  util.puts('argv:' + argv + ' port:' + port);
  if (!(port > 0 && port < 65536)) {
    util.puts('Invalid port');
    return 2;
  }

  util.puts('Starting server on port ' + port);
  var server = http.createServer(function(req, res) {
    var logEntry = req.method + ' ' + req.url;
    if (req.headers['user-agent']) {
      logEntry += ' ' + req.headers['user-agent'];
    }
    util.puts(logEntry);
    if (routing.process(req, res) === false) {
      res.writeHead(404, {'Content-Type': 'text/plain'});
      res.end('No matching route found');
    }
  }).listen(port);
}

// Must be last,
main(process.argv);

<|endoftext|>


<commit_before>var cadence = require('cadence')
var Vizsla = require('vizsla')
var logger = require('prolific.logger').createLogger('compassion.colleague')

var Kibitzer = require('kibitz')

var crypto = require('crypto')

var Monotonic = require('monotonic').asString

// Sencha Connect middleware builder.
var Reactor = require('reactor')

var url = require('url')

var Procession = require('procession')

var coalesce = require('extant')

var Timer = require('happenstance/timer')
var Scheduler = require('happenstance/scheduler')

var Keyify = require('keyify')

var Recorder = require('./recorder')

var Backlogger = require('./backlogger')

function Local (destructible, colleagues, options) {
    this._destructible = destructible

    this.colleagues = colleagues

    this._Conference = options.Conference
    this._population = options.population
    this._networkedUrl = 'http://' + options.bind.networked.address + ':' + options.bind.networked.port + '/'

    var scheduler = new Scheduler
    var timer = new Timer(scheduler)

    destructible.destruct.wait(scheduler, 'clear')
    destructible.destruct.wait(timer.events.pump(this, '_scheduled', destructible.monitor('timer')), 'destroy')
    destructible.destruct.wait(scheduler.events.pump(timer, 'enqueue', destructible.monitor('scheduler')), 'destroy')

    this.scheduler = scheduler

    this._ping = 1000
    this._timeout = 5000
    this._httpTimeout = 5000

    this._instance = 0
    this._ua = new Vizsla

    this.events = new Procession

    this.reactor = new Reactor(this, function (dispatcher) {
        dispatcher.dispatch('GET /', 'index')
        dispatcher.dispatch('POST /register', 'register')
        dispatcher.dispatch('GET /backlog', 'backlog')
        dispatcher.dispatch('POST /broadcast', 'broadcast')
        dispatcher.dispatch('POST /record', 'record')
        dispatcher.dispatch('GET /ping', 'ping')
        dispatcher.dispatch('GET /health', 'health')
    })
}

Local.prototype.index = cadence(function (async) {
    return [ 200, { 'content-type': 'text/plain' }, 'Compassion Local API\n' ]
})

Local.prototype._record = cadence(function (async, destructible, kibitzer, id) {
    destructible.destruct.wait(kibitzer.played.pump(new Recorder(this.events, id, 'kibitzer'), 'record', destructible.monitor('played')), 'destroy')
    destructible.destruct.wait(kibitzer.paxos.outbox.pump(new Recorder(this.events, id, 'paxos'), 'record', destructible.monitor('paxos')), 'destroy')
    destructible.destruct.wait(kibitzer.islander.outbox.pump(new Recorder(this.events, id, 'islander'), 'record', destructible.monitor('islander')), 'destroy')
    return kibitzer
})

Local.prototype.colleague = cadence(function (async, destructible, envelope) {
    var kibitzer
    async(function () {
        var ua = new Vizsla().bind({ timeout: this._timeout })
        kibitzer = new Kibitzer({
            id: envelope.id,
            ping: this._ping,
            timeout: this._timeout,
            ua: {
                send: cadence(function (async, envelope) {
                    logger.info('recorded', { source: 'ua', method: envelope.method, $envelope: envelope })
                    ua.fetch({
                        url: envelope.to.url
                    }, {
                        url: './kibitz',
                        post: envelope,
                        parse: 'json',
                        nullify: true
                    }, async())
                })
            }
        })
        destructible.monitor('kibitzer', kibitzer, 'listen', async())
    }, function () {
        destructible.monitor('record', this, '_record', kibitzer, envelope.id, async())
    }, function (kibitzer) {
        async(function () {
            crypto.randomBytes(32, async())
        }, function (bytes) {
            if (this.colleagues.island[envelope.island] == null) {
                this.colleagues.island[envelope.island] = {}
            }
            var events = this.events
            var conference = new this._Conference(destructible, {
                acclimate: function () { kibitzer.acclimate() },
                publish: function (record, envelope) { kibitzer.publish(envelope) },
                broadcasts: cadence(function (async, promise) {
                    async(function () {
                        var government = kibitzer.paxos.government
                        var leaderUrl = government.properties[government.majority[0]].url
                        colleague.ua.fetch({
                            url: leaderUrl
                        }, {
                            url: './broadcasts',
                            post: { promise: promise },
                            raise: true,
                            parse: 'json'
                        }, async())
                    }, function (body) {
                        events.push({
                            type: 'broadcasts',
                            id: colleague.initalizer.id,
                            body: body
                        })
                        return [ body ]
                    })
                })
            }, envelope, kibitzer)
            conference.log.pump(new Recorder(this.events, envelope.id, 'entry'), 'record', destructible.monitor('log'))
            destructible.destruct.wait(function () { conference.log.push(null) })
            conference.consumed.pump(new Recorder(this.events, envelope.id, 'consumed'), 'record', destructible.monitor('consumed'))
            destructible.destruct.wait(function () { conference.consumed.push(null) })
            var log = kibitzer.paxos.log.pump(conference, 'entry', destructible.monitor('entries'))
            destructible.destruct.wait(log, 'destroy')
            // kibitzer.paxos.log.pump(new Recorder(this.events, envelope.id, 'entry'), 'record', destructible.monitor('events'))
            destructible.destruct.wait(kibitzer.paxos.pinged.pump(this, function () {
                this.scheduler.schedule(Date.now() + 5000, Keyify.stringify({
                    island: envelope.island,
                    id: envelope.id
                }), {
                    name: 'recoverable',
                    island: envelope.island,
                    id: envelope.id
                })
            }, destructible.monitor('pinged')), 'destroy')
            destructible.destruct.wait(this, function () {
                var colleague = this.colleagues.island[envelope.island][envelope.id]
                delete this.colleagues.island[envelope.island][envelope.id]
                delete this.colleagues.token[colleague.token]
            })
            destructible.destruct.wait(function () {
                conference.outbox.push(null)
            })
            destructible.destruct.wait(this, function () {
                this.scheduler.unschedule(Keyify.stringify({
                    island: envelope.island,
                    id: envelope.id
                }))
            })
            var token = bytes.toString('hex')
            var colleague = this.colleagues.island[envelope.island][envelope.id] = {
                token: token,
                destructible: destructible,
                initalizer: envelope,
                kibitzer: kibitzer,
                conference: conference,
                createdAt: Date.now(),
                ua: new Vizsla().bind({
                    url: envelope.url,
                    raise: true,
                    parse: 'json'
                })
            }
            this.colleagues.token[token] = colleague
            this.scheduler.schedule(Date.now(), Keyify.stringify({
                island: colleague.initalizer.island,
                id: colleague.initalizer.id
            }), {
                name: 'register',
                island: colleague.initalizer.island,
                id: colleague.initalizer.id
            })
            return []
        })
    })
})

var discover = require('./discover')
var embark = require('./embark')
var recoverable = require('./recoverable')

Local.prototype.terminate = function (island, id) {
    var island = this.colleagues.island[island]
    if (island != null) {
        var colleague = island[id]
        if (colleague != null) {
            colleague.destructible.destroy()
        }
    }
}

Local.prototype._overwatch = cadence(function (async, envelope, members, complete) {
    var island = this.colleagues.island[envelope.body.island]
    if (island == null) {
        return
    }
    var colleague = island[envelope.body.id]
    if (colleague == null) {
        return null
    }
    var action
    switch (envelope.body.name) {
    case 'register':
        action = { action: 'register' }
        break
    case 'discover':
        action = discover(envelope.body.id, members, complete)
        break
    case 'embark':
        action = embark(members, 0)
        break
    case 'recoverable':
        action = recoverable(envelope.body.id, members)
        break
    }
    switch (action.action) {
    case 'register':
        // TODO Handle 404.
        async(function () {
            this._ua.fetch({
                url: colleague.initalizer.url,
                token: colleague.initalizer.token,
                timeout: 1000,
                raise: true,
                parse: 'json'
            }, {
                url: './register',
                post: {
                    token: colleague.token
                }
            }, async())
        }, function () {
            this.scheduler.schedule(Date.now(), Keyify.stringify({
                island: envelope.body.island,
                id: envelope.body.id
            }), {
                name: 'discover',
                island: envelope.body.island,
                id: envelope.body.id
            })
        })
        break
    case 'bootstrap':
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.initalizer.properties, {})))
        properties.url = action.url
        colleague.kibitzer.bootstrap(0, properties)
        break
    case 'join':
        colleague.kibitzer.join(0)
        this.scheduler.schedule(Date.now(), Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: action.url,
            republic: action.republic
        })
        break
    case 'embark':
        // Schedule a subsequent embarkation. Once our Paxos object starts
        // receiving message, the embarkation is cleared and replaced with a
        // recoverable check.
        this.scheduler.schedule(Date.now() + 5000, Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: envelope.body.url,
            republic: envelope.body.republic
        })
        // If this fails, we don't care. Embarkation is designed to be
        // asynchronous in the macro. You send a message. Maybe it gets there,
        // maybe it doesn't. You'll know when the Paxos object starts working.
        // Until then, keep sending embarkation requests. The leader knows how
        // to deal with duplicate or in-process requests.
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.initalizer.properties, {})))
        properties.url = envelope.body.url
        this._ua.fetch({
            url: action.url,
            timeout: 1000,
            nullify: true,
            parse: 'json'
        }, {
            url: './arrive',
            post: {
                republic: envelope.body.republic,
                id: colleague.kibitzer.paxos.id,
                cookie: colleague.kibitzer.paxos.cookie,
                properties: properties
            }
        }, async())
        break
    case 'retry':
        this.scheduler.schedule(Date.now() + 1000, envelope.key, envelope.body)
        break
    case 'unrecoverable':
        colleague.destructible.destroy()
        break
    }
})

// Race conditions to consider — events are not going to fire for a
// destroyed colleague because we clear their schedule on destruct. However, we
// the colleague might get destroyed while we are gathering the census. That is
// why we check that the colleague still exists in `_overwatch`.
//
// In overwatch, we schedule our next action, an synchronous operation, before
// we do anything asynchronous, so we know that we have a good colleague. Our
// asynchronous operation is a tail operation. If something particularly bad
// happens.

//
Local.prototype._scheduled = cadence(function (async, envelope) {
    if (!(envelope != null && envelope.module == 'happenstance' && envelope.method == 'event')) {
        return
    }
    async(function () {
        this._population.census(envelope.body.island, async())
    }, function (members, complete) {
        this._overwatch(envelope, members, complete, async())
    })
})

Local.prototype._getColleague = function (request) {
    if (request.authorization.scheme != 'Bearer') {
        throw 401
    }
    var colleague = this.colleagues.token[request.authorization.credentials]
    if (colleague == null) {
        throw 401
    }
    return colleague
}

// TODO Abend if the colleague is not waiting on a `join` notification.
Local.prototype.backlog = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    var government = colleague.kibitzer.paxos.government
    async(function () {
        colleague.ua.fetch({
            url: government.properties[government.majority[0]].url
        }, {
            url: './backlog',
            post: {
                promise: government.arrived.promise[colleague.initalizer.id]
            },
            parse: 'stream',
            raise: true
        }, async())
    }, function (body, response) {
        return [ 200, response.headers, Backlogger({
            events: this.events,
            id: colleague.initalizer.id,
            contentType: response.headers['content-type'],
            input: body
        }) ]
    })
})

Local.prototype.register = cadence(function (async, request) {
    // If we already have one and it doesn't match, then we destroy this one.
    // Create a new instance.
    async(function () {
        this._destructible.monitor([ 'colleague', this._instance++ ], true, this, 'colleague', request.body, async())
    }, function () {
        return 200
    })
})

Local.prototype.record = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    this.events.push({
        type: 'record',
        id: colleague.initalizer.id,
        body: request.body
    })
    return request.body
})

Local.prototype.broadcast = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    colleague.conference.broadcast(request.body.method, request.body.message)
    return 200
})

module.exports = Local
<commit_msg>Make all timeouts configurable.<commit_after>var cadence = require('cadence')
var Vizsla = require('vizsla')
var logger = require('prolific.logger').createLogger('compassion.colleague')

var Kibitzer = require('kibitz')

var crypto = require('crypto')

var Monotonic = require('monotonic').asString

// Sencha Connect middleware builder.
var Reactor = require('reactor')

var url = require('url')

var Procession = require('procession')

var coalesce = require('extant')

var Timer = require('happenstance/timer')
var Scheduler = require('happenstance/scheduler')

var Keyify = require('keyify')

var Recorder = require('./recorder')

var Backlogger = require('./backlogger')

function Local (destructible, colleagues, options) {
    this._destructible = destructible

    this.colleagues = colleagues

    this._Conference = options.Conference
    this._population = options.population
    this._networkedUrl = 'http://' + options.bind.networked.address + ':' + options.bind.networked.port + '/'

    var ping = coalesce(options.ping, {})
    var timeout = coalesce(options.timeout, {})

    this._ping = {
        paxos: coalesce(ping.paxos, 1000),
        chaperon: coalesce(timeout.paxos, 1000)
    }
    this._timeout = {
        chaperon: coalesce(timeout.paxos, 5000),
        paxos: coalesce(timeout.paxos, 5000),
        http: coalesce(timeout.http, 5000)
    }

    var scheduler = new Scheduler
    var timer = new Timer(scheduler)

    destructible.destruct.wait(scheduler, 'clear')
    destructible.destruct.wait(timer.events.pump(this, '_scheduled', destructible.monitor('timer')), 'destroy')
    destructible.destruct.wait(scheduler.events.pump(timer, 'enqueue', destructible.monitor('scheduler')), 'destroy')

    this.scheduler = scheduler

    this._instance = 0
    this._ua = new Vizsla

    this.events = new Procession

    this.reactor = new Reactor(this, function (dispatcher) {
        dispatcher.dispatch('GET /', 'index')
        dispatcher.dispatch('POST /register', 'register')
        dispatcher.dispatch('GET /backlog', 'backlog')
        dispatcher.dispatch('POST /broadcast', 'broadcast')
        dispatcher.dispatch('POST /record', 'record')
        dispatcher.dispatch('GET /ping', 'ping')
        dispatcher.dispatch('GET /health', 'health')
    })
}

Local.prototype.index = cadence(function (async) {
    return [ 200, { 'content-type': 'text/plain' }, 'Compassion Local API\n' ]
})

Local.prototype._record = cadence(function (async, destructible, kibitzer, id) {
    destructible.destruct.wait(kibitzer.played.pump(new Recorder(this.events, id, 'kibitzer'), 'record', destructible.monitor('played')), 'destroy')
    destructible.destruct.wait(kibitzer.paxos.outbox.pump(new Recorder(this.events, id, 'paxos'), 'record', destructible.monitor('paxos')), 'destroy')
    destructible.destruct.wait(kibitzer.islander.outbox.pump(new Recorder(this.events, id, 'islander'), 'record', destructible.monitor('islander')), 'destroy')
    return kibitzer
})

Local.prototype.colleague = cadence(function (async, destructible, envelope) {
    var kibitzer
    async(function () {
        var ua = new Vizsla().bind({ timeout: this._timeout.http })
        kibitzer = new Kibitzer({
            id: envelope.id,
            ping: this._ping.paxos,
            timeout: this._timeout.paxos,
            ua: {
                send: cadence(function (async, envelope) {
                    logger.info('recorded', { source: 'ua', method: envelope.method, $envelope: envelope })
                    ua.fetch({
                        url: envelope.to.url
                    }, {
                        url: './kibitz',
                        post: envelope,
                        parse: 'json',
                        nullify: true
                    }, async())
                })
            }
        })
        destructible.monitor('kibitzer', kibitzer, 'listen', async())
    }, function () {
        destructible.monitor('record', this, '_record', kibitzer, envelope.id, async())
    }, function (kibitzer) {
        async(function () {
            crypto.randomBytes(32, async())
        }, function (bytes) {
            if (this.colleagues.island[envelope.island] == null) {
                this.colleagues.island[envelope.island] = {}
            }
            var events = this.events
            var conference = new this._Conference(destructible, {
                acclimate: function () { kibitzer.acclimate() },
                publish: function (record, envelope) { kibitzer.publish(envelope) },
                broadcasts: cadence(function (async, promise) {
                    async(function () {
                        var government = kibitzer.paxos.government
                        var leaderUrl = government.properties[government.majority[0]].url
                        colleague.ua.fetch({
                            url: leaderUrl
                        }, {
                            url: './broadcasts',
                            post: { promise: promise },
                            raise: true,
                            parse: 'json'
                        }, async())
                    }, function (body) {
                        events.push({
                            type: 'broadcasts',
                            id: colleague.initalizer.id,
                            body: body
                        })
                        return [ body ]
                    })
                })
            }, envelope, kibitzer)
            conference.log.pump(new Recorder(this.events, envelope.id, 'entry'), 'record', destructible.monitor('log'))
            destructible.destruct.wait(function () { conference.log.push(null) })
            conference.consumed.pump(new Recorder(this.events, envelope.id, 'consumed'), 'record', destructible.monitor('consumed'))
            destructible.destruct.wait(function () { conference.consumed.push(null) })
            var log = kibitzer.paxos.log.pump(conference, 'entry', destructible.monitor('entries'))
            destructible.destruct.wait(log, 'destroy')
            // kibitzer.paxos.log.pump(new Recorder(this.events, envelope.id, 'entry'), 'record', destructible.monitor('events'))
            destructible.destruct.wait(kibitzer.paxos.pinged.pump(this, function () {
                this.scheduler.schedule(Date.now() + this._timeout.chaperon, Keyify.stringify({
                    island: envelope.island,
                    id: envelope.id
                }), {
                    name: 'recoverable',
                    island: envelope.island,
                    id: envelope.id
                })
            }, destructible.monitor('pinged')), 'destroy')
            destructible.destruct.wait(this, function () {
                var colleague = this.colleagues.island[envelope.island][envelope.id]
                delete this.colleagues.island[envelope.island][envelope.id]
                delete this.colleagues.token[colleague.token]
            })
            destructible.destruct.wait(function () {
                conference.outbox.push(null)
            })
            destructible.destruct.wait(this, function () {
                this.scheduler.unschedule(Keyify.stringify({
                    island: envelope.island,
                    id: envelope.id
                }))
            })
            var token = bytes.toString('hex')
            var colleague = this.colleagues.island[envelope.island][envelope.id] = {
                token: token,
                destructible: destructible,
                initalizer: envelope,
                kibitzer: kibitzer,
                conference: conference,
                createdAt: Date.now(),
                ua: new Vizsla().bind({
                    url: envelope.url,
                    raise: true,
                    parse: 'json'
                })
            }
            this.colleagues.token[token] = colleague
            this.scheduler.schedule(Date.now(), Keyify.stringify({
                island: colleague.initalizer.island,
                id: colleague.initalizer.id
            }), {
                name: 'register',
                island: colleague.initalizer.island,
                id: colleague.initalizer.id
            })
            return []
        })
    })
})

var discover = require('./discover')
var embark = require('./embark')
var recoverable = require('./recoverable')

Local.prototype.terminate = function (island, id) {
    var island = this.colleagues.island[island]
    if (island != null) {
        var colleague = island[id]
        if (colleague != null) {
            colleague.destructible.destroy()
        }
    }
}

Local.prototype._overwatch = cadence(function (async, envelope, members, complete) {
    var island = this.colleagues.island[envelope.body.island]
    if (island == null) {
        return
    }
    var colleague = island[envelope.body.id]
    if (colleague == null) {
        return null
    }
    var action
    switch (envelope.body.name) {
    case 'register':
        action = { action: 'register' }
        break
    case 'discover':
        action = discover(envelope.body.id, members, complete)
        break
    case 'embark':
        action = embark(members, 0)
        break
    case 'recoverable':
        action = recoverable(envelope.body.id, members)
        break
    }
    switch (action.action) {
    case 'register':
        // TODO Handle 404.
        async(function () {
            this._ua.fetch({
                url: colleague.initalizer.url,
                token: colleague.initalizer.token,
                timeout: this._timeout.http,
                raise: true,
                parse: 'json'
            }, {
                url: './register',
                post: {
                    token: colleague.token
                }
            }, async())
        }, function () {
            this.scheduler.schedule(Date.now(), Keyify.stringify({
                island: envelope.body.island,
                id: envelope.body.id
            }), {
                name: 'discover',
                island: envelope.body.island,
                id: envelope.body.id
            })
        })
        break
    case 'bootstrap':
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.initalizer.properties, {})))
        properties.url = action.url
        colleague.kibitzer.bootstrap(0, properties)
        break
    case 'join':
        colleague.kibitzer.join(0)
        this.scheduler.schedule(Date.now(), Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: action.url,
            republic: action.republic
        })
        break
    case 'embark':
        // Schedule a subsequent embarkation. Once our Paxos object starts
        // receiving message, the embarkation is cleared and replaced with a
        // recoverable check.
        this.scheduler.schedule(Date.now() + this._ping.chaperon, Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: envelope.body.url,
            republic: envelope.body.republic
        })
        // If this fails, we don't care. Embarkation is designed to be
        // asynchronous in the macro. You send a message. Maybe it gets there,
        // maybe it doesn't. You'll know when the Paxos object starts working.
        // Until then, keep sending embarkation requests. The leader knows how
        // to deal with duplicate or in-process requests.
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.initalizer.properties, {})))
        properties.url = envelope.body.url
        this._ua.fetch({
            url: action.url,
            timeout: this._timeout.http,
            nullify: true,
            parse: 'json'
        }, {
            url: './arrive',
            post: {
                republic: envelope.body.republic,
                id: colleague.kibitzer.paxos.id,
                cookie: colleague.kibitzer.paxos.cookie,
                properties: properties
            }
        }, async())
        break
    case 'retry':
        this.scheduler.schedule(Date.now() + this._ping.chaperon, envelope.key, envelope.body)
        break
    case 'unrecoverable':
        colleague.destructible.destroy()
        break
    }
})

// Race conditions to consider — events are not going to fire for a
// destroyed colleague because we clear their schedule on destruct. However, we
// the colleague might get destroyed while we are gathering the census. That is
// why we check that the colleague still exists in `_overwatch`.
//
// In overwatch, we schedule our next action, an synchronous operation, before
// we do anything asynchronous, so we know that we have a good colleague. Our
// asynchronous operation is a tail operation. If something particularly bad
// happens.

//
Local.prototype._scheduled = cadence(function (async, envelope) {
    if (!(envelope != null && envelope.module == 'happenstance' && envelope.method == 'event')) {
        return
    }
    async(function () {
        this._population.census(envelope.body.island, async())
    }, function (members, complete) {
        this._overwatch(envelope, members, complete, async())
    })
})

Local.prototype._getColleague = function (request) {
    if (request.authorization.scheme != 'Bearer') {
        throw 401
    }
    var colleague = this.colleagues.token[request.authorization.credentials]
    if (colleague == null) {
        throw 401
    }
    return colleague
}

// TODO Abend if the colleague is not waiting on a `join` notification.
Local.prototype.backlog = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    var government = colleague.kibitzer.paxos.government
    async(function () {
        colleague.ua.fetch({
            url: government.properties[government.majority[0]].url
        }, {
            url: './backlog',
            post: {
                promise: government.arrived.promise[colleague.initalizer.id]
            },
            parse: 'stream',
            raise: true
        }, async())
    }, function (body, response) {
        return [ 200, response.headers, Backlogger({
            events: this.events,
            id: colleague.initalizer.id,
            contentType: response.headers['content-type'],
            input: body
        }) ]
    })
})

Local.prototype.register = cadence(function (async, request) {
    // If we already have one and it doesn't match, then we destroy this one.
    // Create a new instance.
    async(function () {
        this._destructible.monitor([ 'colleague', this._instance++ ], true, this, 'colleague', request.body, async())
    }, function () {
        return 200
    })
})

Local.prototype.record = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    this.events.push({
        type: 'record',
        id: colleague.initalizer.id,
        body: request.body
    })
    return request.body
})

Local.prototype.broadcast = cadence(function (async, request) {
    var colleague = this._getColleague(request)
    colleague.conference.broadcast(request.body.method, request.body.message)
    return 200
})

module.exports = Local
<|endoftext|>


<commit_before>'use strict'

let winston = require('winston')
let Rat = require('../api/models/rat')
let Rescue = require('../api/models/rescue')
let mongoose = require('mongoose')

mongoose.connect('mongodb://localhost/fuelrats')

winston.info('Starting unidentified rats fix')

Rescue.find({ unidentifiedRats: { '$exists': true, '$not': {'$size': 0}}  }).exec().then(function (rescues) {
  winston.info('Found ' + rescues.length + ' rescues with unidentified rats')
  rescues.forEach(function (rescue) {
    rescue.unidentifiedRats.forEach(function (unidentifiedRat) {
      Rat.findOne({ CMDRname: { $regex: new RegExp('^' + unidentifiedRat, 'i') } }).exec().then(function (rat) {
        if (!rat) {
          winston.info('Unable to find rat for ' + unidentifiedRat)
        } else {
          rescue.rats.push(rat)
          rescue.unidentifiedRats.splice(rescue.unidentifiedRats.indexOf(unidentifiedRat), 1)
          rescue.save(function (err) {
            if (err) {
              winston.error(err)
            } else {
              winston.info('Found rat ' + unidentifiedRat + ', adding to rescue')
            }
          })
        }
      }, function (error) {
        winston.error(error)
      })
    })
  })
}, function (error) {
  winston.error(error)
})
<commit_msg>Trim unidentified rat<commit_after>'use strict'

let winston = require('winston')
let Rat = require('../api/models/rat')
let Rescue = require('../api/models/rescue')
let mongoose = require('mongoose')

mongoose.connect('mongodb://localhost/fuelrats')

winston.info('Starting unidentified rats fix')

Rescue.find({ unidentifiedRats: { '$exists': true, '$not': {'$size': 0}}  }).exec().then(function (rescues) {
  winston.info('Found ' + rescues.length + ' rescues with unidentified rats')
  rescues.forEach(function (rescue) {
    rescue.unidentifiedRats.forEach(function (unidentifiedRat) {
      Rat.findOne({ CMDRname: { $regex: new RegExp('^' + unidentifiedRat.trim(), 'i') } }).exec().then(function (rat) {
        if (!rat) {
          winston.info('Unable to find rat for ' + unidentifiedRat)
        } else {
          rescue.rats.push(rat)
          rescue.unidentifiedRats.splice(rescue.unidentifiedRats.indexOf(unidentifiedRat), 1)
          rescue.save(function (err) {
            if (err) {
              winston.error(err)
            } else {
              winston.info('Found rat ' + unidentifiedRat + ', adding to rescue')
            }
          })
        }
      }, function (error) {
        winston.error(error)
      })
    })
  })
}, function (error) {
  winston.error(error)
})
<|endoftext|>


<commit_before>// VERSION 1.0.0

this.__defineGetter__('searchSite', function() { return window.searchSite; });

this.SearchSite = {
	initialized: false,

	kBtnId: "searchsite-go-button-urlbar",
	kMenuId: "searchsite-domain-menu-urlbar",

	get btn () { return $(this.kBtnId); },

	handleEvent: function(e) {
		switch(e.type) {
			case 'click':
			case 'command':
				// Only so searchSite's methods still work, this is actually kind of irrelevant for this case.
				searchSite.domainTriggerNode = e.originalTarget;

				// We need to copy the value in the urlbar to the search bar so that it knows what to search for.
				// This is not a problem when we focused the location bar and the search bar's value already was pasted in it.
				if(gURLBar.valueIsTyped) {
					gSearchBar._textbox.value = gURLBar.value;
				}

				gSearchBar.handleSearchCommand(e);
				break;

			case 'focus':
			case 'input':
			case 'blur':
				this.btn.hidden = !gURLBar.value || (!gURLBar.valueIsTyped && gURLBar.value != gSearchBar._textbox.value);
				break;
		}
	},

	observe: function(aSubject, aTopic, aData) {
		switch(aSubject) {
			case 'showOnlyNonEmptySearchBar':
			case 'searchEnginesInURLBar':
			case 'awesomerURLBar':
				this.toggle();
				break;
		}
	},

	onWidgetAdded: function(aWidgetId) {
		if(aWidgetId == 'search-container') {
			this.toggle();
		}
	},

	onWidgetRemoved: function(aWidgetId) {
		if(aWidgetId == 'search-container') {
			this.toggle();
		}
	},

	toggle: function() {
		if(Prefs.awesomerURLBar && Prefs.searchEnginesInURLBar && Prefs.showOnlyNonEmptySearchBar) {
			let placement = CustomizableUI.getPlacementOfWidget("search-container");
			if(placement && placement.area == CustomizableUI.AREA_NAVBAR) {
				this.init();
				return;
			}
		}
		this.uninit();
	},

	init: function() {
		if(this.initialized) { return; }
		this.initialized = true;

		// Base the context menu on the original, but we need to change the commands when the entries are clicked.
		let menu = $("searchsite-domain-menu").cloneNode(true);
		menu.id += "-urlbar";
		for(let child of menu.childNodes) {
			if(child.id) {
				child.id += "-urlbar";
				removeAttribute(child, 'onclick');
				Listeners.add(child, "command", this);
			}
		}
		$("mainPopupSet").appendChild(menu);

		// Search Site uses a template node to create its button, we can obviously do the same.
		let btn = $("searchsite-template").childNodes[0].cloneNode(true);
		btn.id = this.kBtnId;
		removeAttribute(btn, 'onclick');
		Listeners.add(btn, "click", this);
		$("urlbar-icons").appendChild(btn);

		Listeners.add(gURLBar, 'focus', this);
		Listeners.add(gURLBar, 'input', this);
		Listeners.add(gURLBar, 'blur', this);
	},

	uninit: function() {
		if(!this.initialized) { return; }
		this.initialized = false;

		let menu = $(this.kMenuId);
		for(let child of menu.childNodes) {
			if(child.id) {
				Listeners.remove(child, "command", this);
			}
		}
		menu.remove();

		let btn = this.btn;
		Listeners.remove(btn, "click", this);
		btn.remove();

		Listeners.remove(gURLBar, 'focus', this);
		Listeners.remove(gURLBar, 'input', this);
		Listeners.remove(gURLBar, 'blur', this);
	}
};

Modules.LOADMODULE = function() {
	CustomizableUI.addListener(SearchSite);

	Prefs.listen('showOnlyNonEmptySearchBar', SearchSite);
	Prefs.listen('searchEnginesInURLBar', SearchSite);
	Prefs.listen('awesomerURLBar', SearchSite);

	SearchSite.toggle();
};

Modules.UNLOADMODULE = function() {
	CustomizableUI.removeListener(SearchSite);

	Prefs.unlisten('showOnlyNonEmptySearchBar', SearchSite);
	Prefs.unlisten('searchEnginesInURLBar', SearchSite);
	Prefs.unlisten('awesomerURLBar', SearchSite);

	SearchSite.uninit();
};
<commit_msg>Closes #152 Search Site's icon not being updated correctly sometimes<commit_after>// VERSION 1.0.1

this.__defineGetter__('searchSite', function() { return window.searchSite; });

this.SearchSite = {
	initialized: false,

	kBtnId: "searchsite-go-button-urlbar",
	kMenuId: "searchsite-domain-menu-urlbar",

	get btn () { return $(this.kBtnId); },

	handleEvent: function(e) {
		switch(e.type) {
			case 'click':
			case 'command':
				// Only so searchSite's methods still work, this is actually kind of irrelevant for this case.
				searchSite.domainTriggerNode = e.originalTarget;

				// We need to copy the value in the urlbar to the search bar so that it knows what to search for.
				// This is not a problem when we focused the location bar and the search bar's value already was pasted in it.
				if(gURLBar.valueIsTyped) {
					gSearchBar._textbox.value = gURLBar.value;
				}

				gSearchBar.handleSearchCommand(e);
				break;

			case 'focus':
			case 'input':
			case 'blur':
			case 'keydown':
				if(!Timers.SearchSite) {
					Timers.init('SearchSite', () => { this.buttonVisibility(); });
				}
				break;
		}
	},

	observe: function(aSubject, aTopic, aData) {
		switch(aSubject) {
			case 'showOnlyNonEmptySearchBar':
			case 'searchEnginesInURLBar':
			case 'awesomerURLBar':
				this.toggle();
				break;
		}
	},

	onWidgetAdded: function(aWidgetId) {
		if(aWidgetId == 'search-container') {
			this.toggle();
		}
	},

	onWidgetRemoved: function(aWidgetId) {
		if(aWidgetId == 'search-container') {
			this.toggle();
		}
	},

	buttonVisibility: function() {
		this.btn.hidden = !gURLBar.value || (!gURLBar.valueIsTyped && gURLBar.value != gSearchBar._textbox.value);
	},

	toggle: function() {
		if(Prefs.awesomerURLBar && Prefs.searchEnginesInURLBar && Prefs.showOnlyNonEmptySearchBar) {
			let placement = CustomizableUI.getPlacementOfWidget("search-container");
			if(placement && placement.area == CustomizableUI.AREA_NAVBAR) {
				this.init();
				return;
			}
		}
		this.uninit();
	},

	init: function() {
		if(this.initialized) { return; }
		this.initialized = true;

		// Base the context menu on the original, but we need to change the commands when the entries are clicked.
		let menu = $("searchsite-domain-menu").cloneNode(true);
		menu.id += "-urlbar";
		for(let child of menu.childNodes) {
			if(child.id) {
				child.id += "-urlbar";
				removeAttribute(child, 'onclick');
				Listeners.add(child, "command", this);
			}
		}
		$("mainPopupSet").appendChild(menu);

		// Search Site uses a template node to create its button, we can obviously do the same.
		let btn = $("searchsite-template").childNodes[0].cloneNode(true);
		btn.id = this.kBtnId;
		removeAttribute(btn, 'onclick');
		Listeners.add(btn, "click", this);
		$("urlbar-icons").appendChild(btn);

		Listeners.add(gURLBar, 'focus', this);
		Listeners.add(gURLBar, 'input', this);
		Listeners.add(gURLBar, 'blur', this);
		// Some special keys, like Esc, modify the urlbar's value but don't trigger an input event.
		Listeners.add(gURLBar, 'keydown', this);
	},

	uninit: function() {
		if(!this.initialized) { return; }
		this.initialized = false;

		let menu = $(this.kMenuId);
		for(let child of menu.childNodes) {
			if(child.id) {
				Listeners.remove(child, "command", this);
			}
		}
		menu.remove();

		let btn = this.btn;
		Listeners.remove(btn, "click", this);
		btn.remove();

		Listeners.remove(gURLBar, 'focus', this);
		Listeners.remove(gURLBar, 'input', this);
		Listeners.remove(gURLBar, 'blur', this);
		Listeners.remove(gURLBar, 'keydown', this);
	}
};

Modules.LOADMODULE = function() {
	CustomizableUI.addListener(SearchSite);

	Prefs.listen('showOnlyNonEmptySearchBar', SearchSite);
	Prefs.listen('searchEnginesInURLBar', SearchSite);
	Prefs.listen('awesomerURLBar', SearchSite);

	SearchSite.toggle();
};

Modules.UNLOADMODULE = function() {
	CustomizableUI.removeListener(SearchSite);

	Prefs.unlisten('showOnlyNonEmptySearchBar', SearchSite);
	Prefs.unlisten('searchEnginesInURLBar', SearchSite);
	Prefs.unlisten('awesomerURLBar', SearchSite);

	SearchSite.uninit();
};
<|endoftext|>


<commit_before>var request = require('request'),
  app = require('../../server'),
  cheerio = require('cheerio'),
  _ = require('underscore'),
  h = require('apis-helpers');

var getParamFromURL = function(url, param) {
    // This feels wrong.
    var params = url.split('?')[1].split('&');

    for (p in params) {
        var pieces = params[p].split('=');
	if (pieces[0] === param) {
	    return pieces[1];
	}
    }
    throw 'Key not found in query parameters';
};

app.get('/golf/teetimes', function(req, res) {
  var clubId = req.query.club;
  if(!clubId) {
    return res.json(431, {error: 'Please provide a valid club id to lookup'});
  }

  request.get({
    rejectUnauthorized: false, // http://stackoverflow.com/a/20091919
    headers: {'User-Agent': h.browser()},
    url: 'https://golf.is/pages/rastimar/rastimayfirlit/?club=' + clubId
  }, function(err, response, html) {
    if(err || response.statusCode !== 200) {
      return res.json(500,{error: 'www.golf.is refuses to respond or give back data'});
    }
    $ = cheerio.load(html);
    var rows = $('table.teeTimeTable tbody').children();
    var time = '';
    return res.cache().json({ results: _.map(rows, function(row) {
	var row = $(row);
	time = row.children('td.time').html() === null ? time : row.children('td.time').html();
	return {
	  time: time,
	  name: $(row.children('td.name')).html(),
	  club: $(row.children('td.club')).html(),
	  handicap: $(row.children('td.handicap')).html()
	};
    })});
  });
});

app.get('/golf/clubs', function(req, res) {
  request.get({
    rejectUnauthorized: false, // http://stackoverflow.com/a/20091919
    headers: {'User-Agent': h.browser()},
    url: 'https://www.golf.is/pages/rastimar/'
  }, function(err, response, html) {
      if(err || response.statusCode !== 200)
        return res.json(500,{error: 'www.golf.is refuses to respond or give back data'});
      $ = cheerio.load(html);
      var rows = $('table.golfTable tr').slice(2); // Skip the first element.
      return res.cache(3600).json({ results: _.map(rows, function(row) {
	var row = $(row);
	return {
	  abbreviation: row.children('td.abbreviation').html(),
	  club: {
	    id: getParamFromURL(row.children('td.club').children('a').attr('href'), 'club'),
	    name: row.children('td.club').children('a').html()
	  },
	  location: row.children('td.location').html()
	};
      })});
  });
});
<commit_msg>Fix indenting. No code change.<commit_after>var request = require('request'),
app = require('../../server'),
cheerio = require('cheerio'),
_ = require('underscore'),
h = require('apis-helpers');

var getParamFromURL = function(url, param) {
  // This feels wrong.
  var params = url.split('?')[1].split('&');

  for (p in params) {
    var pieces = params[p].split('=');
    if (pieces[0] === param) {
      return pieces[1];
    }
  }
  throw 'Key not found in query parameters';
};

app.get('/golf/teetimes', function(req, res) {
  var clubId = req.query.club;
  if(!clubId) {
    return res.json(431, {error: 'Please provide a valid club id to lookup'});
  }

  request.get({
    rejectUnauthorized: false, // http://stackoverflow.com/a/20091919
    headers: {'User-Agent': h.browser()},
    url: 'https://golf.is/pages/rastimar/rastimayfirlit/?club=' + clubId
  }, function(err, response, html) {
    if(err || response.statusCode !== 200) {
      return res.json(500,{error: 'www.golf.is refuses to respond or give back data'});
    }
    $ = cheerio.load(html);
    var rows = $('table.teeTimeTable tbody').children();
    var time = '';
    return res.cache().json({ results: _.map(rows, function(row) {
      var row = $(row);
      time = row.children('td.time').html() === null ? time : row.children('td.time').html();
      return {
        time: time,
        name: $(row.children('td.name')).html(),
        club: $(row.children('td.club')).html(),
        handicap: $(row.children('td.handicap')).html()
      };
    })});
  });
});

app.get('/golf/clubs', function(req, res) {
  request.get({
    rejectUnauthorized: false, // http://stackoverflow.com/a/20091919
    headers: {'User-Agent': h.browser()},
    url: 'https://www.golf.is/pages/rastimar/'
  }, function(err, response, html) {
    if(err || response.statusCode !== 200)
      return res.json(500,{error: 'www.golf.is refuses to respond or give back data'});
    $ = cheerio.load(html);
    var rows = $('table.golfTable tr').slice(2); // Skip the first element.
    return res.cache(3600).json({ results: _.map(rows, function(row) {
      var row = $(row);
      return {
        abbreviation: row.children('td.abbreviation').html(),
        club: {
          id: getParamFromURL(row.children('td.club').children('a').attr('href'), 'club'),
          name: row.children('td.club').children('a').html()
        },
        location: row.children('td.location').html()
      };
    })});
  });
});
<|endoftext|>


<commit_before>var cadence = require('cadence')
var UserAgent = require('vizsla')
var logger = require('prolific.logger').createLogger('compassion.colleague')

var Kibitzer = require('kibitz')
var Procession = require('procession')

var coalesce = require('extant')

var Timer = require('happenstance/timer')
var Scheduler = require('happenstance/scheduler')

var Keyify = require('keyify')

var Recorder = require('./recorder')

var Conduit = require('conduit/conduit')

var Deserialize = require('procession/deserialize')

var Staccato = require('staccato')

function Local (destructible, colleagues, options) {
    this._destructible = destructible

    this.colleagues = colleagues

    this._Conference = options.Conference
    this._population = options.population
    this._networkedUrl = 'http://' + options.bind.address + ':' + options.bind.port + '/'

    var ping = coalesce(options.ping, {})
    var timeout = coalesce(options.timeout, {})

    this._ping = {
        paxos: coalesce(ping.paxos, 1000),
        chaperon: coalesce(timeout.paxos, 1000)
    }
    this._timeout = {
        chaperon: coalesce(timeout.paxos, 5000),
        paxos: coalesce(timeout.paxos, 5000),
        http: coalesce(timeout.http, 5000)
    }

    var scheduler = new Scheduler
    var timer = new Timer(scheduler)

    destructible.destruct.wait(scheduler, 'clear')
    destructible.durable('timer', timer.events.pump(this, '_scheduled'), 'destructible', null)
    destructible.durable('scheduler', scheduler.events.pump(timer, 'enqueue'), 'destructible', null)

    this.scheduler = scheduler

    this._instance = 0
    this._ua = new UserAgent().bind({ timeout: this._timeout.http })

    this.events = new Procession
}

Local.prototype._record = cadence(function (async, destructible, kibitzer, id) {
    destructible.durable('played', kibitzer.played.pump(new Recorder(this.events, id, 'kibitzer'), 'record'), 'destructible', null)
    destructible.durable('paxos', kibitzer.paxos.outbox.pump(new Recorder(this.events, id, 'paxos'), 'record'), 'destructible', null)
    destructible.durable('islander', kibitzer.islander.outbox.pump(new Recorder(this.events, id, 'islander'), 'record'), 'destructible', null)
    return kibitzer
})

Local.prototype._connect = cadence(function (async, destructible, inbox, outbox) {
    var shifter = inbox.shifter()
    var connection = new Connection(destructible, this)
    async(function () {
        destructible.durable('conduit', Conduit, shifter, outbox, connection, 'connect', async())
    }, function (conduit) {
        destructible.destruct.wait(conduit.shifter, 'destroy')
        connection.conduit = conduit
    })
})

var discover = require('./discover')
var embark = require('./embark')
var recoverable = require('./recoverable')

function Connection (destructible, colleague, ua) {
    this.destructible = destructible
    this.colleague = colleague
    this.ua = ua
    this.destroyed = false
    destructible.destruct.wait(this, function () { this.destroyed = true })
}

Connection.prototype.connect = cadence(function (async, envelope, inbox, outbox) {
    var ua = this.colleague._ua
    switch (envelope.method) {
    case 'ready':
        this.outbox = outbox
        this.id = envelope.registration.id
        this.island = envelope.registration.island
        this.properties = coalesce(envelope.registration.properties, {})
        this.destructible.context.push(this.island, this.id, this.properties)
        this.destructible.durable('inbox', inbox.pump(this, 'receive'), 'destructible', null)
        this.colleague.outbox = outbox
        this.kibitzer = new Kibitzer({
            id: this.id,
            ping: this.colleague._ping.paxos,
            timeout: this.colleague._timeout.paxos,
            ua: {
                send: cadence(function (async, envelope) {
                    logger.info('recorded', { source: 'ua', method: envelope.method, $envelope: envelope })
                    ua.fetch({
                        url: envelope.to.url
                    }, {
                        url: './kibitz',
                        post: envelope,
                        parse: 'json',
                        nullify: true
                    }, async())
                })
            }
        })
        this.destructible.durable('pinged', this.kibitzer.paxos.pinged.pump(this, function (envelope) {
            if (envelope == null) {
                return
            }
            this.colleague.scheduler.schedule(Date.now() + this.colleague._timeout.chaperon, Keyify.stringify({
                island: this.island,
                id: this.id
            }), {
                name: 'recoverable',
                island: this.island,
                id: this.id
            })
        }), 'destructible', null)
        this.destructible.destruct.wait(this, function () {
            var colleague = this.colleague.colleagues.island[this.island][this.id]
            delete this.colleague.colleagues.island[this.island][this.id]
        })
        this.destructible.destruct.wait(this, function () {
            this.colleague.scheduler.unschedule(Keyify.stringify({ island: this.island, id: this.id }))
        })
        this.destructible.durable('entries', this.kibitzer.paxos.log.pump(this, 'entry'), 'destructible', null)
        async(function () {
            this.destructible.durable('kibitzer', this.kibitzer, 'listen', async())
        }, function () {
            this.destructible.durable('record', this.colleague, '_record', this.kibitzer, this.id, async())
        }, function () {
            this.colleague.scheduler.schedule(Date.now(), Keyify.stringify({
                island: this.island,
                id: this.id
            }), {
                name: 'discover',
                island: this.island,
                id: this.id
            })
            outbox.push({
                method: 'ready',
                island: this.island,
                id: this.id,
                properties: this.properties
            })
            if (this.colleague.colleagues.island[this.island] == null) {
                this.colleague.colleagues.island[this.island] = {}
            }
            this.colleague.colleagues.island[this.island][this.id] = this
            this.createdAt = Date.now()
        })
        break
    case 'broadcasts':
        var government = this.kibitzer.paxos.government
        var leaderUrl = government.properties[government.majority[0]].url
        ua.fetch({
            url: leaderUrl
        }, {
            url: './broadcasts',
            post: { promise: envelope.promise },
            raise: true,
            parse: 'json'
        }, async())
        break
    case 'snapshot':
        var government = this.kibitzer.paxos.government
        var leaderUrl = government.properties[government.majority[0]].url
        async(function () {
            ua.fetch({
                url: leaderUrl
            }, {
                url: './snapshot',
                post: { promise: envelope.promise },
                raise: true,
                parse: 'stream'
            }, async())
        }, function (stream, response) {
            Deserialize(new Staccato.Readable(stream), outbox, async())
        })
        break
    }
})

Connection.prototype.entry = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    this.outbox.push({ method: 'entry', body: envelope })
})

Connection.prototype.receive = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    switch (envelope.method) {
    case 'acclimate':
        this.kibitzer.acclimate()
        return []
    case 'broadcast':
    case 'reduce':
        this.kibitzer.publish(envelope)
        return []
    }
})

Local.prototype._getColleagueByIslandAndId = function (island, id) {
    var island = this.colleagues.island[island]
    if (island != null) {
        var colleague = island[id]
        if (colleague != null) {
            return colleague
        }
    }
    return null
}

Local.prototype.terminate = function (island, id) {
    var colleague = this._getColleagueByIslandAndId(island, id)
    if (colleague != null) {
        colleague.destructible.destroy()
    }
}

Local.prototype.ids = function (island) {
    var ids = []
    var island = this.colleagues.island[island]
    if (island != null) {
        ids = Object.keys(island).sort()
    }
    return ids
}

Local.prototype._overwatch = cadence(function (async, colleague, envelope, members, complete) {
    var action
    switch (envelope.body.name) {
    case 'discover':
        action = discover(envelope.body.id, members, complete)
        break
    case 'embark':
        action = embark(members, envelope.body.republic)
        break
    case 'recoverable':
        action = recoverable(envelope.body.id, members)
        break
    }
    logger.notice('overwatch', {
        $members: members,
        $envelope: envelope,
        $action: action,
        complete: complete
    })
    switch (action.action) {
    case 'bootstrap':
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.properties, {})))
        properties.url = action.url
        colleague.kibitzer.bootstrap(Date.now(), properties)
        break
    case 'join':
        colleague.kibitzer.join(action.republic)
        this.scheduler.schedule(Date.now(), Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: action.url,
            republic: action.republic
        })
        break
    case 'embark':
        // Schedule a subsequent embarkation. Once our Paxos object starts
        // receiving message, the embarkation is cleared and replaced with a
        // recoverable check.
        this.scheduler.schedule(Date.now() + this._ping.chaperon, Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: envelope.body.url,
            republic: envelope.body.republic
        })
        // If this fails, we don't care. Embarkation is designed to be
        // asynchronous in the macro. You send a message. Maybe it gets there,
        // maybe it doesn't. You'll know when the Paxos object starts working.
        // Until then, keep sending embarkation requests. The leader knows how
        // to deal with duplicate or in-process requests.
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.properties, {})))
        properties.url = envelope.body.url
        this._ua.fetch({
            url: action.url,
            timeout: this._timeout.http,
            nullify: true,
            parse: 'json'
        }, {
            url: './embark',
            post: {
                republic: envelope.body.republic,
                id: colleague.kibitzer.paxos.id,
                cookie: colleague.kibitzer.paxos.cookie,
                properties: properties
            }
        }, async())
        break
    case 'retry':
        this.scheduler.schedule(Date.now() + this._ping.chaperon, envelope.key, envelope.body)
        break
    case 'unrecoverable':
        colleague.destructible.destroy()
        break
    }
})

// Race conditions to consider — events are not going to fire for a
// destroyed colleague because we clear their schedule on destruct. However, we
// the colleague might get destroyed while we are gathering the census. That is
// why we check that the colleague still exists in `_overwatch`.
//
// In overwatch, we schedule our next action, an synchronous operation, before
// we do anything asynchronous, so we know that we have a good colleague. Our
// asynchronous operation is a tail operation. If something particularly bad
// happens.

//
Local.prototype._scheduled = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    var colleague = this._getColleagueByIslandAndId(envelope.body.island, envelope.body.id)
    async(function () {
        this._population.census(envelope.body.island, envelope.body.id, async())
    }, function (members, complete) {
        if (!colleague.destroyed) {
            this._overwatch(colleague, envelope, members, complete, async())
        }
    })
})

Local.prototype.connect = cadence(function (async, inbox, outbox) {
    // If we already have one and it doesn't match, then we destroy this one.
    // Create a new instance.
    this._destructible.ephemeral([ 'colleague', this._instance++ ], this, '_connect', inbox, outbox, async())
})

module.exports = Local
<commit_msg>Destroy constituent when any are unrecoverable.<commit_after>var cadence = require('cadence')
var UserAgent = require('vizsla')
var logger = require('prolific.logger').createLogger('compassion.colleague')

var Kibitzer = require('kibitz')
var Procession = require('procession')

var coalesce = require('extant')

var Timer = require('happenstance/timer')
var Scheduler = require('happenstance/scheduler')

var Keyify = require('keyify')

var Recorder = require('./recorder')

var Conduit = require('conduit/conduit')

var Deserialize = require('procession/deserialize')

var Staccato = require('staccato')

function Local (destructible, colleagues, options) {
    this._destructible = destructible

    this.colleagues = colleagues

    this._Conference = options.Conference
    this._population = options.population
    this._networkedUrl = 'http://' + options.bind.address + ':' + options.bind.port + '/'

    var ping = coalesce(options.ping, {})
    var timeout = coalesce(options.timeout, {})

    this._ping = {
        paxos: coalesce(ping.paxos, 1000),
        chaperon: coalesce(timeout.paxos, 1000)
    }
    this._timeout = {
        chaperon: coalesce(timeout.paxos, 5000),
        paxos: coalesce(timeout.paxos, 5000),
        http: coalesce(timeout.http, 5000)
    }

    var scheduler = new Scheduler
    var timer = new Timer(scheduler)

    destructible.destruct.wait(scheduler, 'clear')
    destructible.durable('timer', timer.events.pump(this, '_scheduled'), 'destructible', null)
    destructible.durable('scheduler', scheduler.events.pump(timer, 'enqueue'), 'destructible', null)

    this.scheduler = scheduler

    this._instance = 0
    this._ua = new UserAgent().bind({ timeout: this._timeout.http })

    this.events = new Procession
}

Local.prototype._record = cadence(function (async, destructible, kibitzer, id) {
    destructible.durable('played', kibitzer.played.pump(new Recorder(this.events, id, 'kibitzer'), 'record'), 'destructible', null)
    destructible.durable('paxos', kibitzer.paxos.outbox.pump(new Recorder(this.events, id, 'paxos'), 'record'), 'destructible', null)
    destructible.durable('islander', kibitzer.islander.outbox.pump(new Recorder(this.events, id, 'islander'), 'record'), 'destructible', null)
    return kibitzer
})

Local.prototype._connect = cadence(function (async, destructible, inbox, outbox) {
    var shifter = inbox.shifter()
    var connection = new Connection(destructible, this)
    async(function () {
        destructible.durable('conduit', Conduit, shifter, outbox, connection, 'connect', async())
    }, function (conduit) {
        destructible.destruct.wait(conduit.shifter, 'destroy')
        connection.conduit = conduit
    })
})

var discover = require('./discover')
var embark = require('./embark')
var recoverable = require('./recoverable')

function Connection (destructible, colleague, ua) {
    this.destructible = destructible
    this.colleague = colleague
    this.ua = ua
    this.destroyed = false
    destructible.destruct.wait(this, function () { this.destroyed = true })
}

Connection.prototype.connect = cadence(function (async, envelope, inbox, outbox) {
    var ua = this.colleague._ua
    switch (envelope.method) {
    case 'ready':
        this.outbox = outbox
        this.id = envelope.registration.id
        this.island = envelope.registration.island
        this.properties = coalesce(envelope.registration.properties, {})
        this.destructible.context.push(this.island, this.id, this.properties)
        this.destructible.durable('inbox', inbox.pump(this, 'receive'), 'destructible', null)
        this.colleague.outbox = outbox
        this.kibitzer = new Kibitzer({
            id: this.id,
            ping: this.colleague._ping.paxos,
            timeout: this.colleague._timeout.paxos,
            ua: {
                send: cadence(function (async, envelope) {
                    logger.info('recorded', { source: 'ua', method: envelope.method, $envelope: envelope })
                    ua.fetch({
                        url: envelope.to.url
                    }, {
                        url: './kibitz',
                        post: envelope,
                        parse: 'json',
                        nullify: true
                    }, async())
                })
            }
        })
        this.destructible.durable('pinged', this.kibitzer.paxos.pinged.pump(this, function (envelope) {
            if (envelope == null) {
                return
            }
            this.colleague.scheduler.schedule(Date.now() + this.colleague._timeout.chaperon, Keyify.stringify({
                island: this.island,
                id: this.id
            }), {
                name: 'recoverable',
                island: this.island,
                id: this.id
            })
        }), 'destructible', null)
        this.destructible.destruct.wait(this, function () {
            var colleague = this.colleague.colleagues.island[this.island][this.id]
            delete this.colleague.colleagues.island[this.island][this.id]
        })
        this.destructible.destruct.wait(this, function () {
            this.colleague.scheduler.unschedule(Keyify.stringify({ island: this.island, id: this.id }))
        })
        this.destructible.durable('entries', this.kibitzer.paxos.log.pump(this, 'entry'), 'destructible', null)
        async(function () {
            this.destructible.durable('kibitzer', this.kibitzer, 'listen', async())
        }, function () {
            this.destructible.durable('record', this.colleague, '_record', this.kibitzer, this.id, async())
        }, function () {
            this.colleague.scheduler.schedule(Date.now(), Keyify.stringify({
                island: this.island,
                id: this.id
            }), {
                name: 'discover',
                island: this.island,
                id: this.id
            })
            outbox.push({
                method: 'ready',
                island: this.island,
                id: this.id,
                properties: this.properties
            })
            if (this.colleague.colleagues.island[this.island] == null) {
                this.colleague.colleagues.island[this.island] = {}
            }
            this.colleague.colleagues.island[this.island][this.id] = this
            this.createdAt = Date.now()
        })
        break
    case 'broadcasts':
        var government = this.kibitzer.paxos.government
        var leaderUrl = government.properties[government.majority[0]].url
        ua.fetch({
            url: leaderUrl
        }, {
            url: './broadcasts',
            post: { promise: envelope.promise },
            raise: true,
            parse: 'json'
        }, async())
        break
    case 'snapshot':
        var government = this.kibitzer.paxos.government
        var leaderUrl = government.properties[government.majority[0]].url
        async(function () {
            ua.fetch({
                url: leaderUrl
            }, {
                url: './snapshot',
                post: { promise: envelope.promise },
                raise: true,
                parse: 'stream'
            }, async())
        }, function (stream, response) {
            Deserialize(new Staccato.Readable(stream), outbox, async())
        })
        break
    }
})

Connection.prototype.entry = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    this.outbox.push({ method: 'entry', body: envelope })
})

Connection.prototype.receive = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    switch (envelope.method) {
    case 'acclimate':
        this.kibitzer.acclimate()
        return []
    case 'broadcast':
    case 'reduce':
        this.kibitzer.publish(envelope)
        return []
    }
})

Local.prototype._getColleagueByIslandAndId = function (island, id) {
    var island = this.colleagues.island[island]
    if (island != null) {
        var colleague = island[id]
        if (colleague != null) {
            return colleague
        }
    }
    return null
}

Local.prototype.terminate = function (island, id) {
    var colleague = this._getColleagueByIslandAndId(island, id)
    if (colleague != null) {
        colleague.destructible.destroy()
    }
}

Local.prototype.ids = function (island) {
    var ids = []
    var island = this.colleagues.island[island]
    if (island != null) {
        ids = Object.keys(island).sort()
    }
    return ids
}

Local.prototype._overwatch = cadence(function (async, colleague, envelope, members, complete) {
    var action
    switch (envelope.body.name) {
    case 'discover':
        action = discover(envelope.body.id, members, complete)
        break
    case 'embark':
        action = embark(members, envelope.body.republic)
        break
    case 'recoverable':
        action = recoverable(envelope.body.id, members)
        break
    }
    logger.notice('overwatch', {
        $members: members,
        $envelope: envelope,
        $action: action,
        complete: complete
    })
    switch (action.action) {
    case 'bootstrap':
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.properties, {})))
        properties.url = action.url
        colleague.kibitzer.bootstrap(Date.now(), properties)
        break
    case 'join':
        colleague.kibitzer.join(action.republic)
        this.scheduler.schedule(Date.now(), Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: action.url,
            republic: action.republic
        })
        break
    case 'embark':
        // Schedule a subsequent embarkation. Once our Paxos object starts
        // receiving message, the embarkation is cleared and replaced with a
        // recoverable check.
        this.scheduler.schedule(Date.now() + this._ping.chaperon, Keyify.stringify({
            island: envelope.body.island,
            id: envelope.body.id
        }), {
            name: 'embark',
            island: envelope.body.island,
            id: envelope.body.id,
            url: envelope.body.url,
            republic: envelope.body.republic
        })
        // If this fails, we don't care. Embarkation is designed to be
        // asynchronous in the macro. You send a message. Maybe it gets there,
        // maybe it doesn't. You'll know when the Paxos object starts working.
        // Until then, keep sending embarkation requests. The leader knows how
        // to deal with duplicate or in-process requests.
        var properties = JSON.parse(JSON.stringify(coalesce(colleague.properties, {})))
        properties.url = envelope.body.url
        this._ua.fetch({
            url: action.url,
            timeout: this._timeout.http,
            nullify: true,
            parse: 'json'
        }, {
            url: './embark',
            post: {
                republic: envelope.body.republic,
                id: colleague.kibitzer.paxos.id,
                cookie: colleague.kibitzer.paxos.cookie,
                properties: properties
            }
        }, async())
        break
    case 'retry':
        this.scheduler.schedule(Date.now() + this._ping.chaperon, envelope.key, envelope.body)
        break
    case 'unrecoverable':
        this._destructible.destroy()
        break
    }
})

// Race conditions to consider — events are not going to fire for a
// destroyed colleague because we clear their schedule on destruct. However, we
// the colleague might get destroyed while we are gathering the census. That is
// why we check that the colleague still exists in `_overwatch`.
//
// In overwatch, we schedule our next action, an synchronous operation, before
// we do anything asynchronous, so we know that we have a good colleague. Our
// asynchronous operation is a tail operation. If something particularly bad
// happens.

//
Local.prototype._scheduled = cadence(function (async, envelope) {
    if (envelope == null) {
        return
    }
    var colleague = this._getColleagueByIslandAndId(envelope.body.island, envelope.body.id)
    async(function () {
        this._population.census(envelope.body.island, envelope.body.id, async())
    }, function (members, complete) {
        if (!colleague.destroyed) {
            this._overwatch(colleague, envelope, members, complete, async())
        }
    })
})

Local.prototype.connect = cadence(function (async, inbox, outbox) {
    // If we already have one and it doesn't match, then we destroy this one.
    // Create a new instance.
    this._destructible.ephemeral([ 'colleague', this._instance++ ], this, '_connect', inbox, outbox, async())
})

module.exports = Local
<|endoftext|>