<commit_before>var
	path = require('path')
	, util = require('util')
	, mkdirp = require('mkdirp')
	, upnode = require('upnode')
	, creds = require(path.resolve(__dirname, '..', '..', 'lib', 'credentials'))
	, logger = require(path.resolve(__dirname, '..', '..', 'lib', 'logger'))
	, stream = require('stream')
	, tls = require('tls')
	, net = require('net')
	, fs = require('fs')
	, existsSync = fs.existsSync || path.existsSync
;

function client(opts, app) {

	var
		modules = {}
	;

	if(!opts || Object.keys(opts).length === 0) {

		app.log.error("Invalid opts object provided");
		return false;
	}

	if(!creds || typeof creds !== 'function') {

		app.log.error("Invalid credential provider specified");
		return false;
	}

	stream.call(this);

	this.app = app;
	this.opts = opts || undefined;
	this.sendBuffer = [ ];
	this.modules = { };
	this.devices = { };
	this.log = app.log;
	creds.call(this,opts);

	this.node = undefined; // upnode
	this.transport = opts.secure ? tls : net;
	this.parameters = this.getParameters.call(this, opts);
};

util.inherits(client, stream);

client.prototype.block = require('./block');

client.prototype.getHandlers = function() {

	return {

		revokeCredentials : function revokeCredentials() {

			this.log.info('Invalid token');
			this.app.emit('client::invalidToken', true);

		}.bind(this)
		, execute : function execute(cmd, cb) {

			console.log("Command request: %s", cmd);
			this.command(cmd);

		}.bind(this)
		, update : function update(to) {

			// update client
		}.bind(this)
		, config : function config(dat, cb) {

			// configure module/device
		}
		, install : function install(mod, cb) {

			// install module
		}.bind(this)
		, uninstall : function uninstall(mod, cb) {

			// uninstall module
		}.bind(this)
	}
};

/**
 * Connect the block to the cloud
 */
client.prototype.connect = function connect() {

	var client = this;
	this.node = upnode(this.getHandlers()).connect(this.parameters);

	this.node.on('reconnect', client.reconnect.bind(client));
	this.node.on('up', client.up.bind(client));
	this.initialize();
};

/**
 * Initialize the session with the cloud after a connection
 * has been established.
 */
client.prototype.initialize = function initialize() {

	var
		flushBuffer = function flushBuffer() {

			if(!this.sendBuffer) { this.sendBuffer = [ ]; return; }
			if(this.sendBuffer.length > 0) {

				this.log.debug("Sending buffered commands...");
				this.cloud.data({

					'DEVICE' : this.sendBuffer
				});
				this.sendBuffer = [ ];
			}
			else {

				this.log.debug("No buffered commands to send");
			}
		}
		, initSession = function initSession(cloud) {

			this.cloud = cloud;

			if(this.pulse) { clearInterval(this.pulse); }
			this.pulse = setInterval(beat.bind(this), 5000);

			flushBuffer.call(this);
		}
		, beat = function beat() {

			// this.log.debug("Sending heartbeat");
			this.cloud.heartbeat(JSON.stringify({

				"TIMESTAMP" : (new Date().getTime())
				, "DEVICE" : [ ]

			}));
		}
	;

	this.app.on('client::up', initSession);
};

/**
 * cloud event handlers
 */
client.prototype.up = function up(cloud) {

	this.emit('client::up', cloud);
	this.log.info("Client connected to the Ninja Platform");
};

client.prototype.down = function down() {

	this.app.emit('client::down', true);
	this.log.info("Client disconnected from the Ninja Platform");
	if(this.pulse) {

		clearInterval(this.pulse);
	}
};

client.prototype.reconnect = function reconnect() {

	this.app.emit('client::reconnecting', true);
	this.log.info("Connecting to cloud...");
};

/**
 * Generate scoped parameters for dnode connection
 */
client.prototype.getParameters = function getParameters(opts) {

	var
		cloudPort = this.opts.cloudPort
		, cloudHost = this.opts.cloudHost
		, transport = this.transport
	;

	return {

		ping : 10000
		, timeout : 5000
		, reconnect : 2000
		, createStream : function createStream() {

			return transport.connect(cloudPort, cloudHost);
		}
		, block : this.block.bind(this)
	};
};

client.prototype.registerDevice = function registerDevice(device) {

	if(!device) { return; }

	device.guid = this.getGuid(device);
	device.on('data', this.dataHandler.call(this, device));
	this.log.debug("Registering device %s", device.guid);
	this.devices[device.guid] = device;
};

client.prototype.dataHandler = function dataHandler(device) {

	var self = this;
	return function(data) {

		try {

			self.sendData({

				G : device.G
				, V : device.V
				, D : device.D
				, DA : data
			});
		}
		catch(e) {

			self.log.debug("Error sending data (%s)", self.getGuid(device));
		}
	}
};

client.prototype.sendData = function sendData(dat) {

	if(!dat) { return false; }

	dat.GUID = this.getGuid(dat);
	dat.TIMESTAMP = (new Date().getTime());

	if((this.app.cloud) && this.app.cloud.data) {

		var msg = { 'DEVICE' : [ dat ] };
		return this.app.cloud.data(msg);
	}

	this.bufferData(msg);
};

client.prototype.bufferData = function bufferData(msg) {

	this.sendBuffer.push(msg);

	if(this.sendBuffer.length > 9) {

		this.sendBuffer.shift();
	}
};

client.prototype.command = function command(dat) {

	var
		self = this
		, data = this.getJSON(dat)
	;

	for(var d = 0, ds = data.DEVICE; d < ds.length; d++) {

		console.log("Executing: ");
		console.log(ds[d]);

		var
			guid = ds[d].GUID
			, device
		;
		delete ds[d].GUID;

		ds[d].G = ds[d].G.toString();

		if((device = this.devices[guid]) && typeof device.write == "function") {

			try {

				return this.devices[guid].write(ds[d].DA);
			}
			catch(e) {

				this.log.error("error actuating: %s (%s)", guid, err.message);
			}
		}
		else {

			this.log.debug("actuating %s (%s)", guid, ds[d].DA);
			// write to TTY
		}
	}
};

/**
 * Initiate module loading sequence...
 */
client.prototype.loadModule = function loadModule(name, opts, app) {

	if(!name) {

		this.log.error("loadModule error: invalid module name");
		return false;
	}

	try {

		var
			file = path.resolve(__dirname, '..', '..', 'ninja_modules', name)
		;

		if(existsSync(file)) {

			var mod = require(file);
		}
		else {

			this.log.error("loadModule error: No such module '%s'", name);
			return null;
		}
	}
	catch(e) {

		this.log.error("loadModule error: %s", e);
		return false;
	}

	return this.addModule(name, opts, mod, app);
};

client.prototype.addModule = function addModule(name, params, mod, app) {

	if(!mod) {

		this.log.error(new Error('Invalid module provided'));
		return false;
	}

	var newModule = new mod(params, app);

	this.log.info("loadModule success: %s", name);
	if(!this.modules[name]) { this.modules[name] = {}; }

	this.modules[name][params.id] = newModule;
	this.bindModule(newModule, name);

	return this.modules[name][params.id];
};

client.prototype.bindModule = function bindModule(mod, name) {

	mod.save = function emitSave() { this.emit('save'); }.bind(mod);

	mod.on('register', this.registerDevice.bind(this));
	mod.on('error', this.moduleError.bind(mod));
	mod.on('save', this.saveHandler.call(this, name, mod));
	// set data handlers after registration
};

client.prototype.saveHandler = function saveHandler(name, mod) {

	return function saveConfig() {

		var
			conf = mod.config || null
			, file = path.resolve(

				process.cwd()
				, 'config'
				, name
				, 'config.json'
			)
			, data = null
		;

		if(!conf) {

			this.log.debug("saveConfig: No config to save (%s)", name);
			return false;
		}

		data = JSON.stringify({ config : conf }, null, '\t');

		if(!data) {

			this.log.debug("saveConfig: No JSON parsed (%s)", name);
			return false;
		}

		this.log.debug("saveConfig: writing config (%s)", file);

		mkdirp(path.dirname(file), ready);

		function ready(err) {

			if(err) {

				return this.log.error(

					"saveConfig: directory error: %s (%s)"
					, err
					, path.dirname(file)
				);
			}
			fs.writeFile(file, data, done);
		};

		function done(err) {

			if(err) {

				return this.log.error("saveConfig: write failure (%s)", name);
			}
			this.log.debug("saveConfig: great success! (%s)", name);
		};

		return true;
	}.bind(this);
};

client.prototype.moduleError = function moduleError(err) {

	this.log.error("Module error: %s", err);
};

client.prototype.getGuid = function getGuid(device) {

	return [

		this.serial
		, device.G
		, device.V
		, device.D

	].join('_');
};

client.prototype.getJSON = function getJSON(dat) {

	try {

		return JSON.parse(dat);
	}
	catch(e) {

		this.log.debug('Invalid JSON: %s', e);
		return false;
	}
};

function existsSync(file) {

	if(fs.existsSync) { return fs.existsSync(file); }
	return path.existsSync(file);
};

module.exports = client;
<commit_msg>Fix merge conflicts<commit_after>var
	path = require('path')
	, util = require('util')
	, mkdirp = require('mkdirp')
	, upnode = require('upnode')
	, creds = require(path.resolve(__dirname, '..', '..', 'lib', 'credentials'))
	, logger = require(path.resolve(__dirname, '..', '..', 'lib', 'logger'))
	, stream = require('stream')
	, tls = require('tls')
	, net = require('net')
	, fs = require('fs')
	, existsSync = fs.existsSync || path.existsSync
;

function client(opts, app) {

	var
		modules = {}
	;

	if(!opts || Object.keys(opts).length === 0) {

		app.log.error("Invalid opts object provided");
		return false;
	}

	if(!creds || typeof creds !== 'function') {

		app.log.error("Invalid credential provider specified");
		return false;
	}

	stream.call(this);

	this.app = app;
	this.opts = opts || undefined;
	this.sendBuffer = [ ];
	this.modules = { };
	this.devices = { };
	this.log = app.log;
	creds.call(this,opts);

	this.node = undefined; // upnode
	this.transport = opts.secure ? tls : net;
	this.parameters = this.getParameters.call(this, opts);
};

util.inherits(client, stream);

client.prototype.block = require('./block');

client.prototype.getHandlers = function() {

	return {

		revokeCredentials : function revokeCredentials() {

			this.log.info('Invalid token');
			this.app.emit('client::invalidToken', true);

		}.bind(this)
		, execute : function execute(cmd, cb) {

			console.log("Command request: %s", cmd);
			this.command(cmd);

		}.bind(this)
		, update : function update(to) {

			// update client
		}.bind(this)
		, config : function config(dat, cb) {

			// configure module/device
		}
		, install : function install(mod, cb) {

			// install module
		}.bind(this)
		, uninstall : function uninstall(mod, cb) {

			// uninstall module
		}.bind(this)
	}
};

/**
 * Connect the block to the cloud
 */
client.prototype.connect = function connect() {

	var client = this;
	this.node = upnode(this.getHandlers()).connect(this.parameters);

	this.node.on('reconnect', client.reconnect.bind(client));
	this.node.on('up', client.up.bind(client));
	this.initialize();
};

/**
 * Initialize the session with the cloud after a connection
 * has been established.
 */
client.prototype.initialize = function initialize() {

	var
		flushBuffer = function flushBuffer() {

			if(!this.sendBuffer) { this.sendBuffer = [ ]; return; }
			if(this.sendBuffer.length > 0) {

				this.log.debug("Sending buffered commands...");
				this.cloud.data({

					'DEVICE' : this.sendBuffer
				});
				this.sendBuffer = [ ];
			}
			else {

				this.log.debug("No buffered commands to send");
			}
		}
		, initSession = function initSession(cloud) {

			this.cloud = cloud;

			if(this.pulse) { clearInterval(this.pulse); }
			this.pulse = setInterval(beat.bind(this), 5000);

			flushBuffer.call(this);
		}
		, beat = function beat() {

			// this.log.debug("Sending heartbeat");
			this.cloud.heartbeat(JSON.stringify({

				"TIMESTAMP" : (new Date().getTime())
				, "DEVICE" : [ ]

			}));
		}
	;

	this.app.on('client::up', initSession);
};

/**
 * cloud event handlers
 */
client.prototype.up = function up(cloud) {

	this.emit('client::up', cloud);
	this.log.info("Client connected to the Ninja Platform");
};

client.prototype.down = function down() {

	this.app.emit('client::down', true);
	this.log.info("Client disconnected from the Ninja Platform");
	if(this.pulse) {

		clearInterval(this.pulse);
	}
};

client.prototype.reconnect = function reconnect() {

	this.app.emit('client::reconnecting', true);
	this.log.info("Connecting to cloud...");
};

/**
 * Generate scoped parameters for dnode connection
 */
client.prototype.getParameters = function getParameters(opts) {

	var
		cloudPort = this.opts.cloudPort
		, cloudHost = this.opts.cloudHost
		, transport = this.transport
	;

	return {

		ping : 10000
		, timeout : 5000
		, reconnect : 2000
		, createStream : function createStream() {

			return transport.connect(cloudPort, cloudHost);
		}
		, block : this.block.bind(this)
	};
};

client.prototype.registerDevice = function registerDevice(device) {

	if(!device) { return; }

	device.guid = this.getGuid(device);
	device.on('data', this.dataHandler.call(this, device));
	this.log.debug("Registering device %s", device.guid);
	this.devices[device.guid] = device;
};

client.prototype.dataHandler = function dataHandler(device) {

	var self = this;
	return function(data) {

		try {

			self.sendData({

				G : device.G
				, V : device.V
				, D : device.D
				, DA : data
			});
		}
		catch(e) {

			self.log.debug("Error sending data (%s)", self.getGuid(device));
		}
	}
};

client.prototype.sendData = function sendData(dat) {

	if(!dat) { return false; }

	dat.GUID = this.getGuid(dat);
	dat.TIMESTAMP = (new Date().getTime());

	if((this.app.cloud) && this.app.cloud.data) {

		var msg = { 'DEVICE' : [ dat ] };
		return this.app.cloud.data(msg);
	}

	this.bufferData(msg);
};

client.prototype.bufferData = function bufferData(msg) {

	this.sendBuffer.push(msg);

	if(this.sendBuffer.length > 9) {

		this.sendBuffer.shift();
	}
};

client.prototype.command = function command(dat) {

	var
		self = this
		, data = this.getJSON(dat)
	;

	for(var d = 0, ds = data.DEVICE; d < ds.length; d++) {

		console.log("Executing: ");
		console.log(ds[d]);

		var
			guid = ds[d].GUID
			, device
		;
		delete ds[d].GUID;

		ds[d].G = ds[d].G.toString();

		if((device = this.devices[guid]) && typeof device.write == "function") {

			try {

				return this.devices[guid].write(ds[d].DA);
			}
			catch(e) {

				this.log.error("error actuating: %s (%s)", guid, err.message);
			}
		}
		else {

			this.log.debug("actuating %s (%s)", guid, ds[d].DA);
			// write to TTY
		}
	}
};

/**
 * Initiate module loading sequence...
 */
client.prototype.loadModule = function loadModule(name, opts, app) {

	if(!name) {

		this.log.error("loadModule error: invalid module name");
		return false;
	}

	try {

		var
			file = path.resolve(

				process.cwd()
				, 'ninja_modules'
				, name
			)
		;

		if(existsSync(file)) {

			var mod = require(file);
		}
		else {

			this.log.error("loadModule error: No such module '%s'", name);
			return null;
		}
	}
	catch(e) {

		this.log.error("loadModule error: %s", e);
		return false;
	}

	return this.addModule(name, opts, mod, app);
};

client.prototype.addModule = function addModule(name, params, mod, app) {

	if(!mod) {

		this.log.error(new Error('Invalid module provided'));
		return false;
	}

	var newModule = new mod(params, app);

	this.log.info("loadModule success: %s", name);
	if(!this.modules[name]) { this.modules[name] = {}; }

	this.modules[name][params.id] = newModule;
	this.bindModule(newModule, name);

	return this.modules[name][params.id];
};

client.prototype.bindModule = function bindModule(mod, name) {

	mod.save = function emitSave() { this.emit('save'); }.bind(mod);

	mod.on('register', this.registerDevice.bind(this));
	mod.on('error', this.moduleError.bind(mod));
	mod.on('save', this.saveHandler.call(this, name, mod));
	// set data handlers after registration
};

client.prototype.saveHandler = function saveHandler(name, mod) {

	return function saveConfig() {

		var
			conf = mod.config || null
			, file = path.resolve(

				process.cwd()
				, 'config'
				, name
				, 'config.json'
			)
			, data = null
		;

		if(!conf) {

			this.log.debug("saveConfig: No config to save (%s)", name);
			return false;
		}

		data = JSON.stringify({ config : conf }, null, '\t');

		if(!data) {

			this.log.debug("saveConfig: No JSON parsed (%s)", name);
			return false;
		}

		this.log.debug("saveConfig: writing config (%s)", file);

		mkdirp(path.dirname(file), ready);

		function ready(err) {

			if(err) {

				return this.log.error(

					"saveConfig: directory error: %s (%s)"
					, err
					, path.dirname(file)
				);
			}
			fs.writeFile(file, data, done);
		};

		function done(err) {

			if(err) {

				return this.log.error("saveConfig: write failure (%s)", name);
			}
			this.log.debug("saveConfig: great success! (%s)", name);
		};

		return true;
	}.bind(this);
};

client.prototype.moduleError = function moduleError(err) {

	this.log.error("Module error: %s", err);
};

client.prototype.getGuid = function getGuid(device) {

	return [

		this.serial
		, device.G
		, device.V
		, device.D

	].join('_');
};

client.prototype.getJSON = function getJSON(dat) {

	try {

		return JSON.parse(dat);
	}
	catch(e) {

		this.log.debug('Invalid JSON: %s', e);
		return false;
	}
};

function existsSync(file) {

	if(fs.existsSync) { return fs.existsSync(file); }
	return path.existsSync(file);
};

module.exports = client;
<|endoftext|>


<commit_before>/*globals define,Raphael,_*/
/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved
 *
 * @author brollb / https://github/brollb
 *
 * Adding decorator support for showing connection areas
 */

define(['./SnapEditorWidget.Constants'], function (SNAP_CONSTANTS){

    "use strict";

    //Stuff for displaying connection area
    var CONN_AREA_EDIT_CLASS = "conn-area-edit",
        CONN_AREA_SIZE = 8,
        CONN_AREA = "conn-area-edit-bg",
        DATA_CONN_AREA_ID = "CONN_AREA_ID";

    var SnapEditorWidgetDecoratorBaseConnectionAreas = function(){
    };

    //Displaying Connection Area
    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype.displayConnectionArea = function (id) {
        var w = this.$el.outerWidth(true),
            h = this.$el.outerHeight(true),
            shiftVal = CONN_AREA_SIZE/2,
            line = this._getConnectionHighlightById(id) || this._getConnectionAreaById(id),
            highlight;

        this.hideConnectionAreas();

        if(line){
            if(this._connHighlight === undefined){
                this._connHighlight = $('<div/>', { 'class': CONN_AREA });
                this._connHighlight.css({ 'position': 'absolute',
                                     'left': -shiftVal + 'px',
                                     'top': -shiftVal + 'px'});
            }

            this._svg = Raphael(this._connHighlight[0], w + CONN_AREA_SIZE, h + CONN_AREA_SIZE);
            this._svg.canvas.className.baseVal = CONN_AREA;

            //Create the connection area
            line.x1 += shiftVal;
            line.x2 += shiftVal;
            line.y1 += shiftVal;
            line.y2 += shiftVal;

            //if the area is too small, enlarge it
            if (Math.abs(line.x2 - line.x1) < CONN_AREA_SIZE &&
                Math.abs(line.y2 - line.y1) < CONN_AREA_SIZE) {

                if (line.x2 > line.x1) {
                    line.x1 -= CONN_AREA_SIZE / 2;
                    line.x2 += CONN_AREA_SIZE / 2;
                } else {
                    line.x2 -= CONN_AREA_SIZE / 2;
                    line.x1 += CONN_AREA_SIZE / 2;
                }
            }

            var path = this._svg.path('M ' + line.x1 + ',' + line.y1 + 'L' + line.x2 + ',' + line.y2);
            $(path.node).data(DATA_CONN_AREA_ID, line.id);
            path.attr({ "stroke-width": CONN_AREA_SIZE });
            $(path.node).attr({ "class": CONN_AREA_EDIT_CLASS });

            this.$el.append(this._connHighlight);
        }
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype._getConnectionHighlightById = function (id) {
        var highlights = this.getSVGCustomData(SNAP_CONSTANTS.CONNECTION_HIGHLIGHT),
            i;

        if (highlights){
            i = highlights.length;
            while (i--){
                if (highlights[i].id === id){
                    return _.extend({}, highlights[i]);
                }
            }
        }

        return null;
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype._getConnectionAreaById = function (id) {
        var areas = this.getConnectionAreas(),
            i = areas.length;

        while (i--){
            if (areas[i].id === id){
                return areas[i];
            }
        }

        return null;
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype.hideConnectionAreas = function () {
        if(this._connHighlight){
            this._connHighlight.empty();
        }
    };

    return SnapEditorWidgetDecoratorBaseConnectionAreas;
});
<commit_msg>Added displayAllConnectionAreas<commit_after>/*globals define,Raphael,_*/
/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved
 *
 * @author brollb / https://github/brollb
 *
 * Adding decorator support for showing connection areas
 */

define(['./SnapEditorWidget.Constants'], function (SNAP_CONSTANTS){

    "use strict";

    //Stuff for displaying connection area
    var CONN_AREA_EDIT_CLASS = "conn-area-edit",
        CONN_AREA_SIZE = 8,
        CONN_AREA = "conn-area-edit-bg",
        DATA_CONN_AREA_ID = "CONN_AREA_ID",
        DEFAULT_COLOR = "#666666";//FIXME Move to css

    var SnapEditorWidgetDecoratorBaseConnectionAreas = function(){
    };

    //For debugging
    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype.displayAllConnectionAreas = function (options) {
        //Display all connections areas with the given colors
        var areas = this.getConnectionAreas(),
            i = areas.length,
            id;

        this.hideConnectionAreas();
        while (i--){
            id = areas[i].id;
            this._displayConnectionArea(id, options[id] || DEFAULT_COLOR);
        }
    };

    //Displaying Connection Area
    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype.displayConnectionArea = function (id, color) {
        this.hideConnectionAreas();
        this._displayConnectionArea(id, color);
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype._displayConnectionArea = function (id, color) {
        var w = this.$el.outerWidth(true),
            h = this.$el.outerHeight(true),
            shiftVal = CONN_AREA_SIZE/2,
            line = this._getConnectionHighlightById(id) || this._getConnectionAreaById(id),
            highlight;

        //Color
        color = color || DEFAULT_COLOR;

        if(line){
            if(this._connHighlight === undefined){
                this._connHighlight = $('<div/>', { 'class': CONN_AREA,
                    id: id });
                this._connHighlight.css({ 'position': 'absolute',
                                     'left': -shiftVal + 'px',
                                     'top': -shiftVal + 'px'});

            }

            if (this._connHighlight.children().length === 0){
                this._svg = Raphael(this._connHighlight[0], w + CONN_AREA_SIZE, h + CONN_AREA_SIZE);
                this._svg.canvas.className.baseVal = CONN_AREA;
            }

            //Create the connection area
            line.x1 += shiftVal;
            line.x2 += shiftVal;
            line.y1 += shiftVal;
            line.y2 += shiftVal;

            //if the area is too small, enlarge it
            if (Math.abs(line.x2 - line.x1) < CONN_AREA_SIZE &&
                Math.abs(line.y2 - line.y1) < CONN_AREA_SIZE) {

                if (line.x2 > line.x1) {
                    line.x1 -= CONN_AREA_SIZE / 2;
                    line.x2 += CONN_AREA_SIZE / 2;
                } else {
                    line.x2 -= CONN_AREA_SIZE / 2;
                    line.x1 += CONN_AREA_SIZE / 2;
                }
            }

            var path = this._svg.path('M ' + line.x1 + ',' + line.y1 + 'L' + line.x2 + ',' + line.y2);
            $(path.node).data(DATA_CONN_AREA_ID, line.id);
            path.attr({ "stroke-width": CONN_AREA_SIZE, "fill": color });
            $(path.node).attr({ "class": CONN_AREA_EDIT_CLASS });

            this.$el.append(this._connHighlight);
        }
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype._getConnectionHighlightById = function (id) {
        var highlights = this.getSVGCustomData(SNAP_CONSTANTS.CONNECTION_HIGHLIGHT),
            i;

        if (highlights){
            i = highlights.length;
            while (i--){
                if (highlights[i].id === id){
                    return _.extend({}, highlights[i]);
                }
            }
        }

        return null;
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype._getConnectionAreaById = function (id) {
        var areas = this.getConnectionAreas(),
            i = areas.length;

        while (i--){
            if (areas[i].id === id){
                return areas[i];
            }
        }

        return null;
    };

    SnapEditorWidgetDecoratorBaseConnectionAreas.prototype.hideConnectionAreas = function () {
        if(this._connHighlight){
            this._connHighlight.empty();
        }
    };

    return SnapEditorWidgetDecoratorBaseConnectionAreas;
});
<|endoftext|>


<commit_before>/**
 * Ajax upload
 * Project page - http://valums.com/ajax-upload/
 * Copyright (c) 2008 Andris Valums, http://valums.com
 * Licensed under the MIT license (http://valums.com/mit-license/)
 * Version 3.6 (26.06.2009)
 */

/**
 * Changes from the previous version:
 * 1. Fixed minor bug where click outside the button
 * would open the file browse window
 *
 * For the full changelog please visit:
 * http://valums.com/ajax-upload-changelog/
 */

(function(){

var d = document, w = window;

/**
 * Get element by id
 */
function get(element){
	if (typeof element == "string")
		element = d.getElementById(element);
	return element;
}

/**
 * Attaches event to a dom element
 */
function addEvent(el, type, fn){
	if (w.addEventListener){
		el.addEventListener(type, fn, false);
	} else if (w.attachEvent){
		var f = function(){
		  fn.call(el, w.event);
		};
		el.attachEvent('on' + type, f)
	}
}

/**
 * Creates and returns element from html chunk
 */
var toElement = function(){
	var div = d.createElement('div');
	return function(html){
		div.innerHTML = html;
		var el = div.childNodes[0];
		div.removeChild(el);
		return el;
	}
}();

function hasClass(ele,cls){
	return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
	if (!hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
	var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
	ele.className=ele.className.replace(reg,' ');
}

// getOffset function copied from jQuery lib (http://jquery.com/)
if (document.documentElement["getBoundingClientRect"]){
	// Get Offset using getBoundingClientRect
	// http://ejohn.org/blog/getboundingclientrect-is-awesome/
	var getOffset = function(el){
		var box = el.getBoundingClientRect(),
		doc = el.ownerDocument,
		body = doc.body,
		docElem = doc.documentElement,

		// for ie
		clientTop = docElem.clientTop || body.clientTop || 0,
		clientLeft = docElem.clientLeft || body.clientLeft || 0,

		// In Internet Explorer 7 getBoundingClientRect property is treated as physical,
		// while others are logical. Make all logical, like in IE8.

		zoom = 1;
		if (body.getBoundingClientRect) {
			var bound = body.getBoundingClientRect();
			zoom = (bound.right - bound.left)/body.clientWidth;
		}
		if (zoom > 1){
			clientTop = 0;
			clientLeft = 0;
		}
		var top = box.top/zoom + (window.pageYOffset || docElem && docElem.scrollTop/zoom || body.scrollTop/zoom) - clientTop,
		left = box.left/zoom + (window.pageXOffset|| docElem && docElem.scrollLeft/zoom || body.scrollLeft/zoom) - clientLeft;

		return {
			top: top,
			left: left
		};
	}

} else {
	// Get offset adding all offsets
	var getOffset = function(el){
		if (w.jQuery){
			return jQuery(el).offset();
		}

		var top = 0, left = 0;
		do {
			top += el.offsetTop || 0;
			left += el.offsetLeft || 0;
		}
		while (el = el.offsetParent);

		return {
			left: left,
			top: top
		};
	}
}

function getBox(el){
	var left, right, top, bottom;
	var offset = getOffset(el);
	left = offset.left;
	top = offset.top;

	right = left + el.offsetWidth;
	bottom = top + el.offsetHeight;

	return {
		left: left,
		right: right,
		top: top,
		bottom: bottom
	};
}

/**
 * Crossbrowser mouse coordinates
 */
function getMouseCoords(e){
	// pageX/Y is not supported in IE
	// http://www.quirksmode.org/dom/w3c_cssom.html
	if (!e.pageX && e.clientX){
		// In Internet Explorer 7 some properties (mouse coordinates) are treated as physical,
		// while others are logical (offset).
		var zoom = 1;
		var body = document.body;

		if (body.getBoundingClientRect) {
			var bound = body.getBoundingClientRect();
			zoom = (bound.right - bound.left)/body.clientWidth;
		}

		return {
			x: e.clientX / zoom + d.body.scrollLeft + d.documentElement.scrollLeft,
			y: e.clientY / zoom + d.body.scrollTop + d.documentElement.scrollTop
		};
	}

	return {
		x: e.pageX,
		y: e.pageY
	};

}
/**
 * Function generates unique id
 */
var getUID = function(){
	var id = 0;
	return function(){
		return 'ValumsAjaxUpload' + id++;
	}
}();

function fileFromPath(file){
	return file.replace(/.*(\/|\\)/, "");
}

function getExt(file){
	return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
}

// Please use AjaxUpload , Ajax_upload will be removed in the next version
Ajax_upload = AjaxUpload = function(button, options){
	if (button.jquery){
		// jquery object was passed
		button = button[0];
	} else if (typeof button == "string" && /^#.*/.test(button)){
		button = button.slice(1);
	}
	button = get(button);

	this._input = null;
	this._button = button;
	this._disabled = false;
	this._submitting = false;
	// Variable changes to true if the button was clicked
	// 3 seconds ago (requred to fix Safari on Mac error)
	this._justClicked = false;
	this._parentDialog = d.body;

	if (window.jQuery && jQuery.ui && jQuery.ui.dialog){
		var parentDialog = jQuery(this._button).parents('.ui-dialog');
		if (parentDialog.length){
			this._parentDialog = parentDialog[0];
		}
	}

	this._settings = {
		// Location of the server-side upload script
		action: 'upload.php',
		// File upload name
		name: 'userfile',
		// Additional data to send
		data: {},
		// Submit file as soon as it's selected
		autoSubmit: true,
		// The type of data that you're expecting back from the server.
		// Html and xml are detected automatically.
		// Only useful when you are using json data as a response.
		// Set to "json" in that case.
		responseType: false,
		// When user selects a file, useful with autoSubmit disabled
		onChange: function(file, extension){},
		// Callback to fire before file is uploaded
		// You can return false to cancel upload
		onSubmit: function(file, extension){},
		// Fired when file upload is completed
		// WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
		onComplete: function(file, response) {}
	};

	// Merge the users options with our defaults
	for (var i in options) {
		this._settings[i] = options[i];
	}

	this._createInput();
	this._rerouteClicks();
}

// assigning methods to our class
AjaxUpload.prototype = {
	setData : function(data){
		this._settings.data = data;
	},
	disable : function(){
		this._disabled = true;
	},
	enable : function(){
		this._disabled = false;
	},
	// removes ajaxupload
	destroy : function(){
		if(this._input){
			if(this._input.parentNode){
				this._input.parentNode.removeChild(this._input);
			}
			this._input = null;
		}
	},
	/**
	 * Creates invisible file input above the button
	 */
	_createInput : function(){
		var self = this;
		var input = d.createElement("input");
		input.setAttribute('type', 'file');
		input.setAttribute('name', this._settings.name);
		var styles = {
			'position' : 'absolute'
			,'margin': '-5px 0 0 -175px'
			,'padding': 0
			,'width': '220px'
			,'height': '30px'
			,'fontSize': '14px'
			,'opacity': 0
			,'cursor': 'pointer'
			,'display' : 'none'
			,'zIndex' :  2147483583 //Max zIndex supported by Opera 9.0-9.2x
			// Strange, I expected 2147483647
		};
		for (var i in styles){
			input.style[i] = styles[i];
		}

		// Make sure that element opacity exists
		// (IE uses filter instead)
		if ( ! (input.style.opacity === "0")){
			input.style.filter = "alpha(opacity=0)";
		}

		this._parentDialog.appendChild(input);

		addEvent(input, 'change', function(){
			// get filename from input
			var file = fileFromPath(this.value);
			if(self._settings.onChange.call(self, file, getExt(file)) == false ){
				return;
			}
			// Submit form when value is changed
			if (self._settings.autoSubmit){
				self.submit();
			}
		});

		// Fixing problem with Safari
		// The problem is that if you leave input before the file select dialog opens
		// it does not upload the file.
		// As dialog opens slowly (it is a sheet dialog which takes some time to open)
		// there is some time while you can leave the button.
		// So we should not change display to none immediately
		addEvent(input, 'click', function(){
			self.justClicked = true;
			setTimeout(function(){
				// we will wait 3 seconds for dialog to open
				self.justClicked = false;
			}, 2500);
		});

		this._input = input;
	},
	_rerouteClicks : function (){
		var self = this;

		// IE displays 'access denied' error when using this method
		// other browsers just ignore click()
		// addEvent(this._button, 'click', function(e){
		//   self._input.click();
		// });

		var box, dialogOffset = {top:0, left:0}, over = false;

		addEvent(self._button, 'mouseover', function(e){
			if (!self._input || over) return;

			over = true;
			box = getBox(self._button);

			if (self._parentDialog != d.body){
				dialogOffset = getOffset(self._parentDialog);
			}
		});

		// We can't use mouseout on the button,
		// because invisible input is over it
		addEvent(document, 'mousemove', function(e){
			var input = self._input;
			if (!input || !over) return;

			if (self._disabled){
				removeClass(self._button, 'hover');
				input.style.display = 'none';
				return;
			}

			var c = getMouseCoords(e);

			if ((c.x >= box.left) && (c.x <= box.right) &&
			(c.y >= box.top) && (c.y <= box.bottom)){

				input.style.top = c.y - dialogOffset.top + 'px';
				input.style.left = c.x - dialogOffset.left + 'px';
				input.style.display = 'block';
				addClass(self._button, 'hover');

			} else {
				// mouse left the button
				over = false;

				var check = setInterval(function(){
					// if input was just clicked do not hide it
					// to prevent safari bug

					if (self.justClicked){
						return;
					}

					if ( !over ){
						input.style.display = 'none';
					}

					clearInterval(check);

				}, 25);

				removeClass(self._button, 'hover');
			}
		});

	},
	/**
	 * Creates iframe with unique name
	 */
	_createIframe : function(){
		// unique name
		// We cannot use getTime, because it sometimes return
		// same value in safari :(
		var id = getUID();

		// Remove ie6 "This page contains both secure and nonsecure items" prompt
		// http://tinyurl.com/77w9wh
		var iframe = toElement('');
		iframe.id = id;
		iframe.style.display = 'none';
		d.body.appendChild(iframe);
		return iframe;
	},
	/**
	 * Upload file without refreshing the page
	 */
	submit : function(){
		var self = this, settings = this._settings;

		if (this._input.value === ''){
			// there is no file
			return;
		}

		// get filename from input
		var file = fileFromPath(this._input.value);

		// execute user event
		if (! (settings.onSubmit.call(this, file, getExt(file)) == false)) {
			// Create new iframe for this submission
			var iframe = this._createIframe();

			// Do not submit if user function returns false
			var form = this._createForm(iframe);
			form.appendChild(this._input);

			form.submit();

			d.body.removeChild(form);
			form = null;
			this._input = null;

			// create new input
			this._createInput();

			var toDeleteFlag = false;

			addEvent(iframe, 'load', function(e){

				if (// For Safari
					iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
					// For FF, IE
					iframe.src == "javascript:'';"){

					// First time around, do not delete.
					if( toDeleteFlag ){
						// Fix busy state in FF3
						setTimeout( function() {
							d.body.removeChild(iframe);
						}, 0);
					}
					return;
				}

				var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document;

				// fixing Opera 9.26
				if (doc.readyState && doc.readyState != 'complete'){
					// Opera fires load event multiple times
					// Even when the DOM is not ready yet
					// this fix should not affect other browsers
					return;
				}

				// fixing Opera 9.64
				if (doc.body && doc.body.innerHTML == "false"){
					// In Opera 9.64 event was fired second time
					// when body.innerHTML changed from false
					// to server response approx. after 1 sec
					return;
				}

				var response;

				if (doc.XMLDocument){
					// response is a xml document IE property
					response = doc.XMLDocument;
				} else if (doc.body){
					// response is html document or plain text
					response = doc.body.innerHTML;
					if (settings.responseType && settings.responseType.toLowerCase() == 'json'){
						// If the document was sent as 'application/javascript' or
						// 'text/javascript', then the browser wraps the text in a

						// tag and performs html encoding on the contents.  In this case,
						// we need to pull the original text content from the text node's
						// nodeValue property to retrieve the unmangled content.
						// Note that IE6 only understands text/html
						if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE'){
							response = doc.body.firstChild.firstChild.nodeValue;
						}
						if (response) {
							response = window["eval"]("(" + response + ")");
						} else {
							response = {};
						}
					}
				} else {
					// response is a xml document
					var response = doc;
				}

				settings.onComplete.call(self, file, response);

				// Reload blank page, so that reloading main page
				// does not re-submit the post. Also, remember to
				// delete the frame
				toDeleteFlag = true;

				// Fix IE mixed content issue
				iframe.src = "javascript:'';";
			});

		} else {
			// clear input to allow user to select same file
			// Doesn't work in IE6
			// this._input.value = '';
			d.body.removeChild(this._input);
			this._input = null;

			// create new input
			this._createInput();
		}
	},
	/**
	 * Creates form, that will be submitted to iframe
	 */
	_createForm : function(iframe){
		var settings = this._settings;

		// method, enctype must be specified here
		// because changing this attr on the fly is not allowed in IE 6/7
		var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
		form.style.display = 'none';
		form.action = settings.action;
		form.target = iframe.name;
		d.body.appendChild(form);

		// Create hidden input element for each data key
		for (var prop in settings.data){
			var el = d.createElement("input");
			el.type = 'hidden';
			el.name = prop;
			el.value = settings.data[prop];
			form.appendChild(el);
		}
		return form;
	}
};
})();
<commit_msg>Delete ajaxUpload.js<commit_after><|endoftext|>


<commit_before>let mongodb = require('mongodb');
let Collection = mongodb.Collection;

export default class MongoCollection {
  _mongoCollection:Collection;

  constructor(mongoCollection:Collection) {
    this._mongoCollection = mongoCollection;
  }

  // Does a find with "smart indexing".
  // Currently this just means, if it needs a geoindex and there is
  // none, then build the geoindex.
  // This could be improved a lot but it's not clear if that's a good
  // idea. Or even if this behavior is a good idea.
  find(query, { skip, limit, sort } = {}) {
    return this._rawFind(query, { skip, limit, sort })
      .catch(error => {
        // Check for "no geoindex" error
        if (error.code != 17007 && !error.message.match(/unable to find index for .geoNear/)) {
          throw error;
        }
        // Figure out what key needs an index
        let key = error.message.match(/field=([A-Za-z_0-9]+) /)[1];
        if (!key) {
          throw error;
        }

        var index = {};
        index[key] = '2d';
        return this._mongoCollection.createIndex(index)
          // Retry, but just once.
          .then(() => this._rawFind(query, { skip, limit, sort }));
      });
  }

  _rawFind(query, { skip, limit, sort } = {}) {
    return this._mongoCollection
      .find(query, { skip, limit, sort })
      .toArray();
  }

  count(query, { skip, limit, sort } = {}) {
    return this._mongoCollection.count(query, { skip, limit, sort });
  }

  // Atomically finds and updates an object based on query.
  // The result is the promise with an object that was in the database !AFTER! changes.
  // Postgres Note: Translates directly to `UPDATE * SET * ... RETURNING *`, which will return data after the change is done.
  findOneAndUpdate(query, update) {
    // arguments: query, sort, update, options(optional)
    // Setting `new` option to true makes it return the after document, not the before one.
    return this._mongoCollection.findAndModify(query, [], update, { new: true }).then(document => {
      // Value is the object where mongo returns multiple fields.
      return document.value;
    });
  }

  insertOne(object) {
    return this._mongoCollection.insertOne(object);
  }

  // Atomically updates data in the database for a single (first) object that matched the query
  // If there is nothing that matches the query - does insert
  // Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
  upsertOne(query, update) {
    return this._mongoCollection.update(query, update, { upsert: true });
  }

  updateOne(query, update) {
    return this._mongoCollection.updateOne(query, update);
  }

  updateMany(query, update) {
    return this._mongoCollection.updateMany(query, update);
  }

  deleteOne(query) {
    return this._mongoCollection.deleteOne(query);
  }

  deleteMany(query) {
    return this._mongoCollection.deleteMany(query);
  }

  drop() {
    return this._mongoCollection.drop();
  }
}
<commit_msg>Make find() in MongoStorageAdapter<commit_after>let mongodb = require('mongodb');
let Collection = mongodb.Collection;
import * as transform from './MongoTransform';

export default class MongoCollection {
  _mongoCollection:Collection;

  constructor(mongoCollection:Collection) {
    this._mongoCollection = mongoCollection;
  }

  // Does a find with "smart indexing".
  // Currently this just means, if it needs a geoindex and there is
  // none, then build the geoindex.
  // This could be improved a lot but it's not clear if that's a good
  // idea. Or even if this behavior is a good idea.

  // Depends on the className and schemaController because mongoObjectToParseObject does.
  // TODO: break this dependency
  find(query, { skip, limit, sort } = {}) {
    return this._rawFind(query, { skip, limit, sort })
    .catch(error => {
      // Check for "no geoindex" error
      if (error.code != 17007 && !error.message.match(/unable to find index for .geoNear/)) {
        throw error;
      }
      // Figure out what key needs an index
      let key = error.message.match(/field=([A-Za-z_0-9]+) /)[1];
      if (!key) {
        throw error;
      }

      var index = {};
      index[key] = '2d';
      return this._mongoCollection.createIndex(index)
      // Retry, but just once.
      .then(() => this._rawFind(query, { skip, limit, sort }));
    })
  }

  _rawFind(query, { skip, limit, sort } = {}) {
    return this._mongoCollection
      .find(query, { skip, limit, sort })
      .toArray();
  }

  count(query, { skip, limit, sort } = {}) {
    return this._mongoCollection.count(query, { skip, limit, sort });
  }

  // Atomically finds and updates an object based on query.
  // The result is the promise with an object that was in the database !AFTER! changes.
  // Postgres Note: Translates directly to `UPDATE * SET * ... RETURNING *`, which will return data after the change is done.
  findOneAndUpdate(query, update) {
    // arguments: query, sort, update, options(optional)
    // Setting `new` option to true makes it return the after document, not the before one.
    return this._mongoCollection.findAndModify(query, [], update, { new: true }).then(document => {
      // Value is the object where mongo returns multiple fields.
      return document.value;
    });
  }

  insertOne(object) {
    return this._mongoCollection.insertOne(object);
  }

  // Atomically updates data in the database for a single (first) object that matched the query
  // If there is nothing that matches the query - does insert
  // Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
  upsertOne(query, update) {
    return this._mongoCollection.update(query, update, { upsert: true });
  }

  updateOne(query, update) {
    return this._mongoCollection.updateOne(query, update);
  }

  updateMany(query, update) {
    return this._mongoCollection.updateMany(query, update);
  }

  deleteOne(query) {
    return this._mongoCollection.deleteOne(query);
  }

  deleteMany(query) {
    return this._mongoCollection.deleteMany(query);
  }

  drop() {
    return this._mongoCollection.drop();
  }
}
<|endoftext|>


<commit_before>/*
---

name: ListItem

description: Provides a list item control.

license: MIT-style license.

authors:
	- Jean-Philippe Dery (jeanphilippe.dery@gmail.com)

requires:
	- Control

provides:
	- ListItem

...
*/

/**
 * @name  ListItem
 * @class Provides a list item control.
 *
 * @classdesc
 *
 * [TODO: Description]
 * [TODO: Events]
 * [TODO: Roles]
 * [TODO: Styles]
 * [TODO: Options]
 * [TODO: Element Structure]
 *
 * @extends Control
 *
 * @author  Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @version 0.1
 */
Moobile.ListItem = new Class( /** @lends ListItem.prototype */ {

	Extends: Moobile.Control,

	/**
	 * The image.
	 * @type   Image
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	image: null,

	/**
	 * The label.
	 * @type   Text
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	label: null,

	/**
	 * The detail label.
	 * @type   Text
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	detail: null,

	options: {
		tagName: 'li'
	},

	willBuild: function() {

		this.parent();

		this.element.addClass('list-item');

		var image  = this.element.getRoleElement('image');
		var label  = this.element.getRoleElement('label');
		var detail = this.element.getRoleElement('detail');

		if (label == null) {
			label = new Element('div');
			label.ingest(this.element);
			label.inject(this.element);
			label.setRole('label');
		}

		if (image == null) {
			image = new Element('img');
			image.inject(this.element, 'top');
			image.setRole('image');
		}

		if (detail == null) {
			detail = new Element('div');
			detail.inject(this.element);
			image.setRole('detail');
		}
	},

	destroy: function() {
		this.label = null;
		this.image = null;
		this.detail = null;
		this.parent();
	},

	/**
	 * Sets the image.
	 *
	 * This method will set the label using either a string or an instance of
	 * an `Image`. When provided with a string, this methods creates an `Image`
	 * instance and assign the given string as its source.
	 *
	 * @param {Mixed} image The image as a string or an `Image` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setImage: function(image) {

		if (this.image === image)
			return this;

		if (typeof image == 'string') {
			var source = image;
			image = new Moobile.Image();
			image.setSource(text);
		}

		if (this.image == null) {
			this.image = image;
			this.addChild(image);
		} else {
			this.replaceChild(this.image, image);
			this.image.destroy();
			this.image = image;
		}

		return this;
	},

	/**
	 * Returns the image.
	 *
	 * @return {Image} The image.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getImage: function() {
		return this.image;
	},

	/**
	 * Sets the label.
	 *
	 * This method will set the label using either a string or an instance of a
	 * `Label`. When provided with a string, this methods creates a `Label`
	 * instance and assign the given string as its text.
	 *
	 * @param {Mixed} label The label as a string or `Label` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setLabel: function(label) {

		if (this.label === label)
			return this;

		if (typeof label == 'string') {
			var text = label;
			label = new Moobile.Text();
			label.setText(text);
		}

		if (this.label == null) {
			this.label = label;
			this.addChild(label);
		} else {
			this.replaceChild(this.label, label);
			this.label.destroy();
			this.label = label;
		}

		this.label.addClass('label');

		return this;
	},

	/**
	 * Returns the label.
	 *
	 * @return {Text} The label.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getLabel: function() {
		return this.label;
	},

	/**
	 * Sets the detail label.
	 *
	 * This method will set the label using either a string or an instance of a
	 * `Label`. When provided with a string, this methods creates a `Label`
	 * instance and assign the given string as its text.
	 *
	 * @param {Mixed} label The label as a string or `Label` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setDetail: function(detail) {

		if (this.detail === detail)
			return this;

		if (typeof detail == 'string') {
			var text = detail;
			detail = new Moobile.Text();
			detail.setText(text);
		}

		if (this.detail == null) {
			this.detail = detail;
			this.addChild(detail);
		} else {
			this.replaceChild(this.detail, detail);
			this.detail.destroy();
			this.detail = detail;
		}

		this.detail.addClass('detail');

		return this;
	},

	/**
	 * Returns the detail label.
	 *
	 * @return {Text} The detail label.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getDetail: function() {
		return this.detail;
	}

});

//------------------------------------------------------------------------------
// Roles
//------------------------------------------------------------------------------

Moobile.Component.defineRole('list-item', Moobile.List, function(element) {
	var instance = Moobile.Component.create(Moobile.ListItem, element, 'data-list-item');
	this.addItem(instance);
});

Moobile.Component.defineRole('image', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Image, element, 'data-image');
	this.setImage(instance);
});

Moobile.Component.defineRole('label', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Text, element, 'data-label');
	this.setLabel(instance);
});

Moobile.Component.defineRole('detail', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Text, element, 'data-detail');
	this.setDetail(instance);
});

//------------------------------------------------------------------------------
// Styles
//------------------------------------------------------------------------------

Moobile.Component.defineStyle('checked', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-checked'); },
	detach: function(element) { element.removeClass('style-checked'); }
});

Moobile.Component.defineStyle('disclosed', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-disclosed'); },
	detach: function(element) { element.removeClass('style-disclosed'); }
});

Moobile.Component.defineStyle('detailed', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-detailed'); },
	detach: function(element) { element.removeClass('style-detailed'); }
});
<commit_msg>Roles were not properly set<commit_after>/*
---

name: ListItem

description: Provides a list item control.

license: MIT-style license.

authors:
	- Jean-Philippe Dery (jeanphilippe.dery@gmail.com)

requires:
	- Control

provides:
	- ListItem

...
*/

/**
 * @name  ListItem
 * @class Provides a list item control.
 *
 * @classdesc
 *
 * [TODO: Description]
 * [TODO: Events]
 * [TODO: Roles]
 * [TODO: Styles]
 * [TODO: Options]
 * [TODO: Element Structure]
 *
 * @extends Control
 *
 * @author  Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
 * @version 0.1
 */
Moobile.ListItem = new Class( /** @lends ListItem.prototype */ {

	Extends: Moobile.Control,

	/**
	 * The image.
	 * @type   Image
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	image: null,

	/**
	 * The label.
	 * @type   Text
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	label: null,

	/**
	 * The detail label.
	 * @type   Text
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	detail: null,

	options: {
		tagName: 'li'
	},

	willBuild: function() {

		this.parent();

		this.element.addClass('list-item');

		var image  = this.element.getRoleElement('image');
		var label  = this.element.getRoleElement('label');
		var detail = this.element.getRoleElement('detail');

		if (label == null) {
			label = new Element('div');
			label.ingest(this.element);
			label.inject(this.element);
			label.setRole('label');
		}

		if (image == null) {
			image = new Element('img');
			image.inject(this.element, 'top');
			image.setRole('image');
		}

		if (detail == null) {
			detail = new Element('div');
			detail.inject(this.element);
			detail.setRole('detail');
		}
	},

	destroy: function() {
		this.label = null;
		this.image = null;
		this.detail = null;
		this.parent();
	},

	/**
	 * Sets the image.
	 *
	 * This method will set the label using either a string or an instance of
	 * an `Image`. When provided with a string, this methods creates an `Image`
	 * instance and assign the given string as its source.
	 *
	 * @param {Mixed} image The image as a string or an `Image` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setImage: function(image) {

		if (this.image === image)
			return this;

		if (typeof image == 'string') {
			var source = image;
			image = new Moobile.Image();
			image.setSource(text);
		}

		if (this.image == null) {
			this.image = image;
			this.addChild(image);
		} else {
			this.replaceChild(this.image, image);
			this.image.destroy();
			this.image = image;
		}

		return this;
	},

	/**
	 * Returns the image.
	 *
	 * @return {Image} The image.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getImage: function() {
		return this.image;
	},

	/**
	 * Sets the label.
	 *
	 * This method will set the label using either a string or an instance of a
	 * `Label`. When provided with a string, this methods creates a `Label`
	 * instance and assign the given string as its text.
	 *
	 * @param {Mixed} label The label as a string or `Label` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setLabel: function(label) {

		if (this.label === label)
			return this;

		if (typeof label == 'string') {
			var text = label;
			label = new Moobile.Text();
			label.setText(text);
		}

		if (this.label == null) {
			this.label = label;
			this.addChild(label);
		} else {
			this.replaceChild(this.label, label);
			this.label.destroy();
			this.label = label;
		}

		this.label.addClass('label');

		return this;
	},

	/**
	 * Returns the label.
	 *
	 * @return {Text} The label.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getLabel: function() {
		return this.label;
	},

	/**
	 * Sets the detail label.
	 *
	 * This method will set the label using either a string or an instance of a
	 * `Label`. When provided with a string, this methods creates a `Label`
	 * instance and assign the given string as its text.
	 *
	 * @param {Mixed} label The label as a string or `Label` instance.
	 *
	 * @return {ListItem} This list item.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	setDetail: function(detail) {

		if (this.detail === detail)
			return this;

		if (typeof detail == 'string') {
			var text = detail;
			detail = new Moobile.Text();
			detail.setText(text);
		}

		if (this.detail == null) {
			this.detail = detail;
			this.addChild(detail);
		} else {
			this.replaceChild(this.detail, detail);
			this.detail.destroy();
			this.detail = detail;
		}

		this.detail.addClass('detail');

		return this;
	},

	/**
	 * Returns the detail label.
	 *
	 * @return {Text} The detail label.
	 *
	 * @author Jean-Philippe Dery (jeanphilippe.dery@gmail.com)
	 * @since  0.1.0
	 */
	getDetail: function() {
		return this.detail;
	}

});

//------------------------------------------------------------------------------
// Roles
//------------------------------------------------------------------------------

Moobile.Component.defineRole('list-item', Moobile.List, function(element) {
	var instance = Moobile.Component.create(Moobile.ListItem, element, 'data-list-item');
	this.addItem(instance);
});

Moobile.Component.defineRole('image', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Image, element, 'data-image');
	this.setImage(instance);
});

Moobile.Component.defineRole('label', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Text, element, 'data-label');
	this.setLabel(instance);
});

Moobile.Component.defineRole('detail', Moobile.ListItem, function(element) {
	var instance = Moobile.Component.create(Moobile.Text, element, 'data-detail');
	this.setDetail(instance);
});

//------------------------------------------------------------------------------
// Styles
//------------------------------------------------------------------------------

Moobile.Component.defineStyle('checked', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-checked'); },
	detach: function(element) { element.removeClass('style-checked'); }
});

Moobile.Component.defineStyle('disclosed', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-disclosed'); },
	detach: function(element) { element.removeClass('style-disclosed'); }
});

Moobile.Component.defineStyle('detailed', Moobile.ListItem, {
	attach: function(element) { element.addClass('style-detailed'); },
	detach: function(element) { element.removeClass('style-detailed'); }
});
<|endoftext|>


<commit_before>/*global define*/
define([
        './DeveloperError',
        './Cartesian3',
        './ComponentDatatype',
        './PrimitiveType',
        './defaultValue',
        './BoundingSphere',
        './Geometry',
        './GeometryAttribute',
        './GeometryIndices'
    ], function(
        DeveloperError,
        Cartesian3,
        ComponentDatatype,
        PrimitiveType,
        defaultValue,
        BoundingSphere,
        Geometry,
        GeometryAttribute,
        GeometryIndices) {
    "use strict";

    /**
     * DOC_TBA
     *
     * @alias EllipsoidGeometry
     * @constructor
     *
     * @exception {DeveloperError} All dimensions components must be greater than or equal to zero.
     */
    var BoxGeometry = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        var minimumCorner;
        var maximumCorner;

        if (typeof options.minimumCorner !== 'undefined' && typeof options.maximumCorner !== 'undefined') {
            minimumCorner = options.minimumCorner;
            maximumCorner = options.maximumCorner;
        } else {
            var dimensions = typeof options.dimensions !== 'undefined' ? options.dimensions : new Cartesian3(1.0, 1.0, 1.0);

            if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) {
                throw new DeveloperError('All dimensions components must be greater than or equal to zero.');
            }

            var corner = dimensions.multiplyByScalar(0.5);
            minimumCorner = corner.negate();
            maximumCorner = corner;
        }

        /**
         * DOC_TBA
         */
        this.attributes = {
            position : new GeometryAttribute({
                componentDatatype : ComponentDatatype.FLOAT,
                componentsPerAttribute : 3,
                values : [
                    // 8 corner points.
                    minimumCorner.x, minimumCorner.y, minimumCorner.z,
                    maximumCorner.x, minimumCorner.y, minimumCorner.z,
                    maximumCorner.x, maximumCorner.y, minimumCorner.z,
                    minimumCorner.x, maximumCorner.y, minimumCorner.z,
                    minimumCorner.x, minimumCorner.y, maximumCorner.z,
                    maximumCorner.x, minimumCorner.y, maximumCorner.z,
                    maximumCorner.x, maximumCorner.y, maximumCorner.z,
                    minimumCorner.x, maximumCorner.y, maximumCorner.z
                ]
            })
        };

        /**
         * DOC_TBA
         */
        this.indexLists = [
            new GeometryIndices({
                // 12 triangles:  6 faces, 2 triangles each.
                primitiveType : PrimitiveType.TRIANGLES,
                values : [
                    4, 5, 6, // Top: plane z = corner.Z
                    4, 6, 7,
                    1, 0, 3, // Bottom: plane z = -corner.Z
                    1, 3, 2,
                    1, 6, 5, // Side: plane x = corner.X
                    1, 2, 6,
                    2, 3, 7, // Side: plane y = corner.Y
                    2, 7, 6,
                    3, 0, 4, // Side: plane x = -corner.X
                    3, 4, 7,
                    0, 1, 5, // Side: plane y = -corner.Y
                    0, 5, 4
                ]
            })
        ];

        /**
         * DOC_TBA
         */
        this.boundingSphere = new BoundingSphere(new Cartesian3(), maximumCorner.subtract(minimumCorner).magnitude() * 0.5);
    };

    BoxGeometry.prototype = new Geometry();

    return BoxGeometry;
});<commit_msg>Start of vertex format<commit_after>/*global define*/
define([
        './DeveloperError',
        './Cartesian3',
        './ComponentDatatype',
        './PrimitiveType',
        './defaultValue',
        './BoundingSphere',
        './Geometry',
        './GeometryAttribute',
        './GeometryIndices'
    ], function(
        DeveloperError,
        Cartesian3,
        ComponentDatatype,
        PrimitiveType,
        defaultValue,
        BoundingSphere,
        Geometry,
        GeometryAttribute,
        GeometryIndices) {
    "use strict";

// TODO: move elsewhere

    /**
     * DOC_TBA
     *
     * @alias GeometryVertexFormat
     * @constructor
     */
    var GeometryVertexFormat = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        /**
         * DOC_TBA
         */
        this.position = defaultValue(options.position, true);

        /**
         * DOC_TBA
         */
        this.normal = defaultValue(options.normal, true);

        /**
         * DOC_TBA
         */
        this.textureCoordinates = defaultValue(options.textureCoordinates, true);

        /**
         * DOC_TBA
         */
        this.binormal = defaultValue(options.binormal, false);

        /**
         * DOC_TBA
         */
        this.tangent = defaultValue(options.tangent, false);
    };

 // TODO: freezeObject

    /**
     * DOC_TBA
     */
    GeometryVertexFormat.DEFAULT = new GeometryVertexFormat();

    /**
     * DOC_TBA
     */
    GeometryVertexFormat.POSITION_ONLY = new GeometryVertexFormat({
        normal : false,
        textureCoordinates : false
    });

/////////////////////////////////////////////////////////////////

    /**
     * DOC_TBA
     *
     * @alias EllipsoidGeometry
     * @constructor
     *
     * @exception {DeveloperError} All dimensions components must be greater than or equal to zero.
     */
    var BoxGeometry = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        var minimumCorner;
        var maximumCorner;

        if (typeof options.minimumCorner !== 'undefined' && typeof options.maximumCorner !== 'undefined') {
            minimumCorner = options.minimumCorner;
            maximumCorner = options.maximumCorner;
        } else {
            var dimensions = typeof options.dimensions !== 'undefined' ? options.dimensions : new Cartesian3(1.0, 1.0, 1.0);

            if (dimensions.x < 0 || dimensions.y < 0 || dimensions.z < 0) {
                throw new DeveloperError('All dimensions components must be greater than or equal to zero.');
            }

            var corner = dimensions.multiplyByScalar(0.5);
            minimumCorner = corner.negate();
            maximumCorner = corner;
        }

        var vertexFormat = defaultValue(options.vertexFormat, GeometryVertexFormat.DEFAULT);

        var attributes;
        var indexLists;

//        if (vertexFormat !== GeometryVertexFormat.POSITION_ONLY) {
//        } else {
            // Positions only - no need to duplicate corner points
            attributes = {
                position : new GeometryAttribute({
                    componentDatatype : ComponentDatatype.FLOAT,
                    componentsPerAttribute : 3,
                    values : [
                        // 8 corner points.
                        minimumCorner.x, minimumCorner.y, minimumCorner.z,
                        maximumCorner.x, minimumCorner.y, minimumCorner.z,
                        maximumCorner.x, maximumCorner.y, minimumCorner.z,
                        minimumCorner.x, maximumCorner.y, minimumCorner.z,
                        minimumCorner.x, minimumCorner.y, maximumCorner.z,
                        maximumCorner.x, minimumCorner.y, maximumCorner.z,
                        maximumCorner.x, maximumCorner.y, maximumCorner.z,
                        minimumCorner.x, maximumCorner.y, maximumCorner.z
                    ]
                })
            };

            indexLists = [
                new GeometryIndices({
                    // 12 triangles:  6 faces, 2 triangles each.
                    primitiveType : PrimitiveType.TRIANGLES,
                    values : [
                        4, 5, 6, // Top: plane z = corner.Z
                        4, 6, 7,
                        1, 0, 3, // Bottom: plane z = -corner.Z
                        1, 3, 2,
                        1, 6, 5, // Side: plane x = corner.X
                        1, 2, 6,
                        2, 3, 7, // Side: plane y = corner.Y
                        2, 7, 6,
                        3, 0, 4, // Side: plane x = -corner.X
                        3, 4, 7,
                        0, 1, 5, // Side: plane y = -corner.Y
                        0, 5, 4
                    ]
                })
            ];
//        }

        /**
         * DOC_TBA
         */
        this.attributes = attributes;

        /**
         * DOC_TBA
         */
        this.indexLists = indexLists;

        /**
         * DOC_TBA
         */
        this.boundingSphere = new BoundingSphere(new Cartesian3(), maximumCorner.subtract(minimumCorner).magnitude() * 0.5);
    };

    BoxGeometry.prototype = new Geometry();

    return BoxGeometry;
});<|endoftext|>


<commit_before>/**
 * Base drag class
 *
 * Does all the grunt work for manipulating elements via click-and-drag,
 * while leaving the actual element manipulation up to a subclass.
 */
Garnish.BaseDrag = Garnish.Base.extend({

	$items: null,

	dragging: false,

	mousedownX: null,
	mousedownY: null,
	mouseDistX: null,
	mouseDistY: null,
	$targetItem: null,
	targetItemMouseDiffX: null,
	targetItemMouseDiffY: null,
	mouseX: null,
	mouseY: null,
	lastMouseX: null,
	lastMouseY: null,

	/**
	 * Init
	 */
	init: function(items, settings)
	{
		// Param mapping
		if (!settings && Garnish.isObject(items))
		{
			// (settings)
			settings = items;
			items = null;
		}

		this.settings = $.extend({}, Garnish.BaseDrag.defaults, settings);

		this.$items = $();

		if (items) this.addItems(items);
	},

	/**
	 * On Mouse Down
	 */
	onMouseDown: function(ev)
	{
		// Ignore right clicks
		if (ev.button != Garnish.PRIMARY_CLICK)
		{
			return;
		}

		// ignore if we already have a target
		if (this.$targetItem) return;

		// Make sure the target isn't a button (unless the button is the handle)
		if (this.settings.ignoreButtons && ev.currentTarget != ev.target)
		{
			var $target = $(ev.target);
			if ($target.hasClass('btn') || $target.closest('.btn').length)
			{
				return;
			}
		}

		ev.preventDefault();

		// capture the target
		this.$targetItem = $($.data(ev.currentTarget, 'drag-item'));

		// capture the current mouse position
		this.mousedownX = this.mouseX = ev.pageX;
		this.mousedownY = this.mouseY = ev.pageY;

		// capture the difference between the mouse position and the target item's offset
		var offset = this.$targetItem.offset();
		this.targetItemMouseDiffX = ev.pageX - offset.left;
		this.targetItemMouseDiffY = ev.pageY - offset.top;

		// listen for mousemove, mouseup
		this.addListener(Garnish.$doc, 'mousemove', 'onMouseMove');
		this.addListener(Garnish.$doc, 'mouseup', 'onMouseUp');
	},

	/**
	 * On Moues Move
	 */
	onMouseMove: function(ev)
	{
		ev.preventDefault();

		if (this.settings.axis != Garnish.Y_AXIS) this.mouseX = ev.pageX;
		if (this.settings.axis != Garnish.X_AXIS) this.mouseY = ev.pageY;

		this.mouseDistX = this.mouseX - this.mousedownX;
		this.mouseDistY = this.mouseY - this.mousedownY;

		if (!this.dragging)
		{
			// Has the mouse moved far enough to initiate dragging yet?
			this.onMouseMove._mouseDist = Garnish.getDist(this.mousedownX, this.mousedownY, this.mouseX, this.mouseY);
			if (this.onMouseMove._mouseDist >= Garnish.BaseDrag.minMouseDist)
			{
				this.startDragging();
			}
			else
			{
				return;
			}
		}

		this.onDrag();
	},

	/**
	 * On Moues Up
	 */
	onMouseUp: function(ev)
	{
		// unbind the document events
		this.removeAllListeners(Garnish.$doc);

		if (this.dragging)
		{
			this.stopDragging();
		}

		this.$targetItem = null;
	},

	/**
	 * Start Dragging
	 */
	startDragging: function()
	{
		this.dragging = true;
		this.onDragStart();
	},

	/**
	 * Stop Dragging
	 */
	stopDragging: function()
	{
		this.dragging = false;

		this.onDragStop();
	},

	/**
	 * On Drag Start
	 */
	onDragStart: function()
	{
		this.settings.onDragStart();
	},

	/**
	 * On Drag
	 */
	onDrag: function()
	{
		this.settings.onDrag();
	},

	/**
	 * On Drag Stop
	 */
	onDragStop: function()
	{
		this.settings.onDragStop();
	},

	/**
	 * Add Items
	 */
	addItems: function(items)
	{
		items = $.makeArray(items);

		for (var i = 0; i < items.length; i++)
		{
			var item = items[i];

			// Make sure this element doesn't belong to another dragger
			if ($.data(item, 'drag'))
			{
				Garnish.log('Element was added to more than one dragger');
				$.data(item, 'drag').removeItems(item);
			}

			// Add the item
			$.data(item, 'drag', this);
			this.$items = this.$items.add(item);

			// Get the handle
			if (this.settings.handle)
			{
				if (typeof this.settings.handle == 'object')
				{
					var $handle = $(this.settings.handle);
				}
				else if (typeof this.settings.handle == 'string')
				{
					var $handle = $(item).find(this.settings.handle);
				}
				else if (typeof this.settings.handle == 'function')
				{
					var $handle = $(this.settings.handle(item));
				}
			}
			else
			{
				var $handle = $(item);
			}

			$.data(item, 'drag-handle', $handle);
			$handle.data('drag-item', item);
			this.addListener($handle, 'mousedown', 'onMouseDown');
		}
	},

	/**
	 * Remove Items
	 */
	removeItems: function(items)
	{
		items = $.makeArray(items);

		for (var i = 0; i < items.length; i++)
		{
			var item = items[i];

			// Make sure we actually know about this item
			var index = $.inArray(item, this.$items);
			if (index != -1)
			{
				var $handle = $.data(item, 'drag-handle');
				$handle.data('drag-item', null);
				$.data(item, 'drag', null);
				$.data(item, 'drag-handle', null);
				this.removeAllListeners($handle);
				this.$items.splice(index, 1);
			}
		}
	},

	/**
	 * Remove All Items
	 */
	removeAllItems: function()
	{
		for (var i = 0; i < this.$items.length; i++)
		{
			var item = this.$items[i],
				$handle = $.data(item, 'drag-handle');

			$.data(item, 'drag', null);

			if ($handle)
			{
				$.data(item, 'drag-handle', null);
				$handle.data('drag-item', null);
				this.removeAllListeners($handle);
			}
		}

		this.$items = $();
	}
},
{
	minMouseDist: 1,

	defaults: {
		handle: null,
		axis: null,
		ignoreButtons: true,

		onDragStart: $.noop,
		onDrag:      $.noop,
		onDragStop:  $.noop
	}
});
<commit_msg>BaseDrag.addItems() now resets the element order too<commit_after>/**
 * Base drag class
 *
 * Does all the grunt work for manipulating elements via click-and-drag,
 * while leaving the actual element manipulation up to a subclass.
 */
Garnish.BaseDrag = Garnish.Base.extend({

	$items: null,

	dragging: false,

	mousedownX: null,
	mousedownY: null,
	mouseDistX: null,
	mouseDistY: null,
	$targetItem: null,
	targetItemMouseDiffX: null,
	targetItemMouseDiffY: null,
	mouseX: null,
	mouseY: null,
	lastMouseX: null,
	lastMouseY: null,

	/**
	 * Init
	 */
	init: function(items, settings)
	{
		// Param mapping
		if (!settings && Garnish.isObject(items))
		{
			// (settings)
			settings = items;
			items = null;
		}

		this.settings = $.extend({}, Garnish.BaseDrag.defaults, settings);

		this.$items = $();

		if (items) this.addItems(items);
	},

	/**
	 * On Mouse Down
	 */
	onMouseDown: function(ev)
	{
		// Ignore right clicks
		if (ev.button != Garnish.PRIMARY_CLICK)
		{
			return;
		}

		// ignore if we already have a target
		if (this.$targetItem) return;

		// Make sure the target isn't a button (unless the button is the handle)
		if (this.settings.ignoreButtons && ev.currentTarget != ev.target)
		{
			var $target = $(ev.target);
			if ($target.hasClass('btn') || $target.closest('.btn').length)
			{
				return;
			}
		}

		ev.preventDefault();

		// capture the target
		this.$targetItem = $($.data(ev.currentTarget, 'drag-item'));

		// capture the current mouse position
		this.mousedownX = this.mouseX = ev.pageX;
		this.mousedownY = this.mouseY = ev.pageY;

		// capture the difference between the mouse position and the target item's offset
		var offset = this.$targetItem.offset();
		this.targetItemMouseDiffX = ev.pageX - offset.left;
		this.targetItemMouseDiffY = ev.pageY - offset.top;

		// listen for mousemove, mouseup
		this.addListener(Garnish.$doc, 'mousemove', 'onMouseMove');
		this.addListener(Garnish.$doc, 'mouseup', 'onMouseUp');
	},

	/**
	 * On Moues Move
	 */
	onMouseMove: function(ev)
	{
		ev.preventDefault();

		if (this.settings.axis != Garnish.Y_AXIS) this.mouseX = ev.pageX;
		if (this.settings.axis != Garnish.X_AXIS) this.mouseY = ev.pageY;

		this.mouseDistX = this.mouseX - this.mousedownX;
		this.mouseDistY = this.mouseY - this.mousedownY;

		if (!this.dragging)
		{
			// Has the mouse moved far enough to initiate dragging yet?
			this.onMouseMove._mouseDist = Garnish.getDist(this.mousedownX, this.mousedownY, this.mouseX, this.mouseY);
			if (this.onMouseMove._mouseDist >= Garnish.BaseDrag.minMouseDist)
			{
				this.startDragging();
			}
			else
			{
				return;
			}
		}

		this.onDrag();
	},

	/**
	 * On Moues Up
	 */
	onMouseUp: function(ev)
	{
		// unbind the document events
		this.removeAllListeners(Garnish.$doc);

		if (this.dragging)
		{
			this.stopDragging();
		}

		this.$targetItem = null;
	},

	/**
	 * Start Dragging
	 */
	startDragging: function()
	{
		this.dragging = true;
		this.onDragStart();
	},

	/**
	 * Stop Dragging
	 */
	stopDragging: function()
	{
		this.dragging = false;

		this.onDragStop();
	},

	/**
	 * On Drag Start
	 */
	onDragStart: function()
	{
		this.settings.onDragStart();
	},

	/**
	 * On Drag
	 */
	onDrag: function()
	{
		this.settings.onDrag();
	},

	/**
	 * On Drag Stop
	 */
	onDragStop: function()
	{
		this.settings.onDragStop();
	},

	/**
	 * Add Items
	 */
	addItems: function(items)
	{
		items = $.makeArray(items);

		for (var i = 0; i < items.length; i++)
		{
			var item = items[i];

			// Make sure this element doesn't belong to another dragger
			if ($.data(item, 'drag'))
			{
				Garnish.log('Element was added to more than one dragger');
				$.data(item, 'drag').removeItems(item);
			}

			// Add the item
			$.data(item, 'drag', this);

			// Get the handle
			if (this.settings.handle)
			{
				if (typeof this.settings.handle == 'object')
				{
					var $handle = $(this.settings.handle);
				}
				else if (typeof this.settings.handle == 'string')
				{
					var $handle = $(item).find(this.settings.handle);
				}
				else if (typeof this.settings.handle == 'function')
				{
					var $handle = $(this.settings.handle(item));
				}
			}
			else
			{
				var $handle = $(item);
			}

			$.data(item, 'drag-handle', $handle);
			$handle.data('drag-item', item);
			this.addListener($handle, 'mousedown', 'onMouseDown');
		}

		this.$items = $().add(this.$items.add(items));
	},

	/**
	 * Remove Items
	 */
	removeItems: function(items)
	{
		items = $.makeArray(items);

		for (var i = 0; i < items.length; i++)
		{
			var item = items[i];

			// Make sure we actually know about this item
			var index = $.inArray(item, this.$items);
			if (index != -1)
			{
				var $handle = $.data(item, 'drag-handle');
				$handle.data('drag-item', null);
				$.data(item, 'drag', null);
				$.data(item, 'drag-handle', null);
				this.removeAllListeners($handle);
				this.$items.splice(index, 1);
			}
		}
	},

	/**
	 * Remove All Items
	 */
	removeAllItems: function()
	{
		for (var i = 0; i < this.$items.length; i++)
		{
			var item = this.$items[i],
				$handle = $.data(item, 'drag-handle');

			$.data(item, 'drag', null);

			if ($handle)
			{
				$.data(item, 'drag-handle', null);
				$handle.data('drag-item', null);
				this.removeAllListeners($handle);
			}
		}

		this.$items = $();
	}
},
{
	minMouseDist: 1,

	defaults: {
		handle: null,
		axis: null,
		ignoreButtons: true,

		onDragStart: $.noop,
		onDrag:      $.noop,
		onDragStop:  $.noop
	}
});
<|endoftext|>


<commit_before>var fs = require('fs');
var data = JSON.parse(fs.readFileSync(__dirname + '/api.jsca'));
var Emitter = require('eventemitter2').EventEmitter2;
var util = require('util');
var _ = require('underscore');
var md5 = require('MD5');

// Modify Emitter's prototype to conform with what Ti does
Emitter.prototype.addEventListener = Emitter.prototype.on;
Emitter.prototype.removeEventListener = Emitter.prototype.removeListener;
Emitter.prototype.fireEvent = Emitter.prototype.emit;

Titanium = Ti = {};

function retrieve (root, list) {
  var name = list.shift();
  if (!root[name]) {
    root[name] = {};
    _.extend(root[name], Emitter.prototype);
  }
  var current = root[name];
  if (list.length > 0) return retrieve(current, list);
  else return current;
}

function method (namespace, obj, name) {
  // dont want to overwrite these
  if (['addEventListener', 'removeEventListener', 'fireEvent'].indexOf(name) != -1) return;

  // factory
  if (name.indexOf('create') != -1) {
    obj[name] = function (props) {
      var base = retrieve(Ti, namespace.concat([name.slice(6)]));
      var ret = {};
      _.extend(ret, base, props);
      return ret;
    };

    return;
  }

  if (name == 'add') {
    obj.add = function (view) {
      this.children = this.children || [];
      this.children.push(view);
    };
    return;
  }

  // otherwise it's some method so just make it emit that this method was
  // called
  obj[name] = function () {
    if (this.fireEvent) this.fireEvent('function::' + name, {});
  };
}

data.types.forEach(function (item) {
  // Don't worry about stuff that isn't actually in the Titanium namespace for
  // now
  if (item.name.indexOf('Titanium.') == -1) return;

  var name = item.name.slice(9);
  var namespace = name.split('.').slice(-1);
  var obj = retrieve(Ti, name.split('.'));

  item.functions.forEach(function (fn) {
    method(namespace, obj, fn.name);
  });

  item.properties.forEach(function (prop) {
  });
});

// Some manual stuff

Ti.Platform ={
  osname: 'android',
  version: '6',
  model: 'mock',
  id: 'mock-id',
  is24HourTimeFormat: function () { return false; },
  Android: {API_LEVEL: 18},
  displayCaps: {
    platformWidth: 480,
    platformHeight: 800,
    dpi: 160
  }
};

Ti.Network._requestURLs = {};
Ti.Network._requests = [];
var old = Ti.Network.createHTTPClient;
Ti.Network.createHTTPClient = function (spec) {
  var xhr = old(spec);
  xhr.open = function (method, url) {
    xhr.method = method;
    xhr.url = url;
    Ti.Network._requestURLs[url] = xhr;
    Ti.Network._requests.push(xhr);
    xhr.fireEvent('function::open', arguments);
  };

  xhr.send = function (data) {
    xhr.data = data;
    xhr.fireEvent('function::send', arguments);
  };

  return xhr;
};

Ti.Network._clear = function () {
  Ti.Network._requestURLs = {};
  Ti.Network._requests = [];
};

// Mock some file stuff
Ti.Filesystem._files = {};
// Non-standard, for convenience
Ti.Filesystem.createFile = function (spec) {
  var file = {};
  _.extend(file, Ti.Filesystem.File);
  file._exists = false;
  file.exists = function () {
    return file._exists;
  };

  file.read = function () {
    return {text: spec.text};
  };

  return file;
};
Ti.Filesystem.getFile = function (name) {
  return Ti.Filesystem._files[name] || Ti.Filesystem.createFile();
};
Ti.Filesystem.getApplicationDataDirectory = function () {
  return '';
};

Ti.Utils.md5HexDigest = function (input) {
  return md5(input);
};

Ti.include = function () {};
Ti.App.Properties.getString = function (name, def) { return def; };
Ti.App.Properties.getBool = function (name, def) { return def; };
Ti.App.Properties.getList = function (name, def) { return def; };

module.exports = Ti;
<commit_msg>Set Ti.Network.online = true;<commit_after>var fs = require('fs');
var data = JSON.parse(fs.readFileSync(__dirname + '/api.jsca'));
var Emitter = require('eventemitter2').EventEmitter2;
var util = require('util');
var _ = require('underscore');
var md5 = require('MD5');

// Modify Emitter's prototype to conform with what Ti does
Emitter.prototype.addEventListener = Emitter.prototype.on;
Emitter.prototype.removeEventListener = Emitter.prototype.removeListener;
Emitter.prototype.fireEvent = Emitter.prototype.emit;

Titanium = Ti = {};

function retrieve (root, list) {
  var name = list.shift();
  if (!root[name]) {
    root[name] = {};
    _.extend(root[name], Emitter.prototype);
  }
  var current = root[name];
  if (list.length > 0) return retrieve(current, list);
  else return current;
}

function method (namespace, obj, name) {
  // dont want to overwrite these
  if (['addEventListener', 'removeEventListener', 'fireEvent'].indexOf(name) != -1) return;

  // factory
  if (name.indexOf('create') != -1) {
    obj[name] = function (props) {
      var base = retrieve(Ti, namespace.concat([name.slice(6)]));
      var ret = {};
      _.extend(ret, base, props);
      return ret;
    };

    return;
  }

  if (name == 'add') {
    obj.add = function (view) {
      this.children = this.children || [];
      this.children.push(view);
    };
    return;
  }

  // otherwise it's some method so just make it emit that this method was
  // called
  obj[name] = function () {
    if (this.fireEvent) this.fireEvent('function::' + name, {});
  };
}

data.types.forEach(function (item) {
  // Don't worry about stuff that isn't actually in the Titanium namespace for
  // now
  if (item.name.indexOf('Titanium.') == -1) return;

  var name = item.name.slice(9);
  var namespace = name.split('.').slice(-1);
  var obj = retrieve(Ti, name.split('.'));

  item.functions.forEach(function (fn) {
    method(namespace, obj, fn.name);
  });

  item.properties.forEach(function (prop) {
  });
});

// Some manual stuff

Ti.Platform ={
  osname: 'android',
  version: '6',
  model: 'mock',
  id: 'mock-id',
  is24HourTimeFormat: function () { return false; },
  Android: {API_LEVEL: 18},
  displayCaps: {
    platformWidth: 480,
    platformHeight: 800,
    dpi: 160
  }
};

Ti.Network._requestURLs = {};
Ti.Network._requests = [];
var old = Ti.Network.createHTTPClient;
Ti.Network.createHTTPClient = function (spec) {
  var xhr = old(spec);
  xhr.open = function (method, url) {
    xhr.method = method;
    xhr.url = url;
    Ti.Network._requestURLs[url] = xhr;
    Ti.Network._requests.push(xhr);
    xhr.fireEvent('function::open', arguments);
  };

  xhr.send = function (data) {
    xhr.data = data;
    xhr.fireEvent('function::send', arguments);
  };

  return xhr;
};

Ti.Network._clear = function () {
  Ti.Network._requestURLs = {};
  Ti.Network._requests = [];
};

Ti.Network.online = true;

// Mock some file stuff
Ti.Filesystem._files = {};
// Non-standard, for convenience
Ti.Filesystem.createFile = function (spec) {
  var file = {};
  _.extend(file, Ti.Filesystem.File);
  file._exists = false;
  file.exists = function () {
    return file._exists;
  };

  file.read = function () {
    return {text: spec.text};
  };

  return file;
};
Ti.Filesystem.getFile = function (name) {
  return Ti.Filesystem._files[name] || Ti.Filesystem.createFile();
};
Ti.Filesystem.getApplicationDataDirectory = function () {
  return '';
};

Ti.Utils.md5HexDigest = function (input) {
  return md5(input);
};

Ti.include = function () {};
Ti.App.Properties.getString = function (name, def) { return def; };
Ti.App.Properties.getBool = function (name, def) { return def; };
Ti.App.Properties.getList = function (name, def) { return def; };

module.exports = Ti;
<|endoftext|>


<commit_before>'use strict';

// Set up test bundle
var path = require('path');
var fs = require('fs');
var wrench = require('wrench');
var C = require('./test-constants');

// Create the cfg dir if it does not exist
if (!fs.existsSync(C.CFG_DIR)) {
    fs.mkdirSync(C.CFG_DIR);
}

wrench.copyDirSyncRecursive(C.TEST_BUNDLE_SRC_PATH, C.BUNDLE_DIR, { forceDelete: true });
fs.writeFileSync(C.BUNDLE_CFG_PATH, JSON.stringify({ test: 'the_test_string' }));

// Delete any existing persisted replicants for the test bundle
wrench.rmdirSyncRecursive(path.resolve(__dirname, '../../db/replicants/test-bundle'));
<commit_msg>[tests] Prevent error when test-bundle replicant dir doesn't exist<commit_after>'use strict';

// Set up test bundle
var path = require('path');
var fs = require('fs');
var wrench = require('wrench');
var C = require('./test-constants');

// Create the cfg dir if it does not exist
if (!fs.existsSync(C.CFG_DIR)) {
    fs.mkdirSync(C.CFG_DIR);
}

wrench.copyDirSyncRecursive(C.TEST_BUNDLE_SRC_PATH, C.BUNDLE_DIR, { forceDelete: true });
fs.writeFileSync(C.BUNDLE_CFG_PATH, JSON.stringify({ test: 'the_test_string' }));

// Delete any existing persisted replicants for the test bundle
var replicantDir = path.resolve(__dirname, '../../db/replicants/test-bundle');
if (fs.existsSync(replicantDir)) {
    wrench.rmdirSyncRecursive(replicantDir);
}

<|endoftext|>


<commit_before>// Generators - Yield Statements
// To do: make all tests pass, leave the assert lines unchanged!

describe('generator instance', () => {

  function* generator() {
    yield 'hello';
    yield 'world';
  }

  let genInstance ;

  beforeEach(function() {
    genInstance = generator();
  });

  it('converted to an array returns all yielded values', () => {
    let value = Array.from(genInstance);
    assert.deepEqual(value, ['hello', 'world']);
  });

  it('should have value of "hello" after first next() call', () => {
    let thisStep = genInstance.next();
    let [value, done] = [thisStep.value, thisStep.done];
    assert.equal(value, 'hello');
    assert.equal(done, false);
  });

  it('should have value of "world" after second next() call', () => {
    genInstance.next();
    let thisStep = genInstance.next();
    let [value, done] = [thisStep.value, thisStep.done];
    assert.equal(value, 'world');
    assert.equal(done, false);
  });

  it('done property = true after stepping past all yield statements', () => {
    genInstance.next();
    genInstance.next();
    let done = genInstance.next().done;
    assert.equal(done, true);
  });

});
<commit_msg>make tests fail<commit_after>// Generators - Yield Expressions
// To do: make all tests pass, leave the assert lines unchanged!

describe('generator instance', () => {

  function* generator() {
    yield 'hello';
    yield 'world';
  }

  let genInstance;

  beforeEach(function() {
    genInstance = generator();
  });

  it('instance converted to an array returns all yielded values', () => {
    let values = Array.from();
    assert.deepEqual(values, ['hello', 'world']);
  });

  it('should have value of "hello" after first next() call', () => {
    let thisStep = genInstance;
    let [value, done] = [thisStep.value, thisStep.done];
    assert.equal(value, 'hello');
    assert.equal(done, false);
  });

  it('should have value of "world" after second next() call', () => {
    let thisStep = genInstance.next();
    let [value, done] = [thisStep.value, thisStep.done];
    assert.equal(value, 'world');
    assert.equal(done, false);
  });

  it('done property = true after stepping past all yield statements', () => {
    genInstance.next();
    genInstance.next();
    let done = genInstance.done;
    assert.equal(done, true);
  });

});
<|endoftext|>


<commit_before>'use strict';
/*global describe,beforeEach,inject,it,expect*/
describe('Service: esUrlSvc', function () {

  beforeEach(module('o19s.splainer-search'));

  var esUrlSvc;

  beforeEach(inject(function (_esUrlSvc_) {
    esUrlSvc = _esUrlSvc_;
  }));

  describe('parse URL', function() {
    it('extracts the different parts of a URL', function() {
      var url = 'http://localhost:9200/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
      expect(esUrlSvc.host).toBe('localhost:9200');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');

      var url = 'http://es.quepid.com/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
      expect(esUrlSvc.host).toBe('es.quepid.com');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');

      var url = 'https://es.quepid.com/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('https:');
      expect(esUrlSvc.host).toBe('es.quepid.com');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');
    });

    it('adds http if the protocol is missing', function() {
      var url = 'localhost:9200/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
    });
  });

  describe('build doc URL', function() {
    var url = 'http://localhost:9200/tmdb/_search';

    var doc = {
      _index: 'tmdb',
      _type:  'movies',
      _id:    '1'
    }

    beforeEach( function () {
      esUrlSvc.parseUrl(url);
    });

    it('builds a proper doc URL from the doc info', function() {
      var docUrl = esUrlSvc.buildDocUrl(doc);

      expect(docUrl).toBe('http://localhost:9200/tmdb/movies/1');
    });
  });
});
<commit_msg>Adds tests for the buildUrl for the esUrlSvc.<commit_after>'use strict';
/*global describe,beforeEach,inject,it,expect*/
describe('Service: esUrlSvc', function () {

  beforeEach(module('o19s.splainer-search'));

  var esUrlSvc;

  beforeEach(inject(function (_esUrlSvc_) {
    esUrlSvc = _esUrlSvc_;
  }));

  describe('parse URL', function() {
    it('extracts the different parts of a URL', function() {
      var url = 'http://localhost:9200/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
      expect(esUrlSvc.host).toBe('localhost:9200');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');

      var url = 'http://es.quepid.com/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
      expect(esUrlSvc.host).toBe('es.quepid.com');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');

      var url = 'https://es.quepid.com/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('https:');
      expect(esUrlSvc.host).toBe('es.quepid.com');
      expect(esUrlSvc.pathname).toBe('/tmdb/_search');
    });

    it('adds http if the protocol is missing', function() {
      var url = 'localhost:9200/tmdb/_search';
      esUrlSvc.parseUrl(url);

      expect(esUrlSvc.protocol).toBe('http:');
    });
  });

  describe('build doc URL', function() {
    var url = 'http://localhost:9200/tmdb/_search';

    var doc = {
      _index: 'tmdb',
      _type:  'movies',
      _id:    '1'
    }

    beforeEach( function () {
      esUrlSvc.parseUrl(url);
    });

    it('builds a proper doc URL from the doc info', function() {
      var docUrl = esUrlSvc.buildDocUrl(doc);

      expect(docUrl).toBe('http://localhost:9200/tmdb/movies/1');
    });
  });

  describe('build doc URL', function() {
    var url = 'http://localhost:9200/tmdb/_search';

    it('returns the original URL if no params are passed', function() {
      var returnedUrl = esUrlSvc.buildUrl(url);

      expect(returnedUrl).toBe(url);
    });

    it('returns the original URL if params passed is empty', function() {
      var params = { };
      var returnedUrl = esUrlSvc.buildUrl(url, params);

      expect(returnedUrl).toBe(url);
    });

    it('appends params to the original URL', function() {
      var params = { foo: "bar" };
      var returnedUrl = esUrlSvc.buildUrl(url, params);

      expect(returnedUrl).toBe(url + '?foo=bar');

      var params = { foo: "bar", bar: "foo" };
      var returnedUrl = esUrlSvc.buildUrl(url, params);

      expect(returnedUrl).toBe(url + '?foo=bar&bar=foo');
    });
  });
});
<|endoftext|>


<commit_before>var fs = require('fs'),
	path = require('path'),
	TU = require('../lib/testUtils'),
	CONST = require('../../Alloy/common/constants');

var TIMEOUT_DEFAULT = 1000;

var alloyRoot = path.join(__dirname,'..','..');
var	harnessRoot = path.join(alloyRoot,'test','projects','Harness');
var harness = {
	root: harnessRoot,
	hook: path.join(harnessRoot,'plugins',CONST.PLUGIN_NAME,'hooks','alloy.js'),
	plugin: path.join(harnessRoot,'plugins',CONST.PLUGIN_NAME,'plugin.py')
};
var alloy = {
	root: alloyRoot,
	hook: path.join(alloyRoot,'hooks','alloy.js'),
	plugin: path.join(alloyRoot,'Alloy','plugin','plugin.py')
};

// The alloy command test suite
describe('alloy install', function() {
	describe('plugin', function() {
		it('is supported', function() {
			TU.asyncExecTest('alloy install plugin "' + harness.root + '"', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeFalsy();
				}
			});
		});

		it('generates plugins and hooks', function() {
			expect(path.existsSync(harness.hook)).toBeTruthy();
			expect(path.existsSync(harness.plugin)).toBeTruthy();
		});

		it('plugins and hooks match those in Alloy', function() {
			expect(fs.readFileSync(alloy.hook,'utf8')).toBe(fs.readFileSync(harness.hook,'utf8'));
			expect(fs.readFileSync(alloy.plugin,'utf8')).toBe(fs.readFileSync(harness.plugin,'utf8'));
		});

		it('fails when given a non-existent project path', function() {
			TU.asyncExecTest('alloy install plugin /some/path/that/does/not/exist', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeTruthy();
				}
			});
		});

		it('fails when an invalid type is used', function() {
			TU.asyncExecTest('alloy install invalidType "' + harness.root + '"', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeTruthy();
				}
			});
		});
	});
});<commit_msg>adjust timeout on install test<commit_after>var fs = require('fs'),
	path = require('path'),
	TU = require('../lib/testUtils'),
	CONST = require('../../Alloy/common/constants');

var TIMEOUT_DEFAULT = 5000;

var alloyRoot = path.join(__dirname,'..','..');
var	harnessRoot = path.join(alloyRoot,'test','projects','Harness');
var harness = {
	root: harnessRoot,
	hook: path.join(harnessRoot,'plugins',CONST.PLUGIN_NAME,'hooks','alloy.js'),
	plugin: path.join(harnessRoot,'plugins',CONST.PLUGIN_NAME,'plugin.py')
};
var alloy = {
	root: alloyRoot,
	hook: path.join(alloyRoot,'hooks','alloy.js'),
	plugin: path.join(alloyRoot,'Alloy','plugin','plugin.py')
};

// The alloy command test suite
describe('alloy install', function() {
	describe('plugin', function() {
		it('is supported', function() {
			TU.asyncExecTest('alloy install plugin "' + harness.root + '"', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeFalsy();
				}
			});
		});

		it('generates plugins and hooks', function() {
			expect(path.existsSync(harness.hook)).toBeTruthy();
			expect(path.existsSync(harness.plugin)).toBeTruthy();
		});

		it('plugins and hooks match those in Alloy', function() {
			expect(fs.readFileSync(alloy.hook,'utf8')).toBe(fs.readFileSync(harness.hook,'utf8'));
			expect(fs.readFileSync(alloy.plugin,'utf8')).toBe(fs.readFileSync(harness.plugin,'utf8'));
		});

		it('fails when given a non-existent project path', function() {
			TU.asyncExecTest('alloy install plugin /some/path/that/does/not/exist', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeTruthy();
				}
			});
		});

		it('fails when an invalid type is used', function() {
			TU.asyncExecTest('alloy install invalidType "' + harness.root + '"', {
				timeout: TIMEOUT_DEFAULT,
				test: function() {
					expect(this.output.error).toBeTruthy();
				}
			});
		});
	});
});<|endoftext|>