<commit_before>/* global window, document, jQuery */

// ==UserScript==
// @name         Pixiv hover zoom
// @namespace    https://github.com/rplus
// @version      1.1.0
// @description  hover zoom for Pixiv
// @author       Rplus
// @include      http://www.pixiv.net/*
// @grant        none
// ==/UserScript==

;(function(window, document, $) {
  'use strict';

  var phzw = $('#pixiv-hz-wrap');
  var phzwPos = [];
  var phzwPattern = /member_illust\.php\?mode=medium&illust_id=(\d+)/;
  var phzwAPI = '//www.pixiv.net/rpc/index.php?mode=get_illust_detail_by_ids&illust_ids=';
  var phzwAPICache = {};
  var imgSize = 'm'; // size: '240mw', m', 'big'
  var body = document.body;
  var isAutoLoadNextPage = true;
  var pager = {};

  var phzwToggle = function(_switch) {
    if (_switch) {
      phzw.css({
        'top': phzwPos[0],
        'left': phzwPos[1],
        'opacity': 1,
        'visibility': 'visible',
        'transform': 'translateX(' + phzwPos[2] + '%)'
      });
    } else {
      if ('0' !== phzw[0].style.opacity) {
        phzw.css({
          'visibility': 'hidden',
          'opacity': 0
        });
      }
    }
  };

  var getAllId = function() {
    var ids = [];

    $.each($('._unit').find('a'), function() {
      var _href = $(this).attr('href');
      var _match = _href && _href.match(phzwPattern);
      if (_match) {
        ids.push(_match.pop());
      }
    });

    return ids;
  };

  var pullIdsData = function(idsArr) {
    var deferred = $.Deferred();

    $.getJSON(phzwAPI + idsArr.join())
      .done(function(data) {

        if (!data.error) {
          for (var _item in data.body) {
            if (data.body.hasOwnProperty(_item)) {
              phzwAPICache[_item] = data.body[_item];
            }
          }
          deferred.resolve(); //update state
        } else {
          deferred.reject();
        }

      })
      .fail(function() {
        deferred.reject();
      });

    return deferred.promise();
  };

  var render = function(_id) {
    var data = phzwAPICache[_id];
    if (!data) { return; }

    if (_id !== phzw.data('id')) {
      var tpl = '';
      phzw.html(tpl);

      phzw.data('id', _id);
    }

    phzwToggle(true);
  };

  var updatePos = function(ele) {
    var eleRect = ele.getBoundingClientRect();
    phzwPos = [eleRect.top + body.scrollTop + eleRect.height, eleRect.left, -100 * eleRect.left / body.scrollWidth];
  };

  $(function() {

    // phzw init
    if (!phzw.length) {
      $(body).append('
');
      phzw = $('#pixiv-hz-wrap').css({
        'position': 'absolute',
        'visibility': 'hidden',
        'opacity': 0,
        'box-shadow': '0 0 0 3px #fff, 0 0 3px 3px',
        'transition': 'all .3s',
        'z-index': '1000'
      });

      phzw.on('mouseenter', function() {
        phzw.one('mouseleave', function() {
          phzwToggle(false);
        });
      });
    }

    // delay pre-load data in page
    setTimeout(function() {
      pullIdsData(getAllId());
    }, 1000);

    $('._unit').on('mouseenter.phzw', 'a', function(e) {
      var _match = $(this).attr('href').match(phzwPattern);

      if (!_match) { return; }

      var _id = _match.pop();

      e.stopPropagation();
      updatePos(this);

      if (phzwAPICache[_id]) {
        render(_id);
      } else {
        $.when(pullIdsData([_id]))
          .then(function() {
            render(_id);
          });
      }
    });

    $(document).on('click.phzw', function() {
      phzwToggle(false);
    });

    if (isAutoLoadNextPage) {
      pager.container = $('.pager-container').eq(0);

      if (!pager.container.length) { return; }

      pager.current = pager.container.find('li.current').text();
      pager.baseHref = pager.container.find('a')[0].href.replace(/&p=\d+/, '');
      pager.parent = $('._image-items').eq(0).parent();

      var autoLoadNextPage = function() {
        var nextPage = pager.current * 1 + 1;
        var nextPageHref = pager.baseHref + '&p=' + nextPage;
        var separator = '
::: page: ' + nextPage + '';

        if (nextPage > pager.max) {
          $(window).off('scroll.autoLoadNextPage');
          return;
        }

        // prepare a div container for ajax lond next-page content
        $('
').appendTo( pager.parent.append(separator) )
          .load(nextPageHref + ' ._image-items', function(_html) {
            pager.current = nextPage;
            pager.loading = false;

            // check for max page
            var nextPagerLists = _html.match(/pager-container.+?(<ul .+?<\/ul>)/).pop();
            pager.max = $(nextPagerLists).find('li').last().text() * 1;
          });
      };

      $(window).on('scroll.autoLoadNextPage', function() {
        if (!pager.loading && (pager.parent[0].getBoundingClientRect().bottom < document.documentElement.clientHeight * .3)) {
          pager.loading = true;
          autoLoadNextPage();
        }
      });
    }

    $('.link-item').eq(0).prepend('<button>-♥ preload all ♥-</button>').find('button').on('click', function(e) {
      e.preventDefault();

      $.when(pullIdsData(getAllId()))
        .then(function() {
          $.each(phzwAPICache, function(key, val) {
            // forcedly preload
            $('');
          });
        });

    });
  });

})(window, document, jQuery, undefined);
<commit_msg>select all  to filter, allow more links are able to trigger hoverZoom<commit_after>/* global window, document, jQuery */

// ==UserScript==
// @name         Pixiv hover zoom
// @namespace    https://github.com/rplus
// @version      1.1.1
// @description  hover zoom for Pixiv
// @author       Rplus
// @include      http://www.pixiv.net/*
// @grant        none
// ==/UserScript==

;(function(window, document, $) {
  'use strict';

  var phzw = $('#pixiv-hz-wrap');
  var phzwPos = [];
  var phzwPattern = /member_illust\.php\?mode=medium&illust_id=(\d+)/;
  var phzwAPI = '//www.pixiv.net/rpc/index.php?mode=get_illust_detail_by_ids&illust_ids=';
  var phzwAPICache = {};
  var imgSize = 'm'; // size: '240mw', m', 'big'
  var body = document.body;
  var isAutoLoadNextPage = true;
  var pager = {};

  var phzwToggle = function(_switch) {
    if (_switch) {
      phzw.css({
        'top': phzwPos[0],
        'left': phzwPos[1],
        'opacity': 1,
        'visibility': 'visible',
        'transform': 'translateX(' + phzwPos[2] + '%)'
      });
    } else {
      if ('0' !== phzw[0].style.opacity) {
        phzw.css({
          'visibility': 'hidden',
          'opacity': 0
        });
      }
    }
  };

  var getAllId = function() {
    var ids = [];

    $.each($(document).find('a'), function() {
      var _href = $(this).attr('href');
      var _match = _href && _href.match(phzwPattern);
      if (_match) {
        ids.push(_match.pop());
      }
    });

    return ids;
  };

  var pullIdsData = function(idsArr) {
    var deferred = $.Deferred();

    $.getJSON(phzwAPI + idsArr.join())
      .done(function(data) {

        if (!data.error) {
          for (var _item in data.body) {
            if (data.body.hasOwnProperty(_item)) {
              phzwAPICache[_item] = data.body[_item];
            }
          }
          deferred.resolve(); //update state
        } else {
          deferred.reject();
        }

      })
      .fail(function() {
        deferred.reject();
      });

    return deferred.promise();
  };

  var render = function(_id) {
    var data = phzwAPICache[_id];
    if (!data) { return; }

    if (_id !== phzw.data('id')) {
      var tpl = '';
      phzw.html(tpl);

      phzw.data('id', _id);
    }

    phzwToggle(true);
  };

  var updatePos = function(ele) {
    var eleRect = ele.getBoundingClientRect();
    phzwPos = [eleRect.top + body.scrollTop + eleRect.height, eleRect.left, -100 * eleRect.left / body.scrollWidth];
  };

  $(function() {

    // phzw init
    if (!phzw.length) {
      $(body).append('
');
      phzw = $('#pixiv-hz-wrap').css({
        'position': 'absolute',
        'visibility': 'hidden',
        'opacity': 0,
        'box-shadow': '0 0 0 3px #fff, 0 0 3px 3px',
        'transition': 'all .3s',
        'z-index': '1000'
      });

      phzw.on('mouseenter', function() {
        phzw.one('mouseleave', function() {
          phzwToggle(false);
        });
      });
    }

    // delay pre-load data in page
    setTimeout(function() {
      pullIdsData(getAllId());
    }, 1000);

    $(document).on('mouseenter.phzw', 'a', function(e) {
      var _match = $(this).attr('href').match(phzwPattern);

      if (!_match) { return; }

      var _id = _match.pop();

      e.stopPropagation();
      updatePos(this);

      if (phzwAPICache[_id]) {
        render(_id);
      } else {
        $.when(pullIdsData([_id]))
          .then(function() {
            render(_id);
          });
      }
    });

    $(document).on('click.phzw', function() {
      phzwToggle(false);
    });

    if (isAutoLoadNextPage) {
      pager.container = $('.pager-container').eq(0);

      if (!pager.container.length) { return; }

      pager.current = pager.container.find('li.current').text();
      pager.baseHref = pager.container.find('a')[0].href.replace(/&p=\d+/, '');
      pager.parent = $('._image-items').eq(0).parent();

      var autoLoadNextPage = function() {
        var nextPage = pager.current * 1 + 1;
        var nextPageHref = pager.baseHref + '&p=' + nextPage;
        var separator = '
::: page: ' + nextPage + '';

        if (nextPage > pager.max) {
          $(window).off('scroll.autoLoadNextPage');
          return;
        }

        // prepare a div container for ajax lond next-page content
        $('
').appendTo( pager.parent.append(separator) )
          .load(nextPageHref + ' ._image-items', function(_html) {
            pager.current = nextPage;
            pager.loading = false;

            // check for max page
            var nextPagerLists = _html.match(/pager-container.+?(<ul .+?<\/ul>)/).pop();
            pager.max = $(nextPagerLists).find('li').last().text() * 1;
          });
      };

      $(window).on('scroll.autoLoadNextPage', function() {
        if (!pager.loading && (pager.parent[0].getBoundingClientRect().bottom < document.documentElement.clientHeight * .3)) {
          pager.loading = true;
          autoLoadNextPage();
        }
      });
    }

    $('.link-item').eq(0).prepend('<button>-♥ preload all ♥-</button>').find('button').on('click', function(e) {
      e.preventDefault();

      $.when(pullIdsData(getAllId()))
        .then(function() {
          $.each(phzwAPICache, function(key, val) {
            // forcedly preload
            $('');
          });
        });

    });
  });

})(window, document, jQuery, undefined);
<|endoftext|>


<commit_before>import 'jqueryui/draggable-unpatched'
import 'jquery-ui-touch-punch'
<commit_msg>Fix js on mobile login screen — again!<commit_after>import 'jqueryui/draggable-unpatched'

import 'jqueryui/mouse'
import 'jquery-ui-touch-punch'
<|endoftext|>


<commit_before>'use strict'

const FindMyWay = require('find-my-way')
const avvio = require('avvio')
const http = require('http')
const https = require('https')
const pinoHttp = require('pino-http')
const Middie = require('middie')
const fastseries = require('fastseries')
var shot = null
try { shot = require('shot') } catch (e) { }

const Reply = require('./lib/reply')
const Request = require('./lib/request')
const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
const buildSchema = require('./lib/validation').build
const handleRequest = require('./lib/handleRequest')
const isValidLogger = require('./lib/validation').isValidLogger
const decorator = require('./lib/decorate')
const ContentTypeParser = require('./lib/ContentTypeParser')
const Hooks = require('./lib/hooks')
const serializers = require('./lib/serializers')

function build (options) {
  options = options || {}
  if (typeof options !== 'object') {
    throw new TypeError('Options must be an object')
  }

  var logger
  if (options.logger && isValidLogger(options.logger)) {
    logger = pinoHttp({ logger: options.logger, serializers })
  } else {
    options.logger = options.logger || {}
    options.logger.level = options.logger.level || 'fatal'
    options.logger.serializers = options.logger.serializers || serializers
    logger = pinoHttp(options.logger)
  }

  const router = FindMyWay({ defaultRoute: defaultRoute })
  const middie = Middie(_runMiddlewares)
  const run = middie.run
  const map = new Map()
  const runHooks = fastseries()

  const app = avvio(fastify, {})
  // Override to allow the plugin incapsulation
  app.override = override

  // true when Fastify is ready to go
  var started = false
  app.on('start', () => {
    started = true
  })

  var server
  if (options.https) {
    server = https.createServer(options.https, fastify)
  } else {
    server = http.createServer(fastify)
  }

  if (Number(process.versions.node[0]) >= 6) {
    server.on('clientError', handleClientError)
  }

  // shorthand methods
  fastify.delete = _delete
  fastify.get = _get
  fastify.head = _head
  fastify.patch = _patch
  fastify.post = _post
  fastify.put = _put
  fastify.options = _options
  // extended route
  fastify.route = route
  fastify._RoutePrefix = new RoutePrefix()

  // expose logger instance
  fastify.logger = logger

  // hooks
  fastify.addHook = addHook
  fastify._hooks = new Hooks()
  fastify.close = close

  // custom parsers
  fastify.addContentTypeParser = addContentTypeParser
  fastify.hasContentTypeParser = hasContentTypeParser
  fastify._contentTypeParser = new ContentTypeParser()

  // plugin
  fastify.register = fastify.use
  fastify.listen = listen
  fastify.server = server

  // extend server methods
  fastify.decorate = decorator.add
  fastify.hasDecorator = decorator.exist
  fastify.decorateReply = decorator.decorateReply
  fastify.decorateRequest = decorator.decorateRequest
  fastify.extendServerError = decorator.extendServerError

  fastify._Reply = Reply
  fastify._Request = Request

  // middleware support
  fastify.use = middie.use

  // exposes the routes map
  fastify[Symbol.iterator] = iterator

  // fake http injection (for testing purposes)
  fastify.inject = inject

  return fastify

  function fastify (req, res) {
    logger(req, res)

    // onRequest hook
    setImmediate(
      runHooks,
      new State(req, res),
      hookIterator,
      fastify._hooks.onRequest,
      middlewareCallback
    )
  }

  function _runMiddlewares (err, req, res) {
    if (err) {
      const reply = new Reply(req, res, null)
      reply.send(err)
      return
    }

    // preRouting hook
    setImmediate(
      runHooks,
      new State(req, res),
      hookIterator,
      fastify._hooks.preRouting,
      routeCallback
    )
  }

  function State (req, res) {
    this.req = req
    this.res = res
  }

  function hookIterator (fn, cb) {
    fn(this.req, this.res, cb)
  }

  function middlewareCallback (err, code) {
    if (err) {
      const reply = new Reply(this.req, this.res, null)
      if (code[0]) reply.code(code[0])
      reply.send(err)
      return
    }
    run(this.req, this.res)
  }

  function routeCallback (err, code) {
    if (err) {
      const reply = new Reply(this.req, this.res, null)
      if (code[0]) reply.code(code[0])
      reply.send(err)
      return
    }

    router.lookup(this.req, this.res)
  }

  function listen (port, address, cb) {
    const hasAddress = arguments.length === 3
    const _cb = (hasAddress) ? cb : address
    fastify.ready(function (err) {
      if (err) return _cb(err)
      if (hasAddress) {
        server.listen(port, address, _cb)
      } else {
        server.listen(port, _cb)
      }
    })
  }

  function override (instance, fn, opts) {
    if (fn[Symbol.for('skip-override')]) {
      return instance
    }

    instance = Object.create(instance)
    instance._Reply = Reply.buildReply(instance._Reply)
    instance._Request = Request.buildRequest(instance._Request)
    instance._contentTypeParser = ContentTypeParser.buildContentTypeParser(instance._contentTypeParser)
    instance._hooks = Hooks.buildHooks(instance._hooks)
    instance._RoutePrefix = buildRoutePrefix(instance._RoutePrefix, opts)

    return instance
  }

  function RoutePrefix () {
    this.prefix = ''
  }

  function buildRoutePrefix (r, opts) {
    function _RoutePrefix () {}
    const R = new _RoutePrefix()
    R.prefix = r.prefix
    if (typeof opts.prefix === 'string') {
      R.prefix += opts.prefix
    }
    return R
  }

  function close (cb) {
    runHooks(
      fastify,
      onCloseIterator,
      fastify._hooks.onClose,
      onCloseCallback(cb)
    )
  }

  function onCloseIterator (fn, cb) {
    fn(this, cb)
  }

  function onCloseCallback (cb) {
    return (err) => {
      if (err) {
        throw err
      }

      fastify.server.close(cb)
    }
  }

  // Shorthand methods
  function _delete (url, opts, handler) {
    return _route(this, 'DELETE', url, opts, handler)
  }

  function _get (url, opts, handler) {
    return _route(this, 'GET', url, opts, handler)
  }

  function _head (url, opts, handler) {
    return _route(this, 'HEAD', url, opts, handler)
  }

  function _patch (url, opts, handler) {
    return _route(this, 'PATCH', url, opts, handler)
  }

  function _post (url, opts, handler) {
    return _route(this, 'POST', url, opts, handler)
  }

  function _put (url, opts, handler) {
    return _route(this, 'PUT', url, opts, handler)
  }

  function _options (url, opts, handler) {
    return _route(this, 'OPTIONS', url, opts, handler)
  }

  function _route (self, method, url, options, handler) {
    if (!handler && typeof options === 'function') {
      handler = options
      options = {}
    }
    return route.call(self, {
      method,
      url,
      handler,
      schema: options.schema || {},
      Reply: self._Reply,
      Request: self._Request,
      contentTypeParser: self._contentTypeParser,
      preHandler: self._hooks.preHandler,
      RoutePrefix: self._RoutePrefix,
      beforeHandler: options.beforeHandler
    })
  }

  // Route management
  function route (opts) {
    if (supportedMethods.indexOf(opts.method) === -1) {
      throw new Error(`${opts.method} method is not supported!`)
    }

    if (!opts.handler) {
      throw new Error(`Missing handler function for ${opts.method}:${opts.url} route.`)
    }

    this.ready((notHandledErr, done) => {
      const prefix = (opts.RoutePrefix || this._RoutePrefix).prefix
      const url = prefix + (opts.url || opts.path)

      const store = new Store(
        opts.schema,
        opts.handler,
        opts.Reply || this._Reply,
        opts.Request || this._Request,
        opts.contentTypeParser || this._contentTypeParser,
        []
      )

      buildSchema(store)

      store.preHandler.push.apply(store.preHandler, (opts.preHandler || this._hooks.preHandler))
      if (opts.beforeHandler) {
        opts.beforeHandler = Array.isArray(opts.beforeHandler) ? opts.beforeHandler : [opts.beforeHandler]
        store.preHandler.push.apply(store.preHandler, opts.beforeHandler)
      }

      if (map.has(url)) {
        if (map.get(url)[opts.method]) {
          return done(new Error(`${opts.method} already set for ${url}`))
        }

        map.get(url)[opts.method] = store
        router.on(opts.method, url, handleRequest, store)
      } else {
        const node = {}
        node[opts.method] = store
        map.set(url, node)
        router.on(opts.method, url, handleRequest, store)
      }
      done()
    })

    // chainable api
    return fastify
  }

  function Store (schema, handler, Reply, Request, contentTypeParser, preHandler) {
    this.schema = schema
    this.handler = handler
    this.Reply = Reply
    this.Request = Request
    this.contentTypeParser = contentTypeParser
    this.preHandler = preHandler
  }

  function iterator () {
    var entries = map.entries()
    var it = {}
    it.next = function () {
      var next = entries.next()

      if (next.done) {
        return {
          value: null,
          done: true
        }
      }

      var value = {}
      var methods = {}

      value[next.value[0]] = methods

      // out methods are saved Uppercase,
      // so we lowercase them for a better usability
      for (var method in next.value[1]) {
        methods[method.toLowerCase()] = next.value[1][method]
      }

      return {
        value: value,
        done: false
      }
    }
    return it
  }

  function inject (opts, cb) {
    if (!shot) throw new Error('"shot" library is not installed: "npm install shot --save-dev"')

    if (started) {
      shot.inject(this, opts, cb)
      return
    }

    this.ready(err => {
      if (err) throw err
      shot.inject(this, opts, cb)
    })
  }

  function addHook (name, fn) {
    this._hooks.add(name, fn)
    return this
  }

  function addContentTypeParser (contentType, fn) {
    this._contentTypeParser.add(contentType, fn)
    return this
  }

  function hasContentTypeParser (contentType, fn) {
    return this._contentTypeParser.hasParser(contentType)
  }

  function handleClientError (e, socket) {
    const body = JSON.stringify({
      error: http.STATUS_CODES['400'],
      message: 'Client Error',
      statusCode: 400
    })
    logger.logger.error(e, 'client error')
    socket.end(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`)
  }

  function defaultRoute (req, res, params) {
    const reply = new Reply(req, res, null)
    reply.code(404).send(new Error('Not found'))
  }
}

module.exports = build
<commit_msg>Use after instead of ready<commit_after>'use strict'

const FindMyWay = require('find-my-way')
const avvio = require('avvio')
const http = require('http')
const https = require('https')
const pinoHttp = require('pino-http')
const Middie = require('middie')
const fastseries = require('fastseries')
var shot = null
try { shot = require('shot') } catch (e) { }

const Reply = require('./lib/reply')
const Request = require('./lib/request')
const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
const buildSchema = require('./lib/validation').build
const handleRequest = require('./lib/handleRequest')
const isValidLogger = require('./lib/validation').isValidLogger
const decorator = require('./lib/decorate')
const ContentTypeParser = require('./lib/ContentTypeParser')
const Hooks = require('./lib/hooks')
const serializers = require('./lib/serializers')

function build (options) {
  options = options || {}
  if (typeof options !== 'object') {
    throw new TypeError('Options must be an object')
  }

  var logger
  if (options.logger && isValidLogger(options.logger)) {
    logger = pinoHttp({ logger: options.logger, serializers })
  } else {
    options.logger = options.logger || {}
    options.logger.level = options.logger.level || 'fatal'
    options.logger.serializers = options.logger.serializers || serializers
    logger = pinoHttp(options.logger)
  }

  const router = FindMyWay({ defaultRoute: defaultRoute })
  const middie = Middie(_runMiddlewares)
  const run = middie.run
  const map = new Map()
  const runHooks = fastseries()

  const app = avvio(fastify, {})
  // Override to allow the plugin incapsulation
  app.override = override

  // true when Fastify is ready to go
  var started = false
  app.on('start', () => {
    started = true
  })

  var server
  if (options.https) {
    server = https.createServer(options.https, fastify)
  } else {
    server = http.createServer(fastify)
  }

  if (Number(process.versions.node[0]) >= 6) {
    server.on('clientError', handleClientError)
  }

  // shorthand methods
  fastify.delete = _delete
  fastify.get = _get
  fastify.head = _head
  fastify.patch = _patch
  fastify.post = _post
  fastify.put = _put
  fastify.options = _options
  // extended route
  fastify.route = route
  fastify._RoutePrefix = new RoutePrefix()

  // expose logger instance
  fastify.logger = logger

  // hooks
  fastify.addHook = addHook
  fastify._hooks = new Hooks()
  fastify.close = close

  // custom parsers
  fastify.addContentTypeParser = addContentTypeParser
  fastify.hasContentTypeParser = hasContentTypeParser
  fastify._contentTypeParser = new ContentTypeParser()

  // plugin
  fastify.register = fastify.use
  fastify.listen = listen
  fastify.server = server

  // extend server methods
  fastify.decorate = decorator.add
  fastify.hasDecorator = decorator.exist
  fastify.decorateReply = decorator.decorateReply
  fastify.decorateRequest = decorator.decorateRequest
  fastify.extendServerError = decorator.extendServerError

  fastify._Reply = Reply
  fastify._Request = Request

  // middleware support
  fastify.use = middie.use

  // exposes the routes map
  fastify[Symbol.iterator] = iterator

  // fake http injection (for testing purposes)
  fastify.inject = inject

  return fastify

  function fastify (req, res) {
    logger(req, res)

    // onRequest hook
    setImmediate(
      runHooks,
      new State(req, res),
      hookIterator,
      fastify._hooks.onRequest,
      middlewareCallback
    )
  }

  function _runMiddlewares (err, req, res) {
    if (err) {
      const reply = new Reply(req, res, null)
      reply.send(err)
      return
    }

    // preRouting hook
    setImmediate(
      runHooks,
      new State(req, res),
      hookIterator,
      fastify._hooks.preRouting,
      routeCallback
    )
  }

  function State (req, res) {
    this.req = req
    this.res = res
  }

  function hookIterator (fn, cb) {
    fn(this.req, this.res, cb)
  }

  function middlewareCallback (err, code) {
    if (err) {
      const reply = new Reply(this.req, this.res, null)
      if (code[0]) reply.code(code[0])
      reply.send(err)
      return
    }
    run(this.req, this.res)
  }

  function routeCallback (err, code) {
    if (err) {
      const reply = new Reply(this.req, this.res, null)
      if (code[0]) reply.code(code[0])
      reply.send(err)
      return
    }

    router.lookup(this.req, this.res)
  }

  function listen (port, address, cb) {
    const hasAddress = arguments.length === 3
    const _cb = (hasAddress) ? cb : address
    fastify.ready(function (err) {
      if (err) return _cb(err)
      if (hasAddress) {
        server.listen(port, address, _cb)
      } else {
        server.listen(port, _cb)
      }
    })
  }

  function override (instance, fn, opts) {
    if (fn[Symbol.for('skip-override')]) {
      return instance
    }

    instance = Object.create(instance)
    instance._Reply = Reply.buildReply(instance._Reply)
    instance._Request = Request.buildRequest(instance._Request)
    instance._contentTypeParser = ContentTypeParser.buildContentTypeParser(instance._contentTypeParser)
    instance._hooks = Hooks.buildHooks(instance._hooks)
    instance._RoutePrefix = buildRoutePrefix(instance._RoutePrefix, opts)

    return instance
  }

  function RoutePrefix () {
    this.prefix = ''
  }

  function buildRoutePrefix (r, opts) {
    function _RoutePrefix () {}
    const R = new _RoutePrefix()
    R.prefix = r.prefix
    if (typeof opts.prefix === 'string') {
      R.prefix += opts.prefix
    }
    return R
  }

  function close (cb) {
    runHooks(
      fastify,
      onCloseIterator,
      fastify._hooks.onClose,
      onCloseCallback(cb)
    )
  }

  function onCloseIterator (fn, cb) {
    fn(this, cb)
  }

  function onCloseCallback (cb) {
    return (err) => {
      if (err) {
        throw err
      }

      fastify.server.close(cb)
    }
  }

  // Shorthand methods
  function _delete (url, opts, handler) {
    return _route(this, 'DELETE', url, opts, handler)
  }

  function _get (url, opts, handler) {
    return _route(this, 'GET', url, opts, handler)
  }

  function _head (url, opts, handler) {
    return _route(this, 'HEAD', url, opts, handler)
  }

  function _patch (url, opts, handler) {
    return _route(this, 'PATCH', url, opts, handler)
  }

  function _post (url, opts, handler) {
    return _route(this, 'POST', url, opts, handler)
  }

  function _put (url, opts, handler) {
    return _route(this, 'PUT', url, opts, handler)
  }

  function _options (url, opts, handler) {
    return _route(this, 'OPTIONS', url, opts, handler)
  }

  function _route (self, method, url, options, handler) {
    if (!handler && typeof options === 'function') {
      handler = options
      options = {}
    }
    return route.call(self, {
      method,
      url,
      handler,
      schema: options.schema || {},
      Reply: self._Reply,
      Request: self._Request,
      contentTypeParser: self._contentTypeParser,
      preHandler: self._hooks.preHandler,
      RoutePrefix: self._RoutePrefix,
      beforeHandler: options.beforeHandler
    })
  }

  // Route management
  function route (opts) {
    if (supportedMethods.indexOf(opts.method) === -1) {
      throw new Error(`${opts.method} method is not supported!`)
    }

    if (!opts.handler) {
      throw new Error(`Missing handler function for ${opts.method}:${opts.url} route.`)
    }

    this.after((notHandledErr, done) => {
      const prefix = (opts.RoutePrefix || this._RoutePrefix).prefix
      const url = prefix + (opts.url || opts.path)

      const store = new Store(
        opts.schema,
        opts.handler,
        opts.Reply || this._Reply,
        opts.Request || this._Request,
        opts.contentTypeParser || this._contentTypeParser,
        []
      )

      buildSchema(store)

      store.preHandler.push.apply(store.preHandler, (opts.preHandler || this._hooks.preHandler))
      if (opts.beforeHandler) {
        opts.beforeHandler = Array.isArray(opts.beforeHandler) ? opts.beforeHandler : [opts.beforeHandler]
        store.preHandler.push.apply(store.preHandler, opts.beforeHandler)
      }

      if (map.has(url)) {
        if (map.get(url)[opts.method]) {
          return done(new Error(`${opts.method} already set for ${url}`))
        }

        map.get(url)[opts.method] = store
        router.on(opts.method, url, handleRequest, store)
      } else {
        const node = {}
        node[opts.method] = store
        map.set(url, node)
        router.on(opts.method, url, handleRequest, store)
      }
      done()
    })

    // chainable api
    return fastify
  }

  function Store (schema, handler, Reply, Request, contentTypeParser, preHandler) {
    this.schema = schema
    this.handler = handler
    this.Reply = Reply
    this.Request = Request
    this.contentTypeParser = contentTypeParser
    this.preHandler = preHandler
  }

  function iterator () {
    var entries = map.entries()
    var it = {}
    it.next = function () {
      var next = entries.next()

      if (next.done) {
        return {
          value: null,
          done: true
        }
      }

      var value = {}
      var methods = {}

      value[next.value[0]] = methods

      // out methods are saved Uppercase,
      // so we lowercase them for a better usability
      for (var method in next.value[1]) {
        methods[method.toLowerCase()] = next.value[1][method]
      }

      return {
        value: value,
        done: false
      }
    }
    return it
  }

  function inject (opts, cb) {
    if (!shot) throw new Error('"shot" library is not installed: "npm install shot --save-dev"')

    if (started) {
      shot.inject(this, opts, cb)
      return
    }

    this.ready(err => {
      if (err) throw err
      shot.inject(this, opts, cb)
    })
  }

  function addHook (name, fn) {
    this._hooks.add(name, fn)
    return this
  }

  function addContentTypeParser (contentType, fn) {
    this._contentTypeParser.add(contentType, fn)
    return this
  }

  function hasContentTypeParser (contentType, fn) {
    return this._contentTypeParser.hasParser(contentType)
  }

  function handleClientError (e, socket) {
    const body = JSON.stringify({
      error: http.STATUS_CODES['400'],
      message: 'Client Error',
      statusCode: 400
    })
    logger.logger.error(e, 'client error')
    socket.end(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`)
  }

  function defaultRoute (req, res, params) {
    const reply = new Reply(req, res, null)
    reply.code(404).send(new Error('Not found'))
  }
}

module.exports = build
<|endoftext|>


<commit_before>/**
 * @module esdf/core/EventSourcedAggregate
 */

var EventEmitter = require('eventemitter2').EventEmitter2;
var AggregateSnapshot = require('./utils/AggregateSnapshot.js').AggregateSnapshot;
var when = require('when');
var uuid = require('uuid');
var util = require('util');

var Commit = require('./Commit.js').Commit;

//TODO: Error class documentation.
function AggregateTypeMismatch(expected, got){
	this.name = 'AggregateTypeMismatch';
	this.message = 'Aggregate type mismatch: expected (' + typeof(expected) + ')' + expected + ', got (' + typeof(got) + ')' + got;
	this.labels = {
		expected: expected,
		got: got,
		critical: true // This error precludes retry attempts (assuming a sane retry strategy is employed).
	};
}
util.inherits(AggregateTypeMismatch, Error);

function AggregateUsageError(message){
	this.name = 'AggregateUsageError';
	this.message = message;
	this.labels = {
		critical: true
	};
}
util.inherits(AggregateUsageError, Error);

//TODO: use real, well-defined methods for assigning the EventSink, ID and aggregateType. Refactor the users.
/**
 * Basic constructor for creating an in-memory object representation of an Aggregate. Aggregates are basic business objects in the domain and the primary source of events.
 * The created Aggregate instance supports EventEmitter's on() listener registration to define how events alter the state of the Aggregate (and thus, of the application).
 * An aggregate should typically listen to its own events (define event handlers) and react by issuing such state changes, since it is the only keeper of its own internal state.
 * You *are* supposed to use this as a prototype for your own Aggregate constructors.
 *
 * @constructor
 */
function EventSourcedAggregate(){
	/**
	 * Aggregate ID, used when loading (rehydrating) the object from an Event Sink.
	 * @private
	 */
	this._aggregateID = undefined;
	/**
	 * Pending event sequence number, used for event ordering and optimistic concurrency collision detection.
	 * @private
	 */
	this._nextSequenceNumber = 1;
	/**
	 * Array of the events to be saved to the Event Sink within a single commit when commit() is called.
	 * @private
	 */
	this._stagedEvents = undefined;
	/**
	 * The assigned Event Sink that events will be committed to. This variable should be assigned from the outside using the assignEventSink method.
	 * @public
	 */
	this._eventSink = undefined;
	/**
	 * Aggregate's proper name - used to check if commits belong here when loading. Validation makes it impossible to apply another class' commits.
	 */
	this._aggregateType = undefined;
}

/**
 * Apply the given commit to the aggregate, causing it to apply each event, individually, one after another.
 *
 * @param {module:esdf/core/Commit.Commit} commit The commit object to apply.
 */
EventSourcedAggregate.prototype.applyCommit = function applyCommit(commit){
	var self = this;
	//TODO: aggregate type validation.
	// Check if the commit's saved aggregateType matches our own. If not, bail out - this is not our commit for sure!
	if(this._aggregateType !== commit.aggregateType){
		throw new AggregateTypeMismatch(this._aggregateType, commit.aggregateType);
	}
	commit.events.forEach(function(event){
		// The handler only gets the event and the commit metadata. It is not granted access to other events in the commit.
		self._applyEvent(event, commit);
	});
	// Increment our internal sequence number counter.
	this._nextSequenceNumber++;
};

/**
 * Apply the event to the Aggregate by calling the appropriate registered event handlers.
 *
 * @param {module:esdf/core/Event.Event} event The event to apply.
 * @param {module:esdf/core/Commit.Commit} commit The commit that the event is part of.
 *
 */
//TODO: document the handler function contract using JSDoc or any other means available.
EventSourcedAggregate.prototype._applyEvent = function _applyEvent(event, commit){
	var handlerFunctionName = 'on' + event.eventType;
	if(typeof(this[handlerFunctionName]) === 'function'){
		this[handlerFunctionName](event, commit);
	}
	else{
		throw new AggregateDefinitionError('Event type ' + event.eventType + ' applied, but no handler was available - bailing out to avoid programmer error!');
	}
};

/**
 * Stage an event for committing later. Immediately applies the event to the Aggregate (via the built-in EventEmitter), so rolling back is not possible (reloading the Aggregate from the Event Sink and retrying can be used instead, see utils.tryWith).
 *
 * @param {module:esdf/core/Event.Event} event The event to be enqueued for committing later.
 */
EventSourcedAggregate.prototype._stageEvent = function _stageEvent(event){
	// If this is the first call, we need to initialize the pending event array.
	//  It can not be done via prototypes, because that would mean the array is shared among all instances (a big problem!).
	if(!this._stagedEvents){
		this._stagedEvents = [];
	}
	this._stagedEvents.push(event);
	this._applyEvent(event);
	return true;
};

//TODO: documentation (especially concerning the hack below)
EventSourcedAggregate.prototype._getSnapshotData = function _getSnapshotData(){
	throw new AggregateDefinitionError('Unimplemented!');
};
EventSourcedAggregate.prototype._getSnapshotData.unimplemented = true;

EventSourcedAggregate.prototype.applySnapshot = function applySnapshot(snapshot){
	if(AggregateSnapshot.isAggregateSnapshot(snapshot)){
		if(typeof(this._applySnapshot) === 'function'){
			if(snapshot.aggregateType === this._aggregateType){
				this._applySnapshot(snapshot);
				this._nextSequenceNumber = snapshot.lastSlotNumber + 1;
			}
			else{
				throw new AggregateTypeMismatch(this._aggregateType, snapshot.aggregateType);
			}
		}
		else{
			throw new AggregateUsageError('This aggregate does not support snapshot application (needs to implement _applySnapshot).');
		}
	}
	else{
		throw new AggregateUsageError('Not a snapshot object - can not apply!');
	}
};

EventSourcedAggregate.prototype.supportsSnapshots = function supportsSnapshots(){
	return (typeof(this._applySnapshot) === 'function');
};

/**
 * Save all staged events to the Event Sink (assigned earlier manually from outside to the Aggregate's "_eventSink" property).
 * Emits an "error" event should any saving errors occur (allowing higher layers to reload the Aggregate and retry whatever they were doing with it).
 * @param {Object} metadata
 * @returns {external:Promise} Promise/A-compliant promise object which supports then(). The promise is resolved when the commit is saved, and rejected if the saving fails for any reason (including optimistic concurrency).
 */
EventSourcedAggregate.prototype.commit = function commit(metadata){
	if(typeof(metadata) !== 'object' || metadata === null){
		metadata = {};
	}
	var self = this;
	var emitDeferred = when.defer(); //emission promise - to be resolved when the event batch is saved in the database
	// NOTE: Sinking an empty commit *is a valid operation* from the AR's point of view! It is up to the eventSink how it handles this.
	// Try to sink the commit.
	if(!this._stagedEvents){
		this._stagedEvents = [];
	}
	var commitObject = new Commit(this._stagedEvents, this._aggregateID, this._nextSequenceNumber, this._aggregateType, metadata);
	when(self._eventSink.sink(commitObject),
	function _commitSinkSucceeded(result){
		self._stagedEvents = [];
		self._nextSequenceNumber = self._nextSequenceNumber + 1;
		return emitDeferred.resolver.resolve(result);
	},
	function _commitSinkFailed(reason){
		return emitDeferred.resolver.reject(reason);
	}); //This is a promise (thenable), so return its consumer-facing part.
	return emitDeferred.promise;
};

EventSourcedAggregate.prototype.saveSnapshot = function saveSnapshot(){
	var self = this;
	if(typeof(this._getSnapshotData) === 'function' && !this._getSnapshotData.unimplemented){
		return this._snapshotter.saveSnapshot(new AggregateSnapshot(this._aggregateType, this._aggregateID, this._getSnapshotData(), (this._nextSequenceNumber - 1)));
	}
	else{
		//TODO: Construct an error here? Or just leave as undefined?
		return when.reject();
	}
};

module.exports.EventSourcedAggregate = EventSourcedAggregate;<commit_msg>Removed the obsolete EventEmitter2 dependency from EventSourcedAggregate file.<commit_after>/**
 * @module esdf/core/EventSourcedAggregate
 */

var AggregateSnapshot = require('./utils/AggregateSnapshot.js').AggregateSnapshot;
var when = require('when');
var uuid = require('uuid');
var util = require('util');

var Commit = require('./Commit.js').Commit;

//TODO: Error class documentation.
function AggregateTypeMismatch(expected, got){
	this.name = 'AggregateTypeMismatch';
	this.message = 'Aggregate type mismatch: expected (' + typeof(expected) + ')' + expected + ', got (' + typeof(got) + ')' + got;
	this.labels = {
		expected: expected,
		got: got,
		critical: true // This error precludes retry attempts (assuming a sane retry strategy is employed).
	};
}
util.inherits(AggregateTypeMismatch, Error);

function AggregateUsageError(message){
	this.name = 'AggregateUsageError';
	this.message = message;
	this.labels = {
		critical: true
	};
}
util.inherits(AggregateUsageError, Error);

//TODO: use real, well-defined methods for assigning the EventSink, ID and aggregateType. Refactor the users.
/**
 * Basic constructor for creating an in-memory object representation of an Aggregate. Aggregates are basic business objects in the domain and the primary source of events.
 * The created Aggregate instance supports EventEmitter's on() listener registration to define how events alter the state of the Aggregate (and thus, of the application).
 * An aggregate should typically listen to its own events (define event handlers) and react by issuing such state changes, since it is the only keeper of its own internal state.
 * You *are* supposed to use this as a prototype for your own Aggregate constructors.
 *
 * @constructor
 */
function EventSourcedAggregate(){
	/**
	 * Aggregate ID, used when loading (rehydrating) the object from an Event Sink.
	 * @private
	 */
	this._aggregateID = undefined;
	/**
	 * Pending event sequence number, used for event ordering and optimistic concurrency collision detection.
	 * @private
	 */
	this._nextSequenceNumber = 1;
	/**
	 * Array of the events to be saved to the Event Sink within a single commit when commit() is called.
	 * @private
	 */
	this._stagedEvents = undefined;
	/**
	 * The assigned Event Sink that events will be committed to. This variable should be assigned from the outside using the assignEventSink method.
	 * @public
	 */
	this._eventSink = undefined;
	/**
	 * Aggregate's proper name - used to check if commits belong here when loading. Validation makes it impossible to apply another class' commits.
	 */
	this._aggregateType = undefined;
}

/**
 * Apply the given commit to the aggregate, causing it to apply each event, individually, one after another.
 *
 * @param {module:esdf/core/Commit.Commit} commit The commit object to apply.
 */
EventSourcedAggregate.prototype.applyCommit = function applyCommit(commit){
	var self = this;
	//TODO: aggregate type validation.
	// Check if the commit's saved aggregateType matches our own. If not, bail out - this is not our commit for sure!
	if(this._aggregateType !== commit.aggregateType){
		throw new AggregateTypeMismatch(this._aggregateType, commit.aggregateType);
	}
	commit.events.forEach(function(event){
		// The handler only gets the event and the commit metadata. It is not granted access to other events in the commit.
		self._applyEvent(event, commit);
	});
	// Increment our internal sequence number counter.
	this._nextSequenceNumber++;
};

/**
 * Apply the event to the Aggregate by calling the appropriate registered event handlers.
 *
 * @param {module:esdf/core/Event.Event} event The event to apply.
 * @param {module:esdf/core/Commit.Commit} commit The commit that the event is part of.
 *
 */
//TODO: document the handler function contract using JSDoc or any other means available.
EventSourcedAggregate.prototype._applyEvent = function _applyEvent(event, commit){
	var handlerFunctionName = 'on' + event.eventType;
	if(typeof(this[handlerFunctionName]) === 'function'){
		this[handlerFunctionName](event, commit);
	}
	else{
		throw new AggregateDefinitionError('Event type ' + event.eventType + ' applied, but no handler was available - bailing out to avoid programmer error!');
	}
};

/**
 * Stage an event for committing later. Immediately applies the event to the Aggregate (via the built-in EventEmitter), so rolling back is not possible (reloading the Aggregate from the Event Sink and retrying can be used instead, see utils.tryWith).
 *
 * @param {module:esdf/core/Event.Event} event The event to be enqueued for committing later.
 */
EventSourcedAggregate.prototype._stageEvent = function _stageEvent(event){
	// If this is the first call, we need to initialize the pending event array.
	//  It can not be done via prototypes, because that would mean the array is shared among all instances (a big problem!).
	if(!this._stagedEvents){
		this._stagedEvents = [];
	}
	this._stagedEvents.push(event);
	this._applyEvent(event);
	return true;
};

//TODO: documentation (especially concerning the hack below)
EventSourcedAggregate.prototype._getSnapshotData = function _getSnapshotData(){
	throw new AggregateDefinitionError('Unimplemented!');
};
EventSourcedAggregate.prototype._getSnapshotData.unimplemented = true;

EventSourcedAggregate.prototype.applySnapshot = function applySnapshot(snapshot){
	if(AggregateSnapshot.isAggregateSnapshot(snapshot)){
		if(typeof(this._applySnapshot) === 'function'){
			if(snapshot.aggregateType === this._aggregateType){
				this._applySnapshot(snapshot);
				this._nextSequenceNumber = snapshot.lastSlotNumber + 1;
			}
			else{
				throw new AggregateTypeMismatch(this._aggregateType, snapshot.aggregateType);
			}
		}
		else{
			throw new AggregateUsageError('This aggregate does not support snapshot application (needs to implement _applySnapshot).');
		}
	}
	else{
		throw new AggregateUsageError('Not a snapshot object - can not apply!');
	}
};

EventSourcedAggregate.prototype.supportsSnapshots = function supportsSnapshots(){
	return (typeof(this._applySnapshot) === 'function');
};

/**
 * Save all staged events to the Event Sink (assigned earlier manually from outside to the Aggregate's "_eventSink" property).
 * Emits an "error" event should any saving errors occur (allowing higher layers to reload the Aggregate and retry whatever they were doing with it).
 * @param {Object} metadata
 * @returns {external:Promise} Promise/A-compliant promise object which supports then(). The promise is resolved when the commit is saved, and rejected if the saving fails for any reason (including optimistic concurrency).
 */
EventSourcedAggregate.prototype.commit = function commit(metadata){
	if(typeof(metadata) !== 'object' || metadata === null){
		metadata = {};
	}
	var self = this;
	var emitDeferred = when.defer(); //emission promise - to be resolved when the event batch is saved in the database
	// NOTE: Sinking an empty commit *is a valid operation* from the AR's point of view! It is up to the eventSink how it handles this.
	// Try to sink the commit.
	if(!this._stagedEvents){
		this._stagedEvents = [];
	}
	var commitObject = new Commit(this._stagedEvents, this._aggregateID, this._nextSequenceNumber, this._aggregateType, metadata);
	when(self._eventSink.sink(commitObject),
	function _commitSinkSucceeded(result){
		self._stagedEvents = [];
		self._nextSequenceNumber = self._nextSequenceNumber + 1;
		return emitDeferred.resolver.resolve(result);
	},
	function _commitSinkFailed(reason){
		return emitDeferred.resolver.reject(reason);
	}); //This is a promise (thenable), so return its consumer-facing part.
	return emitDeferred.promise;
};

EventSourcedAggregate.prototype.saveSnapshot = function saveSnapshot(){
	var self = this;
	if(typeof(this._getSnapshotData) === 'function' && !this._getSnapshotData.unimplemented){
		return this._snapshotter.saveSnapshot(new AggregateSnapshot(this._aggregateType, this._aggregateID, this._getSnapshotData(), (this._nextSequenceNumber - 1)));
	}
	else{
		//TODO: Construct an error here? Or just leave as undefined?
		return when.reject();
	}
};

module.exports.EventSourcedAggregate = EventSourcedAggregate;
<|endoftext|>


<commit_before>const io = require('socket.io')();

io.on('connection', (client) => {
  client.on('subscribeToTimer', (interval) => {
    console.log('client is subscribing to timer with interval ', interval);
    setInterval(() => {
      client.emit('timer', new Date());
    }, interval);
  });
});

const port = 8000;
io.listen(port);
console.log('listening on port ', port);

// MongoDB Connection

var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');

var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, db) {
  assert.equal(null, err);
  console.log("Connected correctly to server.");
  db.close();
});
<<<<<<< HEAD

=======
>>>>>>> 1560f1c980c7d77f80fa39ae943717e417f5df5a
<commit_msg>Fixing merge conflicts<commit_after>const io = require('socket.io')();

io.on('connection', (client) => {
  client.on('subscribeToTimer', (interval) => {
    console.log('client is subscribing to timer with interval ', interval);
    setInterval(() => {
      client.emit('timer', new Date());
    }, interval);
  });
});

const port = 8000;
io.listen(port);
console.log('listening on port ', port);

// MongoDB Connection

var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');

var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, db) {
  assert.equal(null, err);
  console.log("Connected correctly to server.");
  db.close();
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const url = require('url');
const request = require('request');
const https = require('https');
const http = require('http');
const utils = require('./lib/utils');
const overrides = require('./lib/overrides');

function Fakeweb() {
  this.allowNetConnect = true;
  this.allowLocalConnect = true;
  this.interceptedUris = {};
  this.ignoredUris = [];
  this.regexMatches = [];

  request.get = overrides.requestGet.bind(this);
  request.post = overrides.requestPost.bind(this);
  https.request = overrides.httpsRequest.bind(this);
  http.request = overrides.httpRequest.bind(this);
}

Fakeweb.prototype.tearDown = function tearDown() {
  this.interceptedUris = {};
  this.regexMatches = [];
  this.allowNetConnect = true;
  this.allowLocalConnect = true;
};

Fakeweb.prototype.registerUri = function registerUri(options) {
  if (options.uri instanceof RegExp) {
    this.regexMatches.push(options.uri);
  } else {
    options.uri = utils.parseUrl(options.uri);
  }
  this.interceptedUris[options.uri] = {};
  if (options.file || options.binaryFile) {
    if (options.binaryFile) {
      this.interceptedUris[options.uri].response = () => fs.readFileSync(options.binaryFile, 'binary');
    } else {
      this.interceptedUris[options.uri].response = () => fs.readFileSync(options.file).toString();
    }
  } else if (options.body !== undefined) {
    this.interceptedUris[options.uri].response = (typeof options.body === 'function') ? options.body : () => options.body;
  } else {
    this.interceptedUris[options.uri].response = () => undefined;
  }
  this.interceptedUris[options.uri].statusCode = options.statusCode || 200;
  this.interceptedUris[options.uri].headers = options.headers || {};
  this.interceptedUris[options.uri].contentType = options.contentType;

  const spy = { used: false, useCount: 0 };
  this.interceptedUris[options.uri].spy = spy;
  return spy;
};

Fakeweb.prototype.ignoreUri = function ignoreUri(options) {
  this.ignoredUris[utils.parseUrl(options.uri)] = true;
};

Fakeweb.prototype.interceptable = function interceptable(uri, method) {
  if (typeof method === 'undefined') {
    method = 'GET';
  }

  uri = utils.parseUrl(uri);

  if (this.fakewebMatch(uri)) {
    return true;
  }
  if (this.ignoredUris[uri] || this.allowNetConnect) {
    return false;
  }

  if (uri) {
    const hostname = url.parse(uri).hostname;
    const requestIsLocal = (hostname === 'localhost' || hostname === '127.0.0.1');
    if (this.allowLocalConnect === true && requestIsLocal) {
      return false;
    }
    const errorMessage = `FAKEWEB: Unhandled ${method} request to ${uri}`;
    console.error(errorMessage);
    throw new Error(errorMessage);
  } else {
    console.error('FAKEWEB: Invalid request');
    throw new Error('FAKEWEB: Invalid request');
  }
};

Fakeweb.prototype.fakewebMatch = function fakewebMatch(uri) {
  for (let i = 0; i < this.regexMatches.length; i += 1) {
    if (uri.match(this.regexMatches[i])) {
      return this.interceptedUris[this.regexMatches[i]];
    }
  }
  if (this.interceptedUris[uri]) {
    return this.interceptedUris[uri];
  }
  return false;
};

module.exports = new Fakeweb();
<commit_msg>use class syntax instead<commit_after>'use strict';

const fs = require('fs');
const url = require('url');
const request = require('request');
const https = require('https');
const http = require('http');
const utils = require('./lib/utils');
const overrides = require('./lib/overrides');

class Fakeweb {
  constructor() {
    this.allowNetConnect = true;
    this.allowLocalConnect = true;
    this.interceptedUris = {};
    this.ignoredUris = [];
    this.regexMatches = [];
    this.setup();
  }

  setup() {
    request.get = overrides.requestGet.bind(this);
    request.post = overrides.requestPost.bind(this);
    https.request = overrides.httpsRequest.bind(this);
    http.request = overrides.httpRequest.bind(this);
  }
}

Fakeweb.prototype.tearDown = function tearDown() {
  this.interceptedUris = {};
  this.regexMatches = [];
  this.allowNetConnect = true;
  this.allowLocalConnect = true;
};

Fakeweb.prototype.registerUri = function registerUri(options) {
  if (options.uri instanceof RegExp) {
    this.regexMatches.push(options.uri);
  } else {
    options.uri = utils.parseUrl(options.uri);
  }
  this.interceptedUris[options.uri] = {};
  if (options.file || options.binaryFile) {
    if (options.binaryFile) {
      this.interceptedUris[options.uri].response = () => fs.readFileSync(options.binaryFile, 'binary');
    } else {
      this.interceptedUris[options.uri].response = () => fs.readFileSync(options.file).toString();
    }
  } else if (options.body !== undefined) {
    this.interceptedUris[options.uri].response = (typeof options.body === 'function') ? options.body : () => options.body;
  } else {
    this.interceptedUris[options.uri].response = () => undefined;
  }
  this.interceptedUris[options.uri].statusCode = options.statusCode || 200;
  this.interceptedUris[options.uri].headers = options.headers || {};
  this.interceptedUris[options.uri].contentType = options.contentType;

  const spy = { used: false, useCount: 0 };
  this.interceptedUris[options.uri].spy = spy;
  return spy;
};

Fakeweb.prototype.ignoreUri = function ignoreUri(options) {
  this.ignoredUris[utils.parseUrl(options.uri)] = true;
};

Fakeweb.prototype.interceptable = function interceptable(uri, method) {
  if (typeof method === 'undefined') {
    method = 'GET';
  }

  uri = utils.parseUrl(uri);

  if (this.fakewebMatch(uri)) {
    return true;
  }
  if (this.ignoredUris[uri] || this.allowNetConnect) {
    return false;
  }

  if (uri) {
    const hostname = url.parse(uri).hostname;
    const requestIsLocal = (hostname === 'localhost' || hostname === '127.0.0.1');
    if (this.allowLocalConnect === true && requestIsLocal) {
      return false;
    }
    const errorMessage = `FAKEWEB: Unhandled ${method} request to ${uri}`;
    console.error(errorMessage);
    throw new Error(errorMessage);
  } else {
    console.error('FAKEWEB: Invalid request');
    throw new Error('FAKEWEB: Invalid request');
  }
};

Fakeweb.prototype.fakewebMatch = function fakewebMatch(uri) {
  for (let i = 0; i < this.regexMatches.length; i += 1) {
    if (uri.match(this.regexMatches[i])) {
      return this.interceptedUris[this.regexMatches[i]];
    }
  }
  if (this.interceptedUris[uri]) {
    return this.interceptedUris[uri];
  }
  return false;
};

module.exports = new Fakeweb();
<|endoftext|>


<commit_before>function MainViewController ($scope, $log, $timeout, LocatorService, MeetService, ErrorHandler) {
    $log.debug('Initialize main view controller...');

    var self = this;
    self.status = '';
    self.current = null;
    $scope.app.isNavbarHidden = false;

    self.Like = function () {
        self.current.like = 1;
        self.current = LocatorService.getNextUser();

        if (angular.isUndefined(self.current)) {
            searchLoop();
        }

//        MeetService.asyncLike(current.mid).then(function () {
//            if (current.likeMe == 1) {
//                // TODO: show modal window
//            }
//        }, function () {
//            current.like = 0;
//        });
    };

    self.Dislike = function () {
        self.current.like = -1;
        self.current = LocatorService.getNextUser();
//        MeetService.asyncDislike(current.mid).then(null, function () {
//            current.like = 0;
//        });
    };

    self.Undo = function () {
        self.current = LocatorService.getPreviousUser();
    };

    function searchLoop () {
        self.status = 'search';
        LocatorService.asyncSearch().then(function (count) {
            self.status = 'done';
            if (count > 0) {
                self.current = LocatorService.getNextUser();
            }
            else {
                $log.debug('No users has been founded');
                $timeout(searchLoop, 10000);
            }
        }, ErrorHandler.handle);
    }

    searchLoop();
}

MainViewController.resolve = {
//    'users': function (LocatorService, ErrorService) {
//        return LocatorService.asyncSearch().then(null, ErrorService.handleError);
//    }
};

angular.module('spacebox').controller('MainViewController',
    ['$scope', '$log', '$timeout', 'LocatorService', 'MeetService', 'ErrorHandler', MainViewController]);
<commit_msg>Remove search timer on destroy.<commit_after>function MainViewController ($scope, $log, $timeout, LocatorService, MeetService, ErrorHandler) {
    $log.debug('Initialize main view controller...');

    var self = this;
    self.status = '';
    self.current = null;
    self.searchTimer = undefined;

    $scope.app.isNavbarHidden = false;

    self.Like = function () {
        self.current.like = 1;
        self.current = LocatorService.getNextUser();

        if (angular.isUndefined(self.current)) {
            searchLoop();
        }

//        MeetService.asyncLike(current.mid).then(function () {
//            if (current.likeMe == 1) {
//                // TODO: show modal window
//            }
//        }, function () {
//            current.like = 0;
//        });
    };

    self.Dislike = function () {
        self.current.like = -1;
        self.current = LocatorService.getNextUser();
//        MeetService.asyncDislike(current.mid).then(null, function () {
//            current.like = 0;
//        });
    };

    self.Undo = function () {
        self.current = LocatorService.getPreviousUser();
    };

    function searchLoop () {
        self.status = 'search';
        LocatorService.asyncSearch().then(function (count) {
            self.status = 'done';
            if (count > 0) {
                self.current = LocatorService.getNextUser();
            }
            else {
                $log.debug('No users has been founded');
                self.searchTimer = $timeout(searchLoop, 20000);
            }
        }, ErrorHandler.handle);
    }

    searchLoop();

    $scope.$on('$destroy', function __destroy (event) {
        if (angular.isDefined(self.searchTimer)) {
            $timeout.cancel(self.searchTimer);
        }
    });
}

MainViewController.resolve = {
//    'users': function (LocatorService, ErrorService) {
//        return LocatorService.asyncSearch().then(null, ErrorService.handleError);
//    }
};

angular.module('spacebox').controller('MainViewController',
    ['$scope', '$log', '$timeout', 'LocatorService', 'MeetService', 'ErrorHandler', MainViewController]);
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const http = require('http');
const dns = require('dns');
const whois = require('whois');

const WebSocketServer = require('websocket').server;

const punycode = require('punycode');
const domaincheck = new RegExp("[^a-z0-9-.]","i");
const anyways = (promise) => promise.then((value) => ({value})).catch((error) => ({error}));
const config = require('./config');

const tsFormat = () => (new Date()).toLocaleTimeString();
const winston = require('winston');
const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({
      timestamp: tsFormat,
      colorize: true,
      level: config.debug ? 'debug' : 'info',
    })
  ]
});
process.on('unhandledRejection', error => {
  logger.error("A promise was rejected but the error wasn't handled:", error)
})

var httpService = (config.ssl) ? require('https') : require('http');
var server = null;
var memcached = null;

if (config.memcached) {
  var Memcached = require('memcached');
  memcached = new Memcached(config.memcached_server, {
    timeout: 2000,
    retries: 0,
    retry: 10000,
    reconnect: 10000,
    idle: 5000
  });
}

var processRequest = function(request, response) {
  response.writeHead(404);
  response.end();
};

if (config.ssl) {
  server = httpService.createServer({
    key: fs.readFileSync(config.ssl_key),
    cert: fs.readFileSync(config.ssl_cert)
  }, processRequest).listen(config.serverport || 8080, config.serverip || "0.0.0.0", function(){
    var addr = server.address();
    logger.info("Secure websocket server (wss) listening at", addr.address + ":" + addr.port);
  });
}
else {
  server = httpService.createServer(processRequest).listen(config.serverport || 8080, config.serverip || "0.0.0.0", function(){
    var addr = server.address();
    logger.info("Websocket server (ws) listening at", addr.address + ":" + addr.port);
  });
}

var wsServer = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: false
});

function checkMemcache(domain) {
  return new Promise((resolve, reject) => {
    if (config.memcached) {
      memcached.get(domain, (err, data) => {
        if (err) {
          logger.debug("Error in memcache query for domain", domain + ":", err);
          reject();
        }
        logger.debug("Result from memcached for domain", domain + ":", data);
        resolve(data);
      })
    }
    else {
      logger.debug("Memcache not configured, rejecting cache check.");
      reject();
    }
  })
}

function addToMemcache(domain, state) {
  return new Promise((resolve, reject) => {
    if (config.memcached) {
      memcached.set(domain, state, 3600, (err) => {
        if (err) {
          logger.debug("Can't insert state", state, "to memcached for domain", domain + ", error:", err);
          reject();
        }
        logger.debug("Inserted state", state, "to memcached for domain", domain);
        resolve();
      })
    }
    else {
      logger.debug("Memcache not configured, rejecting cache insert.");
    }
  })
}

function checkDNS(domain) {
  return new Promise((resolve, reject) => {
    dns.resolveNs(domain, (err, addresses) => {
      if (err) {
        logger.debug("DNS check for", domain, "failed:", err);
        reject(err);
      }
      else {
        logger.debug("DNS check for", domain + ":", addresses);
        resolve();
      }
    })
  })
}

function checkWhois(domain) {
  return new Promise((resolve, reject) => {
    whois.lookup(domain, {
      'follow':  0
    }, (err, whoisdata) => {
      if (err) {
        logger.error('Error during whois query for domain', domainname +':', err);
        reject();
      }
      whoisdata = whoisdata.toLowerCase();

      if (whoisdata.match('whois limit exceeded') ||
      whoisdata.match('request is being rate limited') ||
      whoisdata.match('quota exceeded')) {
        logger.error('Whois request failed for', domain, 'with response:', whoisdata);
        reject();
      }

      resolve(whoisdata);
    })
  })
}

function originIsAllowed(origin) {
  for (var orig in config.allowed_origin) {
        logger.debug('Checking', origin,  'against', config.allowed_origin[orig]);
        if (origin === config.allowed_origin[orig]) {
            return true;
        }
  }

  return false;
}

function whoisNotfound(whoisdata) {
  const whoisresult = JSON.stringify(whoisdata);

  if (whoisresult.includes("domain not found") ||
      whoisresult.includes("no match for") ||
      whoisresult.includes("not found") ||
      whoisresult.includes("status: available") ||
      whoisresult.includes("no domain exists") ||
      whoisresult.includes("no data found") ||
      whoisresult.includes("domain status: no object found") ||
      whoisresult.includes("object does not exist") ||
      whoisresult.includes("domain status: free") ||
      whoisresult.includes("domain name has not been registered")) {
    return true;
  }
  else {
    return false;
  }
}

wsServer.on('request', function(request) {
    if (!originIsAllowed(request.origin)) {
      request.reject();
      logger.info('Connection from origin', request.origin, 'rejected.');
      return;
    }

    var connection = request.accept('echo-protocol', request.origin);
    logger.info('Client', connection.remoteAddress, 'from', request.origin, 'accepted.');

    connection.on('message', function(domainname) {
      domainname = domainname.utf8Data;
      logger.debug('Whois request received for', domainname);

      var domain = punycode.toASCII(String(domainname || ''));
      if (!domain || domaincheck.test(domain) || domain.length < 3) {
        connection.sendUTF(domainname + ":INVALID");
        return;
      }

      anyways(checkMemcache(domainname)).then((result, error) => {
        if (result && result.value != undefined) {
          logger.info('Status for', domainname, 'to', connection.remoteAddress, 'from memcache:', result.value);
          connection.sendUTF(domainname + ':' + result.value);
          return;
        }

        checkDNS(domainname).then((dnsresult) => {
          logger.info('Status for', domainname, 'to', connection.remoteAddress, 'from DNS request: UNAVAILABLE');
          if (config.memcached) {
            addToMemcache(domainname, 'UNAVAILABLE').catch((err) => {
              logger.error('Error when saving result for', domainname + ':', err);
            })
          }
          connection.sendUTF(domainname + ':UNAVAILABLE');
          return;
        }).catch((err) => {

          // checking for known statuses that indicate a domain is taken
          var status;
          if (err.message.match('EBADNAME')) {
            status = 'INVALID';
            logger.info('Domain', domainname, 'is invalid per DNS query.');
          }
          if (err.message.match("SERVFAIL")) {
            logger.info('Domain', domainname, 'is unavailable per DNS query (but is not working).');
            status = 'UNAVAILABLE';
          }
          if (!err.message.match("ENOTFOUND") && !err.message.match("ENODATA")) {
            logger.error('DNS query error was encountered for', domainname + ':', err);
            status = 'SERVFAIL';
          }

          if (status != undefined) {
            connection.sendUTF(domainname + ':' + status);
            if (config.memcached) {
              addToMemcache(domainname, status).catch((err) => {
                logger.error('Error when saving result for', domainname + ':', err);
              })
            }
            return;
          }

          anyways(checkWhois(domainname)).then((whoisresult, error) => {
            if (error) {
              logger.error('Whois query error was encountered for', domainname + ':', err);
              status = 'SERVFAIL';
            }
            else {
              if (whoisNotfound(whoisresult)) {
                logger.info('Domain', domainname, 'is available per whois request.');
                status = 'AVAILABLE';
              }
              else {
                logger.info('Domain', domainname, 'is not available per whois request.');
                status = 'UNAVAILABLE';
              }
            }

            connection.sendUTF(domainname + ':' + status);
            if (config.memcached) {
              addToMemcache(domainname, status).catch((err) => {
                logger.error('Error when saving result for', domainname + ':', err);
              })
            }
            return;
          })

        })
      })
    });

    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Client ' + connection.remoteAddress + ' disconnected.');
    });
});
<commit_msg>Nicer logging, fixed indentation and better DNS result handling.<commit_after>'use strict';

const fs = require('fs');
const http = require('http');
const dns = require('dns');
const whois = require('whois');

const WebSocketServer = require('websocket').server;

const punycode = require('punycode');
const domaincheck = new RegExp("[^a-z0-9-.]","i");
const anyways = (promise) => promise.then((value) => ({value})).catch((error) => ({error}));
const config = require('./config');

const tsFormat = () => (new Date()).toLocaleTimeString();
const winston = require('winston');
const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({
      timestamp: tsFormat,
      colorize: true,
      level: config.debug ? 'debug' : 'info',
    })
  ]
});
process.on('unhandledRejection', error => {
  logger.error("A promise was rejected but the error wasn't handled:", error)
})

var httpService = (config.ssl) ? require('https') : require('http');
var server = null;
var memcached = null;

if (config.memcached) {
  var Memcached = require('memcached');
  memcached = new Memcached(config.memcached_server, {
    timeout: 2000,
    retries: 0,
    retry: 10000,
    reconnect: 10000,
    idle: 5000
  });
}

var processRequest = function(request, response) {
  response.writeHead(404);
  response.end();
};

if (config.ssl) {
  server = httpService.createServer({
    key: fs.readFileSync(config.ssl_key),
    cert: fs.readFileSync(config.ssl_cert)
  }, processRequest).listen(config.serverport || 8080, config.serverip || "0.0.0.0", function(){
    var addr = server.address();
    logger.info("Secure websocket server (wss) listening at", addr.address + ":" + addr.port);
  });
}
else {
  server = httpService.createServer(processRequest).listen(config.serverport || 8080, config.serverip || "0.0.0.0", function(){
    var addr = server.address();
    logger.info("Websocket server (ws) listening at", addr.address + ":" + addr.port);
  });
}

var wsServer = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: false
});

function checkMemcache(domain) {
  return new Promise((resolve, reject) => {
    if (config.memcached) {
      memcached.get(domain, (err, data) => {
        if (err) {
          logger.debug("Error in memcache query for domain", domain + ":", err);
          reject();
        }
        logger.debug("Result from memcached for domain", domain + ":", data);
        resolve(data);
      })
    }
    else {
      logger.debug("Memcache not configured, rejecting cache check.");
      reject();
    }
  })
}

function addToMemcache(domain, state) {
  return new Promise((resolve, reject) => {
    if (config.memcached) {
      memcached.set(domain, state, 3600, (err) => {
        if (err) {
          logger.debug("Can't insert state", state, "to memcached for domain", domain + ", error:", err);
          reject();
        }
        logger.debug("Inserted state", state, "to memcached for domain", domain);
        resolve();
      })
    }
    else {
      logger.debug("Memcache not configured, rejecting cache insert.");
    }
  })
}

function checkDNS(domain) {
  return new Promise((resolve, reject) => {
    dns.resolveNs(domain, (err, addresses) => {
      if (err) {
        logger.debug("DNS check for", domain, "failed:", err);
        reject(err);
      }
      else {
        logger.debug("DNS check for", domain + ":", addresses);
        resolve();
      }
    })
  })
}

function checkWhois(domain) {
  return new Promise((resolve, reject) => {
    whois.lookup(domain, {
      'follow':  0
    }, (err, whoisdata) => {
      if (err) {
        logger.error('Error during whois query for domain', domainname +':', err);
        reject();
      }
      whoisdata = whoisdata.toLowerCase();

      if (whoisdata.match('whois limit exceeded') ||
      whoisdata.match('request is being rate limited') ||
      whoisdata.match('quota exceeded')) {
        logger.error('Whois request failed for', domain, 'with response:', whoisdata);
        reject();
      }

      resolve(whoisdata);
    })
  })
}

function originIsAllowed(origin) {
  for (var orig in config.allowed_origin) {
        logger.debug('Checking', origin,  'against', config.allowed_origin[orig]);
        if (origin === config.allowed_origin[orig]) {
            return true;
        }
  }

  return false;
}

function whoisNotfound(whoisdata) {
  const whoisresult = JSON.stringify(whoisdata);

  if (whoisresult.includes("domain not found") ||
      whoisresult.includes("no match for") ||
      whoisresult.includes("not found") ||
      whoisresult.includes("status: available") ||
      whoisresult.includes("no domain exists") ||
      whoisresult.includes("no data found") ||
      whoisresult.includes("domain status: no object found") ||
      whoisresult.includes("object does not exist") ||
      whoisresult.includes("domain status: free") ||
      whoisresult.includes("domain name has not been registered")) {
    return true;
  }
  else {
    return false;
  }
}

wsServer.on('request', function(request) {

  if (!originIsAllowed(request.origin)) {
    request.reject();
    logger.info('Client', request.remoteAddress, 'from', request.origin, 'rejected.');
    return;
  }

  var connection = request.accept('echo-protocol', request.origin);
  logger.info('Client', connection.remoteAddress, 'from', request.origin, 'connected.');

  connection.on('message', function(domainname) {
    domainname = domainname.utf8Data;
    var status;
    logger.debug('Whois request received for', domainname);

    var domain = punycode.toASCII(String(domainname || ''));
    if (!domain || domaincheck.test(domain) || domain.length < 3) {
      connection.sendUTF(domainname + ":INVALID");
      return;
    }

    anyways(checkMemcache(domainname)).then((result, error) => {
      if (result && result.value != undefined) {
        status = result.value;
        logger.info(connection.remoteAddress, 'DOMAINSTATUS', domainname + ':', status, '(from Memcached)');
        connection.sendUTF(domainname + ':' + status);
        return;
      }

      checkDNS(domainname).then((dnsresult) => {
        status = 'UNAVAILABLE';
        logger.info(connection.remoteAddress, 'DOMAINSTATUS', domainname + ':', status, '(from DNS)');
        if (config.memcached) {
          addToMemcache(domainname, status).catch((err) => {
            logger.error('Error when saving result for', domainname + ':', err);
          })
        }
        connection.sendUTF(domainname + ':' + status);
        return;
      }).catch((err) => {

        // checking for known statuses that indicate a domain is taken
        if (err.message.includes('EBADNAME')) {
          status = 'INVALID';
          logger.debug('Domain', domainname, 'is invalid per DNS query.');
        }
        if (err.message.includes("ESERVFAIL")) {
          logger.debug('Domain', domainname, 'is unavailable per DNS query (but is not working).');
          status = 'UNAVAILABLE';
        }

        if (status != undefined) {
          logger.info(connection.remoteAddress, 'DOMAINSTATUS', domainname + ':', status, '(from DNS)');
          connection.sendUTF(domainname + ':' + status);
          if (config.memcached) {
            addToMemcache(domainname, status).catch((err) => {
              logger.error('Error when saving result for', domainname + ':', err);
            })
          }
          return;
        }

        anyways(checkWhois(domainname)).then((whoisresult, error) => {
          if (error) {
            logger.error('Whois query error was encountered for', domainname + ':', err);
            status = 'SERVFAIL';
          }
          else {
            if (whoisNotfound(whoisresult)) {
              logger.debug('Domain', domainname, 'is available per whois request.');
              status = 'AVAILABLE';
            }
            else {
              logger.debug('Domain', domainname, 'is not available per whois request.');
              status = 'UNAVAILABLE';
            }
          }

          logger.info(connection.remoteAddress, 'DOMAINSTATUS', domainname + ':', status, '(from WHOIS)');
          connection.sendUTF(domainname + ':' + status);
          if (config.memcached) {
            addToMemcache(domainname, status).catch((err) => {
              logger.error('Error when saving result for', domainname + ':', err);
            })
          }
          return;
        })

      })
    })
  });

  connection.on('close', function(reasonCode, description) {
    logger.info('Client', connection.remoteAddress, 'disconnected.');
  });
});
<|endoftext|>


<commit_before>var CONSTANTS = require('./constants.js');
var _ = require('underscore');

var Feather = function(settings){

	/*
		VARIABLES
	*/
	// Reference to "this"
	var _self = this;

	// Should console.logs be printed out
	this._verbose = (settings && settings.verbose) || false;

	// Should request and trigger RSSI update notices
	this._requestRSSI = (settings && settings.rssi) || false;

	// Rate at which RSSI should be updated
	this._requestRSSIRate = (settings && settings.rssi_update_rate) || CONSTANTS.REQUEST_RSSI_UPDATE_RATE;

	// Reference to the requestRSSIInterval
	this._requestRSSIInterval;

	// Pause flag for the requestRSSIInterval
	this._requestRSSIIntervalisPause = false;

	// Noble Peripheral Object
	this._peripheral = (settings && settings.peripheral) || null;

	// Incoming Characteristic
	this._read;

	// inputBuffer String
	this._inputBuffer = "";

	// The last full message recieved
	this._lastMessage;

	// Outgoing Characteristic
	this._write;

	// If wearable is ready
	this._ready = false;

	// If this feather is connected
	this._connected = false;

	// Listener Event callbacks
	this._listeners = {
		// When feather is connected and ready
		"ready": [],

		// When feather disconnects
		"disconnect": [],

		// When the feather sends any message back
		"message": [],

		// When the RSSI updates
		"rssi": []
	};

	/*
		METHODS
	*/
	this.on = function(event, callback){
		_self._listeners[event].push(callback);
	};

	// Connects, finds read/white characteristics, grabs the userID from the arduino, and triggers "ready" listeners
	this.setup = function(){

		_self._peripheral.connect(function(err){

			if (err != null) {
				if (_self._verbose){
					console.log("\tCould not connect.\n\n");
					console.log(err);
				}
				return;
			}

			if (_self._verbose){
				console.log("\tConnected!\n\n");
			}

			_self._connected = true;

			_self._peripheral.once('disconnect', function(){
				if (_self._verbose){
					console.log("\n\nPeripheral disconnected.");
				}

				_self._connected = false;

				// Trigger disconnect callbacks
				_.each(_self._listeners.disconnect, function(callback){
					callback();
				});

			});

			_self._peripheral.discoverServices([CONSTANTS.UART_SERVICE_UUID], function(err, services){

				if (err != null) {
					if (_self._verbose){
						console.log("\tError recieving services.\n\n");
						console.log(err);
					}
					return;
				}

				if (services.length < 1) {
					if (_self._verbose){
						console.log("\tCould not get service(s).\n\n");
					}
					return;
				}

				var characteristicUUIDs = [CONSTANTS.READ_CHARACTERISTIC_UUID, CONSTANTS.WRITE_CHARACTERISTIC_UUID];

				services[0].discoverCharacteristics(characteristicUUIDs, function(err, characteristics){

					if (err != null || characteristics.length < 1) {
						if (_self._verbose){
							console.log("\tCould not get characteristics for service "+service.uuid+".\n\n");
						}
						// console.log("\t" + err);
						return;
					}

					if (_self._verbose){
						console.log("\tCharacteristics found ("+characteristics.length+").\n\n");
					}

					// console.log(characteristics);

					_.each(characteristics, function(characteristic){

						if (characteristic.uuid == CONSTANTS.READ_CHARACTERISTIC_UUID){
							if (_self._verbose){
								console.log("Setting listener for data notification on characteristic "+characteristic.uuid);
							}

							characteristic.on('read', function(data, isNotification){
								//console.log("From read.");
								_self.dataRecieved(characteristic, data, isNotification);
							});

							if (_self._verbose){
								console.log("Trying to subscribe to characteristic "+characteristic.uuid+"...");
							}
							characteristic.notify(true, function(err){

								if (err != null) {
									if (_self._verbose){
										console.log("\tError subscribing.\n\n");
										console.log("\t", err);
									}
									return checkSetupStatus(err);
								}

								if (_self._verbose){
									console.log("\tSubscribed.\n\n");
								}
								_self._read = characteristic;
								checkSetupStatus();
							});
						}

						if (characteristic.uuid == CONSTANTS.WRITE_CHARACTERISTIC_UUID){
							_self._write = characteristic;
							checkSetupStatus();
						}
					});
				});
			});
		});

		function checkSetupStatus(err){

			if (err) {
				// NOTE: Trigger Ready callbacks
				_.each(_self._listeners.ready, function(callback){
					callback(err);
				});
			}

			if (_self._read != null && _self._write != null) {

				// MARK: Request for RSSI Updates if applicable
				if (_self._requestRSSI) {

					_self._requestRSSIInterval = setInterval(function(){
						// NOTE: if interval is not paused
						if (!_self._connected) {
							clearInterval(_self._requestRSSIInterval);

							_.each(_self._listeners.rssi, function(callback){
								var err = new Error("No longer connected to anything...");
								callback(err, -1000);
							});
						}

						if (!_self._requestRSSIIntervalisPause) {

							// NOTE: Pause interval
							_self._requestRSSIIntervalisPause = true;

							// request new rssi
							_self._peripheral.once('rssiUpdate', function(err, rssi){
								if (!_self._connected) {
									clearInterval(_self._requestRSSIInterval);
									return;
								}

								// Trigger RSSI callbacks
								_.each(_self._listeners.rssi, function(callback){
									callback(err, rssi);
								});

								// NOTE: Unpause interval
								_self._requestRSSIIntervalisPause = false;
							});

							// _self._peripheral.updateRssi(function(err, rssi){
							// 	if (!_self._connected) {
							// 		clearInterval(_self._requestRSSIInterval);
							// 		return;
							// 	}

							// 	// Trigger RSSI callbacks
							// 	_.each(_self._listeners.rssi, function(callback){
							// 		callback(err, rssi);
							// 	});

							// 	// NOTE: Unpause interval
							// 	_self._requestRSSIIntervalisPause = false;
							// });
						}
					}, _self._requestRSSIRate);
				}

				_self._ready = true;

				// Trigger ready callbacks
				_.each(_self._listeners.ready, function(callback){
					callback();
				});
			}
		}
	};

	// Send a message (String) to this wearable with a callback on completion
	this.sendMessage = function(msg, callback){

		if (! _self._connected) {
			// Dont try to send messages if not connected
			return;
		}

		if (msg[msg.length] != CONSTANTS.MESSAGE_TERMINATOR) msg += CONSTANTS.MESSAGE_TERMINATOR;

		var messages = chunkString(msg, CONSTANTS.BLE_MAX_CHUNK_SIZE);
		var goalLength = messages.length;

		var sentMessages = {};

		_.each(messages, function(message, index, list){
			(function(msg, i){

				var msgBuffer = new Buffer(msg, "utf-8");
				var key = i.toString();

				sentMessages[key] = {};

				//console.log("SentMessages:", sentMessages);

				_self._write.write(msgBuffer, true, function(err){
					if (err) {
						if (_self._verbose){
							console.log("\tError sending message.\n\n");
							console.log("\t", err);
						}

						sentMessages[key].wasSent = true;
						sentMessages[key].hasError = true;
						sentMessages[key].error = err;

						return checkStatus();
					}

					//console.log("\tMessage sent: "+i+"\n\n");

					sentMessages[key].wasSent = true;
					sentMessages[key].hasError = false;
					sentMessages[key].error = null;

					return checkStatus();
				});
			})(message, index);
		});

		function checkStatus() {

			var numSent = 0;

			var wasError;

			for (var prop in sentMessages) {
				if (sentMessages[prop].wasSent) numSent++;

				if (sentMessages[prop].hasError) wasError = sentMessages[prop].error;
			}

			if (numSent == goalLength) {

				if (callback) return callback(wasError);

			}
		}

		// SOURCE: http://stackoverflow.com/questions/7033639/split-large-string-in-n-size-chunks-in-javascript
		function chunkString(str, length) {
			return str.match(new RegExp('.{1,' + length + '}', 'g'));
		}
	};

	this.dataRecieved = function(characteristic, data, isNotification){
		if (_self._verbose){
			console.log("\nData Recieved:");
			console.log("\tCharacteristic: " + characteristic.uuid);
			console.log("\tNotification: " + isNotification);
			console.log("\tData: " + data + "\n");
		}

		for (var i = 0; i < data.length; i++){
			var c = String.fromCharCode(data[i]);

			// console.log("Recieved: " + c);

			if (c == CONSTANTS.MESSAGE_TERMINATOR) {

				_self._lastMessage = _self._inputBuffer;

				_self._inputBuffer = "";

				// Trigger message callbacks
				_.each(_self._listeners.message, function(callback){
					callback(_self._lastMessage);
				});
			}
			else {
				_self._inputBuffer += c;
			}
		}
	};

	this.disconnect = function(){
		_self._connected = false;
		_self._peripheral.disconnect();
	};

	this.isFeather = function(peripheral){
		// if ((peripheral.id == WEARABLE_PERIFERAL_ID || peripheral.advertisement.localName == WEARABLE_LOCAL_NAME) && _.contains(peripheral.advertisement.serviceUuids, CONSTANTS.UART_SERVICE_UUID)) {
		if (_.contains(peripheral.advertisement.serviceUuids, CONSTANTS.UART_SERVICE_UUID)) {
			return true;
		}

		return false;
	};

};

module.exports = Feather;<commit_msg>change back to updateRssicCalls<commit_after>var CONSTANTS = require('./constants.js');
var _ = require('underscore');

var Feather = function(settings){

	/*
		VARIABLES
	*/
	// Reference to "this"
	var _self = this;

	// Should console.logs be printed out
	this._verbose = (settings && settings.verbose) || false;

	// Should request and trigger RSSI update notices
	this._requestRSSI = (settings && settings.rssi) || false;

	// Rate at which RSSI should be updated
	this._requestRSSIRate = (settings && settings.rssi_update_rate) || CONSTANTS.REQUEST_RSSI_UPDATE_RATE;

	// Reference to the requestRSSIInterval
	this._requestRSSIInterval;

	// Pause flag for the requestRSSIInterval
	this._requestRSSIIntervalisPause = false;

	// Noble Peripheral Object
	this._peripheral = (settings && settings.peripheral) || null;

	// Incoming Characteristic
	this._read;

	// inputBuffer String
	this._inputBuffer = "";

	// The last full message recieved
	this._lastMessage;

	// Outgoing Characteristic
	this._write;

	// If wearable is ready
	this._ready = false;

	// If this feather is connected
	this._connected = false;

	// Listener Event callbacks
	this._listeners = {
		// When feather is connected and ready
		"ready": [],

		// When feather disconnects
		"disconnect": [],

		// When the feather sends any message back
		"message": [],

		// When the RSSI updates
		"rssi": []
	};

	/*
		METHODS
	*/
	this.on = function(event, callback){
		_self._listeners[event].push(callback);
	};

	// Connects, finds read/white characteristics, grabs the userID from the arduino, and triggers "ready" listeners
	this.setup = function(){

		_self._peripheral.connect(function(err){

			if (err != null) {
				if (_self._verbose){
					console.log("\tCould not connect.\n\n");
					console.log(err);
				}
				return;
			}

			if (_self._verbose){
				console.log("\tConnected!\n\n");
			}

			_self._connected = true;

			_self._peripheral.once('disconnect', function(){
				if (_self._verbose){
					console.log("\n\nPeripheral disconnected.");
				}

				_self._connected = false;

				// Trigger disconnect callbacks
				_.each(_self._listeners.disconnect, function(callback){
					callback();
				});

			});

			_self._peripheral.discoverServices([CONSTANTS.UART_SERVICE_UUID], function(err, services){

				if (err != null) {
					if (_self._verbose){
						console.log("\tError recieving services.\n\n");
						console.log(err);
					}
					return;
				}

				if (services.length < 1) {
					if (_self._verbose){
						console.log("\tCould not get service(s).\n\n");
					}
					return;
				}

				var characteristicUUIDs = [CONSTANTS.READ_CHARACTERISTIC_UUID, CONSTANTS.WRITE_CHARACTERISTIC_UUID];

				services[0].discoverCharacteristics(characteristicUUIDs, function(err, characteristics){

					if (err != null || characteristics.length < 1) {
						if (_self._verbose){
							console.log("\tCould not get characteristics for service "+service.uuid+".\n\n");
						}
						// console.log("\t" + err);
						return;
					}

					if (_self._verbose){
						console.log("\tCharacteristics found ("+characteristics.length+").\n\n");
					}

					// console.log(characteristics);

					_.each(characteristics, function(characteristic){

						if (characteristic.uuid == CONSTANTS.READ_CHARACTERISTIC_UUID){
							if (_self._verbose){
								console.log("Setting listener for data notification on characteristic "+characteristic.uuid);
							}

							characteristic.on('read', function(data, isNotification){
								//console.log("From read.");
								_self.dataRecieved(characteristic, data, isNotification);
							});

							if (_self._verbose){
								console.log("Trying to subscribe to characteristic "+characteristic.uuid+"...");
							}
							characteristic.notify(true, function(err){

								if (err != null) {
									if (_self._verbose){
										console.log("\tError subscribing.\n\n");
										console.log("\t", err);
									}
									return checkSetupStatus(err);
								}

								if (_self._verbose){
									console.log("\tSubscribed.\n\n");
								}
								_self._read = characteristic;
								checkSetupStatus();
							});
						}

						if (characteristic.uuid == CONSTANTS.WRITE_CHARACTERISTIC_UUID){
							_self._write = characteristic;
							checkSetupStatus();
						}
					});
				});
			});
		});

		function checkSetupStatus(err){

			if (err) {
				// NOTE: Trigger Ready callbacks
				_.each(_self._listeners.ready, function(callback){
					callback(err);
				});
			}

			if (_self._read != null && _self._write != null) {

				// MARK: Request for RSSI Updates if applicable
				if (_self._requestRSSI) {

					_self._requestRSSIInterval = setInterval(function(){
						// NOTE: if interval is not paused
						if (!_self._connected) {
							clearInterval(_self._requestRSSIInterval);

							_.each(_self._listeners.rssi, function(callback){
								var err = new Error("No longer connected to anything...");
								callback(err, -1000);
							});
						}

						if (!_self._requestRSSIIntervalisPause) {

							// NOTE: Pause interval
							_self._requestRSSIIntervalisPause = true;

							// request new rssi
							// _self._peripheral.once('rssiUpdate', function(err, rssi){
							// 	if (!_self._connected) {
							// 		clearInterval(_self._requestRSSIInterval);
							// 		return;
							// 	}

							// 	// Trigger RSSI callbacks
							// 	_.each(_self._listeners.rssi, function(callback){
							// 		callback(err, rssi);
							// 	});

							// 	// NOTE: Unpause interval
							// 	_self._requestRSSIIntervalisPause = false;
							// });

							_self._peripheral.updateRssi(function(err, rssi){
								if (!_self._connected) {
									clearInterval(_self._requestRSSIInterval);
									return;
								}

								// Trigger RSSI callbacks
								_.each(_self._listeners.rssi, function(callback){
									callback(err, rssi);
								});

								// NOTE: Unpause interval
								_self._requestRSSIIntervalisPause = false;
							});
						}
					}, _self._requestRSSIRate);
				}

				_self._ready = true;

				// Trigger ready callbacks
				_.each(_self._listeners.ready, function(callback){
					callback();
				});
			}
		}
	};

	// Send a message (String) to this wearable with a callback on completion
	this.sendMessage = function(msg, callback){

		if (! _self._connected) {
			// Dont try to send messages if not connected
			return;
		}

		if (msg[msg.length] != CONSTANTS.MESSAGE_TERMINATOR) msg += CONSTANTS.MESSAGE_TERMINATOR;

		var messages = chunkString(msg, CONSTANTS.BLE_MAX_CHUNK_SIZE);
		var goalLength = messages.length;

		var sentMessages = {};

		_.each(messages, function(message, index, list){
			(function(msg, i){

				var msgBuffer = new Buffer(msg, "utf-8");
				var key = i.toString();

				sentMessages[key] = {};

				//console.log("SentMessages:", sentMessages);

				_self._write.write(msgBuffer, true, function(err){
					if (err) {
						if (_self._verbose){
							console.log("\tError sending message.\n\n");
							console.log("\t", err);
						}

						sentMessages[key].wasSent = true;
						sentMessages[key].hasError = true;
						sentMessages[key].error = err;

						return checkStatus();
					}

					//console.log("\tMessage sent: "+i+"\n\n");

					sentMessages[key].wasSent = true;
					sentMessages[key].hasError = false;
					sentMessages[key].error = null;

					return checkStatus();
				});
			})(message, index);
		});

		function checkStatus() {

			var numSent = 0;

			var wasError;

			for (var prop in sentMessages) {
				if (sentMessages[prop].wasSent) numSent++;

				if (sentMessages[prop].hasError) wasError = sentMessages[prop].error;
			}

			if (numSent == goalLength) {

				if (callback) return callback(wasError);

			}
		}

		// SOURCE: http://stackoverflow.com/questions/7033639/split-large-string-in-n-size-chunks-in-javascript
		function chunkString(str, length) {
			return str.match(new RegExp('.{1,' + length + '}', 'g'));
		}
	};

	this.dataRecieved = function(characteristic, data, isNotification){
		if (_self._verbose){
			console.log("\nData Recieved:");
			console.log("\tCharacteristic: " + characteristic.uuid);
			console.log("\tNotification: " + isNotification);
			console.log("\tData: " + data + "\n");
		}

		for (var i = 0; i < data.length; i++){
			var c = String.fromCharCode(data[i]);

			// console.log("Recieved: " + c);

			if (c == CONSTANTS.MESSAGE_TERMINATOR) {

				_self._lastMessage = _self._inputBuffer;

				_self._inputBuffer = "";

				// Trigger message callbacks
				_.each(_self._listeners.message, function(callback){
					callback(_self._lastMessage);
				});
			}
			else {
				_self._inputBuffer += c;
			}
		}
	};

	this.disconnect = function(){
		_self._connected = false;
		_self._peripheral.disconnect();
	};

	this.isFeather = function(peripheral){
		// if ((peripheral.id == WEARABLE_PERIFERAL_ID || peripheral.advertisement.localName == WEARABLE_LOCAL_NAME) && _.contains(peripheral.advertisement.serviceUuids, CONSTANTS.UART_SERVICE_UUID)) {
		if (_.contains(peripheral.advertisement.serviceUuids, CONSTANTS.UART_SERVICE_UUID)) {
			return true;
		}

		return false;
	};

};

module.exports = Feather;<|endoftext|>