<commit_before>'use strict';
var fs = require("fs");
var async = require("async");
var formidable = require('formidable');

var helper = require("./helper");

var lib = {
	initBLModel: function (BLModule, modelName, cb) {
		BLModule.init(modelName, cb);
	},

	checkReturnError: function (req, mainCb, data, cb) {
		if (data.error) {
			if (typeof (data.error) === 'object') {
				req.soajs.log.error(data.error);
			}
			return mainCb({"code": data.code, "msg": data.config.errors[data.code]});
		} else {
			if (cb) {
				return cb();
			}
		}
	}
};

var BL = {

	"import": (config, req, res, cbMain) => {
		let context = {
			errors : []
		};

		/**
		 * 1- unzip the file
		 * 2- parse the file
		 * 3- check mandatory
		 * 4- check duplicates
		 * 5- save file
		 */
		async.series({
			"parse": (mCb) => {
				let form = new formidable.IncomingForm();
				form.encoding = 'utf-8';
				form.keepExtensions = true;

				//unzip and parse the json file, assign it to the context.template if ok
				helper.parse(req, form, context, mCb);
			},
			"checkMandatory": (mCb) => {
				//check that the template has the minimum required schema before proceeding
				let validator = new req.soajs.validator.Validator();
				helper.checkMandatoryTemplateSchema(req, BL, lib, context, validator, mCb);
			},
			"checkDuplicates": (mCb) => {
				helper.checkDuplicate(req, BL, context, lib, () =>{
					if(context.errors && context.errors.length > 0){

						let now = new Date();
						let expires = new Date(now);
						expires.setSeconds(expires.getSeconds() + (7 * 24 * 3600));

						context.template.expires = expires;
						context.template.type = '_import';
						BL.model.insertEntry(req.soajs, {
							collection: 'templates',
							record: context.template
						}, (error, record) =>{
							lib.checkReturnError(req, mCb, {config: config, error: error, code: 600 }, ()=> {
								//inject template id as part of the error response
								context.template.id = record[0]._id;
								context.errors.unshift({"id": context.template.id.toString()});
								return mCb(context.errors);
							});
						});
					}
					return mCb(null, true);
				});
			},
			"save": (mCb) => {
				/*
					1 - save ci
					2 - save cd
					3 - save endpoints
					3.1 - publish endpoints
					4 - create deployment template
					4.1 - save deployment template
				 */

				//save information
				async.series({
					"saveContent": (vCb) =>{
						helper.saveContent(req, BL, context, lib, vCb);
					},
					"generateDeploymentTemplate": (vCb) =>{
						helper.generateDeploymentTemplate(req, config, BL, context, lib, vCb);
					}
				}, (error) =>{
					return mCb(error);
				});
			}
		}, (error) => {
			helper.cleanUp(context.fileName, () => {
				return cbMain(error);
			});
		});
	},

	"correct": (config, req, res, cbMain) => {
		let context = {
			errors : []
		};

		/**
		 * 1- unzip the file
		 * 2- parse the file
		 * 3- check mandatory
		 * 4- check duplicates
		 * 5- save file
		 */
		async.series({
			"validateInputs": (mCb) => {
				if(!req.soajs.inputmaskData || !req.soajs.inputmaskData.id || !req.soajs.inputmaskData.correction || typeof(req.soajs.inputmaskData.correction) !== 'object'){
					return mCb({"code": 173, "msg": "Invalid Inputs provided to correct the previously imported template!"});
				}
				return mCb();
			},
			"merge": (mCb) => {
				//unzip and parse the json file, assign it to the context.template if ok
				helper.mergeToTemplate(req, config, BL, context, lib, mCb);
			},
			"checkMandatory": (mCb) => {
				//check that the template has the minimum required schema before proceeding
				let validator = new req.soajs.validator.Validator();
				helper.checkMandatoryTemplateSchema(req, BL, lib, context, validator, mCb);
			},
			"checkDuplicates": (mCb) => {
				helper.checkDuplicate(req, BL, context, lib, () =>{
					if(context.errors && context.errors.length > 0){
						BL.model.saveEntry(req.soajs, {
							collection: 'templates',
							record: context.template
						}, (error) =>{
							lib.checkReturnError(req, mCb, {config: config, error: error, code: 600 }, ()=> {
								//inject template id as part of the error response
								context.errors.unshift({"id": context.template._id.toString()});
								return mCb(context.errors);
							});
						});
					}
					return mCb(null, true);
				});
			},
			"save": (mCb) => {
				/*
					1 - save ci
					2 - save cd
					3 - save endpoints
					3.1 - publish endpoints
					4 - create deployment template
					4.1 - save deployment template
				 */

				//save information
				async.series({
					"saveContent": (vCb) =>{
						helper.saveContent(req, BL, context, lib, vCb);
					},
					"generateDeploymentTemplate": (vCb) =>{
						helper.generateDeploymentTemplate(req, config, BL, context, lib, vCb);
					}
				}, (error) =>{
					return mCb(error);
				});
			}
		}, (error) => {
			return cbMain(error);
		});
	},

	"export": (config, req, res, cbMain) => {}

};

module.exports = {
	"init": function (modelName, cb) {
		var modelPath;

		if (!modelName) {
			return cb(new Error("No Model Requested!"));
		}

		modelPath = __dirname + "/../../models/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				BL.model = require(filePath);
				return cb(null, BL);
			});
		}
	}
};
<commit_msg>added config to context<commit_after>'use strict';
var fs = require("fs");
var async = require("async");
var formidable = require('formidable');

var helper = require("./helper");

var lib = {
	initBLModel: function (BLModule, modelName, cb) {
		BLModule.init(modelName, cb);
	},

	checkReturnError: function (req, mainCb, data, cb) {
		if (data.error) {
			if (typeof (data.error) === 'object') {
				req.soajs.log.error(data.error);
			}
			return mainCb({"code": data.code, "msg": data.config.errors[data.code]});
		} else {
			if (cb) {
				return cb();
			}
		}
	}
};

var BL = {

	"import": (config, req, res, cbMain) => {
		let context = {
			errors : [],
			config: config
		};

		/**
		 * 1- unzip the file
		 * 2- parse the file
		 * 3- check mandatory
		 * 4- check duplicates
		 * 5- save file
		 */
		async.series({
			"parse": (mCb) => {
				let form = new formidable.IncomingForm();
				form.encoding = 'utf-8';
				form.keepExtensions = true;

				//unzip and parse the json file, assign it to the context.template if ok
				helper.parse(req, form, context, mCb);
			},
			"checkMandatory": (mCb) => {
				//check that the template has the minimum required schema before proceeding
				let validator = new req.soajs.validator.Validator();
				helper.checkMandatoryTemplateSchema(req, BL, lib, context, validator, mCb);
			},
			"checkDuplicates": (mCb) => {
				helper.checkDuplicate(req, BL, context, lib, () =>{
					if(context.errors && context.errors.length > 0){

						let now = new Date();
						let expires = new Date(now);
						expires.setSeconds(expires.getSeconds() + (7 * 24 * 3600));

						context.template.expires = expires;
						context.template.type = '_import';
						BL.model.insertEntry(req.soajs, {
							collection: 'templates',
							record: context.template
						}, (error, record) =>{
							lib.checkReturnError(req, mCb, {config: config, error: error, code: 600 }, ()=> {
								//inject template id as part of the error response
								context.template.id = record[0]._id;
								context.errors.unshift({"id": context.template.id.toString()});
								return mCb(context.errors);
							});
						});
					}
					return mCb(null, true);
				});
			},
			"save": (mCb) => {
				/*
					1 - save ci
					2 - save cd
					3 - save endpoints
					3.1 - publish endpoints
					4 - create deployment template
					4.1 - save deployment template
				 */

				//save information
				async.series({
					"saveContent": (vCb) =>{
						helper.saveContent(req, BL, context, lib, vCb);
					},
					"generateDeploymentTemplate": (vCb) =>{
						helper.generateDeploymentTemplate(req, config, BL, context, lib, vCb);
					}
				}, (error) =>{
					return mCb(error);
				});
			}
		}, (error) => {
			helper.cleanUp(context.fileName, () => {
				return cbMain(error);
			});
		});
	},

	"correct": (config, req, res, cbMain) => {
		let context = {
			errors : [],
			config: config
		};

		/**
		 * 1- unzip the file
		 * 2- parse the file
		 * 3- check mandatory
		 * 4- check duplicates
		 * 5- save file
		 */
		async.series({
			"validateInputs": (mCb) => {
				if(!req.soajs.inputmaskData || !req.soajs.inputmaskData.id || !req.soajs.inputmaskData.correction || typeof(req.soajs.inputmaskData.correction) !== 'object'){
					return mCb({"code": 173, "msg": "Invalid Inputs provided to correct the previously imported template!"});
				}
				return mCb();
			},
			"merge": (mCb) => {
				//unzip and parse the json file, assign it to the context.template if ok
				helper.mergeToTemplate(req, config, BL, context, lib, mCb);
			},
			"checkMandatory": (mCb) => {
				//check that the template has the minimum required schema before proceeding
				let validator = new req.soajs.validator.Validator();
				helper.checkMandatoryTemplateSchema(req, BL, lib, context, validator, mCb);
			},
			"checkDuplicates": (mCb) => {
				helper.checkDuplicate(req, BL, context, lib, () =>{
					if(context.errors && context.errors.length > 0){
						BL.model.saveEntry(req.soajs, {
							collection: 'templates',
							record: context.template
						}, (error) =>{
							lib.checkReturnError(req, mCb, {config: config, error: error, code: 600 }, ()=> {
								//inject template id as part of the error response
								context.errors.unshift({"id": context.template._id.toString()});
								return mCb(context.errors);
							});
						});
					}
					return mCb(null, true);
				});
			},
			"save": (mCb) => {
				/*
					1 - save ci
					2 - save cd
					3 - save endpoints
					3.1 - publish endpoints
					4 - create deployment template
					4.1 - save deployment template
				 */

				//save information
				async.series({
					"saveContent": (vCb) =>{
						helper.saveContent(req, BL, context, lib, vCb);
					},
					"generateDeploymentTemplate": (vCb) =>{
						helper.generateDeploymentTemplate(req, config, BL, context, lib, vCb);
					}
				}, (error) =>{
					return mCb(error);
				});
			}
		}, (error) => {
			return cbMain(error);
		});
	},

	"export": (config, req, res, cbMain) => {}

};

module.exports = {
	"init": function (modelName, cb) {
		var modelPath;

		if (!modelName) {
			return cb(new Error("No Model Requested!"));
		}

		modelPath = __dirname + "/../../models/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				BL.model = require(filePath);
				return cb(null, BL);
			});
		}
	}
};
<|endoftext|>


<commit_before>var fluid = require("infusion"),
    gpii = fluid.registerNamespace("gpii"),
    SerialPort = require("serialport");

fluid.defaults("gpii.nexus.atlasScientificConnection", {
    gradeNames: "fluid.component",

    devicePath: null, // To be provided by user

    serialPortOptions: {
        autoOpen: false,
        baudRate: 9600,
        dataBits: 8,
        stopBits: 1
    },

    members: {
        serialPortParser: "@expand:gpii.nexus.atlasScientificConnection.constructReadlineCrParser()",
        serialPort: {
            expander: {
                funcName: "gpii.nexus.atlasScientificConnection.constructSerialPort",
                args: [
                    "{that}.options.devicePath",
                    "{that}.options.serialPortOptions",
                    "{that}.serialPortParser",
                    "{that}.events.onData"
                ]
            }
        }
    },

    invokers: {
        start: {
            "this": "{that}.serialPort",
            method: "open",
            args: ["{that}.openCallback"]
        },
        openCallback: {
            funcName: "gpii.nexus.atlasScientificConnection.openCallback",
            args: [
                "{arguments}.0", // Error
                "{that}.events.onStarted"
            ]
        },
        sendDeviceInformationRequest: {
            "this": "{that}.serialPort",
            method: "write",
            // TODO: Provide callback for error notification
            args: ["I\r"]
        }
    },

    events: {
        onStarted: null,
        onData: null, // Response data string
        onReading: null, // Array of numbers
        onDeviceInformation: null // Device Information data
    },

    listeners: {
        "onData.parseResponse": {
            listener: "gpii.nexus.atlasScientificConnection.parseResponse",
            args: [
                "{that}",
                "{arguments}.0" // Response
            ]
        }
    }
})

gpii.nexus.atlasScientificConnection.constructReadlineCrParser = function () {
    return SerialPort.parsers.readline("\r");
};

gpii.nexus.atlasScientificConnection.constructSerialPort = function (devicePath, serialPortOptions, serialPortParser, onDataEvent) {

    var options = fluid.extend({}, serialPortOptions);
    options.parser = serialPortParser;

    var port = new SerialPort(devicePath, options);

    port.on("data", function(data) {
        onDataEvent.fire(data);
    });

    return port;
};

gpii.nexus.atlasScientificConnection.openCallback = function (error, event) {
    // TODO: Handle error from SerialPort open
    if (!error) {
        event.fire();
    }
};

gpii.nexus.atlasScientificConnection.parseResponse = function (that, response) {
    if (/^\d/.test(response)) {
        // Response starts with a digit, parse as a reading
        that.events.onReading.fire(fluid.transform(response.split(","), parseFloat));
    } else if (response.startsWith("?I")) {
        // Device information
        var deviceInfoData = response.split(",");
        if (deviceInfoData.length === 3) {
            that.events.onDeviceInformation.fire({
                deviceType: deviceInfoData[1],
                firmwareVersion: deviceInfoData[2]
            });
        }
    }
};

fluid.defaults("gpii.nexus.atlasScientificDriver", {
    gradeNames: "fluid.modelComponent",

    devicePath: null, // To be provided by user
    nexusHost: "localhost",
    nexusPort: 9081,

    circuitTypes: {
        "EC": {
            nexusPeerComponentPath: "conductivitySensor",
            nexusPeerComponentOptions: {
                type: "gpii.nexus.atlasScientificDriver.conductivitySensor"
            }
        },
        "pH": {
            nexusPeerComponentPath: "phSensor",
            nexusPeerComponentOptions: {
                type: "gpii.nexus.atlasScientificDriver.phSensor"
            }
        }
    },

    components: {
        atlasScientificConnection: {
            type: "gpii.nexus.atlasScientificConnection",
            options: {
                devicePath: "{atlasScientificDriver}.options.devicePath",
                listeners: {
                    "onStarted.sendDeviceInformationRequest": {
                        listener: "{that}.sendDeviceInformationRequest"
                    },
                    "onReading.log": function (data) {
                        console.log("Reading: " + JSON.stringify(data));
                    },
                    "onDeviceInformation.log": function (data) {
                        console.log("Device information: " + JSON.stringify(data));
                    }
                }
            }
        },

        nexusBinding: {
            type: "gpii.nexusWebSocketBoundComponent",
            createOnEvent: "{atlasScientificConnection}.events.onDeviceInformation",
            options: {
                circuitType: "{arguments}.0.deviceType",
                members: {
                    nexusHost: "{atlasScientificDriver}.options.nexusHost",
                    nexusPort: "{atlasScientificDriver}.options.nexusPort",
                    nexusPeerComponentPath: {
                        expander: {
                            func: "gpii.nexus.atlasScientificDriver.lookupNexusPeerPath",
                            args: [
                                "{atlasScientificDriver}.options.circuitTypes",
                                "{that}.options.circuitType"
                            ]
                        }
                    },
                    nexusPeerComponentOptions: {
                        expander: {
                            func: "gpii.nexus.atlasScientificDriver.lookupNexusPeerOptions",
                            args: [
                                "{atlasScientificDriver}.options.circuitTypes",
                                "{that}.options.circuitType"
                            ]
                        }
                    },
                    nexusBoundModelPath: "sensorValue",
                    sendsChangesToNexus: true,
                    managesPeer: true
                },
                model: {
                    sensorValue: 0
                },
                events: {
                    onPeerDeleted: "{atlasScientificDriver}.events.onNexusPeerComponentDestroyed"
                },
                listeners: {
                    "{atlasScientificConnection}.events.onReading": {
                        listener: "gpii.nexus.atlasScientificDriver.updateModelSensorValue",
                        args: [
                            "{that}",
                            "{arguments}.0" // Sensor reading
                        ]
                    },
                    "{atlasScientificDriver}.events.doNexusPeerDestroy": {
                        listener: "{that}.deleteNexusPeerComponent"
                    }
                }
            }
        }
    },

    invokers: {
        start: "{atlasScientificConnection}.start",
        destroyNexusPeerComponent: "{that}.events.doNexusPeerDestroy.fire"
    },

    events: {
        doNexusPeerDestroy: null,
        onNexusPeerComponentDestroyed: null
    }

});

gpii.nexus.atlasScientificDriver.lookupNexusPeerPath = function (circuitTypes, deviceType) {
    return circuitTypes[deviceType].nexusPeerComponentPath;
};

gpii.nexus.atlasScientificDriver.lookupNexusPeerOptions = function (circuitTypes, deviceType) {
    return circuitTypes[deviceType].nexusPeerComponentOptions;
};

gpii.nexus.atlasScientificDriver.updateModelSensorValue = function (nexusBinding, sensorReading) {
    // Use the first value from the sensor reading
    //
    // For more information, see the Atlas Scientific circuit
    // documentation:
    //
    // - https://www.atlas-scientific.com/_files/_datasheets/_circuit/pH_EZO_datasheet.pdf
    // - https://www.atlas-scientific.com/_files/_datasheets/_circuit/EC_EZO_Datasheet.pdf
    //
    nexusBinding.applier.change("sensorValue", sensorReading[0]);
};
<commit_msg>GPII-2186: Destroy peer on serial close<commit_after>var fluid = require("infusion"),
    gpii = fluid.registerNamespace("gpii"),
    SerialPort = require("serialport");

fluid.defaults("gpii.nexus.atlasScientificConnection", {
    gradeNames: "fluid.component",

    devicePath: null, // To be provided by user

    serialPortOptions: {
        autoOpen: false,
        baudRate: 9600,
        dataBits: 8,
        stopBits: 1
    },

    members: {
        serialPortParser: "@expand:gpii.nexus.atlasScientificConnection.constructReadlineCrParser()",
        serialPort: {
            expander: {
                funcName: "gpii.nexus.atlasScientificConnection.constructSerialPort",
                args: [
                    "{that}.options.devicePath",
                    "{that}.options.serialPortOptions",
                    "{that}.serialPortParser",
                    "{that}.events.onData",
                    "{that}.events.onClose"
                ]
            }
        }
    },

    invokers: {
        start: {
            "this": "{that}.serialPort",
            method: "open",
            args: ["{that}.openCallback"]
        },
        openCallback: {
            funcName: "gpii.nexus.atlasScientificConnection.openCallback",
            args: [
                "{arguments}.0", // Error
                "{that}.events.onStarted"
            ]
        },
        sendDeviceInformationRequest: {
            "this": "{that}.serialPort",
            method: "write",
            // TODO: Provide callback for error notification
            args: ["I\r"]
        }
    },

    events: {
        onStarted: null,
        onData: null, // Response data string
        onReading: null, // Array of numbers
        onDeviceInformation: null, // Device Information data
        onClose: null
    },

    listeners: {
        "onData.parseResponse": {
            listener: "gpii.nexus.atlasScientificConnection.parseResponse",
            args: [
                "{that}",
                "{arguments}.0" // Response
            ]
        }
    }
})

gpii.nexus.atlasScientificConnection.constructReadlineCrParser = function () {
    return SerialPort.parsers.readline("\r");
};

gpii.nexus.atlasScientificConnection.constructSerialPort = function (devicePath, serialPortOptions, serialPortParser, onDataEvent, onCloseEvent) {

    var options = fluid.extend({}, serialPortOptions);
    options.parser = serialPortParser;

    var port = new SerialPort(devicePath, options);

    port.on("data", function(data) {
        onDataEvent.fire(data);
    });

    port.on("close", function() {
        onCloseEvent.fire();
    });

    return port;
};

gpii.nexus.atlasScientificConnection.openCallback = function (error, event) {
    // TODO: Handle error from SerialPort open
    if (!error) {
        event.fire();
    }
};

gpii.nexus.atlasScientificConnection.parseResponse = function (that, response) {
    if (/^\d/.test(response)) {
        // Response starts with a digit, parse as a reading
        that.events.onReading.fire(fluid.transform(response.split(","), parseFloat));
    } else if (response.startsWith("?I")) {
        // Device information
        var deviceInfoData = response.split(",");
        if (deviceInfoData.length === 3) {
            that.events.onDeviceInformation.fire({
                deviceType: deviceInfoData[1],
                firmwareVersion: deviceInfoData[2]
            });
        }
    }
};

fluid.defaults("gpii.nexus.atlasScientificDriver", {
    gradeNames: "fluid.modelComponent",

    devicePath: null, // To be provided by user
    nexusHost: "localhost",
    nexusPort: 9081,

    circuitTypes: {
        "EC": {
            nexusPeerComponentPath: "conductivitySensor",
            nexusPeerComponentOptions: {
                type: "gpii.nexus.atlasScientificDriver.conductivitySensor"
            }
        },
        "pH": {
            nexusPeerComponentPath: "phSensor",
            nexusPeerComponentOptions: {
                type: "gpii.nexus.atlasScientificDriver.phSensor"
            }
        }
    },

    components: {
        atlasScientificConnection: {
            type: "gpii.nexus.atlasScientificConnection",
            options: {
                devicePath: "{atlasScientificDriver}.options.devicePath",
                listeners: {
                    "onStarted.sendDeviceInformationRequest": {
                        listener: "{that}.sendDeviceInformationRequest"
                    },
                    "onReading.log": function (data) {
                        console.log("Reading: " + JSON.stringify(data));
                    },
                    "onDeviceInformation.log": function (data) {
                        console.log("Device information: " + JSON.stringify(data));
                    },
                    "onClose.log": function () {
                        console.log("Close");
                    },
                    "onClose.destroyPeer": "{atlasScientificDriver}.events.doDestroyNexusPeer"
                }
            }
        },

        nexusBinding: {
            type: "gpii.nexusWebSocketBoundComponent",
            createOnEvent: "{atlasScientificConnection}.events.onDeviceInformation",
            options: {
                circuitType: "{arguments}.0.deviceType",
                members: {
                    nexusHost: "{atlasScientificDriver}.options.nexusHost",
                    nexusPort: "{atlasScientificDriver}.options.nexusPort",
                    nexusPeerComponentPath: {
                        expander: {
                            func: "gpii.nexus.atlasScientificDriver.lookupNexusPeerPath",
                            args: [
                                "{atlasScientificDriver}.options.circuitTypes",
                                "{that}.options.circuitType"
                            ]
                        }
                    },
                    nexusPeerComponentOptions: {
                        expander: {
                            func: "gpii.nexus.atlasScientificDriver.lookupNexusPeerOptions",
                            args: [
                                "{atlasScientificDriver}.options.circuitTypes",
                                "{that}.options.circuitType"
                            ]
                        }
                    },
                    nexusBoundModelPath: "sensorValue",
                    sendsChangesToNexus: true,
                    managesPeer: true
                },
                model: {
                    sensorValue: 0
                },
                events: {
                    onPeerDeleted: "{atlasScientificDriver}.events.onNexusPeerComponentDestroyed"
                },
                listeners: {
                    "{atlasScientificConnection}.events.onReading": {
                        listener: "gpii.nexus.atlasScientificDriver.updateModelSensorValue",
                        args: [
                            "{that}",
                            "{arguments}.0" // Sensor reading
                        ]
                    },
                    "{atlasScientificDriver}.events.doDestroyNexusPeer": {
                        listener: "{that}.deleteNexusPeerComponent"
                    }
                }
            }
        }
    },

    invokers: {
        start: "{atlasScientificConnection}.start",
        destroyNexusPeerComponent: "{that}.events.doDestroyNexusPeer.fire"
    },

    events: {
        doDestroyNexusPeer: null,
        onNexusPeerComponentDestroyed: null
    }

});

gpii.nexus.atlasScientificDriver.lookupNexusPeerPath = function (circuitTypes, deviceType) {
    return circuitTypes[deviceType].nexusPeerComponentPath;
};

gpii.nexus.atlasScientificDriver.lookupNexusPeerOptions = function (circuitTypes, deviceType) {
    return circuitTypes[deviceType].nexusPeerComponentOptions;
};

gpii.nexus.atlasScientificDriver.updateModelSensorValue = function (nexusBinding, sensorReading) {
    // Use the first value from the sensor reading
    //
    // For more information, see the Atlas Scientific circuit
    // documentation:
    //
    // - https://www.atlas-scientific.com/_files/_datasheets/_circuit/pH_EZO_datasheet.pdf
    // - https://www.atlas-scientific.com/_files/_datasheets/_circuit/EC_EZO_Datasheet.pdf
    //
    nexusBinding.applier.change("sensorValue", sensorReading[0]);
};
<|endoftext|>


<commit_before>describe('Playlist', function() {

	beforeEach(function() {
		cy.completeInitialSetup()
		cy.login()
	})

	it('can queue individual track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.contains('ブルーベリーパイ (Blueberry Pie)').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').contains('ブルーベリーパイ (Blueberry Pie)')
	})

	it('can use queue all button for album', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.get('[data-cy=browser-header]').should('have.text', 'Picnic')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 7)
	})

	it('can use queue all button for multiple albums', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 13)
	})

	it('can drag and drop an individual track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.contains('ブルーベリーパイ (Blueberry Pie)').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').contains('ブルーベリーパイ (Blueberry Pie)')
	})

	it('can drag and drop an album', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 7)
	})

	it('can drag and drop multiple albums', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 13)
	})

	it('can remove a track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').first().find('[data-cy=remove]').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 12)
	})

	it('can clear the playlist', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=clear-playlist]').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 0)
	})

	it('can jump to a track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').last().click()
		cy.get('[data-cy=playlist]').find('[data-cy=now-playing]').parents('[data-cy=track]').contains('なぜ (Why?)')
	})

	it('shows total duration', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.get('[data-cy=browser-header]').should('have.text', 'Picnic')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist-duration]').should('have.text', '21s')
	})

});<commit_msg>Tentative fix for flaky test<commit_after>describe('Playlist', function() {

	beforeEach(function() {
		cy.completeInitialSetup()
		cy.login()
	})

	it('can queue individual track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.contains('ブルーベリーパイ (Blueberry Pie)').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').contains('ブルーベリーパイ (Blueberry Pie)')
	})

	it('can use queue all button for album', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.get('[data-cy=browser-header]').should('have.text', 'Picnic')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 7)
	})

	it('can use queue all button for multiple albums', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 13)
	})

	it('can drag and drop an individual track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.contains('ブルーベリーパイ (Blueberry Pie)').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').contains('ブルーベリーパイ (Blueberry Pie)')
	})

	it('can drag and drop an album', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 7)
	})

	it('can drag and drop multiple albums', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').drag('[data-cy=playlist]')
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 13)
	})

	it('can remove a track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').first().find('[data-cy=remove]').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 12)
	})

	it('can clear the playlist', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 13)
		cy.get('[data-cy=clear-playlist]').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').should('have.length', 0)
	})

	it('can jump to a track', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist]').find('[data-cy=track]').last().click()
		cy.get('[data-cy=playlist]').find('[data-cy=now-playing]').parents('[data-cy=track]').contains('なぜ (Why?)')
	})

	it('shows total duration', () => {
		cy.visit('/')
		cy.contains('Music Collection')
		cy.contains('Test').click()
		cy.contains('Tobokegao').click()
		cy.contains('Picnic').click()
		cy.get('[data-cy=browser-header]').should('have.text', 'Picnic')
		cy.contains('Queue All').click()
		cy.get('[data-cy=playlist-duration]').should('have.text', '21s')
	})

});<|endoftext|>


<commit_before>'use strict';

/*jshint expr:true */

/**
 * Module dependencies.
 */
var should = require('should'),
	mongoose = require('mongoose'),
	assert = require('assert'),
	events = mongoose.model('Event');

/**
* Global Variabls used for testing
*/
var event1, event2, event1d;

/**
*  Test functionality of Events
*/

describe('Event Model Unit Tests',function() {

	describe('Method Save',function(){
		beforeEach(function(done){
			event1 = new events({
				name:  'testing1231',
				start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
				end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
				location: 'UF',
				schedule: 'www.google.com'
			});

			event1d = new events({
				name:  'testing1231',
				start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
				end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
				location: 'UF',
				schedule: 'www.google.com'
			});

			event2 = new events({
					name:  'testing1232',
					start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
					end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
					location: 'UF',
				schedule: 'www.google.com'
			});

			done();
		});

		it('should be able to save with out problems',function(done){
			event1.save(done);
		});

		it('should fail to save an existing event again',function(done){
			event1.save(function(err){
				return event2.save(function(err){
					should.not.exist(err);
				});
			});
			event1d.save(function(err){
				return event2.save(function(err){
					should.exist(err);
					done();
				});
			});

		});

		it('should allow an event to share everything but the name',function(done){
			event1.save(function(err1,obj,num){
				event2.name = 'Off campus';
				event2.save(done);
			});
		});

		it('should allow getting the event name',function(done){
			event1.save(function() {
				var query = events.findOne({'name': event1.name});
				query.exec(function (err, result) {
					(result.name === undefined).should.be.false;
					(result.name).should.be.equal(event1.name);
					done();
				});
			});
		});

		it('should allow getting the event start date',function(done){
			event1.save(function() {
				var query = events.findOne({'start_date': event1.start_date});
				query.exec(function (err, result) {
					(result.start_date === undefined).should.be.false;
					(result.start_date).should.be.equal(event1.start_date);
					done();
				});
			});
		});

		it('should allow getting the event end date',function(done){
			event1.save(function() {
				var query = events.findOne({'end_date': event1.end_date});
				query.exec(function (err, result) {
					(result.end_date === undefined).should.be.false;
					(result.end_date).should.be.equal(event1.end_date);
					done();
				});
			});
		});

		it('should allow getting the event location',function(done){
			event1.save(function() {
				var query = events.findOne({'location': event1.location});
				query.exec(function (err, result) {
					(result.location === undefined).should.be.false;
					(result.location).should.be.equal(event1.location);
					done();
				});
			});
		});

		it('should allow getting the event schedule',function(done){
			event1.save(function() {
				var query = events.findOne({'schedule': event1.schedule});
				query.exec(function (err, result) {
					(result.schedule === undefined).should.be.false;
					(result.schedule).should.be.equal(event1.schedule);
					done();
				});
			});
		});

		it('should be able to show an error when trying to save without a name', function(done) {
			event1.name = '';
			return event1.save(function(err, obj, num) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a start date', function(done) {
			event1.start_date= '';
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a end date', function(done) {
			event1.end_date= '';
			return event1.save(function(err, obj) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a location', function(done) {
			event1.location= '';
			return event1.save(function(err, obj) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with a past start date', function(done) {
			event1.start_date= new Date(2013,10,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with a past end date', function(done) {
			event1.end_date= new Date(2013,10,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a valid end date', function(done) {
			event1.end_date= 'this is not a date';
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with an end date before the start date', function(done) {
			event1.end_date=  new Date(2014,11,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		afterEach(function(done){
			event1.remove();
			event2.remove();
			event1d.remove();
			done();
		});
	});
});
<commit_msg>Fixed event test to confirm uniqueness is working.<commit_after>'use strict';

/*jshint expr:true */

/**
 * Module dependencies.
 */
var should = require('should'),
	mongoose = require('mongoose'),
	assert = require('assert'),
	events = mongoose.model('Event');

/**
* Global Variabls used for testing
*/
var event1, event2, event1d;

/**
*  Test functionality of Events
*/

describe('Event Model Unit Tests',function() {

	describe('Method Save',function(){
		beforeEach(function(done){
			event1 = new events({
				name:  'testing1231',
				start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
				end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
				location: 'UF',
				schedule: 'www.google.com'
			});

			event1d = new events({
				name:  'testing1231',
				start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
				end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
				location: 'UF',
				schedule: 'www.google.com'
			});

			event2 = new events({
					name:  'testing1232',
					start_date: new Date(2014,11,30,10,0,0).getTime(), //year, month, day, hour, minute, millisec
					end_date:  new Date(2015,11,30,10,0,0).getTime(),  //month is zero based.  11 = dec
					location: 'UF',
				schedule: 'www.google.com'
			});

			done();
		});

		it('should be able to save with out problems',function(done){
			event1.save(done);
		});

		it('should fail to save an existing event again',function(done){
			event1.save(function(err){
				should.not.exist(err);
				event1d.save(function(err){
					should.exist(err);
					done();
				});
			});

		});

		it('should allow an event to share everything but the name',function(done){
			event1.save(function(err1,obj,num){
				event2.name = 'Off campus';
				event2.save(done);
			});
		});

		it('should allow getting the event name',function(done){
			event1.save(function() {
				var query = events.findOne({'name': event1.name});
				query.exec(function (err, result) {
					(result.name === undefined).should.be.false;
					(result.name).should.be.equal(event1.name);
					done();
				});
			});
		});

		it('should allow getting the event start date',function(done){
			event1.save(function() {
				var query = events.findOne({'start_date': event1.start_date});
				query.exec(function (err, result) {
					(result.start_date === undefined).should.be.false;
					(result.start_date).should.be.equal(event1.start_date);
					done();
				});
			});
		});

		it('should allow getting the event end date',function(done){
			event1.save(function() {
				var query = events.findOne({'end_date': event1.end_date});
				query.exec(function (err, result) {
					(result.end_date === undefined).should.be.false;
					(result.end_date).should.be.equal(event1.end_date);
					done();
				});
			});
		});

		it('should allow getting the event location',function(done){
			event1.save(function() {
				var query = events.findOne({'location': event1.location});
				query.exec(function (err, result) {
					(result.location === undefined).should.be.false;
					(result.location).should.be.equal(event1.location);
					done();
				});
			});
		});

		it('should allow getting the event schedule',function(done){
			event1.save(function() {
				var query = events.findOne({'schedule': event1.schedule});
				query.exec(function (err, result) {
					(result.schedule === undefined).should.be.false;
					(result.schedule).should.be.equal(event1.schedule);
					done();
				});
			});
		});

		it('should be able to show an error when trying to save without a name', function(done) {
			event1.name = '';
			return event1.save(function(err, obj, num) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a start date', function(done) {
			event1.start_date= '';
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a end date', function(done) {
			event1.end_date= '';
			return event1.save(function(err, obj) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a location', function(done) {
			event1.location= '';
			return event1.save(function(err, obj) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with a past start date', function(done) {
			event1.start_date= new Date(2013,10,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with a past end date', function(done) {
			event1.end_date= new Date(2013,10,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save without a valid end date', function(done) {
			event1.end_date= 'this is not a date';
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		it('should be able to show an error when trying to save with an end date before the start date', function(done) {
			event1.end_date=  new Date(2014,11,20,10,0,0).getTime();
			return event1.save(function(err) {
				should.exist(err);
				done();
			});
		});

		afterEach(function(done){
			event1.remove();
			event2.remove();
			event1d.remove();
			done();
		});
	});
});
<|endoftext|>


<commit_before>var domain = require('domain');
var Driver = require('./Drive').Driver;
var AutoLoader = require ('./AutoLoader');
var parse_action = require('./ActionParser').parse;
var Connection = require('./Connection');
var url = require('url');
var Router = require('./Router');

function turnpike_server() {
  return function(req, res) {
    var d = domain.create();

    d.on('error', function(er){
      console.error('Error encountered in connection handler', er.stack);
      try {
        var countdown = setTimeout(function(){
          process.exit(1);
        }, 30000);
        countdown.unref();
        Driver.server.close();
        cluser.worker.disconnect();
        res.writeHead(500, "Unknown fatal server error", {'Content-Type': 'text/plain'});
        res.end("500\nInternal server error.");
      }
      catch (er2) {
        console.error('Failure sending 500 status during error handler'. er2.stack);
      }
    });

    d.add(req);
    d.add(res);
    d.run(function(){
      var connection = new Connection(req, res);
      connection.end(function(){
        console.log("Serving: " + req.url);
        Router.resolve(
          function(err, route) {
            route_handler(connection, err, route)
          },
          url.parse(req.url).pathname);
      });
    });
  };
}

function route_handler(connection, err, route) {
  var controller,
      action;

  connection.route = route;
  controller = parse_action(route.controller);
  if (typeof(controller) !== 'string') {
    action = controller.method;

    if (controller.instance) {
      controller = AutoLoader.invoke(controller.classname);
      controller = new controller(connection);
      action = (controller[action]).bind(controller);
    }
    else {
      controller = AutoLoader.invoke(controller.classname);
      action = function(cb) {
        controller[action](connection, cb);
      }
    }
  }
  else {
    controller = AutoLoader.invoke(connection.route.controller);
    controller = new controller(connection);
    action = controller.prepare;
  }

  if (typeof(action) == "function" ) {
    connection.controller = controller;
    action(controller.deliver);
  }
  else {
    connection.die(404);
  }
}

module.exports = turnpike_server;
<commit_msg>Add new notation for actions for different http methods on a route.<commit_after>var domain = require('domain');
var Driver = require('./Drive').Driver;
var AutoLoader = require ('./AutoLoader');
var parse_action = require('./ActionParser').parse;
var Connection = require('./Connection');
var url = require('url');
var Router = require('./Router');

function turnpike_server() {
  return function(req, res) {
    var d = domain.create();

    d.on('error', function(er){
      console.error('Error encountered in connection handler', er.stack);
      try {
        var countdown = setTimeout(function(){
          process.exit(1);
        }, 30000);
        countdown.unref();
        Driver.server.close();
        cluser.worker.disconnect();
        res.writeHead(500, "Unknown fatal server error", {'Content-Type': 'text/plain'});
        res.end("500\nInternal server error.");
      }
      catch (er2) {
        console.error('Failure sending 500 status during error handler'. er2.stack);
      }
    });

    d.add(req);
    d.add(res);
    d.run(function(){
      var connection = new Connection(req, res);
      connection.end(function(){
        console.log("Serving: " + req.url);
        Router.resolve(
          function(err, route) {
            route_handler(connection, err, route)
          },
          url.parse(req.url).pathname);
      });
    });
  };
}

function route_handler(connection, err, route) {
  var controller,
      http_method = connection.method,
      action;

  if (http_method === 'DELETE') {
    http_method = "DEL";
  }

  connection.route = route;
  console.log(typeof(route.controller));

  if (typeof(route.controller) === 'object') {
    route.controller = route.controller[http_method];
  }

  controller = parse_action(route.controller);

  if (typeof(controller) !== 'string') {
    action = controller.method;

    if (controller.instance) {
      controller = AutoLoader.invoke(controller.classname);
      controller = new controller(connection);
      action = (controller[action]).bind(controller);
    }
    else {
      controller = AutoLoader.invoke(controller.classname);
      action = function(cb) {
        controller[action](connection, cb);
      }
    }
  }
  else {
    controller = AutoLoader.invoke(connection.route.controller);
    controller = new controller(connection);
    action = controller.prepare;
  }

  if (typeof(action) == "function" ) {
    connection.controller = controller;
    action(controller.deliver);
  }
  else {
    connection.die(404);
  }
}

module.exports = turnpike_server;
<|endoftext|>


<commit_before>/*
	Requires gamepadGetters.js to be loaded
*/

// Displays information on connected Gamepads in the HTML
function displayInfo() {

	var activeGPs = getActiveGPs()

	// Display a notice based on the number of connected Gamepads
	if (activeGPs.length == 0) {
		document.getElementById("top").innerHTML =
		"
No Gamepads Detected

If a Gamepad is connected," +
		" try pressing one of its buttons
";
	}
	else if (activeGPs.length == 1){
		document.getElementById("top").innerHTML =
		"
1 Gamepad Detected
";
	}
	else {
		document.getElementById("top").innerHTML =
		"
" + activeGPs.length +
		" Gamepads Detected
";
	}

	// Clear the GPs div
	document.getElementById("GPs").innerHTML = "";

	// Populate the GPs div with a gamepad div for each connected
	// gamepad, containing information about that gamepad
	activeGPs.forEach( function(g){
		var msgPart1 = "
" +
		"

Gamepad " +
		String(g.index) +
		"
ID: " +
		g.id +
		"
" +
		String(g.buttons.length) +
		" buttons
" +
		String(g.axes.length) +
		" axes
Mapping: ";

		if (g.mapping == "") {
			document.getElementById("GPs").innerHTML += msgPart1 +
			"None

";
		}
		else {
			document.getElementById("GPs").innerHTML += msgPart1 +
			String(g.mapping)+
			"

";
		}

	});
}

function displayButtons() {
	var activeGPs = getActiveGPs()

	activeGPs.forEach( function(g) {
		var divID = "bd" + String(g.index);
		// document.getElementById(divID).innerHTML = "";
		// getActiveButtons(g).forEach( function(b){
		// 	document.getElementById(divID).innerHTML += String(b);
		// });
		var bkgrnd = "url('./img/controller.png')";
		getActiveButtons(g).forEach( function(b){
			var highlight = ", url('./img/" + String(b) + ".png)";
			var bkgrnd = bkgrnd + highlight;
		});
		console.log(bkgrnd);
		document.getElementById(divID).style.background = bkgrnd;
	});

}<commit_msg>Attempt 6: Changed to using the concat string function<commit_after>/*
	Requires gamepadGetters.js to be loaded
*/

// Displays information on connected Gamepads in the HTML
function displayInfo() {

	var activeGPs = getActiveGPs()

	// Display a notice based on the number of connected Gamepads
	if (activeGPs.length == 0) {
		document.getElementById("top").innerHTML =
		"
No Gamepads Detected

If a Gamepad is connected," +
		" try pressing one of its buttons
";
	}
	else if (activeGPs.length == 1){
		document.getElementById("top").innerHTML =
		"
1 Gamepad Detected
";
	}
	else {
		document.getElementById("top").innerHTML =
		"
" + activeGPs.length +
		" Gamepads Detected
";
	}

	// Clear the GPs div
	document.getElementById("GPs").innerHTML = "";

	// Populate the GPs div with a gamepad div for each connected
	// gamepad, containing information about that gamepad
	activeGPs.forEach( function(g){
		var msgPart1 = "
" +
		"

Gamepad " +
		String(g.index) +
		"
ID: " +
		g.id +
		"
" +
		String(g.buttons.length) +
		" buttons
" +
		String(g.axes.length) +
		" axes
Mapping: ";

		if (g.mapping == "") {
			document.getElementById("GPs").innerHTML += msgPart1 +
			"None

";
		}
		else {
			document.getElementById("GPs").innerHTML += msgPart1 +
			String(g.mapping)+
			"

";
		}

	});
}

function displayButtons() {
	var activeGPs = getActiveGPs()

	activeGPs.forEach( function(g) {
		var divID = "bd" + String(g.index);
		// document.getElementById(divID).innerHTML = "";
		// getActiveButtons(g).forEach( function(b){
		// 	document.getElementById(divID).innerHTML += String(b);
		// });
		var bkgrnd = "url('./img/controller.png')";
		getActiveButtons(g).forEach( function(b){
			var highlight = ", url('./img/" + String(b) + ".png)";
			var bkgrnd = bkgrnd.concat(highlight);
		});
		console.log(bkgrnd);
		document.getElementById(divID).style.background = bkgrnd;
	});

}<|endoftext|>


<commit_before>
var searchModel = {
	craigslist : {
		domain : 'craigslist.org',
		cities : {
			new_york : {display : 'New York', url : 'newyork'},
			seattle : {display: 'Seattle', url : 'seattle'},
			san_francisco : {display: 'San Francisco', url : 'sfbay'},
			denver : {display: 'Denver', url : 'denver'},
			san_diego : {display: 'San Diego', url : 'sandiego'},
			los_angeles : {display: 'Los Angeles', url : 'losangeles'},
			buffalo : {display: 'Buffalo', url : 'buffalo'},
			austin : {display: 'Austin', url : 'austin'},
			anchorage : {display: 'Anchorage', url : 'anchorage'},
			portland : {display: 'Portland, OR', url : 'portland'}
		},
		categories : {
			jobs : {
					display : 'Jobs',
					subcategories :{
									web : {display : 'Web', url : 'web'},
									software : {display: 'Software', url : 'sof'},
									internet_engineering : {display : 'Internet Engineering', url : 'eng'},
									systems_networking : {display : 'Systems/Networking', url : 'sad'}
					}
			},
			gigs : {
					display : 'Gigs',
					subcategories: {
									computer : {display : 'Computer', url : 'cpg'},
									creative : {display : 'Creative', url : 'crg'}
					}
			}
		}
	}

};

var urlFromRequest = function(requestData){
	if(typeof requestData !== 'object' || !requestData.domain || !requestData.city || !requestData.category || !requestData.subcategory){
		return 'not valid request data';
		return false;
	}
	var model = searchModel[requestData.domain];
	if(!model){
		return 'not valid model';
		return false;
	}
	var cityUrl =  cityUrlFromCity(requestData.city, model);
	var category = model.categories[requestData.category];
	if(!cityUrl || !category){
		return 'not valid city or category';
		return false;
	}
	var subcategory = category.subcategories[requestData.subcategory];
	if(!subcategory){
		return 'not valid subcategory';
		return false;
	}
	var queryString = requestData.query ? '?query=' + encodeURIComponent(requestData.query) : '';
	return cityUrl + '/search/' + subcategory.url + queryString;
}

var cityUrlFromCity = function(city, sModel){
	var model = sModel ? sModel : searchModel['craigslist'];
	var validatedCity = model.cities[city];
	if(validatedCity){
		return 'http://' + validatedCity.url + '.' + model.domain;
	}
	return false;
}

module.exports = {
					'urlFromRequest' : urlFromRequest,
					'model' : searchModel,
					'cityUrlFromCity' : cityUrlFromCity
				};

<commit_msg>add community category to cl model<commit_after>
var searchModel = {
	craigslist : {
		domain : 'craigslist.org',
		cities : {
			new_york : {display : 'New York', url : 'newyork'},
			seattle : {display: 'Seattle', url : 'seattle'},
			san_francisco : {display: 'San Francisco', url : 'sfbay'},
			denver : {display: 'Denver', url : 'denver'},
			san_diego : {display: 'San Diego', url : 'sandiego'},
			los_angeles : {display: 'Los Angeles', url : 'losangeles'},
			buffalo : {display: 'Buffalo', url : 'buffalo'},
			austin : {display: 'Austin', url : 'austin'},
			anchorage : {display: 'Anchorage', url : 'anchorage'},
			portland : {display: 'Portland, OR', url : 'portland'}
		},
		categories : {
			jobs : {
					display : 'Jobs',
					subcategories :{
									web : {display : 'Web', url : 'web'},
									software : {display: 'Software', url : 'sof'},
									internet_engineering : {display : 'Internet Engineering', url : 'eng'},
									systems_networking : {display : 'Systems/Networking', url : 'sad'}
					}
			},
			gigs : {
					display : 'Gigs',
					subcategories: {
									computer : {display : 'Computer', url : 'cpg'},
									creative : {display : 'Creative', url : 'crg'}
					}
			},
			community : {
					display : 'Community',
					subcategories :{
									musicians : {display : 'Musicians', url : 'muc'}
					}
			}
		}
	}

};

var urlFromRequest = function(requestData){
	if(typeof requestData !== 'object' || !requestData.domain || !requestData.city || !requestData.category || !requestData.subcategory){
		return 'not valid request data';
		return false;
	}
	var model = searchModel[requestData.domain];
	if(!model){
		return 'not valid model';
		return false;
	}
	var cityUrl =  cityUrlFromCity(requestData.city, model);
	var category = model.categories[requestData.category];
	if(!cityUrl || !category){
		return 'not valid city or category';
		return false;
	}
	var subcategory = category.subcategories[requestData.subcategory];
	if(!subcategory){
		return 'not valid subcategory';
		return false;
	}
	var queryString = requestData.query ? '?query=' + encodeURIComponent(requestData.query) : '';
	return cityUrl + '/search/' + subcategory.url + queryString;
}

var cityUrlFromCity = function(city, sModel){
	var model = sModel ? sModel : searchModel['craigslist'];
	var validatedCity = model.cities[city];
	if(validatedCity){
		return 'http://' + validatedCity.url + '.' + model.domain;
	}
	return false;
}

module.exports = {
					'urlFromRequest' : urlFromRequest,
					'model' : searchModel,
					'cityUrlFromCity' : cityUrlFromCity
				};

<|endoftext|>


<commit_before>importScripts('workbox.js');
var cache_name = 'my-site-cache-v47';
var urlsToCache = ['/', '/index.html', '/test.html', '/bundle.js'];

self.addEventListener('install', function(event) {
  console.log('install event');
  // Perform install steps
  event.waitUntil(
    caches.open(cache_name).then(function(cache) {
      return cache.addAll(urlsToCache);
    })
  );
});

self.addEventListener('push', event => {
  if (event.data) {
    var img = '/images/icons/icon-128x128.png';
    // star wars vibration pattern
    const options = {
      body: event.data.text(),
      vibrate: [
        500,
        110,
        500,
        110,
        450,
        110,
        200,
        110,
        170,
        40,
        450,
        110,
        200,
        110,
        170,
        40,
        500
      ],
      icon: img,
      badge: img
    };
    const promiseChain = self.registration.showNotification(
      'TodoList notification',
      options
    );
    event.waitUntil(promiseChain);
  } else {
    console.log('this push event has not data');
  }
});
self.addEventListener('notificationclick', event => {
  const clickedNotification = event.notification, examplePage = '/';
  clickedNotification.close();

  const urlToOpen = new URL(examplePage, self.location.origin).href;
  const promiseChain = clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true
    })
    .then(windowClients => {
      let matchingClient = null;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.url === urlToOpen) {
          matchingClient = windowClient;
          break;
        }
      }

      if (matchingClient) {
        return matchingClient.focus();
      } else {
        return clients.openWindow(urlToOpen);
      }
    });
  event.waitUntil(promiseChain);
});

let urlsFetched = [];
self.addEventListener('fetch', function(event) {
  urlsFetched.push(event.request);
  event.respondWith(
    caches.match(event.request).then(function(response) {
      // Cache hit - return response
      if (response) {
        return response;
      }
      return fetch(event.request);
    })
  );
});

self.addEventListener('activate', function(event) {
  console.log('activate triggered', 'SW starts up');
  event.waitUntil(
    caches.keys().then(keyList => {
      Promise.all(
        keyList.map(key => {
          if (key !== cache_name) {
            console.log('deleting old cache');
            return caches.delete(key);
          }
        })
      );
    })
  );
});
<commit_msg>update cache key<commit_after>importScripts('workbox.js');
var cache_name = 'my-site-cache-v48';
var urlsToCache = ['/', '/index.html', '/test.html', '/bundle.js'];

self.addEventListener('install', function(event) {
  console.log('install event');
  // Perform install steps
  event.waitUntil(
    caches.open(cache_name).then(function(cache) {
      return cache.addAll(urlsToCache);
    })
  );
});

self.addEventListener('push', event => {
  if (event.data) {
    var img = '/images/icons/icon-128x128.png';
    // star wars vibration pattern
    const options = {
      body: event.data.text(),
      vibrate: [
        500,
        110,
        500,
        110,
        450,
        110,
        200,
        110,
        170,
        40,
        450,
        110,
        200,
        110,
        170,
        40,
        500
      ],
      icon: img,
      badge: img
    };
    const promiseChain = self.registration.showNotification(
      'TodoList notification',
      options
    );
    event.waitUntil(promiseChain);
  } else {
    console.log('this push event has not data');
  }
});
self.addEventListener('notificationclick', event => {
  const clickedNotification = event.notification, examplePage = '/';
  clickedNotification.close();

  const urlToOpen = new URL(examplePage, self.location.origin).href;
  const promiseChain = clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true
    })
    .then(windowClients => {
      let matchingClient = null;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.url === urlToOpen) {
          matchingClient = windowClient;
          break;
        }
      }

      if (matchingClient) {
        return matchingClient.focus();
      } else {
        return clients.openWindow(urlToOpen);
      }
    });
  event.waitUntil(promiseChain);
});

let urlsFetched = [];
self.addEventListener('fetch', function(event) {
  urlsFetched.push(event.request);
  event.respondWith(
    caches.match(event.request).then(function(response) {
      // Cache hit - return response
      if (response) {
        return response;
      }
      return fetch(event.request);
    })
  );
});

self.addEventListener('activate', function(event) {
  console.log('activate triggered', 'SW starts up');
  event.waitUntil(
    caches.keys().then(keyList => {
      Promise.all(
        keyList.map(key => {
          if (key !== cache_name) {
            console.log('deleting old cache');
            return caches.delete(key);
          }
        })
      );
    })
  );
});
<|endoftext|>


<commit_before>(function gc_controller_Login(l10n){
// is done under M V C pattern
Ext.define('App.um.controller.Login',{
    extend: Ext.app.Controller,

    init: function controllerLoginInit(){
    var user, role, pass, auth
       ,me = this, defer

        if(!App.um.view.Login){
            App.um.view.Login = new App.um.view.LoginWindow
            defer = 0// login
        } else {
            defer = 1// relogin
        }

        App.um.view.Login.el.select("#l10n > span").each(function l10n_changers(el){
            if(!defer){// login
                if(0 == el.dom.className.indexOf(l10n.lang)){
                    el.dom.style.opacity = 0.5// fade out current flag
                    el.dom.style.cursor = 'not-allowed'
                } else {
                    el.dom.onclick = l10n_set_and_change// install changer
                }
                return
            } else {// relogin: disable all
                el.dom.style.opacity = 0.5
                el.dom.style.cursor = 'not-allowed'
                return
            }
        })

        if(defer) return me.relogin()

        App.cfg.createViewport = false// tell `Main`, me will fire `createViewport`
        // UI refs
        user = App.um.view.Login.user
        role = App.um.view.Login.role
        pass = App.um.view.Login.pass
        auth = App.um.view.Login.auth
        // data
        App.User = App.um.model.User// must be required first from app module
        App.User.internalId = ''// reset to be used as User.id copy while offline
        // action
        user.disable()// mark to check exixting session
        user.onTriggerClick = onSessionShutdownClick
        user.focus()
        user.on({
            /* using `ref`s, but this is equivalent to:
                this.listen component:{
                     'field[name=user]':{
                        specialkey: gotoRoles
                }
             * or *
                this.control({
                    'field[name=user]':{
                        specialkey: gotoRoles
                }
             */
            specialkey: gotoRoles,
            change: reqRole
        })
        role.on({
            specialkey: gotoPass// double keypress needed
           ,change: enablePass
        })
        pass.on({
            specialkey: gotoAuth
           ,change: enableAuth
        })
        auth.on({
            click: authenticate
        })

        App.app.listen({// bind to the Application controller as this one
            global:{    // is destroyed after Login
                'initwes@UI': handleInitBackendWaitEvents,
                'usts@UI': changeUserStatus,
                'wes4UI': backendEventsCtlLogin,
                logout: logout
            }
        })

        return App.User.login('?', getSessionInfo)// ask backend for current session

        function l10n_set_and_change(){
            if('l10n-reset' == this.className){
                if(localStorage.l10n){
                    delete localStorage.l10n
                    Ext.Msg.alert({
                        icon: Ext.Msg.INFO,
                        buttons: Ext.Msg.OK,
                        title: l10n.um.l10n,
                        msg: l10n.um.l10nReset,
                        callback: reload
                    })
                }
                return
            }
            localStorage.l10n = this.className.slice(0, 2)// first two
            reload()
        }

        function getSessionInfo(err, ret){
            if(err) return

            user.enable()// mark as ready
            if(ret.can){
                user.emptyText = ret.user.id
                user.applyEmptyText()
                user.setHideTrigger(false)

                role.suspendEvents()// prevent e.g. pass.enable()
                role.setValue(l10n.um.roles[ret.can.__name] || ret.can.__name)
                role.resumeEvents()
                if('developer.local' == ret.can.__name){//!!!devel helper
                    console.log('[supro devel] fast pass for existing session')
                    authenticate()// fast pass in
                    return
                }
                auth.setText(l10n.um.loginCurrentSession)
                auth.enable()// auth is ok in this session
            }
        }

        // auth data actions
        function onSessionShutdownClick(ev){
            App.User.logout()
            ev && Ext.Msg.alert({
                icon: Ext.Msg.INFO,
                buttons: Ext.Msg.OK,
                title: l10n.um.logoutTitle,
                msg: l10n.um.logoutMsg(user.emptyText)
            })
            user.emptyText = l10n.um.loginUserBlank
            user.reset()
            user.focus()
            user.setHideTrigger(true)
            role.store.removeAll(true)
            role.reset()
            auth.disable()
            auth.setText(l10n.um.loginOk)
        }

        function reqRole(field, newUserId){
            if(defer) clearTimeout(defer)
            if(newUserId){
                if(!auth.disabled && newUserId != user.emptyText){
                    onSessionShutdownClick()
                }
                defer = setTimeout(function deferReqRoles(){
                    defer = 0
                    App.User.login(newUserId, function getSessionInfo(err, ret){
                        if(err) return

                        if(ret.can){
                            App.um.view.Login.fadeOut(createViewportAuth)
                            return// auth is ok in this session
                        }

                        if(!ret.roles.length){// no user or roles
                            if(!role.disabled){// if UI has something already
                                role.store.removeAll(true)
                                role.reset()
                                role.disable()
                                pass.reset()
                                pass.disable()
                                auth.disable()
                            }
                            return
                        }
                        var models = new Array(ret.roles.length)
                           ,Role = role.store.model
                           ,i = 0, r

                        for(; i < ret.roles.length; i++){
                            r = ret.roles[i]
                            models[i] = new Role({
                                role: l10n.um.roles[r] || r,
                                '=': r
                            })
                        }
                        role.store.loadRecords(models, false)

                        if(role.disabled){
                            role.enable()
                        }
                    })
                }, 512)
                return
            }
            if(!role.disabled){// empty user id
                role.store.removeAll(true)
                role.reset()
                role.disable()
                pass.reset()
                pass.disable()
                auth.disable()
            }
        }
        function authenticate(field){
            // prevent session activation and stall on sudden page reload/crash
            /* NOTE: there is no way to match reload or window/tab close
             *       in the browser.
             *       Thus session is lost and relogin is required. But
             *       this can be automated by userscripts.
             *       In Chrome `Ext.EventManager.onWindowUnload()` works.
             *
             * node-webkit: session is destroyed only on window `close`
             **/
            Ext.EventManager.onWindowUnload(do_logout)// `browser`
            if(App.backendURL){// `nw`
                app.w.on('close', function nw_close(){
                    App.User.logoutUI()
                    this.close(true)
                })
            }

            if(field){// from button call arguments: `field, ev`
                App.um.view.Login.fadeInProgress(do_auth)
            } else {// from direct call
                App.cfg.extjs.fading = false
                do_auth()// fast `developer.local`
            }

            return

            function do_logout(){
                App.User.logoutUI()
            }

            function do_auth(){
                App.User.auth(
                    user.getValue(),
                    role.getValue(),
                    pass.getValue(),
                    callbackAuth
                )
            }
            function callbackAuth(err, json, res){
                if(!err){
                    return App.um.view.Login.fadeOut(createViewportAuth)
                } else {
                    App.User.logoutUI = Ext.emptyFn// prevent shutdown
                    // reload if no session (e.g. backend reloaded)
                    if(res.status && 402 === res.status) location.reload(true)
                    if(res.status && 409 === res.status){// race inside session
                        user.setHideTrigger(true)
                        user.addCls('redwhite')
                        user.disable()
                        role.disable()
                        pass.disable()
                        auth.disable()
                        auth.setText(l10n.um.loginConflict)
                    } else {// continue (e.g. wrong password)
                        user.selectText()
                    }
                    return App.um.view.Login.fadeOutProgress()
                }
            }
        }
        function createViewportAuth(){
        var bar = App.view.items_Bar, i = 0, f

            me.destroy()// Login is done

            for(i = 0; i < bar.length; ++i){// search user status item
                f = bar[i]
                if('um.usts' == f.id){
                    f.tooltip = l10n.um.userStatus + ':
' + App.User.get('id') + ''
                    f.text = (
                        '' +
                            App.User.get('name') +
                        ' (' + App.User.can.__name + ')'
                    )
                    break
                }
            }
            if(App.User.modules){// per user/role UI module setup
                if((f = App.User.modules.css)) for(i = 0; i < f.length; ++i){
                    app.extjs_helper(f[i], App.backendURL)// css loading
                }
                app.extjs_helper = null// mark for GC
                Ext.syncRequire(App.User.modules.js)
            }

            Ext.globalEvents.fireEvent('createViewport')
            //!!! do not apply shutdown for now !!!
            App.User.logoutUI = Ext.emptyFn
        }

        function gotoRoles(_, ev){
            if(ev.getKey() == ev.ENTER){
                (role.disabled ? auth : role).focus()
            }
        }
        function gotoPass(_, ev){
            if(ev.getKey() == ev.ENTER){
                pass.focus()
            }
        }
        function gotoAuth(_, ev){
            if(ev.getKey() == ev.ENTER){
                auth.focus()
            }
        }
        function enablePass(_, value){
            pass.enable()
        }
        function enableAuth(){
            auth.enable()
        }
    },
    relogin: function relogin(){
    var user, role, pass, auth
       ,me = this

        user = App.um.view.Login.user
        role = App.um.view.Login.role
        pass = App.um.view.Login.pass
        auth = App.um.view.Login.auth
        auth.enable()

        user.onTriggerClick = destroy
        user.emptyText = App.User.id.replace(/....([^ ]+) .*/,'$1')
        user.applyEmptyText()
        user.enable()
        user.setHideTrigger(false)

        role.setValue(l10n.um.roles[App.User.can.__name] || App.User.can.__name)
        role.disable()

        pass.enable()
        pass.on({
            specialkey: function gotoAuth2(_, ev){
                if(ev.getKey() == ev.ENTER){
                    auth.focus()
                }
            }
        })

        pass.setValue(' ')// crutch to activate input field:
        setTimeout(function(){
            pass.setValue('')
            pass.focus()
        }, 128)

        auth.on({ click: function(){
            App.um.view.Login.fadeInProgress(function(){
            App.User.login(App.User.get('id'), function(err, ret){
                if(err) return
            App.User.auth(
                App.User.get('id'),
                App.User.can.__name,
                pass.getValue(), function(err, json, res){
                    if(err){
                        if(res.status && 402 === res.status){
                            App.denyMsg()
                        } else if(res.status && 409 === res.status){
                            user.setHideTrigger(false)
                            role.disable()
                            pass.disable()
                            auth.disable()
                            auth.setText(l10n.um.loginConflict)
                        }
                        return App.um.view.Login.fadeOutProgress()
                    }

            App.um.wes(App.User.internalId.slice(0,4))// saved status
            return App.um.view.Login.fadeOut(destroy)
                }
            )})})
        }})
        return

        function destroy(){
            me.destroy()
        }
    },
    destroy: function destroy(){
    /* in case of logout event from backend, this
     * `App.um.view.Login` is shown again without reloading of all Viewport
     */
        App.um.view.Login.destroy()
        App.um.view.Login = null// GC
        this.application.eventbus.unlisten(this.id)
        this.application.controllers.removeAtKey(this.id)
    }
})

/*
 * Application wide event handlers
 **/
function backendEventsCtlLogin(success, data){
var evn, cmp, s

    App.sts(
       'backend events',
        success ? data.length : data,// data || res.statusText
        success ? l10n.stsOK : l10n.stsHE,
        s = new Date
    )

    console.log('wes: ' + s)
    console.log(data)
    console.table(data)

    if('string' == typeof data) switch (data){// simple event
        case 'Disconnect':
        case 'Unauthorized':
        case 'Payment Required':
            if(App.um.view.Login) return// event is firing again (still no login)

            (cmp = Ext.getCmp('um.usts')) && cmp.setIconCls('appbar-user-offl')
            App.um.view.Login = new App.um.view.LoginWindow({ modal: true })
            Ext.WindowManager.bringToFront(App.um.view.Login)
            App.app.getController('App.um.controller.Login')// only strings
        return
        default:return
    }

    for(evn = 0; evn < data.length; ++evn) switch (data[evn].ev){
        case 'Usts@um': if((cmp = Ext.getCmp('um.usts'))){
            if(cmp.iconCls.slice(12) != (s = data[evn].json.slice(0, 4))){
                cmp.setIconCls('appbar-user-' + s)
            }
        }
        break
        case 'uncaught@global':
            if(App.User.can['uncaught@global'] && Ext.Msg.hidden) Ext.Msg.alert(
                {
                    buttons: Ext.Msg.OK,
                    icon: Ext.Msg.ERROR,
                    title: 'uncaught@global',
                    msg: data[evn].json,
                    fn: function(btn){
                        //if('yes' == btn)...
                    }
                }
            )
        break
        case 'Disconnect':
            (cmp = Ext.getCmp('um.usts')) && cmp.setIconCls('appbar-user-offl')
        break
        case 'Unauthorized':

            App.um.view.Login = new App.um.view.LoginWindow({ modal: true })
            Ext.WindowManager.bringToFront(App.um.view.Login)
            App.app.getController(App.um.controller.Login)

        break
        default:break
    }
}

function handleInitBackendWaitEvents(msg){
    App.sts(
        msg,
       'init backend Wait EventS',
        l10n.stsOK,
        new Date
    )
}

function changeUserStatus(status){
    App.um.wes(status)
}

function logout(){
    App.app.suspendEvents(false)

    Ext.Msg.alert({
        buttons: Ext.Msg.YESNO,
        icon: Ext.Msg.QUESTION,
        title: l10n.um.logoutTitle,
        msg: l10n.um.logoutMsg(
            App.um.model.User.get('id'),
            l10n.um.roles[App.User.can.__name] || App.User.can.__name
        ),
        fn: function(btn){
            if('yes' == btn) App.User.logout(reload)
        }
    })
}

function reload(){
    location.reload(true)
}

App.denyMsg = function denyMsg(){
    Ext.Msg.alert({
        icon: Ext.Msg.WARNING,
        buttons: Ext.Msg.OK,
        title: l10n.um.auth,
        msg: l10n.um.deny,
        fn: function(){ }
    })
}

App.app.getController('App.um.controller.Login')/* init */

})(l10n)
<commit_msg>um.controller.Login: reset role when user id field was changed<commit_after>(function gc_controller_Login(l10n){
// is done under M V C pattern
Ext.define('App.um.controller.Login',{
    extend: Ext.app.Controller,

    init: function controllerLoginInit(){
    var user, role, pass, auth
       ,me = this, defer

        if(!App.um.view.Login){
            App.um.view.Login = new App.um.view.LoginWindow
            defer = 0// login
        } else {
            defer = 1// relogin
        }

        App.um.view.Login.el.select("#l10n > span").each(function l10n_changers(el){
            if(!defer){// login
                if(0 == el.dom.className.indexOf(l10n.lang)){
                    el.dom.style.opacity = 0.5// fade out current flag
                    el.dom.style.cursor = 'not-allowed'
                } else {
                    el.dom.onclick = l10n_set_and_change// install changer
                }
                return
            } else {// relogin: disable all
                el.dom.style.opacity = 0.5
                el.dom.style.cursor = 'not-allowed'
                return
            }
        })

        if(defer) return me.relogin()

        App.cfg.createViewport = false// tell `Main`, me will fire `createViewport`
        // UI refs
        user = App.um.view.Login.user
        role = App.um.view.Login.role
        pass = App.um.view.Login.pass
        auth = App.um.view.Login.auth
        // data
        App.User = App.um.model.User// must be required first from app module
        App.User.internalId = ''// reset to be used as User.id copy while offline
        // action
        user.disable()// mark to check exixting session
        user.onTriggerClick = onSessionShutdownClick
        user.focus()
        user.on({
            /* using `ref`s, but this is equivalent to:
                this.listen component:{
                     'field[name=user]':{
                        specialkey: gotoRoles
                }
             * or *
                this.control({
                    'field[name=user]':{
                        specialkey: gotoRoles
                }
             */
            specialkey: gotoRoles,
            change: reqRole
        })
        role.on({
            specialkey: gotoPass// double keypress needed
           ,change: enablePass
        })
        pass.on({
            specialkey: gotoAuth
           ,change: enableAuth
        })
        auth.on({
            click: authenticate
        })

        App.app.listen({// bind to the Application controller as this one
            global:{    // is destroyed after Login
                'initwes@UI': handleInitBackendWaitEvents,
                'usts@UI': changeUserStatus,
                'wes4UI': backendEventsCtlLogin,
                logout: logout
            }
        })

        return App.User.login('?', getSessionInfo)// ask backend for current session

        function l10n_set_and_change(){
            if('l10n-reset' == this.className){
                if(localStorage.l10n){
                    delete localStorage.l10n
                    Ext.Msg.alert({
                        icon: Ext.Msg.INFO,
                        buttons: Ext.Msg.OK,
                        title: l10n.um.l10n,
                        msg: l10n.um.l10nReset,
                        callback: reload
                    })
                }
                return
            }
            localStorage.l10n = this.className.slice(0, 2)// first two
            reload()
        }

        function getSessionInfo(err, ret){
            if(err) return

            user.enable()// mark as ready
            if(ret.can){
                user.emptyText = ret.user.id
                user.applyEmptyText()
                user.setHideTrigger(false)

                role.suspendEvents()// prevent e.g. pass.enable()
                role.setValue(l10n.um.roles[ret.can.__name] || ret.can.__name)
                role.resumeEvents()
                if('developer.local' == ret.can.__name){//!!!devel helper
                    console.log('[supro devel] fast pass for existing session')
                    authenticate()// fast pass in
                    return
                }
                auth.setText(l10n.um.loginCurrentSession)
                auth.enable()// auth is ok in this session
            }
        }

        // auth data actions
        function onSessionShutdownClick(ev){
            App.User.logout()
            ev && Ext.Msg.alert({
                icon: Ext.Msg.INFO,
                buttons: Ext.Msg.OK,
                title: l10n.um.logoutTitle,
                msg: l10n.um.logoutMsg(user.emptyText)
            })
            user.emptyText = l10n.um.loginUserBlank
            user.reset()
            user.focus()
            user.setHideTrigger(true)
            role.store.removeAll(true)
            role.reset()
            auth.disable()
            auth.setText(l10n.um.loginOk)
        }

        function reqRole(field, newUserId){
            if(defer) clearTimeout(defer)
            if(newUserId){
                if(!auth.disabled && newUserId != user.emptyText){
                    onSessionShutdownClick()
                }
                defer = setTimeout(function deferReqRoles(){
                    defer = 0
                    role.reset()
                    App.User.login(newUserId, function getSessionInfo(err, ret){
                        if(err) return

                        if(ret.can){
                            App.um.view.Login.fadeOut(createViewportAuth)
                            return// auth is ok in this session
                        }

                        if(!ret.roles.length){// no user or roles
                            if(!role.disabled){// if UI has something already
                                role.store.removeAll(true)
                                role.reset()
                                role.disable()
                                pass.reset()
                                pass.disable()
                                auth.disable()
                            }
                            return
                        }
                        var models = new Array(ret.roles.length)
                           ,Role = role.store.model
                           ,i = 0, r

                        for(; i < ret.roles.length; i++){
                            r = ret.roles[i]
                            models[i] = new Role({
                                role: l10n.um.roles[r] || r,
                                '=': r
                            })
                        }
                        role.store.loadRecords(models, false)

                        if(role.disabled){
                            role.enable()
                        }
                    })
                }, 512)
                return
            }
            if(!role.disabled){// empty user id
                role.store.removeAll(true)
                role.reset()
                role.disable()
                pass.reset()
                pass.disable()
                auth.disable()
            }
        }
        function authenticate(field){
            // prevent session activation and stall on sudden page reload/crash
            /* NOTE: there is no way to match reload or window/tab close
             *       in the browser.
             *       Thus session is lost and relogin is required. But
             *       this can be automated by userscripts.
             *       In Chrome `Ext.EventManager.onWindowUnload()` works.
             *
             * node-webkit: session is destroyed only on window `close`
             **/
            Ext.EventManager.onWindowUnload(do_logout)// `browser`
            if(App.backendURL){// `nw`
                app.w.on('close', function nw_close(){
                    App.User.logoutUI()
                    this.close(true)
                })
            }

            if(field){// from button call arguments: `field, ev`
                App.um.view.Login.fadeInProgress(do_auth)
            } else {// from direct call
                App.cfg.extjs.fading = false
                do_auth()// fast `developer.local`
            }

            return

            function do_logout(){
                App.User.logoutUI()
            }

            function do_auth(){
                App.User.auth(
                    user.getValue(),
                    role.getValue(),
                    pass.getValue(),
                    callbackAuth
                )
            }
            function callbackAuth(err, json, res){
                if(!err){
                    return App.um.view.Login.fadeOut(createViewportAuth)
                } else {
                    App.User.logoutUI = Ext.emptyFn// prevent shutdown
                    // reload if no session (e.g. backend reloaded)
                    if(res.status && 402 === res.status) location.reload(true)
                    if(res.status && 409 === res.status){// race inside session
                        user.setHideTrigger(true)
                        user.addCls('redwhite')
                        user.disable()
                        role.disable()
                        pass.disable()
                        auth.disable()
                        auth.setText(l10n.um.loginConflict)
                    } else {// continue (e.g. wrong password)
                        user.selectText()
                    }
                    return App.um.view.Login.fadeOutProgress()
                }
            }
        }
        function createViewportAuth(){
        var bar = App.view.items_Bar, i = 0, f

            me.destroy()// Login is done

            for(i = 0; i < bar.length; ++i){// search user status item
                f = bar[i]
                if('um.usts' == f.id){
                    f.tooltip = l10n.um.userStatus + ':
' + App.User.get('id') + ''
                    f.text = (
                        '' +
                            App.User.get('name') +
                        ' (' + App.User.can.__name + ')'
                    )
                    break
                }
            }
            if(App.User.modules){// per user/role UI module setup
                if((f = App.User.modules.css)) for(i = 0; i < f.length; ++i){
                    app.extjs_helper(f[i], App.backendURL)// css loading
                }
                app.extjs_helper = null// mark for GC
                Ext.syncRequire(App.User.modules.js)
            }

            Ext.globalEvents.fireEvent('createViewport')
            //!!! do not apply shutdown for now !!!
            App.User.logoutUI = Ext.emptyFn
        }

        function gotoRoles(_, ev){
            if(ev.getKey() == ev.ENTER){
                (role.disabled ? auth : role).focus()
            }
        }
        function gotoPass(_, ev){
            if(ev.getKey() == ev.ENTER){
                pass.focus()
            }
        }
        function gotoAuth(_, ev){
            if(ev.getKey() == ev.ENTER){
                auth.focus()
            }
        }
        function enablePass(_, value){
            pass.enable()
        }
        function enableAuth(){
            auth.enable()
        }
    },
    relogin: function relogin(){
    var user, role, pass, auth
       ,me = this

        user = App.um.view.Login.user
        role = App.um.view.Login.role
        pass = App.um.view.Login.pass
        auth = App.um.view.Login.auth
        auth.enable()

        user.onTriggerClick = destroy
        user.emptyText = App.User.id.replace(/....([^ ]+) .*/,'$1')
        user.applyEmptyText()
        user.enable()
        user.setHideTrigger(false)

        role.setValue(l10n.um.roles[App.User.can.__name] || App.User.can.__name)
        role.disable()

        pass.enable()
        pass.on({
            specialkey: function gotoAuth2(_, ev){
                if(ev.getKey() == ev.ENTER){
                    auth.focus()
                }
            }
        })

        pass.setValue(' ')// crutch to activate input field:
        setTimeout(function(){
            pass.setValue('')
            pass.focus()
        }, 128)

        auth.on({ click: function(){
            App.um.view.Login.fadeInProgress(function(){
            App.User.login(App.User.get('id'), function(err, ret){
                if(err) return
            App.User.auth(
                App.User.get('id'),
                App.User.can.__name,
                pass.getValue(), function(err, json, res){
                    if(err){
                        if(res.status && 402 === res.status){
                            App.denyMsg()
                        } else if(res.status && 409 === res.status){
                            user.setHideTrigger(false)
                            role.disable()
                            pass.disable()
                            auth.disable()
                            auth.setText(l10n.um.loginConflict)
                        }
                        return App.um.view.Login.fadeOutProgress()
                    }

            App.um.wes(App.User.internalId.slice(0,4))// saved status
            return App.um.view.Login.fadeOut(destroy)
                }
            )})})
        }})
        return

        function destroy(){
            me.destroy()
        }
    },
    destroy: function destroy(){
    /* in case of logout event from backend, this
     * `App.um.view.Login` is shown again without reloading of all Viewport
     */
        App.um.view.Login.destroy()
        App.um.view.Login = null// GC
        this.application.eventbus.unlisten(this.id)
        this.application.controllers.removeAtKey(this.id)
    }
})

/*
 * Application wide event handlers
 **/
function backendEventsCtlLogin(success, data){
var evn, cmp, s

    App.sts(
       'backend events',
        success ? data.length : data,// data || res.statusText
        success ? l10n.stsOK : l10n.stsHE,
        s = new Date
    )

    console.log('wes: ' + s)
    console.log(data)
    console.table(data)

    if('string' == typeof data) switch (data){// simple event
        case 'Disconnect':
        case 'Unauthorized':
        case 'Payment Required':
            if(App.um.view.Login) return// event is firing again (still no login)

            (cmp = Ext.getCmp('um.usts')) && cmp.setIconCls('appbar-user-offl')
            App.um.view.Login = new App.um.view.LoginWindow({ modal: true })
            Ext.WindowManager.bringToFront(App.um.view.Login)
            App.app.getController('App.um.controller.Login')// only strings
        return
        default:return
    }

    for(evn = 0; evn < data.length; ++evn) switch (data[evn].ev){
        case 'Usts@um': if((cmp = Ext.getCmp('um.usts'))){
            if(cmp.iconCls.slice(12) != (s = data[evn].json.slice(0, 4))){
                cmp.setIconCls('appbar-user-' + s)
            }
        }
        break
        case 'uncaught@global':
            if(App.User.can['uncaught@global'] && Ext.Msg.hidden) Ext.Msg.alert(
                {
                    buttons: Ext.Msg.OK,
                    icon: Ext.Msg.ERROR,
                    title: 'uncaught@global',
                    msg: data[evn].json,
                    fn: function(btn){
                        //if('yes' == btn)...
                    }
                }
            )
        break
        case 'Disconnect':
            (cmp = Ext.getCmp('um.usts')) && cmp.setIconCls('appbar-user-offl')
        break
        case 'Unauthorized':

            App.um.view.Login = new App.um.view.LoginWindow({ modal: true })
            Ext.WindowManager.bringToFront(App.um.view.Login)
            App.app.getController(App.um.controller.Login)

        break
        default:break
    }
}

function handleInitBackendWaitEvents(msg){
    App.sts(
        msg,
       'init backend Wait EventS',
        l10n.stsOK,
        new Date
    )
}

function changeUserStatus(status){
    App.um.wes(status)
}

function logout(){
    App.app.suspendEvents(false)

    Ext.Msg.alert({
        buttons: Ext.Msg.YESNO,
        icon: Ext.Msg.QUESTION,
        title: l10n.um.logoutTitle,
        msg: l10n.um.logoutMsg(
            App.um.model.User.get('id'),
            l10n.um.roles[App.User.can.__name] || App.User.can.__name
        ),
        fn: function(btn){
            if('yes' == btn) App.User.logout(reload)
        }
    })
}

function reload(){
    location.reload(true)
}

App.denyMsg = function denyMsg(){
    Ext.Msg.alert({
        icon: Ext.Msg.WARNING,
        buttons: Ext.Msg.OK,
        title: l10n.um.auth,
        msg: l10n.um.deny,
        fn: function(){ }
    })
}

App.app.getController('App.um.controller.Login')/* init */

})(l10n)
<|endoftext|>