<commit_before>"use strict";

var _ = require('./lodash');
var Dom = require('./packages/dom');

var TYPE = 'application/vnd.sirtrevor+json'

var SelectionHandler = function(wrapper, mediator, editor) {
  this.wrapper = wrapper;
  this.mediator = mediator;
  this.editor = editor;
  this.options = editor.options;

  this.startIndex = this.endIndex = 0;
  this.selecting = false;

  this._bindFunctions();
  this._bindMediatedEvents();

  this.initialize();
};

Object.assign(SelectionHandler.prototype, require('./function-bind'), require('./mediated-events'), {

  eventNamespace: 'selection',

  bound: ['onCopy', 'onCut', 'onKeyDown', 'onMouseUp', 'onPaste'],

  mediatedEvents: {
    'start': 'start',
    'render': 'render',
    'complete': 'complete',
    'all': 'all',
    'copy': 'copy',
    'update': 'update',
    'delete': 'delete',
    'cancel': 'cancel',
    'block': 'block'
  },

  cancelSelectAll: function() {
    // Don't select if within an input field
    var editorEl1 = Dom.getClosest(document.activeElement, 'input');
    if (editorEl1 !== document.body) return true;

    var editorEl2 = Dom.getClosest(document.activeElement, '.st-outer');

    // Don't select all if focused on element outside of the editor.
    if (this.options.selectionLimitToEditor) {
      if (editorEl2 !== this.wrapper) return true;
    }
  },

  initialize: function() {
    if (!this.enabled()) return false;

    window.addEventListener("keydown", this.onKeyDown, false);
    window.addEventListener('mouseup', this.onMouseUp, false);
    document.addEventListener('copy', this.onCopy, false);

    if (this.options.selectionCut) {
      document.addEventListener('cut', this.onCut, false);
    }

    if (this.options.selectionPaste) {
      document.addEventListener('paste', this.onPaste, false);
    }
  },

  enabled: function() {
    return !!this.options.selectionCopy;
  },

  start: function(index, options = {}) {
    if (!this.enabled()) return false;

    options = Object.assign({ mouseEnabled: false }, options);

    this.startIndex = this.endIndex = index;
    this.selecting = true;

    if (options.mouseEnabled) {
      window.mouseDown = true;
      this.selecting = false;
      window.addEventListener("mousemove", this.onMouseMove);
    }

    this.mediator.trigger("selection:render");
  },

  startAtEnd: function() {
    this.start(this.editor.getBlocks().length - 1);
  },

  move: function(offset) {
    this.start(this.endIndex + offset);
  },

  onMouseMove: function() {},

  update: function(index) {
    if (index < 0 || index >= this.editor.getBlocks().length) return;
    this.endIndex = index;
    if (index !== this.startIndex) this.selecting = true;
    this.removeNativeSelection();
    this.mediator.trigger("selection:render");
  },

  expand(offset) {
    this.update(this.endIndex + offset);
  },

  expandToStart() {
    this.update(0);
  },

  expandToEnd() {
    this.update(this.editor.getBlocks().length - 1);
  },

  focusAtEnd() {
    const block = this.editor.getBlocks()[this.endIndex];
    block.el.scrollIntoView({ behavior: "smooth" });
  },

  complete: function() {
    window.removeEventListener("mousemove", this.onMouseMove);
  },

  all: function() {
    if (!this.enabled()) return false;

    this.removeNativeSelection();

    var blocks = this.editor.getBlocks();
    this.selecting = true;
    this.startIndex = 0;
    this.endIndex = blocks.length - 1;
    this.mediator.trigger("selection:render");
  },

  cancel: function() {
    window.mouseDown = false;
    this.selecting = false;
    this.render();
  },

  removeNativeSelection: function() {
    var sel = window.getSelection ? window.getSelection() : document.selection;
    if (sel) {
      if (sel.removeAllRanges) {
        sel.removeAllRanges();
      } else if (sel.empty) {
        sel.empty();
      }
    }
    document.activeElement && document.activeElement.blur();
  },

  render: function() {
    var visible = this.selecting;

    this.editor.getBlocks().forEach((block, idx) => {
      block.select(visible && this.indexSelected(idx));
    });
  },

  getClipboardData: function() {
    this.editor.getData();

    var htmlOutput = [];
    var textOutput = [];
    var dataOutput =  [];

    this.editor.getBlocks().forEach((block, idx) => {
      if (this.indexSelected(idx)) {
        var html = block.asClipboardHTML();
        var text = html;
        htmlOutput.push(html);
        textOutput.push(text);
        dataOutput.push(block.getData());
      }
    });

    return {
      html: htmlOutput.join(""),
      text: textOutput.join("\n\n"),
      data: dataOutput
    };
  },

  copy: function() {
    var copyArea = this.createFakeCopyArea();
    copyArea.innerHTML = this.getClipboardData().html;

    var selection = window.getSelection();
    var range = document.createRange();
    range.selectNodeContents(copyArea);
    selection.removeAllRanges();
    selection.addRange(range);

    try {
      document.execCommand('copy');
      copyArea.blur();
    }
    catch (err) {
      console.log("Copy could not be performed");
    }
  },

  createFakeCopyArea: function() {
    var copyArea = document.body.querySelector(".st-copy-area");
    if (!copyArea) {
      copyArea = Dom.createElement("div", {
        contenteditable: true,
        class: 'st-copy-area st-utils__hidden',
      });
      document.body.appendChild(copyArea);
    }
    return copyArea;
  },

  delete: function() {
    this.editor.getBlocks().forEach((block, idx) => {
      if (!this.indexSelected(idx)) return;
      this.mediator.trigger("block:remove", block.blockID, { focusOnNext: true });
    });
    this.cancel();
  },

  indexSelected: function(index) {
    return index >= this.getStartIndex() && index <= this.getEndIndex();
  },

  block: function(block) {
    var blockPosition = this.editor.blockManager.getBlockPosition(block.el);

    this.mediator.trigger("formatter:hide");
    this.removeNativeSelection();
    this.start(blockPosition);
  },

  getStartIndex: function() {
    return Math.min(this.startIndex, this.endIndex);
  },

  getEndIndex: function() {
    return Math.max(this.startIndex, this.endIndex);
  },

  getStartBlock: function() {
    return this.editor.getBlocks()[this.getStartIndex()];
  },

  getEndBlock: function() {
    return this.editor.getBlocks()[this.getEndIndex()];
  },

  onKeyDown: function(ev) {
    ev = ev || window.event;
    var ctrlKey = ev.ctrlKey || ev.metaKey;
    var key = ev.key;

    if (this.selecting && key === "Backspace") {
      ev.preventDefault();
      this.delete();
    } else if (ctrlKey && key === "a") {
      if (!this.selecting && this.cancelSelectAll()) return;
      ev.preventDefault();
      this.mediator.trigger("selection:all");
    } else if (this.selecting && ["Down", "ArrowDown"].indexOf(key) > -1) {
      ev.preventDefault();
      if (ev.shiftKey && ev.altKey) this.expandToEnd();
      else if (ev.shiftKey) this.expand(1);
      else if (ev.altKey) this.startAtEnd();
      else {
        this.cancel();
        this.mediator.trigger("block:focusNext", this.getEndBlock().blockID, { force: true });
        return;
      }
      this.focusAtEnd();
    } else if (this.selecting && ["Up", "ArrowUp"].indexOf(key) > -1) {
      ev.preventDefault();
      if (ev.shiftKey && ev.altKey) this.expandToStart();
      else if (ev.shiftKey) this.expand(-1);
      else if (ev.altKey) this.start(0);
      else {
        this.cancel();
        this.mediator.trigger("block:focusPrevious", this.getStartBlock().blockID, { force: true });
        return;
      }
      this.focusAtEnd();
    }
  },

  onMouseUp: function() {
    if (!window.mouseDown) {
      this.mediator.trigger("selection:complete");
      this.mediator.trigger("selection:cancel");
      return;
    }

    window.mouseDown = false;
    this.mediator.trigger("selection:complete");
    this.mediator.trigger("selection:render");
  },

  copySelection: function(ev) {
    var content = this.getClipboardData();

    ev.clipboardData.setData(TYPE, JSON.stringify(content.data));
    ev.clipboardData.setData('text/html', content.html);
    ev.clipboardData.setData('text/plain', content.text);
    ev.preventDefault();
  },

  onCopy: function(ev) {
    if (!this.selecting) return;

    this.copySelection(ev);
  },

  onCut: function(ev) {
    if (!this.selecting) return;

    this.copySelection(ev);
    this.delete();
  },

  onPaste: function(ev) {
    // TODO
  }
});

module.exports = SelectionHandler;
<commit_msg>Selectively cut current selection on keypress<commit_after>"use strict";

var _ = require('./lodash');
var Dom = require('./packages/dom');

var TYPE = 'application/vnd.sirtrevor+json'

var SelectionHandler = function(wrapper, mediator, editor) {
  this.wrapper = wrapper;
  this.mediator = mediator;
  this.editor = editor;
  this.options = editor.options;

  this.startIndex = this.endIndex = 0;
  this.selecting = false;

  this._bindFunctions();
  this._bindMediatedEvents();

  this.initialize();
};

Object.assign(SelectionHandler.prototype, require('./function-bind'), require('./mediated-events'), {

  eventNamespace: 'selection',

  bound: ['onCopy', 'onCut', 'onKeyDown', 'onMouseUp', 'onPaste'],

  mediatedEvents: {
    'start': 'start',
    'render': 'render',
    'complete': 'complete',
    'all': 'all',
    'copy': 'copy',
    'update': 'update',
    'delete': 'delete',
    'cancel': 'cancel',
    'block': 'block'
  },

  cancelSelectAll: function() {
    // Don't select if within an input field
    var editorEl1 = Dom.getClosest(document.activeElement, 'input');
    if (editorEl1 !== document.body) return true;

    var editorEl2 = Dom.getClosest(document.activeElement, '.st-outer');

    // Don't select all if focused on element outside of the editor.
    if (this.options.selectionLimitToEditor) {
      if (editorEl2 !== this.wrapper) return true;
    }
  },

  initialize: function() {
    if (!this.enabled()) return false;

    window.addEventListener("keydown", this.onKeyDown, false);
    window.addEventListener('mouseup', this.onMouseUp, false);
    document.addEventListener('copy', this.onCopy, false);

    if (this.options.selectionCut) {
      document.addEventListener('cut', this.onCut, false);
    }

    if (this.options.selectionPaste) {
      document.addEventListener('paste', this.onPaste, false);
    }
  },

  enabled: function() {
    return !!this.options.selectionCopy;
  },

  start: function(index, options = {}) {
    if (!this.enabled()) return false;

    options = Object.assign({ mouseEnabled: false }, options);

    this.startIndex = this.endIndex = index;
    this.selecting = true;

    if (options.mouseEnabled) {
      window.mouseDown = true;
      this.selecting = false;
      window.addEventListener("mousemove", this.onMouseMove);
    }

    this.mediator.trigger("selection:render");
  },

  startAtEnd: function() {
    this.start(this.editor.getBlocks().length - 1);
  },

  move: function(offset) {
    this.start(this.endIndex + offset);
  },

  onMouseMove: function() {},

  update: function(index) {
    if (index < 0 || index >= this.editor.getBlocks().length) return;
    this.endIndex = index;
    if (index !== this.startIndex) this.selecting = true;
    this.removeNativeSelection();
    this.mediator.trigger("selection:render");
  },

  expand(offset) {
    this.update(this.endIndex + offset);
  },

  expandToStart() {
    this.update(0);
  },

  expandToEnd() {
    this.update(this.editor.getBlocks().length - 1);
  },

  focusAtEnd() {
    const block = this.editor.getBlocks()[this.endIndex];
    block.el.scrollIntoView({ behavior: "smooth" });
  },

  complete: function() {
    window.removeEventListener("mousemove", this.onMouseMove);
  },

  all: function() {
    if (!this.enabled()) return false;

    this.removeNativeSelection();

    var blocks = this.editor.getBlocks();
    this.selecting = true;
    this.startIndex = 0;
    this.endIndex = blocks.length - 1;
    this.mediator.trigger("selection:render");
  },

  cancel: function() {
    window.mouseDown = false;
    this.selecting = false;
    this.render();
  },

  removeNativeSelection: function() {
    var sel = window.getSelection ? window.getSelection() : document.selection;
    if (sel) {
      if (sel.removeAllRanges) {
        sel.removeAllRanges();
      } else if (sel.empty) {
        sel.empty();
      }
    }
    document.activeElement && document.activeElement.blur();
  },

  render: function() {
    var visible = this.selecting;

    this.editor.getBlocks().forEach((block, idx) => {
      block.select(visible && this.indexSelected(idx));
    });
  },

  getClipboardData: function() {
    this.editor.getData();

    var htmlOutput = [];
    var textOutput = [];
    var dataOutput =  [];

    this.editor.getBlocks().forEach((block, idx) => {
      if (this.indexSelected(idx)) {
        var html = block.asClipboardHTML();
        var text = html;
        htmlOutput.push(html);
        textOutput.push(text);
        dataOutput.push(block.getData());
      }
    });

    return {
      html: htmlOutput.join(""),
      text: textOutput.join("\n\n"),
      data: dataOutput
    };
  },

  copy: function() {
    var copyArea = this.createFakeCopyArea();
    copyArea.innerHTML = this.getClipboardData().html;

    var selection = window.getSelection();
    var range = document.createRange();
    range.selectNodeContents(copyArea);
    selection.removeAllRanges();
    selection.addRange(range);

    try {
      document.execCommand('copy');
      copyArea.blur();
    }
    catch (err) {
      console.log("Copy could not be performed");
    }
  },

  createFakeCopyArea: function() {
    var copyArea = document.body.querySelector(".st-copy-area");
    if (!copyArea) {
      copyArea = Dom.createElement("div", {
        contenteditable: true,
        class: 'st-copy-area st-utils__hidden',
      });
      document.body.appendChild(copyArea);
    }
    return copyArea;
  },

  delete: function() {
    this.editor.getBlocks().forEach((block, idx) => {
      if (!this.indexSelected(idx)) return;
      this.mediator.trigger("block:remove", block.blockID, { focusOnNext: true });
    });
    this.cancel();
  },

  indexSelected: function(index) {
    return index >= this.getStartIndex() && index <= this.getEndIndex();
  },

  block: function(block) {
    var blockPosition = this.editor.blockManager.getBlockPosition(block.el);

    this.mediator.trigger("formatter:hide");
    this.removeNativeSelection();
    this.start(blockPosition);
  },

  getStartIndex: function() {
    return Math.min(this.startIndex, this.endIndex);
  },

  getEndIndex: function() {
    return Math.max(this.startIndex, this.endIndex);
  },

  getStartBlock: function() {
    return this.editor.getBlocks()[this.getStartIndex()];
  },

  getEndBlock: function() {
    return this.editor.getBlocks()[this.getEndIndex()];
  },

  onKeyDown: function(ev) {
    ev = ev || window.event;
    var ctrlKey = ev.ctrlKey || ev.metaKey;
    var key = ev.key;

    if (this.selecting && key === "Backspace") {
      ev.preventDefault();
      this.delete();
    } else if (ctrlKey && key === "a") {
      if (!this.selecting && this.cancelSelectAll()) return;
      ev.preventDefault();
      this.mediator.trigger("selection:all");
    } else if (this.selecting) {
      if (["Down", "ArrowDown"].indexOf(key) > -1) {
        ev.preventDefault();
        if (ev.shiftKey && ev.altKey) this.expandToEnd();
        else if (ev.shiftKey) this.expand(1);
        else if (ev.altKey) this.startAtEnd();
        else {
          this.cancel();
          this.mediator.trigger("block:focusNext", this.getEndBlock().blockID, { force: true });
          return;
        }
        this.focusAtEnd();
      } else if (["Up", "ArrowUp"].indexOf(key) > -1) {
        ev.preventDefault();
        if (ev.shiftKey && ev.altKey) this.expandToStart();
        else if (ev.shiftKey) this.expand(-1);
        else if (ev.altKey) this.start(0);
        else {
          this.cancel();
          this.mediator.trigger("block:focusPrevious", this.getStartBlock().blockID, { force: true });
          return;
        }
        this.focusAtEnd();
      } else if (["Shift", "Control", "Meta", "Alt"].indexOf(key) === -1) {
        const nextBlock = this.editor.getBlocks()[this.getEndIndex() + 1];
        this.delete();
        if (nextBlock) {
          this.mediator.trigger("block:createBefore", "text", "", nextBlock, { autoFocus: true });
        } else {
          this.mediator.trigger("block:create", "text", "", { autoFocus: true });
        }
      }
    }
  },

  onMouseUp: function() {
    if (!window.mouseDown) {
      this.mediator.trigger("selection:complete");
      this.mediator.trigger("selection:cancel");
      return;
    }

    window.mouseDown = false;
    this.mediator.trigger("selection:complete");
    this.mediator.trigger("selection:render");
  },

  copySelection: function(ev) {
    var content = this.getClipboardData();

    ev.clipboardData.setData(TYPE, JSON.stringify(content.data));
    ev.clipboardData.setData('text/html', content.html);
    ev.clipboardData.setData('text/plain', content.text);
    ev.preventDefault();
  },

  onCopy: function(ev) {
    if (!this.selecting) return;

    this.copySelection(ev);
  },

  onCut: function(ev) {
    if (!this.selecting) return;

    this.copySelection(ev);
    this.delete();
  },

  onPaste: function(ev) {
    // TODO
  }
});

module.exports = SelectionHandler;
<|endoftext|>


<commit_before>/* Copyright G. Hemingway 2015 */
'use strict';

var http = require('http');
var path = require('path');
var io = require('socket.io');
var ioSession = require('socket.io-express-session');
var expSession = require('express-session');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var jade = require('jade');
var CoreServer = require('./core_server');
var util = require('util');
var fs = require('fs');

/************************* Support Site *********************************/

var COOKIE_SECRET = 'imhotep';
var app;

function APIServer() {
  CoreServer.call(this);
  // Setup the session
  this.session = expSession({
    cookie: {httpOnly: false},
    secret: COOKIE_SECRET,
    resave: false,
    saveUninitialized: false,
  });
  // Set up the rest
  this._setExpress();
  this._setSocket();
  var self = this;
  this._setRoutes(function() {
    self._setSite();
  });
}
util.inherits(APIServer, CoreServer);

/*
 * Setup Express Frontend
 */
// Calculate sha1 hash of the file object
APIServer.prototype._setExpress = function() {
  this.express = express();
  this.express.disable('x-powered-by');
  this.express.use(
    require('serve-favicon')
    (__dirname + '/../../public/img/favicon.ico')
  );
  this.express.use(bodyParser.urlencoded({extended: true}));
  this.express.use(bodyParser.json());
  this.express.use(cookieParser('imhotep'));
  this.express.use(this.session);
  this.express.use(require('method-override')());
  this.express.use(
    require('serve-static')
    (path.join(__dirname, '/../../public'))
  );
  this.express.use(require('morgan')('tiny'));
  this.express.engine('jade', jade.__express);
  this.express.set('views', path.join(__dirname, '/views'));
  // Create the core router
  this.router = express.Router();
  this.express.use(this.router);
};

/*
 * Setup the socket server
 */
APIServer.prototype._setSocket = function() {
  // Socket server
  this.server = http.Server(this.express);
  this.ioServer = io(this.server, {});
  this.ioServer.use(ioSession(this.session));
  this.ioServer.on('connection', function(socket) {
    socket.on('disconnect', function() {});
  });
};

/*
 * Core API Routes
 */
APIServer.prototype._setRoutes = function(cb) {
  var self = this;
  require('./api/v3/step')(self, function() {
    require('./api/v3/state')(self, function() {
      require('./api/v3/tool')(self, function() {
        require('./api/v3/tolerances')(self, function() {
          require('./api/v3/geometry')(self, function() {
            if (cb) {
              cb();
            }
          });
        });
      });
    });
  });
};

/*
 * Static Site
 */
APIServer.prototype._setSite = function() {
  var self = this;
  var endpoint = this.config.host ? this.config.protocol + '://' + this.config.host + ':' + app.port : '';
  var services = {
    apiEndpoint: endpoint,
    socket: "",
    version: '/v3',
    machine: self.machinetool,
  };
  // Serve the root client framework - customized as needed
  var _serveRoot = function (req, res) {
    var change = fs.readFileSync("CHANGELOG.md", "utf8");
    var appConfig = {
      title: 'NC.js',
      source: '/js/main.js',
      services: services,
      config: self.config.client,
      changelog: change,
    };
    res.render('base.jade', appConfig);
  };

  this.router.get('/', _serveRoot);
  this.router.get('*', function(req, res) {
    res.status(404).send('404 Error: Not Found');
  });
};

/*
 * Primary run
 */
APIServer.prototype.run = function() {
  var self = this;
  this.server.listen(app.port, function() {
    self.logger.info('CAD.js API Server listening on: ' + app.port);
  });

  process.openStdin().addListener('data', function(inputData) {
    if ((self.server !== null) && (self.server !== 'undefined')) {
      inputData=inputData.toString().trim().toLowerCase();
      if (inputData.length === 0) {
        process.exit(0);
      }
    }
  });
};

/***************************** Run the server *********************************/

app = new APIServer();
app.run();
<commit_msg>style guide<commit_after>/* Copyright G. Hemingway 2015 */
'use strict';

var http = require('http');
var path = require('path');
var io = require('socket.io');
var ioSession = require('socket.io-express-session');
var expSession = require('express-session');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var jade = require('jade');
var CoreServer = require('./core_server');
var util = require('util');
var fs = require('fs');

/************************* Support Site *********************************/

var COOKIE_SECRET = 'imhotep';
var app;

function APIServer() {
  CoreServer.call(this);
  // Setup the session
  this.session = expSession({
    cookie: {httpOnly: false},
    secret: COOKIE_SECRET,
    resave: false,
    saveUninitialized: false,
  });
  // Set up the rest
  this._setExpress();
  this._setSocket();
  var self = this;
  this._setRoutes(function() {
    self._setSite();
  });
}
util.inherits(APIServer, CoreServer);

/*
 * Setup Express Frontend
 */
// Calculate sha1 hash of the file object
APIServer.prototype._setExpress = function() {
  this.express = express();
  this.express.disable('x-powered-by');
  this.express.use(
    require('serve-favicon')
    (__dirname + '/../../public/img/favicon.ico')
  );
  this.express.use(bodyParser.urlencoded({extended: true}));
  this.express.use(bodyParser.json());
  this.express.use(cookieParser('imhotep'));
  this.express.use(this.session);
  this.express.use(require('method-override')());
  this.express.use(
    require('serve-static')
    (path.join(__dirname, '/../../public'))
  );
  this.express.use(require('morgan')('tiny'));
  this.express.engine('jade', jade.__express);
  this.express.set('views', path.join(__dirname, '/views'));
  // Create the core router
  this.router = express.Router();
  this.express.use(this.router);
};

/*
 * Setup the socket server
 */
APIServer.prototype._setSocket = function() {
  // Socket server
  this.server = http.Server(this.express);
  this.ioServer = io(this.server, {});
  this.ioServer.use(ioSession(this.session));
  this.ioServer.on('connection', function(socket) {
    socket.on('disconnect', function() {});
  });
};

/*
 * Core API Routes
 */
APIServer.prototype._setRoutes = function(cb) {
  var self = this;
  require('./api/v3/step')(self, function() {
    require('./api/v3/state')(self, function() {
      require('./api/v3/tool')(self, function() {
        require('./api/v3/tolerances')(self, function() {
          require('./api/v3/geometry')(self, function() {
            if (cb) {
              cb();
            }
          });
        });
      });
    });
  });
};

/*
 * Static Site
 */
APIServer.prototype._setSite = function() {
  var self = this;
  var endpoint = this.config.host ? this.config.protocol + '://' + this.config.host + ':' + app.port : '';
  var services = {
    apiEndpoint: endpoint,
    socket: '',
    version: '/v3',
    machine: self.machinetool,
  };
  // Serve the root client framework - customized as needed
  var _serveRoot = function (req, res) {
    var change = fs.readFileSync('CHANGELOG.md', 'utf8');
    var appConfig = {
      title: 'NC.js',
      source: '/js/main.js',
      services: services,
      config: self.config.client,
      changelog: change,
    };
    res.render('base.jade', appConfig);
  };

  this.router.get('/', _serveRoot);
  this.router.get('*', function(req, res) {
    res.status(404).send('404 Error: Not Found');
  });
};

/*
 * Primary run
 */
APIServer.prototype.run = function() {
  var self = this;
  this.server.listen(app.port, function() {
    self.logger.info('CAD.js API Server listening on: ' + app.port);
  });

  process.openStdin().addListener('data', function(inputData) {
    if ((self.server !== null) && (self.server !== 'undefined')) {
      inputData=inputData.toString().trim().toLowerCase();
      if (inputData.length === 0) {
        process.exit(0);
      }
    }
  });
};

/***************************** Run the server *********************************/

app = new APIServer();
app.run();
<|endoftext|>


<commit_before>// =============================================================================
// This is a routing file that deals with everything related to the API
// =============================================================================
var router = require('express').Router();

// Import all models
var User = require('../models/User');
var Mail = require('../models/Mail.js');

// GET /api/user return all users
router.get('/users', function(req, res, next) {
  User.find(function(err, users) {
    if (err) return next(err);
    res.json(users);
  });
});

// POST /api/user create a new user in the database
router.post('/users', function(req, res, next) {
  User.create(req.body, function(err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

// GET /api/user/:id return the user with the specified id
router.get('/users/:id', function(req, res, next) {
  User.findById(req.params.id, function(err, user) {
    if (err) return next(err);
    res.json(user);
  });
});

// DELETE /api/user/:id delete the user with the specified id
router.delete('/users/:id', function(req, res, next) {
  User.findByIdAndRemove(req.params.id, req.body, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

router.post('/mail', function(req, res, next) {
  console.log('Received Mail');

  var form = new multiparty.Form();

  form.parse(req, function(err, fields, files) {
    mail = JSON.parse(fields.mailinMsg[0]);

    simpleMail = {from: mail.from.address, text: mail.text};
    console.log(simpleMail);
    Mail.create(simpleMail);

    console.log(mail.text);
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });
});

// Check to see if a user is logged in, if not, redirect them
function loggedIn(req, res, next) {
  if (req.user) {
    next();
  } else {
    console.log('not logged in');
    res.redirect('/');
  }
}

module.exports = router;
<commit_msg>Added head request to api<commit_after>// =============================================================================
// This is a routing file that deals with everything related to the API
// =============================================================================
var router = require('express').Router();

// Import all models
var User = require('../models/User');
var Mail = require('../models/Mail.js');

// GET /api/user return all users
router.get('/users', function(req, res, next) {
  User.find(function(err, users) {
    if (err) return next(err);
    res.json(users);
  });
});

// POST /api/user create a new user in the database
router.post('/users', function(req, res, next) {
  User.create(req.body, function(err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

// GET /api/user/:id return the user with the specified id
router.get('/users/:id', function(req, res, next) {
  User.findById(req.params.id, function(err, user) {
    if (err) return next(err);
    res.json(user);
  });
});

// DELETE /api/user/:id delete the user with the specified id
router.delete('/users/:id', function(req, res, next) {
  User.findByIdAndRemove(req.params.id, req.body, function (err, post) {
    if (err) return next(err);
    res.json(post);
  });
});

router.head('/mail', function(req, res, next) {
  console.log('mail HEAD request');
  res.sendStatus(200);
});

router.post('/mail', function(req, res, next) {
  console.log('Received Mail');

  var form = new multiparty.Form();

  form.parse(req, function(err, fields, files) {
    mail = JSON.parse(fields.mailinMsg[0]);

    simpleMail = {from: mail.from.address, text: mail.text};
    console.log(simpleMail);
    Mail.create(simpleMail);

    console.log(mail.text);
    res.writeHead(200, {'content-type': 'text/plain'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));
  });
});

// Check to see if a user is logged in, if not, redirect them
function loggedIn(req, res, next) {
  if (req.user) {
    next();
  } else {
    console.log('not logged in');
    res.redirect('/');
  }
}

module.exports = router;
<|endoftext|>


<commit_before>"use strict";
const proxy = require('koa-proxy');
const _ = require('lodash');
const error = require('../error');
const couch = require('../middleware/couch');

const router = require('koa-router')();

exports.init = function(config) {
    // Get all entries by user
    router.get('/:database/entries/all', couch.allEntries);

    // Get a document
    router.get('/:database/:id', couch.getDocumentByUuid);

    // Get a view
    router.get('/:database/_design/app/_view/:view', couch.queryViewByUser);

    // Create new document
    router.put('/:database/:id', couch.newEntry);

    return router;
};
<commit_msg>prefix database API with 'db'<commit_after>"use strict";
const proxy = require('koa-proxy');
const _ = require('lodash');
const error = require('../error');
const couch = require('../middleware/couch');

const router = require('koa-router')({
    prefix: '/db'
});

exports.init = function(config) {
    // Get all entries by user
    router.get('/:database/entries/all', couch.allEntries);

    // Get a document
    router.get('/:database/:id', couch.getDocumentByUuid);

    // Get a view
    router.get('/:database/_design/app/_view/:view', couch.queryViewByUser);

    // Create new document
    router.put('/:database/:id', couch.newEntry);

    return router;
};
<|endoftext|>


<commit_before>const {gray, yellow, red, blue} = require('chalk');
const readline = require('../lib/readline/readline');
const EventEmitter = require('events');
const {Readable} = require('stream');
const boxen = require('boxen');

module.exports = class Terminal extends EventEmitter {

  static create(options) {
    const readlineInterface = readline.createInterface({
      input: options.interactive ? process.stdin : new Readable({read: () => undefined}),
      output: process.stdout,
      prompt: options.interactive ? blue('>> ') : '',
      historySize: 0
    });
    return new Terminal(console, readlineInterface);
  }

  /**
   * @param {Console} console
   * @param {readline.Interface} readlineInterface
   */
  constructor(console, readlineInterface) {
    super();
    if (!console) {
      throw new Error('console is missing');
    }
    if (!readlineInterface) {
      throw new Error('readlineInterface is missing');
    }
    this._line = null;
    this._console = console;
    this._readline = readlineInterface;
    this._promptEnabled = false;
    this._ignoreInput = true;
    this._readline.on('close', () => this.emit('close'));
    this._readline.on('line', line => {
      if (this._ignoreInput) {
        return;
      }
      if (line) {
        this.emit('line', line);
      } else {
        this.clearInput();
      }
    });
    this._readline.input.prependListener('keypress', (_ev, key) => {
      if (key.name === 'return') {
        this._replacePromptInLineWith(gray('>> '));
      }
    });
    this._readline.input.on('keypress', (ev, key) => {
      if (!this._ignoreInput) {
        this.emit('keypress', key);
      }
    });
  }

  clearInput(line = '') {
    if (line && !this.promptEnabled) {
      throw new Error('Prompt disabled');
    }
    this._clearLine();
    this._line = line;
    this._restorePrompt();
  }

  set promptEnabled(enabled) {
    if (enabled && !this._promptEnabled) {
      this._promptEnabled = true;
      this._restorePrompt();
    } else if (!enabled && this._promptEnabled) {
      this._hidePrompt();
      this._promptEnabled = false;
    }
  }

  get promptEnabled() {
    return this._promptEnabled;
  }

  infoBlock({title, body}) {
    const indentedBody = body.split(/\n/).map(line => `  ${blue(line)}`).join('\n');
    this._hidePrompt();
    this._console.log(boxen(
      yellow(title) + '\n' + indentedBody
      , {padding: {left: 1, right: 1}, borderStyle: 'round'}));
    this._restorePrompt();
  }

  message(text) {
    this._hidePrompt();
    this._console.log(yellow(text));
    this._restorePrompt();
  }

  output(text) {
    this._hidePrompt();
    this._console.log(text);
    this._restorePrompt();
  }

  log(text) {
    this._hidePrompt();
    this._console.log(`  ${text}`);
    this._restorePrompt();
  }

  info(text) {
    this._hidePrompt();
    this._console.log(blue(`>  ${text}`));
    this._restorePrompt();
  }

  debug(text) {
    this._hidePrompt();
    this._console.log(`   ${text}`);
    this._restorePrompt();
  }

  warn(text) {
    this._hidePrompt();
    this._console.log(yellow(`>  ${text}`));
    this._restorePrompt();
  }

  error(text) {
    this._hidePrompt();
    this._console.error(red(`>  ${text}`));
    this._restorePrompt();
  }

  returnValue(text) {
    this._hidePrompt();
    this._console.log(`${gray('<-')} ${text}`);
    this._restorePrompt();
  }

  _replacePromptInLineWith(prefix) {
    this._clearLine();
    this._readline.output.write(prefix + this._readline.line);
  }

  _hidePrompt() {
    if (this._promptEnabled) {
      if (this._line === null) {
        this._line = this._readline.line;
      }
      this._ignoreInput = true;
      this._clearLine();
    }
  }

  _restorePrompt() {
    if (this._promptEnabled) {
      const command = this._line || '';
      this._readline.line = command;
      this._readline.cursor = command.length;
      this._readline.prompt(true);
      this._ignoreInput = false;
      this._line = null;
    }
  }

  _clearLine() {
    this._readline.output.write('\x1b[2K\r');
  }

};
<commit_msg>Fix unexpected character in log output<commit_after>const {gray, yellow, red, blue} = require('chalk');
const readline = require('../lib/readline/readline');
const EventEmitter = require('events');
const {Readable} = require('stream');
const boxen = require('boxen');

module.exports = class Terminal extends EventEmitter {

  static create(options) {
    const readlineInterface = readline.createInterface({
      input: options.interactive ? process.stdin : new Readable({read: () => undefined}),
      output: process.stdout,
      prompt: options.interactive ? blue('>> ') : '',
      historySize: 0
    });
    return new Terminal(console, readlineInterface);
  }

  /**
   * @param {Console} console
   * @param {readline.Interface} readlineInterface
   */
  constructor(console, readlineInterface) {
    super();
    if (!console) {
      throw new Error('console is missing');
    }
    if (!readlineInterface) {
      throw new Error('readlineInterface is missing');
    }
    this._line = null;
    this._console = console;
    this._readline = readlineInterface;
    this._promptEnabled = false;
    this._ignoreInput = true;
    this._readline.on('close', () => this.emit('close'));
    this._readline.on('line', line => {
      if (this._ignoreInput) {
        return;
      }
      if (line) {
        this.emit('line', line);
      } else {
        this.clearInput();
      }
    });
    this._readline.input.prependListener('keypress', (_ev, key) => {
      if (key.name === 'return') {
        this._replacePromptInLineWith(gray('>> '));
      }
    });
    this._readline.input.on('keypress', (ev, key) => {
      if (!this._ignoreInput) {
        this.emit('keypress', key);
      }
    });
  }

  clearInput(line = '') {
    if (line && !this.promptEnabled) {
      throw new Error('Prompt disabled');
    }
    this._clearLine();
    this._line = line;
    this._restorePrompt();
  }

  set promptEnabled(enabled) {
    if (enabled && !this._promptEnabled) {
      this._promptEnabled = true;
      this._restorePrompt();
    } else if (!enabled && this._promptEnabled) {
      this._hidePrompt();
      this._promptEnabled = false;
    }
  }

  get promptEnabled() {
    return this._promptEnabled;
  }

  infoBlock({title, body}) {
    const indentedBody = body.split(/\n/).map(line => `  ${blue(line)}`).join('\n');
    this._hidePrompt();
    this._console.log(boxen(
      yellow(title) + '\n' + indentedBody
      , {padding: {left: 1, right: 1}, borderStyle: 'round'}));
    this._restorePrompt();
  }

  message(text) {
    this._hidePrompt();
    this._console.log(yellow(text));
    this._restorePrompt();
  }

  output(text) {
    this._hidePrompt();
    this._console.log(text);
    this._restorePrompt();
  }

  log(text) {
    this._hidePrompt();
    this._console.log(`  ${text}`);
    this._restorePrompt();
  }

  info(text) {
    this._hidePrompt();
    this._console.log(blue(`>  ${text}`));
    this._restorePrompt();
  }

  debug(text) {
    this._hidePrompt();
    this._console.log(`   ${text}`);
    this._restorePrompt();
  }

  warn(text) {
    this._hidePrompt();
    this._console.log(yellow(`>  ${text}`));
    this._restorePrompt();
  }

  error(text) {
    this._hidePrompt();
    this._console.error(red(`>  ${text}`));
    this._restorePrompt();
  }

  returnValue(text) {
    this._hidePrompt();
    this._console.log(`${gray('<-')} ${text}`);
    this._restorePrompt();
  }

  _replacePromptInLineWith(prefix) {
    this._clearLine();
    this._readline.output.write(prefix + this._readline.line);
  }

  _hidePrompt() {
    if (this._promptEnabled) {
      if (this._line === null) {
        this._line = this._readline.line;
      }
      this._ignoreInput = true;
      this._clearLine();
    }
  }

  _restorePrompt() {
    if (this._promptEnabled) {
      const command = this._line || '';
      this._readline.line = command;
      this._readline.cursor = command.length;
      this._readline.prompt(true);
      this._ignoreInput = false;
      this._line = null;
    }
  }

  _clearLine() {
    this._readline.output.write('\x1b[2K\r');
  }

};
<|endoftext|>


<commit_before>var cluster = require('cluster');
var Promise = require('bluebird');

var Server = require('./Server');
var Pid = require('../lib/Pid');
var logStartTime = require('../lib/logStartTime');

/**
 * Fork a new worker and listen for log any errors
 * @param    {Ceres}    ceres
 */
function forkWorker(ceres, env) {
  env = typeof env === 'object' ? env : {};
  var worker = cluster.fork(env);
  worker.on('error', function(err) {
    ceres.log._ceres.error(err);
  });
}

/**
 * Make sure everything is setup the way we need to be before we start Listening
 * @param  {Ceres}    ceres
 * @return {Promise}
 */
module.exports = function(ceres) {
  // processManagement
  return this
    .connect.call(this, ceres)
    .then(function listen() {
      return new Promise(function(resolve, reject){
        try {
          if (ceres.config.pid) {
            // Setup Pid if we're configure
            ceres.pid = new Pid(ceres.config.pid);
            ceres.log._ceres.silly('pid %d written to %s', ceres.pid.id, ceres.pid.options.path);
          }

          if (!ceres.config.instances || ceres.config.instances === 1) {
            // If we only have a single instance no need to run the cluster
            Server.call(ceres, ceres).listen(ceres.config.port, function(){
              logStartTime('Server took %ds to start listening', ceres);
              ceres.log._ceres.info('Listening on %d (%s)', ceres.config.port, ceres.config.env);
              resolve();
            });
            return;
          }

          if (cluster.isMaster) {
            // Fork children
            for (var i = 0; i < ceres.config.instances; i++) {
              forkWorker(ceres, { CERES_UNIQUE_ID: i });
            }

            // Attempt to restart children that crash
            cluster.on('exit', function(worker, code, signal){
              if (signal) {
                ceres.log._ceres.info('worker %s was killed by %s', worker.process.pid, signal);
              } else if (code !== 0) {
                ceres.log._ceres.error('worker %s exited with %s. Starting new worker...', worker.process.pid, code, {
                  exitCode: code,
                  argv: process.argv
                });
                try {
                  forkWorker(ceres, { CERES_UNIQUE_ID: worker.process.env.CERES_UNIQUE_ID });
                } catch(err) {
                  // If we fail to form then just exit to prevent infinite loop
                  ceres.log._ceres.error(err, function(e) {
                    ceres.log._ceres.error(e);
                    // Just kill the main process because this normally indices a bigger problem
                    process.exit(1);
                  });
                }
              }
            });

            ceres.log._ceres.info('Master spawned %d children', ceres.config.instances);
          } else {
            // Setup the server
            Server.call(ceres, ceres).listen(ceres.config.port, function(){
              logStartTime('Child ready after %ds', ceres);
              ceres.log._ceres.info('Listening on %d (%s)', ceres.config.port, ceres.config.env);
              resolve();
            });
          }
        } catch(err) {
          reject(err);
        }
      });
    });
};
<commit_msg>updated comments<commit_after>var cluster = require('cluster');
var Promise = require('bluebird');

var Server = require('./Server');
var Pid = require('../lib/Pid');
var logStartTime = require('../lib/logStartTime');

/**
 * Fork a new worker and listen for log any errors
 * @param    {Ceres}    ceres
 */
function forkWorker(ceres, env) {
  env = typeof env === 'object' ? env : {};
  var worker = cluster.fork(env);
  worker.on('error', function(err) {
    ceres.log._ceres.error(err);
  });
}

/**
 * Make sure everything is setup the way we need to be before we start Listening
 * @param  {Ceres}    ceres
 * @return {Promise}
 */
module.exports = function(ceres) {
  // processManagement
  return this
    .connect.call(this, ceres)
    .then(function listen() {
      return new Promise(function(resolve, reject){
        try {
          if (ceres.config.pid) {
            // Setup Pid if we're configure
            ceres.pid = new Pid(ceres.config.pid);
            ceres.log._ceres.silly('pid %d written to %s', ceres.pid.id, ceres.pid.options.path);
          }

          if (!ceres.config.instances || ceres.config.instances === 1) {
            // If we only have a single instance no need to run the cluster
            Server.call(ceres, ceres).listen(ceres.config.port, function(){
              logStartTime('Server took %ds to start listening', ceres);
              ceres.log._ceres.info('Listening on %d (%s)', ceres.config.port, ceres.config.env);
              resolve();
            });
            return;
          }

          if (cluster.isMaster) {
            // Fork children
            for (var i = 0; i < ceres.config.instances; i++) {
              forkWorker(ceres, { CERES_UNIQUE_ID: i });
            }

            // Attempt to restart children that crash
            cluster.on('exit', function(worker, code, signal){
              if (signal) {
                ceres.log._ceres.info('worker %s was killed by %s', worker.process.pid, signal);
              } else if (code !== 0) {
                ceres.log._ceres.error('worker %s exited with %s. Starting new worker...', worker.process.pid, code, {
                  exitCode: code,
                  argv: process.argv
                });
                try {
                  forkWorker(ceres, { CERES_UNIQUE_ID: worker.process.env.CERES_UNIQUE_ID });
                } catch(err) {
                  ceres.log._ceres.error(err, function(e) {
                    if (e) {
                      ceres.log._ceres.error(e);
                    }
                    // If we fail to fork, then just exit to prevent infinite loop
                    process.exit(1);
                  });
                }
              }
            });

            ceres.log._ceres.info('Master spawned %d children', ceres.config.instances);
          } else {
            // Setup the server
            Server.call(ceres, ceres).listen(ceres.config.port, function(){
              logStartTime('Child ready after %ds', ceres);
              ceres.log._ceres.info('Listening on %d (%s)', ceres.config.port, ceres.config.env);
              resolve();
            });
          }
        } catch(err) {
          reject(err);
        }
      });
    });
};
<|endoftext|>


<commit_before>import React from 'react';
import LogoSpinner from '../_common/LogoSpinner';
import ErrorMsg from '../_common/ErrorMsg';
import InputGroup from '../_common/InputGroup';
import LanguagePicker from '../_common/LanguagePicker';

export default class SigninCard extends React.Component {

	static propTypes = {
		actions: React.PropTypes.object.isRequired,
		history: React.PropTypes.object.isRequired,
		signin: React.PropTypes.object.isRequired,
	};

	// validate() {
	// 	const emailNotValid = !isValidEmail(this.state.email);
	// 	const passwordNotEntered = this.state.password.length === 0;
	//
	// 	// actions.validation
	// 	this.setState({
	// 		validatedOnce: true,
	// 		emailNotValid,
	// 		passwordNotEntered,
	// 	});
	// }

	// trySignin() {
	// 	this.validate();
	//
	// 	if (!(this.state.emailNotValid || this.state.passwordNotEntered)) {
	// 		this.performLogin();
	// 	}
	// }

	onTokenChange(event) {
		this.props.actions.signinFieldUpdate('token', event.target.value);
	}

	onLanguageChange(event) {
		this.props.actions.signinFieldUpdate('language', event.target.value);
	}

	trySignin() {
		const {actions, history} = this.props;
		actions.signinFieldUpdate('progress', true);

		history.pushState({}, '/');	// no need to authorize here onEnter hook will authorize
	}

	// <InputGroup type="email" placeholder="Email" onChange={::this.emailChange} />
	// <ErrorMsg shown={validatedOnce && emailNotValid} text="You need to enter an email" />
	// <InputGroup type="password" placeholder="Password" onChange={::this.passwordChange} />
	// <ErrorMsg shown={passwordNotEntered} text="Need a pass" />
	// <ErrorMsg shown={credentialsInvalid} text="Access denied" />

	render() {
		const { signin } = this.props;

		return (

					<LogoSpinner spinning={signin.get('progress')}/>

				<InputGroup id="token-input" type="text" placeholder="Token" onChange={::this.onTokenChange} />
				<ErrorMsg
					shown={signin.get('validatedOnce') && signin.get('tokenNotEntered')}
					text="You need to enter a token" />
				<ErrorMsg
					shown={!!signin.get('credentialsInvalid')}
					text="Access denied" />

				<LanguagePicker onChange={::this.onLanguageChange} />
				<button className="outline-link signin-btn" onClick={::this.trySignin}>Sign In</button>
				Get your API token

				Create Account

		);
	}
}
<commit_msg>Sign redirect depends on UserAgent<commit_after>import React from 'react';
import LogoSpinner from '../_common/LogoSpinner';
import ErrorMsg from '../_common/ErrorMsg';
import InputGroup from '../_common/InputGroup';
import LanguagePicker from '../_common/LanguagePicker';

export default class SigninCard extends React.Component {

	static propTypes = {
		actions: React.PropTypes.object.isRequired,
		history: React.PropTypes.object.isRequired,
		signin: React.PropTypes.object.isRequired,
	};

	// validate() {
	// 	const emailNotValid = !isValidEmail(this.state.email);
	// 	const passwordNotEntered = this.state.password.length === 0;
	//
	// 	// actions.validation
	// 	this.setState({
	// 		validatedOnce: true,
	// 		emailNotValid,
	// 		passwordNotEntered,
	// 	});
	// }

	// trySignin() {
	// 	this.validate();
	//
	// 	if (!(this.state.emailNotValid || this.state.passwordNotEntered)) {
	// 		this.performLogin();
	// 	}
	// }

	onTokenChange(event) {
		this.props.actions.signinFieldUpdate('token', event.target.value);
	}

	onLanguageChange(event) {
		this.props.actions.signinFieldUpdate('language', event.target.value);
	}

	trySignin() {
		const {actions, history} = this.props;
		actions.signinFieldUpdate('progress', true);

		const mainPagePath = window.navigator.userAgent.match(/Mobile/) !== null ? '/' : '/workspace';
		history.pushState({}, mainPagePath);	// no need to authorize here onEnter hook will authorize
	}

	// <InputGroup type="email" placeholder="Email" onChange={::this.emailChange} />
	// <ErrorMsg shown={validatedOnce && emailNotValid} text="You need to enter an email" />
	// <InputGroup type="password" placeholder="Password" onChange={::this.passwordChange} />
	// <ErrorMsg shown={passwordNotEntered} text="Need a pass" />
	// <ErrorMsg shown={credentialsInvalid} text="Access denied" />

	render() {
		const { signin } = this.props;

		return (

					<LogoSpinner spinning={signin.get('progress')}/>

				<InputGroup id="token-input" type="text" placeholder="Token" onChange={::this.onTokenChange} />
				<ErrorMsg
					shown={signin.get('validatedOnce') && signin.get('tokenNotEntered')}
					text="You need to enter a token" />
				<ErrorMsg
					shown={!!signin.get('credentialsInvalid')}
					text="Access denied" />

				<LanguagePicker onChange={::this.onLanguageChange} />
				<button className="outline-link signin-btn" onClick={::this.trySignin}>Sign In</button>
				Get your API token

				Create Account

		);
	}
}
<|endoftext|>


<commit_before>

$(document).ready(function() {

  // https://github.com/js-cookie/js-cookie
  // each post id looks like: workout-series#-week#-day#

  /*
      store user's workout log
      only stores checked workouts, as 'true'
      if it's not checked it'll act as undefined

      looks like: where workout-series-week-day

      workoutLog [
        'workout-1-2-3',
        'workout-1-2-4',
      ]
  */

  let workoutLog = getWorkouts();
  console.log(workoutLog);
  processWorkouts(workoutLog)
  setWeekCleared();

  // populate log from cookies
  // console.log('hey');
  // Cookies.set('name', { foo: 'bar' });
  // let storage = Cookies.getJSON('name');
  // console.log(storage.foo)

  $('html').on( "click", '.trigger', function() {
    const workout = $(this).parent();
    // if( $(workout).hasClass('checked') ) {
    //   console.log('111')
    //   $(workout).removeClass('checked');
    //   setWeekCleared();
    //   repositionWeek($(workout).parent());
    //   removeFromLog($(workout).attr('id'), workoutLog);
    //   setWorkouts(workoutLog)
    // } else {
    console.log('222');
    $(workout).addClass('checked');
    repositionWeek($(workout).parent());
    addToLog($(workout), workoutLog);
    setWorkouts(workoutLog);
    setWeekCleared();

    scrollToObj($(this).parent());
    // }
  })

  $('html').on( "click", '.workout.checked', function() {
    // console.log('000');
    $(this).removeClass('checked');
    setWeekCleared();
    repositionWeek(this);
    removeFromLog($(this).attr('id'), workoutLog);
    setWorkouts(workoutLog)

    scrollToObj($(this).parent());
  });

});

//
//  Process the DOM
//

function repositionWeek(week) {
  console.log('repo week')
  $('.week .workout').each(function() {

    if($(this).hasClass('checked')) {
      // $(this).appendTo($(this).parent());
    }
  });
}

// runs through a week's workouts; folds it up if it's all donw
function setWeekCleared() {
  $('.week').each(function() {
    let cleared = true;
    $(this).find('.workout:not(.checked)').each(function() {
      cleared = false;
    });
    if(!cleared) {
      $(this).removeClass('cleared');
    } else {
      $(this).addClass('cleared');
    }
  });
}

// given a log, search all the matching IDs and set them to checked
function processWorkouts(log) {
  log.forEach(function (workout) {
    $('#'+workout).addClass('checked');
  })

  $('.week').each(function() {
    repositionWeek($(this))
  })
}

//
//  workout log
//

function addToLog(workoutObj, log) {
 const series = workoutObj.data('series'), week = workoutObj.data('week'), day = workoutObj.data('day');
 const workout = `workout-${series}-${week}-${day}`

 // don't add dupes
 if ($.inArray( workout, log ) < 0 )
  log.push(workout);
}

// takes an id that matches the value
function removeFromLog(idName, log) {
  const position = $.inArray( idName, log );
  if( position > -1 ) {
    log.splice(position, 1);
  };
}

//
//  cookie / storage handling
//

function setWorkouts(workoutLog) {
  if (typeof(Storage) !== "undefined") {
    localStorage.setItem('workoutLog', workoutLog);
  } else {
    Cookies.set('workoutLog', workoutLog);
    console.log(Cookies.getJSON('workoutLog'))
  }
}
function getWorkouts() {
  if (typeof(Storage) !== "undefined") {
    $('_debug').append(localStorage.getItem('workoutLog').split(','))
    return (localStorage.getItem('workoutLog').split(',')) || [];
  }
  return Cookies.getJSON('workoutLog') || [];
}

  // ************************************************************
  // Smooth Scrolling
  // - can link to a # of another page, and smooth-scroll down to that #

  var scrollElement = 'html, body';

  // Smooth scrolling for internal links
  function scrollToObj(obj) {
    event.preventDefault();

    var $this = $(this);
    let offset = 30;

    if( !(typeof obj.offset() === "undefined")) {
      $(scrollElement).stop().animate({
        'scrollTop': obj.offset().top - offset,
      }, 500, 'swing');
    }
  }

<commit_msg>added localstorage support instead of cookies<commit_after>

$(document).ready(function() {

  // https://github.com/js-cookie/js-cookie
  // each post id looks like: workout-series#-week#-day#

  /*
      store user's workout log
      only stores checked workouts, as 'true'
      if it's not checked it'll act as undefined

      looks like: where workout-series-week-day

      workoutLog [
        'workout-1-2-3',
        'workout-1-2-4',
      ]
  */

  let workoutLog = getWorkouts();
  console.log(workoutLog);
  processWorkouts(workoutLog)
  setWeekCleared();

  // populate log from cookies
  // console.log('hey');
  // Cookies.set('name', { foo: 'bar' });
  // let storage = Cookies.getJSON('name');
  // console.log(storage.foo)

  $('html').on( "click", '.trigger', function() {
    const workout = $(this).parent();
    // if( $(workout).hasClass('checked') ) {
    //   console.log('111')
    //   $(workout).removeClass('checked');
    //   setWeekCleared();
    //   repositionWeek($(workout).parent());
    //   removeFromLog($(workout).attr('id'), workoutLog);
    //   setWorkouts(workoutLog)
    // } else {
    console.log('222');
    $(workout).addClass('checked');
    repositionWeek($(workout).parent());
    addToLog($(workout), workoutLog);
    setWorkouts(workoutLog);
    setWeekCleared();

    scrollToObj($(this).parent());
    // }
  })

  $('html').on( "click", '.workout.checked', function() {
    // console.log('000');
    $(this).removeClass('checked');
    setWeekCleared();
    repositionWeek(this);
    removeFromLog($(this).attr('id'), workoutLog);
    setWorkouts(workoutLog)

    scrollToObj($(this).parent());
  });

});

//
//  Process the DOM
//

function repositionWeek(week) {
  console.log('repo week')
  $('.week .workout').each(function() {

    if($(this).hasClass('checked')) {
      // $(this).appendTo($(this).parent());
    }
  });
}

// runs through a week's workouts; folds it up if it's all donw
function setWeekCleared() {
  $('.week').each(function() {
    let cleared = true;
    $(this).find('.workout:not(.checked)').each(function() {
      cleared = false;
    });
    if(!cleared) {
      $(this).removeClass('cleared');
    } else {
      $(this).addClass('cleared');
    }
  });
}

// given a log, search all the matching IDs and set them to checked
function processWorkouts(log) {
  log.forEach(function (workout) {
    $('#'+workout).addClass('checked');
  })

  $('.week').each(function() {
    repositionWeek($(this))
  })
}

//
//  workout log
//

function addToLog(workoutObj, log) {
 const series = workoutObj.data('series'), week = workoutObj.data('week'), day = workoutObj.data('day');
 const workout = `workout-${series}-${week}-${day}`

 // don't add dupes
 if ($.inArray( workout, log ) < 0 )
  log.push(workout);
}

// takes an id that matches the value
function removeFromLog(idName, log) {
  const position = $.inArray( idName, log );
  if( position > -1 ) {
    log.splice(position, 1);
  };
}

//
//  cookie / storage handling
//

function setWorkouts(workoutLog) {
  if (typeof(Storage) !== "undefined") {
    localStorage.setItem('workoutLog', workoutLog);
  } else {
    Cookies.set('workoutLog', workoutLog);
    console.log(Cookies.getJSON('workoutLog'))
  }
}
function getWorkouts() {
  if (typeof(Storage) !== "undefined") {
    if(localStorage.getItem('workoutLog')) {
      $('_debug').append(localStorage.getItem('workoutLog').split(','))
      return (localStorage.getItem('workoutLog').split(',')) || [];
    }
  }
  return Cookies.getJSON('workoutLog') || [];
}

  // ************************************************************
  // Smooth Scrolling
  // - can link to a # of another page, and smooth-scroll down to that #

  var scrollElement = 'html, body';

  // Smooth scrolling for internal links
  function scrollToObj(obj) {
    event.preventDefault();

    var $this = $(this);
    let offset = 30;

    if( !(typeof obj.offset() === "undefined")) {
      $(scrollElement).stop().animate({
        'scrollTop': obj.offset().top - offset,
      }, 500, 'swing');
    }
  }

<|endoftext|>


<commit_before>define(['jquery',
        'underscore',
        'backbone',
        'base',
        'SQL',
        'text!views/tablelist.html',
        'text!views/tablelistitem.html',
        'text!views/tableinfo.html',
        'bootstrap',
    ], function ($, _, Backbone, base, SQL, TableListTemplate, TableListItemTemplate, TableInfoTemplate) {

    var Tables = {};

    Tables.TableInfo = Backbone.Model.extend({
        idAttribute: 'name',

        primaryShards: function () {
            return _.filter(this.get('shardInfo'), function (node) {
                return node.primary;
            });
        },

        size: function () {
            return _.reduce(this.primaryShards(), function (memo, shard) {
                return memo + shard.size;
            }, 0);
        },

        totalRecords: function () {
            return _.reduce(this.primaryShards(), function (memo, shard) {
                return memo + shard.records_total;
            }, 0);
        },

        missingShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'UNASSIGNED' && shard.primary;
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        underreplicatedShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'UNASSIGNED' && !shard.primary;
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        startedShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'STARTED';
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        underreplicatedRecords: function () {
            if (this.underreplicatedShards() === 0) {
                return 0;
            }
            if (this.primaryShards().length === 0) {
                return '--';
            }
            return this.underreplicatedShards() * _.first(this.primaryShards()).avg_docs;
        },

        unavailableRecords: function () {
            if (this.missingShards() === 0) {
                return 0;
            }

            var shard = _.find(this.get('shardInfo'), function (shard) {
                return shard.state === 'STARTED';
            });
            return shard.avg_docs * this.missingShards();
        },

        health: function () {
            if (this.primaryShards().length === 0) {
                return 'critical';
            }
            if (this.missingShards() > 0) {
                return 'critical';
            }
            if (this.underreplicatedShards() > 0) {
                return 'warning';
            }
            return 'good';
        }

    });

    Tables.TableList = Backbone.Collection.extend({

        model: Tables.TableInfo,

        comparator: function (item) {
            var health = item.health();

            switch (health) {
                case 'critical':
                    health = '0';
                    break;
                case 'warning':
                    health = '1';
                    break;
                case 'good':
                    health = '2';
                    break;
            }

            return health + item.get('name');
        },

        fetch: function (options) {
            var self = this,
                sqInfo, sqShardInfo, sqColumns, dInfo, dShardInfo, dColumns, d;

            d = $.Deferred();
            sqInfo = new SQL.Query(
                'select table_name, sum(number_of_shards), sum(number_of_replicas) ' +
                'from information_schema.tables ' +
                'group by table_name');

            sqShardInfo = new SQL.Query(
                'select table_name, sum(num_docs), "primary", avg(num_docs), count(*), state, sum(size) '+
                'from sys.shards group by table_name, "primary", state ' +
                'order by table_name, "primary"');

            sqColumns = new SQL.Query(
                'select table_name, column_name, data_type ' +
                'from information_schema.columns order by ordinal_position'
            );

            dInfo = sqInfo.execute();
            dShardInfo = sqShardInfo.execute();
            dColumns = sqColumns.execute();

            $.when(dInfo, dShardInfo, dColumns).then(function (info, shardInfo, columns) {
                // Collect and assemble list of tables as objects
                var tables = _.map(info[0].rows, function (row) {
                    return _.object(['name', 'shards_configured', 'replicas_configured'], row);
                });

                shardInfo = _.map(shardInfo[0].rows, function (row) {
                    return _.object(['name', 'records_total', 'primary', 'avg_docs', 'shards_active', 'state', 'size'], row);
                });

                tables = _.map(tables, function (table) {
                    table.shardInfo = _.filter(shardInfo, function (si) { return si.name === table.name; } );
                    return table;
                });

                columns = _.map(columns[0].rows, function (row) {
                    return _.object(['table_name', 'column_name', 'data_type'], row);
                });

                columns = _.groupBy(columns, function (column) { return column.table_name; });

                // Reject system tables
                // select table_name from information_schema.tables where schema_name='doc'
                tables = _.reject(tables, function (table) {
                    return _.contains(['tables', 'shards', 'columns', 'cluster', 'nodes'], table.name);
                });

                _.each(tables, function (table) {
                    table.columns = columns[table.name];
                });

                if (options && options.reset) {
                    self.reset(tables);
                } else {
                    self.set(tables);
                }

                d.resolve(tables);
            }, d.reject);
            return d.promise();
        }

    });

    Tables.TableListView = base.CrateView.extend({

        template: _.template(TableListTemplate),

        initialize: function () {
            var self = this;
            this.listenTo(this.collection, 'reset', this.render);
            this.listenTo(this.collection, 'add', this.addTable);
            this.refreshTimeout = setTimeout(function () { self.refresh(); }, 5000);
        },

        selectedItem: null,

        refresh: function () {
            var self = this;
            this.collection.fetch();
            this.refreshTimeout = setTimeout(function () { self.refresh(); }, 5000);
        },

        deactivateAll: function () {
            this.$('li').removeClass('active');
        },

        showDetails: function (name) {
            var t = this.collection.get(name),
                v = new Tables.TableInfoView({model: t});
            this.$('#table-info').html(v.render().$el);
            this.selectedItem = name;
        },

        addTable: function (table) {
            var v = new Tables.TableListItemView({model: table});
            this.$('ul').append(v.render().$el);
            this.addView(table.get('name'), v);
        },

        render: function () {
            var self = this;

            this.$el.html(this.template());
            _.each(this.collection.models, function (table) {
                self.addTable(table);
            });
            if (!this.selectedItem && this.collection.length) {
                this.showDetails(this.collection.first().id);
                this.$('#sidebar-wrapper ul').children().first().addClass('active');
            }
            return this;
        },

        dispose: function () {
            clearTimeout(this.refreshTimeout);
            base.CrateView.prototype.dispose.call(this);
        }

    });

    Tables.TableListItemView = base.CrateView.extend({

        tagName: 'li',

        template: _.template(TableListItemTemplate),

        events: {
            'click ': 'selectTable'
        },

        initialize: function () {
            this.listenTo(this.model, 'change', this.render);
        },

        selectTable: function (ev) {
            ev.preventDefault();
            ev.stopPropagation();
            this.parentView.deactivateAll();
            this.$el.addClass('active');
            this.parentView.showDetails(this.model.get('name'));

        },

        summary: function () {
            // Show in the summary the size of the "primary" shards
            return base.humanReadableSize(this.model.size());
        },

        render: function () {
            var data = this.model.toJSON();
            data.health = this.model.health();
            this.$el.html(this.template(data));
            return this;
        }
    });

    Tables.TableInfoView = base.CrateView.extend({

        template: _.template(TableInfoTemplate),

        initialize: function () {
            this.listenTo(this.model, 'change', this.render);
        },

        render: function () {
            var data = this.model.toJSON();
            data.missingShards = this.model.missingShards();
            data.startedShards = this.model.startedShards();
            data.tableSize = base.humanReadableSize(this.model.size());
            data.totalRecords = this.model.totalRecords();
            data.underreplicatedShards = this.model.underreplicatedShards();
            data.underreplicatedRecords = this.model.underreplicatedRecords();
            data.unavailableRecords = this.model.unavailableRecords();
            data.health = this.model.health();
            this.$el.html(this.template(data));
            return this;
        }

    });

    return Tables;
});
<commit_msg>respond to remove event in TableListView<commit_after>define(['jquery',
        'underscore',
        'backbone',
        'base',
        'SQL',
        'text!views/tablelist.html',
        'text!views/tablelistitem.html',
        'text!views/tableinfo.html',
        'bootstrap',
    ], function ($, _, Backbone, base, SQL, TableListTemplate, TableListItemTemplate, TableInfoTemplate) {

    var Tables = {};

    Tables.TableInfo = Backbone.Model.extend({
        idAttribute: 'name',

        primaryShards: function () {
            return _.filter(this.get('shardInfo'), function (node) {
                return node.primary;
            });
        },

        size: function () {
            return _.reduce(this.primaryShards(), function (memo, shard) {
                return memo + shard.size;
            }, 0);
        },

        totalRecords: function () {
            return _.reduce(this.primaryShards(), function (memo, shard) {
                return memo + shard.records_total;
            }, 0);
        },

        missingShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'UNASSIGNED' && shard.primary;
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        underreplicatedShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'UNASSIGNED' && !shard.primary;
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        startedShards: function () {
            var shards = _.filter(this.get('shardInfo'), function (shard) {
                return shard.state == 'STARTED';
            });
            return _.reduce(shards, function(memo, shard) {return shard.shards_active + memo; }, 0);
        },

        underreplicatedRecords: function () {
            if (this.underreplicatedShards() === 0) {
                return 0;
            }
            if (this.primaryShards().length === 0) {
                return '--';
            }
            return this.underreplicatedShards() * _.first(this.primaryShards()).avg_docs;
        },

        unavailableRecords: function () {
            if (this.missingShards() === 0) {
                return 0;
            }

            var shard = _.find(this.get('shardInfo'), function (shard) {
                return shard.state === 'STARTED';
            });
            return shard.avg_docs * this.missingShards();
        },

        health: function () {
            if (this.primaryShards().length === 0) {
                return 'critical';
            }
            if (this.missingShards() > 0) {
                return 'critical';
            }
            if (this.underreplicatedShards() > 0) {
                return 'warning';
            }
            return 'good';
        }

    });

    Tables.TableList = Backbone.Collection.extend({

        model: Tables.TableInfo,

        comparator: function (item) {
            var health = item.health();

            switch (health) {
                case 'critical':
                    health = '0';
                    break;
                case 'warning':
                    health = '1';
                    break;
                case 'good':
                    health = '2';
                    break;
            }

            return health + item.get('name');
        },

        fetch: function (options) {
            var self = this,
                sqInfo, sqShardInfo, sqColumns, dInfo, dShardInfo, dColumns, d;

            d = $.Deferred();
            sqInfo = new SQL.Query(
                'select table_name, sum(number_of_shards), sum(number_of_replicas) ' +
                'from information_schema.tables ' +
                'group by table_name');

            sqShardInfo = new SQL.Query(
                'select table_name, sum(num_docs), "primary", avg(num_docs), count(*), state, sum(size) '+
                'from sys.shards group by table_name, "primary", state ' +
                'order by table_name, "primary"');

            sqColumns = new SQL.Query(
                'select table_name, column_name, data_type ' +
                'from information_schema.columns order by ordinal_position'
            );

            dInfo = sqInfo.execute();
            dShardInfo = sqShardInfo.execute();
            dColumns = sqColumns.execute();

            $.when(dInfo, dShardInfo, dColumns).then(function (info, shardInfo, columns) {
                // Collect and assemble list of tables as objects
                var tables = _.map(info[0].rows, function (row) {
                    return _.object(['name', 'shards_configured', 'replicas_configured'], row);
                });

                shardInfo = _.map(shardInfo[0].rows, function (row) {
                    return _.object(['name', 'records_total', 'primary', 'avg_docs', 'shards_active', 'state', 'size'], row);
                });

                tables = _.map(tables, function (table) {
                    table.shardInfo = _.filter(shardInfo, function (si) { return si.name === table.name; } );
                    return table;
                });

                columns = _.map(columns[0].rows, function (row) {
                    return _.object(['table_name', 'column_name', 'data_type'], row);
                });

                columns = _.groupBy(columns, function (column) { return column.table_name; });

                // Reject system tables
                // select table_name from information_schema.tables where schema_name='doc'
                tables = _.reject(tables, function (table) {
                    return _.contains(['tables', 'shards', 'columns', 'cluster', 'nodes'], table.name);
                });

                _.each(tables, function (table) {
                    table.columns = columns[table.name];
                });

                if (options && options.reset) {
                    self.reset(tables);
                } else {
                    self.set(tables);
                }

                d.resolve(tables);
            }, d.reject);
            return d.promise();
        }

    });

    Tables.TableListView = base.CrateView.extend({

        template: _.template(TableListTemplate),

        initialize: function () {
            var self = this;
            this.listenTo(this.collection, 'reset', this.render);
            this.listenTo(this.collection, 'add', this.addTable);
            this.listenTo(this.collection, 'remove', this.removeTable);
            this.refreshTimeout = setTimeout(function () { self.refresh(); }, 5000);
        },

        selectedItem: null,

        refresh: function () {
            var self = this;
            this.collection.fetch();
            this.refreshTimeout = setTimeout(function () { self.refresh(); }, 5000);
        },

        deactivateAll: function () {
            this.$('li').removeClass('active');
        },

        showDetails: function (name) {
            var t = this.collection.get(name),
                v = new Tables.TableInfoView({model: t});
            this.$('#table-info').html(v.render().$el);
            this.selectedItem = name;
        },

        addTable: function (table) {
            var v = new Tables.TableListItemView({model: table});
            this.$('ul').append(v.render().$el);
            this.addView(table.id, v);
        },

        removeTable: function (table) {
            if (_.has(this.subviews, table.id)) {
                this.subviews[table.id].dispose();
            }
        },

        render: function () {
            var self = this;

            this.$el.html(this.template());
            _.each(this.collection.models, function (table) {
                self.addTable(table);
            });
            if (!this.selectedItem && this.collection.length) {
                this.showDetails(this.collection.first().id);
                this.$('#sidebar-wrapper ul').children().first().addClass('active');
            }
            return this;
        },

        dispose: function () {
            clearTimeout(this.refreshTimeout);
            base.CrateView.prototype.dispose.call(this);
        }

    });

    Tables.TableListItemView = base.CrateView.extend({

        tagName: 'li',

        template: _.template(TableListItemTemplate),

        events: {
            'click ': 'selectTable'
        },

        initialize: function () {
            this.listenTo(this.model, 'change', this.render);
        },

        selectTable: function (ev) {
            ev.preventDefault();
            ev.stopPropagation();
            this.parentView.deactivateAll();
            this.$el.addClass('active');
            this.parentView.showDetails(this.model.get('name'));

        },

        summary: function () {
            // Show in the summary the size of the "primary" shards
            return base.humanReadableSize(this.model.size());
        },

        render: function () {
            var data = this.model.toJSON();
            data.health = this.model.health();
            this.$el.html(this.template(data));
            return this;
        }
    });

    Tables.TableInfoView = base.CrateView.extend({

        template: _.template(TableInfoTemplate),

        initialize: function () {
            this.listenTo(this.model, 'change', this.render);
        },

        render: function () {
            var data = this.model.toJSON();
            data.missingShards = this.model.missingShards();
            data.startedShards = this.model.startedShards();
            data.tableSize = base.humanReadableSize(this.model.size());
            data.totalRecords = this.model.totalRecords();
            data.underreplicatedShards = this.model.underreplicatedShards();
            data.underreplicatedRecords = this.model.underreplicatedRecords();
            data.unavailableRecords = this.model.unavailableRecords();
            data.health = this.model.health();
            this.$el.html(this.template(data));
            return this;
        }

    });

    return Tables;
});
<|endoftext|>


<commit_before>'use strict';

/**
 * @name send
 * @module send
 * @description utilities for sendind different type of messages on top of
 *              open311-messages
 *
 * @see {@link Message}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const environment = require('execution-environment');
const infobip = require('open311-infobip');
const phone = require('phone');
const mongoose = require('mongoose');
const Message = mongoose.model('Message');

/**
 * @name formatPhoneNumberToE164
 * @description format provided mobile phone number to E.164 format
 * @param  {String} phoneNumber a phone number to be formatted
 * @param {String} [country] 2 or 3 letter ISO country code. default to TZ
 * @return {String} E.164 formated phone number without leading plus sign
 * @see {@link https://en.wikipedia.org/wiki/E.164|e.164}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */
exports.formatPhoneNumberToE164 = function (phoneNumber, countryCode) {

  //try convert give phone number to e.164
  try {
    countryCode = countryCode || 'TZ';
    phoneNumber = phone(phoneNumber, countryCode);
    phoneNumber = _.first(phoneNumber).replace(/\+/g, '');
    return phoneNumber;
  }

  //fail to convert, return original format
  catch (error) {
    return phoneNumber;
  }

};

/**
 * @name sms
 * @description send a given message as an sms
 * @param  {Message|Object}   message valid message instance or definition
 * @param  {Function} done    a callback to invoke on success or failure
 * @see {@link Message}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */
exports.sms = function (message, done) {

  //obtain current execution environment
  const isProduction = environment.isProd();

  //obtain sms configuration
  const options = config.get('infobip');
  const shouldSendSynchronously = options.sync;

  //prepare sms message
  const isMessageInstance = message instanceof Message;
  message = isMessageInstance ? message : new Message(message);

  //ensure message sender
  message.from = message.from || options.from;

  //ensure number is in e.164 format
  let to = _.map(message.to, function (to) {
    return exports.formatPhoneNumberToE164(to); //TODO support other countries
  });
  to = _.compact(to);
  message.to = to;

  //queue message in production
  //or if is asynchronous send
  if (isProduction || !shouldSendSynchronously) {
    infobip.queue(message);
    done(null, message); //TODO listen to queue errors and notify
  }

  //direct sms send in development & test
  else {

    //force message persist
    message.save(function (error, saved) {

      //back-off in case of error
      if (error) {
        done(error);
      }

      //continue with sending
      else {
        //send sms synchronous
        infobip.send(saved, function (error /*, result*/ ) {
          done(error, saved);
        });
      }

    });

  }

};
<commit_msg>ensure default configs<commit_after>'use strict';

/**
 * @name send
 * @module send
 * @description utilities for sendind different type of messages on top of
 *              open311-messages
 *
 * @see {@link Message}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const environment = require('execution-environment');
const infobip = require('open311-infobip');
const phone = require('phone');
const mongoose = require('mongoose');
const Message = mongoose.model('Message');

/**
 * @name formatPhoneNumberToE164
 * @description format provided mobile phone number to E.164 format
 * @param  {String} phoneNumber a phone number to be formatted
 * @param {String} [country] 2 or 3 letter ISO country code. default to TZ
 * @return {String} E.164 formated phone number without leading plus sign
 * @see {@link https://en.wikipedia.org/wiki/E.164|e.164}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */
exports.formatPhoneNumberToE164 = function (phoneNumber, countryCode) {

  //try convert give phone number to e.164
  try {
    countryCode = countryCode || 'TZ';
    phoneNumber = phone(phoneNumber, countryCode);
    phoneNumber = _.first(phoneNumber).replace(/\+/g, '');
    return phoneNumber;
  }

  //fail to convert, return original format
  catch (error) {
    return phoneNumber;
  }

};

/**
 * @name sms
 * @description send a given message as an sms
 * @param  {Message|Object}   message valid message instance or definition
 * @param  {Function} done    a callback to invoke on success or failure
 * @see {@link Message}
 * @author lally elias <lallyelias87@mail.com>
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */
exports.sms = function (message, done) {

  //obtain current execution environment
  const isProduction = environment.isProd();

  //obtain sms configuration
  const options = config.get('infobip');
  const shouldSendSynchronously = options.sync;

  //prepare sms message
  const isMessageInstance = message instanceof Message;
  message = isMessageInstance ? message : new Message(message);

  //ensure message sender
  message.from = message.from || options.from;

  //ensure number is in e.164 format
  let to = _.map(message.to, function (to) {
    return exports.formatPhoneNumberToE164(to); //TODO support other countries
  });
  to = _.compact(to);
  message.to = to;

  //queue message in production
  //or if is asynchronous send
  if (isProduction && !shouldSendSynchronously) {
    infobip.queue(message);
    done(null, message); //TODO listen to queue errors and notify
  }

  //direct sms send in development & test
  else {

    //force message persist
    message.save(function (error, saved) {

      //back-off in case of error
      if (error) {
        done(error);
      }

      //continue with sending
      else {
        //send sms synchronous
        infobip.send(saved, function (error /*, result*/ ) {
          done(error, saved);
        });
      }

    });

  }

};
<|endoftext|>