<commit_before>'use strict';

const {
	app,
	BrowserWindow,
	clipboard,
	Menu,
	ipcMain
} = require('electron');

const MENU_SEPARATOR = { type: 'separator' };

const defaultMenuStates = {
	canAddTab: true,
	canCloseTab: true,
	canCloseWindow: true,
	canUndo: false,
	canRedo: false,
	canCapture: true,
	isImageEmpty: true,
	canReload: true,
	canToggleDevTools: true,
	canMinimize: true,
	canMove: true,
	canInteractWithTabs: true,
	canZoomIn: true,
	canZoomOut: true
};

let template = null;

module.exports = {
	init(negative) {
		ipcMain.on('refresh-menu', (evt, menuStates) => this.refresh(menuStates));

		template = [
			{
				label: 'Negative',
				submenu: [
					{ label: 'About Negative', role: 'about' },
					MENU_SEPARATOR,
					{ label: 'Preferences...', accelerator: 'Command+,', click: negative.initSettingsWindow },
					MENU_SEPARATOR,
					{ label: 'Quit Negative',  accelerator: 'Command+Q', click: app.quit }
				]
			},
			{
				label: 'File',
				submenu: [
					{ label: 'New Tab',      accelerator: 'Command+T',       click: negative.addTab },
					{ label: 'New Window',   accelerator: 'Command+N',       click: negative.initNegativeWindow },
					MENU_SEPARATOR,
					{ label: 'Close Tab',    accelerator: 'Command+W',       click: negative.closeTab },
					{ label: 'Close Window', accelerator: 'Shift+Command+W', selector: 'performClose:' },
					{ label: 'Close',        accelerator: 'Command+W',       selector: 'performClose:', visible: false }
				]
			},
			{
				label: 'Edit',
				submenu: [
					{ label: 'Undo',  accelerator: 'Command+Z',       click: negative.undo,  enabled: false },
					{ label: 'Redo',  accelerator: 'Shift+Command+Z', click: negative.redo,  enabled: false },
					MENU_SEPARATOR,
					{ label: 'Copy',  accelerator: 'Command+C',       click: negative.copy,  enabled: false },
					{ label: 'Paste', accelerator: 'Command+V',       click: negative.paste, enabled: false }
				]
			},
			{
				label: 'View',
				submenu: [
					{ label: 'Capture',         accelerator: 'Command+G',         click: negative.captureRegionBehindWindow },
					{ label: 'Clear',           accelerator: 'Command+Backspace', click: negative.removeImage,     enabled: false },
					MENU_SEPARATOR,
					{ label: 'Actual Size',     accelerator: 'Command+0',         click: () => negative.zoomTo(1), enabled: false },
					{ label: 'Zoom In',         accelerator: 'Command+=',         click: negative.zoomIn,          enabled: false },
					{ label: 'Zoom Out',        accelerator: 'Command+-',         click: negative.zoomOut,         enabled: false },
					MENU_SEPARATOR,
					{ label: 'Reload',          accelerator: 'Command+R',         click: () => BrowserWindow.getFocusedWindow().reload() },
					{ label: 'Toggle DevTools', accelerator: 'Alt+Command+I',     click: () => BrowserWindow.getFocusedWindow().toggleDevTools() }
				]
			},
			{
				label: 'Window',
				submenu: [
					{ label: 'Minimize',                accelerator: 'Command+M',     role: 'minimize' },
					{ label: 'Fit Window to Image',     accelerator: 'Command+F',     click: negative.fitWindowToImage,                           enabled: false },
					{ label: 'Next Tab',                accelerator: 'Command+}',     click: negative.selectNextTab,                              enabled: false },
					{ label: 'Previous Tab',            accelerator: 'Command+{',     click: negative.selectPreviousTab,                          enabled: false },
					{ label: 'Next Tab and Resize',     accelerator: 'Alt+Command+}', click: negative.selectNextTabAndResizeWindowToFitImage,     enabled: false },
					{ label: 'Previous Tab and Resize', accelerator: 'Alt+Command+{', click: negative.selectPreviousTabAndResizeWindowToFitImage, enabled: false },
					{
						label: 'Move',
						submenu: [
							{ label: 'Right by 1px',  accelerator: 'Right',       click: () => negative.move('right', 1) },
							{ label: 'Left by 1px',   accelerator: 'Left',        click: () => negative.move('left', 1) },
							{ label: 'Up by 1px',     accelerator: 'Up',          click: () => negative.move('up', 1) },
							{ label: 'Down by 1px',   accelerator: 'Down',        click: () => negative.move('down', 1) },
							MENU_SEPARATOR,
							{ label: 'Right by 10px', accelerator: 'Shift+Right', click: () => negative.move('right', 10) },
							{ label: 'Left by 10px',  accelerator: 'Shift+Left',  click: () => negative.move('left', 10) },
							{ label: 'Up by 10px',    accelerator: 'Shift+Up',    click: () => negative.move('up', 10) },
							{ label: 'Down by 10px',  accelerator: 'Shift+Down',  click: () => negative.move('down', 10) }
						]
					}
				]
			}
		];

		const menu = Menu.buildFromTemplate(template);
		Menu.setApplicationMenu(menu);
	},

	refresh(menuStates, isSettingsWindow) {
		const menus = [ 'File', 'Edit', 'View', 'Window' ];

		isSettingsWindow = !!isSettingsWindow;

		menuStates = Object.assign({}, defaultMenuStates, menuStates);

		if (menuStates) {
			for (const item of template) {
				if (menus.indexOf(item.label) !== -1) {
					for (const submenuItem of item.submenu) {
						switch (submenuItem.label) {
							// File
							case 'New Tab':
								submenuItem.enabled = menuStates.canAddTab;
								break;
							case 'Close Tab':
								submenuItem.enabled = menuStates.canCloseTab;
								submenuItem.visible = !isSettingsWindow;
								break;
							case 'Close Window':
								submenuItem.enabled = menuStates.canCloseWindow;
								submenuItem.visible = !isSettingsWindow;
								break;
							case 'Close':
								submenuItem.visible = isSettingsWindow;
								break;
							// Edit
							case 'Undo':
								submenuItem.enabled = menuStates.canUndo;
								break;
							case 'Redo':
								submenuItem.enabled = menuStates.canRedo;
								break;
							case 'Copy':
								submenuItem.enabled = !menuStates.isImageEmpty;
								break;
							case 'Paste':
								submenuItem.enabled = !clipboard.readImage().isEmpty();
								break;
							// View
							case 'Capture':
								submenuItem.enabled = menuStates.canCapture;
								break;
							case 'Clear':
								submenuItem.enabled = !menuStates.isImageEmpty;
								break;
							case 'Actual Size':
								submenuItem.enabled = !menuStates.isImageEmpty;
								break;
							case 'Zoom In':
								submenuItem.enabled = menuStates.canZoomIn;
								break;
							case 'Zoom Out':
								submenuItem.enabled = menuStates.canZoomOut;
								break;
							case 'Reload':
								submenuItem.enabled = menuStates.canReload;
								break;
							case 'Toggle DevTools':
								submenuItem.enabled = menuStates.canToggleDevTools;
								break;
							// Window
							case 'Minimize':
								submenuItem.enabled = menuStates.canMinimize;
								break;
							case 'Fit Window to Image':
								submenuItem.enabled = !menuStates.isImageEmpty;
								break;
							case 'Next Tab':
							case 'Next Tab and Resize':
							case 'Previous Tab':
							case 'Previous Tab and Resize':
								submenuItem.enabled = menuStates.canInteractWithTabs;
								break;
							case 'Move':
								for (const moveItem of submenuItem.submenu) {
									moveItem.enabled = menuStates.canMove;
								}
								break;
						}
					}
				}
			}
		}

		const menu = Menu.buildFromTemplate(template);
		Menu.setApplicationMenu(menu);
	},

	refreshForSettingsWindow() {
		this.refresh({
			canAddTab: false,
			canCloseTab: false,
			canCloseWindow: false,
			canUndo: false,
			canRedo: false,
			canCapture: false,
			isImageEmpty: true,
			canReload: true,
			canToggleDevTools: true,
			canMinimize: true,
			canMove: false,
			canInteractWithTabs: false,
			canZoomIn: false,
			canZoomOut: false
		}, true);
	},

	refreshForNoWindow() {
		this.refresh({
			canAddTab: false,
			canCloseTab: false,
			canCloseWindow: false,
			canUndo: false,
			canRedo: false,
			canCapture: false,
			isImageEmpty: true,
			canReload: false,
			canToggleDevTools: false,
			canMinimize: false,
			canMove: false,
			canInteractWithTabs: false,
			canZoomIn: false,
			canZoomOut: false
		}, false);
	}
};
<commit_msg>Simplify menu<commit_after>'use strict';

const {
	app,
	BrowserWindow,
	clipboard,
	Menu,
	ipcMain
} = require('electron');

const MENU_SEPARATOR = { type: 'separator' };

const defaultMenuStates = {
	canAddTab: true,
	canCloseTab: true,
	canCloseWindow: true,
	canUndo: false,
	canRedo: false,
	canCapture: true,
	isImageEmpty: true,
	canReload: true,
	canToggleDevTools: true,
	canMinimize: true,
	canMove: true,
	canInteractWithTabs: true,
	canZoomIn: true,
	canZoomOut: true
};

module.exports = {
	init(negative) {
		ipcMain.on('refresh-menu', (evt, menuStates) => this.createMenu(menuStates));

		this.negative = negative;
		this.createMenu();
	},

	createMenu(menuStates, isSettingsWindow) {
		menuStates       = Object.assign({}, defaultMenuStates, menuStates);
		isSettingsWindow = !!isSettingsWindow;

		const negative = this.negative;

		const {
			canAddTab,
			canCloseTab,
			canCloseWindow,
			canUndo,
			canRedo,
			canCapture,
			isImageEmpty,
			canReload,
			canToggleDevTools,
			canMinimize,
			canMove,
			canInteractWithTabs,
			canZoomIn,
			canZoomOut
		} = menuStates;

		const template = [
			{
				label: 'Negative',
				submenu: [
					{ label: 'About Negative', role: 'about' },
					MENU_SEPARATOR,
					{ label: 'Preferences...', accelerator: 'Command+,', click: negative.initSettingsWindow },
					MENU_SEPARATOR,
					{ label: 'Quit Negative', accelerator: 'Command+Q', click: app.quit }
				]
			},
			{
				label: 'File',
				submenu: [
					{ label: 'New Tab', accelerator: 'Command+T', click: negative.addTab, enabled: canAddTab },
					{ label: 'New Window', accelerator: 'Command+N', click: negative.initNegativeWindow },
					MENU_SEPARATOR,
					{ label: 'Close Tab', accelerator: 'Command+W', click: negative.closeTab, enabled: canCloseTab, visible: !isSettingsWindow },
					{ label: 'Close Window', accelerator: 'Shift+Command+W', selector: 'performClose:', enabled: canCloseWindow, visible: !isSettingsWindow },
					{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:', visible: isSettingsWindow }
				]
			},
			{
				label: 'Edit',
				submenu: [
					{ label: 'Undo',  accelerator: 'Command+Z', click: negative.undo, enabled: canUndo },
					{ label: 'Redo',  accelerator: 'Shift+Command+Z', click: negative.redo, enabled: canRedo },
					MENU_SEPARATOR,
					{ label: 'Copy',  accelerator: 'Command+C', click: negative.copy, enabled: !isImageEmpty },
					{ label: 'Paste', accelerator: 'Command+V', click: negative.paste, enabled: !clipboard.readImage().isEmpty() }
				]
			},
			{
				label: 'View',
				submenu: [
					{ label: 'Capture', accelerator: 'Command+G', click: negative.captureRegionBehindWindow, enabled: canCapture },
					{ label: 'Clear', accelerator: 'Command+Backspace', click: negative.removeImage, enabled: !isImageEmpty },
					MENU_SEPARATOR,
					{ label: 'Actual Size', accelerator: 'Command+0', click: () => negative.zoomTo(1), enabled: !isImageEmpty },
					{ label: 'Zoom In', accelerator: 'Command+=', click: negative.zoomIn, enabled: canZoomIn },
					{ label: 'Zoom Out', accelerator: 'Command+-', click: negative.zoomOut, enabled: canZoomOut },
					MENU_SEPARATOR,
					{ label: 'Reload', accelerator: 'Command+R', click: () => BrowserWindow.getFocusedWindow().reload(), enabled: canReload },
					{ label: 'Toggle DevTools', accelerator: 'Alt+Command+I', click: () => BrowserWindow.getFocusedWindow().toggleDevTools(), enabled: canToggleDevTools }
				]
			},
			{
				label: 'Window',
				submenu: [
					{ label: 'Minimize', accelerator: 'Command+M', role: 'minimize' },
					{ label: 'Fit Window to Image', accelerator: 'Command+F', click: negative.fitWindowToImage, enabled: canMinimize },
					{ label: 'Next Tab', accelerator: 'Command+}', click: negative.selectNextTab, enabled: canInteractWithTabs },
					{ label: 'Previous Tab', accelerator: 'Command+{', click: negative.selectPreviousTab, enabled: canInteractWithTabs },
					{ label: 'Next Tab and Resize', accelerator: 'Alt+Command+}', click: negative.selectNextTabAndResizeWindowToFitImage, enabled: canInteractWithTabs },
					{ label: 'Previous Tab and Resize', accelerator: 'Alt+Command+{', click: negative.selectPreviousTabAndResizeWindowToFitImage, enabled: canInteractWithTabs },
					{
						label: 'Move',
						submenu: [
							{ label: 'Right by 1px', accelerator: 'Right', click: () => negative.move('right', 1), enabled: canMove },
							{ label: 'Left by 1px', accelerator: 'Left', click: () => negative.move('left', 1), enabled: canMove },
							{ label: 'Up by 1px', accelerator: 'Up', click: () => negative.move('up', 1), enabled: canMove },
							{ label: 'Down by 1px', accelerator: 'Down', click: () => negative.move('down', 1), enabled: canMove },
							MENU_SEPARATOR,
							{ label: 'Right by 10px', accelerator: 'Shift+Right', click: () => negative.move('right', 10), enabled: canMove },
							{ label: 'Left by 10px', accelerator: 'Shift+Left',  click: () => negative.move('left', 10), enabled: canMove },
							{ label: 'Up by 10px', accelerator: 'Shift+Up', click: () => negative.move('up', 10), enabled: canMove },
							{ label: 'Down by 10px', accelerator: 'Shift+Down', click: () => negative.move('down', 10), enabled: canMove }
						]
					}
				]
			}
		];

		const menu = Menu.buildFromTemplate(template);
		Menu.setApplicationMenu(menu);
	},

	refreshForSettingsWindow() {
		this.createMenu({
			canAddTab: false,
			canCloseTab: false,
			canCloseWindow: false,
			canUndo: false,
			canRedo: false,
			canCapture: false,
			isImageEmpty: true,
			canReload: true,
			canToggleDevTools: true,
			canMinimize: true,
			canMove: false,
			canInteractWithTabs: false,
			canZoomIn: false,
			canZoomOut: false
		}, true);
	},

	refreshForNoWindow() {
		this.createMenu({
			canAddTab: false,
			canCloseTab: false,
			canCloseWindow: false,
			canUndo: false,
			canRedo: false,
			canCapture: false,
			isImageEmpty: true,
			canReload: false,
			canToggleDevTools: false,
			canMinimize: false,
			canMove: false,
			canInteractWithTabs: false,
			canZoomIn: false,
			canZoomOut: false
		}, false);
	}
};
<|endoftext|>


<commit_before>
/*!
 * Stylus - Literal
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Node = require('./node')
  , nodes = require('./');

/**
 * Initialize a new `Literal` with the given `str`.
 *
 * @param {String} str
 * @api public
 */

var Literal = module.exports = function Literal(str){
  Node.call(this);
  this.val = str;
};

/**
 * Inherit from `Node.prototype`.
 */

Literal.prototype.__proto__ = Node.prototype;

/**
 * Return hash.
 *
 * @return {String}
 * @api public
 */

Literal.prototype.__defineGetter__('hash', function(){
  return this.val;
});

/**
 * Return literal value.
 *
 * @return {String}
 * @api public
 */

Literal.prototype.toString = function(){
  return this.val;
};

/**
 * Coerce `other` to a literal.
 *
 * @param {Node} other
 * @return {String}
 * @api public
 */

Literal.prototype.coerce = function(other){
  if (other instanceof Literal ||
      other instanceof nodes.Ident) {
    return other;
  } else {
    return Node.prototype.coerce.call(this, other);
  }
};
<commit_msg>coerce string to literal<commit_after>
/*!
 * Stylus - Literal
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Node = require('./node')
  , nodes = require('./');

/**
 * Initialize a new `Literal` with the given `str`.
 *
 * @param {String} str
 * @api public
 */

var Literal = module.exports = function Literal(str){
  Node.call(this);
  this.val = str;
};

/**
 * Inherit from `Node.prototype`.
 */

Literal.prototype.__proto__ = Node.prototype;

/**
 * Return hash.
 *
 * @return {String}
 * @api public
 */

Literal.prototype.__defineGetter__('hash', function(){
  return this.val;
});

/**
 * Return literal value.
 *
 * @return {String}
 * @api public
 */

Literal.prototype.toString = function(){
  return this.val;
};

/**
 * Coerce `other` to a literal.
 *
 * @param {Node} other
 * @return {String}
 * @api public
 */

Literal.prototype.coerce = function(other){
  if (other instanceof Literal ||
      other instanceof nodes.Ident ||
      other instanceof nodes.String) {
    return new Literal(other.string);
  } else {
    return Node.prototype.coerce.call(this, other);
  }
};
<|endoftext|>


<commit_before>
var oauth2 = require('./oauth2')

module.exports = function (options) {

  var seneca = options.seneca;
  var app = options.app;

  var oauth2srv = oauth2.init({seneca:seneca})

  var clientent = seneca.make('sys/project')
  var accesstokenent = seneca.make('accesstoken')

  function buildClientContext(req, res, next) {
    if( !req.body ) return next();

    var clientId = req.body['client_id']
    var clientSecret = req.body['client_secret']

    if( clientId && clientSecret ) {
      clientent.load$({appid:clientId,secret:clientSecret},function(err,client){
        if(err) return next(err);
        req.user = client
        next()
      })
    }
    else return next();
  };

  function info(req, res, next) {
    var access_token = req.query.access_token
    if( !access_token ) {
      access_token = req.headers.authorization
      if( access_token ) {
        access_token = access_token.substring('Bearer '.length)
      }
    }

    if( access_token ) {
      accesstokenent.load$(access_token,function(err,at){
      	if(err) return next(err);
      	if (at) {
        	res.send({hi:1})
      	} else {
      		res.send(400, "Invalid access token")
      	}
      })
    }
    else res.send(400, "Missing access token")
  };

  app.use(buildClientContext);

  app.get('/dialog/authorize', oauth2srv.authorization);
  app.post('/dialog/authorize/decision', oauth2srv.decision);
  app.post('/oauth/token', oauth2srv.token);
  app.get('/api/userinfo', info);

  return {
    buildClientContext: buildClientContext,
    info: info
  };

}<commit_msg>Returning basic profile info for /api/userinfo endpoint<commit_after>
var oauth2 = require('./oauth2')

module.exports = function (options) {

  var seneca = options.seneca;
  var app = options.app;

  var oauth2srv = oauth2.init({seneca:seneca})

  var userent = seneca.make('sys/user')
  var clientent = seneca.make('sys/project')
  var accesstokenent = seneca.make('accesstoken')

  function buildClientContext(req, res, next) {
    if( !req.body ) return next();

    var clientId = req.body['client_id']
    var clientSecret = req.body['client_secret']

    if( clientId && clientSecret ) {
      clientent.load$({appid:clientId,secret:clientSecret},function(err,client){
        if(err) return next(err);
        req.user = client
        next()
      })
    }
    else return next();
  };

  function info(req, res, next) {
    var access_token = req.query.access_token
    if( !access_token ) {
      access_token = req.headers.authorization
      if( access_token ) {
        access_token = access_token.substring('Bearer '.length)
      }
    }

    if( access_token ) {
      accesstokenent.load$(access_token,function(err,at){
      	if(err) return next(err);
      	if (at) {
          userent.load$(at.userID, function(err, user){
            if(user) {
              res.send({email:user.email, nick:user.nick, name:user.name, image:user.image, active:user.active, confirmed:user.confirmed})
            }
            else {
              res.send(400, "Invalid access token")
            }
          })
      	} else {
      		res.send(400, "Invalid access token")
      	}
      })
    }
    else res.send(400, "Missing access token")
  };

  app.use(buildClientContext);

  app.get('/dialog/authorize', oauth2srv.authorization);
  app.post('/dialog/authorize/decision', oauth2srv.decision);
  app.post('/oauth/token', oauth2srv.token);
  app.get('/api/userinfo', info);

  return {
    buildClientContext: buildClientContext,
    info: info
  };

}<|endoftext|>


<commit_before>
// ================================================================
// Container
// ================================================================

var Okdoki_Applet = {

  _data : {'EVERY SEC' : []},

  data : function (k, v) {
    var name = WWW_Applet.standardize_name(k);
    if (arguments.length == 2)
      this._data[name] = v;
    return this._data[name];
  },

  lead_zero : function (val) {
    if (val < 10)
      return '0' + val;
    return '' + val;
  },

  'new': function (source) {
    var app = WWW_Applet.new(source, Okdoki_Applet.Base_Funcs);
    app.data('boxs', {});
    app.data('every sec', []);
    return app;
  },

  target : function (name) {
    return $('div.okdoki_applet_' + WWW_Applet.standardize_name(name).toLowerCase());
  },

  Base_Funcs : {
    'About:'          : function (env, app) {
      return app.def_var('about', env.args[0]);
    },

    // === BOXes
    'box create'     : function (env, app) {
      var box    = $('

');
      var target = Okdoki_Applet.target(env.args[0]);

      var raw_name = env.args[1];
      if (raw_name) {
        var name   = WWW_Applet.standardize_name(env.args[1]);
        app.data('boxs')[name] = box;
      };

      box.addClass('box');
      target.append(box);

      return box;
    },

    'box #' : function (env, app) {
      var parent = app.read_var('about');
      if (!parent)
        return null;
      var box  = $($(parent).children('.box')[env.args[0]]);
      if (box.length == 0)
        return null;

      return box;
    },

    'first box'    : function (env, app) {
      env.args.unshift(1-1);
      return Okdoki_Applet.Base_Funcs['box #'](env, app);
    },

    'second box'    : function (env, app) {
      env.args.unshift(2-1);
      return Okdoki_Applet.Base_Funcs['box #'](env, app);
    },

    // === TEXT
    'text create'    : function (env, app) {
      var parent = app.read_var('about');
      if (!parent)
        return null;

      var box = $('');
      box.addClass('box');

      if (env.args.length == 1) {
        var txt = env.args[0];
      } else {
        var txt = env.args[1];
        var name   = WWW_Applet.standardize_name(env.args[0]);
        app.data('boxs')[name] = box;
      };

      box.text(txt);
      parent.append(box);

      return box;
    },

    'text update'    : not_ready ,

    'down letter case' : function (env, app) {
      var target = app.read_var('about');
      return target.css('textTransform', 'lowercase');
    },

    'up letter case' : function (env, app) {
      var target = app.read_var('about');
      return target.css('textTransform', 'uppercase');
    },

    'time now' : function (env, app) {
      var format = env.args[0];
      var now = new Date;
      format.replace(/\{(\.+)\}/g, function (sub, cap) {
        switch (cap) {

          case 'D':
            return Okdoki_Applet.lead_zero(now.getDate());
            break;

          case 'd':
            return now.getDate();
            break;

          case "H":
            return Okdoki_Applet.lead_zero(now.getHours());
            break;

          case "h":
            return now.getHours();
            break;

          case "M":
            return Okdoki_Applet.lead_zero(now.getMinutes());
            break;

          case "m":
            return now.getMinutes();
            break;

          case "S":
            return Okdoki_Applet.lead_zero(now.getMinutes());
            break;

          case "s":
            return now.getSeconds();
            break;

        }; // === switch

        switch (cap.toUpperCase()) {
          case 'DAY NAME':
            return 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' ')[now.getDay()]
            break;

          case 'DAY':
            return 'Sun Mon Tues Wed Thur Fri Sat'.split(' ')[now.getDay()]
            break;

          case 'P.A.M.':
            return (now.getHours() < 12) ? 'a.m.' : 'p.m.';
            break;

          case 'PAM':
            return (now.getHours() < 12) ? 'am' : 'pm';
            break;
        }; // === switch

        return cap;
      });
    },

    // === TIME
    'on every second' : function (env, app) {
      var run_this = function () {
        app.eval(env.args);
      };
      Okdoki_Applet.data('every sec').push(run_this);
      return true;
    }
  }
};

setTimeout(function () {
  _.each(Okdoki_Applet.data('every sec'), function (v, k) {
    v();
  });
}, 1000);

Okdoki_Applet.Base_Funcs['on every second'].use_raw = true;

// === Hello, World.
var source = [
  'log', ['Hello, World.']
];

var app = Okdoki_Applet.new(source);
app.run();

// === Create and Color Boxes/Text.
var source = [
  'About:', [ 'box create', ['workspace', 'The_Time'] ],
  'text create', ['7:00'],
  'text create', ['a.m.'],
  'text create', ['Friday'],

  'About:', ['second box', []],
  'up letter case', [],
];

var app = Okdoki_Applet.new(source);
app.run();

// === Clock
var source = [
  'About:', [ 'box create', ['workspace', 'The_Clock'] ],
  'text create', ['TIME', '-:--'],
  'text create', ['MERI', '*.*.'],
  'text create', ['DAY',  'Friday'],

  'on every second', [
    'text update', ['TIME', 'time NOW', ['{H}:{M}']],
    'text update', ['MERI', 'time NOW', ['{P.A.M.}']],
    'text update', ['DAY',  'time NOW', ['{Day name}']],
  ]
];

var app = Okdoki_Applet.new(source);
app.run();

// === Who's Crushing On Me?

function not_ready (env, app) {
  console['log']('Not ready: ' + env.name);
}

// ================================================================
// Helpers
// ================================================================

  // 'form . text_input' : {
    // args    : [req_name, {id: id, lines: num_of_lines}],
    // content : string,
    // run     : function (meta, args, content) {
      // return element('input', [args, 'id', 'name', {type: "text", ok_type: 'text_box'}], (content || ""));
    // }
  // }

  // , 'button' : {
    // args : [id, {type: button_type}],
    // content : req_string,
    // run : function (meta, args, content) {
      // return element('button', [args, 'id', {ok_type: 'button'}], content);
    // }
  // }

  // , 'link' :  {
    // args    : [href],
    // content : req_string,
    // run : function (meta, attrs, content) {
      // return element('a', [args, 'href', {ok_type:'link'}], content);
    // }
  // }

  // , 'on_click' : function (meta, content) {
    // meta.data.js.push(_.last(meta.data.eles), 'on_click', content);
    // return "";
  // }

// };

<commit_msg>Fixed: 'on every second' saves 'about' value.<commit_after>
// ================================================================
// Container
// ================================================================

var Okdoki_Applet = {

  _data : {'EVERY SEC' : []},

  data : function (k, v) {
    var name = WWW_Applet.standardize_name(k);
    if (arguments.length == 2)
      this._data[name] = v;
    return this._data[name];
  },

  lead_zero : function (val) {
    if (val < 10)
      return '0' + val;
    return '' + val;
  },

  'new': function (source) {
    var app = WWW_Applet.new(source, Okdoki_Applet.Base_Funcs);
    app.data('boxs', {});
    app.data('every sec', []);
    return app;
  },

  target : function (name) {
    return $('div.okdoki_applet_' + WWW_Applet.standardize_name(name).toLowerCase());
  },

  Base_Funcs : {
    'About:'          : function (env, app) {
      return app.def_var('about', env.args[0]);
    },

    // === BOXes
    'box create'     : function (env, app) {
      var box    = $('

');
      var target = Okdoki_Applet.target(env.args[0]);

      var raw_name = env.args[1];
      if (raw_name) {
        var name   = WWW_Applet.standardize_name(env.args[1]);
        app.data('boxs')[name] = box;
      };

      box.addClass('box');
      target.append(box);

      return box;
    },

    'box #' : function (env, app) {
      var parent = app.read_var('about');
      if (!parent)
        return null;
      var box  = $($(parent).children('.box')[env.args[0]]);
      if (box.length == 0)
        return null;

      return box;
    },

    'first box'    : function (env, app) {
      env.args.unshift(1-1);
      return Okdoki_Applet.Base_Funcs['box #'](env, app);
    },

    'second box'    : function (env, app) {
      env.args.unshift(2-1);
      return Okdoki_Applet.Base_Funcs['box #'](env, app);
    },

    // === TEXT
    'text create'    : function (env, app) {
      var parent = app.read_var('about');
      if (!parent)
        return null;

      var box = $('');
      box.addClass('box');

      if (env.args.length == 1) {
        var txt = env.args[0];
      } else {
        var txt = env.args[1];
        var name   = WWW_Applet.standardize_name(env.args[0]);
        app.data('boxs')[name] = box;
      };

      box.text(txt);
      parent.append(box);

      return box;
    },

    'text update'    : not_ready ,

    'down letter case' : function (env, app) {
      var target = app.read_var('about');
      return target.css('textTransform', 'lowercase');
    },

    'up letter case' : function (env, app) {
      var target = app.read_var('about');
      return target.css('textTransform', 'uppercase');
    },

    'time now' : function (env, app) {
      var format = env.args[0];
      var now = new Date;
      format.replace(/\{(\.+)\}/g, function (sub, cap) {
        switch (cap) {

          case 'D':
            return Okdoki_Applet.lead_zero(now.getDate());
            break;

          case 'd':
            return now.getDate();
            break;

          case "H":
            return Okdoki_Applet.lead_zero(now.getHours());
            break;

          case "h":
            return now.getHours();
            break;

          case "M":
            return Okdoki_Applet.lead_zero(now.getMinutes());
            break;

          case "m":
            return now.getMinutes();
            break;

          case "S":
            return Okdoki_Applet.lead_zero(now.getMinutes());
            break;

          case "s":
            return now.getSeconds();
            break;

        }; // === switch

        switch (cap.toUpperCase()) {
          case 'DAY NAME':
            return 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' ')[now.getDay()]
            break;

          case 'DAY':
            return 'Sun Mon Tues Wed Thur Fri Sat'.split(' ')[now.getDay()]
            break;

          case 'P.A.M.':
            return (now.getHours() < 12) ? 'a.m.' : 'p.m.';
            break;

          case 'PAM':
            return (now.getHours() < 12) ? 'am' : 'pm';
            break;
        }; // === switch

        return cap;
      });
    },

    // === TIME
    'on every second' : function (env, app) {
      app.local_def_var('about', app.read_var('about'));
      var run_this = function () {
        app.eval(env.args);
      };
      Okdoki_Applet.data('every sec').push(run_this);
      return true;
    }
  }
};

setTimeout(function () {
  _.each(Okdoki_Applet.data('every sec'), function (v, k) {
    v();
  });
}, 1000);

Okdoki_Applet.Base_Funcs['on every second'].use_raw = true;

// === Hello, World.
var source = [
  'log', ['Hello, World.']
];

var app = Okdoki_Applet.new(source);
app.run();

// === Create and Color Boxes/Text.
var source = [
  'About:', [ 'box create', ['workspace', 'The_Time'] ],
  'text create', ['7:00'],
  'text create', ['a.m.'],
  'text create', ['Friday'],

  'About:', ['second box', []],
  'up letter case', [],
];

var app = Okdoki_Applet.new(source);
app.run();

// === Clock
var source = [
  'About:', [ 'box create', ['workspace', 'The_Clock'] ],
  'text create', ['TIME', '-:--'],
  'text create', ['MERI', '*.*.'],
  'text create', ['DAY',  'Friday'],

  'on every second', [
    'text update', ['TIME', 'time NOW', ['{H}:{M}']],
    'text update', ['MERI', 'time NOW', ['{P.A.M.}']],
    'text update', ['DAY',  'time NOW', ['{Day name}']],
  ]
];

var app = Okdoki_Applet.new(source);
app.run();

// === Who's Crushing On Me?

function not_ready (env, app) {
  console['log']('Not ready: ' + env.name);
}

// ================================================================
// Helpers
// ================================================================

  // 'form . text_input' : {
    // args    : [req_name, {id: id, lines: num_of_lines}],
    // content : string,
    // run     : function (meta, args, content) {
      // return element('input', [args, 'id', 'name', {type: "text", ok_type: 'text_box'}], (content || ""));
    // }
  // }

  // , 'button' : {
    // args : [id, {type: button_type}],
    // content : req_string,
    // run : function (meta, args, content) {
      // return element('button', [args, 'id', {ok_type: 'button'}], content);
    // }
  // }

  // , 'link' :  {
    // args    : [href],
    // content : req_string,
    // run : function (meta, attrs, content) {
      // return element('a', [args, 'href', {ok_type:'link'}], content);
    // }
  // }

  // , 'on_click' : function (meta, content) {
    // meta.data.js.push(_.last(meta.data.eles), 'on_click', content);
    // return "";
  // }

// };

<|endoftext|>


<commit_before>'use strict';

var node_path = require('path');
var profile = require('../profile');

exports.shorthands = {
    p: 'short'
};

exports.options = {
    port: {
        type: Number,
        info: 'server port.',
        default: 0
    },

    open: {
        type: Boolean,
        info: 'if `true`, your browser will automatically open cortex server root when started.',
        default: true
    }
};

exports.info = 'Start cortex server';

exports.usage = '{{name}} server [options]';
<commit_msg>set options.open of command server to false<commit_after>'use strict';

var node_path = require('path');
var profile = require('../profile');

exports.shorthands = {
    p: 'short'
};

exports.options = {
    port: {
        type: Number,
        info: 'server port.',
        default: 0
    },

    open: {
        type: Boolean,
        info: 'if `true`, your browser will automatically open cortex server root when started.',
        default: false
    }
};

exports.info = 'Start cortex server';

exports.usage = '{{name}} server [options]';
<|endoftext|>


<commit_before>import Rx from 'rx';
import { Router, observeEvent } from 'esp-js/src';
import { PricingService, ExecutionService } from '../../../services';
import { logger } from '../../../system';
import { ModelBase, RegionManagerHelper } from '../../common';
import { TradeExecutionNotification, TextNotification, NotificationBase, NotificationType } from './';
import { RegionManager, RegionNames, view  } from '../../regions';
import { TradeStatus } from '../../../services/model';
import { SchedulerService, } from '../../../system';
const DISMISS_NOTIFICATION_AFTER_X_IN_MS = 4000;

import {
  GetSpotStreamRequest,
  SpotPrice,
  Direction,
  ExecuteTradeRequest,
  CurrencyPair,
  ExecuteTradeResponse
} from '../../../services/model';
import { SpotTileView } from '../views';

@view(SpotTileView)
export default class SpotTileModel extends ModelBase {
  // non view state
  _pricingService:PricingService;
  _executionService:ExecutionService;
  _executionDisposable:Rx.SerialDisposable;
  _priceSubscriptionDisposable:Rx.SerialDisposable;
  _toastNotificationTimerDisposable:Rx.SerialDisposable;
  _log:logger.Logger;
  _regionManagerHelper:RegionManagerHelper;

  // React doesn't seem to pickup ES6 properties (last time I looked it seemed to be because Babel doesn't spit them out as enumerable)
  // So we're just exposing the state as fields.
  currencyPair:CurrencyPair;
  currentSpotPrice:SpotPrice;
  notification:NotificationBase;
  tileTitle:string;
  notional:number;
  pricingConnected:boolean;
  executionConnected:boolean;
  isTradeExecutionInFlight:boolean;

  constructor(modelId:string,
              currencyPair:CurrencyPair, // in a real system you'd take a specific state object, not just a piece of state (currencyPair) as we do here
              router:Router,
              pricingService:PricingService,
              executionService:ExecutionService,
              regionManager:RegionManager,
              schedulerService:SchedulerService) {
    super(modelId, router);
    this._log = logger.create(`${this.modelId}:${currencyPair.symbol}`);// can't change ccy pair in this demo app, so reasonable to use the symbol in the logger name
    this._pricingService = pricingService;
    this._executionService = executionService;
    this._regionManager = regionManager;
    this._schedulerService = schedulerService;
    this.currencyPair = currencyPair;
    this._executionDisposable = new Rx.SerialDisposable();
    this.addDisposable(this._executionDisposable);
    this._priceSubscriptionDisposable = new Rx.SerialDisposable();
    this._toastNotificationTimerDisposable = new Rx.SerialDisposable();
    this.addDisposable(this._priceSubscriptionDisposable);
    this.addDisposable(this._toastNotificationTimerDisposable);

    this.tileTitle = `${currencyPair.base} / ${currencyPair.terms}`;
    this.notification = null;
    this.notional = 1000000;
    this.currentSpotPrice = null;

    // If things get much messier we could look at introducing a state machine, but for now we really only have these 3 conditions to worry about
    this.pricingConnected = false;
    this.executionConnected = false;
    this.isTradeExecutionInFlight = false;
    this._regionManagerHelper = new RegionManagerHelper(RegionNames.workspace, regionManager, this);
  }

  get hasNotification() {
    return this.notification !== null;
  }

  @observeEvent('init')
  _onInit() {
    this._log.info(`Cash tile starting for pair ${this.currencyPair.symbol}`);
    this._subscribeToPriceStream();
    this._subscribeToConnectionStatus();
    this._regionManagerHelper.addToRegion();
  }

  @observeEvent('tileClosed')
  _onTileClosed() {
    this._log.info(`Cash tile closing`);
    this._regionManager.removeFromRegion(RegionNames.workspace, this);
  }

  @observeEvent('popOutTile')
  _onPopOutTile() {
    this._log.info(`Popping out tile`);
    this._regionManagerHelper.popout(332, 190);
  }

  @observeEvent('tradeNotificationDismissed')
  _onTradeNotificationDismissed() {
    this._log.debug(`message dismissed`);
    this.notification = null;
    this._updatePricingDownStatusNotification();
  }

  @observeEvent('notionalChanged')
  _onNotionalChanged(e:{notional:number}) {
    this._log.info(`Updating notional to ${e.notional}`);
    this.notional = e.notional;
  }

  @observeEvent('executeTrade')
  _onExecuteTrade(e:{direction:Direction}) {
    if (this.pricingConnected && this.executionConnected) {
      // stop the price stream so the users can see what the traded
      let request = this._createTradeRequest(e.direction);
      this._log.info(`Will execute ${request.toString()}`);
      this.isTradeExecutionInFlight = true;

      this._executionDisposable.setDisposable(
        this._executionService.executeTrade(request)
         .subscribeWithRouter(
            this.router,
            this.modelId,
            (response:ExecuteTradeResponse) => {
              this.isTradeExecutionInFlight = false;
              this.notification = response.hasError
                ? TradeExecutionNotification.createForError(response.error)
                : TradeExecutionNotification.createForSuccess(response.trade);
              if (!response.hasError && response.trade.status === TradeStatus.Done) {
                this._toastNotificationTimerDisposable.setDisposable(
                  this._schedulerService.async(DISMISS_NOTIFICATION_AFTER_X_IN_MS, () => this.router.publishEvent(this.modelId, 'tradeNotificationDismissed', {}))
                );
              }
            },
            err => {
              this.isTradeExecutionInFlight = false;
              this._log.error(`Error executing ${request.toString()}. ${err}`, err);
              this.notification = TradeExecutionNotification.createForError(`Unknown stream error`);
            })
        );
    } else {
      this._log.warn(`Ignoring execute request as we can't trade with pricing and execution down`);
    }
  }

  _createTradeRequest(direction:Direction) {
    var spotRate = direction == Direction.Buy
      ? this.currentSpotPrice.ask.rawRate
      : this.currentSpotPrice.bid.rawRate;
    var dealtCurrency = direction == Direction.Buy ? this.currencyPair.base : this.currencyPair.terms;
    return new ExecuteTradeRequest(
      this.currencyPair.symbol,
      spotRate,
      direction.name,
      this.notional,
      dealtCurrency
    );
  }

  _subscribeToPriceStream() {
    this._priceSubscriptionDisposable.setDisposable(
      this._pricingService
        .getSpotPriceStream(new GetSpotStreamRequest(this.currencyPair.symbol))
        .subscribeWithRouter(
          this.router,
          this.modelId,
          (price:SpotPrice) => {
            // we don't update the price if we have an inflight trade, the users will want to see what they are buying
            if(!this.isTradeExecutionInFlight) {
              this.currentSpotPrice = price;
            }
          },
          err => {
            this._log.error('Error on getSpotPriceStream stream stream', err);
          }
        )
    );
  }

  _subscribeToConnectionStatus() {
    let serviceStatusStream = Rx.Observable.combineLatest(
      this._pricingService.serviceStatusStream,
      this._executionService.serviceStatusStream,
      (pricingStatus, executionStatus) => {
        return {
          pricingConnected: pricingStatus.isConnected,
          executionConnected: executionStatus.isConnected
        };
      }
    );
    this.addDisposable(
      serviceStatusStream.subscribeWithRouter(
        this.router,
        this._modelId,
        (tuple:{pricingConnected:boolean, executionConnected:boolean}) => {
          this.pricingConnected = tuple.pricingConnected;
          this.executionConnected = tuple.executionConnected;
          this._updatePricingDownStatusNotification();
        })
    );
  }

  _updatePricingDownStatusNotification() {
    if(this.notification === null || this.notification.notificationType === NotificationType.Text) {
        if (this.pricingConnected) {
          this.notification = null;
        } else {
          this.notification = new TextNotification('Pricing is unavailable');
        }
    }
  }
}
<commit_msg>correct scheduler syntax<commit_after>import Rx from 'rx';
import { Router, observeEvent } from 'esp-js/src';
import { PricingService, ExecutionService } from '../../../services';
import { logger } from '../../../system';
import { ModelBase, RegionManagerHelper } from '../../common';
import { TradeExecutionNotification, TextNotification, NotificationBase, NotificationType } from './';
import { RegionManager, RegionNames, view  } from '../../regions';
import { TradeStatus } from '../../../services/model';
import { SchedulerService, } from '../../../system';
const DISMISS_NOTIFICATION_AFTER_X_IN_MS = 4000;

import {
  GetSpotStreamRequest,
  SpotPrice,
  Direction,
  ExecuteTradeRequest,
  CurrencyPair,
  ExecuteTradeResponse
} from '../../../services/model';
import { SpotTileView } from '../views';

@view(SpotTileView)
export default class SpotTileModel extends ModelBase {
  // non view state
  _pricingService:PricingService;
  _executionService:ExecutionService;
  _executionDisposable:Rx.SerialDisposable;
  _priceSubscriptionDisposable:Rx.SerialDisposable;
  _toastNotificationTimerDisposable:Rx.SerialDisposable;
  _log:logger.Logger;
  _regionManagerHelper:RegionManagerHelper;

  // React doesn't seem to pickup ES6 properties (last time I looked it seemed to be because Babel doesn't spit them out as enumerable)
  // So we're just exposing the state as fields.
  currencyPair:CurrencyPair;
  currentSpotPrice:SpotPrice;
  notification:NotificationBase;
  tileTitle:string;
  notional:number;
  pricingConnected:boolean;
  executionConnected:boolean;
  isTradeExecutionInFlight:boolean;

  constructor(modelId:string,
              currencyPair:CurrencyPair, // in a real system you'd take a specific state object, not just a piece of state (currencyPair) as we do here
              router:Router,
              pricingService:PricingService,
              executionService:ExecutionService,
              regionManager:RegionManager,
              schedulerService:SchedulerService) {
    super(modelId, router);
    this._log = logger.create(`${this.modelId}:${currencyPair.symbol}`);// can't change ccy pair in this demo app, so reasonable to use the symbol in the logger name
    this._pricingService = pricingService;
    this._executionService = executionService;
    this._regionManager = regionManager;
    this._schedulerService = schedulerService;
    this.currencyPair = currencyPair;
    this._executionDisposable = new Rx.SerialDisposable();
    this.addDisposable(this._executionDisposable);
    this._priceSubscriptionDisposable = new Rx.SerialDisposable();
    this._toastNotificationTimerDisposable = new Rx.SerialDisposable();
    this.addDisposable(this._priceSubscriptionDisposable);
    this.addDisposable(this._toastNotificationTimerDisposable);

    this.tileTitle = `${currencyPair.base} / ${currencyPair.terms}`;
    this.notification = null;
    this.notional = 1000000;
    this.currentSpotPrice = null;

    // If things get much messier we could look at introducing a state machine, but for now we really only have these 3 conditions to worry about
    this.pricingConnected = false;
    this.executionConnected = false;
    this.isTradeExecutionInFlight = false;
    this._regionManagerHelper = new RegionManagerHelper(RegionNames.workspace, regionManager, this);
  }

  get hasNotification() {
    return this.notification !== null;
  }

  @observeEvent('init')
  _onInit() {
    this._log.info(`Cash tile starting for pair ${this.currencyPair.symbol}`);
    this._subscribeToPriceStream();
    this._subscribeToConnectionStatus();
    this._regionManagerHelper.addToRegion();
  }

  @observeEvent('tileClosed')
  _onTileClosed() {
    this._log.info(`Cash tile closing`);
    this._regionManager.removeFromRegion(RegionNames.workspace, this);
  }

  @observeEvent('popOutTile')
  _onPopOutTile() {
    this._log.info(`Popping out tile`);
    this._regionManagerHelper.popout(332, 190);
  }

  @observeEvent('tradeNotificationDismissed')
  _onTradeNotificationDismissed() {
    this._log.debug(`message dismissed`);
    this.notification = null;
    this._updatePricingDownStatusNotification();
  }

  @observeEvent('notionalChanged')
  _onNotionalChanged(e:{notional:number}) {
    this._log.info(`Updating notional to ${e.notional}`);
    this.notional = e.notional;
  }

  @observeEvent('executeTrade')
  _onExecuteTrade(e:{direction:Direction}) {
    if (this.pricingConnected && this.executionConnected) {
      // stop the price stream so the users can see what the traded
      let request = this._createTradeRequest(e.direction);
      this._log.info(`Will execute ${request.toString()}`);
      this.isTradeExecutionInFlight = true;

      this._executionDisposable.setDisposable(
        this._executionService.executeTrade(request)
         .subscribeWithRouter(
            this.router,
            this.modelId,
            (response:ExecuteTradeResponse) => {
              this.isTradeExecutionInFlight = false;
              this.notification = response.hasError
                ? TradeExecutionNotification.createForError(response.error)
                : TradeExecutionNotification.createForSuccess(response.trade);
              if (!response.hasError && response.trade.status === TradeStatus.Done) {
                this._toastNotificationTimerDisposable.setDisposable(
                  this._schedulerService.async.scheduleFuture('', DISMISS_NOTIFICATION_AFTER_X_IN_MS, () => this.router.publishEvent(this.modelId, 'tradeNotificationDismissed', {}))
                );
              }
            },
            err => {
              this.isTradeExecutionInFlight = false;
              this._log.error(`Error executing ${request.toString()}. ${err}`, err);
              this.notification = TradeExecutionNotification.createForError(`Unknown stream error`);
            })
        );
    } else {
      this._log.warn(`Ignoring execute request as we can't trade with pricing and execution down`);
    }
  }

  _createTradeRequest(direction:Direction) {
    var spotRate = direction == Direction.Buy
      ? this.currentSpotPrice.ask.rawRate
      : this.currentSpotPrice.bid.rawRate;
    var dealtCurrency = direction == Direction.Buy ? this.currencyPair.base : this.currencyPair.terms;
    return new ExecuteTradeRequest(
      this.currencyPair.symbol,
      spotRate,
      direction.name,
      this.notional,
      dealtCurrency
    );
  }

  _subscribeToPriceStream() {
    this._priceSubscriptionDisposable.setDisposable(
      this._pricingService
        .getSpotPriceStream(new GetSpotStreamRequest(this.currencyPair.symbol))
        .subscribeWithRouter(
          this.router,
          this.modelId,
          (price:SpotPrice) => {
            // we don't update the price if we have an inflight trade, the users will want to see what they are buying
            if(!this.isTradeExecutionInFlight) {
              this.currentSpotPrice = price;
            }
          },
          err => {
            this._log.error('Error on getSpotPriceStream stream stream', err);
          }
        )
    );
  }

  _subscribeToConnectionStatus() {
    let serviceStatusStream = Rx.Observable.combineLatest(
      this._pricingService.serviceStatusStream,
      this._executionService.serviceStatusStream,
      (pricingStatus, executionStatus) => {
        return {
          pricingConnected: pricingStatus.isConnected,
          executionConnected: executionStatus.isConnected
        };
      }
    );
    this.addDisposable(
      serviceStatusStream.subscribeWithRouter(
        this.router,
        this._modelId,
        (tuple:{pricingConnected:boolean, executionConnected:boolean}) => {
          this.pricingConnected = tuple.pricingConnected;
          this.executionConnected = tuple.executionConnected;
          this._updatePricingDownStatusNotification();
        })
    );
  }

  _updatePricingDownStatusNotification() {
    if(this.notification === null || this.notification.notificationType === NotificationType.Text) {
        if (this.pricingConnected) {
          this.notification = null;
        } else {
          this.notification = new TextNotification('Pricing is unavailable');
        }
    }
  }
}
<|endoftext|>


<commit_before>// Not using strict mode here because strict mode has an impact on evaled source code

/* eslint-disable no-unused-vars, camelcase */
// Yep, they are unused intentionally. They are just available for convenience reasons.
var webpage = require("webpage");
var system = require("system");
var fs = require("fs");
var webserver = require("webserver");
var child_process = require("child_process");
var configPath = system.args[1];
var config = JSON.parse(fs.read(configPath));
/*eslint-enable no-unused-vars, camelcase */
var pages = {};
var context = {};
var commandHandlers = {};
var socket = {};

/**
 * inits the communication between the phantom to the node.
 */
function init() {
    // Reads the websocket port from the stdin
    var line = system.stdin.readLine();
    var message = JSON.parse(line);

    socket = new WebSocket("ws://127.0.0.1:" + message.port + "/");
    socket.onerror = function (err) {
        console.log("Error on websocket connection: " + err);
    };
    socket.emit = function (event, data) {
        socket.send(JSON.stringify({ event: event, data: data }));
    };
    socket.onopen = function (evt) {
        clearTimeout(timer);
    };
    socket.onmessage = function (e) {
        var message = JSON.parse(e.data);
        var handler = commandHandlers[message.action];

        if (!handler) {
            throw new Error("Unknown action '" + message.action + "'");
        }

        handler(message);
    };
    var timer = setTimeout(function () {
        console.log("Timeout connecting back triggered... Trying again.");
        init();
    }, 5000);
}

/**
 * Returns a function that should be called to return the result for this message.
 *
 * @param {Object} message
 * @returns {resolve}
 */
function createResolver(message) {
    /**
     * @param {Object} data
     */
    function resolve(data) {
        write({
            status: "success",
            id: message.done ? null : message.id,
            data: data
        });
        message.done = true;
    }

    return resolve;
}

/**
 * Returns a function that should be called to indicate that this message yielded to an error.
 *
 * @param {Object} message
 * @returns {reject}
 */
function createRejecter(message) {
    /**
     * @param {Object} data
     */
    function reject(data) {
        var stack;

        try {
            throw new Error(data ? data.message || "Error" : "Error");
        } catch (err) {
            stack = err.stack;

            stack += "\n" +
                "    -----------------------------------------------------------------------\n" +
                message.from;

            data = {
                message: err.message,
                stack: stack
            };
        }

        write({
            status: "fail",
            id: message.done ? null : message.id,
            data: data
        });
        message.done = true;
    }

    return reject;
}

/**
 * Runs message.data.src in the given context.
 *
 * @param {Object} message
 * @param {Object} context
 */
function run(message, context) {
    var resolve = createResolver(message);
    var reject = createRejecter(message);

    try {
        evalSrc(message.data.src, context, resolve, reject);
    } catch (err) {
        reject(err);
    }
}

/**
 * Helper function for run() to avoid scope pollution. `context`, `resolve` and `reject` are needed according
 * to the serializeFn-module.
 *
 * @param {string} src
 * @param {object} context
 * @param {Function} resolve
 * @param {Function} reject
 */
function evalSrc(src, context, resolve, reject) {
    eval(src); // eslint-disable-line
}

/**
 * Helper function that stringifies the given object and writes it via the websocket
 *
 * @param {Object} message
 */
function write(message) {
    socket.send(JSON.stringify(message));
}

/**
 * Collection of request-able commands (as defined in the action-property of the message).
 *
 * @type {Object}
 */
commandHandlers = {

    /**
     * The ping command is a neat trick so PhantomJS isn't hang
     * while waiting for an asynchronous event. A ping-command is sent by node as long as it
     * waits for PhantomJS to respond. We're responding with a pong to tell node that we're waiting
     * for the next ping.
     */
    ping: function () {
        write({
            status: "pong"
        });
    },

    /**
     * Runs message.data.src in the default context.
     *
     * @param {Object} message
     */
    run: function (message) {
        run(message, context);
    },

    /**
     * Runs message.data.src in the requested page context. If the page context doesn't exist, a new webpage
     * is created implicitly.
     *
     * @param {Object} message
     */
    "run-on-page": function (message) {
        var pageId = message.data.pageId;
        var page = pages[pageId];

        if (!page) {
            pages[pageId] = page = webpage.create();
            page.pageId = pageId;
        }

        run(message, page);
    }
};

/* eslint-disable no-unused-vars, camelcase */
// This function is unused on this context, It will be used by the user
/**
 * User function that pass a command to call an function on the node.js module
 *
 * @param {Object} data
 * @param {Object} page
 */
function nodeCallback(data, page) {
    var dataToSend = {
        status: "callback",
        data: data
    };

    if (page) {
        dataToSend.pageId = page.pageId;
    }

    write(dataToSend);
}
/*eslint-enable no-unused-vars, camelcase */

// remove the config as it is not needed anymore
fs.remove(configPath);

// send hi to node so node knows that we're ready
system.stdout.writeLine("message to node: hi");

init();<commit_msg>Minor fix<commit_after>// Not using strict mode here because strict mode has an impact on evaled source code

/* eslint-disable no-unused-vars, camelcase */
// Yep, they are unused intentionally. They are just available for convenience reasons.
var webpage = require("webpage");
var system = require("system");
var fs = require("fs");
var webserver = require("webserver");
var child_process = require("child_process");
var configPath = system.args[1];
var config = JSON.parse(fs.read(configPath));
/*eslint-enable no-unused-vars, camelcase */
var pages = {};
var context = {};
var commandHandlers = {};
var socket = {};

/**
 * inits the communication between the phantom to the node.
 */
function init() {
    // Reads the websocket port from the stdin
    var line = system.stdin.readLine();
    var message = JSON.parse(line);
    var timer;

    socket = new window.WebSocket("ws://127.0.0.1:" + message.port + "/");
    socket.onerror = function (err) {
        console.log("Error on websocket connection: " + err);
    };
    socket.emit = function (event, data) {
        socket.send(JSON.stringify({ event: event, data: data }));
    };
    socket.onopen = function (evt) {
        clearTimeout(timer);
    };
    socket.onmessage = function (e) {
        var message = JSON.parse(e.data);
        var handler = commandHandlers[message.action];

        if (!handler) {
            throw new Error("Unknown action '" + message.action + "'");
        }

        handler(message);
    };
    timer = setTimeout(function () {
        console.log("Timeout connecting back triggered... Trying again.");
        init();
    }, 5000);
}

/**
 * Returns a function that should be called to return the result for this message.
 *
 * @param {Object} message
 * @returns {resolve}
 */
function createResolver(message) {
    /**
     * @param {Object} data
     */
    function resolve(data) {
        write({
            status: "success",
            id: message.done ? null : message.id,
            data: data
        });
        message.done = true;
    }

    return resolve;
}

/**
 * Returns a function that should be called to indicate that this message yielded to an error.
 *
 * @param {Object} message
 * @returns {reject}
 */
function createRejecter(message) {
    /**
     * @param {Object} data
     */
    function reject(data) {
        var stack;

        try {
            throw new Error(data ? data.message || "Error" : "Error");
        } catch (err) {
            stack = err.stack;

            stack += "\n" +
                "    -----------------------------------------------------------------------\n" +
                message.from;

            data = {
                message: err.message,
                stack: stack
            };
        }

        write({
            status: "fail",
            id: message.done ? null : message.id,
            data: data
        });
        message.done = true;
    }

    return reject;
}

/**
 * Runs message.data.src in the given context.
 *
 * @param {Object} message
 * @param {Object} context
 */
function run(message, context) {
    var resolve = createResolver(message);
    var reject = createRejecter(message);

    try {
        evalSrc(message.data.src, context, resolve, reject);
    } catch (err) {
        reject(err);
    }
}

/**
 * Helper function for run() to avoid scope pollution. `context`, `resolve` and `reject` are needed according
 * to the serializeFn-module.
 *
 * @param {string} src
 * @param {object} context
 * @param {Function} resolve
 * @param {Function} reject
 */
function evalSrc(src, context, resolve, reject) {
    eval(src); // eslint-disable-line
}

/**
 * Helper function that stringifies the given object and writes it via the websocket
 *
 * @param {Object} message
 */
function write(message) {
    socket.send(JSON.stringify(message));
}

/**
 * Collection of request-able commands (as defined in the action-property of the message).
 *
 * @type {Object}
 */
commandHandlers = {

    /**
     * The ping command is a neat trick so PhantomJS isn't hang
     * while waiting for an asynchronous event. A ping-command is sent by node as long as it
     * waits for PhantomJS to respond. We're responding with a pong to tell node that we're waiting
     * for the next ping.
     */
    ping: function () {
        write({
            status: "pong"
        });
    },

    /**
     * Runs message.data.src in the default context.
     *
     * @param {Object} message
     */
    run: function (message) {
        run(message, context);
    },

    /**
     * Runs message.data.src in the requested page context. If the page context doesn't exist, a new webpage
     * is created implicitly.
     *
     * @param {Object} message
     */
    "run-on-page": function (message) {
        var pageId = message.data.pageId;
        var page = pages[pageId];

        if (!page) {
            pages[pageId] = page = webpage.create();
            page.pageId = pageId;
        }

        run(message, page);
    }
};

/* eslint-disable no-unused-vars, camelcase */
// This function is unused on this context, It will be used by the user
/**
 * User function that pass a command to call an function on the node.js module
 *
 * @param {Object} data
 * @param {Object} page
 */
function nodeCallback(data, page) {
    var dataToSend = {
        status: "callback",
        data: data
    };

    if (page) {
        dataToSend.pageId = page.pageId;
    }

    write(dataToSend);
}
/*eslint-enable no-unused-vars, camelcase */

// remove the config as it is not needed anymore
fs.remove(configPath);

// send hi to node so node knows that we're ready
system.stdout.writeLine("message to node: hi");

init();<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var times = require('../times');
var units = require('../units')();

var offset = times.mins(2.5).msecs;

function init() {

  var bgnow = {
    name: 'bgnow'
    , label: 'BG Now'
    , pluginType: 'pill-primary'
  };

  bgnow.setProperties = function setProperties (sbx) {
    sbx.offerProperty('bgnow', function setBGNow ( ) {
      var buckets = bgnow.fillBuckets(sbx);

      var property = {
        buckets: buckets
      };

      var delta = bgnow.calcDelta(buckets, sbx);
      if (!_.isEmpty(delta)) {
        property.delta = delta;
      }

      return property;
    });
  };

  bgnow.fillBuckets = function fillBuckets (sbx, opts) {

    var bucketCount = (opts && opts.bucketCount) || 4;
    var bucketMins = (opts && opts.bucketMins) || 5;
    var bucketMsecs = times.mins(bucketMins).msecs;

    var lastSGVMills = sbx.lastSGVMills();

    var buckets = _.times(bucketCount, function createBucket (index) {
      var fromMills = lastSGVMills + offset - (index * bucketMsecs);
      return {
        index: index
        , fromMills: fromMills
        , toMills: fromMills + bucketMsecs
        , sgvs: [ ]
      }
    });

    _.takeRightWhile(sbx.data.sgvs, function addToBucket (sgv) {

      //if in the future, return true and keep taking right
      if (sgv.mills > sbx.time) {
        return true;
      }

      var bucket = _.find(buckets, function containsSGV (bucket) {
        return sgv.mills >= bucket.fromMills && sgv.mills <= bucket.toMills;
      });

      if (bucket) {
        bucket.sgvs.push(sgv);
      }

      return bucket;
    });

    _.forEach(buckets, bgnow.analyzeBucket);

//    _.forEach(buckets, function log (bucket) {
//      console.info('>>>bucket', bucket);
//    });

    return buckets;
  };

  function notError (entry) {
    return entry.mgdl > 39; //TODO maybe lower instead of expecting dexcom?
  }

  function isError (entry) {
    return entry.mgdl < 39;
  }

  bgnow.analyzeBucket = function analyzeBucket (bucket) {

    if (_.isEmpty(bucket.sgvs)) {
      bucket.isEmpty = true
      return;
    }

    var details = { };

    var mean = _.chain(bucket.sgvs)
      .filter(notError)
      .meanBy('mgdl')
      .value();

    if (_.isNumber(mean)) {
      details.mean = mean;
    }

    var mostRecent = _.chain(bucket.sgvs)
      .filter(notError)
      .maxBy('mills')
      .value();

    var mills = mostRecent && mostRecent.mills;

    if (_.isNumber(mills)) {
      details.mills = mills;
    }

    var errors = _.filter(bucket.sgvs, isError);

    if (!_.isEmpty(errors)) {
      details.errors = errors;
    }

    _.merge(bucket, details);
  };

  bgnow.calcDelta = function calcDelta (buckets, sbx) {

    var first = _.find(buckets, function notEmpty (bucket) {
      return bucket && !bucket.isEmpty
    });

    if (_.isEmpty(first)) {
      console.info('>>>all buckets are empty', buckets);
      return null;
    }

    var next = _.chain(buckets).find(function afterFirstNotEmpty (bucket) {
      return bucket.mills < first.mills && !bucket.isEmpty;
    }).value();

    if (_.isEmpty(next)) {
      console.info('>>>next bucket not found', buckets);
      return null;
    }

    console.info('found buckets, first:', first, 'next:', next);

    var delta = {
      absolute: first.mean - next.mean
      , elapsedMins: (first.mills - next.mills) / times.min().msecs
    };

    delta.interpolated = delta.elapsedMins > 9;

    delta.mean5MinsAgo = delta.interpolated ?
      first.mean - delta.absolute / delta.elapsedMins * 5 : first.mean - delta.absolute;

    delta.mgdl = Math.round(first.mean - delta.mean5MinsAgo);

    delta.scaled = sbx.settings.units === 'mmol' ?
      sbx.roundBGToDisplayFormat(sbx.scaleMgdl(first.mean) - sbx.scaleMgdl(delta.mean5MinsAgo)) : delta.mgdl;

    delta.display = (delta.scaled >= 0 ? '+' : '') + delta.scaled;

    return delta;

  };

  bgnow.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.bgnow && sbx.properties.bgnow.delta;

    var info = [];
    var display = prop && prop.display;

    if (prop && prop.interpolated) {
      display += ' *';
      info.push({label: 'Elapsed Time', value: Math.round(prop.elapsedMins) + ' mins'});
      info.push({label: 'Absolute Delta', value: sbx.roundBGToDisplayFormat(sbx.scaleMgdl(prop.absolute)) + ' ' + sbx.unitsLabel});
      info.push({label: 'Interpolated', value: sbx.roundBGToDisplayFormat(sbx.scaleMgdl(prop.mean5MinsAgo)) + ' ' + sbx.unitsLabel});
    }

    sbx.pluginBase.updatePillText({
      name: 'delta'
      , label: 'BG Delta'
      , pluginType: 'pill-major'
      , pillFlip: true
    }, {
      value: display
      , label: sbx.unitsLabel
      , info: info
    });
  };

  return bgnow;

}

module.exports = init;
<commit_msg>clean up<commit_after>'use strict';

var _ = require('lodash');
var times = require('../times');

var offset = times.mins(2.5).msecs;

function init() {

  var bgnow = {
    name: 'bgnow'
    , label: 'BG Now'
    , pluginType: 'pill-primary'
  };

  bgnow.setProperties = function setProperties (sbx) {
    sbx.offerProperty('bgnow', function setBGNow ( ) {
      var buckets = bgnow.fillBuckets(sbx);

      var property = {
        buckets: buckets
      };

      var delta = bgnow.calcDelta(buckets, sbx);
      if (!_.isEmpty(delta)) {
        property.delta = delta;
      }

      return property;
    });
  };

  bgnow.fillBuckets = function fillBuckets (sbx, opts) {

    var bucketCount = (opts && opts.bucketCount) || 4;
    var bucketMins = (opts && opts.bucketMins) || 5;
    var bucketMsecs = times.mins(bucketMins).msecs;

    var lastSGVMills = sbx.lastSGVMills();

    var buckets = _.times(bucketCount, function createBucket (index) {
      var fromMills = lastSGVMills + offset - (index * bucketMsecs);
      return {
        index: index
        , fromMills: fromMills
        , toMills: fromMills + bucketMsecs
        , sgvs: [ ]
      };
    });

    _.takeRightWhile(sbx.data.sgvs, function addToBucket (sgv) {

      //if in the future, return true and keep taking right
      if (sgv.mills > sbx.time) {
        return true;
      }

      var bucket = _.find(buckets, function containsSGV (bucket) {
        return sgv.mills >= bucket.fromMills && sgv.mills <= bucket.toMills;
      });

      if (bucket) {
        bucket.sgvs.push(sgv);
      }

      return bucket;
    });

    _.forEach(buckets, bgnow.analyzeBucket);

//    _.forEach(buckets, function log (bucket) {
//      console.info('>>>bucket', bucket);
//    });

    return buckets;
  };

  function notError (entry) {
    return entry.mgdl > 39; //TODO maybe lower instead of expecting dexcom?
  }

  function isError (entry) {
    return entry.mgdl < 39;
  }

  bgnow.analyzeBucket = function analyzeBucket (bucket) {

    if (_.isEmpty(bucket.sgvs)) {
      bucket.isEmpty = true;
      return;
    }

    var details = { };

    var mean = _.chain(bucket.sgvs)
      .filter(notError)
      .meanBy('mgdl')
      .value();

    if (_.isNumber(mean)) {
      details.mean = mean;
    }

    var mostRecent = _.chain(bucket.sgvs)
      .filter(notError)
      .maxBy('mills')
      .value();

    var mills = mostRecent && mostRecent.mills;

    if (_.isNumber(mills)) {
      details.mills = mills;
    }

    var errors = _.filter(bucket.sgvs, isError);

    if (!_.isEmpty(errors)) {
      details.errors = errors;
    }

    _.merge(bucket, details);
  };

  bgnow.calcDelta = function calcDelta (buckets, sbx) {

    var first = _.find(buckets, function notEmpty (bucket) {
      return bucket && !bucket.isEmpty;
    });

    if (_.isEmpty(first)) {
      console.info('>>>all buckets are empty', buckets);
      return null;
    }

    var next = _.chain(buckets).find(function afterFirstNotEmpty (bucket) {
      return bucket.mills < first.mills && !bucket.isEmpty;
    }).value();

    if (_.isEmpty(next)) {
      console.info('>>>next bucket not found', buckets);
      return null;
    }

    console.info('found buckets, first:', first, 'next:', next);

    var delta = {
      absolute: first.mean - next.mean
      , elapsedMins: (first.mills - next.mills) / times.min().msecs
    };

    delta.interpolated = delta.elapsedMins > 9;

    delta.mean5MinsAgo = delta.interpolated ?
      first.mean - delta.absolute / delta.elapsedMins * 5 : first.mean - delta.absolute;

    delta.mgdl = Math.round(first.mean - delta.mean5MinsAgo);

    delta.scaled = sbx.settings.units === 'mmol' ?
      sbx.roundBGToDisplayFormat(sbx.scaleMgdl(first.mean) - sbx.scaleMgdl(delta.mean5MinsAgo)) : delta.mgdl;

    delta.display = (delta.scaled >= 0 ? '+' : '') + delta.scaled;

    return delta;

  };

  bgnow.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.bgnow && sbx.properties.bgnow.delta;

    var info = [];
    var display = prop && prop.display;

    if (prop && prop.interpolated) {
      display += ' *';
      info.push({label: 'Elapsed Time', value: Math.round(prop.elapsedMins) + ' mins'});
      info.push({label: 'Absolute Delta', value: sbx.roundBGToDisplayFormat(sbx.scaleMgdl(prop.absolute)) + ' ' + sbx.unitsLabel});
      info.push({label: 'Interpolated', value: sbx.roundBGToDisplayFormat(sbx.scaleMgdl(prop.mean5MinsAgo)) + ' ' + sbx.unitsLabel});
    }

    sbx.pluginBase.updatePillText({
      name: 'delta'
      , label: 'BG Delta'
      , pluginType: 'pill-major'
      , pillFlip: true
    }, {
      value: display
      , label: sbx.unitsLabel
      , info: info
    });
  };

  return bgnow;

}

module.exports = init;
<|endoftext|>


<commit_before>var path = require('path');
var p = require('pfork');
var fs = require('fs');
var fse = require('fs-extra');
var EventEmitter = require('events').EventEmitter;
var pluginMgr = new EventEmitter();
var extend = require('util')._extend;
var colors = require('colors/safe');
var comUtil = require('../util');
var util = require('./util');
var config = require('../config');
var getPluginsSync = require('./get-plugins-sync');
var getPlugin = require('./get-plugins');
var rulesMgr = require('../rules');
var RulesMgr = rulesMgr.Rules;
var PLUGIN_MAIN = path.join(__dirname, './load-plugin');
var PLUGIN_URL_RE = /^(?:http|ws)s?:\/\/([\da-z]+)\.local\.whistlejs\.com\//i;
var RULE_VALUE_HEADER = 'x-whistle-rule-value';
var SSL_FLAG_HEADER = 'x-whistle-https';
var INTERVAL = 6000;
var UTF8_OPTIONS = {encoding: 'utf8'};
var plugins = getPluginsSync();
var rules = plugins.rules;
plugins = plugins.plugins;

function sortRules(list) {
	return list.sort(function(a, b) {
					return a > b ? 1 : -1;
				});
}

pluginMgr.on('updateRules', function() {
	rulesMgr.clearAppend();
	sortRules(Object.keys(rules)).forEach(function(name) {
		var plugin = rules[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
		rulesMgr.append(plugin.rules, plugin.path);
	});
});
parsePluginRules();
pluginMgr.emit('updateRules', rules);

function parsePluginRules() {
	sortRules(Object.keys(plugins)).forEach(function(name) {
		var plugin = plugins[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
	});
}

function showVerbose(tag, oldData, newData) {
	if (!config.debugMode) {
		return;
	}
	var uninstallData, installData, updateData;
	Object.keys(oldData).forEach(function(name) {
		var oldItem = oldData[name];
		var newItem = newData[name];
		if (!newItem) {
			uninstallData = uninstallData || {};
			uninstallData[name] = oldItem;
		} else if (newItem.path != oldItem.path || newItem.mtime != oldItem.mtime) {
			updateData = updateData || {};
			updateData[name] = newItem;
		}
	});

	Object.keys(newData).forEach(function(name) {
		if (!oldData[name]) {
			installData = installData || {};
			installData[name] = newData[name];
		}
	});

	if (uninstallData || installData || updateData) {
		console.log('\n***********[%s] %s has changed***********', comUtil.formatDate(), tag + 's');
	}

	uninstallData && Object.keys(uninstallData).forEach(function(name) {
		console.log(colors.red('[' + comUtil.formatDate(new Date(uninstallData[name].mtime)) + '] [uninstall ' + tag + '] ' + name.slice(0, -1)));
	});
	installData && Object.keys(installData).forEach(function(name) {
		console.log(colors.green('[' + comUtil.formatDate(new Date(installData[name].mtime)) + '] [install ' + tag + '] ' + name.slice(0, -1)));
	});
	updateData && Object.keys(updateData).forEach(function(name) {
		console.log(colors.yellow('[' + comUtil.formatDate(new Date(updateData[name].mtime)) + '] [update ' + tag + '] ' + name.slice(0, -1)));
	});
}

function readPackages(obj, callback) {
	var _plugins = {};
	var _rules = {};
	var count = 0;
	Object.keys(obj).forEach(function(name) {
		var pkg = plugins[name] || rules[name];
		var newPkg = obj[name];
		if (!pkg || pkg.path != newPkg.path || pkg.mtime != newPkg.mtime) {
			++count;
			fse.readJson(newPkg.pkgPath, function(err, pkg) {
				if (pkg && pkg.version) {
					newPkg.version = pkg.version;
					newPkg.homepage = util.getHomePageFromPackage(pkg);
					var pureRules = !!pkg.pureRules;
					newPkg.pureRules = pureRules;
					if (!pureRules) {
						_plugins[name] = newPkg;
					}
					fs.readFile(path.join(path.join(newPkg.path, 'rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
						rulesText = comUtil.trim(rulesText);
						newPkg.rules = rulesText;
						if (rulesText) {
							_rules[name] = newPkg;
						}
						fs.readFile(path.join(path.join(newPkg.path, '_rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
							newPkg._rules = comUtil.trim(rulesText);
							if (--count <= 0) {
								callback(_plugins, _rules);
							}
						});
					});
				}

			});

		} else {
			if (pkg.pureRules || pkg.rules) {
				_rules[name] = pkg;
			}

			if (!pkg.pureRules) {
				_plugins[name] = pkg;
			}
		}
	});

	if (count <= 0) {
		callback(_plugins, _rules);
	}
}

(function update() {
	setTimeout(function() {
		getPlugin(function(result) {
			readPackages(result, function(_plugins, _rules) {
				var hasUpdateRules;
				var rulesKeys = Object.keys(rules);
				var _rulesKeys = Object.keys(_rules);
				hasUpdateRules = rulesKeys.length != _rulesKeys.length;

				if (!hasUpdateRules) {
					for (var i = 0, len = rulesKeys.length; i < len; i++) {
						var name = rulesKeys[i];
						var rule = rules[name];
						var _rule = _rules[name];
						if (!rule || !_rule || rule.rules != _rule.rules) {
							hasUpdateRules = true;
							break;
						}
					}
				}

				showVerbose('rule', rules, _rules);
				rules = _rules;
				hasUpdateRules && pluginMgr.emit('updateRules', rules);

				var updatePlugins, uninstallPlugins;
				Object.keys(plugins).forEach(function(name) {
					var plugin = plugins[name];
					var newPlugin = _plugins[name];
					if (!newPlugin) {
						uninstallPlugins = uninstallPlugins || {};
						uninstallPlugins[name] = plugin;
					} else if (newPlugin.path != plugin.path || newPlugin.mtime != plugin.mtime) {
						updatePlugins = updatePlugins || {};
						updatePlugins[name] = newPlugin;
					}
				});
				showVerbose('plugin', plugins, _plugins);
				plugins = _plugins;
				uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);
				updatePlugins && pluginMgr.emit('update', updatePlugins);
				parsePluginRules();
				update();
			});
		});
	}, INTERVAL);
})();

pluginMgr.RULE_VALUE_HEADER = RULE_VALUE_HEADER;
pluginMgr.SSL_FLAG_HEADER = SSL_FLAG_HEADER;

pluginMgr.loadPlugin = function(plugin, callback) {
	config.debugMode && console.log(colors.cyan('[' + comUtil.formatDate() + '] [access plugin] ' + plugin.path));
	p.fork({
		script: PLUGIN_MAIN,
		value: plugin.path,
		RULE_VALUE_HEADER: RULE_VALUE_HEADER,
		SSL_FLAG_HEADER: SSL_FLAG_HEADER,
		debugMode: config.debugMode
	}, function(err, ports, child) {
		callback && callback(err, ports, child);
		if (config.debugMode) {
			if (err) {
				console.log(colors.red(err));
			} else if (!child.debugMode) {
				child.debugMode = true;
				child.on('data', function(data) {
					if (data && data.type == 'console.log') {
						console.log('[' + comUtil.formatDate() + '] [plugin] [' + plugin.path.substring(plugin.path.lastIndexOf('.') + 1) + ']', data.message);
					}
				});
				child.sendData({
					type: 'console.log',
					status: 'ready'
				});
			}
		}
	});
};

pluginMgr.stopPlugin = function(plugin) {
	p.kill({
		script: PLUGIN_MAIN,
		value: plugin.path
	}, 10000);
};

pluginMgr.getPlugins = function() {
	return plugins;
};

pluginMgr.getPlugin = function(protocol) {
	return plugins[protocol];
};

pluginMgr.getPluginByRuleUrl = function(ruleUrl) {
	if (!ruleUrl || typeof ruleUrl != 'string') {
		return;
	}
	var index = ruleUrl.indexOf(':');
	return index == -1 ? null : plugins[ruleUrl.substring(0, index + 1)];
};

pluginMgr.getPluginByHomePage = function(url) {
	return PLUGIN_URL_RE.test(url)
				&& plugins[RegExp.$1 + ':'];
};

pluginMgr.getRules = function() {
	return rules;
};

module.exports = pluginMgr;<commit_msg>feat: sort plugin rules packages<commit_after>var path = require('path');
var p = require('pfork');
var fs = require('fs');
var fse = require('fs-extra');
var EventEmitter = require('events').EventEmitter;
var pluginMgr = new EventEmitter();
var extend = require('util')._extend;
var colors = require('colors/safe');
var comUtil = require('../util');
var util = require('./util');
var config = require('../config');
var getPluginsSync = require('./get-plugins-sync');
var getPlugin = require('./get-plugins');
var rulesMgr = require('../rules');
var RulesMgr = rulesMgr.Rules;
var PLUGIN_MAIN = path.join(__dirname, './load-plugin');
var PLUGIN_URL_RE = /^(?:http|ws)s?:\/\/([\da-z]+)\.local\.whistlejs\.com\//i;
var RULE_VALUE_HEADER = 'x-whistle-rule-value';
var SSL_FLAG_HEADER = 'x-whistle-https';
var INTERVAL = 6000;
var UTF8_OPTIONS = {encoding: 'utf8'};
var plugins = getPluginsSync();
var rules = plugins.rules;
plugins = plugins.plugins;

pluginMgr.on('updateRules', function() {
	rulesMgr.clearAppend();
	Object.keys(rules).sort(function(a, b) {
		return a > b ? 1 : -1;
	}).forEach(function(name) {
		var plugin = rules[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
		rulesMgr.append(plugin.rules, plugin.path);
	});
});
parsePluginRules();
pluginMgr.emit('updateRules', rules);

function parsePluginRules() {
	Object.keys(plugins).forEach(function(name) {
		var plugin = plugins[name];
		if (plugin._rules && !plugin.rulesMgr) {
			plugin.rulesMgr = new RulesMgr();
			plugin.rulesMgr.parse(plugin._rules, plugin.path);
		}
	});
}

function showVerbose(tag, oldData, newData) {
	if (!config.debugMode) {
		return;
	}
	var uninstallData, installData, updateData;
	Object.keys(oldData).forEach(function(name) {
		var oldItem = oldData[name];
		var newItem = newData[name];
		if (!newItem) {
			uninstallData = uninstallData || {};
			uninstallData[name] = oldItem;
		} else if (newItem.path != oldItem.path || newItem.mtime != oldItem.mtime) {
			updateData = updateData || {};
			updateData[name] = newItem;
		}
	});

	Object.keys(newData).forEach(function(name) {
		if (!oldData[name]) {
			installData = installData || {};
			installData[name] = newData[name];
		}
	});

	if (uninstallData || installData || updateData) {
		console.log('\n***********[%s] %s has changed***********', comUtil.formatDate(), tag + 's');
	}

	uninstallData && Object.keys(uninstallData).forEach(function(name) {
		console.log(colors.red('[' + comUtil.formatDate(new Date(uninstallData[name].mtime)) + '] [uninstall ' + tag + '] ' + name.slice(0, -1)));
	});
	installData && Object.keys(installData).forEach(function(name) {
		console.log(colors.green('[' + comUtil.formatDate(new Date(installData[name].mtime)) + '] [install ' + tag + '] ' + name.slice(0, -1)));
	});
	updateData && Object.keys(updateData).forEach(function(name) {
		console.log(colors.yellow('[' + comUtil.formatDate(new Date(updateData[name].mtime)) + '] [update ' + tag + '] ' + name.slice(0, -1)));
	});
}

function readPackages(obj, callback) {
	var _plugins = {};
	var _rules = {};
	var count = 0;
	Object.keys(obj).forEach(function(name) {
		var pkg = plugins[name] || rules[name];
		var newPkg = obj[name];
		if (!pkg || pkg.path != newPkg.path || pkg.mtime != newPkg.mtime) {
			++count;
			fse.readJson(newPkg.pkgPath, function(err, pkg) {
				if (pkg && pkg.version) {
					newPkg.version = pkg.version;
					newPkg.homepage = util.getHomePageFromPackage(pkg);
					var pureRules = !!pkg.pureRules;
					newPkg.pureRules = pureRules;
					if (!pureRules) {
						_plugins[name] = newPkg;
					}
					fs.readFile(path.join(path.join(newPkg.path, 'rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
						rulesText = comUtil.trim(rulesText);
						newPkg.rules = rulesText;
						if (rulesText) {
							_rules[name] = newPkg;
						}
						fs.readFile(path.join(path.join(newPkg.path, '_rules.txt')), UTF8_OPTIONS, function(err, rulesText) {
							newPkg._rules = comUtil.trim(rulesText);
							if (--count <= 0) {
								callback(_plugins, _rules);
							}
						});
					});
				}

			});

		} else {
			if (pkg.pureRules || pkg.rules) {
				_rules[name] = pkg;
			}

			if (!pkg.pureRules) {
				_plugins[name] = pkg;
			}
		}
	});

	if (count <= 0) {
		callback(_plugins, _rules);
	}
}

(function update() {
	setTimeout(function() {
		getPlugin(function(result) {
			readPackages(result, function(_plugins, _rules) {
				var hasUpdateRules;
				var rulesKeys = Object.keys(rules);
				var _rulesKeys = Object.keys(_rules);
				hasUpdateRules = rulesKeys.length != _rulesKeys.length;

				if (!hasUpdateRules) {
					for (var i = 0, len = rulesKeys.length; i < len; i++) {
						var name = rulesKeys[i];
						var rule = rules[name];
						var _rule = _rules[name];
						if (!rule || !_rule || rule.rules != _rule.rules) {
							hasUpdateRules = true;
							break;
						}
					}
				}

				showVerbose('rule', rules, _rules);
				rules = _rules;
				hasUpdateRules && pluginMgr.emit('updateRules', rules);

				var updatePlugins, uninstallPlugins;
				Object.keys(plugins).forEach(function(name) {
					var plugin = plugins[name];
					var newPlugin = _plugins[name];
					if (!newPlugin) {
						uninstallPlugins = uninstallPlugins || {};
						uninstallPlugins[name] = plugin;
					} else if (newPlugin.path != plugin.path || newPlugin.mtime != plugin.mtime) {
						updatePlugins = updatePlugins || {};
						updatePlugins[name] = newPlugin;
					}
				});
				showVerbose('plugin', plugins, _plugins);
				plugins = _plugins;
				uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);
				updatePlugins && pluginMgr.emit('update', updatePlugins);
				parsePluginRules();
				update();
			});
		});
	}, INTERVAL);
})();

pluginMgr.RULE_VALUE_HEADER = RULE_VALUE_HEADER;
pluginMgr.SSL_FLAG_HEADER = SSL_FLAG_HEADER;

pluginMgr.loadPlugin = function(plugin, callback) {
	config.debugMode && console.log(colors.cyan('[' + comUtil.formatDate() + '] [access plugin] ' + plugin.path));
	p.fork({
		script: PLUGIN_MAIN,
		value: plugin.path,
		RULE_VALUE_HEADER: RULE_VALUE_HEADER,
		SSL_FLAG_HEADER: SSL_FLAG_HEADER,
		debugMode: config.debugMode
	}, function(err, ports, child) {
		callback && callback(err, ports, child);
		if (config.debugMode) {
			if (err) {
				console.log(colors.red(err));
			} else if (!child.debugMode) {
				child.debugMode = true;
				child.on('data', function(data) {
					if (data && data.type == 'console.log') {
						console.log('[' + comUtil.formatDate() + '] [plugin] [' + plugin.path.substring(plugin.path.lastIndexOf('.') + 1) + ']', data.message);
					}
				});
				child.sendData({
					type: 'console.log',
					status: 'ready'
				});
			}
		}
	});
};

pluginMgr.stopPlugin = function(plugin) {
	p.kill({
		script: PLUGIN_MAIN,
		value: plugin.path
	}, 10000);
};

pluginMgr.getPlugins = function() {
	return plugins;
};

pluginMgr.getPlugin = function(protocol) {
	return plugins[protocol];
};

pluginMgr.getPluginByRuleUrl = function(ruleUrl) {
	if (!ruleUrl || typeof ruleUrl != 'string') {
		return;
	}
	var index = ruleUrl.indexOf(':');
	return index == -1 ? null : plugins[ruleUrl.substring(0, index + 1)];
};

pluginMgr.getPluginByHomePage = function(url) {
	return PLUGIN_URL_RE.test(url)
				&& plugins[RegExp.$1 + ':'];
};

pluginMgr.getRules = function() {
	return rules;
};

module.exports = pluginMgr;<|endoftext|>


<commit_before>// $begin{copyright}
//
// This file is part of WebSharper
//
// Copyright (c) 2008-2013 IntelliFactory
//
// GNU Affero General Public License Usage
// WebSharper 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.
//
// WebSharper 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 at <http://www.gnu.org/licenses/>.
//
// If you are unsure which license is appropriate for your use, please contact
// IntelliFactory at http://intellifactory.com/contact.
//
// $end{copyright}

try {
    Object.defineProperty(Error.prototype, 'message', { enumerable: true });
} catch (e) { }

var IntelliFactory =
{
    Runtime:
    {

        Class:
            function (p, s) {
                function r() { }
                r.prototype = p;
                for (var f in s) { r[f] = s[f]; }
                return r;
            },

        Define:
            function (a, b) {
                function define(a, b) {
                    for (var k in b) {
                        var t1 = typeof a[k];
                        var t2 = typeof b[k];
                        if (t1 == "undefined") {
                            a[k] = b[k];
                        } else if (t1 == "object" && t2 == "object") {
                            define(a[k], b[k]);
                        } else {
                            throw new Error("Name conflict: " + k);
                        }
                    }
                }
                define(a, b);
            },

        Field:
            function (f) {
                var value, ready = false;
                return function () {
                    if (!ready) { ready = true; value = f(); }
                    return value;
                }
            },

        For:
            function (lowerBound, upperBound, body) {
                for (var i = lowerBound; i <= upperBound; i++) {
                    body(i);
                }
            },

        ForEach:
            function (obj, body) {
                for (var f in obj) {
                    body(f);
                }
            },

        New:
            function (ctor, fields) {
                var r = new ctor();
                for (var f in fields) {
                    if (!(f in r)) {
                        r[f] = fields[f];
                    }
                }
                return r
            },

        OnInit:
            function (f) {
                if (!("init" in this)) {
                    this.init = [];
                }
                this.init.push(f);
            },

        OnLoad:
            function (f) {
                if (!("load" in this)) {
                    this.load = [];
                }
                this.load.push(f);
            },

        Inherit:
            function (a, b) {
                var p = a.prototype;
                a.prototype = new b();
                for (var f in p) {
                    a.prototype[f] = p[f];
                }
            },

        Safe:
            function (x) {
                if (x === undefined) return {};
                return x;
            },

        Start:
            function () {
                function run(c) {
                    for (var i = 0; i < c.length; i++) {
                        c[i]();
                    }
                }
                if ("init" in this) {
                    run(this.init);
                    this.init = [];
                }
                if ("load" in this) {
                    run(this.load);
                    this.load = [];
                }
            },

        Throw:
            function (e) {
                throw e;
            },

        Tupled:
            function (f) {
                return function (x) {
                    if (arguments.length > 1) {
                        return f(arguments);
                    } else {
                        return f(x);
                    }
                }
            },

        Try:
            function (block, handler) {
                try {
                    return block();
                } catch (e) {
                    return handler(e);
                }
            },

        TryFinally:
            function (block, handler) {
                try {
                    return block();
                } finally {
                    handler();
                }
            },

        While:
            function (guard, body) {
                while (guard()) {
                    body();
                }
            }
    }
};
<commit_msg>Add flag to IntelliFactory.Runtime to temporarily allow overwriting definitions<commit_after>// $begin{copyright}
//
// This file is part of WebSharper
//
// Copyright (c) 2008-2013 IntelliFactory
//
// GNU Affero General Public License Usage
// WebSharper 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.
//
// WebSharper 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 at <http://www.gnu.org/licenses/>.
//
// If you are unsure which license is appropriate for your use, please contact
// IntelliFactory at http://intellifactory.com/contact.
//
// $end{copyright}

try {
    Object.defineProperty(Error.prototype, 'message', { enumerable: true });
} catch (e) { }

var IntelliFactory =
{
    Runtime:
    {

        Class:
            function (p, s) {
                function r() { }
                r.prototype = p;
                for (var f in s) { r[f] = s[f]; }
                return r;
            },

        Define:
            function (a, b) {
                var overwrite = !!this.overwrite;
                function define(a, b) {
                    for (var k in b) {
                        var t1 = typeof a[k];
                        var t2 = typeof b[k];
                        if (t1 == "object" && t2 == "object") {
                            define(a[k], b[k]);
                        } else if (t1 == "undefined" || overwrite) {
                            a[k] = b[k];
                        } else {
                            throw new Error("Name conflict: " + k);
                        }
                    }
                }
                define(a, b);
            },

        Field:
            function (f) {
                var value, ready = false;
                return function () {
                    if (!ready) { ready = true; value = f(); }
                    return value;
                }
            },

        For:
            function (lowerBound, upperBound, body) {
                for (var i = lowerBound; i <= upperBound; i++) {
                    body(i);
                }
            },

        ForEach:
            function (obj, body) {
                for (var f in obj) {
                    body(f);
                }
            },

        New:
            function (ctor, fields) {
                var r = new ctor();
                for (var f in fields) {
                    if (!(f in r)) {
                        r[f] = fields[f];
                    }
                }
                return r
            },

        OnInit:
            function (f) {
                if (!("init" in this)) {
                    this.init = [];
                }
                this.init.push(f);
            },

        OnLoad:
            function (f) {
                if (!("load" in this)) {
                    this.load = [];
                }
                this.load.push(f);
            },

        Inherit:
            function (a, b) {
                var p = a.prototype;
                a.prototype = new b();
                for (var f in p) {
                    a.prototype[f] = p[f];
                }
            },

        Safe:
            function (x) {
                if (x === undefined) return {};
                return x;
            },

        Start:
            function () {
                function run(c) {
                    for (var i = 0; i < c.length; i++) {
                        c[i]();
                    }
                }
                if ("init" in this) {
                    run(this.init);
                    this.init = [];
                }
                if ("load" in this) {
                    run(this.load);
                    this.load = [];
                }
            },

        Throw:
            function (e) {
                throw e;
            },

        Tupled:
            function (f) {
                return function (x) {
                    if (arguments.length > 1) {
                        return f(arguments);
                    } else {
                        return f(x);
                    }
                }
            },

        Try:
            function (block, handler) {
                try {
                    return block();
                } catch (e) {
                    return handler(e);
                }
            },

        TryFinally:
            function (block, handler) {
                try {
                    return block();
                } finally {
                    handler();
                }
            },

        While:
            function (guard, body) {
                while (guard()) {
                    body();
                }
            }
    }
};
<|endoftext|>