<commit_before>/**
 * Module dependencies.
 */
var fs = require('fs')
  , xmpp = require('node-xmpp')
  , application = require('./application')
  , utils = require('./utils')
  , StanzaError = require('./stanzaerror');

// expose create() as the module
exports = module.exports = create;

/**
 * Framework version.
 */
require('pkginfo')(module, 'version');

function create() {
  function app(stanza) { app.handle(stanza); }
  utils.merge(app, application);
  app._stack = [];
  app._filters = [];
  for (var i = 0; i < arguments.length; ++i) {
    app.use(arguments[i]);
  }
  return app;
}

create.create = create;

/**
 * Expose constructors.
 */
exports.JID = xmpp.JID;
exports.XMLElement = xmpp.Element;
exports.StanzaError = StanzaError;

/**
 * Expose element constructors.
 */

exports.elements = {};
exports.elements.Element = require('./elements/element');
exports.elements.IQ = require('./elements/iq');
exports.elements.Message = require('./elements/message');
exports.elements.Presence = require('./elements/presence');

/**
 * Expose bundled filters.
 */

exports.filters = {};
exports.filters.pending = require('./filters/pending');
exports.filters.dump = require('./filters/dump');

/**
 * Auto-load bundled middleware.
 */

exports.middleware = {};

fs.readdirSync(__dirname + '/middleware').forEach(function(filename) {
  if (/\.js$/.test(filename)) {
    var name = filename.substr(0, filename.lastIndexOf('.'));
    exports.middleware.__defineGetter__(name, function(){
      return require('./middleware/' + name);
    });
  }
});

/**
 * Expose middleware as first-class exports.
 */

exports.__proto__ = exports.middleware;
<commit_msg>Improve loading of middleware and filters.<commit_after>/**
 * Module dependencies.
 */
var fs = require('fs')
  , path = require('path')
  , xmpp = require('node-xmpp')
  , application = require('./application')
  , utils = require('./utils')
  , StanzaError = require('./stanzaerror');

// expose create() as the module
exports = module.exports = create;

/**
 * Framework version.
 */
require('pkginfo')(module, 'version');

/**
 * Create a new Junction application.
 *
 * @return {Function}
 * @api public
 */
function create() {
  function app(stanza) { app.handle(stanza); }
  utils.merge(app, application);
  app._stack = [];
  app._filters = [];
  for (var i = 0; i < arguments.length; ++i) {
    app.use(arguments[i]);
  }
  return app;
}

/**
 * Expose `.create()` as module method.
 */
create.create = create;

/**
 * Expose constructors.
 */
exports.JID = xmpp.JID;
exports.XMLElement = xmpp.Element;
exports.StanzaError = StanzaError;

/**
 * Expose element constructors.
 */
exports.elements = {};
exports.elements.Element = require('./elements/element');
exports.elements.IQ = require('./elements/iq');
exports.elements.Message = require('./elements/message');
exports.elements.Presence = require('./elements/presence');

/**
 * Auto-load bundled filters.
 */
exports.filters = {};

fs.readdirSync(__dirname + '/filters').forEach(function(filename) {
  if (/\.js$/.test(filename)) {
    var name = path.basename(filename, '.js');
    function load() { return require('./filters/' + name); }
    exports.filters.__defineGetter__(name, load);
  }
});

/**
 * Auto-load bundled middleware.
 */
exports.middleware = {};

fs.readdirSync(__dirname + '/middleware').forEach(function(filename) {
  if (/\.js$/.test(filename)) {
    var name = path.basename(filename, '.js');
    function load() { return require('./middleware/' + name); }
    exports.middleware.__defineGetter__(name, load);
    exports.__defineGetter__(name, load);
  }
});
<|endoftext|>


<commit_before>require ('./styles')
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
const Loader = require('./loader')

const normalBtn = document.getElementById('game-normal-mode')
const speedBtn = document.getElementById('game-speed-mode')

normalBtn.addEventListener('click', () => {
  normalBtn.classList.add('hidden')
  speedBtn.classList.add('hidden')
  const playerOneScore = document.getElementById('player-one-score').classList
  playerOneScore.remove('hidden')
  const playerTwoScore = document.getElementById('player-two-score').classList
  playerTwoScore.remove('hidden')
  const quitText = document.getElementById('quit-text').classList
  quitText.remove('hidden')
  const bomberman = new Loader(canvas, context, 'DefaultMap')
  bomberman.startGame
})

speedBtn.addEventListener('click', () => {
  speedBtn.classList.add('hidden')
  normalBtn.classList.add('hidden')
  const playerOneScore = document.getElementById('player-one-score').classList
  playerOneScore.remove('hidden')
  const playerTwoScore = document.getElementById('player-two-score').classList
  playerTwoScore.remove('hidden')
  const quitText = document.getElementById('quit-text').classList
  quitText.remove('hidden')
  const bomberman = new Loader(canvas, context, 'SmallMap')
  bomberman.startGame
})

document.addEventListener('keydown', e => {
  document.getElementById('game-over').classList.add('hidden')
  document.getElementById('game').classList.remove('end-game')
  document.getElementById('next-game').classList.add('hidden')
  if (e.keyCode === 49) {
    context.clearRect(0, 0, canvas.width, canvas.height)
    const bomberman = new Loader(canvas, context, 'DefaultMap')
    bomberman.startGame
  } else if (e.keyCode === 50) {
    context.clearRect(0, 0, canvas.width, canvas.height)
    const bomberman = new Loader(canvas, context, 'SmallMap')
    bomberman.startGame
  }
})
<commit_msg>refactor<commit_after>require ('./styles')
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
const Loader = require('./loader')

const normalBtn = document.getElementById('game-normal-mode')
const speedBtn = document.getElementById('game-speed-mode')

normalBtn.addEventListener('click', () => {
  gamePrep('DefaultMap')
})

speedBtn.addEventListener('click', () => {
  gamePrep('SmallMap')
})

function gamePrep(level) {
  normalBtn.classList.add('hidden')
  speedBtn.classList.add('hidden')
  const playerOneScore = document.getElementById('player-one-score').classList
  playerOneScore.remove('hidden')
  const playerTwoScore = document.getElementById('player-two-score').classList
  playerTwoScore.remove('hidden')
  const quitText = document.getElementById('quit-text').classList
  quitText.remove('hidden')
  const bomberman = new Loader(canvas, context, level)
  bomberman.startGame
}

document.addEventListener('keydown', e => {
  document.getElementById('game-over').classList.add('hidden')
  document.getElementById('game').classList.remove('end-game')
  document.getElementById('next-game').classList.add('hidden')
  if (e.keyCode === 49) {
    context.clearRect(0, 0, canvas.width, canvas.height)
    const bomberman = new Loader(canvas, context, 'DefaultMap')
    bomberman.startGame
  } else if (e.keyCode === 50) {
    context.clearRect(0, 0, canvas.width, canvas.height)
    const bomberman = new Loader(canvas, context, 'SmallMap')
    bomberman.startGame
  }
})
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var url = require('url');
var util = require('util');
var net = require('net');
var http = require('http');
var spdy = require('spdy');

var plugins = require('./plugins');
var cache = require('./cache');
var log = require('./log');

var NAME = exports.name = 'proxy';
var RESPONSE_TIMEOUT = 10000;

var metrics = require('./metrics').session(NAME);

// Shortens the given URL to given maxLen by inserting '...'.
function shortenUrl(url, maxLen) {
  var len = Math.max(5, (maxLen || 33));

  if (url.length <= len) {
    return url;
  }

  var hLen = (len - 3) / 2;
  var shortUrl = url.substr(0, hLen) + '...' + url.substr(url.length - hLen);
  return shortUrl;
}

var SpdyProxy = function(options) {

  function handleListen() {
    cache.init(options);
    log.info('%s listens on port %d', options.name, options.proxy.port);
  }

  function parseRequestOptions(request) {
    var options = { enabled: [], disabled: [] };
    var header = request.headers['x-janus-options'];
    if (!header) {
      return options;
    }

    var split = header.trim().split(' ');
    split.forEach(function(token) {
      if (token[0] === '+') {
        options.enabled.push(token.substring(1));
      } else if (token[0] === '-') {
        options.disabled.push(token.substring(1));
      }
    });

    return options;
  }

  // Handles GET and POST request.
  function handleRequest(request, response) {
    metrics.count('request');
    metrics.streamTimer(response, 'response', RESPONSE_TIMEOUT);

    // Normalize request path.
    request.path = request.headers.path || url.parse(request.url).path;

    request.originalUrl = url.format({
      protocol: request.headers.scheme || 'http',
      host: request.headers.host,
      pathname: request.path
    });

    log.logify(request, shortenUrl(request.originalUrl.toString()));
    request.info('HTTP/' + request.httpVersion + ' ' + request.method);

    var requestOpts = parseRequestOptions(request);
    request.debug('request options', requestOpts);

    response.on('close', function() {
      request.warn('unexpected close in ' + NAME);
    });

    response.on('error', function(e) {
      request.error(NAME + ' error: ' + e.message);
    });

    var pluginRequestTimer = metrics.timer('plugin.request');

    plugins.handleRequest(request, response, requestOpts, function(e, handled) {
      pluginRequestTimer.stop();

      if (handled) {
        // Request was serviced by a plugin.
        return;
      }

      var fwdOpts = {
        host: request.headers.host.split(':')[0],
        port: request.headers.host.split(':')[1] || 80,
        path: request.path,
        method: request.method,
        headers: request.headers
      };

      var forwardRequest = http.request(fwdOpts, function(forwardResponse) {
        // Pass non-2XX responses straight through.
        if (forwardResponse.statusCode < 200 ||
            forwardResponse.statusCode >= 300)
        {
          response.writeHead(forwardResponse.statusCode,
                             forwardResponse.headers);
          forwardResponse.pipe(response);
          return;
        }

        metrics.streamTimer(response, 'plugin.response');

        forwardResponse.headers.via = options.title;
        plugins.handleResponse(request, forwardResponse, response, requestOpts);
      });

      forwardRequest.on('error', function(e) {
        request.error(NAME + ' error: ' + e.message);
        response.writeHead(502, 'Proxy fetch failed');
        response.end();
      });

      forwardRequest.on('close', function() {
        request.warn('unexpected close in ' + NAME);
      });

      forwardRequest.on('error', function(e) {
        request.error(NAME + ' error: ' + e.message);
      });

      response.on('close', function() {
        forwardRequest.abort();
      });

      response.on('error', function() {
        forwardRequest.abort();
      });

      // Pipe POST data.
      request.pipe(forwardRequest);
    });
  }

  // Handles CONNECT request.
  function handleConnect(request, socket) {
    metrics.count('connect');

    var tunnelOpts = {
      host: request.url.split(':')[0],
      port: request.url.split(':')[1] || 443,
    };

    log.info('%s\tHTTPS/%s\t%s\t%s',
        new Date().toISOString(),
        request.httpVersion,
        request.method,
        tunnelOpts.host
    );

    var tunnel = net.createConnection(tunnelOpts, function() {
      synReply(socket, 200, 'Connection established',
        {
          Connection: 'keep-alive',
          'via': options.title
        },
        function() {
          tunnel.pipe(socket);
          socket.pipe(tunnel);
        }
      );
    });

    tunnel.setNoDelay(true);

    tunnel.on('error', function(e) {
      log.error('Tunnel error: %s', e);
      synReply(socket, 502, 'Tunnel Error', {}, function() {
        socket.end();
      });
    });
  }

  function synReply(socket, code, reason, headers, callback) {
    try {
      if (!socket._lock) {
        // Not a SPDY socket.
        log.error('Not a SPDY socket');
        return;
      }

      socket._lock(function() {
        var socket = this;
        this._spdyState.framer.replyFrame(
          this._spdyState.id, code, reason, headers,
          function(e, frame) {
            socket.connection.write(frame);
            socket._unlock();
            callback.call();
          }
        );
      });
    } catch (error) {
      callback.call();
    }
  }

  if (options.proxy.followRedirects) {
    // Resolve redirects before responding.
    http = require('follow-redirects').http;
  }

  spdy.server.Server.call(this, {
    key: fs.readFileSync(options.proxy.sslKeyPath),
    cert: fs.readFileSync(options.proxy.sslCertPath)
  });

  this.on('connect', handleConnect);
  this.on('request', handleRequest);
  this.on('listening', handleListen);
};
util.inherits(SpdyProxy, spdy.server.Server);

module.exports = SpdyProxy;

module.exports.log = log;
<commit_msg>Set the outgoing connection limit to Infinity<commit_after>'use strict';

var fs = require('fs');
var url = require('url');
var util = require('util');
var net = require('net');
var http = require('http');
var spdy = require('spdy');

var plugins = require('./plugins');
var cache = require('./cache');
var log = require('./log');

var NAME = exports.name = 'proxy';
var RESPONSE_TIMEOUT = 10000;

var metrics = require('./metrics').session(NAME);

// Don't artificially limit the number of outgoing connections.
// This is the default in Node 0.12
http.globalAgent.maxSockets = Infinity;

// Shortens the given URL to given maxLen by inserting '...'.
function shortenUrl(url, maxLen) {
  var len = Math.max(5, (maxLen || 33));

  if (url.length <= len) {
    return url;
  }

  var hLen = (len - 3) / 2;
  var shortUrl = url.substr(0, hLen) + '...' + url.substr(url.length - hLen);
  return shortUrl;
}

var SpdyProxy = function(options) {

  function handleListen() {
    cache.init(options);
    log.info('%s listens on port %d', options.name, options.proxy.port);
  }

  function parseRequestOptions(request) {
    var options = { enabled: [], disabled: [] };
    var header = request.headers['x-janus-options'];
    if (!header) {
      return options;
    }

    var split = header.trim().split(' ');
    split.forEach(function(token) {
      if (token[0] === '+') {
        options.enabled.push(token.substring(1));
      } else if (token[0] === '-') {
        options.disabled.push(token.substring(1));
      }
    });

    return options;
  }

  // Handles GET and POST request.
  function handleRequest(request, response) {
    metrics.count('request');
    metrics.streamTimer(response, 'response', RESPONSE_TIMEOUT);

    // Normalize request path.
    request.path = request.headers.path || url.parse(request.url).path;

    request.originalUrl = url.format({
      protocol: request.headers.scheme || 'http',
      host: request.headers.host,
      pathname: request.path
    });

    log.logify(request, shortenUrl(request.originalUrl.toString()));
    request.info('HTTP/' + request.httpVersion + ' ' + request.method);

    var requestOpts = parseRequestOptions(request);
    request.debug('request options', requestOpts);

    response.on('close', function() {
      request.warn('unexpected close in ' + NAME);
    });

    response.on('error', function(e) {
      request.error(NAME + ' error: ' + e.message);
    });

    var pluginRequestTimer = metrics.timer('plugin.request');

    plugins.handleRequest(request, response, requestOpts, function(e, handled) {
      pluginRequestTimer.stop();

      if (handled) {
        // Request was serviced by a plugin.
        return;
      }

      var fwdOpts = {
        host: request.headers.host.split(':')[0],
        port: request.headers.host.split(':')[1] || 80,
        path: request.path,
        method: request.method,
        headers: request.headers
      };

      var forwardRequest = http.request(fwdOpts, function(forwardResponse) {
        // Pass non-2XX responses straight through.
        if (forwardResponse.statusCode < 200 ||
            forwardResponse.statusCode >= 300)
        {
          response.writeHead(forwardResponse.statusCode,
                             forwardResponse.headers);
          forwardResponse.pipe(response);
          return;
        }

        metrics.streamTimer(response, 'plugin.response');

        forwardResponse.headers.via = options.title;
        plugins.handleResponse(request, forwardResponse, response, requestOpts);
      });

      forwardRequest.on('error', function(e) {
        request.error(NAME + ' error: ' + e.message);
        response.writeHead(502, 'Proxy fetch failed');
        response.end();
      });

      forwardRequest.on('close', function() {
        request.warn('unexpected close in ' + NAME);
      });

      forwardRequest.on('error', function(e) {
        request.error(NAME + ' error: ' + e.message);
      });

      response.on('close', function() {
        forwardRequest.abort();
      });

      response.on('error', function() {
        forwardRequest.abort();
      });

      // Pipe POST data.
      request.pipe(forwardRequest);
    });
  }

  // Handles CONNECT request.
  function handleConnect(request, socket) {
    metrics.count('connect');

    var tunnelOpts = {
      host: request.url.split(':')[0],
      port: request.url.split(':')[1] || 443,
    };

    log.info('%s\tHTTPS/%s\t%s\t%s',
        new Date().toISOString(),
        request.httpVersion,
        request.method,
        tunnelOpts.host
    );

    var tunnel = net.createConnection(tunnelOpts, function() {
      synReply(socket, 200, 'Connection established',
        {
          Connection: 'keep-alive',
          'via': options.title
        },
        function() {
          tunnel.pipe(socket);
          socket.pipe(tunnel);
        }
      );
    });

    tunnel.setNoDelay(true);

    tunnel.on('error', function(e) {
      log.error('Tunnel error: %s', e);
      synReply(socket, 502, 'Tunnel Error', {}, function() {
        socket.end();
      });
    });
  }

  function synReply(socket, code, reason, headers, callback) {
    try {
      if (!socket._lock) {
        // Not a SPDY socket.
        log.error('Not a SPDY socket');
        return;
      }

      socket._lock(function() {
        var socket = this;
        this._spdyState.framer.replyFrame(
          this._spdyState.id, code, reason, headers,
          function(e, frame) {
            socket.connection.write(frame);
            socket._unlock();
            callback.call();
          }
        );
      });
    } catch (error) {
      callback.call();
    }
  }

  if (options.proxy.followRedirects) {
    // Resolve redirects before responding.
    http = require('follow-redirects').http;
  }

  spdy.server.Server.call(this, {
    key: fs.readFileSync(options.proxy.sslKeyPath),
    cert: fs.readFileSync(options.proxy.sslCertPath)
  });

  this.on('connect', handleConnect);
  this.on('request', handleRequest);
  this.on('listening', handleListen);
};
util.inherits(SpdyProxy, spdy.server.Server);

module.exports = SpdyProxy;

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


<commit_before>import React, { PropTypes } from 'react';
import LazyLoad from 'react-lazyload';
import createURL from '../utils/utils';

const Photos = (props) => {

    const handleClick = (index) => {
        props._onClick(index);
    }

    const photoContainerStyle = {
        width: props.imageWidth + 'px',
        height: props.imageWidth + 'px',
        marginRight: 5 + 'px'
    }

    const photos = props.images.map((image, index) => {
        if (image.imageURL) {
            const source = image.imageURL;
            const sourceWithSize = createURL('large', source);
            const title = image.title;
            const description = image.description;
            const orientation = image.orientation;
            return (
                <LazyLoad height={200} offset={1000} once key={index}>

{title}

                </LazyLoad>
            );
        }
    });

    return (

              {photos}

    );
}

Photos.propTypes = {
    images: PropTypes.array.isRequired,
    _onClick: PropTypes.func.isRequired,
}

export default Photos;
<commit_msg>Addes image description<commit_after>import React, { PropTypes } from 'react';
import LazyLoad from 'react-lazyload';
import createURL from '../utils/utils';

const Photos = (props) => {

    const handleClick = (index) => {
        props._onClick(index);
    }

    const photoContainerStyle = {
        width: props.imageWidth + 'px',
        height: props.imageWidth + 'px',
        marginRight: 5 + 'px'
    }

    const photos = props.images.map((image, index) => {
        if (image.imageURL) {
            const source = image.imageURL;
            const sourceWithSize = createURL('large', source);
            const title = image.title;
            const description = image.description;
            const orientation = image.orientation;
            return (
                <LazyLoad height={200} offset={1000} once key={index}>

                                {title}

                                {description}

                </LazyLoad>
            );
        }
    });

    return (

              {photos}

    );
}

Photos.propTypes = {
    images: PropTypes.array.isRequired,
    _onClick: PropTypes.func.isRequired,
}

export default Photos;
<|endoftext|>


<commit_before>/* ===========================================================
 * Bootstrap: tooltip.js v3.0.0
 * http://twitter.github.com/bootstrap/javascript.html#tooltips
 * Inspired by the original jQuery.tipsy by Jason Frame
 * ===========================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function ($) { "use strict";

  // TOOLTIP PUBLIC CLASS DEFINITION
  // ===============================

  var Tooltip = function (element, options) {
    this.type       =
    this.options    =
    this.enabled    =
    this.timeout    =
    this.hoverState =
    this.$element   = null

    this.init('tooltip', element, options)
  }

  Tooltip.DEFAULTS = {
    animation: true
  , placement: 'top'
  , selector: false
  , template: '

'
  , trigger: 'hover focus'
  , title: ''
  , delay: 0
  , html: false
  , container: false
  }

  Tooltip.prototype.init = function (type, element, options) {
    this.type     = type
    this.options  = this.getOptions(options)
    this.enabled  = true
    this.$element = $(element)

    var triggers = this.options.trigger.split(' ')

    for (var i = triggers.length; i--;) {
      var trigger = triggers[i]

      if (trigger == 'click') {
        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
      } else if (trigger != 'manual') {
        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus'
        var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'

        this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
      }
    }

    this.options.selector ?
      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
      this.fixTitle()
  }

  Tooltip.prototype.getDefaults = function () {
    return Tooltip.DEFAULTS
  }

  Tooltip.prototype.getOptions = function (options) {
    options = $.extend({}, this.getDefaults(), this.$element.data(), options)

    if (options.delay && typeof options.delay == 'number') {
      options.delay = {
        show: options.delay
      , hide: options.delay
      }
    }

    return options
  }

  Tooltip.prototype.enter = function (e) {
    var defaults = $.fn[this.type].defaults
    var options  = {}

    this._options && $.each(this._options, function (key, value) {
      if (defaults[key] != value) options[key] = value
    }, this)

    var self = $(e.currentTarget)[this.type](options).data(this.type)

    if (!self.options.delay || !self.options.delay.show) return self.show()

    clearTimeout(this.timeout)

    self.hoverState = 'in'
    this.timeout    = setTimeout(function() {
      if (self.hoverState == 'in') self.show()
    }, self.options.delay.show)
  }

  Tooltip.prototype.leave = function (e) {
    var self = $(e.currentTarget)[this.type](this._options).data(this.type)

    if (this.timeout) clearTimeout(this.timeout)
    if (!self.options.delay || !self.options.delay.hide) return self.hide()

    self.hoverState = 'out'
    this.timeout    = setTimeout(function() {
      if (self.hoverState == 'out') self.hide()
    }, self.options.delay.hide)
  }

  Tooltip.prototype.show = function () {
    var e = $.Event('bs:'+ this.type + ':show')

    if (this.hasContent() && this.enabled) {
      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      var $tip = this.tip()

      this.setContent()

      if (this.options.animation) $tip.addClass('fade')

      var placement = typeof this.options.placement == 'function' ?
        this.options.placement.call(this, $tip[0], this.$element[0]) :
        this.options.placement

      $tip
        .detach()
        .css({ top: 0, left: 0, display: 'block' })

      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)

      var tp
      var pos          = this.getPosition()
      var actualWidth  = $tip[0].offsetWidth
      var actualHeight = $tip[0].offsetHeight

      switch (placement) {
        case 'bottom':
          tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'top':
          tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'left':
          tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
          break
        case 'right':
          tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
          break
      }

      this.applyPlacement(tp, placement)
      this.$element.trigger('shown')
    }

  Tooltip.prototype.applyPlacement = function(offset, placement) {
    var $tip   = this.tip()
    var width  = $tip[0].offsetWidth
    var height = $tip[0].offsetHeight

    $tip
      .offset(offset)
      .addClass(placement)
      .addClass('in')

    var actualWidth  = $tip[0].offsetWidth
    var actualHeight = $tip[0].offsetHeight

    if (placement == 'top' && actualHeight != height) {
      var replace = true
      offset.top  = offset.top + height - actualHeight
    }

    if (placement == 'bottom' || placement == 'top') {
      var delta = 0

      if (offset.left < 0){
        delta       = offset.left * -2
        offset.left = 0

        $tip.offset(offset)

        actualWidth  = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight
      }

      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
    } else {
      this.replaceArrow(actualHeight - height, actualHeight, 'top')
    }

    if (replace) $tip.offset(offset)
  }

  Tooltip.prototype.replaceArrow = function(delta, dimension, position){
    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
  }

  Tooltip.prototype.setContent = function () {
    var $tip  = this.tip()
    var title = this.getTitle()

    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
    $tip.removeClass('fade in top bottom left right')
  }

  Tooltip.prototype.hide = function () {
    var that = this
    var $tip = this.tip()
    var e    = $.Event('hide')

    this.$element.trigger(e)

    if (e.isDefaultPrevented()) return

    $tip.removeClass('in')

    function removeWithAnimation() {
      var timeout = setTimeout(function () {
        $tip.off($.support.transition.end).detach()
      }, 500)

      $tip.one($.support.transition.end, function () {
        clearTimeout(timeout)
        $tip.detach()
      })
    }

    $.support.transition && this.$tip.hasClass('fade') ?
      removeWithAnimation() :
      $tip.detach()

    this.$element.trigger('hidden')

    return this
  }

  Tooltip.prototype.fixTitle = function () {
    var $e = this.$element
    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
    }
  }

  Tooltip.prototype.hasContent = function () {
    return this.getTitle()
  }

  Tooltip.prototype.getPosition = function () {
    var el = this.$element[0]
    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
      width: el.offsetWidth
    , height: el.offsetHeight
    }, this.$element.offset())
  }

  Tooltip.prototype.getTitle = function () {
    var title
    var $e = this.$element
    var o  = this.options

    title = $e.attr('data-original-title')
      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)

    return title
  }

  Tooltip.prototype.tip = function () {
    return this.$tip = this.$tip || $(this.options.template)
  }

  Tooltip.prototype.arrow =function(){
    return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
  }

  Tooltip.prototype.validate = function () {
    if (!this.$element[0].parentNode) {
      this.hide()
      this.$element = null
      this.options  = null
    }
  }

  Tooltip.prototype.enable = function () {
    this.enabled = true
  }

  Tooltip.prototype.disable = function () {
    this.enabled = false
  }

  Tooltip.prototype.toggleEnabled = function () {
    this.enabled = !this.enabled
  }

  Tooltip.prototype.toggle = function (e) {
    var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
    self.tip().hasClass('in') ? self.hide() : self.show()
  }

  Tooltip.prototype.destroy = function () {
    this.hide().$element.off('.' + this.type).removeData(this.type)
  }

  // TOOLTIP PLUGIN DEFINITION
  // =========================

  var old = $.fn.tooltip

  $.fn.tooltip = function ( option ) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('tooltip')
      var options = typeof option == 'object' && option

      if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.tooltip.Constructor = Tooltip

  // TOOLTIP NO CONFLICT
  // ===================

  $.fn.tooltip.noConflict = function () {
    $.fn.tooltip = old
    return this
  }

}(window.jQuery);
<commit_msg>add bs- prefix to tooltip<commit_after>/* ===========================================================
 * Bootstrap: tooltip.js v3.0.0
 * http://twitter.github.com/bootstrap/javascript.html#tooltips
 * Inspired by the original jQuery.tipsy by Jason Frame
 * ===========================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function ($) { "use strict";

  // TOOLTIP PUBLIC CLASS DEFINITION
  // ===============================

  var Tooltip = function (element, options) {
    this.type       =
    this.options    =
    this.enabled    =
    this.timeout    =
    this.hoverState =
    this.$element   = null

    this.init('tooltip', element, options)
  }

  Tooltip.DEFAULTS = {
    animation: true
  , placement: 'top'
  , selector: false
  , template: '

'
  , trigger: 'hover focus'
  , title: ''
  , delay: 0
  , html: false
  , container: false
  }

  Tooltip.prototype.init = function (type, element, options) {
    this.type     = type
    this.options  = this.getOptions(options)
    this.enabled  = true
    this.$element = $(element)

    var triggers = this.options.trigger.split(' ')

    for (var i = triggers.length; i--;) {
      var trigger = triggers[i]

      if (trigger == 'click') {
        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
      } else if (trigger != 'manual') {
        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus'
        var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'

        this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
      }
    }

    this.options.selector ?
      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
      this.fixTitle()
  }

  Tooltip.prototype.getDefaults = function () {
    return Tooltip.DEFAULTS
  }

  Tooltip.prototype.getOptions = function (options) {
    options = $.extend({}, this.getDefaults(), this.$element.data(), options)

    if (options.delay && typeof options.delay == 'number') {
      options.delay = {
        show: options.delay
      , hide: options.delay
      }
    }

    return options
  }

  Tooltip.prototype.enter = function (e) {
    var defaults = $.fn[this.type].defaults
    var options  = {}

    this._options && $.each(this._options, function (key, value) {
      if (defaults[key] != value) options[key] = value
    }, this)

    var self = $(e.currentTarget)[this.type](options).data(this.type)

    if (!self.options.delay || !self.options.delay.show) return self.show()

    clearTimeout(this.timeout)

    self.hoverState = 'in'
    this.timeout    = setTimeout(function() {
      if (self.hoverState == 'in') self.show()
    }, self.options.delay.show)
  }

  Tooltip.prototype.leave = function (e) {
    var self = $(e.currentTarget)[this.type](this._options).data(this.type)

    if (this.timeout) clearTimeout(this.timeout)
    if (!self.options.delay || !self.options.delay.hide) return self.hide()

    self.hoverState = 'out'
    this.timeout    = setTimeout(function() {
      if (self.hoverState == 'out') self.hide()
    }, self.options.delay.hide)
  }

  Tooltip.prototype.show = function () {
    var e = $.Event('bs:'+ this.type + ':show')

    if (this.hasContent() && this.enabled) {
      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      var $tip = this.tip()

      this.setContent()

      if (this.options.animation) $tip.addClass('fade')

      var placement = typeof this.options.placement == 'function' ?
        this.options.placement.call(this, $tip[0], this.$element[0]) :
        this.options.placement

      $tip
        .detach()
        .css({ top: 0, left: 0, display: 'block' })

      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)

      var tp
      var pos          = this.getPosition()
      var actualWidth  = $tip[0].offsetWidth
      var actualHeight = $tip[0].offsetHeight

      switch (placement) {
        case 'bottom':
          tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'top':
          tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
          break
        case 'left':
          tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
          break
        case 'right':
          tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
          break
      }

      this.applyPlacement(tp, placement)
      this.$element.trigger('shown')
    }

  Tooltip.prototype.applyPlacement = function(offset, placement) {
    var $tip   = this.tip()
    var width  = $tip[0].offsetWidth
    var height = $tip[0].offsetHeight

    $tip
      .offset(offset)
      .addClass(placement)
      .addClass('in')

    var actualWidth  = $tip[0].offsetWidth
    var actualHeight = $tip[0].offsetHeight

    if (placement == 'top' && actualHeight != height) {
      var replace = true
      offset.top  = offset.top + height - actualHeight
    }

    if (placement == 'bottom' || placement == 'top') {
      var delta = 0

      if (offset.left < 0){
        delta       = offset.left * -2
        offset.left = 0

        $tip.offset(offset)

        actualWidth  = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight
      }

      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
    } else {
      this.replaceArrow(actualHeight - height, actualHeight, 'top')
    }

    if (replace) $tip.offset(offset)
  }

  Tooltip.prototype.replaceArrow = function(delta, dimension, position){
    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
  }

  Tooltip.prototype.setContent = function () {
    var $tip  = this.tip()
    var title = this.getTitle()

    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
    $tip.removeClass('fade in top bottom left right')
  }

  Tooltip.prototype.hide = function () {
    var that = this
    var $tip = this.tip()
    var e    = $.Event('bs:' + this.type + ':hide')

    this.$element.trigger(e)

    if (e.isDefaultPrevented()) return

    $tip.removeClass('in')

    function removeWithAnimation() {
      var timeout = setTimeout(function () {
        $tip.off($.support.transition.end).detach()
      }, 500)

      $tip.one($.support.transition.end, function () {
        clearTimeout(timeout)
        $tip.detach()
      })
    }

    $.support.transition && this.$tip.hasClass('fade') ?
      removeWithAnimation() :
      $tip.detach()

    this.$element.trigger('hidden')

    return this
  }

  Tooltip.prototype.fixTitle = function () {
    var $e = this.$element
    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
    }
  }

  Tooltip.prototype.hasContent = function () {
    return this.getTitle()
  }

  Tooltip.prototype.getPosition = function () {
    var el = this.$element[0]
    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
      width: el.offsetWidth
    , height: el.offsetHeight
    }, this.$element.offset())
  }

  Tooltip.prototype.getTitle = function () {
    var title
    var $e = this.$element
    var o  = this.options

    title = $e.attr('data-original-title')
      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)

    return title
  }

  Tooltip.prototype.tip = function () {
    return this.$tip = this.$tip || $(this.options.template)
  }

  Tooltip.prototype.arrow =function(){
    return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
  }

  Tooltip.prototype.validate = function () {
    if (!this.$element[0].parentNode) {
      this.hide()
      this.$element = null
      this.options  = null
    }
  }

  Tooltip.prototype.enable = function () {
    this.enabled = true
  }

  Tooltip.prototype.disable = function () {
    this.enabled = false
  }

  Tooltip.prototype.toggleEnabled = function () {
    this.enabled = !this.enabled
  }

  Tooltip.prototype.toggle = function (e) {
    var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
    self.tip().hasClass('in') ? self.hide() : self.show()
  }

  Tooltip.prototype.destroy = function () {
    this.hide().$element.off('.' + this.type).removeData(this.type)
  }

  // TOOLTIP PLUGIN DEFINITION
  // =========================

  var old = $.fn.tooltip

  $.fn.tooltip = function ( option ) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs-tooltip')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs-tooltip', (data = new Tooltip(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.tooltip.Constructor = Tooltip

  // TOOLTIP NO CONFLICT
  // ===================

  $.fn.tooltip.noConflict = function () {
    $.fn.tooltip = old
    return this
  }

}(window.jQuery);
<|endoftext|>


<commit_before>import Handlebars from 'handlebars'
import fs from 'fs'
import logger from 'winston'
import moment from 'moment'
import * as authorisation from './api/authorisation'
import { config, appRoot } from './config'
import * as contact from './contact'
import * as metrics from './metrics'
import { UserModel } from './model/users'
import * as utils from './utils'

config.reports = config.get('reports')

let utcOffset = 0

if (config.reports.utcOffset){
    utcOffset = config.reports.utcOffset
}

// Function Sends the reports
function sendReports (job, flag, done) {
  let fetchUsers
  let from
  let to

  const reportMap = {}
  const channelReportMap = {}
  const channelMap = {}

  if (flag === 'dailyReport') {
    from = moment().subtract(1, 'days').startOf('day').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
    to = moment().subtract(1, 'days').endOf('day').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
  } else {
    from = moment().startOf('isoWeek').subtract(1, 'weeks').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
    to = moment().endOf('isoWeek').subtract(1, 'weeks').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
  }

  // Select the right subscribers for the report
  if (flag === 'dailyReport') {
    fetchUsers = fetchDailySubscribers
  }
  if (flag === 'weeklyReport') {
    fetchUsers = fetchWeeklySubscribers
  }

  return fetchUsers((err, users) => {
    if (err) { return done(err) }
    const usersArray = []
    const promises = Array.from(users).map((user, userIndex) => {
      return authorisation.getUserViewableChannels(user)
        .then((channels) => {
          usersArray[userIndex] = user
          usersArray[userIndex].allowedChannels = channels
          for (const channel of Array.from(usersArray[userIndex].allowedChannels)) {
            channelMap[channel._id] = {
              user,
              channel
            }
          }
        })
    })

    // Loop through the enriched user array
    return Promise.all(promises).then(() => {
      // Pre-Fetch report data into Channel Map
      const innerPromises = Object.entries(channelMap).map(([key, obj]) => {
        return new Promise((resolve, reject) => {
          fetchChannelReport(obj.channel, obj.user, flag, from, to, (err, item) => {
            if (err) { return reject(err) }
            channelReportMap[key] = item
            return resolve()
          })
        })
      })

      return Promise.all(innerPromises).then(() => {
        for (const user of Array.from(usersArray)) {
          const userKey = user.email
          for (const channel of Array.from(user.allowedChannels)) {
            if (reportMap[userKey]) {
              // Do nothing since object already exists
            } else {
              // Create the object
              reportMap[userKey] = {
                email: user.email,
                data: []
              }
            }

            // If report has been fetched get it from the map
            if (channelReportMap[channel._id]) {
              const data = channelReportMap[channel._id]
              // add report - always add if the channel is enabled (treating undefined status as enabled), otherwise only if there is data
              if ((data.channel.status == null) || (data.channel.status === 'enabled') || (data.data.length !== 0)) {
                reportMap[userKey].data.push(data)
              }
            } else {
              return logger.error('should never be here since channels have been pre-fetched')
            }
          }
        }

        // Iterate over reports and send the emails
        for (const key in reportMap) {
          const report = reportMap[key]
          if (flag === 'dailyReport') {
            report.type = 'Daily'
            report.isDaily = true
          } else {
            report.type = 'Weekly'
            report.isDaily = false
          }

          report.instance = config.alerts.himInstance
          report.consoleURL = config.alerts.consoleURL

          report.from = from
          report.to = to

          try {
            for (let i = 0; i < report.data.length; i++) {
              const data = report.data[i]
              const colorGrey = 'color: grey;'
              let rowColor = 'background-color: #d9ead3'
              if (i % 2) {
                rowColor = 'background-color: #b6d7a8;'
              }

              const totals = calculateTotalsFromGrouping(data)
              for (const key in totals) {
                report.data[i][key] = totals[key]
              }

              report.data[i].totalStyle = (report.data[i].total > 0 ? '' : colorGrey)
              report.data[i].avgRespStyle = (report.data[i].avgResp > 0 ? '' : colorGrey)
              report.data[i].failedStyle = (report.data[i].failed > 0 ? 'color: red;' : colorGrey)
              report.data[i].successfulStyle = (report.data[i].successful > 0 ? '' : colorGrey)
              report.data[i].processingStyle = (report.data[i].processing > 0 ? '' : colorGrey)
              report.data[i].completedStyle = (report.data[i].completed > 0 ? 'color: orange;' : colorGrey)
              report.data[i].completedWErrorsStyle = (report.data[i].completedWErrors > 0 ? 'color: orangered;' : colorGrey)
              report.data[i].rowColor = rowColor
            }

            sendUserEmail(report)
          } catch (err) {
            logger.error(err)
            job.fail(`Failed to send report reason: ${err}`)
          }
        }

        return done()
      }).catch(done)
    })
  })
}

function calculateTotalsFromGrouping (data) {
  const reduced = data.data.reduce((totals, metric, index) => ({
    requests: totals.requests + metric.requests,
    responseTime: totals.responseTime + metric.responseTime,
    successful: totals.successful + metric.successful,
    failed: totals.failed + metric.failed,
    processing: totals.processing + metric.processing,
    completed: totals.completed + metric.completed,
    completedWithErrors: totals.completedWithErrors + metric.completedWithErrors
  }), {
    requests: 0,
    responseTime: 0,
    successful: 0,
    failed: 0,
    processing: 0,
    completed: 0,
    completedWithErrors: 0
  })

  return {
    total: reduced.requests,
    avgResp: Math.round(calculateAverage(reduced.responseTime, reduced.requests) / 1000),
    failed: reduced.failed,
    successful: reduced.successful,
    processing: reduced.processing,
    completed: reduced.completed,
    completedWErrors: reduced.completedWithErrors
  }
}

function calculateAverage (total, count) {
  if (count === 0) {
    return 0
  }
  return total / count
}

function sendUserEmail (report) {
  report.date = new Date().toString()
  return renderTemplate('report/html.handlebars', report, reportHtml => contact.contactUser('email', report.email, `${report.type} report for: ${report.instance}`, plainTemplate(report), reportHtml, (err) => afterEmail(err, report.type, report.email)))
}

function fetchChannelReport (channel, user, flag, from, to, callback) {
  let period
  if (flag === 'dailyReport') {
    period = 'day'
  } else {
    period = 'week'
  }

  const item = {}

  logger.info(`fetching ${flag} for #${channel.name} ${user.email} ${channel._id}`)

  const filters = {
    startDate: from,
    endDate: to,
    timeSeries: period,
    channels: [channel._id]
  }

  return metrics.calculateMetrics(filters)
    .then((data) => {
      item.channel = channel
      item.data = data
      return callback(null, item)
    }).catch((err) => {
      logger.error('Error calculating metrics: ', err)
      return callback(err)
    })
}

const fetchDailySubscribers = callback => { UserModel.find({ dailyReport: true }, callback) }

const fetchWeeklySubscribers = callback => { UserModel.find({ weeklyReport: true }, callback) }

function plainTemplate (report) {
  let text = `Generated on: ${moment().utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")}`
  for (const data of Array.from(report.data)) {
    text += ` \r\n \r\n <---------- Start Channel  ${data.channel.name} ---------------------------> \r\n \r\n \
Channel Name: ${data.channel.name} \r\n \
Channel total: ${((data.data[0] != null ? data.data[0].total : undefined) != null) ? data.data[0].total : 0} transactions  \r\n \
Ave response time: ${((data.data[0] != null ? data.data[0].avgResp : undefined) != null) ? data.data[0].avgResp : 0} \r\n \
Failed:  ${((data.data[0] != null ? data.data[0].failed : undefined) != null) ? data.data[0].failed : 0}  \r\n \
Successful:  ${((data.data[0] != null ? data.data[0].successful : undefined) != null) ? data.data[0].successful : 0}  \r\n \
Processing: ${((data.data[0] != null ? data.data[0].processing : undefined) != null) ? data.data[0].processing : 0}  \r\n \
Completed:  ${((data.data[0] != null ? data.data[0].completed : undefined) != null) ? data.data[0].completed : 0}  \r\n \
Completed with errors: ${((data.data[0] != null ? data.data[0].completedWErrors : undefined) != null) ? data.data[0].completedWErrors : 0} \r\n \r\n \
<---------- End Channel -------------------------------------------------> \r\n \r\n \
\r\n \
\r\n\
`
  }
  return text
}

function renderTemplate (templateName, templateData, callback) {
  const templateDir = `${appRoot}/templates/${templateName}`

  fs.readFile(templateDir, (err, data) => {
    if (!err) {
      // make the buffer into a string
      const source = data.toString()

      // call the render function
      renderToString(source, templateData, callback)
    } else {
      // handle file read error
      logger.error('Error reading report template')
    }
  })
}

const renderToString = (source, data, callback) => {
  const template = Handlebars.compile(source)
  const htmlResult = template(data)

  return callback(htmlResult.toString())
}

const afterEmail = (err, type, email) => {
  if (err) {
    return logger.error(err)
  }
  logger.info(`${type} report email sent to ${email}`)
}

export function setupAgenda (agenda) {
  agenda.define('send weekly channel metrics', (job, done) => sendReports(job, 'weeklyReport', done))

  agenda.define('send daily channel metrics', (job, done) => sendReports(job, 'dailyReport', done))

  agenda.every('1 minutes', 'send weekly channel metrics', null, { timezone: utils.serverTimezone() })
  return agenda.every('5 minutes', 'send daily channel metrics', null, { timezone: utils.serverTimezone() })
}

if (process.env.NODE_ENV === 'test') {
  exports.sendReports = sendReports
  exports.fetchDailySubscribers = fetchDailySubscribers
  exports.fetchWeeklySubscribers = fetchWeeklySubscribers
  exports.fetchChannelReport = fetchChannelReport
  exports.sendUserEmail = sendUserEmail
  exports.calculateTotalsFromGrouping = calculateTotalsFromGrouping
}
<commit_msg>change the intervals between the daily and weekly channel reports to daily and weekly<commit_after>import Handlebars from 'handlebars'
import fs from 'fs'
import logger from 'winston'
import moment from 'moment'
import * as authorisation from './api/authorisation'
import { config, appRoot } from './config'
import * as contact from './contact'
import * as metrics from './metrics'
import { UserModel } from './model/users'
import * as utils from './utils'

config.reports = config.get('reports')

let utcOffset = 0

if (config.reports.utcOffset){
    utcOffset = config.reports.utcOffset
}

// Function Sends the reports
function sendReports (job, flag, done) {
  let fetchUsers
  let from
  let to

  const reportMap = {}
  const channelReportMap = {}
  const channelMap = {}

  if (flag === 'dailyReport') {
    from = moment().subtract(1, 'days').startOf('day').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
    to = moment().subtract(1, 'days').endOf('day').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
  } else {
    from = moment().startOf('isoWeek').subtract(1, 'weeks').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
    to = moment().endOf('isoWeek').subtract(1, 'weeks').utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")
  }

  // Select the right subscribers for the report
  if (flag === 'dailyReport') {
    fetchUsers = fetchDailySubscribers
  }
  if (flag === 'weeklyReport') {
    fetchUsers = fetchWeeklySubscribers
  }

  return fetchUsers((err, users) => {
    if (err) { return done(err) }
    const usersArray = []
    const promises = Array.from(users).map((user, userIndex) => {
      return authorisation.getUserViewableChannels(user)
        .then((channels) => {
          usersArray[userIndex] = user
          usersArray[userIndex].allowedChannels = channels
          for (const channel of Array.from(usersArray[userIndex].allowedChannels)) {
            channelMap[channel._id] = {
              user,
              channel
            }
          }
        })
    })

    // Loop through the enriched user array
    return Promise.all(promises).then(() => {
      // Pre-Fetch report data into Channel Map
      const innerPromises = Object.entries(channelMap).map(([key, obj]) => {
        return new Promise((resolve, reject) => {
          fetchChannelReport(obj.channel, obj.user, flag, from, to, (err, item) => {
            if (err) { return reject(err) }
            channelReportMap[key] = item
            return resolve()
          })
        })
      })

      return Promise.all(innerPromises).then(() => {
        for (const user of Array.from(usersArray)) {
          const userKey = user.email
          for (const channel of Array.from(user.allowedChannels)) {
            if (reportMap[userKey]) {
              // Do nothing since object already exists
            } else {
              // Create the object
              reportMap[userKey] = {
                email: user.email,
                data: []
              }
            }

            // If report has been fetched get it from the map
            if (channelReportMap[channel._id]) {
              const data = channelReportMap[channel._id]
              // add report - always add if the channel is enabled (treating undefined status as enabled), otherwise only if there is data
              if ((data.channel.status == null) || (data.channel.status === 'enabled') || (data.data.length !== 0)) {
                reportMap[userKey].data.push(data)
              }
            } else {
              return logger.error('should never be here since channels have been pre-fetched')
            }
          }
        }

        // Iterate over reports and send the emails
        for (const key in reportMap) {
          const report = reportMap[key]
          if (flag === 'dailyReport') {
            report.type = 'Daily'
            report.isDaily = true
          } else {
            report.type = 'Weekly'
            report.isDaily = false
          }

          report.instance = config.alerts.himInstance
          report.consoleURL = config.alerts.consoleURL

          report.from = from
          report.to = to

          try {
            for (let i = 0; i < report.data.length; i++) {
              const data = report.data[i]
              const colorGrey = 'color: grey;'
              let rowColor = 'background-color: #d9ead3'
              if (i % 2) {
                rowColor = 'background-color: #b6d7a8;'
              }

              const totals = calculateTotalsFromGrouping(data)
              for (const key in totals) {
                report.data[i][key] = totals[key]
              }

              report.data[i].totalStyle = (report.data[i].total > 0 ? '' : colorGrey)
              report.data[i].avgRespStyle = (report.data[i].avgResp > 0 ? '' : colorGrey)
              report.data[i].failedStyle = (report.data[i].failed > 0 ? 'color: red;' : colorGrey)
              report.data[i].successfulStyle = (report.data[i].successful > 0 ? '' : colorGrey)
              report.data[i].processingStyle = (report.data[i].processing > 0 ? '' : colorGrey)
              report.data[i].completedStyle = (report.data[i].completed > 0 ? 'color: orange;' : colorGrey)
              report.data[i].completedWErrorsStyle = (report.data[i].completedWErrors > 0 ? 'color: orangered;' : colorGrey)
              report.data[i].rowColor = rowColor
            }

            sendUserEmail(report)
          } catch (err) {
            logger.error(err)
            job.fail(`Failed to send report reason: ${err}`)
          }
        }

        return done()
      }).catch(done)
    })
  })
}

function calculateTotalsFromGrouping (data) {
  const reduced = data.data.reduce((totals, metric, index) => ({
    requests: totals.requests + metric.requests,
    responseTime: totals.responseTime + metric.responseTime,
    successful: totals.successful + metric.successful,
    failed: totals.failed + metric.failed,
    processing: totals.processing + metric.processing,
    completed: totals.completed + metric.completed,
    completedWithErrors: totals.completedWithErrors + metric.completedWithErrors
  }), {
    requests: 0,
    responseTime: 0,
    successful: 0,
    failed: 0,
    processing: 0,
    completed: 0,
    completedWithErrors: 0
  })

  return {
    total: reduced.requests,
    avgResp: Math.round(calculateAverage(reduced.responseTime, reduced.requests) / 1000),
    failed: reduced.failed,
    successful: reduced.successful,
    processing: reduced.processing,
    completed: reduced.completed,
    completedWErrors: reduced.completedWithErrors
  }
}

function calculateAverage (total, count) {
  if (count === 0) {
    return 0
  }
  return total / count
}

function sendUserEmail (report) {
  report.date = new Date().toString()
  return renderTemplate('report/html.handlebars', report, reportHtml => contact.contactUser('email', report.email, `${report.type} report for: ${report.instance}`, plainTemplate(report), reportHtml, (err) => afterEmail(err, report.type, report.email)))
}

function fetchChannelReport (channel, user, flag, from, to, callback) {
  let period
  if (flag === 'dailyReport') {
    period = 'day'
  } else {
    period = 'week'
  }

  const item = {}

  logger.info(`fetching ${flag} for #${channel.name} ${user.email} ${channel._id}`)

  const filters = {
    startDate: from,
    endDate: to,
    timeSeries: period,
    channels: [channel._id]
  }

  return metrics.calculateMetrics(filters)
    .then((data) => {
      item.channel = channel
      item.data = data
      return callback(null, item)
    }).catch((err) => {
      logger.error('Error calculating metrics: ', err)
      return callback(err)
    })
}

const fetchDailySubscribers = callback => { UserModel.find({ dailyReport: true }, callback) }

const fetchWeeklySubscribers = callback => { UserModel.find({ weeklyReport: true }, callback) }

function plainTemplate (report) {
  let text = `Generated on: ${moment().utcOffset(utcOffset).format("Do MMM YYYY HH:mm:ss Z")}`
  for (const data of Array.from(report.data)) {
    text += ` \r\n \r\n <---------- Start Channel  ${data.channel.name} ---------------------------> \r\n \r\n \
Channel Name: ${data.channel.name} \r\n \
Channel total: ${((data.data[0] != null ? data.data[0].total : undefined) != null) ? data.data[0].total : 0} transactions  \r\n \
Ave response time: ${((data.data[0] != null ? data.data[0].avgResp : undefined) != null) ? data.data[0].avgResp : 0} \r\n \
Failed:  ${((data.data[0] != null ? data.data[0].failed : undefined) != null) ? data.data[0].failed : 0}  \r\n \
Successful:  ${((data.data[0] != null ? data.data[0].successful : undefined) != null) ? data.data[0].successful : 0}  \r\n \
Processing: ${((data.data[0] != null ? data.data[0].processing : undefined) != null) ? data.data[0].processing : 0}  \r\n \
Completed:  ${((data.data[0] != null ? data.data[0].completed : undefined) != null) ? data.data[0].completed : 0}  \r\n \
Completed with errors: ${((data.data[0] != null ? data.data[0].completedWErrors : undefined) != null) ? data.data[0].completedWErrors : 0} \r\n \r\n \
<---------- End Channel -------------------------------------------------> \r\n \r\n \
\r\n \
\r\n\
`
  }
  return text
}

function renderTemplate (templateName, templateData, callback) {
  const templateDir = `${appRoot}/templates/${templateName}`

  fs.readFile(templateDir, (err, data) => {
    if (!err) {
      // make the buffer into a string
      const source = data.toString()

      // call the render function
      renderToString(source, templateData, callback)
    } else {
      // handle file read error
      logger.error('Error reading report template')
    }
  })
}

const renderToString = (source, data, callback) => {
  const template = Handlebars.compile(source)
  const htmlResult = template(data)

  return callback(htmlResult.toString())
}

const afterEmail = (err, type, email) => {
  if (err) {
    return logger.error(err)
  }
  logger.info(`${type} report email sent to ${email}`)
}

export function setupAgenda (agenda) {
  agenda.define('send weekly channel metrics', (job, done) => sendReports(job, 'weeklyReport', done))

  agenda.define('send daily channel metrics', (job, done) => sendReports(job, 'dailyReport', done))

  agenda.every(config.reports.weeklyReportAt, 'send weekly channel metrics', null, { timezone: utils.serverTimezone() })
  return agenda.every(config.reports.dailyReportAt, 'send daily channel metrics', null, { timezone: utils.serverTimezone() })
}

if (process.env.NODE_ENV === 'test') {
  exports.sendReports = sendReports
  exports.fetchDailySubscribers = fetchDailySubscribers
  exports.fetchWeeklySubscribers = fetchWeeklySubscribers
  exports.fetchChannelReport = fetchChannelReport
  exports.sendUserEmail = sendUserEmail
  exports.calculateTotalsFromGrouping = calculateTotalsFromGrouping
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

var fs = require('fs'),
    path = require('path'),
    utils = require(path.join(__dirname, '../../management/utils')),
    usage = 'mojito jslint [app | mojit] [<name>] {options}\n' +
            '\nOPTIONS: \n' +
            '\t  --print      :  print results to stdout \n' +
            '\t   -p          :  short for --print\n',
    options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

// ---------- Generic file system utilities ----------

/*
 * A version of fs.mkdirSync that ensures that all intermediate directories
 * are created.
 */
function mkdirsSync(dir, mode) {
    var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

    if (path.existsSync(dir)) {
        return;
    }
    mode = mode || modeDefault;
    mkdirsSync(path.dirname(dir), mode);
    fs.mkdirSync(dir, mode);
}

/*
 * Deletes a directory including any contained files and directories. This is
 * roughly equivalent to the Unix "rm -rf" command.
 */
function rmSync(file) {
    var failures = [],
        files,
        stat;

    try {
        stat = fs.lstatSync(file);
    } catch (e1) {
        if (/^ENOENT/.test(e1.message)) {
            return null;
        }
        return [file];
    }

    if (stat.isFile() || stat.isSymbolicLink()) {
        try {
            fs.unlinkSync(file);
        } catch (e2) {
            failures.push(file);
        }
    } else if (stat.isDirectory()) {
        files = fs.readdirSync(file);
        files.forEach(function(f) {
            try {
                failures.concat(rmSync(path.join(file, f)));
            } catch (e3) {
                failures.push(path.join(file, f));
            }
        });
    }

    return failures;
}

/*
 * Recursively processes all of the files in a directory tree. Each file is
 * passed to a 'processor' function that can do whatever it needs to do. An
 * optional 'filter' function allows files to be skipped (i.e. not passed to
 * the processor) by returning false.
 */
function processDir(dir, processor, filter) {
    var files = fs.readdirSync(dir),
        file,
        stat;

    filter = filter || function() { return true; };

    files.forEach(function(f) {
        file = path.join(dir, f);
        stat = fs.statSync(file);
        if (filter(file, stat)) {
            if (stat.isDirectory()) {
                processDir(file, processor, filter);
            } else {
                processor(file);
            }
        }
    });
}

/*
 * A very simple class to allow writes to a file that is created only if it
 * needs to be written to, without the caller needing to check on every write.
 */
function OutputFile(filename) {
    var outstream = null;

    this.write = function(s) {
        if (!outstream) {
            mkdirsSync(path.dirname(filename));
            outstream = fs.createWriteStream(filename);
        }
        outstream.write(s);
    };
    this.done = function() {
        if (outstream) {
            outstream.end();
            outstream = null;
        }
    };
}

/*
 * A very simple class to allow writes to stdout.
 */
function OutputStdout(filename) {
    var printedName = false;

    this.write = function(s) {
        if (!printedName) {
            printedName = true;
            process.stdout.write(filename + '\n');
        }
        process.stdout.write(s);
    };
    this.done = function() {};
}

// ---------- JSLint processing ----------

/*
 * Run JSLint over a single file, writing any errors to the provided output,
 * and returning the number of errors encountered.
 */
function lintOneFile(infile, outfile) {
    var jslint = require('jslint'),
        OPTS = {
            'continue': true, // Tolerate continue
            node: true,
            predef: [
                // CommonJS
                'exports',
                // YUI
                'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                // Node
                'global', 'process', 'require', '__filename', 'module',
                // Browser
                'document', 'navigator', 'console', 'self', 'window'
            ]
        },
        errors = 0,
        i = 0,
        e,
        input,
        len,
        success,
        pad,
        data;

    input = fs.readFileSync(infile);
    if (!input) {
        utils.error("Failed to open file '" + infile + "'.", usage);
        return 0;
    }
    input = input.toString('utf8');

    // remove shebang (lifted from node.js)
    input = input.replace(/^\#\!.*/, '');

    success = jslint(input, OPTS);
    if (!success) {
        errors = len = jslint.errors.length;
        for (i = 0; i < len; i += 1) {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            e = jslint.errors[i];
            if (e) {
                outfile.write(pad + ' ' + e.line + ',' + e.character + ': ' +
                        e.reason + '\n');
                outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
            }
        }
    }

    data = jslint.data();
    data.functions.forEach(function(f) {
        if (f.name.charAt(0) === '_') {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            outfile.write(pad + ' ' + f.line + ': ' +
                    "Unexpected leading '_' in '" + f.name + "'.\n");
            i += 1;
            errors += 1;
        }
        if (f['var']) {
            f['var'].forEach(function(v) {
                if (v.charAt(0) === '_') {
                    pad = String(i + 1);
                    while (pad.length < 4) {
                        pad = ' ' + pad;
                    }
                    outfile.write(pad + ' ' + f.line + ': ' +
                            "Unexpected leading '_' in '" + v + "'.\n");
                    outfile.write('     ' +
                            'function ' + f.name + '\n');
                    i += 1;
                    errors += 1;
                }
            });
        }
    });

    return errors;
}

// ---------- Mojito JSLint usage ----------

/*
 * Return the location of te Mojito framework. We're currently making an
 * assumption about relative location; we really need a way to validate that.
 */
function getFrameworkDir() {
    return path.normalize(__dirname + '/../../..');
}

/*
 * Return the directory for the application. If no application name is given,
 * we look to see if we are inside an application already. If a name is given,
 * we check for an app in the current directory, and then look to see if we
 * are already there.
 */
function getAppDir(appName) {
    var cwd = process.cwd(),
        dir,
        file,
        stat,
        parts;

    // If appName was not supplied, see if we're in an app.
    if (!appName) {
        file = path.join(cwd, 'server.js');
        return (path.existsSync(file) ? cwd : null);
    }

    // Look for the app where we are.
    dir = path.join(cwd, appName);
    if (path.existsSync(dir)) {
        stat = fs.statSync(dir);
        if (stat.isDirectory()) {
            return dir;
        }
    }

    // See if we are already in the app.
    parts = cwd.split('/');
    if (parts[parts.length - 1] === appName) {
        return cwd;
    }

    // App not found.
    return null;
}

/*
 * Return the directory for the mojit. Currently assumes we are in the app.
 */
function getMojitDir(mojitPath) {
    var cwd = process.cwd();

    return path.join(cwd, mojitPath);
}

/*
 * Return the name of the mojit, extracted from the path supplied.
 */
function getMojitName(mojitPath) {
    var parts = mojitPath.split('/');

    return parts[parts.length - 1];
}

/*
 * Write an HTML page that makes it easy to navigate through the results. The
 * implementation of this is very "lazy" but also very simple in terms of file
 * system interaction.
 */
function writePage(outDir, rowdata, totalErrors) {
    var outfile = path.join(outDir, 'jslint.html'),
        html = '' +
            '<title>JSLint Report</title>' +
            '
JSLint Report
' +
            '
Total of ' + totalErrors + ' errors in ' +
            rowdata.length + ' files.
' +
            '
\n';

    rowdata.forEach(function(row) {
        html += '
' +
            row.count +
            "	" +
            row.text +
            '
\n';
    });

    html += '
';

    fs.writeFileSync(outfile, html);
}

/*
 * Process all of the files in the input directory, writing any JSLint errors
 * encountered to the output directory. Return the total count of errors.
 */
function processFiles(inDir, outDir, excludeMatcher) {
    var totalErrors = 0,
        rowdata = [];

    inDir = path.normalize(inDir);
    if (outDir) {
        outDir = path.normalize(outDir);
    }

    processDir(inDir,
        function(f) {
            var relname = f.replace(inDir, '').replace(/^\//, ''),
                outname = relname.replace(/\.js$/, '.txt'),
                out,
                errors;

            if (!outDir) {
                out = new OutputStdout(relname);
            } else {
                out = new OutputFile(path.join(outDir, outname));
            }

            errors = lintOneFile(f, out);

            if (errors > 0) {
                rowdata.push({count: errors, text: relname, url: outname});
            }
            totalErrors += errors;
            out.done();
        },
        function(f, s) {
            var p;

            if (excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                return false;
            }
            p = f.split('/');
            return (p[p.length - 1]).charAt(0) !== '.' &&
                (s.isDirectory() || /\.js$/.test(p));
        });

    if (outDir && totalErrors > 0) {
        writePage(outDir, rowdata, totalErrors);
    }

    return totalErrors;
}

/*
 * Run this command. The options (framework, application, or mojit check, and
 * an optional path) are processed, and JSLint is run on each file. If there
 * are errors, a non-zero process exit is made.
 */
function run(params, options) {
    var excludes,
        inDir,
        outDir,
        failures,
        errors,
        print = options && options.print;

    // Process params to determine input and output locations.
    if (!params || params.length === 0) {
        // framework
        inDir = getFrameworkDir();
        outDir = path.join(process.cwd(), 'artifacts/framework/jslint');
        excludes = [
            /\/artifacts$/,
            /\/lib\/tests$/,
            /\/lib\/libs$/,
            /\/lib\/app\/autoload\/transport$/,
            /\/lib\/app\/autoload\/handlebars\.common\.js$/,
            /\/lib\/app\/libs$/,
            /\/lib\/management$/,
            /\/node_modules$/,
            /\/mojito$/ // for the CI environment
        ];
    } else if (params[0] === 'app') {
        // application
        inDir = getAppDir(params[1]);
        if (!inDir) {
            utils.error('Application not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint');
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else if (params[0] === 'mojit') {
        // mojit
        inDir = getMojitDir(params[1]);
        if (!inDir) {
            utils.error('Mojit not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint/mojits',
            getMojitName(params[1]));
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else {
        // TODO: [Issue 85] Maybe allow for a single file?
        utils.error('Unrecognized option: ' + params[0], usage);
        return;
    }

    // Delete any files currently in the output location.
    failures = rmSync(outDir);
    if (failures && failures.length) {
        console.log('Some files could not be deleted:\n');
        failures.forEach(function(f) {
            console.log('\t' + f);
        });
    }

    // Set outDir to null if we just plan on printing to stdout
    if (print) {
        outDir = null;
    }

    // Process the files with JSLint.
    errors = processFiles(inDir, outDir, utils.getExclusionMatcher(excludes));
    if (!print && errors) {
        console.log('Lint report: ' + path.normalize(outDir));
    }
    process.stdout.write(errors + ' errors found.\n', 'utf8',
        function() {
            // TODO: The following is commented out until we figure out
            // how to flush all of our streams before the process exits.
            // Right now, output is lost if we exit like this.
            // if (errors) {
            //     process.exit(2);
            // }
        });
}

/**
 * Standard usage string export.
 */
exports.usage = usage;

/**
 * Standard options export.
 */
exports.options = options;

/**
 * Standard run method hook export.
 */
exports.run = run;
<commit_msg>Fixed jslint framework directories<commit_after>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

var fs = require('fs'),
    path = require('path'),
    utils = require(path.join(__dirname, '../../management/utils')),
    usage = 'mojito jslint [app | mojit] [<name>] {options}\n' +
            '\nOPTIONS: \n' +
            '\t  --print      :  print results to stdout \n' +
            '\t   -p          :  short for --print\n',
    options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

// ---------- Generic file system utilities ----------

/*
 * A version of fs.mkdirSync that ensures that all intermediate directories
 * are created.
 */
function mkdirsSync(dir, mode) {
    var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

    if (path.existsSync(dir)) {
        return;
    }
    mode = mode || modeDefault;
    mkdirsSync(path.dirname(dir), mode);
    fs.mkdirSync(dir, mode);
}

/*
 * Deletes a directory including any contained files and directories. This is
 * roughly equivalent to the Unix "rm -rf" command.
 */
function rmSync(file) {
    var failures = [],
        files,
        stat;

    try {
        stat = fs.lstatSync(file);
    } catch (e1) {
        if (/^ENOENT/.test(e1.message)) {
            return null;
        }
        return [file];
    }

    if (stat.isFile() || stat.isSymbolicLink()) {
        try {
            fs.unlinkSync(file);
        } catch (e2) {
            failures.push(file);
        }
    } else if (stat.isDirectory()) {
        files = fs.readdirSync(file);
        files.forEach(function(f) {
            try {
                failures.concat(rmSync(path.join(file, f)));
            } catch (e3) {
                failures.push(path.join(file, f));
            }
        });
    }

    return failures;
}

/*
 * Recursively processes all of the files in a directory tree. Each file is
 * passed to a 'processor' function that can do whatever it needs to do. An
 * optional 'filter' function allows files to be skipped (i.e. not passed to
 * the processor) by returning false.
 */
function processDir(dir, processor, filter) {
    var files = fs.readdirSync(dir),
        file,
        stat;

    filter = filter || function() { return true; };

    files.forEach(function(f) {
        file = path.join(dir, f);
        stat = fs.statSync(file);
        if (filter(file, stat)) {
            if (stat.isDirectory()) {
                processDir(file, processor, filter);
            } else {
                processor(file);
            }
        }
    });
}

/*
 * A very simple class to allow writes to a file that is created only if it
 * needs to be written to, without the caller needing to check on every write.
 */
function OutputFile(filename) {
    var outstream = null;

    this.write = function(s) {
        if (!outstream) {
            mkdirsSync(path.dirname(filename));
            outstream = fs.createWriteStream(filename);
        }
        outstream.write(s);
    };
    this.done = function() {
        if (outstream) {
            outstream.end();
            outstream = null;
        }
    };
}

/*
 * A very simple class to allow writes to stdout.
 */
function OutputStdout(filename) {
    var printedName = false;

    this.write = function(s) {
        if (!printedName) {
            printedName = true;
            process.stdout.write(filename + '\n');
        }
        process.stdout.write(s);
    };
    this.done = function() {};
}

// ---------- JSLint processing ----------

/*
 * Run JSLint over a single file, writing any errors to the provided output,
 * and returning the number of errors encountered.
 */
function lintOneFile(infile, outfile) {
    var jslint = require('jslint'),
        OPTS = {
            'continue': true, // Tolerate continue
            node: true,
            predef: [
                // CommonJS
                'exports',
                // YUI
                'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                // Node
                'global', 'process', 'require', '__filename', 'module',
                // Browser
                'document', 'navigator', 'console', 'self', 'window'
            ]
        },
        errors = 0,
        i = 0,
        e,
        input,
        len,
        success,
        pad,
        data;

    input = fs.readFileSync(infile);
    if (!input) {
        utils.error("Failed to open file '" + infile + "'.", usage);
        return 0;
    }
    input = input.toString('utf8');

    // remove shebang (lifted from node.js)
    input = input.replace(/^\#\!.*/, '');

    success = jslint(input, OPTS);
    if (!success) {
        errors = len = jslint.errors.length;
        for (i = 0; i < len; i += 1) {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            e = jslint.errors[i];
            if (e) {
                outfile.write(pad + ' ' + e.line + ',' + e.character + ': ' +
                        e.reason + '\n');
                outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
            }
        }
    }

    data = jslint.data();
    data.functions.forEach(function(f) {
        if (f.name.charAt(0) === '_') {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            outfile.write(pad + ' ' + f.line + ': ' +
                    "Unexpected leading '_' in '" + f.name + "'.\n");
            i += 1;
            errors += 1;
        }
        if (f['var']) {
            f['var'].forEach(function(v) {
                if (v.charAt(0) === '_') {
                    pad = String(i + 1);
                    while (pad.length < 4) {
                        pad = ' ' + pad;
                    }
                    outfile.write(pad + ' ' + f.line + ': ' +
                            "Unexpected leading '_' in '" + v + "'.\n");
                    outfile.write('     ' +
                            'function ' + f.name + '\n');
                    i += 1;
                    errors += 1;
                }
            });
        }
    });

    return errors;
}

// ---------- Mojito JSLint usage ----------

/*
 * Return the location of te Mojito framework. We're currently making an
 * assumption about relative location; we really need a way to validate that.
 */
function getFrameworkDir() {
    return path.normalize(__dirname + '/../../../lib');
}

/*
 * Return the directory for the application. If no application name is given,
 * we look to see if we are inside an application already. If a name is given,
 * we check for an app in the current directory, and then look to see if we
 * are already there.
 */
function getAppDir(appName) {
    var cwd = process.cwd(),
        dir,
        file,
        stat,
        parts;

    // If appName was not supplied, see if we're in an app.
    if (!appName) {
        file = path.join(cwd, 'server.js');
        return (path.existsSync(file) ? cwd : null);
    }

    // Look for the app where we are.
    dir = path.join(cwd, appName);
    if (path.existsSync(dir)) {
        stat = fs.statSync(dir);
        if (stat.isDirectory()) {
            return dir;
        }
    }

    // See if we are already in the app.
    parts = cwd.split('/');
    if (parts[parts.length - 1] === appName) {
        return cwd;
    }

    // App not found.
    return null;
}

/*
 * Return the directory for the mojit. Currently assumes we are in the app.
 */
function getMojitDir(mojitPath) {
    var cwd = process.cwd();

    return path.join(cwd, mojitPath);
}

/*
 * Return the name of the mojit, extracted from the path supplied.
 */
function getMojitName(mojitPath) {
    var parts = mojitPath.split('/');

    return parts[parts.length - 1];
}

/*
 * Write an HTML page that makes it easy to navigate through the results. The
 * implementation of this is very "lazy" but also very simple in terms of file
 * system interaction.
 */
function writePage(outDir, rowdata, totalErrors) {
    var outfile = path.join(outDir, 'jslint.html'),
        html = '' +
            '<title>JSLint Report</title>' +
            '
JSLint Report
' +
            '
Total of ' + totalErrors + ' errors in ' +
            rowdata.length + ' files.
' +
            '
\n';

    rowdata.forEach(function(row) {
        html += '
' +
            row.count +
            "	" +
            row.text +
            '
\n';
    });

    html += '
';

    fs.writeFileSync(outfile, html);
}

/*
 * Process all of the files in the input directory, writing any JSLint errors
 * encountered to the output directory. Return the total count of errors.
 */
function processFiles(inDir, outDir, excludeMatcher) {
    var totalErrors = 0,
        rowdata = [];

    inDir = path.normalize(inDir);
    if (outDir) {
        outDir = path.normalize(outDir);
    }

    processDir(inDir,
        function(f) {
            var relname = f.replace(inDir, '').replace(/^\//, ''),
                outname = relname.replace(/\.js$/, '.txt'),
                out,
                errors;

            if (!outDir) {
                out = new OutputStdout(relname);
            } else {
                out = new OutputFile(path.join(outDir, outname));
            }

            errors = lintOneFile(f, out);

            if (errors > 0) {
                rowdata.push({count: errors, text: relname, url: outname});
            }
            totalErrors += errors;
            out.done();
        },
        function(f, s) {
            var p;

            if (excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                return false;
            }
            p = f.split('/');
            return (p[p.length - 1]).charAt(0) !== '.' &&
                (s.isDirectory() || /\.js$/.test(p));
        });

    if (outDir && totalErrors > 0) {
        writePage(outDir, rowdata, totalErrors);
    }

    return totalErrors;
}

/*
 * Run this command. The options (framework, application, or mojit check, and
 * an optional path) are processed, and JSLint is run on each file. If there
 * are errors, a non-zero process exit is made.
 */
function run(params, options) {
    var excludes,
        inDir,
        outDir,
        failures,
        errors,
        print = options && options.print;

    // Process params to determine input and output locations.
    if (!params || params.length === 0) {
        // framework
        inDir = getFrameworkDir();
        outDir = path.join(process.cwd(), 'artifacts/framework/jslint');
        excludes = [
            /\/artifacts$/,
            /\/tests$/,
            /\/libs$/,
            /\/app\/autoload\/transport$/,
            /\/app\/autoload\/handlebars\.common\.js$/,
            /\/app\/libs$/,
            /\/management$/,
            /\/node_modules$/,
            /\/mojito$/ // for the CI environment
        ];
    } else if (params[0] === 'app') {
        // application
        inDir = getAppDir(params[1]);
        if (!inDir) {
            utils.error('Application not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint');
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else if (params[0] === 'mojit') {
        // mojit
        inDir = getMojitDir(params[1]);
        if (!inDir) {
            utils.error('Mojit not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint/mojits',
            getMojitName(params[1]));
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else {
        // TODO: [Issue 85] Maybe allow for a single file?
        utils.error('Unrecognized option: ' + params[0], usage);
        return;
    }

    // Delete any files currently in the output location.
    failures = rmSync(outDir);
    if (failures && failures.length) {
        console.log('Some files could not be deleted:\n');
        failures.forEach(function(f) {
            console.log('\t' + f);
        });
    }

    // Set outDir to null if we just plan on printing to stdout
    if (print) {
        outDir = null;
    }

    // Process the files with JSLint.
    errors = processFiles(inDir, outDir, utils.getExclusionMatcher(excludes));
    if (!print && errors) {
        console.log('Lint report: ' + path.normalize(outDir));
    }
    process.stdout.write(errors + ' errors found.\n', 'utf8',
        function() {
            // TODO: The following is commented out until we figure out
            // how to flush all of our streams before the process exits.
            // Right now, output is lost if we exit like this.
            // if (errors) {
            //     process.exit(2);
            // }
        });
}

/**
 * Standard usage string export.
 */
exports.usage = usage;

/**
 * Standard options export.
 */
exports.options = options;

/**
 * Standard run method hook export.
 */
exports.run = run;
<|endoftext|>


<commit_before>var gnames = gNames();
console.log(gnames);
for(var i=0; i<gnames.length; i++){
	$(".result").append('
'+gnames[i].state+' - '+gnames[i].city+'
');
	$(".result").append('<gcse:searchresults-only gname="'+gnames[i].gname+'"></gcse:searchresults-only>');
}
var QueryString = queryString();
if(jQuery.isEmptyObject(QueryString) === false) {
	for(var t in QueryString) {
		if(t == "q") $("#search_string").val(QueryString[t]);
		if(t == "zipcode") {
			$("#location").val(QueryString[t]);
			if(jQuery.isEmptyObject(gnames) === true) {
				$(".result").html('
No Result found
');
			}
		}
		if(t == "max_dist") $("#max_dist").val(QueryString[t]);
		if(t == "min_price") $("#minAsk8").val(QueryString[t]);
		if(t == "max_price") $("#maxAsk8").val(QueryString[t]);
		if(t == "min_meter") $("#minYear8").val(QueryString[t]);
		if(t == "max_meter") $("#maxYear8").val(QueryString[t]);
	}

	window.__gcse = {
			callback: googleCSELoaded
	};

	// CSE Code. This is a free version, so please don't make too many requests.
	(function() {
		//var cx = '001386805071419863133:cb1vfab8b4y';
		var cx = '014940883816157527715:_1dsxowig5w';
		var gcse = document.createElement('script');
		gcse.type = 'text/javascript';
		gcse.async = true;
		gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
		var s = document.getElementsByTagName('script')[0];
		s.parentNode.insertBefore(gcse, s);
	})();
}
<commit_msg>Added distance<commit_after>var gnames = gNames();
console.log(gnames);
for(var i=0; i<gnames.length; i++){
	$(".result").append('
'+gnames[i].state+" - "+gnames[i].city+' ('+Math.floor(gnames[i].distance)+' Km)
');
	$(".result").append('<gcse:searchresults-only gname="'+gnames[i].gname+'"></gcse:searchresults-only>');
}
var QueryString = queryString();
if(jQuery.isEmptyObject(QueryString) === false) {
	for(var t in QueryString) {
		if(t == "q") $("#search_string").val(QueryString[t]);
		if(t == "zipcode") {
			$("#location").val(QueryString[t]);
			if(jQuery.isEmptyObject(gnames) === true) {
				$(".result").html('
No Result found
');
			}
		}
		if(t == "max_dist") $("#max_dist").val(QueryString[t]);
		if(t == "min_price") $("#minAsk8").val(QueryString[t]);
		if(t == "max_price") $("#maxAsk8").val(QueryString[t]);
		if(t == "min_meter") $("#minYear8").val(QueryString[t]);
		if(t == "max_meter") $("#maxYear8").val(QueryString[t]);
	}

	window.__gcse = {
			callback: googleCSELoaded
	};

	// CSE Code. This is a free version, so please don't make too many requests.
	(function() {
		//var cx = '001386805071419863133:cb1vfab8b4y';
		var cx = '014940883816157527715:_1dsxowig5w';
		var gcse = document.createElement('script');
		gcse.type = 'text/javascript';
		gcse.async = true;
		gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
		var s = document.getElementsByTagName('script')[0];
		s.parentNode.insertBefore(gcse, s);
	})();
}
<|endoftext|>