<commit_before>"use strict";

import express from "express";
import favicon from "serve-favicon";
import bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import session from "express-session";
// import csrf from 'csurf';
import consolidate from "consolidate"; // Templating library adapter for Express
import swig from "swig";
// import helmet from "helmet";
import { MongoClient } from "mongodb"; // Driver for connecting to MongoDB
import http from "http";
import marked from 'marked';
//import helmet from "helmet";
//import nosniff from 'dont-sniff-mimetype';
const app = express(); // Web framework to handle routing requests
import routes from "./../dist/app/routes/index";
import config from "./config/config"; // Application config properties
/*
 // Fix for A6-Sensitive Data Exposure
 // Load keys for establishing secure HTTPS connection
 import fs from "fs";
 import https from "https";
 import path from "path";
 const httpsOptions = {
 key: fs.readFileSync(path.resolve(__dirname, "./artifacts/cert/server.key")),
 cert: fs.readFileSync(path.resolve(__dirname, "./artifacts/cert/server.crt"))
 };
 */

MongoClient.connect(config.db, (err, db) => {
  if (err) {
    console.log("Error: DB: connect");
    console.log(err);

    process.exit(1);
  }
  console.log(`Connected to the database: ${config.db}`);

  // Remove default x-powered-by response header
  app.disable("x-powered-by");
  /*
   // Fix for A5 - Security MisConfig
   // TODO: Review the rest of helmet options, like "xssFilter"

   // Prevent opening page in frame or iframe to protect from clickjacking
   app.use(helmet.xframe());

   // Prevents browser from caching and storing page
   app.use(helmet.noCache());

   // Allow loading resources only from white-listed domains
   app.use(helmet.csp());

   // Allow communication only on HTTPS
   app.use(helmet.hsts());

   // TODO: Add another vuln: https://github.com/helmetjs/helmet/issues/26
   // Enable XSS filter in IE (On by default)
   // app.use(helmet.iexss());
   // Now it should be used in hit way, but the README alerts that could be
   // dangerous, like specified in the issue.
   // app.use(helmet.xssFilter({ setOnOldIE: true }));

   // Forces browser to only use the Content-Type set in the response header instead of sniffing or guessing it
   app.use(nosniff());
   */

  // Adding/ remove HTTP Headers for security
  app.use(favicon(__dirname + "./../app/assets/favicon.ico"));

  // Express middleware to populate "req.body" so we can access POST variables
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));// Mandatory in Express v4

  app.use(cookieParser());
  // Enable session management using express middleware
  app.use(session({
    // TODO: Add another vuln?
    // genid: function(req) {
    //    return genuuid() // use UUIDs for session IDs
    //},
    secret: config.cookieSecret,
    // Both mandatory in Express v4
    saveUninitialized: true,
    resave: true,
    /*
     // Fix for A5 - Security MisConfig
     // Use generic cookie name
     key: "sessionId",
     */
    // Fix for A3 - XSS
    cookie: {
      httpOnly: true,
      maxAge: config.maxAge
      // Remember to start an HTTPS server to get this working
      // secure: true
    }

  }));

  /*
   // Fix for A8 - CSRF
   // Enable Express csrf protection
   app.use(csrf());
   // Make csrf token available in templates
   app.use(function(req, res, next) {
   res.locals.csrftoken = req.csrfToken();
   next();
   });
   */

  // Register templating engine
  app.engine(".html", consolidate.swig);
  app.set("view engine", "html");
  app.set("views", __dirname + "./../app/views");
  app.use(express.static(__dirname + "./../app/assets"));

  // Initializing marked library
  // Fix for A9 - Insecure Dependencies
  marked.setOptions({sanitize: true});
  app.locals.marked = marked;

  // Application routes
  routes(app, db);

  // Template system setup
  swig.setDefaults({
    // Autoescape disabled
    autoescape: false
    /*
     // Fix for A3 - XSS, enable auto escaping
     autoescape: true // default value
     */
  });

  // Insecure HTTP connection
  http.createServer(app).listen(config.port, () => console.log(`Express http server listening on port ${config.port}`));

  // Fix for A6-Sensitive Data Exposure
  // Use secure HTTPS protocol
  // https.createServer(httpsOptions, app).listen(config.port, () =>console.log(`Express https server listening on port ${config.port}`));

});
<commit_msg>A3 fix, escaping to prevent CSS<commit_after>"use strict";

import express from "express";
import favicon from "serve-favicon";
import bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import session from "express-session";
// import csrf from 'csurf';
import consolidate from "consolidate"; // Templating library adapter for Express
import swig from "swig";
// import helmet from "helmet";
import { MongoClient } from "mongodb"; // Driver for connecting to MongoDB
import http from "http";
import marked from 'marked';
//import helmet from "helmet";
//import nosniff from 'dont-sniff-mimetype';
const app = express(); // Web framework to handle routing requests
import routes from "./../dist/app/routes/index";
import config from "./config/config"; // Application config properties
/*
 // Fix for A6-Sensitive Data Exposure
 // Load keys for establishing secure HTTPS connection
 import fs from "fs";
 import https from "https";
 import path from "path";
 const httpsOptions = {
 key: fs.readFileSync(path.resolve(__dirname, "./artifacts/cert/server.key")),
 cert: fs.readFileSync(path.resolve(__dirname, "./artifacts/cert/server.crt"))
 };
 */

MongoClient.connect(config.db, (err, db) => {
  if (err) {
    console.log("Error: DB: connect");
    console.log(err);

    process.exit(1);
  }
  console.log(`Connected to the database: ${config.db}`);

  // Remove default x-powered-by response header
  app.disable("x-powered-by");
  /*
   // Fix for A5 - Security MisConfig
   // TODO: Review the rest of helmet options, like "xssFilter"

   // Prevent opening page in frame or iframe to protect from clickjacking
   app.use(helmet.xframe());

   // Prevents browser from caching and storing page
   app.use(helmet.noCache());

   // Allow loading resources only from white-listed domains
   app.use(helmet.csp());

   // Allow communication only on HTTPS
   app.use(helmet.hsts());

   // TODO: Add another vuln: https://github.com/helmetjs/helmet/issues/26
   // Enable XSS filter in IE (On by default)
   // app.use(helmet.iexss());
   // Now it should be used in hit way, but the README alerts that could be
   // dangerous, like specified in the issue.
   // app.use(helmet.xssFilter({ setOnOldIE: true }));

   // Forces browser to only use the Content-Type set in the response header instead of sniffing or guessing it
   app.use(nosniff());
   */

  // Adding/ remove HTTP Headers for security
  app.use(favicon(__dirname + "./../app/assets/favicon.ico"));

  // Express middleware to populate "req.body" so we can access POST variables
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));// Mandatory in Express v4

  app.use(cookieParser());
  // Enable session management using express middleware
  app.use(session({
    // TODO: Add another vuln?
    // genid: function(req) {
    //    return genuuid() // use UUIDs for session IDs
    //},
    secret: config.cookieSecret,
    // Both mandatory in Express v4
    saveUninitialized: true,
    resave: true,
    /*
     // Fix for A5 - Security MisConfig
     // Use generic cookie name
     key: "sessionId",
     */
    // Fix for A3 - XSS
    cookie: {
      httpOnly: true,
      maxAge: config.maxAge
      // Remember to start an HTTPS server to get this working
      // secure: true
    }

  }));

  /*
   // Fix for A8 - CSRF
   // Enable Express csrf protection
   app.use(csrf());
   // Make csrf token available in templates
   app.use(function(req, res, next) {
   res.locals.csrftoken = req.csrfToken();
   next();
   });
   */

  // Register templating engine
  app.engine(".html", consolidate.swig);
  app.set("view engine", "html");
  app.set("views", __dirname + "./../app/views");
  app.use(express.static(__dirname + "./../app/assets"));

  // Initializing marked library
  // Fix for A9 - Insecure Dependencies
  marked.setOptions({sanitize: true});
  app.locals.marked = marked;

  // Application routes
  routes(app, db);

  // Template system setup
  swig.setDefaults({
    // Fix for A3 - XSS, enable auto escaping
    root: __dirname + "./../app/views",
    autoescape: true // default value
  });

  // Insecure HTTP connection
  http.createServer(app).listen(config.port, () => console.log(`Express http server listening on port ${config.port}`));

  // Fix for A6-Sensitive Data Exposure
  // Use secure HTTPS protocol
  // https.createServer(httpsOptions, app).listen(config.port, () =>console.log(`Express https server listening on port ${config.port}`));

});
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var inquirer = require('inquirer');
var utils = require('../lib/utils');
var shelljs = require('shelljs');
var mustache = require('mustache');
var child = require('child_process');

function saveUserConfFile(configPath, webosConf) {
    var userConfData = {};
    if (fs.existsSync(configPath)) {
        userConfData = JSON.parse(fs.readFileSync(configPath));
    }
    userConfData.webos = webosConf;
    fs.writeFileSync(configPath, JSON.stringify(userConfData, null, 2), {
        encoding: 'utf8'
    });
}

function getNextSemVersion(curver) {    // just increase revision
    var tmp = curver.split('.');
    var major = parseInt(tmp[0], 10);
    var minor = parseInt(tmp[1], 10);
    var revision = (parseInt(tmp[2], 10) || 0) + 1;

    return major + '.' + minor + '.' + revision;
}

function getValidWebosConfData(configPath) {
    console.log(configPath);
    if (!(fs.existsSync(configPath))) {
        // userconf.json is not exists
        return null;
    }
    // userconf.json is already exists
    var userConfData = JSON.parse(fs.readFileSync(configPath));

    if (!(userConfData.hasOwnProperty('webos'))) {
        // userconf.json doesn't have webos data
        return null;
    }
    var webosData = userConfData.webos;
    if (typeof webosData.name !== 'string' || webosData.name.length <= 0) {
        // name is invalid
        return null;
    }
    if (typeof webosData.vendor !== 'string' || webosData.vendor.length <= 0) {
        // vendor is invalid
        return null;
    }
    if (typeof webosData.icon !== 'string' || webosData.icon.length <= 0) {
        // icon is invalid
        return null;
    }
    if (typeof webosData.largeicon !== 'string' || webosData.largeicon.length <= 0) {
        // largeicon is invalid
        return null;
    }
    if (!validateWebosVersion(webosData.version)) {
        // version is invalid
        return null;
    }

    return webosData;
}

function validateWebosVersion(version) {
    var tmp = version.split('.'),
        i;
    if (tmp.length !== 3) {
        return false;
    }
    for (i = 0; i < tmp.length; i++) {
        if (isNaN(tmp[i])) {
            return false;
        }
    }

    return i === tmp.length;
}

function confirmUseExistingData(userData, callback) {
    console.log('');
    console.log('      > [ Stored Information ]');
    console.log('      > name        : ' + userData.name);
    console.log('      > version     : ' + userData.version);
    console.log('      > vendor      : ' + userData.vendor);

    var ask = [{
        type: 'confirm',
        name: 'useExisting',
        message: 'Already have \'userconf.json\', Do you want to use this data?'
    }];

    inquirer.prompt(ask, function(answers) {
        callback(!!answers.useExisting);
    });
}

function askUserData(cordovaConf, callback, versionOnly, userData) {
    var ask;
    if(versionOnly) {
        var curVer = userData.version;
        var updateVer = getNextSemVersion(userData.version);
        ask = [{
            type: 'input',
            name: 'version',
            message: 'Current version is ' + curVer + '. Application version: ',
            default: updateVer,
            validate: function(input) {
                return /\d.\d.\d/.test(input) ? true : 'invalid version string for webos platform';
            }
        }];
        inquirer.prompt(ask, function(answers) {
            callback(answers);
        });
    }
    else {
        ask = [{
            type: 'input',
            name: 'name',
            message: 'What\'s the application\'s name?',
            default: cordovaConf.name,
            validate: function(input) {
                return /^[a-zA-Z][^~!\.\;\\\/\|\"\'@\#$%<>^&*\()\-=+_\’\n\t\s]*$/.test(input) ? true : 'invalid name for webos platform';
            }
        },  {
            type: 'input',
            name: 'version',
            message: 'Application Version (Valid RegExp: /\d./\d./\d)',
            default: cordovaConf.version,
            validate: function(input) {
                return /\d.\d.\d/.test(input) ? true : 'invalid version string for webos platform';
            }
        }, {
            type: 'input',
            name: 'vendor',
            message: 'vendor (default is \'My Company\')',
            default: 'My Company',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid vendor';
            }
        },{
            type: 'input',
            name: 'icon',
            message: 'Icon path (default is \'img/logo.png\')',
            default: 'img/logo.png',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid iconpath';
            }
        }, {
            type: 'input',
            name: 'largeicon',
            message: 'LargeIcon path (default is \'img/logo.png\')',
            default: 'img/logo.png',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid iconpath';
            }
        }, {
            type: 'list',
            name: 'resolution',
            message: 'Which resolution does your application developed for?',
            default: '1920x1080',
            choices: [
                '1280x720',
                '1920x1080'
            ]
        }];
        inquirer.prompt(ask, function(answers) {
            callback(answers);
        });
    }
}

function prepareDir(dir) {
    mkdirp.sync(dir);
}

function getManualWebosConfData(platformsData){
    var i,
        manualWebosConfData = null;

    if (platformsData) {
        for (i=0; i < platformsData.length; i++) {
            if (platformsData[i].$.name === 'tv-webos') {
                delete platformsData[i].$;
                manualWebosConfData = utils.trim(platformsData[i]._);
            }
        }
    }
    return manualWebosConfData;
}

module.exports = {
    prepare: function(successCallback, errorCallback, wwwSrc, dest, platformRepos, scripts) {
        console.log('\nStart preparing codes for Webos TV Platform......');

        // file path
        wwwSrc = path.resolve(wwwSrc);
        dest = path.resolve(dest);
        platformRepos = path.resolve(platformRepos);
        var userConfPath = path.join('platforms', 'userconf.json');

        // config
        var cordovaConf = utils.getCordovaConfig();

        var userData = getValidWebosConfData(userConfPath);
        if(userData) {
            confirmUseExistingData(userData, function (useExisting) {
                if(useExisting) {
                    askUserData(cordovaConf, function (data) {
                        userData.version = data.version;
                        userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                        buildProject();
                    }, true, userData);
                }
                else {
                    askUserData(cordovaConf, function (data) {
                        userData = data;
                        userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                        buildProject();
                    });
                }
            });
        }
        else {
            askUserData(cordovaConf, function (data) {
                userData = data;
                userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                buildProject();
            });
        }

        function buildProject() {
            copySrcToDest() || (errorCallback && errorCallback());
            buildPlatformAdditions() || (errorCallback && errorCallback());
            replaceTemplates() || (errorCallback && errorCallback());
            console.log('Prepared at ' + dest);

            // warning for existing meta tag for csp setting
            var targetFile = path.join(wwwSrc, 'index.html');
            var target = fs.readFileSync(targetFile, {
                encoding: 'utf8'
            });

            console.log('\nPlease note below.');
            console.log('1. On WebOS, recommended maximum icon size is 80x80.');
            if(target.match(/(<meta.*)(http-equiv)(.*=*.)("Content-Security-Policy"|'Content-Security-Policy')/)) {
                console.log('2. Remove the meta tag for csp setting in [index.html]');
                console.log('   It is not supported on this platform. It would be caused abnormal operation.');
                console.log('   Please check your file : ' + targetFile);
            }

            saveUserConfFile(userConfPath, userData);
            successCallback && successCallback();
        }

        function copySrcToDest() {
            prepareDir(dest);

            for (var key in scripts) {
                if (scripts.hasOwnProperty(key)) {
                    var to = path.join(dest, key);
                    var from = path.resolve(scripts[key]);
                    shelljs.cp('-f', from, to);
                }
            }

            shelljs.cp('-rf', path.join(wwwSrc, '*'), dest);

            return true;
        }

        function buildPlatformAdditions() {
            shelljs.cp('-rf', path.join(platformRepos, 'www', '*'), dest);
            // hack: copying hidden files(starts with dot) at root('www') to the dest directory.
            // if the dest is not exist, we can copy the platform files without this hack by using this command: cp -rf <PLATFORMREPOS>/www <destDir>
            // But the dest directory has the application files at this moment. So we need to use this hack.
            shelljs.cp('-rf', path.join(platformRepos, 'www', '.*'), dest);
            return true;
        }

        function replaceTemplates() {
            var files = fs.readdirSync(dest);
            for(var i=0; i<files.length; i++) {
                var fileName = files[i];
                if(fileName.match(/\.tmpl$/)) {
                    var tmplFilePath = path.join(dest, fileName);
                    var destFilePath = path.join(dest, fileName.replace(/\.tmpl$/, ''));
                    var template = fs.readFileSync(tmplFilePath, {
                        encoding: 'utf8'
                    });
                    var rendered = mustache.render(template, userData);

                    if(fileName.match(/\.json.tmpl$/)) {
                        var renderedJSON = JSON.parse(rendered);
                        var manualConfDataJSON = JSON.parse(userData.manualConfData);

                        var key;
                        for(key in manualConfDataJSON) {
                            if(manualConfDataJSON.hasOwnProperty(key)) {
                                renderedJSON[key] =  manualConfDataJSON[key];
                            }
                        }
                        rendered = JSON.stringify(renderedJSON, '', '\t');
                    }

                    fs.writeFileSync(destFilePath, rendered, {
                        encoding: 'utf8'
                    });
                    shelljs.rm(path.join(dest, fileName));
                }
            }
            return true;
        }
    },
    build: function(successCallback, errorCallback, data) {
        console.log('\nStart packaging Webos TV Platform......');
        var www = data.www || path.join('platforms', 'tv-webos', 'www');
        var dest = data.dest || path.join('platforms', 'tv-webos', 'build');

        www = path.resolve(www);
        dest = path.resolve(dest);
        child.exec('ares --version', function(err, stdout, stderr) {
            if(err) {
                console.log(stderr);
                throw Error('The command \"ares\" failed. Make sure you have the latest Webos SDK installed, and the \"ares\" command (inside the CLI folder) is added to your path.');
            }
            console.log(stdout);

            // reference url : http://developer.lge.com/webOSTV/sdk/web-sdk/webos-tv-cli/using-webos-tv-cli/
            var result = shelljs.exec('ares-package ' + path.resolve(www));
            if(result.code) {
                throw Error(result.output);
            }
            else {
                var packagePath = result.output.match(/Creating package (.*) in/);
                if(packagePath && packagePath[1]) {
                    prepareDir(dest);
                    shelljs.mv('-f', packagePath[1], path.resolve(dest));
                    console.log('Package created at ' + path.join(dest, packagePath[1]));
                }
                else {
                    throw Error('Fail to retrieve Package File Location.');
                }
            }
        });
    }
};
<commit_msg>Add to ask id and show more Existing data<commit_after>/*
 * Copyright 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var inquirer = require('inquirer');
var utils = require('../lib/utils');
var shelljs = require('shelljs');
var mustache = require('mustache');
var child = require('child_process');

function saveUserConfFile(configPath, webosConf) {
    var userConfData = {};
    if (fs.existsSync(configPath)) {
        userConfData = JSON.parse(fs.readFileSync(configPath));
    }
    userConfData.webos = webosConf;
    fs.writeFileSync(configPath, JSON.stringify(userConfData, null, 2), {
        encoding: 'utf8'
    });
}

function getNextSemVersion(curver) {    // just increase revision
    var tmp = curver.split('.');
    var major = parseInt(tmp[0], 10);
    var minor = parseInt(tmp[1], 10);
    var revision = (parseInt(tmp[2], 10) || 0) + 1;

    return major + '.' + minor + '.' + revision;
}

function getValidWebosConfData(configPath) {
    console.log(configPath);
    if (!(fs.existsSync(configPath))) {
        // userconf.json is not exists
        return null;
    }
    // userconf.json is already exists
    var userConfData = JSON.parse(fs.readFileSync(configPath));

    if (!(userConfData.hasOwnProperty('webos'))) {
        // userconf.json doesn't have webos data
        return null;
    }
    var webosData = userConfData.webos;
    if (typeof webosData.name !== 'string' || webosData.name.length <= 0) {
        // name is invalid
        return null;
    }
    if (typeof webosData.vendor !== 'string' || webosData.vendor.length <= 0) {
        // vendor is invalid
        return null;
    }
    if (typeof webosData.icon !== 'string' || webosData.icon.length <= 0) {
        // icon is invalid
        return null;
    }
    if (typeof webosData.largeicon !== 'string' || webosData.largeicon.length <= 0) {
        // largeicon is invalid
        return null;
    }
    if (!validateWebosVersion(webosData.version)) {
        // version is invalid
        return null;
    }

    return webosData;
}

function validateWebosVersion(version) {
    var tmp = version.split('.'),
        i;
    if (tmp.length !== 3) {
        return false;
    }
    for (i = 0; i < tmp.length; i++) {
        if (isNaN(tmp[i])) {
            return false;
        }
    }

    return i === tmp.length;
}

function confirmUseExistingData(userData, callback) {
    console.log('');
    console.log('      > [ Stored Information ]');
    console.log('      > id          : ' + userData.id);
    console.log('      > name        : ' + userData.name);
    console.log('      > version     : ' + userData.version);
    console.log('      > vendor      : ' + userData.vendor);
    console.log('      > resolution  : ' + userData.resolution);

    var ask = [{
        type: 'confirm',
        name: 'useExisting',
        message: 'Already have \'userconf.json\', Do you want to use this data?'
    }];

    inquirer.prompt(ask, function(answers) {
        callback(!!answers.useExisting);
    });
}

function askUserData(cordovaConf, callback, versionOnly, userData) {
    var ask;
    if(versionOnly) {
        var curVer = userData.version;
        var updateVer = getNextSemVersion(userData.version);
        ask = [{
            type: 'input',
            name: 'version',
            message: 'Current version is ' + curVer + '. Application version: ',
            default: updateVer,
            validate: function(input) {
                return /\d.\d.\d/.test(input) ? true : 'invalid version string for webos platform';
            }
        }];
        inquirer.prompt(ask, function(answers) {
            callback(answers);
        });
    }
    else {
        ask = [{
            type: 'input',
            name: 'id',
            message: 'What\'s the application\'s id? (Valid format: DNS naming)',
            default: 'com.yourdomain.app',
            validate: function(input) {
                return /^([a-z0-9]+\.)+[a-z0-9]+$/.test(input) ? true : 'invalid id for webos platform';
            }
        },{
            type: 'input',
            name: 'name',
            message: 'What\'s the application\'s name?',
            default: cordovaConf.name,
            validate: function(input) {
                return /^[a-zA-Z0-9]+$/.test(input) ? true : 'invalid name for webos platform';
            }
        },  {
            type: 'input',
            name: 'version',
            message: 'Application Version (Valid RegExp: /\d./\d./\d/)',
            default: cordovaConf.version,
            validate: function(input) {
                return /\d.\d.\d/.test(input) ? true : 'invalid version string for webos platform';
            }
        }, {
            type: 'input',
            name: 'vendor',
            message: 'vendor (default is \'My Company\')',
            default: 'My Company',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid vendor';
            }
        },{
            type: 'input',
            name: 'icon',
            message: 'Icon path (default is \'img/logo.png\')',
            default: 'img/logo.png',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid iconpath';
            }
        }, {
            type: 'input',
            name: 'largeicon',
            message: 'LargeIcon path (default is \'img/logo.png\')',
            default: 'img/logo.png',
            validate: function(input) {
                return typeof(input) === 'string' ? true : 'invalid iconpath';
            }
        }, {
            type: 'list',
            name: 'resolution',
            message: 'Which resolution does your application developed for?',
            default: '1920x1080',
            choices: [
                '1280x720',
                '1920x1080'
            ]
        }];
        inquirer.prompt(ask, function(answers) {
            callback(answers);
        });
    }
}

function prepareDir(dir) {
    mkdirp.sync(dir);
}

function getManualWebosConfData(platformsData){
    var i,
        manualWebosConfData = null;

    if (platformsData) {
        for (i=0; i < platformsData.length; i++) {
            if (platformsData[i].$.name === 'tv-webos') {
                delete platformsData[i].$;
                manualWebosConfData = utils.trim(platformsData[i]._);
            }
        }
    }
    return manualWebosConfData;
}

module.exports = {
    prepare: function(successCallback, errorCallback, wwwSrc, dest, platformRepos, scripts) {
        console.log('\nStart preparing codes for Webos TV Platform......');

        // file path
        wwwSrc = path.resolve(wwwSrc);
        dest = path.resolve(dest);
        platformRepos = path.resolve(platformRepos);
        var userConfPath = path.join('platforms', 'userconf.json');

        // config
        var cordovaConf = utils.getCordovaConfig();

        var userData = getValidWebosConfData(userConfPath);
        if(userData) {
            confirmUseExistingData(userData, function (useExisting) {
                if(useExisting) {
                    askUserData(cordovaConf, function (data) {
                        userData.version = data.version;
                        userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                        buildProject();
                    }, true, userData);
                }
                else {
                    askUserData(cordovaConf, function (data) {
                        userData = data;
                        userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                        buildProject();
                    });
                }
            });
        }
        else {
            askUserData(cordovaConf, function (data) {
                userData = data;
                userData.manualConfData = getManualWebosConfData(cordovaConf.platform);
                buildProject();
            });
        }

        function buildProject() {
            copySrcToDest() || (errorCallback && errorCallback());
            buildPlatformAdditions() || (errorCallback && errorCallback());
            replaceTemplates() || (errorCallback && errorCallback());
            console.log('Prepared at ' + dest);

            // warning for existing meta tag for csp setting
            var targetFile = path.join(wwwSrc, 'index.html');
            var target = fs.readFileSync(targetFile, {
                encoding: 'utf8'
            });

            console.log('\nPlease note below.');
            console.log('1. On WebOS, recommended maximum icon size is 80x80.');
            if(target.match(/(<meta.*)(http-equiv)(.*=*.)("Content-Security-Policy"|'Content-Security-Policy')/)) {
                console.log('2. Remove the meta tag for csp setting in [index.html]');
                console.log('   It is not supported on this platform. It would be caused abnormal operation.');
                console.log('   Please check your file : ' + targetFile);
            }

            saveUserConfFile(userConfPath, userData);
            successCallback && successCallback();
        }

        function copySrcToDest() {
            prepareDir(dest);

            for (var key in scripts) {
                if (scripts.hasOwnProperty(key)) {
                    var to = path.join(dest, key);
                    var from = path.resolve(scripts[key]);
                    shelljs.cp('-f', from, to);
                }
            }

            shelljs.cp('-rf', path.join(wwwSrc, '*'), dest);

            return true;
        }

        function buildPlatformAdditions() {
            shelljs.cp('-rf', path.join(platformRepos, 'www', '*'), dest);
            // hack: copying hidden files(starts with dot) at root('www') to the dest directory.
            // if the dest is not exist, we can copy the platform files without this hack by using this command: cp -rf <PLATFORMREPOS>/www <destDir>
            // But the dest directory has the application files at this moment. So we need to use this hack.
            shelljs.cp('-rf', path.join(platformRepos, 'www', '.*'), dest);
            return true;
        }

        function replaceTemplates() {
            var files = fs.readdirSync(dest);
            for(var i=0; i<files.length; i++) {
                var fileName = files[i];
                if(fileName.match(/\.tmpl$/)) {
                    var tmplFilePath = path.join(dest, fileName);
                    var destFilePath = path.join(dest, fileName.replace(/\.tmpl$/, ''));
                    var template = fs.readFileSync(tmplFilePath, {
                        encoding: 'utf8'
                    });
                    var rendered = mustache.render(template, userData);

                    if(fileName.match(/\.json.tmpl$/)) {
                        var renderedJSON = JSON.parse(rendered);
                        var manualConfDataJSON = JSON.parse(userData.manualConfData);

                        var key;
                        for(key in manualConfDataJSON) {
                            if(manualConfDataJSON.hasOwnProperty(key)) {
                                renderedJSON[key] =  manualConfDataJSON[key];
                            }
                        }
                        rendered = JSON.stringify(renderedJSON, '', '\t');
                    }

                    fs.writeFileSync(destFilePath, rendered, {
                        encoding: 'utf8'
                    });
                    shelljs.rm(path.join(dest, fileName));
                }
            }
            return true;
        }
    },
    build: function(successCallback, errorCallback, data) {
        console.log('\nStart packaging Webos TV Platform......');
        var www = data.www || path.join('platforms', 'tv-webos', 'www');
        var dest = data.dest || path.join('platforms', 'tv-webos', 'build');

        www = path.resolve(www);
        dest = path.resolve(dest);
        child.exec('ares --version', function(err, stdout, stderr) {
            if(err) {
                console.log(stderr);
                throw Error('The command \"ares\" failed. Make sure you have the latest Webos SDK installed, and the \"ares\" command (inside the CLI folder) is added to your path.');
            }
            console.log(stdout);

            // reference url : http://developer.lge.com/webOSTV/sdk/web-sdk/webos-tv-cli/using-webos-tv-cli/
            var result = shelljs.exec('ares-package ' + path.resolve(www));
            if(result.code) {
                throw Error(result.output);
            }
            else {
                var packagePath = result.output.match(/Creating package (.*) in/);
                if(packagePath && packagePath[1]) {
                    prepareDir(dest);
                    shelljs.mv('-f', packagePath[1], path.resolve(dest));
                    console.log('Package created at ' + path.join(dest, packagePath[1]));
                }
                else {
                    throw Error('Fail to retrieve Package File Location.');
                }
            }
        });
    }
};
<|endoftext|>


<commit_before>'use strict';

(function() {
	// Section 16 Controller Spec
	describe('Section 16 Controller Tests', function() {
		// Initialize global variables
		var Section16Controller,
			scope,
			$httpBackend,
			$stateParams,
			$location;

		// The $resource service augments the response object with methods for updating and deleting the resource.
		// If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
		// the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
		// When the toEqualData matcher compares two objects, it takes only object properties into
		// account and ignores methods.
		beforeEach(function() {
			jasmine.addMatchers({
				toEqualData: function(util, customEqualityTesters) {
					return {
						compare: function(actual, expected) {
							return {
								pass: angular.equals(actual, expected)
							};
						}
					};
				}
			});
		});

		// Then we can start by loading the main application module
		beforeEach(module(ApplicationConfiguration.applicationModuleName));

		// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
		// This allows us to inject a service but then attach it to a variable
		// with the same name as the service.
		beforeEach(inject(function($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_) {
			// Set a new global scope
			scope = $rootScope.$new();

			// Point global variables to injected services
			$stateParams = _$stateParams_;
			$httpBackend = _$httpBackend_;
			$location = _$location_;

			// Initialize the Section 16 controller.
			Section16Controller = $controller('Section16Controller', {
				$scope: scope
			});
		}));

		it('Should do some controller test', inject(function() {
			// The test logic
			// ...
		}));
	});
}());<commit_msg>[79713572] Testing for section16<commit_after>'use strict';

(function() {
	// Section 16 Controller Spec
	describe('Section 16 Controller Tests', function() {
		// Initialize global variables
		var Section16Controller,
			scope,
			$httpBackend,
			$stateParams,
			$location;

		// The $resource service augments the response object with methods for updating and deleting the resource.
		// If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
		// the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
		// When the toEqualData matcher compares two objects, it takes only object properties into
		// account and ignores methods.
		beforeEach(function() {
			jasmine.addMatchers({
				toEqualData: function(util, customEqualityTesters) {
					return {
						compare: function(actual, expected) {
							return {
								pass: angular.equals(actual, expected)
							};
						}
					};
				}
			});
		});

		// Then we can start by loading the main application module
		beforeEach(module(ApplicationConfiguration.applicationModuleName));

		// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
		// This allows us to inject a service but then attach it to a variable
		// with the same name as the service.
		beforeEach(inject(function($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_) {
			// Set a new global scope
			scope = $rootScope.$new();

			// Point global variables to injected services
			$stateParams = _$stateParams_;
			$httpBackend = _$httpBackend_;
			$location = _$location_;

			// Initialize the Section 16 controller.
			Section16Controller = $controller('Section16Controller', {
				$scope: scope
			});
		}));

		it('Should successfully submit a POST to the backend', inject(function(Section16) {
			// Create a fake Governance object
			var fakeGovernance = new Section16({
				govStr: 'This is all test information.'
			});

			// Create a Governance response
			var fakeGovernanceResponse = new Section16({
				govStr: 'This is all test information.'
			});

			// Fixture mock form input values
			scope.govStr = 'This is all test information';

			// Set POST response
			$httpBackend.expectPOST('reports', fakeGovernance).respond(fakeGovernanceResponse);

			// Run controller functionality
			scope.update();
			$httpBackend.flush();

			// Test form inputs are reset
			expect(scope.govStr).toEqual('');
		}));
	});
}());<|endoftext|>


<commit_before>import Ember from 'ember';
import PowerSelectBaseComponent from './base';
import layout from '../../templates/components/power-select/single';

const { computed } = Ember;

export default PowerSelectBaseComponent.extend({
  layout: layout,

  // CPs
  selection: computed('selected', {
    get() { return this.get('selected'); },
    set(_, v) { return v; }
  }),

  concatenatedClasses: computed('class', function() {
    const classes = ['ember-power-select'];
    if (this.get('class')) { classes.push(this.get('class')); }
    return classes.join(' ');
  }),

  // Actions
  actions: {
    clear(dropdown, e) {
      e.stopPropagation();
      e.preventDefault();
      this.set('selection', null);
      this.get('onchange')(null, dropdown);
    },

    triggerKeydown(dropdown, e) {
      if (e.keyCode === 40 || e.keyCode === 38) {
        dropdown.open(e);
        e.preventDefault();
      }
    }
  },

  // Methods
  onKeydown(dropdown, e) {
    if (e.defaultPrevented) { return; }
    if (e.keyCode === 13 && dropdown.isOpen) { // Enter
      this.select(this.get('_highlighted'), dropdown, e);
    } else {
      this._super(...arguments);
    }
  },

  defaultHighlighted() {
    return this.get('selection') || this.optionAtIndex(0);
  },

  select(option, dropdown, e) {
    e.preventDefault();
    e.stopPropagation();
    if (this.get('closeOnSelect')) {
      dropdown.close(e);
    }
    if (this.get('selection') !== option) {
      this.get('onchange')(option, dropdown);
    }
  },

  focusSearch() {
    Ember.$('.ember-power-select-search input').focus();
  }
});
<commit_msg>Remove unused code.<commit_after>import Ember from 'ember';
import PowerSelectBaseComponent from './base';
import layout from '../../templates/components/power-select/single';

const { computed } = Ember;

export default PowerSelectBaseComponent.extend({
  layout: layout,

  // CPs
  selection: computed('selected', {
    get() { return this.get('selected'); },
    set(_, v) { return v; }
  }),

  concatenatedClasses: computed('class', function() {
    const classes = ['ember-power-select'];
    if (this.get('class')) { classes.push(this.get('class')); }
    return classes.join(' ');
  }),

  // Actions
  actions: {
    clear(dropdown, e) {
      e.stopPropagation();
      e.preventDefault();
      this.set('selection', null);
      this.get('onchange')(null, dropdown);
    }
  },

  // Methods
  onKeydown(dropdown, e) {
    if (e.defaultPrevented) { return; }
    if (e.keyCode === 13 && dropdown.isOpen) { // Enter
      this.select(this.get('_highlighted'), dropdown, e);
    } else {
      this._super(...arguments);
    }
  },

  defaultHighlighted() {
    return this.get('selection') || this.optionAtIndex(0);
  },

  select(option, dropdown, e) {
    e.preventDefault();
    e.stopPropagation();
    if (this.get('closeOnSelect')) {
      dropdown.close(e);
    }
    if (this.get('selection') !== option) {
      this.get('onchange')(option, dropdown);
    }
  },

  focusSearch() {
    Ember.$('.ember-power-select-search input').focus();
  }
});
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var port = process.env.PORT || 9000;

var server = require('http').Server(app);
var io = require('socket.io')(server);

var util = require('util');
var utilInspectOpts = { showHidden: false, depth: 1, colors: true };

var players = {};
var adjNoun = require('adj-noun');
adjNoun.seed(Math.floor(Math.random() * 1000));
var bricks;
var allBricks;

// Uncomment to see Express debugging
// app.use(express.logger());

// Retrieve mongodb and mongoose
var MongoClient = require('mongodb').MongoClient;
var mongoose = require('mongoose');

// Connect to the db
mongoose.connect('mongodb://localhost/multiplayer-breakout');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
  console.log('it worked');
});

// Setup Player model in db
var playerSchema = mongoose.Schema({
  name: String,
  score: Number
}, {collection: 'Player'});

var Player = mongoose.model('Player', playerSchema);
var highScores;

/*
 * Socket.IO code
 */

io.on('connection', onSocketConnection);

function onSocketConnection(client) {
  util.log(client.id + ' connected');
  client.on('new player', onNewPlayer);
  client.on('disconnect', onClientDisconnect);
  client.on('update paddle position', onUpdatePaddlePosition);
  client.on('brick kill from client', onBrickKillFromClient);
  client.on('paddle release ball', onPaddleReleaseBall);
  client.on('ball hit paddle', onBallHitPaddle);
  client.on('existing ball', onExistingBall);
  client.on('kill remote ball', onKillRemoteBall);
  client.on('update ball', onUpdateBall);
  client.on('player game over', onPlayerGameOver);
  client.on('player final score', onPlayerFinalScore);
}

function onUpdateBall(data) {
  this.broadcast.emit('update ball', {
    x: data.x,
    y: data.y,
    id: this.id
  });
}

function onKillRemoteBall() {
  this.broadcast.emit('kill remote ball', { remotePlayerID: this.id });
}

function onExistingBall(data) {
  this.broadcast.emit('existing ball', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    posX: data.posX,
    posY: data.posY,
    remotePlayerID: this.id
  });
}

function onBallHitPaddle(data) {
  this.broadcast.emit('ball hit paddle', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    remotePlayerID: this.id
  });
}

function onPaddleReleaseBall(data) {
  this.broadcast.emit('paddle release ball', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    posX: data.posX,
    posY: data.posY,
    remotePlayerID: this.id
  });
}

function onNewPlayer(data) {

  var color = '0x' + Math.floor(Math.random() * 16777215).toString(16);

  util.log(this.id + ' sent "new player" message');

  // First 'new player' message sets initial brick layout
  // This might be a reconnecting client with a partially played game
  if (isEmpty(players)) {

    bricks = data.existingBricks;
    util.log('first new player setting brick layout to: ' + bricks);

    // string of "1", repeated data.brickCount times
    allBricks = new Array(data.existingBricks.length + 1).join("1");
  }

  // Send existing players to the new player
  for (var playerID in players) {
    if (players.hasOwnProperty(playerID)) {
      util.log(this.id + ' has been sent the existing player ' + playerID);
      this.emit('new player', {
        id: playerID,
        name: players[playerID].name,
        score: players[playerID].score,
        color: players[playerID].color,
        gameOver: players[playerID].gameOver
      });
    }
  }

  // Send existing brick layout to the new player
  this.emit('initial bricks', { initialBricks: bricks });
  util.log(this.id + ' has been sent the existing brick layout: ' + bricks);

  // Assign player id and name
  var funnyName = adjNoun().join(' ');
  this.emit('local player', {
    id: this.id,
    name: funnyName,
    color: color,
    gameOver: false
  });

  // Add new player to players array
  players[this.id] = {
    color: color,
    name: funnyName,
    score: 0,
    gameOver: false
  };
  util.log(this.id + ' added to players');
  util.log('players = ' + util.inspect(players, utilInspectOpts));

  // Broadcast new player to all socket clients except this new one
  this.broadcast.emit('new player', {
    id: this.id,
    name: funnyName,
    color: color,
    score: 0,
    gameOver: false
  });
  util.log(this.id + ' broadcast to all existing players');

  loadHighScores();
}

function onClientDisconnect() {
  util.log(this.id + ' disconnected');

  if (delete players[this.id]) {
    util.log(this.id + ' removed from players');
    util.log('players = ' + util.inspect(players, utilInspectOpts));
  } else {
    util.log(this.id + ' not found in players');
  }

  // Broadcast removed player to connected socket clients
  this.broadcast.emit('remove player', { id: this.id });
  util.log(this.id + ' removal broadcast to existing players');

  if (isEmpty(players)) {
    resetBricks();
  }
}

function onBrickKillFromClient(data) {
  util.log(this.id + ' sent "brick kill from client" message. brickIndex = ' + data.brickIndex);

  if (bricks.charAt(data.brickIndex) === "0") {
    util.log('brick ' + data.brickIndex + ' already dead. Brick kill message not broadcasted to other clients and no points rewarded to ' +  this.id);
    return;
  }

  bricks = bricks.slice(0, data.brickIndex) + "0" + bricks.slice(data.brickIndex + 1);

  if (bricks.indexOf("1") === -1) {
    resetBricks();
    players[this.id].score += 1000;
  } else {
    players[this.id].score += 100;
  }
  util.log(this.id + " new score = " + players[this.id].score);

  util.log('"brick kill to other clients" message broadcast to other clients');
  this.broadcast.emit('brick kill to other clients', {
    brickIndex: data.brickIndex,
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    remotePlayerID: this.id
  });

  this.emit('update local score', {
    id: this.id,
    score: players[this.id].score
  });

  this.broadcast.emit('update remote score', {
    id: this.id,
    score: players[this.id].score
  });

}

function resetBricks() {
  util.log('All users disconnected. resetting bricks to: ' + allBricks);
  bricks = allBricks;
}

function isEmpty(obj) {
  return (Object.getOwnPropertyNames(obj).length === 0);
}

function onUpdatePaddlePosition(data) {
  this.broadcast.emit('update paddle position', {
    id: this.id,
    x: data.x
  });
}

function onPlayerGameOver() {
  players[this.id].gameOver = true;
  this.broadcast.emit('remote player game over', {
    id: this.id,
  });
}

function onPlayerFinalScore(data) {
  util.log('onPlayerFinalScore data.name = ' + data.name);
  util.log('onPlayerFinalScore data.score = ' + data.score);

  var player = new Player({ name: data.name, score: data.score });

  player.save(function(err, player){
  if (err) return console.log(err);
    console.log('player was saved!');
    console.log('player: ' + player);
  });
}

function loadHighScores(){
  allScores = Player.find({}, function(err, allScoresArray) {
    console.log(allScoresArray);
  });
}

/*
 * HTTP code
 */

server.listen(port, function() {
  'use strict';
  util.log('HTTP: listening on port ' + port);
});

app.use('/', express.static(__dirname + '/public'));
<commit_msg>loads scores in descending order<commit_after>var express = require('express');
var app = express();
var port = process.env.PORT || 9000;

var server = require('http').Server(app);
var io = require('socket.io')(server);

var util = require('util');
var utilInspectOpts = { showHidden: false, depth: 1, colors: true };

var players = {};
var adjNoun = require('adj-noun');
adjNoun.seed(Math.floor(Math.random() * 1000));
var bricks;
var allBricks;

// Uncomment to see Express debugging
// app.use(express.logger());

// Retrieve mongodb and mongoose
var MongoClient = require('mongodb').MongoClient;
var mongoose = require('mongoose');

// Connect to the db
mongoose.connect('mongodb://localhost/multiplayer-breakout');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
  console.log('it worked');
});

// Setup Player model in db
var playerSchema = mongoose.Schema({
  name: String,
  score: Number
}, {collection: 'Player'});

var Player = mongoose.model('Player', playerSchema);
var highScores;

/*
 * Socket.IO code
 */

io.on('connection', onSocketConnection);

function onSocketConnection(client) {
  util.log(client.id + ' connected');
  client.on('new player', onNewPlayer);
  client.on('disconnect', onClientDisconnect);
  client.on('update paddle position', onUpdatePaddlePosition);
  client.on('brick kill from client', onBrickKillFromClient);
  client.on('paddle release ball', onPaddleReleaseBall);
  client.on('ball hit paddle', onBallHitPaddle);
  client.on('existing ball', onExistingBall);
  client.on('kill remote ball', onKillRemoteBall);
  client.on('update ball', onUpdateBall);
  client.on('player game over', onPlayerGameOver);
  client.on('player final score', onPlayerFinalScore);
}

function onUpdateBall(data) {
  this.broadcast.emit('update ball', {
    x: data.x,
    y: data.y,
    id: this.id
  });
}

function onKillRemoteBall() {
  this.broadcast.emit('kill remote ball', { remotePlayerID: this.id });
}

function onExistingBall(data) {
  this.broadcast.emit('existing ball', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    posX: data.posX,
    posY: data.posY,
    remotePlayerID: this.id
  });
}

function onBallHitPaddle(data) {
  this.broadcast.emit('ball hit paddle', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    remotePlayerID: this.id
  });
}

function onPaddleReleaseBall(data) {
  this.broadcast.emit('paddle release ball', {
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    posX: data.posX,
    posY: data.posY,
    remotePlayerID: this.id
  });
}

function onNewPlayer(data) {

  var color = '0x' + Math.floor(Math.random() * 16777215).toString(16);

  util.log(this.id + ' sent "new player" message');

  // First 'new player' message sets initial brick layout
  // This might be a reconnecting client with a partially played game
  if (isEmpty(players)) {

    bricks = data.existingBricks;
    util.log('first new player setting brick layout to: ' + bricks);

    // string of "1", repeated data.brickCount times
    allBricks = new Array(data.existingBricks.length + 1).join("1");
  }

  // Send existing players to the new player
  for (var playerID in players) {
    if (players.hasOwnProperty(playerID)) {
      util.log(this.id + ' has been sent the existing player ' + playerID);
      this.emit('new player', {
        id: playerID,
        name: players[playerID].name,
        score: players[playerID].score,
        color: players[playerID].color,
        gameOver: players[playerID].gameOver
      });
    }
  }

  // Send existing brick layout to the new player
  this.emit('initial bricks', { initialBricks: bricks });
  util.log(this.id + ' has been sent the existing brick layout: ' + bricks);

  // Assign player id and name
  var funnyName = adjNoun().join(' ');
  this.emit('local player', {
    id: this.id,
    name: funnyName,
    color: color,
    gameOver: false
  });

  // Add new player to players array
  players[this.id] = {
    color: color,
    name: funnyName,
    score: 0,
    gameOver: false
  };
  util.log(this.id + ' added to players');
  util.log('players = ' + util.inspect(players, utilInspectOpts));

  // Broadcast new player to all socket clients except this new one
  this.broadcast.emit('new player', {
    id: this.id,
    name: funnyName,
    color: color,
    score: 0,
    gameOver: false
  });
  util.log(this.id + ' broadcast to all existing players');

  loadHighScores();
}

function onClientDisconnect() {
  util.log(this.id + ' disconnected');

  if (delete players[this.id]) {
    util.log(this.id + ' removed from players');
    util.log('players = ' + util.inspect(players, utilInspectOpts));
  } else {
    util.log(this.id + ' not found in players');
  }

  // Broadcast removed player to connected socket clients
  this.broadcast.emit('remove player', { id: this.id });
  util.log(this.id + ' removal broadcast to existing players');

  if (isEmpty(players)) {
    resetBricks();
  }
}

function onBrickKillFromClient(data) {
  util.log(this.id + ' sent "brick kill from client" message. brickIndex = ' + data.brickIndex);

  if (bricks.charAt(data.brickIndex) === "0") {
    util.log('brick ' + data.brickIndex + ' already dead. Brick kill message not broadcasted to other clients and no points rewarded to ' +  this.id);
    return;
  }

  bricks = bricks.slice(0, data.brickIndex) + "0" + bricks.slice(data.brickIndex + 1);

  if (bricks.indexOf("1") === -1) {
    resetBricks();
    players[this.id].score += 1000;
  } else {
    players[this.id].score += 100;
  }
  util.log(this.id + " new score = " + players[this.id].score);

  util.log('"brick kill to other clients" message broadcast to other clients');
  this.broadcast.emit('brick kill to other clients', {
    brickIndex: data.brickIndex,
    velocityX: data.velocityX,
    velocityY: data.velocityY,
    remotePlayerID: this.id
  });

  this.emit('update local score', {
    id: this.id,
    score: players[this.id].score
  });

  this.broadcast.emit('update remote score', {
    id: this.id,
    score: players[this.id].score
  });

}

function resetBricks() {
  util.log('All users disconnected. resetting bricks to: ' + allBricks);
  bricks = allBricks;
}

function isEmpty(obj) {
  return (Object.getOwnPropertyNames(obj).length === 0);
}

function onUpdatePaddlePosition(data) {
  this.broadcast.emit('update paddle position', {
    id: this.id,
    x: data.x
  });
}

function onPlayerGameOver() {
  players[this.id].gameOver = true;
  this.broadcast.emit('remote player game over', {
    id: this.id,
  });
}

function onPlayerFinalScore(data) {
  util.log('onPlayerFinalScore data.name = ' + data.name);
  util.log('onPlayerFinalScore data.score = ' + data.score);

  var player = new Player({ name: data.name, score: data.score });

  player.save(function(err, player){
  if (err) return console.log(err);
    console.log('player was saved!');
    console.log('player: ' + player);
  });
}

function loadHighScores(){
  Player.find().sort({ score: -1 }).exec(loadHighScoresCallback);
}

function loadHighScoresCallback(err, scoresArray) {
  allScores = scoresArray;
  console.log(allScores);
}

/*
 * HTTP code
 */

server.listen(port, function() {
  'use strict';
  util.log('HTTP: listening on port ' + port);
});

app.use('/', express.static(__dirname + '/public'));
<|endoftext|>


<commit_before>var gulp        = require('gulp'),
    del         = require('del'),
    util        = require('gulp-util'),
    sass        = require('gulp-sass'),
    prefixer    = require('gulp-autoprefixer'),
    uglify      = require('gulp-uglify'),
    concat      = require('gulp-concat'),
    rename      = require('gulp-rename'),
    handlebars  = require('gulp-compile-handlebars'),
    browserSync = require('browser-sync'),
    ghPages     = require('gulp-gh-pages');

gulp.task('serve', function() {
  browserSync.init({
    server: './dist',
    open: false,
    notify: false
  });
});

gulp.task('styles', function() {
  gulp.src(['src/scss/main.scss'])
    .pipe(sass())
    .on('error', util.log)
    .pipe(prefixer('last 2 versions'))
    .on('error', util.log)
    .pipe(gulp.dest('dist/css/'))
    .pipe(browserSync.reload({stream: true}));
});

/*
* Compile handlebars/partials into html
*/
gulp.task('templates', function() {
  var opts = {
    ignorePartials: true,
    batch: ['src/partials']
  };

  gulp.src(['src/*.hbs'])
    .pipe(handlebars(null, opts))
    .on('error', util.log)
    .pipe(rename({
      extname: '.html'
    }))
    .on('error', util.log)
    .pipe(gulp.dest('dist/'));
});

/*
* Bundle all javascript files
*/
gulp.task('scripts', function() {
  gulp.src(['src/js/**/*.js', '!src/js/libs/*.js'])
    .pipe(concat('bundle.js'))
    .on('error', util.log)
    .pipe(uglify())
    .on('error', util.log)
    .pipe(gulp.dest('dist/js/'));

  /*
  * Uglify JS libs and move to dist folder
  */
  gulp.src(['src/js/libs/*.js'])
    .pipe(uglify())
    .on('error', util.log)
    .pipe(rename({
      suffix: '.min'
    }))
    .on('error', util.log)
    .pipe(gulp.dest('dist/js/libs'));
});

gulp.task('images', function() {
  gulp.src(['src/images/**/*.{jpg,jpeg,svg,png,gif}'])
    .pipe(gulp.dest('dist/images'));
});

gulp.task('clean:images', function(a) {
  del(['dist/images/**/*.{jpg,jpeg,svg,png,gif}'], a);
});

gulp.task('watch', function() {
  gulp.watch('src/scss/**/*.scss', ['styles']);
  gulp.watch('src/js/**/*.js', ['scripts']);
  gulp.watch('src/**/*.hbs', ['templates']);
  gulp.watch('src/images/**/*.{jpg,jpeg,svg,png,gif}', ['clean:images', 'images']);
});

gulp.task('deploy', function() {
  gulp.src(['dist/**/*'])
    .pipe(ghPages());
});

gulp.task('default', ['watch', 'serve', 'images', 'styles', 'scripts', 'templates']);<commit_msg>Add BS stream on templates<commit_after>var gulp        = require('gulp'),
    del         = require('del'),
    util        = require('gulp-util'),
    sass        = require('gulp-sass'),
    prefixer    = require('gulp-autoprefixer'),
    uglify      = require('gulp-uglify'),
    concat      = require('gulp-concat'),
    rename      = require('gulp-rename'),
    handlebars  = require('gulp-compile-handlebars'),
    browserSync = require('browser-sync'),
    ghPages     = require('gulp-gh-pages');

gulp.task('serve', function() {
  browserSync.init({
    server: './dist',
    open: false,
    notify: false
  });
});

gulp.task('styles', function() {
  gulp.src(['src/scss/main.scss'])
    .pipe(sass())
    .on('error', util.log)
    .pipe(prefixer('last 2 versions'))
    .on('error', util.log)
    .pipe(gulp.dest('dist/css/'))
    .pipe(browserSync.reload({stream: true}));
});

/*
* Compile handlebars/partials into html
*/
gulp.task('templates', function() {
  var opts = {
    ignorePartials: true,
    batch: ['src/partials']
  };

  gulp.src(['src/*.hbs'])
    .pipe(handlebars(null, opts))
    .on('error', util.log)
    .pipe(rename({
      extname: '.html'
    }))
    .on('error', util.log)
    .pipe(gulp.dest('dist/'))
    .pipe(browserSync.reload({stream: true}));;
});

/*
* Bundle all javascript files
*/
gulp.task('scripts', function() {
  gulp.src(['src/js/**/*.js', '!src/js/libs/*.js'])
    .pipe(concat('bundle.js'))
    .on('error', util.log)
    .pipe(uglify())
    .on('error', util.log)
    .pipe(gulp.dest('dist/js/'));

  /*
  * Uglify JS libs and move to dist folder
  */
  gulp.src(['src/js/libs/*.js'])
    .pipe(uglify())
    .on('error', util.log)
    .pipe(rename({
      suffix: '.min'
    }))
    .on('error', util.log)
    .pipe(gulp.dest('dist/js/libs'));
});

gulp.task('images', function() {
  gulp.src(['src/images/**/*.{jpg,jpeg,svg,png,gif}'])
    .pipe(gulp.dest('dist/images'));
});

gulp.task('clean:images', function(a) {
  del(['dist/images/**/*.{jpg,jpeg,svg,png,gif}'], a);
});

gulp.task('watch', function() {
  gulp.watch('src/scss/**/*.scss', ['styles']);
  gulp.watch('src/js/**/*.js', ['scripts']);
  gulp.watch('src/**/*.hbs', ['templates']);
  gulp.watch('src/images/**/*.{jpg,jpeg,svg,png,gif}', ['clean:images', 'images']);
});

gulp.task('deploy', function() {
  gulp.src(['dist/**/*'])
    .pipe(ghPages());
});

gulp.task('default', ['watch', 'serve', 'images', 'styles', 'scripts', 'templates']);<|endoftext|>


<commit_before>Template.movementItem.events({
  'dragstart': function(e) {

  },
  'dragend': function(e) {

  }
});<commit_msg>Added Session variables to store object being dragged and its' type for movement objects.<commit_after>Template.movementItem.events({
  'dragstart': function(e) {
     // Set effectAllowed so we can drop it
    e.originalEvent.dataTransfer.effectAllowed = 'all';

    // Store object being dragged and type in Session
    Session.set("dragObject", this);
    Session.set("dragItemType", "movement");
  }
});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
var path = require('path'),
    mojito,
    fs = require('fs'),
    gear = require('gear'),
    async = require('async'),
    mkdirp = require('mkdirp'),
    mime = require('mime'),
    ShakerCore = require('./core').ShakerCore,
    logger = require('./core').logger,
    utils = require('./utils');

/*
 * Gear.js builder for images.
 *
 * @param name {string} Name of image to build
 * @param file {string} Filename of image
 */
function Image(name, file) {
    this._name = name;
    this._file = file;
}

Image.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._file);

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/*
 * Gear.js builder for Javascript/CSS.
 *
 * @param name {string} Name of rollup to build
 * @param files {array} Filenames to rollup
 */
function Rollup(name, files) {
    this._name = name;
    this._files = files;
}

Rollup.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._files)
                .concat();

        if (options.minify) {
            queue.task(mime.lookup(this._name) === 'application/javascript' ? 'jsminify' : 'cssminify');
        }

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/*
 * Gear.js builder for templates.
 *
 * @param name {string} Name of template to build
 * @param files {array} Filenames to rollup
 */
function ClientRollup(name, base, files) {
    this._name = name;
    this._base = base;
    this._files = files;
}

ClientRollup.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._files)
                .concat({
                    callback: function (blob) {
                        var filename = blob.name,
                            result = blob.result;

                        if (mime.lookup(filename) === 'text/html') {
                            var parts = filename.match(/.*\/?mojits\/(.*)\/views.*\/(.*)\.(.*)\.html/),
                                mojit = parts[1],
                                action = parts[2],
                                renderer = parts[3],
                                json = JSON.stringify(result);

                            result = 'YUI.add("views/' + mojit + '/' + action + '", function (Y, NAME) {\n';
                            result += '\tYUI.namespace("_mojito._cache.compiled.' + mojit + '.views");\n';
                            result += '\tYUI._mojito._cache.compiled.' + mojit + '.views.' + action + ' = ' + json + ';\n';
                            result += '});';
                        }

                        return result;
                    }
                });

        if (options.minify) {
            queue.jsminify();
        }

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/**
 * Shaker compiler.
 *
 * Compiler takes metadata generated by the core and depending on the environment context
 * will run tasks against the assets. Tasks including concatenating, minifying, and deploying to
 * a variety of locations (local, cdn).
 *
 * Sample application.json:
 *   ...
 *   {
 *       "settings": ["environment:test"],
 *       "shaker": {
 *           "task": "local"
 *       }
 *   },
 *   ...
 *
 * Example:
 * new Shaker({store: store}).run(function (metadata) {
 *    console.log(metadata);
 * )});
 *
 * @param core {Object} A Shaker core object for a Mojito app.
 */
function Shaker (options) {
    this._core = new ShakerCore(options);

    var shaker = this._core.getConfig();
    this._task = Shaker.DEFAULT_TASK;
    if (shaker.task) { // Task to run when compiling assets
        this._task = shaker.task === 'local' ? 'write' : shaker.task;
    }
    this._compiled_dir = shaker.compiled_dir || Shaker.COMPILED_DIR;
    this._images = shaker.images !== undefined ? shaker.images : false; // Deploy images
    this._parallel = shaker.parallel !== undefined ? shaker.parallel : 20; // How many tasks the async queue runs
    this._delay = shaker.delay !== undefined ? shaker.delay : 0; // Add some network delay for slow connections
    this._lint = shaker.lint !== undefined ? shaker.lint : true; // lint or not
    this._minify = shaker.minify !== undefined ? shaker.minify : true; // Uglify or not
    this._config = shaker.config || {}; // Config object passed through to task
    this._config.root = this._core.getStaticRoot();

    this._registry = new gear.Registry({dirname: path.resolve(__dirname, '../', 'node_modules', 'gear-lib', 'lib')});

    if (path.existsSync(Shaker.TASKS_DIR)) {    // Load more tasks
        this._registry.load({dirname: Shaker.TASKS_DIR});
    }
}

Shaker.DEFAULT_TASK = 'raw';
Shaker.TASKS_DIR = __dirname + '/tasks/'; // Tasks in this directory can be directly referenced in application.json
Shaker.ASSETS_DIR = 'assets/';
Shaker.COMPILED_DIR = Shaker.ASSETS_DIR + 'compiled/'; // Where we write the rollups
Shaker.IMAGES_DIR = Shaker.ASSETS_DIR + 'images/';

Shaker.prototype = {
    /*
     * Start compiling assets.
     *
     * @param callback {function} Callback when compiling is complete.
     */
    run: function (callback) {
        logger.log('[SHAKER] - Analizying application assets to Shake... ');
        var metadata = this._core.run({ignore_dir: this._compiled_dir});

        if (this._lint) {
            this._lintFiles(this._runTask.bind(this, metadata, callback));
        } else {
            this._runTask(metadata, callback);
        }
    },

    _runTask: function(metadata, callback) {
        if (this._task === Shaker.DEFAULT_TASK) {
            this._rename(metadata);
            this._writeMeta(metadata);
            callback(null, metadata);
        }
        else {
            this._compileRollups(metadata, callback);
        }
    },

    /*
     * Rename assets from local filename to URL.
     *
     * @param metadata {Object} Core metadata to augment.
     */
    _rename: function (metadata) {
        logger.log('[SHAKER] - Processing assets for development env.');
        var mojit, action, dim, item, list, renamed;

        // Mojit assets
        for (mojit in metadata.mojits) {
            for (action in metadata.mojits[mojit]) {
                for (dim in metadata.mojits[mojit][action].shaken) {
                    for (item in metadata.mojits[mojit][action].shaken[dim]) {
                        list = metadata.mojits[mojit][action].shaken[dim];
                        list[item] = this._core.getURL(list[item]);
                    }
                }
                for(item in (list = metadata.mojits[mojit][action].client)){
                    list[item] = this._core.getURL(list[item]);
                }
            }
        }

        // App level assets
        for (action in metadata.app) {
            for (dim in metadata.app[action].shaken) {
                for (item in (list = metadata.app[action].shaken[dim])) {
                    list[item] = this._core.getURL(list[item]);
                }
            }
            for(item in (list = list = metadata.app[action].client)){
                    list[item] = this._core.getURL(list[item]);
            }
        }
        //Core
        for (item in (list = metadata.core)) {
            list[item] = this._core.getURL(list[item]);
        }
    },

    _lintFiles: function (callback) {
        var files = [],
            file,
            filtered = {},
            queue;

        for (file in this._core.getFiles()) {
            files.push(file);
        }

        filtered = this._filterFiles(files);

        if (filtered.css.length > 0) {
            new gear.Queue({registry: this._registry})
                .read(filtered.css)
                .csslint({callback: function(blob) {
                    var name = (blob.name ? blob.name : 'files...');
                    logger.log('[SHAKER] - Linting ' + name);
                    if (blob.csslint.length) {
                        logger.error(name + ' - ' + blob.csslint.length + ' CSSLint errors:');
                        blob.csslint.forEach(function(error) {
                            logger.error(error);
                        });
                        return '[SHAKER] - Aborting';
                    }
                }})
                .run(function(err, results) {
                    if (err) {
                        logger.error(err);
                    } else {
                        callback();
                    }
                });
        }
    },

    /*
     * The workhorse of compiler. Loads the queue and processes the results. We modify the original metadata
     * lists for simplicity.
     *
     * @param metadata {Object} Core metadata to augment.
     * @param callback {function} Callback when rollups are compiled.
     */
    _compileRollups: function (metadata, callback) {
        logger.log('[SHAKER] - Compiling rollups...');
        var queue,
            self = this;

        queue = async.queue(function (item, taskCallback) {
            setTimeout(function () {
                var options = {
                    task: self._task,
                    minify: self._minify,
                    config: self._config
                };
                item.object.build(self._registry, options, function (err, url) {
                    if (err) {
                        // No way to kill queue so throw exception (async bug)
                        logger.error('[SHAKER] Stack trace:');
                        throw err;
                    }

                    logger.log('[SHAKER] - Pushed file ' + url);
                    item.files.push(url);
                    taskCallback();
                });
            }, self._delay);
        }, this._parallel);

        queue.drain = function () {
            self._writeMeta(metadata);
            callback(null, metadata);
        };

        this._queueRollups(queue, metadata);
    },

    /*
     * Add all assets referenced by metadata to an async queue.
     *
     * @param queue {Object} Queue to append tasks to.
     * @param metadata {Object} Core metadata to look through.
     */
    _queueRollups: function (queue, metadata) {
        var mojit, action, dim, files, name, client, filtered;

        // We need to add images so they can be referenced by relative URLs in the deployed CSS.
        if (this._images) {
            metadata.images.forEach(function (image) {
                queue.push({
                    object: new Image(Shaker.IMAGES_DIR + path.basename(image), image),
                    files: metadata.images
                });
            });
            metadata.images.length = 0;
        }

        // Mojito core assets
        queue.push({
            object: new Rollup(this._compiled_dir + 'core_{checksum}.js', metadata.core.slice() /* Clone */),
            files: metadata.core
        });
        metadata.core.length = 0;

        // Mojit assets
        for (mojit in metadata.mojits) {
            for (action in metadata.mojits[mojit]) {
                // Mojit client assets
                client = metadata.mojits[mojit][action].client;
                if (client.length) {
                    queue.push({
                        object: new ClientRollup(this._compiled_dir + 'client_' + mojit + '_{checksum}.js', this._core.getAppRoot(), client.slice() /* Clone */),
                        files: client
                    });
                    client.length = 0;
                }

                for (dim in (files = metadata.mojits[mojit][action].shaken)) {
                    if (files[dim].length) {
                        name = mojit + '_' + action.replace('*', 'default') + '_{checksum}';
                        filtered = this._filterFiles(files[dim]);

                        if (filtered.js.length) {
                            queue.push({
                                object: new Rollup(this._compiled_dir + name + '.js', filtered.js),
                                files: files[dim]
                            });
                        }
                        if (filtered.css.length) {
                            queue.push({
                                object: new Rollup(this._compiled_dir + name + '.css', filtered.css),
                                files: files[dim]
                            });
                        }
                        files[dim].length = 0;
                    }
                }
            }
        }

        // App level assets
        for (action in metadata.app) {
            // App client assets
            client = metadata.app[action].client;
            if (client.length) {
                queue.push({
                    object: new ClientRollup(this._compiled_dir + 'appclient_' + action + '_{checksum}.js', this._core.getAppRoot(), client.slice() /* Clone */ ),
                    files: client
                });
                client.length = 0;
            }

            for (dim in (files = metadata.app[action].shaken)) {
                if (files[dim].length) {
                    name = 'app_' + action.replace('*', 'default') + '_{checksum}';
                    filtered = this._filterFiles(files[dim]);

                    if (filtered.js.length) {
                        queue.push({
                            object: new Rollup(this._compiled_dir + name + '.js', filtered.js),
                            files: files[dim]
                        });
                    }
                    if (filtered.css.length) {
                        queue.push({
                            object: new Rollup(this._compiled_dir + name + '.css', filtered.css),
                            files: files[dim]
                        });
                    }
                    files[dim].length = 0;
                }
            }
        }
    },

    /*
     * Separate JS and CSS files into separate arrays.
     *
     * @param files {array} List of JS/CSS filenames.
     */
    _filterFiles: function (files) {
        var js = [],
            css = [];

        files.forEach(function (file) {
            var type = mime.lookup(file);

            if (type === 'application/javascript') {
                js.push(file);
            } else if (type === 'text/css') {
                css.push(file);
            }
        });

        return {'js': js, 'css': css};
    },

    /*
     * Write the modified metadata.
     *
     * @param metadata {Object} Core metadata to write to file.
     */
    _writeMeta: function (metadata) {
        var self = this,
            content = "";

        content += 'YUI.add("shaker/metaMojits", function (Y, NAME) {\n';
        content += 'YUI.namespace("_mojito._cache.shaker");\n';
        content += 'YUI._mojito._cache.shaker.meta = \n';
        content += JSON.stringify(metadata, null, '\t');
        content += '});';

        logger.log('[SHAKER] - Writting addon metadata file');
        mkdirp.sync(self._core.getAppRoot() + '/autoload/compiled', 0777 & (~process.umask()));
        fs.writeFileSync(self._core.getAppRoot() + '/autoload/compiled/shaker-meta.common.js', content);
    }
};

exports.Shaker = Shaker;
<commit_msg>Update master<commit_after>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
var path = require('path'),
    mojito,
    fs = require('fs'),
    gear = require('gear'),
    async = require('async'),
    mkdirp = require('mkdirp'),
    mime = require('mime'),
    ShakerCore = require('./core').ShakerCore,
    logger = require('./core').logger,
    utils = require('./utils');

/*
 * Gear.js builder for images.
 *
 * @param name {string} Name of image to build
 * @param file {string} Filename of image
 */
function Image(name, file) {
    this._name = name;
    this._file = file;
}

Image.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._file);

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/*
 * Gear.js builder for Javascript/CSS.
 *
 * @param name {string} Name of rollup to build
 * @param files {array} Filenames to rollup
 */
function Rollup(name, files) {
    this._name = name;
    this._files = files;
}

Rollup.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._files)
                .concat();

        if (options.minify) {
            queue.task(mime.lookup(this._name) === 'application/javascript' ? 'jsminify' : 'cssminify');
        }

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/*
 * Gear.js builder for templates.
 *
 * @param name {string} Name of template to build
 * @param files {array} Filenames to rollup
 */
function ClientRollup(name, base, files) {
    this._name = name;
    this._base = base;
    this._files = files;
}

ClientRollup.prototype = {
    build: function (registry, options, callback) {
        var config = utils.simpleClone(options.config),
            queue = new gear.Queue({registry: registry})
                .read(this._files)
                .concat({
                    callback: function (blob) {
                        var filename = blob.name,
                            result = blob.result;

                        if (mime.lookup(filename) === 'text/html') {
                            var parts = filename.match(/.*\/?mojits\/(.*)\/views.*\/(.*)\.(.*)\.html/),
                                mojit = parts[1],
                                action = parts[2],
                                renderer = parts[3],
                                json = JSON.stringify(result);

                            result = 'YUI.add("views/' + mojit + '/' + action + '", function (Y, NAME) {\n';
                            result += '\tYUI.namespace("_mojito._cache.compiled.' + mojit + '.views");\n';
                            result += '\tYUI._mojito._cache.compiled.' + mojit + '.views.' + action + ' = ' + json + ';\n';
                            result += '});';
                        }

                        return result;
                    }
                });

        if (options.minify) {
            queue.jsminify();
        }

        config.name = this._name;
        queue.task(options.task, config)
            .run(function(err, results) {
                if (err) {
                    logger.error('[SHAKER] - Failed to build file ' + config.name + ' (' + err + ')');
                    callback(err);
                    return;
                }
                var result = results[0];
                callback(null, result.url ? result.url : config.root + result.name);
            });
    }
};

/**
 * Shaker compiler.
 *
 * Compiler takes metadata generated by the core and depending on the environment context
 * will run tasks against the assets. Tasks including concatenating, minifying, and deploying to
 * a variety of locations (local, cdn).
 *
 * Sample application.json:
 *   ...
 *   {
 *       "settings": ["environment:test"],
 *       "shaker": {
 *           "task": "local"
 *       }
 *   },
 *   ...
 *
 * Example:
 * new Shaker({store: store}).run(function (metadata) {
 *    console.log(metadata);
 * )});
 *
 * @param core {Object} A Shaker core object for a Mojito app.
 */
function Shaker (options) {
    this._core = new ShakerCore(options);

    var shaker = this._core.getConfig(),
        cwd = process.cwd();

    this._task = Shaker.DEFAULT_TASK;
    if (shaker.task) { // Task to run when compiling assets
        this._task = shaker.task === 'local' ? 'write' : shaker.task;
    }
    this._compiled_dir = shaker.compiled_dir || Shaker.COMPILED_DIR;
    this._images = shaker.images !== undefined ? shaker.images : false; // Deploy images
    this._parallel = shaker.parallel !== undefined ? shaker.parallel : 20; // How many tasks the async queue runs
    this._delay = shaker.delay !== undefined ? shaker.delay : 0; // Add some network delay for slow connections
    this._lint = shaker.lint !== undefined ? shaker.lint : true; // lint or not
    this._minify = shaker.minify !== undefined ? shaker.minify : true; // Uglify or not
    this._config = shaker.config || {}; // Config object passed through to task
    this._config.root = this._core.getStaticRoot();

    this._registry = new gear.Registry({dirname: path.resolve(__dirname, '../', 'node_modules', 'gear-lib', 'lib')});

    if (path.existsSync(Shaker.TASKS_DIR)) {    // Load more tasks
        this._registry.load({dirname: Shaker.TASKS_DIR});
    }

    if (shaker.module) {
        this._registry.load({module: shaker.module});
    }
}

Shaker.DEFAULT_TASK = 'raw';
Shaker.TASKS_DIR = __dirname + '/tasks/'; // Tasks in this directory can be directly referenced in application.json
Shaker.ASSETS_DIR = 'assets/';
Shaker.COMPILED_DIR = Shaker.ASSETS_DIR + 'compiled/'; // Where we write the rollups
Shaker.IMAGES_DIR = Shaker.ASSETS_DIR + 'images/';

Shaker.prototype = {
    /*
     * Start compiling assets.
     *
     * @param callback {function} Callback when compiling is complete.
     */
    run: function (callback) {
        logger.log('[SHAKER] - Analizying application assets to Shake... ');
        var metadata = this._core.run({ignore_dir: this._compiled_dir});

        if (this._lint) {
            this._lintFiles(this._runTask.bind(this, metadata, callback));
        } else {
            this._runTask(metadata, callback);
        }
    },

    _runTask: function(metadata, callback) {
        if (this._task === Shaker.DEFAULT_TASK) {
            this._rename(metadata);
            this._writeMeta(metadata);
            callback(null, metadata);
        }
        else {
            this._compileRollups(metadata, callback);
        }
    },

    /*
     * Rename assets from local filename to URL.
     *
     * @param metadata {Object} Core metadata to augment.
     */
    _rename: function (metadata) {
        logger.log('[SHAKER] - Processing assets for development env.');
        var mojit, action, dim, item, list, renamed;

        // Mojit assets
        for (mojit in metadata.mojits) {
            for (action in metadata.mojits[mojit]) {
                for (dim in metadata.mojits[mojit][action].shaken) {
                    for (item in metadata.mojits[mojit][action].shaken[dim]) {
                        list = metadata.mojits[mojit][action].shaken[dim];
                        list[item] = this._core.getURL(list[item]);
                    }
                }
                for(item in (list = metadata.mojits[mojit][action].client)){
                    list[item] = this._core.getURL(list[item]);
                }
            }
        }

        // App level assets
        for (action in metadata.app) {
            for (dim in metadata.app[action].shaken) {
                for (item in (list = metadata.app[action].shaken[dim])) {
                    list[item] = this._core.getURL(list[item]);
                }
            }
            for(item in (list = list = metadata.app[action].client)){
                    list[item] = this._core.getURL(list[item]);
            }
        }
        //Core
        for (item in (list = metadata.core)) {
            list[item] = this._core.getURL(list[item]);
        }
    },

    _lintFiles: function (callback) {
        var files = [],
            file,
            filtered = {},
            queue;

        for (file in this._core.getFiles()) {
            files.push(file);
        }

        filtered = this._filterFiles(files);

        if (filtered.css.length > 0) {
            new gear.Queue({registry: this._registry})
                .read(filtered.css)
                .csslint({callback: function(blob) {
                    var name = (blob.name ? blob.name : 'files...');
                    logger.log('[SHAKER] - Linting ' + name);
                    if (blob.csslint.length) {
                        logger.error(name + ' - ' + blob.csslint.length + ' CSSLint errors:');
                        blob.csslint.forEach(function(error) {
                            logger.error(error);
                        });
                        return '[SHAKER] - Aborting';
                    }
                }})
                .run(function(err, results) {
                    if (err) {
                        logger.error(err);
                    } else {
                        callback();
                    }
                });
        }
    },

    /*
     * The workhorse of compiler. Loads the queue and processes the results. We modify the original metadata
     * lists for simplicity.
     *
     * @param metadata {Object} Core metadata to augment.
     * @param callback {function} Callback when rollups are compiled.
     */
    _compileRollups: function (metadata, callback) {
        logger.log('[SHAKER] - Compiling rollups...');
        var queue,
            self = this;

        queue = async.queue(function (item, taskCallback) {
            setTimeout(function () {
                var options = {
                    task: self._task,
                    minify: self._minify,
                    config: self._config
                };
                item.object.build(self._registry, options, function (err, url) {
                    if (err) {
                        // No way to kill queue so throw exception (async bug)
                        logger.error('[SHAKER] Stack trace:');
                        throw err;
                    }

                    logger.log('[SHAKER] - Pushed file ' + url);
                    item.files.push(url);
                    taskCallback();
                });
            }, self._delay);
        }, this._parallel);

        queue.drain = function () {
            self._writeMeta(metadata);
            callback(null, metadata);
        };

        this._queueRollups(queue, metadata);
    },

    /*
     * Add all assets referenced by metadata to an async queue.
     *
     * @param queue {Object} Queue to append tasks to.
     * @param metadata {Object} Core metadata to look through.
     */
    _queueRollups: function (queue, metadata) {
        var mojit, action, dim, files, name, client, filtered;

        // We need to add images so they can be referenced by relative URLs in the deployed CSS.
        if (this._images) {
            metadata.images.forEach(function (image) {
                queue.push({
                    object: new Image(Shaker.IMAGES_DIR + path.basename(image), image),
                    files: metadata.images
                });
            });
            metadata.images.length = 0;
        }

        // Mojito core assets
        queue.push({
            object: new Rollup(this._compiled_dir + 'core_{checksum}.js', metadata.core.slice() /* Clone */),
            files: metadata.core
        });
        metadata.core.length = 0;

        // Mojit assets
        for (mojit in metadata.mojits) {
            for (action in metadata.mojits[mojit]) {
                // Mojit client assets
                client = metadata.mojits[mojit][action].client;
                if (client.length) {
                    queue.push({
                        object: new ClientRollup(this._compiled_dir + 'client_' + mojit + '_{checksum}.js', this._core.getAppRoot(), client.slice() /* Clone */),
                        files: client
                    });
                    client.length = 0;
                }

                for (dim in (files = metadata.mojits[mojit][action].shaken)) {
                    if (files[dim].length) {
                        name = mojit + '_' + action.replace('*', 'default') + '_{checksum}';
                        filtered = this._filterFiles(files[dim]);

                        if (filtered.js.length) {
                            queue.push({
                                object: new Rollup(this._compiled_dir + name + '.js', filtered.js),
                                files: files[dim]
                            });
                        }
                        if (filtered.css.length) {
                            queue.push({
                                object: new Rollup(this._compiled_dir + name + '.css', filtered.css),
                                files: files[dim]
                            });
                        }
                        files[dim].length = 0;
                    }
                }
            }
        }

        // App level assets
        for (action in metadata.app) {
            // App client assets
            client = metadata.app[action].client;
            if (client.length) {
                queue.push({
                    object: new ClientRollup(this._compiled_dir + 'appclient_' + action + '_{checksum}.js', this._core.getAppRoot(), client.slice() /* Clone */ ),
                    files: client
                });
                client.length = 0;
            }

            for (dim in (files = metadata.app[action].shaken)) {
                if (files[dim].length) {
                    name = 'app_' + action.replace('*', 'default') + '_{checksum}';
                    filtered = this._filterFiles(files[dim]);

                    if (filtered.js.length) {
                        queue.push({
                            object: new Rollup(this._compiled_dir + name + '.js', filtered.js),
                            files: files[dim]
                        });
                    }
                    if (filtered.css.length) {
                        queue.push({
                            object: new Rollup(this._compiled_dir + name + '.css', filtered.css),
                            files: files[dim]
                        });
                    }
                    files[dim].length = 0;
                }
            }
        }
    },

    /*
     * Separate JS and CSS files into separate arrays.
     *
     * @param files {array} List of JS/CSS filenames.
     */
    _filterFiles: function (files) {
        var js = [],
            css = [];

        files.forEach(function (file) {
            var type = mime.lookup(file);

            if (type === 'application/javascript') {
                js.push(file);
            } else if (type === 'text/css') {
                css.push(file);
            }
        });

        return {'js': js, 'css': css};
    },

    /*
     * Write the modified metadata.
     *
     * @param metadata {Object} Core metadata to write to file.
     */
    _writeMeta: function (metadata) {
        var self = this,
            content = "";

        content += 'YUI.add("shaker/metaMojits", function (Y, NAME) {\n';
        content += 'YUI.namespace("_mojito._cache.shaker");\n';
        content += 'YUI._mojito._cache.shaker.meta = \n';
        content += JSON.stringify(metadata, null, '\t');
        content += '});';

        logger.log('[SHAKER] - Writting addon metadata file');
        mkdirp.sync(self._core.getAppRoot() + '/autoload/compiled', 0777 & (~process.umask()));
        fs.writeFileSync(self._core.getAppRoot() + '/autoload/compiled/shaker-meta.common.js', content);
    }
};

exports.Shaker = Shaker;
<|endoftext|>