<commit_before>/*
 * Copyright (c) 2015-2018 PointSource, LLC.
 * MIT Licensed
 */

var path = require('path');
var fs = require('fs');
var debug = require('debug')('swagger');
var async = require('async');
var parser = require('swagger-parser');
var swaggerUtil = require('../lib/swaggerUtil');
var refCompiler = require('../lib/swaggerRefCompiler');
var tv4 = require('tv4');
var _ = require('lodash');
var util = require('util');

var specs = {
    dereferenced: {},
    bundled: {},
    names: []
};

var httpMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'];

var responseModelValidationLevel;
var polymorphicValidation;
var refCompilation;

exports.discriminatorKeyMap = 'x-bos-generated-disc-map';

exports.init = function (logger, config, callback) {
    var cfg = config.get('swagger');
    // default responseModelValidationLevel to level zero, i.e. off
    responseModelValidationLevel = /warn|error|fail/.test(cfg.validateResponseModels) ?
        cfg.validateResponseModels : 0;
    if (responseModelValidationLevel) {
        logger.info('Response model validation is on and set to level "%s"', responseModelValidationLevel);
    }

    // default polymorphicValidation to 'on'; allow both 'off' and false to turn it off
    polymorphicValidation = /on|warn|off/.test(cfg.polymorphicValidation) ?
        cfg.polymorphicValidation : ((cfg.polymorphicValidation === false) ? 'off' : 'on');
    if (polymorphicValidation !== 'on') {
        logger.info('Polymorphic validation is disabled (%s)', polymorphicValidation);
    }

    refCompilation = typeof cfg.refCompiler === 'object';
    if (refCompilation) {
        logger.info('Ref compilation is enabled (%s), compiling references...', refCompilation);
        refCompiler.compileSpecs(logger, cfg);
    }

    var swaggerDir = null;
    if (isBlueoakProject()) {
        swaggerDir = path.resolve(global.__appDir, '../common/swagger');
    } else {
        swaggerDir = path.resolve(global.__appDir, 'swagger');
    }

    debug('Loading swagger files from %s', swaggerDir);

    var files = [];

    try {
        fs.readdirSync(swaggerDir).forEach(function (fileName) {
            //look for json and yaml
            if (path.extname(fileName) === '.json') {
                files.push(path.resolve(swaggerDir, fileName));
            } else if (path.extname(fileName) === '.yaml') {
                files.push(path.resolve(swaggerDir, fileName));
            }
        });
    } catch (err) {
        //swagger dir probably doesn't exist
        return callback();
    }

    //Loop through all our swagger models
    //For each model, parse it, and automatically hook up the routes to the appropriate handler
    async.eachSeries(files, function parseSwagger(file, swagCallback) {
        var handlerName = path.basename(file); //use the swagger filename as our handler module id
        handlerName = handlerName.substring(0, handlerName.lastIndexOf('.')); //strip extensions

        var derefPromise = parser.validate(file);
        parser.bundle(file)
            .then(function (bundledApi) {
                specs.bundled[handlerName] = bundledApi;
                return derefPromise;
            })
            .then(function (dereferencedApi) {
                specs.dereferenced[handlerName] = dereferencedApi;
                if (polymorphicValidation !== 'off') {
                    preparePathsForPolymorphicValidation(dereferencedApi.paths, responseModelValidationLevel);
                }
                prepareDefinitionsForPolymorphicValidation(dereferencedApi.definitions);
                specs.names.push(handlerName);
                return swagCallback();
            })
            .catch(function (error) {
                // don't generate an error if it was a non-Swagger Spec JSON file
                var swagErr = error;
                if (path.extname(file) !== '.yaml') {
                    try {
                        var json = JSON.parse(fs.readFileSync(file, {
                            encoding: 'utf8'
                        }));
                        if (!isSwaggerFile(json)) {
                            logger.info('Skipping non-Swagger JSON file %s', file);
                            swagErr = null;
                        }
                    } catch (err) {
                        logger.error('%s is not valid JSON', file);
                    }
                }
                return swagCallback(swagErr);
            });
    }, function (err) {
        callback(err);
    });

};

exports.getResponseModelValidationLevel = function () {
    return responseModelValidationLevel;
};

exports.isPolymorphicValidationEnabled = function () {
    return polymorphicValidation;
};

exports.getValidHttpMethods = function () {
    return httpMethods;
};

exports.getSpecNames = function () {
    return specs.names;
};

exports.getSimpleSpecs = function () {
    return specs.dereferenced;
};

exports.getPrettySpecs = function () {
    return specs.bundled;
};

exports.addFormat = function (format, validationFunction) {
    tv4.addFormat(format, validationFunction);
};

/**
 * Validate an arbitrary object against a model definition in a given specification
 *
 * @param {Object|String} config - configuration for this validation,
 *          or, simply, the name of the spec to use with the default config
 * @param {String|Object} config.spec - the name of the specification in which the model is defined
 *          or the specification model definition object to use for validation
 * @param {Boolean} [config.banUnknownProperties=false] - whether to fail validation when there are undefined properties
 * @param {Boolean} [config.failFast=false] - whether to stop validation when the first error is found
 * @param {Boolean} [config.skipPolymorphicChecks=false] - whether to disable polymorphic checks
 * @param {String|Object} model - the name of the model, in the given spec, to validate against
 *          or the actual model to use
 * @param {Object} object - the object to be validated
 *
 * @returns {Object} an object containing the validation result:
 *          .valid is a boolean indicated whether the object validated against the model;
 *          .errors is an array of tv4 validation errors for particular fields;
 *          .polymorphicValidationErrors is an array of tv4 validation errors that only
 *              show with polymorphic validation;
 *
 * @throws {Error} when the spec or model cannot be found
 */
exports.validateObject = function (config, model, object) {
    var specObject, specName;
    if (typeof config === 'string') {
        specName = config;
    } else if (typeof config.spec === 'string') {
        specName = config.spec;
    } else if (typeof config.spec === 'object') {
        specObject = config.spec;
        specName = '<inline>';
    }
    if (!specObject) {
        specObject = _.get(specs.dereferenced, specName);

        if (!specObject) {
            throw _createObjectValidationError(
                util.format('Cannot validate object for unknown specification "%s"', specName));
        }
    }

    var modelObject, modelName;
    if (typeof model === 'object') {
        modelObject = model;
        modelName = '<inline>';
    } else {
        modelName = model;
        modelObject = _.get(specObject, ['definitions', modelName].join('.'));
    }
    if (!modelObject) {
        throw _createObjectValidationError(
            util.format('Cannot validate object for unknown model "%s" in specification "%s"', modelName, specName));
    }

    var result = swaggerUtil.validateJSONType(modelObject, object, config);
    if (!(config.skipPolymorphicChecks || _.isEmpty(modelObject[exports.discriminatorKeyMap]))
        && (result.valid || !config.failFast)) {
        result.polymorphicValidationErrors = swaggerUtil.validateIndividualObjects(
            specObject, modelObject[exports.discriminatorKeyMap], object, config);
        if (result.polymorphicValidationErrors.length > 0) {
            result.valid = false;
        }
    }

    return result;

    function _createObjectValidationError(message) {
        var error = new Error(message);
        error.name = 'SwaggerObjectValidationError';
        error.specName = specName;
        error.modelName = modelName;
        return error;
    }
};

function preparePathsForPolymorphicValidation(paths, doResponseValidation) {
    var pathKeys = _.keys(paths);
    pathKeys.forEach(function (path) {
        var methodKeys = _.keys(paths[path]);
        methodKeys.forEach(function (method) {
            if (httpMethods.indexOf(method) !== -1) {//is this key actually an http method
                if (doResponseValidation) {
                    var responseCodeKeys = _.keys(paths[path][method].responses);
                    responseCodeKeys.forEach(function (responseCode) {
                        var responseDefinition = paths[path][method].responses[responseCode];
                        _makeSchemaPolymorphic(responseDefinition);
                    });
                }
                if (paths[path][method].parameters) {
                    var requestParamKeys = _.keys(paths[path][method].parameters);
                    requestParamKeys.forEach(function (param) {
                        var requestParameter = paths[path][method].parameters[param];
                        _makeSchemaPolymorphic(requestParameter);
                    });
                }
            }
        });
    });

    function _makeSchemaPolymorphic(parentObject) {
        if (!parentObject.schema) {
            return;
        }
        _addDiscMapToSchema(parentObject.schema);
        parentObject.schema = _createFlattenedSchema(parentObject.schema);
    }
}

function _addDiscMapToSchema(targetSchema, schemaName) {
    var discMap = swaggerUtil.getObjectsWithDiscriminator(targetSchema, schemaName);
    if (targetSchema.allOf) {
        var completeDiscMap = {
            type: 'object'
        };
        completeDiscMap[exports.discriminatorKeyMap] = discMap;
        targetSchema.allOf.push(completeDiscMap);
    } else {
        targetSchema[exports.discriminatorKeyMap] = discMap;
    }
}

function _createFlattenedSchema(sourceSchema) {
    var flattenedSchema;
    if (sourceSchema.type === 'array') {
        flattenedSchema = _.clone(sourceSchema);
        flattenedSchema.items = _createFlattenedSchema(sourceSchema.items);
    } else if (Array.isArray(sourceSchema.allOf)) {
        flattenedSchema = _doFlatMerge({}, sourceSchema);
    } else if (sourceSchema.type === 'object') {
        flattenedSchema = _.cloneDeep(sourceSchema);
    } else {
        flattenedSchema = _.clone(sourceSchema);
    }
    return flattenedSchema;

    function _doFlatMerge(target, source) {
        if (Array.isArray(source.allOf)) {
            source.allOf.forEach(function (duckType) {
                _doFlatMerge(target, duckType);
            });
            return target;
        }

        // we have to handle merging the array `required` on our own
        var reqArr = _.union(target.required, source.required);
        _.merge(target, source);
        target.required = reqArr;
        return target;
    }
}

function prepareDefinitionsForPolymorphicValidation(definitions) {
    var modelNames = _.keys(definitions);
    modelNames.forEach(function (modelName) {
        var schema = definitions[modelName];
        _addDiscMapToSchema(schema, modelName);
        definitions[modelName] = _createFlattenedSchema(schema);
    });
}

//Try to determine if this is supposed to be a swagger file
//For now look for the required "swagger" field, which contains the version
function isSwaggerFile(json) {
    return json.swagger;
}

//There are two possible places for loading swagger.
//If we're part of a broader blueoak client-server project, blueoak is running
//from a 'server' directory, and there's a sibling directory named 'common' which contains the swagger directory.
//Otherwise we just look in the normal swagger folder within the project
function isBlueoakProject() {
    if ((path.basename(global.__appDir) !== 'server')) {
        return false;
    }
    try {
        return fs.statSync(path.resolve(global.__appDir, '../common/swagger')).isDirectory();
    } catch (err) {
        return false;
    }
}<commit_msg>Add tracking of swagger processing for error reporting<commit_after>/*
 * Copyright (c) 2015-2018 PointSource, LLC.
 * MIT Licensed
 */

var path = require('path');
var fs = require('fs');
var debug = require('debug')('swagger');
var async = require('async');
var parser = require('swagger-parser');
var swaggerUtil = require('../lib/swaggerUtil');
var refCompiler = require('../lib/swaggerRefCompiler');
var tv4 = require('tv4');
var _ = require('lodash');
var util = require('util');

var specs = {
    dereferenced: {},
    bundled: {},
    names: []
};

var httpMethods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'];

var responseModelValidationLevel;
var polymorphicValidation;
var refCompilation;

exports.discriminatorKeyMap = 'x-bos-generated-disc-map';

exports.init = function (logger, config, callback) {
    var cfg = config.get('swagger');
    // default responseModelValidationLevel to level zero, i.e. off
    responseModelValidationLevel = /warn|error|fail/.test(cfg.validateResponseModels) ?
        cfg.validateResponseModels : 0;
    if (responseModelValidationLevel) {
        logger.info('Response model validation is on and set to level "%s"', responseModelValidationLevel);
    }

    // default polymorphicValidation to 'on'; allow both 'off' and false to turn it off
    polymorphicValidation = /on|warn|off/.test(cfg.polymorphicValidation) ?
        cfg.polymorphicValidation : ((cfg.polymorphicValidation === false) ? 'off' : 'on');
    if (polymorphicValidation !== 'on') {
        logger.info('Polymorphic validation is disabled (%s)', polymorphicValidation);
    }

    refCompilation = typeof cfg.refCompiler === 'object';
    if (refCompilation) {
        logger.info('Ref compilation is enabled (%s), compiling references...', refCompilation);
        refCompiler.compileSpecs(logger, cfg);
    }

    var swaggerDir = null;
    if (isBlueoakProject()) {
        swaggerDir = path.resolve(global.__appDir, '../common/swagger');
    } else {
        swaggerDir = path.resolve(global.__appDir, 'swagger');
    }

    debug('Loading swagger files from %s', swaggerDir);

    var files = [];

    try {
        fs.readdirSync(swaggerDir).forEach(function (fileName) {
            //look for json and yaml
            if (path.extname(fileName) === '.json') {
                files.push(path.resolve(swaggerDir, fileName));
            } else if (path.extname(fileName) === '.yaml') {
                files.push(path.resolve(swaggerDir, fileName));
            }
        });
    } catch (err) {
        //swagger dir probably doesn't exist
        return callback();
    }

    //Loop through all our swagger models
    //For each model, parse it, and automatically hook up the routes to the appropriate handler
    async.eachSeries(files, function parseSwagger(file, swagCallback) {
        var handlerName = path.basename(file); //use the swagger filename as our handler module id
        handlerName = handlerName.substring(0, handlerName.lastIndexOf('.')); //strip extensions

        var derefPromise = parser.validate(file);
        parser.bundle(file)
            .then(function (bundledApi) {
                specs.bundled[handlerName] = bundledApi;
                return derefPromise;
            })
            .then(function (dereferencedApi) {
                specs.dereferenced[handlerName] = dereferencedApi;
                if (polymorphicValidation !== 'off') {
                    preparePathsForPolymorphicValidation(dereferencedApi.paths, responseModelValidationLevel);
                }
                prepareDefinitionsForPolymorphicValidation(dereferencedApi.definitions);
                specs.names.push(handlerName);
                return swagCallback();
            })
            .catch(function (error) {
                // don't generate an error if it was a non-Swagger Spec JSON file
                var swagErr = error;
                if (path.extname(file) !== '.yaml') {
                    try {
                        var json = JSON.parse(fs.readFileSync(file, {
                            encoding: 'utf8'
                        }));
                        if (!isSwaggerFile(json)) {
                            logger.info('Skipping non-Swagger JSON file %s', file);
                            swagErr = null;
                        }
                    } catch (err) {
                        logger.error('%s is not valid JSON', file);
                    }
                }
                return swagCallback(swagErr);
            });
    }, function (err) {
        callback(err);
    });

};

exports.getResponseModelValidationLevel = function () {
    return responseModelValidationLevel;
};

exports.isPolymorphicValidationEnabled = function () {
    return polymorphicValidation;
};

exports.getValidHttpMethods = function () {
    return httpMethods;
};

exports.getSpecNames = function () {
    return specs.names;
};

exports.getSimpleSpecs = function () {
    return specs.dereferenced;
};

exports.getPrettySpecs = function () {
    return specs.bundled;
};

exports.addFormat = function (format, validationFunction) {
    tv4.addFormat(format, validationFunction);
};

/**
 * Validate an arbitrary object against a model definition in a given specification
 *
 * @param {Object|String} config - configuration for this validation,
 *          or, simply, the name of the spec to use with the default config
 * @param {String|Object} config.spec - the name of the specification in which the model is defined
 *          or the specification model definition object to use for validation
 * @param {Boolean} [config.banUnknownProperties=false] - whether to fail validation when there are undefined properties
 * @param {Boolean} [config.failFast=false] - whether to stop validation when the first error is found
 * @param {Boolean} [config.skipPolymorphicChecks=false] - whether to disable polymorphic checks
 * @param {String|Object} model - the name of the model, in the given spec, to validate against
 *          or the actual model to use
 * @param {Object} object - the object to be validated
 *
 * @returns {Object} an object containing the validation result:
 *          .valid is a boolean indicated whether the object validated against the model;
 *          .errors is an array of tv4 validation errors for particular fields;
 *          .polymorphicValidationErrors is an array of tv4 validation errors that only
 *              show with polymorphic validation;
 *
 * @throws {Error} when the spec or model cannot be found
 */
exports.validateObject = function (config, model, object) {
    var specObject, specName;
    if (typeof config === 'string') {
        specName = config;
    } else if (typeof config.spec === 'string') {
        specName = config.spec;
    } else if (typeof config.spec === 'object') {
        specObject = config.spec;
        specName = '<inline>';
    }
    if (!specObject) {
        specObject = _.get(specs.dereferenced, specName);

        if (!specObject) {
            throw _createObjectValidationError(
                util.format('Cannot validate object for unknown specification "%s"', specName));
        }
    }

    var modelObject, modelName;
    if (typeof model === 'object') {
        modelObject = model;
        modelName = '<inline>';
    } else {
        modelName = model;
        modelObject = _.get(specObject, ['definitions', modelName].join('.'));
    }
    if (!modelObject) {
        throw _createObjectValidationError(
            util.format('Cannot validate object for unknown model "%s" in specification "%s"', modelName, specName));
    }

    var result = swaggerUtil.validateJSONType(modelObject, object, config);
    if (!(config.skipPolymorphicChecks || _.isEmpty(modelObject[exports.discriminatorKeyMap]))
        && (result.valid || !config.failFast)) {
        result.polymorphicValidationErrors = swaggerUtil.validateIndividualObjects(
            specObject, modelObject[exports.discriminatorKeyMap], object, config);
        if (result.polymorphicValidationErrors.length > 0) {
            result.valid = false;
        }
    }

    return result;

    function _createObjectValidationError(message) {
        var error = new Error(message);
        error.name = 'SwaggerObjectValidationError';
        error.specName = specName;
        error.modelName = modelName;
        return error;
    }
};

function preparePathsForPolymorphicValidation(paths, doResponseValidation) {
    var pathKeys = _.keys(paths);
    pathKeys.forEach(function (path) {
        var methodKeys = _.keys(paths[path]);
        methodKeys.forEach(function (method) {
            if (httpMethods.indexOf(method) !== -1) {//is this key actually an http method
                var methodName = method.toUpperCase();
                if (doResponseValidation) {
                    var responseCodeKeys = _.keys(paths[path][method].responses);
                    responseCodeKeys.forEach(function (responseCode) {
                        var responseDefinition = paths[path][method].responses[responseCode];
                        _makeSchemaPolymorphic(responseDefinition,
                            util.format('%s %s (response %s)', methodName, path, responseCode));
                    });
                }
                if (paths[path][method].parameters) {
                    var requestParamKeys = _.keys(paths[path][method].parameters);
                    requestParamKeys.forEach(function (param) {
                        var requestParameter = paths[path][method].parameters[param];
                        _makeSchemaPolymorphic(requestParameter,
                            util.format('%s %s (request parameter "%s"', methodName, path, param));
                    });
                }
            }
        });
    });

    function _makeSchemaPolymorphic(parentObject, methodRef) {
        if (!parentObject.schema) {
            return;
        }
        _addDiscMapToSchema(parentObject.schema, methodRef);
        parentObject.schema = _createFlattenedSchema(parentObject.schema);
    }
}

function _addDiscMapToSchema(targetSchema, schemaName) {
    var discMap = swaggerUtil.getObjectsWithDiscriminator(targetSchema, schemaName);
    if (targetSchema.allOf) {
        var completeDiscMap = {
            type: 'object'
        };
        completeDiscMap[exports.discriminatorKeyMap] = discMap;
        targetSchema.allOf.push(completeDiscMap);
    } else {
        targetSchema[exports.discriminatorKeyMap] = discMap;
    }
}

function _createFlattenedSchema(sourceSchema) {
    var flattenedSchema;
    if (sourceSchema.type === 'array') {
        flattenedSchema = _.clone(sourceSchema);
        flattenedSchema.items = _createFlattenedSchema(sourceSchema.items);
    } else if (Array.isArray(sourceSchema.allOf)) {
        flattenedSchema = _doFlatMerge({}, sourceSchema);
    } else if (sourceSchema.type === 'object') {
        flattenedSchema = _.cloneDeep(sourceSchema);
    } else {
        flattenedSchema = _.clone(sourceSchema);
    }
    return flattenedSchema;

    function _doFlatMerge(target, source) {
        if (Array.isArray(source.allOf)) {
            source.allOf.forEach(function (duckType) {
                _doFlatMerge(target, duckType);
            });
            return target;
        }

        // we have to handle merging the array `required` on our own
        var reqArr = _.union(target.required, source.required);
        _.merge(target, source);
        target.required = reqArr;
        return target;
    }
}

function prepareDefinitionsForPolymorphicValidation(definitions) {
    var modelNames = _.keys(definitions);
    modelNames.forEach(function (modelName) {
        var schema = definitions[modelName];
        _addDiscMapToSchema(schema, modelName);
        definitions[modelName] = _createFlattenedSchema(schema);
    });
}

//Try to determine if this is supposed to be a swagger file
//For now look for the required "swagger" field, which contains the version
function isSwaggerFile(json) {
    return json.swagger;
}

//There are two possible places for loading swagger.
//If we're part of a broader blueoak client-server project, blueoak is running
//from a 'server' directory, and there's a sibling directory named 'common' which contains the swagger directory.
//Otherwise we just look in the normal swagger folder within the project
function isBlueoakProject() {
    if ((path.basename(global.__appDir) !== 'server')) {
        return false;
    }
    try {
        return fs.statSync(path.resolve(global.__appDir, '../common/swagger')).isDirectory();
    } catch (err) {
        return false;
    }
}<|endoftext|>


<commit_before>/*
 * NAME: demo.js
 * AUTH: Brent Ely (https://github.com/gitbrent/)
 * DATE: 20190916
 * DESC: PptxGenJS feature demos for Node.js
 * REQS: npm 4.x + `npm install pptxgenjs`
 *
 * USAGE: `node demo.js`       (runs local tests with callbacks etc)
 * USAGE: `node demo.js All`   (runs all pre-defined tests in `../common/demos.js`)
 * USAGE: `node demo.js Text`  (runs pre-defined single test in `../common/demos.js`)
 */

// ============================================================================
const express = require('express'); // Not core - Only required for streaming
const app = express(); // Not core - Only required for streaming
let verboseMode = true;
let PptxGenJS;

function getTimestamp() {
	var dateNow = new Date();
	var dateMM = dateNow.getMonth() + 1; dateDD = dateNow.getDate(); dateYY = dateNow.getFullYear(), h = dateNow.getHours(); m = dateNow.getMinutes();
	return dateNow.getFullYear() +''+ (dateMM<=9 ? '0' + dateMM : dateMM) +''+ (dateDD<=9 ? '0' + dateDD : dateDD) + (h<=9 ? '0' + h : h) + (m<=9 ? '0' + m : m);
}
// ============================================================================

if (verboseMode) console.log(`
-------------
STARTING DEMO
-------------
`);

// STEP 1: Load pptxgenjs library
if ( (process.argv[2] && process.argv[2].toLowerCase() == '-local') || (process.argv[3] && process.argv[3].toLowerCase() == '-local' ) ) {
	// for LOCAL TESTING
	PptxGenJS = require('../../dist/pptxgen.cjs.js');
	if (verboseMode) console.log('---==~==[ LOCAL MODE ]==~==---');
	let pptx = new PptxGenJS();
	if (verboseMode) console.log(`* pptxgenjs ver: ${pptx.version}`);
}
else {
	PptxGenJS = require("pptxgenjs");
}
var pptx = new PptxGenJS();
var demo = require("../common/demos.js");

if (verboseMode) console.log(`* save location: ${__dirname}`);

// ============================================================================

// EX: Regular callback - will be sent the export filename once the file has been written to fs
function saveCallback(filename) {
	if (verboseMode) {
		console.log('`saveCallback()`: Export filename: '+ filename);
	}
}

// EX: JSZip callback - take the specified output (`data`) and do whatever
function jszipCallback(data) {
	if (verboseMode) {
		console.log('jszipCallback(): Here are 0-100 chars of `data`:\n');
		console.log( data.substring(0,100) );
	}
}

// EX: Callback that receives the PPT binary data - use this to stream file
function streamCallback(data) {
	var strFilename = "Node-Demo-Streamed-Callback.pptx";

	app.get('/', (req,res) => {
		res.writeHead(200, { 'Content-disposition':'attachment;filename='+strFilename, 'Content-Length':data.length });
		res.end(new Buffer(data, 'binary'));
	});

	app.listen(3000, () => {
		console.log('PptxGenJS Node Demo app listening on port 3000!');
		console.log('Visit: http://localhost:3000/');
		console.log('(press Ctrl-C to quit app)');
	});
}

// ============================================================================

// STEP 2: Run predefined test from `../common/demos.js` //-OR-// Local Tests (callbacks, etc.)
if ( process.argv.length == 3 ) {
	if ( process.argv[2].toLowerCase() == 'all' ) demo.runEveryTest();
	else demo.execGenSlidesFuncs( process.argv[2] );
}
else {
	// STEP 3: Omit an arg to run only these below
	var exportName = 'PptxGenJS_Demo_Node_'+getTimestamp();
	var pptx = new PptxGenJS();
	var slide = pptx.addSlide();
	slide.addText( 'New Node Presentation', {x:1.5, y:1.5, w:6, h:2, margin:0.1, fill:'FFFCCC'} );
	// Test that `pptxgen.shapes.js` was loaded/is available
	slide.addShape(pptx.shapes.OVAL_CALLOUT, { x:6, y:2, w:3, h:2, fill:'00FF00', line:'000000', lineSize:1 });

	// **NOTE**: Only uncomment one EXAMPLE at a time

	// EXAMPLE 1: Inline save (saves to the local directory where this process is running)
	//pptx.save( exportName+'-ex1' ); if (verboseMode) console.log('\nFile created:\n'+' * '+exportName+'-ex1');

	// EXAMPLE 2: Use an inline callback function
	pptx.writeFile(exportName+'-ex2')
		.catch(err => {throw err})
		.then(fileName => { console.log('Ex2 inline callback exported: '+fileName); } )
		.catch(err => { console.log('ERROR: '+err) });

	// EXAMPLE 3: Use defined callback function
	//pptx.save( exportName+'-ex3', saveCallback );

	// EXAMPLE 4: Save in various formats - JSZip offers: ['arraybuffer', 'base64', 'binarystring', 'blob', 'nodebuffer', 'uint8array']
	//pptx.save( exportName+'-jszip', jszipCallback, 'base64' );

	// EXAMPLE 5: Use callback with 'http' in filename to get content back instead of writing a file - use this for streaming, cloud, etc.
	// TEST: Use your browser and go to "http://localhost:3000/", then the file will be downloaded
	//pptx.save( 'http', streamCallback );

	// **NOTE** If you continue to use the `pptx` variable, new Slides will be added to the existing set
}

// ============================================================================

if (verboseMode) console.log(`
--------------
DEMO COMPLETE!
--------------
`);
<commit_msg>Fixed: demo works now<commit_after>/*
 * NAME: demo.js
 * AUTH: Brent Ely (https://github.com/gitbrent/)
 * DATE: 20190922
 * DESC: PptxGenJS feature demos for Node.js
 * REQS: npm 4.x + `npm install pptxgenjs`
 *
 * USAGE: `node demo.js`       (runs local tests with callbacks etc)
 * USAGE: `node demo.js All`   (runs all pre-defined tests in `../common/demos.js`)
 * USAGE: `node demo.js Text`  (runs pre-defined single test in `../common/demos.js`)
 * USAGE: `node demo.js Table -local` (uses local pptxgen.cjs.js file *TESTING ONLY*)
 */

// ============================================================================
let verboseMode = true;
let PptxGenJS;

function getTimestamp() {
	var dateNow = new Date();
	var dateMM = dateNow.getMonth() + 1;
	dateDD = dateNow.getDate();
	(dateYY = dateNow.getFullYear()), (h = dateNow.getHours());
	m = dateNow.getMinutes();
	return (
		dateNow.getFullYear() +
		"" +
		(dateMM <= 9 ? "0" + dateMM : dateMM) +
		"" +
		(dateDD <= 9 ? "0" + dateDD : dateDD) +
		(h <= 9 ? "0" + h : h) +
		(m <= 9 ? "0" + m : m)
	);
}
// ============================================================================

if (verboseMode)
	console.log(`
-------------
STARTING DEMO
-------------
`);

// STEP 1: Load pptxgenjs library
if (process.argv.length == 4 && process.argv[3].toLowerCase() == "-local") {
	// for LOCAL TESTING
	PptxGenJS = require("../../dist/pptxgen.cjs.js");
	if (verboseMode) console.log("---==~==[ LOCAL MODE ]==~==---");
	let pptx = new PptxGenJS();
	if (verboseMode) console.log(`* pptxgenjs ver: ${pptx.version}`);
} else {
	PptxGenJS = require("pptxgenjs");
}
let pptx = new PptxGenJS();
let demo = require("../common/demos.js");

if (verboseMode) console.log(`* save location: ${__dirname}`);

// STEP 2: Run predefined test from `../common/demos.js` //-OR-// Local Tests (callbacks, etc.)
if (process.argv.length == 3 || process.argv.length == 4) {
	Promise.resolve()
		.then(() => {
			if (process.argv[2].toLowerCase() == "all") return demo.runEveryTest();
			return demo.execGenSlidesFuncs(process.argv[2]);
		})
		.catch(err => {
			throw err;
		})
		.then(fileName => {
			console.log("EX1 exported: " + fileName);
		})
		.catch(err => {
			console.log("ERROR: " + err);
		});
} else {
	// STEP 3: Omit an arg to run only these below
	let exportName = "PptxGenJS_Demo_Node_" + getTimestamp();
	let pptx = new PptxGenJS();
	let slide = pptx.addSlide();
	slide.addText("New Node Presentation", { x: 1.5, y: 1.5, w: 6, h: 2, margin: 0.1, fill: "FFFCCC" });
	slide.addShape(pptx.shapes.OVAL_CALLOUT, { x: 6, y: 2, w: 3, h: 2, fill: "00FF00", line: "000000", lineSize: 1 }); // Test shapes availablity

	// EXAMPLE 1: Saves output file to the local directory where this process is running
	pptx.writeFile(exportName + "-ex1.pptx")
		.catch(err => {
			throw err;
		})
		.then(fileName => {
			console.log("EX1 exported: " + fileName);
		})
		.catch(err => {
			console.log("ERROR: " + err);
		});

	// EXAMPLE 2: Save in various formats - JSZip offers: ['arraybuffer', 'base64', 'binarystring', 'blob', 'nodebuffer', 'uint8array']
	pptx.write("base64")
		.catch(err => {
			throw err;
		})
		.then(data => {
			console.log("write as base64: Here are 0-100 chars of `data`:\n");
			console.log(data.substring(0, 100));
		})
		.catch(err => {
			console.log("ERROR: " + err);
		});

	// **NOTE** If you continue to use the `pptx` variable, new Slides will be added to the existing set
}

// ============================================================================

if (verboseMode)
	console.log(`
--------------
DEMO COMPLETE!
--------------
`);
<|endoftext|>


<commit_before>'use strict';

var app = angular.module('myApp.controllers', [
  'myApp.authentications.controller',
  'myApp.brands.controller',
  'myApp.boxes.controller',
  'myApp.events.controller',
  'myApp.heartbeats.controller',
  'myApp.jobs.controller',
  'myApp.invoices.controller',
  'myApp.locations.controller',
  'myApp.port_forwards.controller',
  'myApp.reports.controller',
  'myApp.speedtests.controller',
  'myApp.splash_pages.controller',
  'myApp.registrations.controller',
  'myApp.zones.controller',
  'myApp.users.controller',
  'myApp.vouchers.controller'
]);

app.controller('MainCtrl', ['$rootScope', '$scope', '$localStorage', '$window', '$location', '$routeParams', 'AccessToken', 'RefreshToken', 'Auth', 'API_END_POINT', '$pusher', '$route', 'onlineStatus', '$cookies', 'Brand', 'locationHelper', 'BrandName', 'CTLogin', 'User', 'Me', 'AUTH_URL', 'menu', 'designer', '$mdSidenav', 'docs', '$mdMedia', '$q', 'INTERCOM', 'PUSHER', 'gettextCatalog', 'Translate', 'COMMITHASH',

  function ($rootScope, $scope, $localStorage, $window, $location, $routeParams, AccessToken, RefreshToken, Auth, API, $pusher, $route, onlineStatus, $cookies, Brand, locationHelper, BrandName, CTLogin, User, Me, AUTH_URL, menu, designer, $mdSidenav, docs, $mdMedia, $q, INTERCOM, PUSHER, gettextCatalog, Translate, COMMITHASH) {

    var domain = 'ctapp.io';

    $scope.brandName  = BrandName;
    $scope.commit     = COMMITHASH;
    $scope.ct_login   = CTLogin;

    function isOpen(section) {
      return (menu.isSectionSelected(section) && menu.isOpen());
    }

    $scope.toggle = function(section){
      $mdSidenav(section || 'left').toggle();
    };

    var toggleOpen = function(){
      if ($mdMedia('gt-sm')) {
        vm.menu.isOpenLeft = false;
        menu.isOpen = !menu.isOpen;
        vm._ctm = !vm._ctm;
        $cookies.put('_ctm', !menu.isOpen);
      } else {
        vm.menu.isOpenLeft = !vm.menu.isOpenLeft;
      }
      $(window).trigger('resize');
    };

    function toggle(section) {
      menu.toggleSelectSection(section);
    }

    var vm = this;

    vm._ctm = $cookies.get('_ctm');
    vm.menu = menu;

    vm.designer = designer;
    vm.toggle = toggle;

    if ($cookies.get('_ctm') === 'true') {
      vm.menu.isOpenLeft = false;
      vm.menu.isOpen = false;
    }

    vm.menu.main = [];
    vm.menu.reports = [];
    vm.settingsMenu = [];
    vm.menuRight = [];

    vm.menu.reports.push({
      title: gettextCatalog.getString('Reports'),
      type: 'link',
      link: '/#/reports',
      icon: 'timeline'
    });

    vm.menu.reports.push({
      title: gettextCatalog.getString('Audit'),
      type: 'link',
      link: '/#/audit',
      icon: 'assignment'
    });

    vm.status = {
      isFirstOpen: true,
      isFirstDisabled: false
    };

    vm.menuRight.push({
      name: gettextCatalog.getString('Home'),
      link: '/#/',
      type: 'link',
      icon: 'home'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Locations'),
      link: '/#/locations',
      type: 'link',
      icon: 'business'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Events'),
      link: '/#/events',
      type: 'link',
      icon: 'warning'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Reports'),
      link: '/#/reports',
      type: 'link',
      icon: 'timeline'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Audit'),
      link: '/#/audit',
      type: 'link',
      icon: 'assignment'
    });

    vm.menuRight.push({
      type: 'divider',
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Developer'),
      link: '/#/apps',
      type: 'link',
      icon: 'android'
    });

    // Permissions //
    vm.menuRight.push({
      name: gettextCatalog.getString('Documentation'),
      link: 'http://docs.cucumberwifi.io',
      type: 'link',
      target: '_blank',
      icon: 'account_balance'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Discussions'),
      link: 'https://discuss.cucumberwifi.io',
      target: '_blank',
      type: 'link',
      icon: 'forum'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Support'),
      icon: 'get_app',
      id: 'intercom'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Downloads'),
      link: '/#/downloads',
      type: 'link',
      icon: 'get_app'
    });

    vm.menuRight.push({
      type: 'divider',
    });

    $scope.toggleOpen = toggleOpen;

    $scope.$on('logout', function(args) {
      logout().then(function(response) {
        $route.reload();
        console.log('Refreshing Token.');
      }, function() {
        Auth.fullLogin();
      });
    });

    var logout = function(args) {
      var deferred = $q.defer();
      var user = $localStorage.user;
      var path = $location.path();
      AccessToken.del();
      if ( user && user.refresh ) {
        var host  = locationHelper.domain();
        $cookies.put('_ctp', JSON.stringify(path) );
        RefreshToken.refresh(user.refresh, path).then(function(response) {
          Auth.refresh(response).then(function(a){
            deferred.resolve();
          });
        }, function(er) {
          deferred.reject();
        });
      } else {
        deferred.reject();
      }
      return deferred.promise;
    };

    $scope.$on('login', function(args,event) {
      console.log('Logging in...');
      var cname = event.data.cname;
      if ((cname === null || cname === '') && event.data.url !== 'default') {
        var sub   = locationHelper.subdomain();
        var host  = locationHelper.domain();
        // put back when on public //
        // if (event.data.url && (sub !== event.data.url)) {
        //   var newUrl = locationHelper.reconstructed(event.data.url);
        //   var reconstructed = newUrl + '/#/switch?return_to=' + event.path;
        //   $cookies.put('event', JSON.stringify(event), {'domain': host});
        //   window.location = reconstructed;
        // } else {
          doLogin(event);
        // }
      } else {
        doLogin(event);
      }
    });

    function doLogin(event) {
      Auth.login(event.data).then(function(a) {
        var path;
        $scope.loggedIn = true;
        if (event.path) {
          path = event.path;
        } else {
          var raw = $cookies.get('_ctp');
          if (raw) {
            try{
              path = JSON.parse(raw);
            }catch(e){
              console.log('Couldn\'t parse JSON to redirect');
            }
          }
        }
        $location.path(path || '/').search(event.search || {});
        if (event !== undefined && event.rdir !== undefined) {
          $location.search({rdir: event.rdir});
        }
        $cookies.remove('_ctp');
        $scope.ct_login = undefined;
        Translate.load();
      });
    }

    $scope.$on('intercom', function(args,event) {
      if (Auth.currentUser() && (Auth.currentUser().chat_enabled !== false && Auth.currentUser().user_hash !== undefined)) {
        vm.menu.Intercom = true;
        var settings = {
            app_id: INTERCOM,
            user_id: Auth.currentUser().accountId,
            email: Auth.currentUser().email,
            name: Auth.currentUser().username,
            created_at: Auth.currentUser().created_at,
            user_hash: Auth.currentUser().user_hash,
            brand_name: Auth.currentUser().url,
            cname: Auth.currentUser().cname,
            sense_active: Auth.currentUser().sense_active,
            plan_name: Auth.currentUser().plan_name,
            paid_plan: Auth.currentUser().paid_plan,
            locs: Auth.currentUser().locs,
            version: '2'
        };
        settings.widget = {
          activator: '#intercom'
        };
        window.Intercom('boot', settings);
      }
    });

    function menuPush() {
      if (vm.menu.main.length === 0) {
        vm.menu.main.push({
          title: gettextCatalog.getString('Locations'),
          type: 'link',
          link: '/#/locations',
          icon: 'business'
        });

        vm.menu.main.push({
          title: gettextCatalog.getString('Events'),
          type: 'link',
          link: '/#/events',
          icon: 'warning'
        });

        if (Auth.currentUser() && Auth.currentUser().guest === false) {

          vm.menu.main.push({
            title: gettextCatalog.getString('Brands'),
            type: 'link',
            link: '/#/brands',
            icon: 'branding_watermark'
          });

          vm.menu.main.push({
            title: gettextCatalog.getString('Users'),
            type: 'link',
            link: '/#/users',
            icon: 'people'
          });

          vm.menuRight.push({
            name: gettextCatalog.getString('Brands'),
            type: 'link',
            link: '/#/brands',
            icon: 'branding_watermark'
          });

          vm.menuRight.push({
            name: gettextCatalog.getString('Users'),
            type: 'link',
            link: '/#/users',
            icon: 'people'
          });

          vm.menuRight.push({
            type: 'divider',
          });
        }

        vm.menuRight.push({
          name: gettextCatalog.getString('Profile'),
          link: '/#/me',
          type: 'link',
          icon: 'face'
        });

        vm.menuRight.push({
          type: 'divider',
        });
      }
    }

    var setDefaultImages = function(sub) {
      $scope.brandName.name = 'Cucumber';
    };

    function getBrand(sub, cname) {
      if (Auth.currentUser() && Auth.currentUser().url !== null) {
        sub = Auth.currentUser().url;
      }

      Brand.query({}, {
        id: sub,
        cname: cname,
        type: 'showcase'
      }).$promise.then(function(results) {
        // Decide to switch the brand here
        // Can we turn Cucumber into a variable so we don't just set
        // Maybe use the config files - Simon TBD //

        var t = $cookies.get('_ctt');
        // if (t !== results.theme_primary) {
        $cookies.put('_ctt', results.theme_primary + '.' + results.theme_accent);
        // }
        $scope.brandName.name  = results.brand_name || 'Cucumber';
        // Maybe use the config files - Simon TBD //
        $scope.brandName.admin = results.admin;
        $scope.brandName.url   = results.url;
        $scope.brandName.id    = results.id;
      }, function() {
        setDefaultImages(sub);
      });
    }

    var removeCtCookie = function() {
      $cookies.remove('_ct', { domain: domain });
    };

    function getSubdomain () {
      var sub   = locationHelper.subdomain();
      var host  = locationHelper.domain();
      var parts = $location.host().split('.');
      var cname;
      if (host !== 'ctapp.io' && host !== 'ctapp.dev') {
        getBrand(host, true);
      }
      else if (parts.length === 3) {
        sub = parts[0];
        if (sub !== 'my' && sub !== 'dashboard' ) {
          getBrand(sub);
          return;
        }
        setDefaultImages();
      } else {
        console.log('Domain error occured');
      }
    }

    function getMe() {
      Me.get({}).$promise.then(function(res) {
        Auth.login(res).then(function(a) {
          $scope.user = Auth.currentUser();
          $scope.loggedIn = true;
          menuPush();
        });
      });
    }

    function routeChangeStart() {
      var pusher;

      // If user logged in, load pusher
      if (Auth.currentUser() && Auth.currentUser().key !== null) {
        $scope.$broadcast('intercom', {hi: 'user'});
        window.client = new Pusher(PUSHER, {
          authEndpoint: API + '/pusherAuth?token=' + Auth.currentUser().key
        });
        pusher = $pusher(client);
      }

      var a = AccessToken.get();
      if ( (!Auth.currentUser() && a ) || Auth.currentUser() && (Auth.currentUser().url !== 'default' )) {
        getMe();
      }
      getSubdomain();

      // Adds followup link if using referral
      var cookie = $cookies.get('_ct', { 'domain': domain });
      if (cookie && Auth.currentUser() && Auth.currentUser().id) {
        User.distro({dst: cookie}).$promise.then(function(result) {
          removeCtCookie();
        }, function() {
          removeCtCookie();
        });
      }

      // Load the translations
      Translate.load();
    }

    var setLoggedIn = function(isLoggedIn) {
      $scope.loggedIn = isLoggedIn;
      return isLoggedIn;
    };

    setLoggedIn(AccessToken.get() !== undefined);

    $scope.logout = function() {
      Auth.logout();
    };

    // $scope.onlineStatus = onlineStatus;
    // if ( $scope.loggedIn ) {
      // $scope.$watch('onlineStatus.isOnline()', function(online) {
      //   $scope.online_status_string = online ? 'online' : 'offline';
      //   if ($scope.online_status_string === 'offline') {
      //     $scope.open('md');
      //   } else if ($scope.online_status_string === 'online' ){
      //     // if ($scope.modalInstance !== undefined) {
      //     //   $scope.modalInstance.close();
      //     // }
      //   }
      // });
    // }

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      routeChangeStart();
    });

}]);

app.controller( 'ParentCtrl', function ParentCtrl($scope, onlineStatus) {
});
<commit_msg>Removes distro stuff for later date<commit_after>'use strict';

var app = angular.module('myApp.controllers', [
  'myApp.authentications.controller',
  'myApp.brands.controller',
  'myApp.boxes.controller',
  'myApp.events.controller',
  'myApp.heartbeats.controller',
  'myApp.jobs.controller',
  'myApp.invoices.controller',
  'myApp.locations.controller',
  'myApp.port_forwards.controller',
  'myApp.reports.controller',
  'myApp.speedtests.controller',
  'myApp.splash_pages.controller',
  'myApp.registrations.controller',
  'myApp.zones.controller',
  'myApp.users.controller',
  'myApp.vouchers.controller'
]);

app.controller('MainCtrl', ['$rootScope', '$scope', '$localStorage', '$window', '$location', '$routeParams', 'AccessToken', 'RefreshToken', 'Auth', 'API_END_POINT', '$pusher', '$route', 'onlineStatus', '$cookies', 'Brand', 'locationHelper', 'BrandName', 'CTLogin', 'User', 'Me', 'AUTH_URL', 'menu', 'designer', '$mdSidenav', 'docs', '$mdMedia', '$q', 'INTERCOM', 'PUSHER', 'gettextCatalog', 'Translate', 'COMMITHASH',

  function ($rootScope, $scope, $localStorage, $window, $location, $routeParams, AccessToken, RefreshToken, Auth, API, $pusher, $route, onlineStatus, $cookies, Brand, locationHelper, BrandName, CTLogin, User, Me, AUTH_URL, menu, designer, $mdSidenav, docs, $mdMedia, $q, INTERCOM, PUSHER, gettextCatalog, Translate, COMMITHASH) {

    var domain = 'ctapp.io';

    $scope.brandName  = BrandName;
    $scope.commit     = COMMITHASH;
    $scope.ct_login   = CTLogin;

    function isOpen(section) {
      return (menu.isSectionSelected(section) && menu.isOpen());
    }

    $scope.toggle = function(section){
      $mdSidenav(section || 'left').toggle();
    };

    var toggleOpen = function(){
      if ($mdMedia('gt-sm')) {
        vm.menu.isOpenLeft = false;
        menu.isOpen = !menu.isOpen;
        vm._ctm = !vm._ctm;
        $cookies.put('_ctm', !menu.isOpen);
      } else {
        vm.menu.isOpenLeft = !vm.menu.isOpenLeft;
      }
      $(window).trigger('resize');
    };

    function toggle(section) {
      menu.toggleSelectSection(section);
    }

    var vm = this;

    vm._ctm = $cookies.get('_ctm');
    vm.menu = menu;

    vm.designer = designer;
    vm.toggle = toggle;

    if ($cookies.get('_ctm') === 'true') {
      vm.menu.isOpenLeft = false;
      vm.menu.isOpen = false;
    }

    vm.menu.main = [];
    vm.menu.reports = [];
    vm.settingsMenu = [];
    vm.menuRight = [];

    vm.menu.reports.push({
      title: gettextCatalog.getString('Reports'),
      type: 'link',
      link: '/#/reports',
      icon: 'timeline'
    });

    vm.menu.reports.push({
      title: gettextCatalog.getString('Audit'),
      type: 'link',
      link: '/#/audit',
      icon: 'assignment'
    });

    vm.status = {
      isFirstOpen: true,
      isFirstDisabled: false
    };

    vm.menuRight.push({
      name: gettextCatalog.getString('Home'),
      link: '/#/',
      type: 'link',
      icon: 'home'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Locations'),
      link: '/#/locations',
      type: 'link',
      icon: 'business'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Events'),
      link: '/#/events',
      type: 'link',
      icon: 'warning'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Reports'),
      link: '/#/reports',
      type: 'link',
      icon: 'timeline'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Audit'),
      link: '/#/audit',
      type: 'link',
      icon: 'assignment'
    });

    vm.menuRight.push({
      type: 'divider',
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Developer'),
      link: '/#/apps',
      type: 'link',
      icon: 'android'
    });

    // Permissions //
    vm.menuRight.push({
      name: gettextCatalog.getString('Documentation'),
      link: 'http://docs.cucumberwifi.io',
      type: 'link',
      target: '_blank',
      icon: 'account_balance'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Discussions'),
      link: 'https://discuss.cucumberwifi.io',
      target: '_blank',
      type: 'link',
      icon: 'forum'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Support'),
      icon: 'get_app',
      id: 'intercom'
    });

    vm.menuRight.push({
      name: gettextCatalog.getString('Downloads'),
      link: '/#/downloads',
      type: 'link',
      icon: 'get_app'
    });

    vm.menuRight.push({
      type: 'divider',
    });

    $scope.toggleOpen = toggleOpen;

    $scope.$on('logout', function(args) {
      logout().then(function(response) {
        $route.reload();
        console.log('Refreshing Token.');
      }, function() {
        Auth.fullLogin();
      });
    });

    var logout = function(args) {
      var deferred = $q.defer();
      var user = $localStorage.user;
      var path = $location.path();
      AccessToken.del();
      if ( user && user.refresh ) {
        var host  = locationHelper.domain();
        $cookies.put('_ctp', JSON.stringify(path) );
        RefreshToken.refresh(user.refresh, path).then(function(response) {
          Auth.refresh(response).then(function(a){
            deferred.resolve();
          });
        }, function(er) {
          deferred.reject();
        });
      } else {
        deferred.reject();
      }
      return deferred.promise;
    };

    $scope.$on('login', function(args,event) {
      console.log('Logging in...');
      var cname = event.data.cname;
      if ((cname === null || cname === '') && event.data.url !== 'default') {
        var sub   = locationHelper.subdomain();
        var host  = locationHelper.domain();
        // put back when on public //
        // if (event.data.url && (sub !== event.data.url)) {
        //   var newUrl = locationHelper.reconstructed(event.data.url);
        //   var reconstructed = newUrl + '/#/switch?return_to=' + event.path;
        //   $cookies.put('event', JSON.stringify(event), {'domain': host});
        //   window.location = reconstructed;
        // } else {
          doLogin(event);
        // }
      } else {
        doLogin(event);
      }
    });

    function doLogin(event) {
      Auth.login(event.data).then(function(a) {
        var path;
        $scope.loggedIn = true;
        if (event.path) {
          path = event.path;
        } else {
          var raw = $cookies.get('_ctp');
          if (raw) {
            try{
              path = JSON.parse(raw);
            }catch(e){
              console.log('Couldn\'t parse JSON to redirect');
            }
          }
        }
        $location.path(path || '/').search(event.search || {});
        if (event !== undefined && event.rdir !== undefined) {
          $location.search({rdir: event.rdir});
        }
        $cookies.remove('_ctp');
        $scope.ct_login = undefined;
        Translate.load();
      });
    }

    $scope.$on('intercom', function(args,event) {
      if (Auth.currentUser() && (Auth.currentUser().chat_enabled !== false && Auth.currentUser().user_hash !== undefined)) {
        vm.menu.Intercom = true;
        var settings = {
            app_id: INTERCOM,
            user_id: Auth.currentUser().accountId,
            email: Auth.currentUser().email,
            name: Auth.currentUser().username,
            created_at: Auth.currentUser().created_at,
            user_hash: Auth.currentUser().user_hash,
            brand_name: Auth.currentUser().url,
            cname: Auth.currentUser().cname,
            sense_active: Auth.currentUser().sense_active,
            plan_name: Auth.currentUser().plan_name,
            paid_plan: Auth.currentUser().paid_plan,
            locs: Auth.currentUser().locs,
            version: '2'
        };
        settings.widget = {
          activator: '#intercom'
        };
        window.Intercom('boot', settings);
      }
    });

    function menuPush() {
      if (vm.menu.main.length === 0) {
        vm.menu.main.push({
          title: gettextCatalog.getString('Locations'),
          type: 'link',
          link: '/#/locations',
          icon: 'business'
        });

        vm.menu.main.push({
          title: gettextCatalog.getString('Events'),
          type: 'link',
          link: '/#/events',
          icon: 'warning'
        });

        if (Auth.currentUser() && Auth.currentUser().guest === false) {

          vm.menu.main.push({
            title: gettextCatalog.getString('Brands'),
            type: 'link',
            link: '/#/brands',
            icon: 'branding_watermark'
          });

          vm.menu.main.push({
            title: gettextCatalog.getString('Users'),
            type: 'link',
            link: '/#/users',
            icon: 'people'
          });

          vm.menuRight.push({
            name: gettextCatalog.getString('Brands'),
            type: 'link',
            link: '/#/brands',
            icon: 'branding_watermark'
          });

          vm.menuRight.push({
            name: gettextCatalog.getString('Users'),
            type: 'link',
            link: '/#/users',
            icon: 'people'
          });

          vm.menuRight.push({
            type: 'divider',
          });
        }

        vm.menuRight.push({
          name: gettextCatalog.getString('Profile'),
          link: '/#/me',
          type: 'link',
          icon: 'face'
        });

        vm.menuRight.push({
          type: 'divider',
        });
      }
    }

    var setDefaultImages = function(sub) {
      $scope.brandName.name = 'Cucumber';
    };

    function getBrand(sub, cname) {
      if (Auth.currentUser() && Auth.currentUser().url !== null) {
        sub = Auth.currentUser().url;
      }

      Brand.query({}, {
        id: sub,
        cname: cname,
        type: 'showcase'
      }).$promise.then(function(results) {
        // Decide to switch the brand here
        // Can we turn Cucumber into a variable so we don't just set
        // Maybe use the config files - Simon TBD //

        var t = $cookies.get('_ctt');
        // if (t !== results.theme_primary) {
        $cookies.put('_ctt', results.theme_primary + '.' + results.theme_accent);
        // }
        $scope.brandName.name  = results.brand_name || 'Cucumber';
        // Maybe use the config files - Simon TBD //
        $scope.brandName.admin = results.admin;
        $scope.brandName.url   = results.url;
        $scope.brandName.id    = results.id;
      }, function() {
        setDefaultImages(sub);
      });
    }

    var removeCtCookie = function() {
      $cookies.remove('_ct', { domain: domain });
    };

    function getSubdomain () {
      var sub   = locationHelper.subdomain();
      var host  = locationHelper.domain();
      var parts = $location.host().split('.');
      var cname;
      if (host !== 'ctapp.io' && host !== 'ctapp.dev') {
        getBrand(host, true);
      }
      else if (parts.length === 3) {
        sub = parts[0];
        if (sub !== 'my' && sub !== 'dashboard' ) {
          getBrand(sub);
          return;
        }
        setDefaultImages();
      } else {
        console.log('Domain error occured');
      }
    }

    function getMe() {
      Me.get({}).$promise.then(function(res) {
        Auth.login(res).then(function(a) {
          $scope.user = Auth.currentUser();
          $scope.loggedIn = true;
          menuPush();
        });
      });
    }

    function routeChangeStart() {
      var pusher;

      // If user logged in, load pusher
      if (Auth.currentUser() && Auth.currentUser().key !== null) {
        $scope.$broadcast('intercom', {hi: 'user'});
        window.client = new Pusher(PUSHER, {
          authEndpoint: API + '/pusherAuth?token=' + Auth.currentUser().key
        });
        pusher = $pusher(client);
      }

      var a = AccessToken.get();
      if ( (!Auth.currentUser() && a ) || Auth.currentUser() && (Auth.currentUser().url !== 'default' )) {
        getMe();
      }
      getSubdomain();

      // Adds followup link if using referral
      // var cookie = $cookies.get('_ct', { 'domain': domain });
      // if (cookie && Auth.currentUser() && Auth.currentUser().id) {
      //   User.distro({dst: cookie}).$promise.then(function(result) {
      //     removeCtCookie();
      //   }, function() {
      //     removeCtCookie();
      //   });
      // }

      // Load the translations
      Translate.load();
    }

    var setLoggedIn = function(isLoggedIn) {
      $scope.loggedIn = isLoggedIn;
      return isLoggedIn;
    };

    setLoggedIn(AccessToken.get() !== undefined);

    $scope.logout = function() {
      Auth.logout();
    };

    // $scope.onlineStatus = onlineStatus;
    // if ( $scope.loggedIn ) {
      // $scope.$watch('onlineStatus.isOnline()', function(online) {
      //   $scope.online_status_string = online ? 'online' : 'offline';
      //   if ($scope.online_status_string === 'offline') {
      //     $scope.open('md');
      //   } else if ($scope.online_status_string === 'online' ){
      //     // if ($scope.modalInstance !== undefined) {
      //     //   $scope.modalInstance.close();
      //     // }
      //   }
      // });
    // }

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      routeChangeStart();
    });

}]);

app.controller( 'ParentCtrl', function ParentCtrl($scope, onlineStatus) {
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const _ = require('lodash');
const io = require('socket.io-client');
const deepEqual = require('deep-equal');
const debug = require('debug')('socketio');
const engineUtil = require('./engine_util');
const EngineHttp = require('./engine_http');
const template = engineUtil.template;
module.exports = SocketIoEngine;

function SocketIoEngine(script) {
  this.config = script.config;

  if (script.config.socketio) {
    if (script.config.socketio.transports) {
      this.transports = {
        transports: script.config.socketio.transports
      };
    }

    if (script.config.socketio.query) {
      this.query = {
        query: script.config.socketio.query
      };
    }

	if (script.config.socketio.path) {
      this.path = {
        path: script.config.socketio.path
      };
    }
  }

  this.httpDelegate = new EngineHttp(script);
}

SocketIoEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;
  let tasks = _.map(scenarioSpec.flow, function(rs) {
    if (rs.think) {
      return engineUtil.createThink(rs, _.get(self.config, 'defaults.think', {}));
    }
    return self.step(rs, ee);
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

function markEndTime(ee, context, startedAt) {
  let endedAt = process.hrtime(startedAt);
  let delta = (endedAt[0] * 1e9) + endedAt[1];
  ee.emit('response', delta, 0, context._uid);
}

function isResponseRequired(spec) {
  return (spec.emit && spec.emit.response && spec.emit.response.channel);
}

function processResponse(ee, data, response, context, callback) {
  // Do we have supplied data to validate?
  if (response.data && !deepEqual(data, response.data)) {
    debug(data);
    let err = 'data is not valid';
    ee.emit('error', err);
    return callback(err, context);
  }

  // If no capture or match specified, then we consider it a success at this point...
  if (!response.capture && !response.match) {
    return callback(null, context);
  }

  // Construct the (HTTP) response...
  let fauxResponse = {body: JSON.stringify(data)};

  // Handle the capture or match clauses...
  engineUtil.captureOrMatch(response, fauxResponse, context, function(err, result) {
    // Were we unable to invoke captureOrMatch?
    if (err) {
      debug(data);
      ee.emit('error', err);
      return callback(err, context);
    }

    // Do we have any failed matches?
    let haveFailedMatches = _.some(result.matches, function(v, k) {
      return !v.success;
    });

    // How to handle failed matches?
    if (haveFailedMatches) {
      // TODO: Should log the details of the match somewhere
      ee.emit('error', 'Failed match');
      return callback(new Error('Failed match'), context);
    } else {
      // Emit match events...
      _.each(result.matches, function(v, k) {
        ee.emit('match', v.success, {
          expected: v.expected,
          got: v.got,
          expression: v.expression
        });
      });

      // Populate the context with captured values
      _.each(result.captures, function(v, k) {
        context.vars[k] = v;
      });

      // Replace the base object context
      // Question: Should this be JSON object or String?
      context.vars.$ = fauxResponse.body;

      // Increment the success count...
      context._successCount++;

      return callback(null, context);
    }
  });
}

SocketIoEngine.prototype.step = function (requestSpec, ee) {
  let self = this;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      if (!rs.emit) {
        return self.httpDelegate.step(rs, ee);
      }
      return self.step(rs, ee);
    });

    return engineUtil.createLoopWithCount(requestSpec.count || -1, steps);
  }

  let f = function(context, callback) {
    // Only process emit requests; delegate the rest to the HTTP engine (or think utility)
    if (requestSpec.think) {
      return engineUtil.createThink(requestSpec, _.get(self.config, 'defaults.think', {}));
    }
    if (!requestSpec.emit) {
      let delegateFunc = self.httpDelegate.step(requestSpec, ee);
      return delegateFunc(context, callback);
    }
    ee.emit('request');
    let startedAt = process.hrtime();
    let socketio = context.sockets[requestSpec.emit.namespace] || null;

    if (!(requestSpec.emit && requestSpec.emit.channel && socketio)) {
      return ee.emit('error', 'invalid arguments');
    }

    let outgoing = {
      channel: template(requestSpec.emit.channel, context),
      data: template(requestSpec.emit.data, context)
    };

    if (isResponseRequired(requestSpec)) {
      let response = {
        channel: template(requestSpec.emit.response.channel, context),
        data: template(requestSpec.emit.response.data, context),
        capture: template(requestSpec.emit.response.capture, context),
        match: template(requestSpec.emit.response.match, context)
      };
      // Listen for the socket.io response on the specified channel
      let done = false;
      socketio.on(response.channel, function receive(data) {
        done = true;
        processResponse(ee, data, response, context, function(err) {
          if (!err) {
            markEndTime(ee, context, startedAt);
          }
          // Stop listening on the response channel
          socketio.off(response.channel);
          return callback(err, context);
        });
      });
      // Send the data on the specified socket.io channel
      socketio.emit(outgoing.channel, outgoing.data);
      // If we don't get a response within the timeout, fire an error
      let waitTime = self.config.timeout || 10;
      waitTime *= 1000;
      setTimeout(function responseTimeout() {
        if (!done) {
          let err = 'response timeout';
          ee.emit('error', err);
          return callback(err, context);
        }
      }, waitTime);
    } else {
      // No return data is expected, so emit without a listener
      socketio.emit(outgoing.channel, outgoing.data);
      markEndTime(ee, context, startedAt);
      return callback(null, context);
    }
  };

  function preStep(context, callback){
    // Set default namespace in emit action
    requestSpec.emit.namespace = template(requestSpec.emit.namespace, context) || "/";

    self.loadContextSocket(requestSpec.emit.namespace, context, function(err, socket){
      if(err) {
        debug(err);
        ee.emit('error', err.message);
        return callback(err, context);
      }

      return f(context, callback);
    });
  }

  if(requestSpec.emit) {
    return preStep;
  } else {
    return f;
  }
};

SocketIoEngine.prototype.loadContextSocket = function(namespace, context, cb) {
  context.sockets = context.sockets || {};

  if(!context.sockets[namespace]) {
    let target = this.config.target + namespace;
    let tls = this.config.tls || {};
    let transports = this.transports || {};
    let path = this.path || {};
    let query = this.query || {};
    let options = _.extend(
      {},
      tls,
      transports,
      query,
      path
    );

    context.sockets[namespace] = io.connect(target, options);

    context.sockets[namespace].once('connect', function(){
      cb(null, context.sockets[namespace]);
    });
    context.sockets[namespace].once('connect_error', function(err){
      cb(err, null);
    });
  } else {
    cb(null, context.sockets[namespace]);
  }
};

SocketIoEngine.prototype.closeContextSockets = function (context) {
  // if(context.socketio) {
  //   context.socketio.disconnect();
  // }
  if(context.sockets && Object.keys(context.sockets).length > 0) {
    var namespaces = Object.keys(context.sockets);
    namespaces.forEach(function(namespace){
      context.sockets[namespace].disconnect();
    });
  }
};

SocketIoEngine.prototype.compile = function (tasks, scenarioSpec, ee) {
  let config = this.config;
  let self = this;

  function zero(callback, context) {
    ee.emit('started');
    return callback(null, context);
  }

  return function scenario(initialContext, callback) {
    initialContext._successCount = 0;
    initialContext._pendingRequests = _.size(
        _.reject(scenarioSpec, function(rs) {
          return (typeof rs.think === 'number');
        }));

    let steps = _.flatten([
      function z(cb) {
        return zero(cb, initialContext);
      },
      tasks
    ]);

    async.waterfall(
        steps,
        function scenarioWaterfallCb(err, context) {
          if (err) {
            debug(err);
          }
          if (context) {
            self.closeContextSockets(context);
          }
          return callback(err, context);
        });
  };
};
<commit_msg>fix(socketio): Virtual users connect to default namespace on launch<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

const async = require('async');
const _ = require('lodash');
const io = require('socket.io-client');
const deepEqual = require('deep-equal');
const debug = require('debug')('socketio');
const engineUtil = require('./engine_util');
const EngineHttp = require('./engine_http');
const template = engineUtil.template;
module.exports = SocketIoEngine;

function SocketIoEngine(script) {
  this.config = script.config;

  if (script.config.socketio) {
    if (script.config.socketio.transports) {
      this.transports = {
        transports: script.config.socketio.transports
      };
    }

    if (script.config.socketio.query) {
      this.query = {
        query: script.config.socketio.query
      };
    }

	if (script.config.socketio.path) {
      this.path = {
        path: script.config.socketio.path
      };
    }
  }

  this.httpDelegate = new EngineHttp(script);
}

SocketIoEngine.prototype.createScenario = function(scenarioSpec, ee) {
  var self = this;
  let tasks = _.map(scenarioSpec.flow, function(rs) {
    if (rs.think) {
      return engineUtil.createThink(rs, _.get(self.config, 'defaults.think', {}));
    }
    return self.step(rs, ee);
  });

  return self.compile(tasks, scenarioSpec.flow, ee);
};

function markEndTime(ee, context, startedAt) {
  let endedAt = process.hrtime(startedAt);
  let delta = (endedAt[0] * 1e9) + endedAt[1];
  ee.emit('response', delta, 0, context._uid);
}

function isResponseRequired(spec) {
  return (spec.emit && spec.emit.response && spec.emit.response.channel);
}

function processResponse(ee, data, response, context, callback) {
  // Do we have supplied data to validate?
  if (response.data && !deepEqual(data, response.data)) {
    debug(data);
    let err = 'data is not valid';
    ee.emit('error', err);
    return callback(err, context);
  }

  // If no capture or match specified, then we consider it a success at this point...
  if (!response.capture && !response.match) {
    return callback(null, context);
  }

  // Construct the (HTTP) response...
  let fauxResponse = {body: JSON.stringify(data)};

  // Handle the capture or match clauses...
  engineUtil.captureOrMatch(response, fauxResponse, context, function(err, result) {
    // Were we unable to invoke captureOrMatch?
    if (err) {
      debug(data);
      ee.emit('error', err);
      return callback(err, context);
    }

    // Do we have any failed matches?
    let haveFailedMatches = _.some(result.matches, function(v, k) {
      return !v.success;
    });

    // How to handle failed matches?
    if (haveFailedMatches) {
      // TODO: Should log the details of the match somewhere
      ee.emit('error', 'Failed match');
      return callback(new Error('Failed match'), context);
    } else {
      // Emit match events...
      _.each(result.matches, function(v, k) {
        ee.emit('match', v.success, {
          expected: v.expected,
          got: v.got,
          expression: v.expression
        });
      });

      // Populate the context with captured values
      _.each(result.captures, function(v, k) {
        context.vars[k] = v;
      });

      // Replace the base object context
      // Question: Should this be JSON object or String?
      context.vars.$ = fauxResponse.body;

      // Increment the success count...
      context._successCount++;

      return callback(null, context);
    }
  });
}

SocketIoEngine.prototype.step = function (requestSpec, ee) {
  let self = this;

  if (requestSpec.loop) {
    let steps = _.map(requestSpec.loop, function(rs) {
      if (!rs.emit) {
        return self.httpDelegate.step(rs, ee);
      }
      return self.step(rs, ee);
    });

    return engineUtil.createLoopWithCount(requestSpec.count || -1, steps);
  }

  let f = function(context, callback) {
    // Only process emit requests; delegate the rest to the HTTP engine (or think utility)
    if (requestSpec.think) {
      return engineUtil.createThink(requestSpec, _.get(self.config, 'defaults.think', {}));
    }
    if (!requestSpec.emit) {
      let delegateFunc = self.httpDelegate.step(requestSpec, ee);
      return delegateFunc(context, callback);
    }
    ee.emit('request');
    let startedAt = process.hrtime();
    let socketio = context.sockets[requestSpec.emit.namespace] || null;

    if (!(requestSpec.emit && requestSpec.emit.channel && socketio)) {
      return ee.emit('error', 'invalid arguments');
    }

    let outgoing = {
      channel: template(requestSpec.emit.channel, context),
      data: template(requestSpec.emit.data, context)
    };

    if (isResponseRequired(requestSpec)) {
      let response = {
        channel: template(requestSpec.emit.response.channel, context),
        data: template(requestSpec.emit.response.data, context),
        capture: template(requestSpec.emit.response.capture, context),
        match: template(requestSpec.emit.response.match, context)
      };
      // Listen for the socket.io response on the specified channel
      let done = false;
      socketio.on(response.channel, function receive(data) {
        done = true;
        processResponse(ee, data, response, context, function(err) {
          if (!err) {
            markEndTime(ee, context, startedAt);
          }
          // Stop listening on the response channel
          socketio.off(response.channel);
          return callback(err, context);
        });
      });
      // Send the data on the specified socket.io channel
      socketio.emit(outgoing.channel, outgoing.data);
      // If we don't get a response within the timeout, fire an error
      let waitTime = self.config.timeout || 10;
      waitTime *= 1000;
      setTimeout(function responseTimeout() {
        if (!done) {
          let err = 'response timeout';
          ee.emit('error', err);
          return callback(err, context);
        }
      }, waitTime);
    } else {
      // No return data is expected, so emit without a listener
      socketio.emit(outgoing.channel, outgoing.data);
      markEndTime(ee, context, startedAt);
      return callback(null, context);
    }
  };

  function preStep(context, callback){
    // Set default namespace in emit action
    requestSpec.emit.namespace = template(requestSpec.emit.namespace, context) || "/";

    self.loadContextSocket(requestSpec.emit.namespace, context, function(err, socket){
      if(err) {
        debug(err);
        ee.emit('error', err.message);
        return callback(err, context);
      }

      return f(context, callback);
    });
  }

  if(requestSpec.emit) {
    return preStep;
  } else {
    return f;
  }
};

SocketIoEngine.prototype.loadContextSocket = function(namespace, context, cb) {
  context.sockets = context.sockets || {};

  if(!context.sockets[namespace]) {
    let target = this.config.target + namespace;
    let tls = this.config.tls || {};
    let transports = this.transports || {};
    let path = this.path || {};
    let query = this.query || {};
    let options = _.extend(
      {},
      tls,
      transports,
      query,
      path
    );

    context.sockets[namespace] = io.connect(target, options);

    context.sockets[namespace].once('connect', function(){
      cb(null, context.sockets[namespace]);
    });
    context.sockets[namespace].once('connect_error', function(err){
      cb(err, null);
    });
  } else {
    cb(null, context.sockets[namespace]);
  }
};

SocketIoEngine.prototype.closeContextSockets = function (context) {
  // if(context.socketio) {
  //   context.socketio.disconnect();
  // }
  if(context.sockets && Object.keys(context.sockets).length > 0) {
    var namespaces = Object.keys(context.sockets);
    namespaces.forEach(function(namespace){
      context.sockets[namespace].disconnect();
    });
  }
};

SocketIoEngine.prototype.compile = function (tasks, scenarioSpec, ee) {
  let config = this.config;
  let self = this;

  function zero(callback, context) {
    ee.emit('started');
    self.loadContextSocket('/', context, function done(err) {
      if (err) {
        ee.emit('error', err);
        return callback(err, context);
      }

      return callback(null, context);
    });
  }

  return function scenario(initialContext, callback) {
    initialContext._successCount = 0;
    initialContext._pendingRequests = _.size(
        _.reject(scenarioSpec, function(rs) {
          return (typeof rs.think === 'number');
        }));

    let steps = _.flatten([
      function z(cb) {
        return zero(cb, initialContext);
      },
      tasks
    ]);

    async.waterfall(
        steps,
        function scenarioWaterfallCb(err, context) {
          if (err) {
            debug(err);
          }
          if (context) {
            self.closeContextSockets(context);
          }
          return callback(err, context);
        });
  };
};
<|endoftext|>