<commit_before>/*!
 * Vardyger
 * Copyright(c) 2015 Rob Ferguson
 * MIT Licensed
 */

'use strict';

/**
 * Module dependencies.
 * @private
 */

var util = require('util');  // console.log(util.inspect(anyObject));
var extend = util._extend;
var mongoose = require('mongoose');
var status = require('../../utils/http-status-codes');

var Post = mongoose.model('Post');

var LOCATION = '/posts/';
var APPLICATION_JSON = 'application/json';
var TEXT_HTML = 'text/html';

/**
 * addPost
 *
 * creates a new post in the Posts collection.

 * Location (in the response header) is used to specify the URI of the newly created resource.
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was created.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function addPost(req, res) {

  var model = new Post(req.body);

  res.type(APPLICATION_JSON);

  model.save(function(error) {
    if (! error) {

      returnId(res, status.CREATED, model._id);

      /*

      res.format({
        html: function(){
          return res.status(201).send('
addPost()
');
        },
        json: function(){
          // res.type(APPLICATION_JSON);
          returnId(res, status.CREATED, model._id);
        },
        'default': function() {
          returnError(res, status.NOT_ACCEPTABLE);
        }
      })

      */

    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

/**
 * findPosts
 *
 * returns all the posts in the Posts collection.

 *
 * When we return the array of posts, we don’t simply return the model as returned from the database.
 * That would expose internal implementation details. Instead, we pick the information we need and construct a
 * new object to return.

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {posts} posts - The posts collection.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function findPosts(req, res) {

  var filteredQuery = {};

  res.type(APPLICATION_JSON);

  Post.find(filteredQuery, function(error, posts) {
    if (! error) {

      if (null === posts || undefined === posts)
      {
        returnError(res, status.NOT_FOUND);
      }

      // Node defaults to "transfer-encoding": "chunked"
      // Sending a 'Content-length' header will disable the default encoding.
      // res.setHeader("Content-Length", Buffer.byteLength(posts));

      res.format({
        html: function(){
          res.type(TEXT_HTML);
          res.status(status.OK).send('
findPosts()
');
        },

        json: function(){
          res.type(APPLICATION_JSON);
          res.status(status.OK).json(posts.map(function(a){
            return {
              id: a._id,
              title: a.title,
              slug: a.slug,
              markdown: a.markdown,
              html: a.html,
              image: a.image,
              featured: a.featured,
              page: a.page,
              state: a.state,
              locale: a.locale,
              metaTitle: a.metaTitle,
              metaDescription: a.metaDescription
            }
          }));
        },

        'default': function() {
          returnError(res, status.NOT_ACCEPTABLE);
        }
      })
    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

/**
 * findPostById
 *
 * returns a post in the Posts collection.

 *
 * When we return the post, we don’t simply return the model as returned from the database.
 * That would expose internal implementation details. Instead, we pick the information we need and construct a
 * new object to return.

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {post} post - A post from the posts collection.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function findPostById(req, res) {

  // Note: req.swagger.params.id.value not req.params.id
  // See: https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md
  var id = req.swagger.params.id.value,
      view = 'post',
      response;

  // console.log('id: ' + id);

  Post.findById(id).exec(function(error, model) {

    if (! error) {

      if (null === model || undefined === model) { returnError(res, status.NOT_FOUND); }

      response = formatResponse(model);
      // setResponseContext(req, res, response);

      res.format({
        html: function() {
          // The view engine will return a content type of text/html and a status code of 200 by default.
          // res.type(TEXT_HTML);
          res.render(view, response);
        },
        json: function() {
          res.type(APPLICATION_JSON);
          res.status(status.OK).send(JSON.stringify(response));
        },
        'default': function() { returnError(res, status.NOT_ACCEPTABLE); }
      })

    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

var url = 'http://robferguson.org';
// var url = 'http://localhost:10010';

/**
 * formatResponse
 *
 * returns a formatted response object see: {@link http://themes.ghost.org/v0.6.4/docs/post-context}

 *
 * Except where the doco is incorrect:

 *   - 'content' helper returns (#post) this.html

 *   - 'meta_title' helper returns this.metaTitle

 *   - 'meta_description' helper returns this.metaDescription

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {model} model - The Post model.
 * @return {post} post - A Ghost formatted post object.
 */
function formatResponse(model) {
  return {
    meta_title: model.metaTitle,
    meta_description: model.metaDescription,
    navigation: [{
      label: 'Home',
      url: url,
      current: false,
      slug: ''
    }, {
      label: 'About',
      url: url + '/about',
      current: false,
      slug: 'about'
    }],
    post: {
      id: model._id,
      title: model.title,
      excerpt: model.html,
      html: model.html,
      url: url,
      image: model.image,
      featured: model.featured,
      page: model.page,
      published_at: model.publishedAt,
      updated_at: model.updatedAt,
      created_at: model.createdAt,
      author: {
        name: 'Rob Ferguson',
        location: 'Sydney, Australia'
      },
      tags: ''
    }
  }
}

// website:
// bio:
// excerpt: model.html.slice(0, 255),

function setResponseContext(req, res, data) {

  var contexts = [];

  contexts.push('post');

  res.locals.context = contexts;
}

function findPostBySlug(req, res) {

}

/**
 * updatePost
 *
 * updates a post in the Posts collection.

 *
 * Swagger sets req.swagger.params.id.value not req.params.id, see:
 * {@link https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md}

 *
 * Rule: PUT must be used to both insert and update a stored resource

 * Rule: PUT must be used to update mutable resources
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was updated.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function updatePost(req, res) {

  var id = req.swagger.params.id.value;

  // console.log('id: ' + id);

  var body = req.body;

  // console.log(util.inspect(body));

  delete body.id;

  // console.log(util.inspect(body));

  res.type(APPLICATION_JSON);

  Post.findById(id)
    .exec(function(error, model) {
      if (! error) {

        if (null === model || undefined === model)
        {
          returnError(res, status.NOT_FOUND);
        }

        // console.log(util.inspect(model));
        model = extend(model, body);
        // console.log(util.inspect(model));

        model.save(function(error) {
          if (! error) {
            returnId(res, status.OK, model._id) ;
          } else {
            returnError(res, status.INTERNAL_SERVER_ERROR);
          }
        });
      } else {
        returnError(res, status.NOT_FOUND);
      }
    });
}

/**
 * deletePost
 *
 * deletes a post in the Posts collection.

 *
 * Swagger sets req.swagger.params.id.value not req.params.id, see:
 * {@link https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md}

 *
 * Rule: DELETE must be used to remove a resource from its parent
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was deleted.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function deletePost(req, res) {

  var id = req.swagger.params.id.value;

  // console.log('id: ' + id);

  res.type(APPLICATION_JSON);

  Post.findByIdAndRemove(id, function(error) {
    if (! error) {
      returnId(res, status.OK, id);
    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

function returnId(res, statusCode, objectId) {
  res.location(LOCATION + objectId);
  return res.status(statusCode).send(JSON.stringify({ id: objectId }));
}

function returnError(res, statusCode) {

  var errorMessage = 'Something went wrong :(';

  switch(statusCode) {

    case status.BAD_REQUEST:
      errorMessage = 'Bad request';
      break;

    case status.NOT_FOUND:
      errorMessage = 'Resource not found';
      break;

    case status.NOT_ACCEPTABLE:
      errorMessage = 'Not acceptable';
      break;

    case status.INTERNAL_SERVER_ERROR:
    default:
      break;
  }

  return res.status(statusCode).send(JSON.stringify({ code: statusCode, message: errorMessage }));
}

module.exports = {
  addPost:        addPost,
  findPosts:      findPosts,
  findPostById:   findPostById,
  // findPostBySlug: findPostBySlug,
  updatePost:     updatePost,
  deletePost:     deletePost
}

// ./node_modules/.bin/jsdoc /Users/robferguson/opt/WebStorm/projects/Vardyger/core/server/api/controllers/posts.js
// ./node_modules/.bin/jsdoc /Users/robferguson/opt/WebStorm/projects/Vardyger/core/server/api/controllers/posts.js -t
//   ./node_modules/ink-docstrap/template -c ./node_modules/jsdoc/conf.json

//        http://themes.ghost.org/v0.6.4/docs/navigation
//        Ghost/core/server/controllers/frontend.js
// Note: You can override the default template by placing a file called navigation.hbs in the partials directory
//       of your theme.

/*

// validateResponse: true,

json: function(){
  res.type(APPLICATION_JSON);
  res.status(status.OK).send(JSON.stringify(
    {
      id: model._id,
      title: model.title,
      slug: model.slug,
      markdown: model.markdown,
      html: model.html,
      image: model.image,
      featured: model.featured,
      page: model.page,
      state: model.state,
      locale: model.locale,
      metaTitle: model.metaTitle,
      metaDescription: model.metaDescription
    }));
}

*/
<commit_msg>Updated the JSDoc entries for the Posts API<commit_after>/*!
 * Vardyger
 * Copyright(c) 2015 Rob Ferguson
 * MIT Licensed
 */

'use strict';

/**
 * Module dependencies.
 * @private
 */

var util = require('util');  // console.log(util.inspect(anyObject));
var extend = util._extend;
var mongoose = require('mongoose');
var status = require('../../utils/http-status-codes');

var Post = mongoose.model('Post');

var LOCATION = '/posts/';
var APPLICATION_JSON = 'application/json';
var TEXT_HTML = 'text/html';

/**
 * addPost
 *
 * creates a new post in the Posts collection.

 * Location (in the response header) is used to specify the URI of the newly created resource.
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was created.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function addPost(req, res) {

  var model = new Post(req.body);

  res.type(APPLICATION_JSON);

  model.save(function(error) {
    if (! error) {

      returnId(res, status.CREATED, model._id);

      /*

      res.format({
        html: function(){
          return res.status(201).send('
addPost()
');
        },
        json: function(){
          // res.type(APPLICATION_JSON);
          returnId(res, status.CREATED, model._id);
        },
        'default': function() {
          returnError(res, status.NOT_ACCEPTABLE);
        }
      })

      */

    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

/**
 * findPosts
 *
 * returns all the posts in the Posts collection.

 *
 * When we return the array of posts, we don’t simply return the model as returned from the database.
 * That would expose internal implementation details. Instead, we pick the information we need and construct a
 * new object to return.

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {posts} posts - The posts collection.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function findPosts(req, res) {

  var filteredQuery = {};

  res.type(APPLICATION_JSON);

  Post.find(filteredQuery, function(error, posts) {
    if (! error) {

      if (null === posts || undefined === posts)
      {
        returnError(res, status.NOT_FOUND);
      }

      // Node defaults to "transfer-encoding": "chunked"
      // Sending a 'Content-length' header will disable the default encoding.
      // res.setHeader("Content-Length", Buffer.byteLength(posts));

      res.format({
        html: function(){
          res.type(TEXT_HTML);
          res.status(status.OK).send('
findPosts()
');
        },

        json: function(){
          res.type(APPLICATION_JSON);
          res.status(status.OK).json(posts.map(function(a){
            return {
              id: a._id,
              title: a.title,
              slug: a.slug,
              markdown: a.markdown,
              html: a.html,
              image: a.image,
              featured: a.featured,
              page: a.page,
              state: a.state,
              locale: a.locale,
              metaTitle: a.metaTitle,
              metaDescription: a.metaDescription
            }
          }));
        },

        'default': function() {
          returnError(res, status.NOT_ACCEPTABLE);
        }
      })
    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

/**
 * findPostById
 *
 * returns a post in the Posts collection.

 *
 * When we return the post, we don’t simply return the model as returned from the database.
 * That would expose internal implementation details. Instead, we pick the information we need and construct a
 * new object to return.

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {post} post - A post from the posts collection.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function findPostById(req, res) {

  // Note: req.swagger.params.id.value not req.params.id
  // See: https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md
  var id = req.swagger.params.id.value,
      view = 'post',
      response;

  // console.log('id: ' + id);

  Post.findById(id).exec(function(error, model) {

    if (! error) {

      if (null === model || undefined === model) { returnError(res, status.NOT_FOUND); }

      response = formatResponse(model);
      // setResponseContext(req, res, response);

      res.format({
        html: function() {
          // The view engine will return a content type of text/html and a status code of 200 by default.
          // res.type(TEXT_HTML);
          res.render(view, response);
        },
        json: function() {
          res.type(APPLICATION_JSON);
          res.status(status.OK).send(JSON.stringify(response));
        },
        'default': function() { returnError(res, status.NOT_ACCEPTABLE); }
      })

    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

var url = 'http://robferguson.org';
// var url = 'http://localhost:10010';

/**
 * formatResponse
 *
 * returns a formatted response object see: {@link http://themes.ghost.org/v0.6.4/docs/post-context}

 *
 * Except where the doco is incorrect:

 *   - 'content' helper returns (#post) this.html

 *   - 'meta_title' helper returns this.metaTitle

 *   - 'meta_description' helper returns this.metaDescription

 *
 * Rule: GET must be used to retrieve a representation of a resource
 *
 * @public
 * @param {model} model - The Post model.
 * @return {post} post - A Ghost formatted post object.
 */
function formatResponse(model) {
  return {
    meta_title: model.metaTitle,
    meta_description: model.metaDescription,
    navigation: [{
      label: 'Home',
      url: url,
      current: false,
      slug: ''
    }, {
      label: 'About',
      url: url + '/about',
      current: false,
      slug: 'about'
    }],
    post: {
      id: model._id,
      title: model.title,
      excerpt: model.html,
      html: model.html,
      url: url,
      image: model.image,
      featured: model.featured,
      page: model.page,
      published_at: model.publishedAt,
      updated_at: model.updatedAt,
      created_at: model.createdAt,
      author: {
        name: 'Rob Ferguson',
        location: 'Sydney, Australia'
      },
      tags: ''
    }
  }
}

// website:
// bio:
// excerpt: model.html.slice(0, 255),

function setResponseContext(req, res, data) {

  var contexts = [];

  contexts.push('post');

  res.locals.context = contexts;
}

function findPostBySlug(req, res) {

}

/**
 * updatePost
 *
 * updates a post in the Posts collection.

 *
 * Swagger sets req.swagger.params.id.value not req.params.id, see:
 * {@link https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md}

 *
 * Rule: PUT must be used to both insert and update a stored resource

 * Rule: PUT must be used to update mutable resources
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was updated.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function updatePost(req, res) {

  var id = req.swagger.params.id.value;

  // console.log('id: ' + id);

  var body = req.body;

  // console.log(util.inspect(body));

  delete body.id;
  // make sure no one changes the author
  // delete body.author;

  // console.log(util.inspect(body));

  res.type(APPLICATION_JSON);

  Post.findById(id)
    .exec(function(error, model) {
      if (! error) {

        if (null === model || undefined === model)
        {
          returnError(res, status.NOT_FOUND);
        }

        // console.log(util.inspect(model));
        model = extend(model, body);
        // console.log(util.inspect(model));

        model.save(function(error) {
          if (! error) {
            returnId(res, status.OK, model._id) ;
          } else {
            returnError(res, status.INTERNAL_SERVER_ERROR);
          }
        });
      } else {
        returnError(res, status.NOT_FOUND);
      }
    });
}

/**
 * deletePost
 *
 * deletes a post in the Posts collection.

 *
 * Swagger sets req.swagger.params.id.value not req.params.id, see:
 * {@link https://github.com/apigee-127/swagger-tools/blob/master/docs/Middleware.md}

 *
 * Rule: DELETE must be used to remove a resource from its parent
 *
 * @public
 * @param {req} req - The HTTP request object.
 * @param {res} res - The HTTP response object.
 * @return {id} string - The unique id of the resource that was deleted.
 * @see {@link http://expressjs.com/api.html#req}
 * @see {@link http://expressjs.com/api.html#res}
 */
function deletePost(req, res) {

  var id = req.swagger.params.id.value;

  // console.log('id: ' + id);

  res.type(APPLICATION_JSON);

  Post.findByIdAndRemove(id, function(error) {
    if (! error) {
      returnId(res, status.OK, id);
    } else {
      returnError(res, status.INTERNAL_SERVER_ERROR);
    }
  });
}

function returnId(res, statusCode, objectId) {
  res.location(LOCATION + objectId);
  return res.status(statusCode).send(JSON.stringify({ id: objectId }));
}

function returnError(res, statusCode) {

  var errorMessage = 'Something went wrong :(';

  switch(statusCode) {

    case status.BAD_REQUEST:
      errorMessage = 'Bad request';
      break;

    case status.NOT_FOUND:
      errorMessage = 'Resource not found';
      break;

    case status.NOT_ACCEPTABLE:
      errorMessage = 'Not acceptable';
      break;

    case status.INTERNAL_SERVER_ERROR:
    default:
      break;
  }

  return res.status(statusCode).send(JSON.stringify({ code: statusCode, message: errorMessage }));
}

module.exports = {
  addPost:        addPost,
  findPosts:      findPosts,
  findPostById:   findPostById,
  // findPostBySlug: findPostBySlug,
  updatePost:     updatePost,
  deletePost:     deletePost
}

// ./node_modules/.bin/jsdoc /Users/robferguson/opt/WebStorm/projects/Vardyger/core/server/api/controllers/posts.js
// ./node_modules/.bin/jsdoc /Users/robferguson/opt/WebStorm/projects/Vardyger/core/server/api/controllers/posts.js -t
//   ./node_modules/ink-docstrap/template -c ./node_modules/jsdoc/conf.json

//        http://themes.ghost.org/v0.6.4/docs/navigation
//        Ghost/core/server/controllers/frontend.js
// Note: You can override the default template by placing a file called navigation.hbs in the partials directory
//       of your theme.

/*

// validateResponse: true,

json: function(){
  res.type(APPLICATION_JSON);
  res.status(status.OK).send(JSON.stringify(
    {
      id: model._id,
      title: model.title,
      slug: model.slug,
      markdown: model.markdown,
      html: model.html,
      image: model.image,
      featured: model.featured,
      page: model.page,
      state: model.state,
      locale: model.locale,
      metaTitle: model.metaTitle,
      metaDescription: model.metaDescription
    }));
}

*/
<|endoftext|>


<commit_before>/* Minimalistic Couch In Node */
var request = require('request')
  , fs      = require('fs')
  , error   = require('./error')
  , headers = { "content-type": "application/json"
              , "accept": "application/json"
              }
  , nano;

module.exports = exports = nano = function nano_module(cfg) {
  var public_functions = {};
  if(typeof cfg === "string") {
    cfg = require(cfg); // No CFG? Maybe it's a file path?
  }

 /*
  * Request DB
  *
  * An auxiliary function to do the request to CouchDB
  *
  * @error {request:connect_db} There was a problem connecting to CouchDB
  * @error {couch:*} Any error that CouchDB returns when creating a DB
  *
  * @param {opts} The request options; e.g. {db: "test", method: "GET"}
  * @param {callback} The function to callback
  *
  * @return Execution of the code in your callback. Hopefully you are handling
  */
  function request_db(opts,callback) {
    var url = cfg.database(opts.db)
      , req
      , status_code
      , parsed;
    if(!callback) { callback = function () { return; }; } // Void Callback
    if(opts.doc) { url += "/" + opts.doc; } // Add the document to the URL
    req =
      { uri: url
      , method: opts.method
      , headers: headers
      };
    request(req, function(e,h,b){
      status_code = h.statusCode;
      if(e) {
        callback(error.request_err(e,"connect_db",req,status_code));
        return;
      }
      parsed = JSON.parse(b);
      if (status_code === 200 || status_code === 201) { callback(null,parsed); }
      else { // Proxy the error
        callback(error.couch_err(parsed.reason,parsed.error,req,status_code));
      }
    });
  }

 /*
  * Creates a CouchDB Database
  *
  * e.g. nano.db.create(db_name, function (e,b) {
  *        if(tried.tried === tried.max_retries) {
  *          callback("Retries work");
  *          return;
  *        }
  *        else {
  *          tried.tried += 1;
  *          recursive_retries_create_db(tried,callback);
  *        }
  *      });
  *
  * @see request_db
  */
  function create_db(name, callback) {
    request_db({db: name, method: "PUT"},callback);
  }

 /*
  * Annihilates a CouchDB Database
  *
  * e.g. nano.db.delete(db_name);
  *
  * Even though this looks sync it is an async function
  * and therefor order is not guaranteed
  *
  * @see request_db
  */
  function destroy_db(name, callback) {
    request_db({db: name, method: "DELETE"},callback);
  }

 /*
  * Gets information about a CouchDB Database
  *
  * e.g. nano.db.get(db_name, function(e,b) {
  *        console.log(b);
  *      });
  *
  * @see request_db
  */
  function get_db(name, callback) {
    request_db({db: name, method: "GET"},callback);
  }

 /*
  * Lists all the databases in CouchDB
  *
  * e.g. nano.db.get(db_name, function(e,b) {
  *        console.log(b);
  *      });
  *
  * @see request_db
  */
  function list_dbs(callback) {
    request_db({db: "_all_dbs", method: "GET"},callback);
  }

  public_functions = { db:  { create: create_db
                            , get: get_db
                            , destroy: destroy_db
                            , list: list_dbs
                            //, replicate: replicate_db
                            //, compact: compact_db
                            //, changes: { add: add_listener
                            //           , remove: remove_listener}
                            }
                     //, create: create_doc
                     //, get: get_doc
                     //, destroy: destroy_doc
                     //, bulk: bulk_doc
                     //, list: list_docs
                     };

  return public_functions;
};

nano.version = JSON.parse(
  fs.readFileSync(__dirname + "/package.json")).version;
nano.path    = __dirname;<commit_msg>changed comments<commit_after>/* Minimalistic Couch In Node */
var request = require('request')
  , fs      = require('fs')
  , error   = require('./error')
  , headers = { "content-type": "application/json"
              , "accept": "application/json"
              }
  , nano;

module.exports = exports = nano = function nano_module(cfg) {
  var public_functions = {};
  if(typeof cfg === "string") {
    cfg = require(cfg); // No CFG? Maybe it's a file path?
  }

 /*
  * Request DB
  *
  * An auxiliary function to do the request to CouchDB
  *
  * @error {request:connect_db} There was a problem connecting to CouchDB
  * @error {couch:*} Any error that CouchDB returns when creating a DB
  *
  * @param {opts} The request options; e.g. {db: "test", method: "GET"}
  * @param {callback} The function to callback
  *
  * @return Execution of the code in your callback. Hopefully you are handling
  */
  function request_db(opts,callback) {
    var url = cfg.database(opts.db)
      , req
      , status_code
      , parsed;
    if(!callback) { callback = function () { return; }; } // Void Callback
    if(opts.doc) { url += "/" + opts.doc; } // Add the document to the URL
    req =
      { uri: url
      , method: opts.method
      , headers: headers
      };
    request(req, function(e,h,b){
      status_code = h.statusCode;
      if(e) {
        callback(error.request_err(e,"connect_db",req,status_code));
        return;
      }
      parsed = JSON.parse(b);
      if (status_code === 200 || status_code === 201) { callback(null,parsed); }
      else { // Proxy the error
        callback(error.couch_err(parsed.reason,parsed.error,req,status_code));
      }
    });
  }

 /*
  * Creates a CouchDB Database
  *
  * e.g. nano.db.create(db_name, function (e,b) {
  *        if(tried.tried === tried.max_retries) {
  *          callback("Retries work");
  *          return;
  *        }
  *        else {
  *          tried.tried += 1;
  *          recursive_retries_create_db(tried,callback);
  *        }
  *      });
  *
  * @see request_db
  */
  function create_db(name, callback) {
    request_db({db: name, method: "PUT"},callback);
  }

 /*
  * Annihilates a CouchDB Database
  *
  * e.g. nano.db.delete(db_name);
  *
  * Even though this looks sync it is an async function
  * and therefor order is not guaranteed
  *
  * @see request_db
  */
  function destroy_db(name, callback) {
    request_db({db: name, method: "DELETE"},callback);
  }

 /*
  * Gets information about a CouchDB Database
  *
  * e.g. nano.db.get(db_name, function(e,b) {
  *        console.log(b);
  *      });
  *
  * @see request_db
  */
  function get_db(name, callback) {
    request_db({db: name, method: "GET"},callback);
  }

 /*
  * Lists all the databases in CouchDB
  *
  * e.g. nano.db.list(function(e,b) {
  *        console.log(b);
  *      });
  *
  * @see request_db
  */
  function list_dbs(callback) {
    request_db({db: "_all_dbs", method: "GET"},callback);
  }

  public_functions = { db:  { create: create_db
                            , get: get_db
                            , destroy: destroy_db
                            , list: list_dbs
                            //, replicate: replicate_db
                            //, compact: compact_db
                            //, changes: { add: add_listener
                            //           , remove: remove_listener}
                            }
                     // doc: { create: create_doc
                     //      , get: get_doc
                     //      , destroy: destroy_doc
                     //      , bulk: bulk_doc
                     //      , list: list_docs
                     //      }
                     // request: request_db
                     };

  return public_functions;
};

nano.version = JSON.parse(
  fs.readFileSync(__dirname + "/package.json")).version;
nano.path    = __dirname;<|endoftext|>


<commit_before>/**
 * Service for launching a text search.
 */

var search = function (_, $http, $interpolate, $location, $window, $q, routes) {
    var self = this; // in case some event handler or constructor overrides "this"

    this.config = {
        fields: [
            'active',
            'author',
            'common_name',
            'description',
            'expert_db',
            'function',
            'gene',
            'gene_synonym',
            'has_genomic_coordinates',
            'length',
            'locus_tag',
            'organelle',
            'pub_title',
            'product',
            'rfam_problem_found',
            'rfam_problems',
            'rna_type',
            'standard_name'
        ],
        fieldWeights: {
            'active': 0,
            'author': 2,
            'common_name': 3,
            'description': 2,
            'expert_db': 4,
            'function': 4,
            'gene': 4,
            'gene_synonym': 3,
            'has_genomic_coordinates': 0,
            'length': 0,
            'locus_tag': 2,
            'organelle': 3,
            'pub_title': 2,
            'product': 1,
            'rna_type': 2,
            'standard_name': 2
        },
        fieldVerboseNames: {
            'active': 'Active',
            'author': 'Author',
            'common_name': 'Species',
            'description': 'Description',
            'expert_db': 'Source',
            'function': 'Function',
            'gene': 'Gene',
            'gene_synonym': 'Gene synonym',
            'has_genomic_coordinates': 'Genomic coordinates',
            'locus_tag': 'Locus tag',
            'length': 'Length',
            'organelle': 'Organelle',
            'pub_title': 'Publication title',
            'product': 'Product',
            'rna_type': 'RNA type',
            'standard_name': 'Standard name'
        },
        facetfields: ['length', 'rna_type', 'TAXONOMY', 'expert_db', 'rfam_problem_found', 'has_genomic_coordinates', 'popular_species'], // will be displayed in this order
        sortableFields: [
            { label: 'Popular species, Length ↓', value: 'boost:descending,length:descending' },
            // { label: 'Popular species ↑', value: 'boost:ascending' },
            { label: 'Length ↓', value: 'length:descending' },
            { label: 'Length ↑', value: 'length:ascending' },
            // { label: 'Citations number ↓', value: 'n_citations:descending' },
            // { label: 'Citations number ↑', value: 'n_citations:ascending' },
            // { label: 'Annotations number ↓', value: 'n_xrefs:descending' },
            // { label: 'Annotations number ↑', value: 'n_xrefs:ascending' }
        ],
        facetcount: 30,
        pagesize: 15,
    };

    /**
     * Service initialization.
     */
    this.result = {
        hitCount: null,
        entries: [],
        facets: []
    };

    this.status = 'off'; // possible values: 'off', 'in progress', 'success', 'error'

    this.query = ''; // the query will be observed by watches
    this.sort = 'boost:descending,length:descending' // EBI search endpoint sorts results by this field value
    this.sortTiebreaker = 'length:descending'; // secondary search field, used in case first field is even

    this.callbacks = []; // callbacks to be called after each search.search(); done for slider redraw

    /**
     * Launch EBeye autocompletion.
     *
     * Note that we're using $q CommonJS deferred syntax here,
     * not simpler ES6-ish, cause we need to cancel autocomplete request,
     * if search is started, which is not possible with ES6.
     *
     * @param query
     * @returns {Promise}
     */
    this.autocomplete = function (query) {
        self = this;

        self.autocompleteDeferred = $q.defer();

        if (query.length < 3) {
            self.autocompleteDeferred.reject("query too short!");
        }
        else {
            // get queryUrl ready
            var ebeyeUrl = routes.ebiAutocomplete({query: query});
            var queryUrl = routes.ebiSearchProxy({ebeyeUrl: encodeURIComponent(ebeyeUrl)});

            $http.get(queryUrl, {ignoreLoadingBar: true}).then(
                function(response) {
                    self.autocompleteDeferred.resolve(response);
                },
                function(response) {
                    self.autocompleteDeferred.reject(response);
                }
            );
        }

        return self.autocompleteDeferred.promise;
    };

    /**
     * For length slider we need to do updates after each search.
     * @param callback - function to be called after every successful search
     */
    this.registerSearchCallback = function (callback) {
        this.callbacks.push(callback);
    };

    /**
     * Launch EBeye search.
     * @param query
     * @param start - determines the range of the results to be returned.
     * @creates self.promise
     */
    this.search = function (query, start) {
        start = start || 0;

        // hopscotch.endTour(); // end guided tour when a search is launched
        self.autocompleteDeferred && self.autocompleteDeferred.reject(); // if autocompletion was launched - reject it

        self.query = query;
        self.status = 'in progress';

        // display search spinner if not a "load more" request
        if (start === 0) self.result.hitCount = null;

        // change page title, which is also used in browser tabs
        $window.document.title = 'Search: ' + query;

        query = self.preprocessQuery(query);

        // get queryUrl ready
        var ebeyeUrl = routes.ebiSearch({
            ebiBaseUrl: global_settings.EBI_SEARCH_ENDPOINT,
            query: query,
            hlfields: self.config.fields.join(),
            facetcount: self.config.facetcount,
            facetfields: self.config.facetfields.join(),
            size: self.config.pagesize,
            start: start,
            sort: self.sort
        });
        var queryUrl = routes.ebiSearchProxy({ebeyeUrl: encodeURIComponent(ebeyeUrl)});

        // perform search
        var overwriteResults = (start === 0);

        self.promise = $http.get(queryUrl).then(
            function(response) {
                var data = self.preprocessResults(response.data);

                overwriteResults = overwriteResults || false;
                if (overwriteResults) {
                    self.result = data; // replace
                } else {
                    self.result.entries = self.result.entries.concat(data.entries); // append new entries
                }

                // after each search send a pageview to GA
                $window.ga('send', 'pageview', $location.path());
                self.status = 'success';

                // run callbacks
                self.callbacks.forEach(function(callback) { callback() });
            },
            function(response) {
                self.status = 'error';
            }
        );
    };

    /**
     * Split query into words and then:
     *  - append wildcards to all terms without double quotes and not ending with wildcards
     *  - escape special symbols
     *  - capitalize logical operators
     *
     *  Splitting into words is based on this SO question:
     *  http://stackoverflow.com/questions/366202/regex-for-splitting-a-string-using-space-when-not-surrounded-by-single-or-double
     * Each "word" is a sequence of characters that aren't spaces or quotes,
     * or a sequence of characters that begin and end with a quote, with no quotes in between.
     */
    this.preprocessQuery = function (query) {

        // replace URS/taxid with URS_taxid - replace slashes with underscore
        query = query.replace(/(URS[0-9A-F]{10})\/(\d+)/ig, '$1_$2');

        // replace length query with a placeholder, example: length:[100 TO 200]
        var lengthClause = query.match(/length\:\[\d+\s+to\s+\d+\]/i);
        var placeholder = 'length_clause';
        if (lengthClause) {
          query = query.replace(lengthClause[0], placeholder);
          lengthClause[0] = lengthClause[0].replace(/to/i, 'TO');
        }

        var words = query.match(/[^\s"]+|"[^"]*"/g);
        var arrayLength = words.length;
        for (var i = 0; i < arrayLength; i++) {
            if ( words[i].match(/^(and|or|not)$/gi) ) {
                // capitalize logical operators
                words[i] = words[i].toUpperCase();
            } else if ( words[i].match(/\:$/gi) ) {
                // faceted search term + a colon, e.g. expert_db:
                var term = words[i].replace(':','');
                var xrefs = ['pubmed', 'doi', 'taxonomy'];
                if ( term.match(new RegExp('^(' + xrefs.join('|') + ')$', 'i') ) ) {
                    // xref fields must be capitalized
                    term = term.toUpperCase();
                }
                words[i] = term + ':';
            } else if ( words[i].match(/\//)) {
                // do not add wildcards to DOIs
                words[i] = escapeSearchTerm(words[i]);
            } else if ( words[i].match(/^".+?"$/) ) {
                // double quotes, do nothing
            } else if ( words[i].match(/\*$/) ) {
                // wildcard, escape term
                words[i] = escapeSearchTerm(words[i]);
            } else if ( words[i].match(/\)$/) ) {
                // right closing grouping parenthesis, don't add a wildcard
            } else if ( words[i].length < 3 ) {
                // the word is too short for wildcards, do nothing
            } else {
                // all other words
                // escape term, add wildcard
                words[i] = escapeSearchTerm(words[i]) + '*';
            }
        }
        query = words.join(' ');
        query = query.replace(/\: /g, ':'); // to avoid spaces after faceted search terms
        // replace placeholder with the original search term
        if (lengthClause) {
          query = query.replace(placeholder + '*', lengthClause[0]);
        }
        return query;

        /**
         * Escape special symbols used by Lucene
         * Escaped: + - && || ! { } [ ] ^ ~ ? : \ /
         * Not escaped: * " ( ) because they may be used deliberately by the user
         */
        function escapeSearchTerm (searchTerm) {
            return searchTerm.replace(/[\+\-&|!\{\}\[\]\^~\?\:\\\/]/g, "\\$&");
        }
    };

    // /**
    //  * Looks into self.sort (e.g. [['boost', 'ascending'], ['length', 'descending']])
    //  * @returns {string} - e.g. 'boost:ascending,length:descending'
    //  */
    // this.preprocessSort = function () {
    //     var sort = "";
    //     self.sort.forEach( function (field) { sort.concat("," + field[0] + ":" + field[1]) } );
    //     return sort;
    // };

    /**
     * Preprocess data received from the server.
     */
    this.preprocessResults = function (data) {

        _mergeSpeciesFacets(data);

        // order facets the same way as in the config
        data.facets = _.sortBy(data.facets, function(facet){
            return _.indexOf(self.config.facetfields, facet.id);
        });

        // update rfam_problem_found labels from True/Fase to Yes/No
        for (var i=0; i < data.facets.length; i++) {
            if (data.facets[i].id == 'rfam_problem_found') {
                for (var j=0; j < data.facets[i].facetValues.length; j++) {
                    if (data.facets[i].facetValues[j].label == 'True') {
                        data.facets[i].facetValues[j].label = 'Yes';
                    } else if (data.facets[i].facetValues[j].label == 'False') {
                        data.facets[i].facetValues[j].label = 'No';
                    }
                }
            }
        }

         // Use `hlfields` with highlighted matches instead of `fields`.
        for (var i=0; i < data.entries.length; i++) {
            data.entries[i].fields = data.entries[i].highlights;
            data.entries[i].fields.length[0] = data.entries[i].fields.length[0].replace(/<[^>]+>/gm, '');
            data.entries[i].id_with_slash = data.entries[i].id.replace('_', '/');
        }

        return data;

        /**
         * Merge the two species facets putting popularSpecies
         * at the top of the list.
         * Species facets:
         * - TAXONOMY (all species)
         * - popularSpecies (manually curated set of top organisms).
         */
        function _mergeSpeciesFacets (data) {

            // find the popular species facet
            var topSpeciesFacetId = _findFacetId('popular_species', data);

            if (topSpeciesFacetId) {
                // get top species names
                var popularSpecies = _.pluck(data.facets[topSpeciesFacetId].facetValues, 'label');

                // find the taxonomy facet
                var taxonomyFacetId = _findFacetId('TAXONOMY', data);

                // extract other species from the taxonomy facet
                var otherSpecies = _getOtherSpecies(data);

                // merge popularSpecies with otherSpecies
                data.facets[taxonomyFacetId].facetValues = data.facets[topSpeciesFacetId].facetValues.concat(otherSpecies);

                // remove the Popular species facet
                delete data.facets[topSpeciesFacetId];
                data.facets = _.compact(data.facets);
            }

            /**
             * Find objects in array by attribute value.
             * Given an array like:
             * [{'id': 'a'}, {'id': 'b'}, {'id': 'c'}]
             * findFacetId('b') -> 1
             */
            function _findFacetId (facetLabel, data) {
                var index;
                _.find(data.facets, function(facet, i) {
                    if (facet.id === facetLabel) {
                        index = i;
                        return true;
                    }
                });
                return index;
            }

            /**
             * Get Taxonomy facet values that are not also in popularSpecies.
             */
            function _getOtherSpecies (data) {
                var taxonomyFacet = data.facets[taxonomyFacetId].facetValues;
                var otherSpecies = [];
                for (var i=0; i<taxonomyFacet.length; i++) {
                    if (_.indexOf(popularSpecies, taxonomyFacet[i].label) === -1) {
                        otherSpecies.push(taxonomyFacet[i]);
                    }
                }
                return otherSpecies;
            }
        }
    };

    /**
     * Load more results starting from the last loaded index.
     */
    this.loadMoreResults = function () {
        self.search(self.query, self.result.entries.length);
    };

    /**
     * Checks, if search query contains any lucene-specific syntax, or if it's a plain text
     */
    this.luceneSyntaxUsed = function (query) {
        if (/[\+\-\&\|\!\{\}\[\]\^~\?\:\\\/\*\"\(]/.test(query)) return true;
        if (/[\s\"]OR[\s\"]/.test(query)) return true;
        if (/[\s\"]AND[\s\"]/.test(query)) return true;
        return false;
    }
};

angular.module('textSearch')
    .service('search', ['_', '$http', '$interpolate', '$location', '$window', '$q', 'routes', search]);
<commit_msg>Show text search highlights for tax_string<commit_after>/**
 * Service for launching a text search.
 */

var search = function (_, $http, $interpolate, $location, $window, $q, routes) {
    var self = this; // in case some event handler or constructor overrides "this"

    this.config = {
        fields: [
            'active',
            'author',
            'common_name',
            'description',
            'expert_db',
            'function',
            'gene',
            'gene_synonym',
            'has_genomic_coordinates',
            'length',
            'locus_tag',
            'organelle',
            'pub_title',
            'product',
            'rfam_problem_found',
            'rfam_problems',
            'rna_type',
            'standard_name',
            'tax_string'
        ],
        fieldWeights: {
            'active': 0,
            'author': 2,
            'common_name': 3,
            'description': 2,
            'expert_db': 4,
            'function': 4,
            'gene': 4,
            'gene_synonym': 3,
            'has_genomic_coordinates': 0,
            'length': 0,
            'locus_tag': 2,
            'organelle': 3,
            'pub_title': 2,
            'product': 1,
            'rna_type': 2,
            'standard_name': 2,
            'tax_string': 2,
        },
        fieldVerboseNames: {
            'active': 'Active',
            'author': 'Author',
            'common_name': 'Species',
            'description': 'Description',
            'expert_db': 'Source',
            'function': 'Function',
            'gene': 'Gene',
            'gene_synonym': 'Gene synonym',
            'has_genomic_coordinates': 'Genomic coordinates',
            'locus_tag': 'Locus tag',
            'length': 'Length',
            'organelle': 'Organelle',
            'pub_title': 'Publication title',
            'product': 'Product',
            'rna_type': 'RNA type',
            'standard_name': 'Standard name',
            'tax_string': 'Taxonomy'
        },
        facetfields: ['length', 'rna_type', 'TAXONOMY', 'expert_db', 'rfam_problem_found', 'has_genomic_coordinates', 'popular_species'], // will be displayed in this order
        sortableFields: [
            { label: 'Popular species, Length ↓', value: 'boost:descending,length:descending' },
            // { label: 'Popular species ↑', value: 'boost:ascending' },
            { label: 'Length ↓', value: 'length:descending' },
            { label: 'Length ↑', value: 'length:ascending' },
            // { label: 'Citations number ↓', value: 'n_citations:descending' },
            // { label: 'Citations number ↑', value: 'n_citations:ascending' },
            // { label: 'Annotations number ↓', value: 'n_xrefs:descending' },
            // { label: 'Annotations number ↑', value: 'n_xrefs:ascending' }
        ],
        facetcount: 30,
        pagesize: 15,
    };

    /**
     * Service initialization.
     */
    this.result = {
        hitCount: null,
        entries: [],
        facets: []
    };

    this.status = 'off'; // possible values: 'off', 'in progress', 'success', 'error'

    this.query = ''; // the query will be observed by watches
    this.sort = 'boost:descending,length:descending' // EBI search endpoint sorts results by this field value
    this.sortTiebreaker = 'length:descending'; // secondary search field, used in case first field is even

    this.callbacks = []; // callbacks to be called after each search.search(); done for slider redraw

    /**
     * Launch EBeye autocompletion.
     *
     * Note that we're using $q CommonJS deferred syntax here,
     * not simpler ES6-ish, cause we need to cancel autocomplete request,
     * if search is started, which is not possible with ES6.
     *
     * @param query
     * @returns {Promise}
     */
    this.autocomplete = function (query) {
        self = this;

        self.autocompleteDeferred = $q.defer();

        if (query.length < 3) {
            self.autocompleteDeferred.reject("query too short!");
        }
        else {
            // get queryUrl ready
            var ebeyeUrl = routes.ebiAutocomplete({query: query});
            var queryUrl = routes.ebiSearchProxy({ebeyeUrl: encodeURIComponent(ebeyeUrl)});

            $http.get(queryUrl, {ignoreLoadingBar: true}).then(
                function(response) {
                    self.autocompleteDeferred.resolve(response);
                },
                function(response) {
                    self.autocompleteDeferred.reject(response);
                }
            );
        }

        return self.autocompleteDeferred.promise;
    };

    /**
     * For length slider we need to do updates after each search.
     * @param callback - function to be called after every successful search
     */
    this.registerSearchCallback = function (callback) {
        this.callbacks.push(callback);
    };

    /**
     * Launch EBeye search.
     * @param query
     * @param start - determines the range of the results to be returned.
     * @creates self.promise
     */
    this.search = function (query, start) {
        start = start || 0;

        // hopscotch.endTour(); // end guided tour when a search is launched
        self.autocompleteDeferred && self.autocompleteDeferred.reject(); // if autocompletion was launched - reject it

        self.query = query;
        self.status = 'in progress';

        // display search spinner if not a "load more" request
        if (start === 0) self.result.hitCount = null;

        // change page title, which is also used in browser tabs
        $window.document.title = 'Search: ' + query;

        query = self.preprocessQuery(query);

        // get queryUrl ready
        var ebeyeUrl = routes.ebiSearch({
            ebiBaseUrl: global_settings.EBI_SEARCH_ENDPOINT,
            query: query,
            hlfields: self.config.fields.join(),
            facetcount: self.config.facetcount,
            facetfields: self.config.facetfields.join(),
            size: self.config.pagesize,
            start: start,
            sort: self.sort
        });
        var queryUrl = routes.ebiSearchProxy({ebeyeUrl: encodeURIComponent(ebeyeUrl)});

        // perform search
        var overwriteResults = (start === 0);

        self.promise = $http.get(queryUrl).then(
            function(response) {
                var data = self.preprocessResults(response.data);

                overwriteResults = overwriteResults || false;
                if (overwriteResults) {
                    self.result = data; // replace
                } else {
                    self.result.entries = self.result.entries.concat(data.entries); // append new entries
                }

                // after each search send a pageview to GA
                $window.ga('send', 'pageview', $location.path());
                self.status = 'success';

                // run callbacks
                self.callbacks.forEach(function(callback) { callback() });
            },
            function(response) {
                self.status = 'error';
            }
        );
    };

    /**
     * Split query into words and then:
     *  - append wildcards to all terms without double quotes and not ending with wildcards
     *  - escape special symbols
     *  - capitalize logical operators
     *
     *  Splitting into words is based on this SO question:
     *  http://stackoverflow.com/questions/366202/regex-for-splitting-a-string-using-space-when-not-surrounded-by-single-or-double
     * Each "word" is a sequence of characters that aren't spaces or quotes,
     * or a sequence of characters that begin and end with a quote, with no quotes in between.
     */
    this.preprocessQuery = function (query) {

        // replace URS/taxid with URS_taxid - replace slashes with underscore
        query = query.replace(/(URS[0-9A-F]{10})\/(\d+)/ig, '$1_$2');

        // replace length query with a placeholder, example: length:[100 TO 200]
        var lengthClause = query.match(/length\:\[\d+\s+to\s+\d+\]/i);
        var placeholder = 'length_clause';
        if (lengthClause) {
          query = query.replace(lengthClause[0], placeholder);
          lengthClause[0] = lengthClause[0].replace(/to/i, 'TO');
        }

        var words = query.match(/[^\s"]+|"[^"]*"/g);
        var arrayLength = words.length;
        for (var i = 0; i < arrayLength; i++) {
            if ( words[i].match(/^(and|or|not)$/gi) ) {
                // capitalize logical operators
                words[i] = words[i].toUpperCase();
            } else if ( words[i].match(/\:$/gi) ) {
                // faceted search term + a colon, e.g. expert_db:
                var term = words[i].replace(':','');
                var xrefs = ['pubmed', 'doi', 'taxonomy'];
                if ( term.match(new RegExp('^(' + xrefs.join('|') + ')$', 'i') ) ) {
                    // xref fields must be capitalized
                    term = term.toUpperCase();
                }
                words[i] = term + ':';
            } else if ( words[i].match(/\//)) {
                // do not add wildcards to DOIs
                words[i] = escapeSearchTerm(words[i]);
            } else if ( words[i].match(/^".+?"$/) ) {
                // double quotes, do nothing
            } else if ( words[i].match(/\*$/) ) {
                // wildcard, escape term
                words[i] = escapeSearchTerm(words[i]);
            } else if ( words[i].match(/\)$/) ) {
                // right closing grouping parenthesis, don't add a wildcard
            } else if ( words[i].length < 3 ) {
                // the word is too short for wildcards, do nothing
            } else {
                // all other words
                // escape term, add wildcard
                words[i] = escapeSearchTerm(words[i]) + '*';
            }
        }
        query = words.join(' ');
        query = query.replace(/\: /g, ':'); // to avoid spaces after faceted search terms
        // replace placeholder with the original search term
        if (lengthClause) {
          query = query.replace(placeholder + '*', lengthClause[0]);
        }
        return query;

        /**
         * Escape special symbols used by Lucene
         * Escaped: + - && || ! { } [ ] ^ ~ ? : \ /
         * Not escaped: * " ( ) because they may be used deliberately by the user
         */
        function escapeSearchTerm (searchTerm) {
            return searchTerm.replace(/[\+\-&|!\{\}\[\]\^~\?\:\\\/]/g, "\\$&");
        }
    };

    // /**
    //  * Looks into self.sort (e.g. [['boost', 'ascending'], ['length', 'descending']])
    //  * @returns {string} - e.g. 'boost:ascending,length:descending'
    //  */
    // this.preprocessSort = function () {
    //     var sort = "";
    //     self.sort.forEach( function (field) { sort.concat("," + field[0] + ":" + field[1]) } );
    //     return sort;
    // };

    /**
     * Preprocess data received from the server.
     */
    this.preprocessResults = function (data) {

        _mergeSpeciesFacets(data);

        // order facets the same way as in the config
        data.facets = _.sortBy(data.facets, function(facet){
            return _.indexOf(self.config.facetfields, facet.id);
        });

        // update rfam_problem_found labels from True/Fase to Yes/No
        for (var i=0; i < data.facets.length; i++) {
            if (data.facets[i].id == 'rfam_problem_found') {
                for (var j=0; j < data.facets[i].facetValues.length; j++) {
                    if (data.facets[i].facetValues[j].label == 'True') {
                        data.facets[i].facetValues[j].label = 'Yes';
                    } else if (data.facets[i].facetValues[j].label == 'False') {
                        data.facets[i].facetValues[j].label = 'No';
                    }
                }
            }
        }

         // Use `hlfields` with highlighted matches instead of `fields`.
        for (var i=0; i < data.entries.length; i++) {
            data.entries[i].fields = data.entries[i].highlights;
            data.entries[i].fields.length[0] = data.entries[i].fields.length[0].replace(/<[^>]+>/gm, '');
            data.entries[i].id_with_slash = data.entries[i].id.replace('_', '/');
        }

        return data;

        /**
         * Merge the two species facets putting popularSpecies
         * at the top of the list.
         * Species facets:
         * - TAXONOMY (all species)
         * - popularSpecies (manually curated set of top organisms).
         */
        function _mergeSpeciesFacets (data) {

            // find the popular species facet
            var topSpeciesFacetId = _findFacetId('popular_species', data);

            if (topSpeciesFacetId) {
                // get top species names
                var popularSpecies = _.pluck(data.facets[topSpeciesFacetId].facetValues, 'label');

                // find the taxonomy facet
                var taxonomyFacetId = _findFacetId('TAXONOMY', data);

                // extract other species from the taxonomy facet
                var otherSpecies = _getOtherSpecies(data);

                // merge popularSpecies with otherSpecies
                data.facets[taxonomyFacetId].facetValues = data.facets[topSpeciesFacetId].facetValues.concat(otherSpecies);

                // remove the Popular species facet
                delete data.facets[topSpeciesFacetId];
                data.facets = _.compact(data.facets);
            }

            /**
             * Find objects in array by attribute value.
             * Given an array like:
             * [{'id': 'a'}, {'id': 'b'}, {'id': 'c'}]
             * findFacetId('b') -> 1
             */
            function _findFacetId (facetLabel, data) {
                var index;
                _.find(data.facets, function(facet, i) {
                    if (facet.id === facetLabel) {
                        index = i;
                        return true;
                    }
                });
                return index;
            }

            /**
             * Get Taxonomy facet values that are not also in popularSpecies.
             */
            function _getOtherSpecies (data) {
                var taxonomyFacet = data.facets[taxonomyFacetId].facetValues;
                var otherSpecies = [];
                for (var i=0; i<taxonomyFacet.length; i++) {
                    if (_.indexOf(popularSpecies, taxonomyFacet[i].label) === -1) {
                        otherSpecies.push(taxonomyFacet[i]);
                    }
                }
                return otherSpecies;
            }
        }
    };

    /**
     * Load more results starting from the last loaded index.
     */
    this.loadMoreResults = function () {
        self.search(self.query, self.result.entries.length);
    };

    /**
     * Checks, if search query contains any lucene-specific syntax, or if it's a plain text
     */
    this.luceneSyntaxUsed = function (query) {
        if (/[\+\-\&\|\!\{\}\[\]\^~\?\:\\\/\*\"\(]/.test(query)) return true;
        if (/[\s\"]OR[\s\"]/.test(query)) return true;
        if (/[\s\"]AND[\s\"]/.test(query)) return true;
        return false;
    }
};

angular.module('textSearch')
    .service('search', ['_', '$http', '$interpolate', '$location', '$window', '$q', 'routes', search]);
<|endoftext|>


<commit_before>import {Route, Redirect} from 'react-router';
import {EventEmitter} from 'events';

import {ROUTING_CHANGE} from './EventTypes';

function throwError(error) {
  if (global.__DEV__) {
    throw error;
  }
}

class RoutingService extends EventEmitter {

  constructor() {
    super();

    this.definition = [];

    const context = {
      deferredTasks: [],
      instance: this
    };

    this.defer = this.defer.bind(context);
    this.processDeferred = this.processDeferred.bind(context);

    this.on(ROUTING_CHANGE, this.processDeferred);
  }

  processDeferred() {
    const tasks = this.deferredTasks.slice(0);
    this.deferredTasks = [];

    // If Task is unable to resolve at this point of time it will re-defer itself
    tasks.forEach((args) => {
      this.instance.registerTab.apply(this.instance, args);
    });
  }

  defer(args) {
    this.deferredTasks.push(args);
  }

  getDefinition() {
    return this.definition.slice(0);
  }

  /**
   * registerPage - adds a page route to the queue
   *
   * @param  {String} path a path to the page
   * @param  {React.Component} component a React.js component of the page
   * @return {undefined}
   */
  registerPage(path, component) {
    if (!path || !component) {
      return throwError(new Error('Please provide all required arguments'));
    }

    const existingPage = this.definition.find((route) => route.path === path);

    if (existingPage && existingPage.component === component) {
      return;
    } else if (existingPage) {
      return throwError(new Error(`Attempt to override a page at ${path}!`));
    }

    this.definition.push({
      component,
      path,
      type: Route
    });

    this.emit(ROUTING_CHANGE);
  }

  /**
   * registerTab - adds a tab route to the queue
   *
   * @param  {String} pagePath a path to a parent page
   * @param  {String} tabPath a path to the tab
   * @param  {React.Component} component a React.js component of the tab
   * @return {undefined}
   */
  registerTab(pagePath, tabPath, component) {
    if (!pagePath || !tabPath || !component) {
      return throwError(new Error('Please provide all required arguments'));
    }

    const page = this.definition.find((route) => route.path === pagePath);

    if (!page) {
      return this.defer(arguments);
    }

    if (!page.children) {
      page.children = [];
    }

    const existingTab = page.children.find((route) => route.path === tabPath);

    if (existingTab && existingTab.component === component) {
      return;
    } else if (existingTab) {
      return throwError(new Error(`Attempt to override a tab at ${pagePath}/${tabPath}!`));
    }

    page.children.push({
      component,
      path: tabPath,
      type: Route
    });

    this.emit(ROUTING_CHANGE);
  }

  /**
   * registerRedirect
   *
   * @param  {String} path a path to redirect from
   * @param  {String} to   a path to redirect to
   * @return {undefined}
   */
  registerRedirect(path, to) {
    const existingRedirect = this.definition.find((route) => {
      return route.type === Redirect && route.path === path;
    });

    if (existingRedirect && existingRedirect.to === to) {
      return;
    } else if (existingRedirect) {
      return throwError(new Error(`Attempt to override Redirect of ${path} from ${existingRedirect.to} to ${to}!`));
    }

    this.definition.push({
      path,
      to,
      type: Redirect
    });
    this.emit(ROUTING_CHANGE);
  }

}

module.exports = RoutingService;
<commit_msg>Include definition in private context<commit_after>import {Route, Redirect} from 'react-router';
import {EventEmitter} from 'events';

import {ROUTING_CHANGE} from './EventTypes';

function throwError(error) {
  if (global.__DEV__) {
    throw error;
  }
}

class RoutingService extends EventEmitter {

  constructor() {
    super();

    const privateContext = {
      definition: [],
      deferredTasks: [],
      instance: this
    };

    this.defer = this.defer.bind(privateContext);
    this.processDeferred = this.processDeferred.bind(privateContext);
    this.getDefinition = this.getDefinition.bind(privateContext);
    this.registerPage = this.registerPage.bind(privateContext);
    this.registerTab = this.registerTab.bind(privateContext);
    this.registerRedirect = this.registerRedirect.bind(privateContext);

    this.on(ROUTING_CHANGE, this.processDeferred);
  }

  processDeferred() {
    const tasks = this.deferredTasks.slice(0);
    this.deferredTasks = [];

    // If Task is unable to resolve at this point of time it will re-defer itself
    tasks.forEach((args) => {
      this.instance.registerTab.apply(this.instance, args);
    });
  }

  defer(args) {
    this.deferredTasks.push(args);
  }

  getDefinition() {
    return this.definition.slice(0);
  }

  /**
   * registerPage - adds a page route to the queue
   *
   * @param  {String} path a path to the page
   * @param  {React.Component} component a React.js component of the page
   * @return {undefined}
   */
  registerPage(path, component) {
    if (!path || !component) {
      return throwError(new Error('Please provide all required arguments'));
    }

    const existingPage = this.definition.find((route) => route.path === path);

    if (existingPage && existingPage.component === component) {
      return;
    } else if (existingPage) {
      return throwError(new Error(`Attempt to override a page at ${path}!`));
    }

    this.definition.push({
      component,
      path,
      type: Route
    });

    this.instance.emit(ROUTING_CHANGE);
  }

  /**
   * registerTab - adds a tab route to the queue
   *
   * @param  {String} pagePath a path to a parent page
   * @param  {String} tabPath a path to the tab
   * @param  {React.Component} component a React.js component of the tab
   * @return {undefined}
   */
  registerTab(pagePath, tabPath, component) {
    if (!pagePath || !tabPath || !component) {
      return throwError(new Error('Please provide all required arguments'));
    }

    const page = this.definition.find((route) => route.path === pagePath);

    if (!page) {
      return this.instance.defer(arguments);
    }

    if (!page.children) {
      page.children = [];
    }

    const existingTab = page.children.find((route) => route.path === tabPath);

    if (existingTab && existingTab.component === component) {
      return;
    } else if (existingTab) {
      return throwError(new Error(`Attempt to override a tab at ${pagePath}/${tabPath}!`));
    }

    page.children.push({
      component,
      path: tabPath,
      type: Route
    });

    this.instance.emit(ROUTING_CHANGE);
  }

  /**
   * registerRedirect
   *
   * @param  {String} path a path to redirect from
   * @param  {String} to   a path to redirect to
   * @return {undefined}
   */
  registerRedirect(path, to) {
    const existingRedirect = this.definition.find((route) => {
      return route.type === Redirect && route.path === path;
    });

    if (existingRedirect && existingRedirect.to === to) {
      return;
    } else if (existingRedirect) {
      return throwError(new Error(`Attempt to override Redirect of ${path} from ${existingRedirect.to} to ${to}!`));
    }

    this.definition.push({
      path,
      to,
      type: Redirect
    });

    this.instance.emit(ROUTING_CHANGE);
  }

}

module.exports = RoutingService;
<|endoftext|>


<commit_before>import moment from 'moment'

import {
  USER_REFERRAL_VC_REWARD
} from '../constants'

class Globals {
  constructor () {
    this.raised = 350447.62
    this.raisedUpdateTime = moment('2017-03-20 12:40:11')
    this.dollarsPerDayRate = 400.00
  }
}

function isGlobalCampaignLive () {
  return process.env.IS_GLOBAL_CAMPAIGN_LIVE === 'true' || false
}

/**
 * Get an estimate of how much we've raised, using a combination of
 * the most recent manual entry and the estimated rate of money
 * raised over time.
 * @return {number}  A decimal rounded to two decimal places
 */
function getMoneyRaised () {
  const globals = new Globals()
  const secsInDay = 60 * 60 * 24

  const totalRaised = globals.raised
  const datetimeOfLastEntry = globals.raisedUpdateTime
  const moneyRaisedRate = globals.dollarsPerDayRate
  const now = moment()
  const diff = now.diff(datetimeOfLastEntry, 'seconds')
  const secondsToDays = diff / secsInDay
  const finalRaised = totalRaised + (secondsToDays * moneyRaisedRate)
  return finalRaised.toFixed(2)
}

function getDollarsPerDayRate () {
  const globals = new Globals()
  return globals.dollarsPerDayRate
}

function getReferralVcReward () {
  return USER_REFERRAL_VC_REWARD
}

export {
  Globals,
  getMoneyRaised,
  getDollarsPerDayRate,
  getReferralVcReward,
  isGlobalCampaignLive
}
<commit_msg>Update daily money raised estimate<commit_after>import moment from 'moment'

import {
  USER_REFERRAL_VC_REWARD
} from '../constants'

class Globals {
  constructor () {
    this.raised = 490100.00
    this.raisedUpdateTime = moment('2018-02-28 16:00:00')
    this.dollarsPerDayRate = 600.00
  }
}

function isGlobalCampaignLive () {
  return process.env.IS_GLOBAL_CAMPAIGN_LIVE === 'true' || false
}

/**
 * Get an estimate of how much we've raised, using a combination of
 * the most recent manual entry and the estimated rate of money
 * raised over time.
 * @return {number}  A decimal rounded to two decimal places
 */
function getMoneyRaised () {
  const globals = new Globals()
  const secsInDay = 60 * 60 * 24

  const totalRaised = globals.raised
  const datetimeOfLastEntry = globals.raisedUpdateTime
  const moneyRaisedRate = globals.dollarsPerDayRate
  const now = moment()
  const diff = now.diff(datetimeOfLastEntry, 'seconds')
  const secondsToDays = diff / secsInDay
  const finalRaised = totalRaised + (secondsToDays * moneyRaisedRate)
  return finalRaised.toFixed(2)
}

function getDollarsPerDayRate () {
  const globals = new Globals()
  return globals.dollarsPerDayRate
}

function getReferralVcReward () {
  return USER_REFERRAL_VC_REWARD
}

export {
  Globals,
  getMoneyRaised,
  getDollarsPerDayRate,
  getReferralVcReward,
  isGlobalCampaignLive
}
<|endoftext|>


<commit_before>import Async from 'asyncawait/async'
import Await from 'asyncawait/await'

import {
  Widget,
  getWidget,
  getWidgets
} from './widget/baseWidget'

import {
  getUserWidgets as getAllUserWidgets,
  getUserWidgetsByEnabledState,
  updateWidgetData,
  updateWidgetVisibility,
  updateWidgetEnabled,
  updateWidgetConfig
} from './userWidget/userWidget'

/**
  * Merge the user widget with the widget data to create an object
  * with all the widget information.
  * @param {Object<UserWidget>} userWidget
  * @param {Object<UserWidget>} widget
  * @return {Object<UserWidget>}  Returns an instance of UserWidget
  * with all the widget information.
  */
function getFullWidget (userWidget, widget) {
  return Object.assign({},
    userWidget,
    widget,
    {
      id: userWidget.widgetId,
      data: JSON.stringify(userWidget.data),
      config: JSON.stringify(userWidget.config),
      settings: JSON.stringify(widget.settings)
    }
  )
}

/**
 * Fetch the widgets for a user. The result includes the widget data
 * as well as the user-widget related data.
 * The user-widget data field gets serialized into a string.
 * @param {string} userId - The user id.
 * @return {Object[]}  Returns a list of object that with the widget and
 * the user data on the widget information.
 */

var getUserWidgets = Async(function (userId, enabled) {
  var userWidgets
  if (typeof enabled !== 'undefined') {
    userWidgets = Await(getUserWidgetsByEnabledState(userId, enabled))
  } else {
    userWidgets = Await(getAllUserWidgets(userId))
  }

  const keys = []
  const indexMapper = {}

  var widgetId
  for (var index in userWidgets) {
    widgetId = userWidgets[index].widgetId

    keys.push({
      id: widgetId
    })

    indexMapper[widgetId] = index
  }

  if (!keys.length) { return [] }

  var widgets = Await(Widget.getBatch(keys))
  Widget.sorted(widgets)

  const result = []
  var userWidget
  for (var i in widgets) {
    widgetId = widgets[i].id
    userWidget = userWidgets[indexMapper[widgetId]]

    result.push(
          getFullWidget(userWidget, widgets[i]))
  }
  return result
})

/**
 * Update widget data.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} data - The new widget data.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var updateUserWidgetData = Async(function (userId, widgetId, data) {
  const parsedData = JSON.parse(data)
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetData(userId, widgetId, parsedData))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget visible state.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {boolean} visible - The new visible state.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * widget.
 */
var updateUserWidgetVisibility = Async(function (userId, widgetId, visible) {
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetVisibility(userId, widgetId, visible))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget enabled state.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {boolean} enabled - The new enabled state.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * widget.
 */
var updateUserWidgetEnabled = Async(function (userId, widgetId, enabled) {
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetEnabled(userId, widgetId, enabled))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget config.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} config - The new widget config.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var updateUserWidgetConfig = Async(function (userId, widgetId, config) {
  const parsedConfig = JSON.parse(config)
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetConfig(userId, widgetId, parsedConfig))
  return getFullWidget(userWidget, widget)
})

/**
 * Get all widgets.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} config - The new widget config.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var getAllWidgets = Async(function () {
  const widgets = Await(getWidgets())
  for (var i = 0; i < widgets.length; i++) {
    widgets[i].widgetId = widgets[i].id
    widgets[i].settings = JSON.stringify(widgets[i].settings)
  }
  return widgets
})

export {
  getUserWidgets,
  updateUserWidgetData,
  updateUserWidgetVisibility,
  updateUserWidgetEnabled,
  updateUserWidgetConfig,
  getAllWidgets
}
<commit_msg>Return sorted widgets for settings.<commit_after>import Async from 'asyncawait/async'
import Await from 'asyncawait/await'

import {
  Widget,
  getWidget,
  getWidgets
} from './widget/baseWidget'

import {
  getUserWidgets as getAllUserWidgets,
  getUserWidgetsByEnabledState,
  updateWidgetData,
  updateWidgetVisibility,
  updateWidgetEnabled,
  updateWidgetConfig
} from './userWidget/userWidget'

/**
  * Merge the user widget with the widget data to create an object
  * with all the widget information.
  * @param {Object<UserWidget>} userWidget
  * @param {Object<UserWidget>} widget
  * @return {Object<UserWidget>}  Returns an instance of UserWidget
  * with all the widget information.
  */
function getFullWidget (userWidget, widget) {
  return Object.assign({},
    userWidget,
    widget,
    {
      id: userWidget.widgetId,
      data: JSON.stringify(userWidget.data),
      config: JSON.stringify(userWidget.config),
      settings: JSON.stringify(widget.settings)
    }
  )
}

/**
 * Fetch the widgets for a user. The result includes the widget data
 * as well as the user-widget related data.
 * The user-widget data field gets serialized into a string.
 * @param {string} userId - The user id.
 * @return {Object[]}  Returns a list of object that with the widget and
 * the user data on the widget information.
 */

var getUserWidgets = Async(function (userId, enabled) {
  var userWidgets
  if (typeof enabled !== 'undefined') {
    userWidgets = Await(getUserWidgetsByEnabledState(userId, enabled))
  } else {
    userWidgets = Await(getAllUserWidgets(userId))
  }

  const keys = []
  const indexMapper = {}

  var widgetId
  for (var index in userWidgets) {
    widgetId = userWidgets[index].widgetId

    keys.push({
      id: widgetId
    })

    indexMapper[widgetId] = index
  }

  if (!keys.length) { return [] }

  var widgets = Await(Widget.getBatch(keys))
  Widget.sorted(widgets)

  const result = []
  var userWidget
  for (var i in widgets) {
    widgetId = widgets[i].id
    userWidget = userWidgets[indexMapper[widgetId]]

    result.push(
          getFullWidget(userWidget, widgets[i]))
  }
  return result
})

/**
 * Update widget data.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} data - The new widget data.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var updateUserWidgetData = Async(function (userId, widgetId, data) {
  const parsedData = JSON.parse(data)
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetData(userId, widgetId, parsedData))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget visible state.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {boolean} visible - The new visible state.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * widget.
 */
var updateUserWidgetVisibility = Async(function (userId, widgetId, visible) {
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetVisibility(userId, widgetId, visible))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget enabled state.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {boolean} enabled - The new enabled state.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * widget.
 */
var updateUserWidgetEnabled = Async(function (userId, widgetId, enabled) {
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetEnabled(userId, widgetId, enabled))
  return getFullWidget(userWidget, widget)
})

/**
 * Update widget config.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} config - The new widget config.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var updateUserWidgetConfig = Async(function (userId, widgetId, config) {
  const parsedConfig = JSON.parse(config)
  const widget = Await(getWidget(widgetId))
  const userWidget = Await(updateWidgetConfig(userId, widgetId, parsedConfig))
  return getFullWidget(userWidget, widget)
})

/**
 * Get all widgets.
 * @param {string} userId - The user id.
 * @param {string} widgetId - The widget id.
 * @param {Object} config - The new widget config.
 * @return {Promise<Widget>}  Returns a promise that resolves into a
 * Widget.
 */
var getAllWidgets = Async(function () {
  const widgets = Await(getWidgets())
  for (var i = 0; i < widgets.length; i++) {
    widgets[i].widgetId = widgets[i].id
    widgets[i].settings = JSON.stringify(widgets[i].settings)
  }
  Widget.sorted(widgets)
  return widgets
})

export {
  getUserWidgets,
  updateUserWidgetData,
  updateUserWidgetVisibility,
  updateUserWidgetEnabled,
  updateUserWidgetConfig,
  getAllWidgets
}
<|endoftext|>


<commit_before>/*    Copyright 2016 Rottiesoft LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';
var log;
var redis = require("redis");
var rclient = redis.createClient();
var sclient = redis.createClient();
sclient.setMaxListeners(0);

/*
 * Channels
 *
 *   DiscoveryEvent
 *     -> Notice:Detected
 *     -> Scan:Done
 *     -> DiscoveryStart
 *
 */

var instance = null;

module.exports = class {
    constructor(loglevel) {
        if (instance == null) {
            log = require("./logger.js")("MessageBus", loglevel);
            instance = this;
            this.callbacks = {};
            sclient.on("message", (channel, message) => {
                let m = JSON.parse(message);
                console.log("Reciving Msg:", m);
                let notified = 0;
                if (m.ip.length > 3 && this.callbacks[channel + '.' + m.type + "." + m.ip] != null) {
                    this.callbacks[channel + "." + m.type + "." + m.ip](channel, m.type, m.ip, m.msg);
                    notified += 1;
                }
                if (this.callbacks[channel + "." + m.type]) {
                    this.callbacks[channel + "." + m.type](channel, m.type, m.ip, m.msg);
                    notified += 1;
                }
                console.log("Notified ", notified);
            });
        }
        return instance;
    }

    publish(channel, type, ip, msg) {
        let o = {
            type: type,
            ip: ip,
            msg: msg
        };
        log.debug("MBus:Publish", channel, o);
        rclient.publish(channel, JSON.stringify(o));
    }

    subscribe(channel, type, ip, callback) {
        //log.debug("MBus:Subscribe",channel,type,ip);
        sclient.subscribe(channel);
        if (ip == null) {
            this.callbacks[channel + "." + type] = callback;
        } else {
            this.callbacks[channel + "." + type + "." + ip] = callback;
        }

    }
};
<commit_msg>emergency fix for crash<commit_after>/*    Copyright 2016 Rottiesoft LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';
var log;
var redis = require("redis");
var rclient = redis.createClient();
var sclient = redis.createClient();
sclient.setMaxListeners(0);

/*
 * Channels
 *
 *   DiscoveryEvent
 *     -> Notice:Detected
 *     -> Scan:Done
 *     -> DiscoveryStart
 *
 */

var instance = null;

module.exports = class {
    constructor(loglevel) {
        if (instance == null) {
            log = require("./logger.js")("MessageBus", loglevel);
            instance = this;
            this.callbacks = {};
            sclient.on("message", (channel, message) => {
                let m = JSON.parse(message);
                console.log("Reciving Msg:", m);
                let notified = 0;
                if (m.ip && m.ip.length > 3 && this.callbacks[channel + '.' + m.type + "." + m.ip] != null) {
                    this.callbacks[channel + "." + m.type + "." + m.ip](channel, m.type, m.ip, m.msg);
                    notified += 1;
                }
                if (this.callbacks[channel + "." + m.type]) {
                    this.callbacks[channel + "." + m.type](channel, m.type, m.ip, m.msg);
                    notified += 1;
                }
                console.log("Notified ", notified);
            });
        }
        return instance;
    }

    publish(channel, type, ip, msg) {
        let o = {
            type: type,
            ip: ip,
            msg: msg
        };
        log.debug("MBus:Publish", channel, o);
        rclient.publish(channel, JSON.stringify(o));
    }

    subscribe(channel, type, ip, callback) {
        //log.debug("MBus:Subscribe",channel,type,ip);
        sclient.subscribe(channel);
        if (ip == null) {
            this.callbacks[channel + "." + type] = callback;
        } else {
            this.callbacks[channel + "." + type + "." + ip] = callback;
        }

    }
};
<|endoftext|>


<commit_before>/**
 *
 * Tiramisu Unit Tests
 * ~~~~~~~~~~~~~~~~~~~
 *
 * Use the online version for working AJAX tests:
 * http://dl.dropbox.com/u/2060843/tiramisu/test/runtests.html
 *
 **/

module('Generic tests');

test('Should pass', function() {
    ok( true, "Ok");
});

test('1 plus 2', function() {
    equal(1 + 2, 3, 'equals 3');
});

test('[1, 2, 3] + [4, 5, 6]', function() {
    var a = [1, 2, 3];
    var b = [4, 5, 6];
    var sum = function(a, b) {
        var i, c = [];
        for (i = 0; i < a.length; i++) {
            c[i] = a[i] + b[i];
        }
        return c;
    }
    deepEqual(sum(a, b), [5, 7, 9], '= [5, 7, 9]');
});

module('Selector Tests');

test('Selecting “#headline”', function() {
    var rs = tiramisu.get('#headline');
    ok(rs, 'Should not be empty');
    equal(rs[0].id, "headline", "Should have an element node with id “headline”");
    equal(rs[0].innerHTML, "Tiramisu Tests", "Should contain “Tiramisu Tests”");
});

test('Selecting “h3 .red”', function() {
    var rs = tiramisu.get('h3 .red');
    ok(rs, 'Should not be empty');
    equal(rs[0].className, "red", "Should have an element node with class “red”");
    equal(rs[0].innerHTML, "tiramisu", "Should contain “tiramisu”");
});

test('Selecting “p.underlined”', function() {
    var rs = tiramisu.get('p.underlined');
    ok(rs, 'Should not be empty');
    equal(rs[0].className, "underlined", "Should have an element node with class “underlined”");
    equal(rs[0].nodeName, "P", "Should be a P node");
    equal(rs[0].innerHTML, "This text is underlined", "Should contain “This text is underlined”");
});

test('Selecting “h1#headline”', function() {
    var rs = tiramisu.get('h1#headline');
    ok(rs, 'Should not be empty');
    equal(rs[0].id, "headline", "Should have an element node with id “headline”");
    equal(rs[0].nodeName, "H1", "Should be an H1 node")
    equal(rs[0].innerHTML, "Tiramisu Tests", "Should contain “Tiramisu Tests”");
});

module('Browser detection tests');

test('Detect check', function() {
    var browser = tiramisu.detect('browser');
    ok(browser, 'Ok result : '+browser);
	//equal(tiramisu.detect('isChrome'), true, "We are into Chrome");
	//equal(tiramisu.detect('isFirefox'), true, "We are into Firefox");
	//equal(tiramisu.detect('isIE'), true, "We are into IE 'BOOOO'");
});

module('Selector list methods tests');

test('Calling “t.get("h2").each(function() { this.innerHTML = "Test"; });”', function() {
    var i;

    t.get('#qunit-fixture h2').each(function() {
        this.innerHTML = "Test";
    });

    var rs = t.get('#qunit-fixture h2');

    var all = true;
    for (i = 0; i < rs.length; i++) {
        if (rs[i].innerHTML !== "Test") { all = false; break; }
    }

    ok(all, "Should set all H2's content to “Test”");

});

test('Calling “t.get("ul li").each(function() { return this.innerHTML; });”', function() {
    t.get('ul li').each(function() {
        t.get('#append-here')[0].innerHTML += this.innerHTML;
    });

    equal(t.get("#append-here")[0].innerHTML, '
This text is underlined
Another lineOki-Doki.');
});

test('Calling “t.get("#qunit-fixture h2 .red").html()', function() {
    var rs = t.get('#qunit-fixture h2 .red').html();
    equal(rs, 'good', 'should return “good”');
});

test('Calling “t.get("#qunit-fixture h2 .red").html("bad")”', function() {
    t.get('#qunit-fixture h2 .red').html('bad');
    var rs = t.get('#qunit-fixture h2 .red').html();
    equal(rs, 'bad', 'should set element\'s HTML value to “bad”');
});

test('Calling “t.get("#qunit-fixture select").value()', function() {
    var rs = t.get('#qunit-fixture select').value();
    equal(rs, 'First.', 'should return “First.”');
});

module('CSS Manipulation tests');

test('Setting the color of a node to #f6f6f6', function() {
    t.get('#headline').css({
        'color': '#f6f6f6'
    });

    equal(t.get('#headline')[0].style['color'], 'rgb(246, 246, 246)');
});

module('AJAX module tests');

asyncTest('Calling a basic AJAX GET', function() {
    var success = function(result) {
        ok(true);
        ok(result, "the result is not undefined/null");
        equal(result, '
Hello GET
\n');
        start();
    };

    var error = function(errorType, message) {
        ok(false, "error called");
        start();
    }

    expect(3);

    tiramisu.ajax({
        'method': 'GET',
        'url': 'data/async.html',
        'success': success,
        'error': error
    });
});

asyncTest('Calling a basic AJAX POST', function() {
    var success = function(result) {
        ok(true);
        ok(result, "the result is not undefined/null");
        equal(result, '
Hello GET
\n');
        start();
    };

    var error = function(errorType, message) {
        ok(false, "error called");
        start();
    }

    expect(3);

    tiramisu.ajax({
        'method': 'POST',
        'url': 'data/async.html',
        'success': success,
        'error': error
    });
});
<commit_msg>Fixed test #2 for “value()”<commit_after>/**
 *
 * Tiramisu Unit Tests
 * ~~~~~~~~~~~~~~~~~~~
 *
 * Use the online version for working AJAX tests:
 * http://dl.dropbox.com/u/2060843/tiramisu/test/runtests.html
 *
 **/

module('Generic tests');

test('Should pass', function() {
    ok( true, "Ok");
});

test('1 plus 2', function() {
    equal(1 + 2, 3, 'equals 3');
});

test('[1, 2, 3] + [4, 5, 6]', function() {
    var a = [1, 2, 3];
    var b = [4, 5, 6];
    var sum = function(a, b) {
        var i, c = [];
        for (i = 0; i < a.length; i++) {
            c[i] = a[i] + b[i];
        }
        return c;
    }
    deepEqual(sum(a, b), [5, 7, 9], '= [5, 7, 9]');
});

module('Selector Tests');

test('Selecting “#headline”', function() {
    var rs = tiramisu.get('#headline');
    ok(rs, 'Should not be empty');
    equal(rs[0].id, "headline", "Should have an element node with id “headline”");
    equal(rs[0].innerHTML, "Tiramisu Tests", "Should contain “Tiramisu Tests”");
});

test('Selecting “h3 .red”', function() {
    var rs = tiramisu.get('h3 .red');
    ok(rs, 'Should not be empty');
    equal(rs[0].className, "red", "Should have an element node with class “red”");
    equal(rs[0].innerHTML, "tiramisu", "Should contain “tiramisu”");
});

test('Selecting “p.underlined”', function() {
    var rs = tiramisu.get('p.underlined');
    ok(rs, 'Should not be empty');
    equal(rs[0].className, "underlined", "Should have an element node with class “underlined”");
    equal(rs[0].nodeName, "P", "Should be a P node");
    equal(rs[0].innerHTML, "This text is underlined", "Should contain “This text is underlined”");
});

test('Selecting “h1#headline”', function() {
    var rs = tiramisu.get('h1#headline');
    ok(rs, 'Should not be empty');
    equal(rs[0].id, "headline", "Should have an element node with id “headline”");
    equal(rs[0].nodeName, "H1", "Should be an H1 node")
    equal(rs[0].innerHTML, "Tiramisu Tests", "Should contain “Tiramisu Tests”");
});

module('Browser detection tests');

test('Detect check', function() {
    var browser = tiramisu.detect('browser');
    ok(browser, 'Ok result : '+browser);
	//equal(tiramisu.detect('isChrome'), true, "We are into Chrome");
	//equal(tiramisu.detect('isFirefox'), true, "We are into Firefox");
	//equal(tiramisu.detect('isIE'), true, "We are into IE 'BOOOO'");
});

module('Selector list methods tests');

test('Calling “t.get("h2").each(function() { this.innerHTML = "Test"; });”', function() {
    var i;

    t.get('#qunit-fixture h2').each(function() {
        this.innerHTML = "Test";
    });

    var rs = t.get('#qunit-fixture h2');

    var all = true;
    for (i = 0; i < rs.length; i++) {
        if (rs[i].innerHTML !== "Test") { all = false; break; }
    }

    ok(all, "Should set all H2's content to “Test”");

});

test('Calling “t.get("ul li").each(function() { return this.innerHTML; });”', function() {
    t.get('ul li').each(function() {
        t.get('#append-here')[0].innerHTML += this.innerHTML;
    });

    equal(t.get("#append-here")[0].innerHTML, '
This text is underlined
Another lineOki-Doki.');
});

test('Calling “t.get("#qunit-fixture h2 .red").html()', function() {
    var rs = t.get('#qunit-fixture h2 .red').html();
    equal(rs, 'good', 'should return “good”');
});

test('Calling “t.get("#qunit-fixture h2 .red").html("bad")”', function() {
    t.get('#qunit-fixture h2 .red').html('bad');
    var rs = t.get('#qunit-fixture h2 .red').html();
    equal(rs, 'bad', 'should set element\'s HTML value to “bad”');
});

test('Calling “t.get("#qunit-fixture select").value()', function() {
    var rs = t.get('#qunit-fixture select').value();
    equal(rs, 'First.', 'should return “First.”');
});

test('Calling “t.get("#qunit-fixture select").value("Second.")', function() {
    t.get('#qunit-fixture select').value("Second.");
    var rs = t.get('#qunit-fixture select').value();
    equal(rs, 'Second.', 'should set element\'s HTML value to “Second”');
});

module('CSS Manipulation tests');

test('Setting the color of a node to #f6f6f6', function() {
    t.get('#headline').css({
        'color': '#f6f6f6'
    });

    equal(t.get('#headline')[0].style['color'], 'rgb(246, 246, 246)');
});

module('AJAX module tests');

asyncTest('Calling a basic AJAX GET', function() {
    var success = function(result) {
        ok(true);
        ok(result, "the result is not undefined/null");
        equal(result, '
Hello GET
\n');
        start();
    };

    var error = function(errorType, message) {
        ok(false, "error called");
        start();
    }

    expect(3);

    tiramisu.ajax({
        'method': 'GET',
        'url': 'data/async.html',
        'success': success,
        'error': error
    });
});

asyncTest('Calling a basic AJAX POST', function() {
    var success = function(result) {
        ok(true);
        ok(result, "the result is not undefined/null");
        equal(result, '
Hello GET
\n');
        start();
    };

    var error = function(errorType, message) {
        ok(false, "error called");
        start();
    }

    expect(3);

    tiramisu.ajax({
        'method': 'POST',
        'url': 'data/async.html',
        'success': success,
        'error': error
    });
});
<|endoftext|>