<commit_before>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    function createTexture(gl, width, height) {
        var texture = gl.createTexture();
        //set properties for the texture
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        return texture;
    };

    var Frame = function (gl, width, height) {
        var _width = width;
        var _height = height;
        var _gl = gl;
        var _fb = _gl.createFramebuffer();
        //bind framebuffer to texture
        _gl.bindFramebuffer(_gl.FRAMEBUFFER, _fb);
        _texture = createTexture(_gl, _width, _height);
        _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, _texture, 0);

        return {
            load : function (element) {
                _gl.bindTexture(_gl.TEXTURE_2D, _texture);
                _gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, element);
            },
            asTexture: function () {
                return _texture;
            }
        }
    };

    var frame = function (element, width, height) {
        var canvas = global.canvas();
        var w = width || (element ? element.width || element.videoWidth: canvas.width);
        var h = height || (element ? element.height || element.videoHeight: canvas.height);
        var f = new Frame(canvas.gl, w, h);
        if(element) {
            f.load(element);
        }
        return f;
    };

    global.frame = frame;

})(glimp);

<commit_msg>Removed obsolete local frame buffer initialization that was preventing texture to be bound to another framebuffer<commit_after>/**
 * @author David Corvoysier / Copyright Orange 2013
 */
(function(global) {

    function createTexture(gl, width, height) {
        var texture = gl.createTexture();
        //set properties for the texture
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        return texture;
    };

    var Frame = function (gl, width, height) {
        var _width = width;
        var _height = height;
        var _gl = gl;
        var _texture = createTexture(_gl, _width, _height);

        return {
            load : function (element) {
                _gl.bindTexture(_gl.TEXTURE_2D, _texture);
                _gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, element);
            },
            asTexture: function () {
                return _texture;
            }
        }
    };

    var frame = function (element, width, height) {
        var canvas = global.canvas();
        var w = width || (element ? element.width || element.videoWidth: canvas.width);
        var h = height || (element ? element.height || element.videoHeight: canvas.height);
        var f = new Frame(canvas.gl, w, h);
        if(element) {
            f.load(element);
        }
        return f;
    };

    global.frame = frame;

})(glimp);

<|endoftext|>


<commit_before>if (!window.__skate0) {
  window.__skate0 = {
    registerIfNotExists (name, value) {
      if (!this[name]) {
        this[name] = value;
      }

      return this[name];
    }
  };
}

export default window.__skate0;
<commit_msg>Ensure that the global sharing is incremented since it's not compatible with earlier versions.<commit_after>const VERSION = '__skate_0_14_0';

if (!window[VERSION]) {
  window[VERSION] = {
    registerIfNotExists (name, value) {
      return this[name] || (this[name] = value);
    }
  };
}

export default window[VERSION];
<|endoftext|>


<commit_before>/* jslint node: true */
'use strict';

var MenuModule		= require('../core/menu_module.js').MenuModule;
var Log				= require('../core/logger.js').log;
var resetScreen		= require('../core/ansi_term.js').resetScreen;

var async			= require('async');
var _				= require('lodash');
var http			= require('http');
var net				= require('net');
var crypto			= require('crypto');
var buffers			= require('buffers');

var packageJson 	= require('../package.json');

/*
	Expected configuration block:

	{
		module: bbs_link
		...
		config: {
			sysCode: XXXXX
			authCode: XXXXX
			schemeCode: XXXX
			door: lord

			//	default hoss: games.bbslink.net
			host: games.bbslink.net

			//	defualt port: 23
			port: 23
		}
	}
*/

//	:TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors
//	:TODO: ENH: Support nodeMax and tooManyArt

exports.getModule	= BBSLinkModule;

exports.moduleInfo = {
	name	: 'BBSLink',
	desc	: 'BBSLink Access Module',
	author	: 'NuSkooler',
};

function BBSLinkModule(options) {
	MenuModule.call(this, options);

	var self 	= this;
	this.config = options.menuConfig.config;

	this.config.host = this.config.host || 'games.bbslink.net';
	this.config.port = this.config.port || 23;

	this.initSequence = function() {
		var token;
		var randomKey;
		var clientTerminated;

		async.series(
			[
				function validateConfig(callback) {
					if(_.isString(self.config.sysCode) &&
						_.isString(self.config.authCode) &&
						_.isString(self.config.schemeCode) &&
						_.isString(self.config.door))
					{
						callback(null);
					} else {
						callback(new Error('Configuration is missing option(s)'));
					}
				},
				function acquireToken(callback) {
					//
					//	Acquire an authentication token
					//
					crypto.randomBytes(16, function rand(ex, buf) {
						if(ex) {
							callback(ex);
						} else {
							randomKey = buf.toString('base64').substr(0, 6);
							self.simpleHttpRequest('/token.php?key=' + randomKey, null, function resp(err, body) {
								if(err) {
									callback(err);
								} else {
									token = body.trim();
									self.client.log.trace( { token : token }, 'BBSLink token');
									callback(null);
								}
							});
						}
					});
				},
				function authenticateToken(callback) {
					//
					//	Authenticate the token we acquired previously
					//
					var headers = {
						'X-User'	: self.client.user.userId.toString(),
						'X-System'	: self.config.sysCode,
						'X-Auth'	: crypto.createHash('md5').update(self.config.authCode + token).digest('hex'),
						'X-Code'	: crypto.createHash('md5').update(self.config.schemeCode + token).digest('hex'),
						'X-Rows'	: self.client.term.termHeight.toString(),
						'X-Key'		: randomKey,
						'X-Door'	: self.config.door,
						'X-Token'	: token,
						'X-Type'	: 'enigma-bbs',
						'X-Version'	: packageJson.version,
					};

					self.simpleHttpRequest('/auth.php?key=' + randomKey, headers, function resp(err, body) {
						var status = body.trim();

						if('complete' === status) {
							callback(null);
						} else {
							callback(new Error('Bad authentication status: ' + status));
						}
					});
				},
				function createTelnetBridge(callback) {
					//
					//	Authentication with BBSLink successful. Now, we need to create a telnet
					//	bridge from us to them
					//
					var connectOpts = {
						port	: self.config.port,
						host	: self.config.host,
					};

					var clientTerminated;

					self.client.term.write(resetScreen());
					self.client.term.write('  Connecting to BBSLink.net, please wait...\n');

					var bridgeConnection = net.createConnection(connectOpts, function connected() {
						self.client.log.info(connectOpts, 'BBSLink bridge connection established');

						self.client.term.output.pipe(bridgeConnection);

						self.client.once('end', function clientEnd() {
							self.client.log.info('Connection ended. Terminating BBSLink connection');
							clientTerminated = true;
							bridgeConnection.end();
						});
					});

					var restorePipe = function() {
						self.client.term.output.unpipe(bridgeConnection);
						self.client.term.output.resume();
					};

					bridgeConnection.on('data', function incomingData(data) {
						//	pass along
						//	:TODO: just pipe this as well
						self.client.term.rawWrite(data);
					});

					bridgeConnection.on('end', function connectionEnd() {
						restorePipe();
						callback(clientTerminated ? new Error('Client connection terminated') : null);
					});

					bridgeConnection.on('error', function error(err) {
						self.client.log.info('BBSLink bridge connection error: ' + err.message);
						restorePipe();
						callback(err);
					});
				}
			],
			function complete(err) {
				if(err) {
					self.client.log.warn( { error : err.toString() }, 'BBSLink connection error');
				}

				if(!clientTerminated) {
					self.prevMenu();
				}
			}
		);
	};

	this.simpleHttpRequest = function(path, headers, cb) {
		var getOpts = {
			host	: this.config.host,
			path	: path,
			headers	: headers,
		};

		var req = http.get(getOpts, function response(resp) {
			var data = '';

			resp.on('data', function chunk(c) {
				data += c;
			});

			resp.on('end', function respEnd() {
				cb(null, data);
				req.end();
			});
		});

		req.on('error', function reqErr(err) {
			cb(err);
		});
	};
}

require('util').inherits(BBSLinkModule, MenuModule);<commit_msg>Use const<commit_after>/* jslint node: true */
'use strict';

const MenuModule	= require('../core/menu_module.js').MenuModule;
const resetScreen	= require('../core/ansi_term.js').resetScreen;

const async			= require('async');
const _				= require('lodash');
const http			= require('http');
const net			= require('net');
const crypto		= require('crypto');

const packageJson 	= require('../package.json');

/*
	Expected configuration block:

	{
		module: bbs_link
		...
		config: {
			sysCode: XXXXX
			authCode: XXXXX
			schemeCode: XXXX
			door: lord

			//	default hoss: games.bbslink.net
			host: games.bbslink.net

			//	defualt port: 23
			port: 23
		}
	}
*/

//	:TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors
//	:TODO: ENH: Support nodeMax and tooManyArt

exports.getModule	= BBSLinkModule;

exports.moduleInfo = {
	name	: 'BBSLink',
	desc	: 'BBSLink Access Module',
	author	: 'NuSkooler',
};

function BBSLinkModule(options) {
	MenuModule.call(this, options);

	var self 	= this;
	this.config = options.menuConfig.config;

	this.config.host = this.config.host || 'games.bbslink.net';
	this.config.port = this.config.port || 23;

	this.initSequence = function() {
		var token;
		var randomKey;
		var clientTerminated;

		async.series(
			[
				function validateConfig(callback) {
					if(_.isString(self.config.sysCode) &&
						_.isString(self.config.authCode) &&
						_.isString(self.config.schemeCode) &&
						_.isString(self.config.door))
					{
						callback(null);
					} else {
						callback(new Error('Configuration is missing option(s)'));
					}
				},
				function acquireToken(callback) {
					//
					//	Acquire an authentication token
					//
					crypto.randomBytes(16, function rand(ex, buf) {
						if(ex) {
							callback(ex);
						} else {
							randomKey = buf.toString('base64').substr(0, 6);
							self.simpleHttpRequest('/token.php?key=' + randomKey, null, function resp(err, body) {
								if(err) {
									callback(err);
								} else {
									token = body.trim();
									self.client.log.trace( { token : token }, 'BBSLink token');
									callback(null);
								}
							});
						}
					});
				},
				function authenticateToken(callback) {
					//
					//	Authenticate the token we acquired previously
					//
					var headers = {
						'X-User'	: self.client.user.userId.toString(),
						'X-System'	: self.config.sysCode,
						'X-Auth'	: crypto.createHash('md5').update(self.config.authCode + token).digest('hex'),
						'X-Code'	: crypto.createHash('md5').update(self.config.schemeCode + token).digest('hex'),
						'X-Rows'	: self.client.term.termHeight.toString(),
						'X-Key'		: randomKey,
						'X-Door'	: self.config.door,
						'X-Token'	: token,
						'X-Type'	: 'enigma-bbs',
						'X-Version'	: packageJson.version,
					};

					self.simpleHttpRequest('/auth.php?key=' + randomKey, headers, function resp(err, body) {
						var status = body.trim();

						if('complete' === status) {
							callback(null);
						} else {
							callback(new Error('Bad authentication status: ' + status));
						}
					});
				},
				function createTelnetBridge(callback) {
					//
					//	Authentication with BBSLink successful. Now, we need to create a telnet
					//	bridge from us to them
					//
					var connectOpts = {
						port	: self.config.port,
						host	: self.config.host,
					};

					var clientTerminated;

					self.client.term.write(resetScreen());
					self.client.term.write('  Connecting to BBSLink.net, please wait...\n');

					var bridgeConnection = net.createConnection(connectOpts, function connected() {
						self.client.log.info(connectOpts, 'BBSLink bridge connection established');

						self.client.term.output.pipe(bridgeConnection);

						self.client.once('end', function clientEnd() {
							self.client.log.info('Connection ended. Terminating BBSLink connection');
							clientTerminated = true;
							bridgeConnection.end();
						});
					});

					var restorePipe = function() {
						self.client.term.output.unpipe(bridgeConnection);
						self.client.term.output.resume();
					};

					bridgeConnection.on('data', function incomingData(data) {
						//	pass along
						//	:TODO: just pipe this as well
						self.client.term.rawWrite(data);
					});

					bridgeConnection.on('end', function connectionEnd() {
						restorePipe();
						callback(clientTerminated ? new Error('Client connection terminated') : null);
					});

					bridgeConnection.on('error', function error(err) {
						self.client.log.info('BBSLink bridge connection error: ' + err.message);
						restorePipe();
						callback(err);
					});
				}
			],
			function complete(err) {
				if(err) {
					self.client.log.warn( { error : err.toString() }, 'BBSLink connection error');
				}

				if(!clientTerminated) {
					self.prevMenu();
				}
			}
		);
	};

	this.simpleHttpRequest = function(path, headers, cb) {
		var getOpts = {
			host	: this.config.host,
			path	: path,
			headers	: headers,
		};

		var req = http.get(getOpts, function response(resp) {
			var data = '';

			resp.on('data', function chunk(c) {
				data += c;
			});

			resp.on('end', function respEnd() {
				cb(null, data);
				req.end();
			});
		});

		req.on('error', function reqErr(err) {
			cb(err);
		});
	};
}

require('util').inherits(BBSLinkModule, MenuModule);<|endoftext|>


<commit_before>var fs = require('fs');
var commands = [];
var modules = [];

function InitModules() {
    console.log('Loading modules...');
    fs.readdir('modules', function(err, files) {
        if (err) throw err;
        for(var i = 0; i < files.length; i++) {
            Loader(files[i]);
            }
    });
}

function Loader(filename) {
    var mod = require('./modules/' + filename);
    modules[mod.info['name']] = mod;
    commands.push(mod.info['command']);
    console.log('Loaded module %s(%s) !', mod.info['name'], filename);
}

function Unload() {
    modules = [];
    commands = [];
    fs.readdir('modules', function(err, files) {
        if (err) throw err;
        for(var i = 0; i < files.length; i++) {
            delete require.cache[require.resolve('.modules/' + files[i])];
        }
    });
    console.log('All Unloaded!');
}

exports.InitModules = InitModules;
exports.Unload = Unload;
exports.commands = commands;
exports.modules = modules;
<commit_msg>can't fix reload<commit_after>var fs = require('fs');
var commands = [];
var modules = [];

function InitModules() {
    console.log('Loading modules...');
    fs.readdir('modules', function(err, files) {
        if (err) throw err;
        for(var i = 0; i < files.length; i++) {
            Loader(files[i]);
            }
    });
}

function Loader(filename) {
    var mod = require('./modules/' + filename);
    modules[mod.info['name']] = mod;
    commands.push(mod.info['command']);
    console.log('Loaded module %s(%s) !', mod.info['name'], filename);
}

function Unload() {
    modules = [];
    commands = [];
    console.log('All Unloaded!');
}

exports.InitModules = InitModules;
exports.Unload = Unload;
exports.commands = commands;
exports.modules = modules;
<|endoftext|>


<commit_before>import {inject, customElement, bindable} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
import {BindingEngine} from 'aurelia-binding';
import d3 from 'd3';
import GraphModel from './graph-model';
import {GraphLibD3} from './graphlib-d3';
import {GraphFinder} from './graph-finder';
import {GraphModifier} from './graph-modifier';
import {ActionManager} from './action-manager';

@customElement('graph')
@inject(Element, EventAggregator, GraphLibD3, GraphModel, GraphFinder, GraphModifier, ActionManager, BindingEngine)
export class Graph {
  @bindable data;
  @bindable graphInteractionModel;

  constructor(element, pubSub, graphLibD3, graphModel, graphFinder, graphModifier, actionManager, bindingEngine) {
    this.element = element;
    this.pubSub = pubSub;
    this.graphLibD3 = graphLibD3;
    this.graphModel = graphModel;
    this.graphFinder = graphFinder;
    this.graphModifier = graphModifier;
    this.actionManager = actionManager;
    this.bindingEngine = bindingEngine;

    this.windowSizeAdapter();

    this.displayNodes = [];
    this.displayEdges = [];

    this.pubSub.subscribe('search.node', nodeId => {
      this.addNodeAction([nodeId], true);
    });
  }

  /**
   * TODO Need to respond to window resize so that svg can resize.
   * TODO This is too big, need to subtract size of menu and search box
   */
  windowSizeAdapter() {
    this.presentationSVG = {
      width: window.innerWidth - 100,
      height: window.innerHeight - 200
    };
  }

  /**
   * A new global graph (i.e. project analysis) is available.
   * Create an empty display graph and initialize it with some
   * sample data from the global graph.
   */
  dataChanged(newVal) {
    let unusedTypes;

    if (newVal) {
      unusedTypes = this.graphFinder.findUnusedTypes(this.graphModel.globalGraphModel);
      this.graphModel.initRadii();
      this.displayGraph = this.graphModel.emptyGraph();
      this.addNodeAction([unusedTypes[0]], true);
    }
  }

  /**
   * Perform the add action, and register undo and redo handlers.
   */
  addNodeAction(nodeIds, withNeighbours) {
    this.addAndDisplay(nodeIds, withNeighbours);
    this.actionManager.addAction(this,
      this.removeAndDisplay, [nodeIds, withNeighbours],
      this.addAndDisplay, [nodeIds, withNeighbours]
    );
  }

  addAndDisplay(nodeIds, withNeighbours) {
    this.addToDisplayGraphModel(nodeIds, withNeighbours);
    this.updateForceLayout();
  }

  removeAndDisplay(nodeIds, withNeighbours) {
    this.removeFromDisplayGraphModel(nodeIds, withNeighbours);
    this.updateForceLayout();
  }

  /**
   * Modify the graph display model to include the given node id's.
   * If withNeihbours is true, then also include each node's neighbours.
   */
  addToDisplayGraphModel(nodeIds, withNeighbours) {
    nodeIds.forEach( nodeId => {
      if (withNeighbours) {
        this.graphModifier.addNodeEnv(this.displayGraph, nodeId, 1, this.svgText);
      } else {
        this.graphModifier.addNodeOnly(this.displayGraph, nodeId, this.svgText);
      }
    });
  }

  /**
   * Modify the graph display model to remove the given node id's.
   * If withNeihbours is true, then also remove each node's neighbours.
   */
  removeFromDisplayGraphModel(nodeIds, withNeighbours) {
    nodeIds.forEach( nodeId => {
      if (withNeighbours) {
        this.graphModifier.removeNodeEnv(this.displayGraph, nodeId, 1, this.svgText);
      } else {
        this.graphModifier.removeNodeOnly(this.displayGraph, nodeId, this.svgText);
      }
    });
  }

  /**
   * Use D3 force layout to calculate node positions.
   */
  updateForceLayout() {
    let d3Data = this.graphLibD3.mapToD3(this.displayGraph),
      numNodes = d3Data.nodes.length,
      currentNode;

    let force = d3.layout.force()
       .nodes(d3Data.nodes)
       .links(d3Data.links)
       .size([this.presentationSVG.width, this.presentationSVG.height])
       .gravity(0.4)
       .linkDistance(100)
       .charge(-150);

    this.computeLayout(force, numNodes);

    // TODO: port collision detection or line adjustment from legacy

    this.updateDisplayData(d3Data);
  }

  computeLayout(force, numIterations) {
    force.start();
    for (var j = 0; j < numIterations; ++j) force.tick();
    force.stop();
  }

  /**
   * Update list of display nodes and edges from d3 data.
   */
  updateDisplayData(d3Data) {
    // add nodes that are in d3Data, but not in display
    d3Data.nodes.forEach( node => {
      if (!this.graphLibD3.containsNode(this.displayNodes, node)) {
        this.displayNodes.push(node);
      }
    });

    // add links that are in d3Data, but not in display
    d3Data.links.forEach( link => {
      if (!this.graphLibD3.containsEdge(this.displayEdges, link)) {
        this.displayEdges.push(link);
      }
    });

    // remove nodes that are in display, but not in d3Data (iterate in reverse because splice re-indexes the array)
    let numDisplayNodes = this.displayNodes.length;
    for (let i=numDisplayNodes-1; i >= 0; i--) {
      if (!this.graphLibD3.containsNode(d3Data.nodes, this.displayNodes[i])) {
        this.displayNodes.splice(i, 1);
      }
    }

    // remove edges that are in display, but not in d3Data (iterate in reverse because splice re-indexes the array)
    let numDisplayEdges = this.displayEdges.length;
    for (let j=numDisplayEdges-1; j >=0; j--) {
      if (!this.graphLibD3.containsEdge(d3Data.links, this.displayEdges[j])) {
        this.displayEdges.splice(j, 1);
      }
    }

  }

  /**
   * Based on the currently selected nodes, use 'interaction' and 'type'
   * to find nodes from globalGraph that should be added, but only if
   * they're not already in the display graph.
   */
  findNodesToAdd(interaction, type) {
    let selectedNodeIds,
      relationship,
      nodesByEdgeRelationship;

    selectedNodeIds = this.graphFinder.findSelectedNodeIds(this.displayGraph);
    relationship = type === 'of it' ? 'target' : 'source';
    nodesByEdgeRelationship = this.graphFinder.findNodesByEdgeRelationship(
      this.graphModel.globalGraphModel, selectedNodeIds, interaction, relationship
    );

    return this.graphFinder.filterAlreadyInGraph(nodesByEdgeRelationship, this.displayGraph);
  }

  /**
   * Determine nodes that should be added, and add them as an undo-able action.
   */
  addNodesToDisplay(interaction, type) {
    let nodesToAdd = this.findNodesToAdd(interaction, type);

    if (nodesToAdd.length > 0) {
      this.addNodeAction(nodesToAdd);
    } else {
      // TODO notify https://github.com/CANVE/canve-viz/issues/32
      console.warn('No interaction results');
    }
  }

  /**
   * Invoked by Aurelia when 'graphInteractionModel' binding value has changed.
   * In practice this object it set once in the parent view. After that,
   * register property observers to fire when specific properties of the interaction model change,
   * indicating that the graph should respond to the change by adding more nodes.
   */
  graphInteractionModelChanged(newValue) {
    if (newValue) {
      this.graphInteractionModel = newValue;

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'callsSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('uses', newValue);
      });

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'extensionsSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('extends', newValue);
      });

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'ownershipSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('declares member', newValue);
      });

      // TODO dispose in appropriate lifecycle method http://stackoverflow.com/questions/30283569/array-subscription-in-aurelia
    }
  }

}
<commit_msg>#37 adjust link distance, charge, and force compute technique for an easier to read graph with things spaced out nicely and not overlapping<commit_after>import {inject, customElement, bindable} from 'aurelia-framework';
import {EventAggregator} from 'aurelia-event-aggregator';
import {BindingEngine} from 'aurelia-binding';
import d3 from 'd3';
import GraphModel from './graph-model';
import {GraphLibD3} from './graphlib-d3';
import {GraphFinder} from './graph-finder';
import {GraphModifier} from './graph-modifier';
import {ActionManager} from './action-manager';

@customElement('graph')
@inject(Element, EventAggregator, GraphLibD3, GraphModel, GraphFinder, GraphModifier, ActionManager, BindingEngine)
export class Graph {
  @bindable data;
  @bindable graphInteractionModel;

  constructor(element, pubSub, graphLibD3, graphModel, graphFinder, graphModifier, actionManager, bindingEngine) {
    this.element = element;
    this.pubSub = pubSub;
    this.graphLibD3 = graphLibD3;
    this.graphModel = graphModel;
    this.graphFinder = graphFinder;
    this.graphModifier = graphModifier;
    this.actionManager = actionManager;
    this.bindingEngine = bindingEngine;

    this.windowSizeAdapter();

    this.displayNodes = [];
    this.displayEdges = [];

    this.pubSub.subscribe('search.node', nodeId => {
      this.addNodeAction([nodeId], true);
    });
  }

  /**
   * TODO Need to respond to window resize so that svg can resize.
   * TODO This is too big, need to subtract size of menu and search box
   */
  windowSizeAdapter() {
    this.presentationSVG = {
      width: window.innerWidth - 100,
      height: window.innerHeight - 200
    };
  }

  /**
   * A new global graph (i.e. project analysis) is available.
   * Create an empty display graph and initialize it with some
   * sample data from the global graph.
   */
  dataChanged(newVal) {
    let unusedTypes;

    if (newVal) {
      unusedTypes = this.graphFinder.findUnusedTypes(this.graphModel.globalGraphModel);
      this.graphModel.initRadii();
      this.displayGraph = this.graphModel.emptyGraph();
      this.addNodeAction([unusedTypes[0]], true);
    }
  }

  /**
   * Perform the add action, and register undo and redo handlers.
   */
  addNodeAction(nodeIds, withNeighbours) {
    this.addAndDisplay(nodeIds, withNeighbours);
    this.actionManager.addAction(this,
      this.removeAndDisplay, [nodeIds, withNeighbours],
      this.addAndDisplay, [nodeIds, withNeighbours]
    );
  }

  addAndDisplay(nodeIds, withNeighbours) {
    this.addToDisplayGraphModel(nodeIds, withNeighbours);
    this.updateForceLayout();
  }

  removeAndDisplay(nodeIds, withNeighbours) {
    this.removeFromDisplayGraphModel(nodeIds, withNeighbours);
    this.updateForceLayout();
  }

  /**
   * Modify the graph display model to include the given node id's.
   * If withNeihbours is true, then also include each node's neighbours.
   */
  addToDisplayGraphModel(nodeIds, withNeighbours) {
    nodeIds.forEach( nodeId => {
      if (withNeighbours) {
        this.graphModifier.addNodeEnv(this.displayGraph, nodeId, 1, this.svgText);
      } else {
        this.graphModifier.addNodeOnly(this.displayGraph, nodeId, this.svgText);
      }
    });
  }

  /**
   * Modify the graph display model to remove the given node id's.
   * If withNeihbours is true, then also remove each node's neighbours.
   */
  removeFromDisplayGraphModel(nodeIds, withNeighbours) {
    nodeIds.forEach( nodeId => {
      if (withNeighbours) {
        this.graphModifier.removeNodeEnv(this.displayGraph, nodeId, 1, this.svgText);
      } else {
        this.graphModifier.removeNodeOnly(this.displayGraph, nodeId, this.svgText);
      }
    });
  }

  /**
   * Use D3 force layout to calculate node positions.
   * A high negative charge value avoids node overlap.
   * Setting link distance as function of width creates
   * a visually pleasing, uncluttered effect.
   */
  updateForceLayout() {
    let d3Data = this.graphLibD3.mapToD3(this.displayGraph),
      numNodes = d3Data.nodes.length,
      currentNode;

    let force = d3.layout.force()
       .nodes(d3Data.nodes)
       .links(d3Data.links)
       .size([this.presentationSVG.width, this.presentationSVG.height])
       .gravity(0.4)
       .linkDistance(this.presentationSVG.width/4)
       .charge(-3000);

    this.computeLayout(force, numNodes);
    this.updateDisplayData(d3Data);
  }

  /**
   * Technique to minimize D3 chaotic bouncing in force layout.
   * http://stackoverflow.com/questions/13463053/calm-down-initial-tick-of-a-force-layout
   */
  computeLayout(force, numIterations) {
    let safety = 0;

    force.start();

    while(force.alpha() > 0.05) {
      force.tick();
      if(safety++ > 500) {
        break;
      }
    }

    force.stop();

    if(safety >= 500) {
      console.warn('Unable to stabilize force layout.');
    }
  }

  /**
   * Update list of display nodes and edges from d3 data.
   */
  updateDisplayData(d3Data) {
    // add nodes that are in d3Data, but not in display
    d3Data.nodes.forEach( node => {
      if (!this.graphLibD3.containsNode(this.displayNodes, node)) {
        this.displayNodes.push(node);
      }
    });

    // add links that are in d3Data, but not in display
    d3Data.links.forEach( link => {
      if (!this.graphLibD3.containsEdge(this.displayEdges, link)) {
        this.displayEdges.push(link);
      }
    });

    // remove nodes that are in display, but not in d3Data (iterate in reverse because splice re-indexes the array)
    let numDisplayNodes = this.displayNodes.length;
    for (let i=numDisplayNodes-1; i >= 0; i--) {
      if (!this.graphLibD3.containsNode(d3Data.nodes, this.displayNodes[i])) {
        this.displayNodes.splice(i, 1);
      }
    }

    // remove edges that are in display, but not in d3Data (iterate in reverse because splice re-indexes the array)
    let numDisplayEdges = this.displayEdges.length;
    for (let j=numDisplayEdges-1; j >=0; j--) {
      if (!this.graphLibD3.containsEdge(d3Data.links, this.displayEdges[j])) {
        this.displayEdges.splice(j, 1);
      }
    }

  }

  /**
   * Based on the currently selected nodes, use 'interaction' and 'type'
   * to find nodes from globalGraph that should be added, but only if
   * they're not already in the display graph.
   */
  findNodesToAdd(interaction, type) {
    let selectedNodeIds,
      relationship,
      nodesByEdgeRelationship;

    selectedNodeIds = this.graphFinder.findSelectedNodeIds(this.displayGraph);
    relationship = type === 'of it' ? 'target' : 'source';
    nodesByEdgeRelationship = this.graphFinder.findNodesByEdgeRelationship(
      this.graphModel.globalGraphModel, selectedNodeIds, interaction, relationship
    );

    return this.graphFinder.filterAlreadyInGraph(nodesByEdgeRelationship, this.displayGraph);
  }

  /**
   * Determine nodes that should be added, and add them as an undo-able action.
   */
  addNodesToDisplay(interaction, type) {
    let nodesToAdd = this.findNodesToAdd(interaction, type);

    if (nodesToAdd.length > 0) {
      this.addNodeAction(nodesToAdd);
    } else {
      // TODO notify https://github.com/CANVE/canve-viz/issues/32
      console.warn('No interaction results');
    }
  }

  /**
   * Invoked by Aurelia when 'graphInteractionModel' binding value has changed.
   * In practice this object it set once in the parent view. After that,
   * register property observers to fire when specific properties of the interaction model change,
   * indicating that the graph should respond to the change by adding more nodes.
   */
  graphInteractionModelChanged(newValue) {
    if (newValue) {
      this.graphInteractionModel = newValue;

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'callsSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('uses', newValue);
      });

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'extensionsSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('extends', newValue);
      });

      this.bindingEngine.propertyObserver(this.graphInteractionModel, 'ownershipSelectedVal').subscribe((newValue, oldValue) => {
        this.addNodesToDisplay('declares member', newValue);
      });

      // TODO dispose in appropriate lifecycle method http://stackoverflow.com/questions/30283569/array-subscription-in-aurelia
    }
  }

}
<|endoftext|>


<commit_before>var ctx                = require('./_ctx')
  , invoke             = require('./_invoke')
  , html               = require('./_html')
  , cel                = require('./_dom-create')
  , global             = require('./_global')
  , process            = global.process
  , setTask            = global.setImmediate
  , clearTask          = global.clearImmediate
  , MessageChannel     = global.MessageChannel
  , counter            = 0
  , queue              = {}
  , ONREADYSTATECHANGE = 'onreadystatechange'
  , defer, channel, port;
var run = function(){
  var id = +this;
  if(queue.hasOwnProperty(id)){
    var fn = queue[id];
    delete queue[id];
    fn();
  }
};
var listener = function(event){
  run.call(event.data);
};
// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
if(!setTask || !clearTask){
  setTask = function setImmediate(fn){
    var args = [], i = 1;
    while(arguments.length > i)args.push(arguments[i++]);
    queue[++counter] = function(){
      invoke(typeof fn == 'function' ? fn : Function(fn), args);
    };
    defer(counter);
    return counter;
  };
  clearTask = function clearImmediate(id){
    delete queue[id];
  };
  // Node.js 0.8-
  if(require('./_cof')(process) == 'process'){
    defer = function(id){
      process.nextTick(ctx(run, id, 1));
    };
  // Browsers with MessageChannel, includes WebWorkers
  } else if(MessageChannel){
    channel = new MessageChannel;
    port    = channel.port2;
    channel.port1.onmessage = listener;
    defer = ctx(port.postMessage, port, 1);
  // Browsers with postMessage, skip WebWorkers
  // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
  } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){
    defer = function(id){
      global.postMessage(id + '', '*');
    };
    global.addEventListener('message', listener, false);
  // IE8-
  } else if(ONREADYSTATECHANGE in cel('script')){
    defer = function(id){
      html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){
        html.removeChild(this);
        run.call(id);
      };
    };
  // Rest old browsers
  } else {
    defer = function(id){
      setTimeout(ctx(run, id, 1), 0);
    };
  }
}
module.exports = {
  set:   setTask,
  clear: clearTask
};<commit_msg>Add support for the Sphere Dispatch API<commit_after>var ctx                = require('./_ctx')
  , invoke             = require('./_invoke')
  , html               = require('./_html')
  , cel                = require('./_dom-create')
  , global             = require('./_global')
  , process            = global.process
  , setTask            = global.setImmediate
  , clearTask          = global.clearImmediate
  , MessageChannel     = global.MessageChannel
  , Dispatch           = global.Dispatch
  , counter            = 0
  , queue              = {}
  , ONREADYSTATECHANGE = 'onreadystatechange'
  , defer, channel, port;
var run = function(){
  var id = +this;
  if(queue.hasOwnProperty(id)){
    var fn = queue[id];
    delete queue[id];
    fn();
  }
};
var listener = function(event){
  run.call(event.data);
};
// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
if(!setTask || !clearTask){
  setTask = function setImmediate(fn){
    var args = [], i = 1;
    while(arguments.length > i)args.push(arguments[i++]);
    queue[++counter] = function(){
      invoke(typeof fn == 'function' ? fn : Function(fn), args);
    };
    defer(counter);
    return counter;
  };
  clearTask = function clearImmediate(id){
    delete queue[id];
  };
  // Node.js 0.8-
  if(require('./_cof')(process) == 'process'){
    defer = function(id){
      process.nextTick(ctx(run, id, 1));
    };
  // Sphere (JS game engine) Dispatch API
  } else if(Dispatch && Dispatch.now){
    defer = function(id){
      Dispatch.now(ctx(run, id, 1));
    };
  // Browsers with MessageChannel, includes WebWorkers
  } else if(MessageChannel){
    channel = new MessageChannel;
    port    = channel.port2;
    channel.port1.onmessage = listener;
    defer = ctx(port.postMessage, port, 1);
  // Browsers with postMessage, skip WebWorkers
  // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
  } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){
    defer = function(id){
      global.postMessage(id + '', '*');
    };
    global.addEventListener('message', listener, false);
  // IE8-
  } else if(ONREADYSTATECHANGE in cel('script')){
    defer = function(id){
      html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){
        html.removeChild(this);
        run.call(id);
      };
    };
  // Rest old browsers
  } else {
    defer = function(id){
      setTimeout(ctx(run, id, 1), 0);
    };
  }
}
module.exports = {
  set:   setTask,
  clear: clearTask
};<|endoftext|>


<commit_before>import {inject, noView, processContent, bindable, TargetInstruction} from 'aurelia-framework';

@noView
@processContent((compiler, resources, element, instruction) => {
  let html = element.innerHTML;
  if (html !== '') {
    instruction.template = html;
  }

  return true;
})
@inject(TargetInstruction)
export class AuCol {
  @bindable aggregates;
  @bindable attributes;
  @bindable columns;
  @bindable command;
  @bindable editor;
  @bindable encoded;
  @bindable field;
  @bindable filterable;
  @bindable footerTemplate;
  @bindable format = '';
  @bindable groupable;
  @bindable groupFooterTemplate;
  @bindable groupHeaderTemplate;
  @bindable headerAttributes;
  @bindable headerTemplate;
  @bindable hidden;
  @bindable lockable;
  @bindable locked;
  @bindable menu;
  @bindable minScreenWidth;
  @bindable sortable;
  @bindable title;
  @bindable values;
  @bindable width;
  template;

  constructor(targetInstruction) {
    this.template = targetInstruction.elementInstruction.template;
  }
}
<commit_msg>fix(grid): enable bindable column template<commit_after>import {inject, noView, processContent, bindable, TargetInstruction} from 'aurelia-framework';

@noView
@processContent((compiler, resources, element, instruction) => {
  let html = element.innerHTML;
  if (html !== '') {
    instruction.template = html;
  }

  return true;
})
@inject(TargetInstruction)
export class AuCol {
  @bindable aggregates;
  @bindable attributes;
  @bindable columns;
  @bindable command;
  @bindable editor;
  @bindable encoded;
  @bindable field;
  @bindable filterable;
  @bindable footerTemplate;
  @bindable format = '';
  @bindable groupable;
  @bindable groupFooterTemplate;
  @bindable groupHeaderTemplate;
  @bindable headerAttributes;
  @bindable headerTemplate;
  @bindable hidden;
  @bindable lockable;
  @bindable locked;
  @bindable menu;
  @bindable minScreenWidth;
  @bindable sortable;
  @bindable title;
  @bindable values;
  @bindable width;
  @bindable template;

  constructor(targetInstruction) {
    this.template = targetInstruction.elementInstruction.template;
  }
}
<|endoftext|>


<commit_before>suite('Library set up', function () {
    test('firstBy should be in global scope', function (done) {
        if (firstBy) {
            done();
        }
    });
});

suite('Sorting with functions', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by Country, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.country < v2.country ? -1 : (v1.country > v2.country ? 1 : 0); })
                .thenBy(function (v1, v2) { return v1.population - v2.population; });
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by Country, then by Population, using unary functions', function(done) {
        var s = firstBy(function(v) { return v.country; })
                .thenBy(function(v) { return v.population; });
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by length of name, then by population, then by ID', function (done) {
        var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                 .thenBy(function (v1, v2) { return v1.population - v2.population; })
                 .thenBy(function (v1, v2) { return v1.id - v2.id; });
        cityData.sort(s);
        // shortest name
        assert.equal("Berlin", cityData[0].name);
        // longest name
        assert.equal("Düsseldorf", cityData[5].name);

        // expect Stutgard just after Rotterdam, same name length, same population, higher ID
        assert.equal("Rotterdam", cityData[2].name);
        assert.equal("Stuttgard", cityData[3].name);
        done();
    });
    test('Sort by length of name, then by population, then by ID, using unary functions', function (done) {
        var s = firstBy(function (v) { return v.name.length; })
                 .thenBy(function (v) { return v.population; })
                 .thenBy(function (v) { return v.id; });
        cityData.sort(s);
        // shortest name
        assert.equal("Berlin", cityData[0].name);
        // longest name
        assert.equal("Düsseldorf", cityData[5].name);

        // expect Stutgard just after Rotterdam, same name length, same population, higher ID
        assert.equal("Rotterdam", cityData[2].name);
        assert.equal("Stuttgard", cityData[3].name);
        done();
    });

});
suite('Sorting with property names', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by Country, then by Population', function (done) {
    	var s = firstBy("country")
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by Country desc, then by Population', function (done) {
    	var s = firstBy("country", -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[5].name);
        assert.equal("The Hague", cityData[0].name);
        done();
    });
    test('Sort by Country, then by Population desc', function (done) {
    	var s = firstBy("country")
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("The Hague", cityData[5].name);
        assert.equal("Berlin", cityData[0].name);
        done();
    });
});
suite('Sorting with functions and property names together', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by name length, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[0].name);
        assert.equal("Düsseldorf", cityData[5].name);
        done();
    });
    test('Sort by name length using a unary function, then by Population', function (done) {
    	var s = firstBy(function (v) { return v.name.length; })
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[0].name);
        assert.equal("Düsseldorf", cityData[5].name);
        done();
    });
    test('Sort by name length desc, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; }, -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[4].name);
        assert.equal("The Hague", cityData[1].name);
        done();
    });
    test('Sort by name length desc using a unary function, then by Population', function (done) {
    	var s = firstBy(function (v) { return v.name.length; }, -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[4].name);
        assert.equal("The Hague", cityData[1].name);
        done();
    });
    test('Sort by name length, then by Population desc', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[1].name);
        assert.equal("The Hague", cityData[4].name);
        done();
    });
    test('Sort by name length using a unary function, then by Population desc', function (done) {
    	var s = firstBy(function (v) { return v.name.length; })
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[1].name);
        assert.equal("The Hague", cityData[4].name);
        done();
    });
});
<commit_msg>Add tests for undefined properties<commit_after>suite('Library set up', function () {
    test('firstBy should be in global scope', function (done) {
        if (firstBy) {
            done();
        }
    });
});

suite('Sorting with functions', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by Country, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.country < v2.country ? -1 : (v1.country > v2.country ? 1 : 0); })
                .thenBy(function (v1, v2) { return v1.population - v2.population; });
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by Country, then by Population, using unary functions', function(done) {
        var s = firstBy(function(v) { return v.country; })
                .thenBy(function(v) { return v.population; });
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by length of name, then by population, then by ID', function (done) {
        var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                 .thenBy(function (v1, v2) { return v1.population - v2.population; })
                 .thenBy(function (v1, v2) { return v1.id - v2.id; });
        cityData.sort(s);
        // shortest name
        assert.equal("Berlin", cityData[0].name);
        // longest name
        assert.equal("Düsseldorf", cityData[5].name);

        // expect Stutgard just after Rotterdam, same name length, same population, higher ID
        assert.equal("Rotterdam", cityData[2].name);
        assert.equal("Stuttgard", cityData[3].name);
        done();
    });
    test('Sort by length of name, then by population, then by ID, using unary functions', function (done) {
        var s = firstBy(function (v) { return v.name.length; })
                 .thenBy(function (v) { return v.population; })
                 .thenBy(function (v) { return v.id; });
        cityData.sort(s);
        // shortest name
        assert.equal("Berlin", cityData[0].name);
        // longest name
        assert.equal("Düsseldorf", cityData[5].name);

        // expect Stutgard just after Rotterdam, same name length, same population, higher ID
        assert.equal("Rotterdam", cityData[2].name);
        assert.equal("Stuttgard", cityData[3].name);
        done();
    });

});
suite('Sorting with property names', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by Country, then by Population', function (done) {
    	var s = firstBy("country")
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Düsseldorf", cityData[0].name);
        done();
    });
    test('Sort by Country desc, then by Population', function (done) {
    	var s = firstBy("country", -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[5].name);
        assert.equal("The Hague", cityData[0].name);
        done();
    });
    test('Sort by Country, then by Population desc', function (done) {
    	var s = firstBy("country")
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("The Hague", cityData[5].name);
        assert.equal("Berlin", cityData[0].name);
        done();
    });
});
suite('Sorting with functions and property names together', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000, country: "Netherlands" },
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", population: 550000, country: "Germany" },
            { id: 44, name: "Stuttgard", population: 600000, country: "Germany" },
        ];

    test('Sort by name length, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[0].name);
        assert.equal("Düsseldorf", cityData[5].name);
        done();
    });
    test('Sort by name length using a unary function, then by Population', function (done) {
    	var s = firstBy(function (v) { return v.name.length; })
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Berlin", cityData[0].name);
        assert.equal("Düsseldorf", cityData[5].name);
        done();
    });
    test('Sort by name length desc, then by Population', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; }, -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[4].name);
        assert.equal("The Hague", cityData[1].name);
        done();
    });
    test('Sort by name length desc using a unary function, then by Population', function (done) {
    	var s = firstBy(function (v) { return v.name.length; }, -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[4].name);
        assert.equal("The Hague", cityData[1].name);
        done();
    });
    test('Sort by name length, then by Population desc', function (done) {
    	var s = firstBy(function (v1, v2) { return v1.name.length - v2.name.length; })
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[1].name);
        assert.equal("The Hague", cityData[4].name);
        done();
    });
    test('Sort by name length using a unary function, then by Population desc', function (done) {
    	var s = firstBy(function (v) { return v.name.length; })
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[1].name);
        assert.equal("The Hague", cityData[4].name);
        done();
    });
});
suite('Sorting with property names and undefined properties', function () {
	var cityData =  [
            { id: 7, name:  "Amsterdam", population: 750000, country: "Netherlands" },
            { id: 12, name: "The Hague", population: 450000, country: "Netherlands" },
            { id: 43, name: "Rotterdam", population: 600000 }, // Missing country: Netherlands
            { id: 5, name:  "Berlin", population: 3000000, country: "Germany" },
            { id: 42, name: "Düsseldorf", country: "Germany" }, // Missing population: 550000
            { id: 44, population: 600000, country: "Germany" }, // Missing name: Stuttgard
        ];

    test('Sort by Country, then by Population, missing country comes first', function (done) {
    	var s = firstBy("country")
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Amsterdam", cityData[5].name);
        assert.equal("Rotterdam", cityData[0].name);
        done();
    });
    test('Sort by Country desc, then by Population, missing country comes last', function (done) {
    	var s = firstBy("country", -1)
                .thenBy("population");
        cityData.sort(s);
        assert.equal("Rotterdam", cityData[5].name);
        assert.equal("The Hague", cityData[0].name);
        done();
    });
    test('Sort by Country, then by Population desc', function (done) {
    	var s = firstBy("country")
                .thenBy("population", -1);
        cityData.sort(s);
        assert.equal("The Hague", cityData[5].name);
        assert.equal("Rotterdam", cityData[0].name);
        done();
    });
});
<|endoftext|>


<commit_before>QUnit.module("Stomp Message");

QUnit.test("Send and receive a message", function (assert) {
  var done = assert.async();

  var body = Math.random();

  var client = stompClient();
  client.debug = TEST.debug;
  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        assert.equal(message.body, body);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, body);
    });
});

QUnit.test("Send and receive a message with a JSON body", function (assert) {
  var done = assert.async();

  var client = stompClient();
  var payload = { text: "hello", bool: true, value: Math.random() };

  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        var res = JSON.parse(message.body);
        assert.equal(res.text, payload.text);
        assert.equal(res.bool, payload.bool);
        assert.equal(res.value, payload.value);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, JSON.stringify(payload));
    });
});

QUnit.test("Send and receive a message with a multi-byte UTF8 string", function (assert) {
  var done = assert.async();

  var client = stompClient();
  var payload = "Älä sinä yhtään and السابق";

  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        assert.equal(message.body, payload);
        assert.equal(message.body.lenth, 26);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, JSON.stringify(payload));
    });
});<commit_msg>fixing a typo<commit_after>QUnit.module("Stomp Message");

QUnit.test("Send and receive a message", function (assert) {
  var done = assert.async();

  var body = Math.random();

  var client = stompClient();
  client.debug = TEST.debug;
  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        assert.equal(message.body, body);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, body);
    });
});

QUnit.test("Send and receive a message with a JSON body", function (assert) {
  var done = assert.async();

  var client = stompClient();
  var payload = { text: "hello", bool: true, value: Math.random() };

  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        var res = JSON.parse(message.body);
        assert.equal(res.text, payload.text);
        assert.equal(res.bool, payload.bool);
        assert.equal(res.value, payload.value);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, JSON.stringify(payload));
    });
});

QUnit.test("Send and receive a message with a multi-byte UTF8 string", function (assert) {
  var done = assert.async();

  var client = stompClient();
  var payload = "Älä sinä yhtään and السابق";

  client.connect(TEST.login, TEST.password,
    function () {
      client.subscribe(TEST.destination, function (message) {
        assert.equal(message.body, payload);
        assert.equal(message.body.length, 26);
        client.disconnect();

        done();
      });

      client.send(TEST.destination, {}, JSON.stringify(payload));
    });
});
<|endoftext|>


<commit_before>/**
	headjs CSS3 feature detector

	copyright: "tipiirai" / Tero Piirainen
	license: MIT
*/

/*
	To add a new test:

	head.feature("video", function() {
		var tag = document.createElement('video');
		return !!tag.canPlayType;
	});

	Good place to grab more tests

	https://github.com/Modernizr/Modernizr/blob/master/modernizr.js
*/
(function(api) {

	/* CSS modernizer */
	var el = document.createElement("i"),
		 style = el.style,
		 prefs = ' -o- -moz- -ms- -webkit- -khtml- '.split(' ');

	/*
		runs a vendor property test (-moz, ...)

		testAll("box-shadow: 0 0 0 red;");
	*/
	function testAll(definition)  {
		style.cssText = prefs.join(definition + ";");
		var len = style.cssText ? style.cssText.length : 0;
		return len > 0 && !style.cssText.split(";")[1];
	}

	var tests = {

		gradient: function() {
			var s1 = 'background-image:',
				 s2 = 'gradient(linear,left top,right bottom,from(#9f9),to(#fff));',
				 s3 = 'linear-gradient(left top,#eee,#fff);';

			style.cssText = (s1 + prefs.join(s2 + s1) + prefs.join(s3 + s1)).slice(0,-s1.length);
			return !!style.backgroundImage;
		},

		rgba: function() {
			style.cssText = "background-color:rgba(0,0,0,0.5)";
			return !!style.backgroundColor;
		},

		boxshadow: function() {
			return testAll("box-shadow: 0 0 0 red");
		},

		textshadow: function() {
			return style.textShadow === '';
		},

		multiplebgs: function() {
			style.cssText = "background:url(//:),url(//:),red url(//:)";
			return new RegExp("(url\\s*\\(.*?){3}").test(style.background);
		},

		borderimage: function() {
			return testAll("border-image: url(m.png) 1 1 stretch");
		},

		borderradius: function() {
			return testAll('border-radius:0');
		},

		opacity: function() {
			return el.style.opacity === "";
		},

		reflections: function() {
			return testAll("box-reflect:right 0");
		},

		transforms: function() {
			return testAll("transform:rotate(1deg)");
		},

		transitions: function() {
			return testAll("transition:all .1s linear");
		}

	};

	// queue features
	for (var key in tests) {
		if (tests[key]) {
			api.feature(key, tests[key].call(), true);
		}
	}

	// enable features at once
	api.feature();

})(window[head_conf.head]);

<commit_msg>Issue 22: CSS detection now works correctly.<commit_after>/**
	headjs CSS3 feature detector

	copyright: "tipiirai" / Tero Piirainen
	license: MIT
*/

/*
	To add a new test:

	head.feature("video", function() {
		var tag = document.createElement('video');
		return !!tag.canPlayType;
	});

	Good place to grab more tests

	https://github.com/Modernizr/Modernizr/blob/master/modernizr.js
*/
(function(api) {

	/* CSS modernizer */
	var el = document.createElement("i"),
		 style = el.style,
		 prefs = ' -o- -moz- -ms- -webkit- -khtml- '.split(' ');

	/*
		runs a vendor property test (-moz, ...)

		testAll("box-shadow: 0 0 0 red;");
	*/
	function testAll(definition)  {
		style.cssText = prefs.join(definition + ";");
		var val = style.cssText;
		if (val.indexOf("-o") != -1 && val.indexOf("-ms") != -1) { return false; }
		return !!val;
	}

	var tests = {

		gradient: function() {
			var s1 = 'background-image:',
				 s2 = 'gradient(linear,left top,right bottom,from(#9f9),to(#fff));',
				 s3 = 'linear-gradient(left top,#eee,#fff);';

			style.cssText = (s1 + prefs.join(s2 + s1) + prefs.join(s3 + s1)).slice(0,-s1.length);
			return !!style.backgroundImage;
		},

		rgba: function() {
			style.cssText = "background-color:rgba(0,0,0,0.5)";
			return !!style.backgroundColor;
		},

		boxshadow: function() {
			return testAll("box-shadow: 0 0 0 red");
		},

		textshadow: function() {
			return style.textShadow === '';
		},

		multiplebgs: function() {
			style.cssText = "background:url(//:),url(//:),red url(//:)";
			return new RegExp("(url\\s*\\(.*?){3}").test(style.background);
		},

		borderimage: function() {
			return testAll("border-image: url(m.png) 1 1 stretch");
		},

		borderradius: function() {
			return testAll('border-radius:0');
		},

		opacity: function() {
			return el.style.opacity === "";
		},

		reflections: function() {
			return testAll("box-reflect:right 0");
		},

		transforms: function() {
			return testAll("transform:rotate(1deg)");
		},

		transitions: function() {
			return testAll("transition:all .1s linear");
		}

	};

	// queue features
	for (var key in tests) {
		if (tests[key]) {
			api.feature(key, tests[key].call(), true);
		}
	}

	// enable features at once
	api.feature();

})(window[head_conf.head]);

<|endoftext|>


<commit_before>var InstanceManager = function() {
  this.instances = [];
  var my = this
  // TODO(mzero): better be only one of these, either assert or make work with multiple
  window.addEventListener('message', function(e) {
    for (i in my.instances) {
      if (e.source == my.instances[i].window) {
        e.data.port = e.ports[0];
        return my.instances[i].windowReady(e.data);
      }
    }
    return false;
  });
};

var instanceManager = new InstanceManager();

InstanceManager.prototype.openWindowed = function(url) {
  var w = window.open(url, 'testInstance');
  // TODO(mzero): only works if the user has allowed pop-ups!
  var i = new InstanceManager.Instance(w);
  this.instances.push(i);
  return i;
};

InstanceManager.prototype.closeAll = function() {
  for (i in this.instances) {
    this.instances[i].close();
  }
  this.instances = [];
};

InstanceManager.Instance = function(window) {
  this.window = window;
  this.ready = false;
  this.remoteInstID = null;
  this.initialSer = null;
  this.tunnel = null;
};

InstanceManager.Instance.prototype.initialized = function() {
  return this.ready;
};

InstanceManager.Instance.prototype.windowReady = function(data) {
  this.remoteInstID = data.instID;
  this.initialSer = data.ser;
  this.ready = true;
  this.tunnel = new CapTunnel(data.port);
};

InstanceManager.Instance.prototype.close = function() {
//  this.window.close();
};

describe("CapTunnels", function() {
  var instance;

  beforeEach(function() {
    instance = instanceManager.openWindowed("testInstance.html");
    waitsFor(function() { return instance.initialized(); }, "initialized timeout", 1000);
  });

  afterEach(function() {
    instanceManager.closeAll();
    instance = null;
  });

  it("should get notice of a new window", function() {
    runs(function() {
      expect(instance.ready).toBeTruthy();
      expect(typeof instance.initialSer).toEqual("string");
      expect(typeof instance.remoteInstID).toEqual("string");
      expect(typeof instance.tunnel).not.toBe(null);
    });
  });

  describe("with local capservers", function() {
    var localServer1, localServer2;

    beforeEach(function() {
      localServer1 = new CapServer();
      localServer2 = new CapServer();

      var ifaceMap = {};
      ifaceMap[instance.remoteInstID] = instance.tunnel.sendInterface;
      ifaceMap[localServer1.instanceID] = localServer1.publicInterface;
      ifaceMap[localServer2.instanceID] = localServer2.publicInterface;

      var resolver = function(instID) { return ifaceMap[instID] || null; };
      instance.tunnel.setLocalResolver(resolver);
      localServer1.setResolver(resolver);
      localServer2.setResolver(resolver);
    });

    it("should be able to invoke a remote cap", function() {
      var result;
      var done = false;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("answer",
          function(data) { result = data; done = true; },
          function(err) { done = true; });
      });
      waitsFor(function() { return done; }, "invoke timeout", 250);
      runs(function() { expect(result).toEqual(42); });
    });

    it("should be able to invoke a local cap from the remote side", function() {
      var invokeWithThreeCap;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("invokeWithThree",
          function(data) { invokeWithThreeCap = data; },
          function(err) { });
      });
      waitsFor(function() { return invokeWithThreeCap; }, "get invokeWithThree cap", 250);

      var received = false;
      var succeeded = false;
      var receivedMessage;
      var threeResult;
      runs(function() {
        var receiveCap = localServer1.grant(function(v) {
          received = true;
          receivedMessage = v;
          return v + 42;
        });
        invokeWithThreeCap.invoke(receiveCap,
				  function(result) {
				     succeeded = true;
				     threeResult = result;
				  },
				  function(result) {
				  });
      });
      waitsFor(function() { return received; }, "invoking invokeWithThree cap", 250);
      runs(function() {
        expect(receivedMessage).toEqual(3);
	expect(succeeded).toBe(true);
	expect(threeResult).toBe(32);
      });
    });

    it("should be able to invoke remote caps in async-mode", function() {
      /* receiveCap = function(v) { return v + 42; };
       * asyncResult =
       *   remoteSeedCap.invoke("remoteAsync").invoke(receiveCap)
       *
       * expect(force(asyncResult)).toBe(1041)
       *
       */

      var remoteAsyncCap;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("remoteAsync",
			    function(data) { remoteAsyncCap = data; },
			    function(err) { });
      });

      waitsFor(function() { return remoteAsyncCap; },
               "get invokeWithThreeAsync cap", 250);

      var asyncResult = false;
      var messageFromRemote = false;

      runs(function() {
        var receiveCap = localServer1.grant(function(v) {
          messageFromRemote = [v];
          return v + 42;
        });
        remoteAsyncCap.invoke(receiveCap,
		              function(v) { asyncResult = [v]; },
			      function(result) { });
      });
      waitsFor(function() { return asyncResult && messageFromRemote; },
               "invoking invokeWithThreeAsync cap", 250);
      runs(function() {
        expect(messageFromRemote[0]).toEqual(999);
	expect(asyncResult[0]).toBe(1041);
      });

    });
  });
});
<commit_msg>fixed descriptive strings<commit_after>var InstanceManager = function() {
  this.instances = [];
  var my = this
  // TODO(mzero): better be only one of these, either assert or make work with multiple
  window.addEventListener('message', function(e) {
    for (i in my.instances) {
      if (e.source == my.instances[i].window) {
        e.data.port = e.ports[0];
        return my.instances[i].windowReady(e.data);
      }
    }
    return false;
  });
};

var instanceManager = new InstanceManager();

InstanceManager.prototype.openWindowed = function(url) {
  var w = window.open(url, 'testInstance');
  // TODO(mzero): only works if the user has allowed pop-ups!
  var i = new InstanceManager.Instance(w);
  this.instances.push(i);
  return i;
};

InstanceManager.prototype.closeAll = function() {
  for (i in this.instances) {
    this.instances[i].close();
  }
  this.instances = [];
};

InstanceManager.Instance = function(window) {
  this.window = window;
  this.ready = false;
  this.remoteInstID = null;
  this.initialSer = null;
  this.tunnel = null;
};

InstanceManager.Instance.prototype.initialized = function() {
  return this.ready;
};

InstanceManager.Instance.prototype.windowReady = function(data) {
  this.remoteInstID = data.instID;
  this.initialSer = data.ser;
  this.ready = true;
  this.tunnel = new CapTunnel(data.port);
};

InstanceManager.Instance.prototype.close = function() {
//  this.window.close();
};

describe("CapTunnels", function() {
  var instance;

  beforeEach(function() {
    instance = instanceManager.openWindowed("testInstance.html");
    waitsFor(function() { return instance.initialized(); }, "initialized timeout", 1000);
  });

  afterEach(function() {
    instanceManager.closeAll();
    instance = null;
  });

  it("should get notice of a new window", function() {
    runs(function() {
      expect(instance.ready).toBeTruthy();
      expect(typeof instance.initialSer).toEqual("string");
      expect(typeof instance.remoteInstID).toEqual("string");
      expect(typeof instance.tunnel).not.toBe(null);
    });
  });

  describe("with local capservers", function() {
    var localServer1, localServer2;

    beforeEach(function() {
      localServer1 = new CapServer();
      localServer2 = new CapServer();

      var ifaceMap = {};
      ifaceMap[instance.remoteInstID] = instance.tunnel.sendInterface;
      ifaceMap[localServer1.instanceID] = localServer1.publicInterface;
      ifaceMap[localServer2.instanceID] = localServer2.publicInterface;

      var resolver = function(instID) { return ifaceMap[instID] || null; };
      instance.tunnel.setLocalResolver(resolver);
      localServer1.setResolver(resolver);
      localServer2.setResolver(resolver);
    });

    it("should be able to invoke a remote cap", function() {
      var result;
      var done = false;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("answer",
          function(data) { result = data; done = true; },
          function(err) { done = true; });
      });
      waitsFor(function() { return done; }, "invoke timeout", 250);
      runs(function() { expect(result).toEqual(42); });
    });

    it("should be able to invoke a local cap from the remote side", function() {
      var invokeWithThreeCap;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("invokeWithThree",
          function(data) { invokeWithThreeCap = data; },
          function(err) { });
      });
      waitsFor(function() { return invokeWithThreeCap; }, "get invokeWithThree cap", 250);

      var received = false;
      var succeeded = false;
      var receivedMessage;
      var threeResult;
      runs(function() {
        var receiveCap = localServer1.grant(function(v) {
          received = true;
          receivedMessage = v;
          return v + 42;
        });
        invokeWithThreeCap.invoke(receiveCap,
				  function(result) {
				     succeeded = true;
				     threeResult = result;
				  },
				  function(result) {
				  });
      });
      waitsFor(function() { return received; }, "invoking invokeWithThree cap", 250);
      runs(function() {
        expect(receivedMessage).toEqual(3);
	expect(succeeded).toBe(true);
	expect(threeResult).toBe(32);
      });
    });

    it("should be able to invoke remote caps in async-mode", function() {
      /* receiveCap = function(v) { return v + 42; };
       * asyncResult =
       *   remoteSeedCap.invoke("remoteAsync").invoke(receiveCap)
       *
       * expect(force(asyncResult)).toBe(1041)
       *
       */

      var remoteAsyncCap;
      runs(function() {
        var remoteSeedCap = localServer1.restore(instance.initialSer);
        remoteSeedCap.invoke("remoteAsync",
			    function(data) { remoteAsyncCap = data; },
			    function(err) { });
      });

      waitsFor(function() { return remoteAsyncCap; },
               "get remoteAsync cap", 250);

      var asyncResult = false;
      var messageFromRemote = false;

      runs(function() {
        var receiveCap = localServer1.grant(function(v) {
          messageFromRemote = [v];
          return v + 42;
        });
        remoteAsyncCap.invoke(receiveCap,
		              function(v) { asyncResult = [v]; },
			      function(result) { });
      });
      waitsFor(function() { return asyncResult && messageFromRemote; },
               "get asyncResult", 250);
      runs(function() {
        expect(messageFromRemote[0]).toEqual(999);
	expect(asyncResult[0]).toBe(1041);
      });

    });
  });
});
<|endoftext|>


<commit_before>(function(doc){
  var scriptElm = doc.scripts[doc.scripts.length - 1];

  var parts = scriptElm.src.split('/');
  parts.pop();
  parts.push('corporate-ui');
  var url = parts.join('/');

  scriptElm = doc.createElement('script');
  scriptElm.src = url + '/corporate-ui.js';

  doc.head.appendChild(scriptElm);

})(document);<commit_msg>Added script to stencil before other scripts<commit_after>(function(doc){
  var scriptElm = doc.scripts[doc.scripts.length - 1];

  var parentScript = doc.querySelectorAll('script');

  var parts = scriptElm.src.split('/');
  parts.pop();
  parts.push('corporate-ui');
  var url = parts.join('/');

  scriptElm = doc.createElement('script');
  scriptElm.src = url + '/corporate-ui.js';

  doc.head.insertBefore(scriptElm, parentScript[0]);

})(document);<|endoftext|>


<commit_before>import {join, resolve, dirname} from 'path'
import os from 'os'
import fs from 'fs'
import MainProcess from '../server/main-process'

/**
 * attach cli hooks to titanium CLI
 * this function must be named "init"
 * @public
 */
export function init(logger, config, cli) {

    const scope = {
        logger: logger,
        config: config,
        cli   : cli,
        host  : getAddress()
    }

    const hooks = {

        'build.android.config': scope::attachFasterFlag,

        'build.ios.config': scope::attachFasterFlag,

        'build.ios.copyResource': { pre: scope::modifyEntryName },

        'build.android.copyResource': { pre: scope::modifyEntryName },

        'build.post.compile': scope::launchServer
    }

    Object.keys(hooks)
        .forEach(hookName => cli.addHook(hookName, hooks[hookName]))
}

/**
 * attach --faster flag
 * @private (export for test)
 */
export function attachFasterFlag(data) {

    const { flags, options } = data.result[1]

    flags.faster = { default: false, desc: 'enables faster rebuilding' }
    options['faster-port1'] = { default: 4157, desc: 'port number of http server for faster-titanium' }
    options['faster-port2'] = { default: 4156, desc: 'port number of tcp server for faster-titanim' }
}

/**
 * app.js => second-entry-after-faster-titanium.js
 * @private (export for test)
 */
export function modifyEntryName(data) {
    const { 'project-dir': projectDir, faster } = this.cli.argv
    if (!faster) return;

    const [src, dist] = data.args
    const isAppJS = ['', 'android', 'ipad', 'iphone', 'ios']
        .map(name => join(projectDir, 'Resources', name, 'app.js'))
        .some(path => this.logger.info(path) || src === path)

    if (!isAppJS) return;

    const newname = 'second-entry-after-faster-titanium.js'

    this.logger.info(`[FasterTitanium] Renaming original app.js to ${newname}`)

    this.logger.warn(`modifyEntryName ${newname}`)

    data.args[1] = join(dirname(dist), newname) // modify distination

    this::createAppJS(dist)
}

/**
 * faster-titanium => app.js
 * @private (export for test)
 */
export function createAppJS(dist) {

    const { 'faster-port1': fPort, 'faster-port2': ePort } = this.cli.argv

    const optsForFasterTi = {
        fPort: parseInt(fPort, 10),
        ePort: parseInt(ePort, 10),
        host : this.host
    }
    const codeToRun = `Ti.FasterTitanium.run(${JSON.stringify(optsForFasterTi)})`

    const appJSPath = resolve(__dirname, '../../dist/app.js')
    const appJSCode = [fs.readFileSync(appJSPath, 'utf8'), codeToRun].join('\n')

    this.logger.info(`[FasterTitanium] Creating new app.js with host: ${this.host}, fPort: ${fPort}, ePort: ${ePort}`)

    fs.writeFileSync(dist, appJSCode)
}

/**
 * launch file/event server to communicate with App
 */
export function launchServer(data) {

    const { 'faster-port1': fPort, 'faster-port2': ePort } = this.cli.argv

    const optsForServer = {
        fPort: parseInt(fPort, 10),
        ePort: parseInt(ePort, 10),
        host : this.host
    }
    const {projectDir} = data

    new MainProcess(projectDir, optsForServer).start()
}

export function getAddress() {

    const interfaces = os.networkInterfaces();

    for (let k in interfaces) {
        const itf = interfaces[k]
        for (let j in itf) {
            if (itf[j].family === 'IPv4' && !itf[j].internal) {
                return itf[j].address
            }
        }
    }
    return 'localhost'
}

<commit_msg>don't launch server when faster option is not given<commit_after>import {join, resolve, dirname} from 'path'
import os from 'os'
import fs from 'fs'
import MainProcess from '../server/main-process'

/**
 * attach cli hooks to titanium CLI
 * this function must be named "init"
 *
 * ti build --faster [--port1 4157] [--port2 4156]
 * @public
 */
export function init(logger, config, cli) {

    const scope = {
        logger: logger,
        config: config,
        cli   : cli,
        host  : getAddress()
    }

    const hooks = {

        'build.android.config': scope::attachFasterFlag,
        'build.ios.config'    : scope::attachFasterFlag,

        'build.ios.copyResource'    : { pre: scope::modifyEntryName },
        'build.android.copyResource': { pre: scope::modifyEntryName },

        'build.post.compile': scope::launchServer
    }

    Object.keys(hooks)
        .forEach(hookName => cli.addHook(hookName, hooks[hookName]))
}

/**
 * attach --faster flag
 * @private (export for test)
 */
export function attachFasterFlag(data) {

    const { flags, options } = data.result[1]

    flags.faster = { default: false, desc: 'enables faster rebuilding' }
    options['faster-port1'] = { default: 4157, desc: 'port number of http server for faster-titanium' }
    options['faster-port2'] = { default: 4156, desc: 'port number of tcp server for faster-titanim' }
}

/**
 * app.js => second-entry-after-faster-titanium.js
 * @private (export for test)
 */
export function modifyEntryName(data) {
    const { 'project-dir': projectDir, faster } = this.cli.argv
    if (!faster) return;

    const [src, dist] = data.args
    const isAppJS = ['', 'android', 'ipad', 'iphone', 'ios']
        .map(name => join(projectDir, 'Resources', name, 'app.js'))
        .some(path => this.logger.info(path) || src === path)

    if (!isAppJS) return;

    const newname = 'second-entry-after-faster-titanium.js'

    this.logger.info(`[FasterTitanium] Renaming original app.js to ${newname}`)

    this.logger.warn(`modifyEntryName ${newname}`)

    data.args[1] = join(dirname(dist), newname) // modify distination

    this::createAppJS(dist)
}

/**
 * faster-titanium => app.js
 * @private (export for test)
 */
export function createAppJS(dist) {

    const { 'faster-port1': fPort, 'faster-port2': ePort } = this.cli.argv

    const optsForFasterTi = {
        fPort: parseInt(fPort, 10),
        ePort: parseInt(ePort, 10),
        host : this.host
    }
    const codeToRun = `Ti.FasterTitanium.run(${JSON.stringify(optsForFasterTi)})`

    const appJSPath = resolve(__dirname, '../../dist/app.js')
    const appJSCode = [fs.readFileSync(appJSPath, 'utf8'), codeToRun].join('\n')

    this.logger.info(`[FasterTitanium] Creating new app.js with host: ${this.host}, fPort: ${fPort}, ePort: ${ePort}`)

    fs.writeFileSync(dist, appJSCode)
}

/**
 * launch file/event servers to communicate with App
 */
export function launchServer(data) {

    const { 'project-dir': projectDir, faster } = this.cli.argv
    if (!faster) return;

    const { 'faster-port1': fPort, 'faster-port2': ePort } = this.cli.argv

    const optsForServer = {
        fPort: parseInt(fPort, 10),
        ePort: parseInt(ePort, 10),
        host : this.host
    }

    new MainProcess(projectDir, optsForServer).start()
}

/**
 * @returns {string} IP Adress (v4)
 * @private (export for test)
 */
export function getAddress() {

    const interfaces = os.networkInterfaces();

    for (let k in interfaces) {
        const itf = interfaces[k]
        for (let j in itf) {
            if (itf[j].family === 'IPv4' && !itf[j].internal) {
                return itf[j].address
            }
        }
    }
    return 'localhost'
}

<|endoftext|>