<commit_before>
/*jshint browser: true */
'use strict';
var _ = require('underscore');
var Crafty = require('craftyjs');
var Kono = require('./kono');

var res = require('./res');
var layout = require('./layout');

window.Crafty = Crafty; // DEBUG

var config = {
    selfTile: 'red',
    selfName: 'Player 1',
    enemyTile: 'blue',
    enemyName: 'Player 2',
};

var game = Kono();
var picked = null;

var image = function (imageName) {
    return 'assets/images/' + imageName + '.png';
};

var flashingImage = function (entity, imageName) {
    var emphasized = true;
    entity.requires('Image, Delay')
        .image(image(imageName + 'Em'))
        .delay(function () {
            if (emphasized) {
                this.image(image(imageName + 'Em'));
            } else {
                this.image(image(imageName));
            }
            emphasized = !emphasized;
        }, 400, -1);
};

var renderAll = function (memo) {
    memo.enemyIcon = Crafty.e("2D, DOM, Image")
        .attr(layout.enemyIcon)
        .image(image('blueIcon'));

    memo.enemyName = Crafty.e("2D, DOM, Text")
        .attr(layout.enemyName)
        .text(config.enemyName)
        .textFont({size: '50px', family: 'grobedeutschmeister'});

    memo.selfIcon = Crafty.e("2D, DOM, Image")
        .attr(layout.selfIcon)
        .image(image('redIcon'));

    memo.selfName = Crafty.e("2D, DOM, Text")
        .attr(layout.selfName)
        .text(config.selfName)
        .textFont({size: '50px', family: 'grobedeutschmeister'})
        .css({'text-align': 'right'});

    memo.board = Crafty.e("2D, DOM, Image")
        .attr(layout.board)
        .image(image('board'));

    _.times(4, function (i) {
        _.times(4, function (j) {
            var tile = game.at({x: i, y: j});
            memo[i + ',' + j] = Crafty.e("2D, DOM, Image")
                .attr(layout['grid:' + i + ',' + j])
                .image(image(tile + 'Tile'));
        });
    });
};

var flashingIcon = function (entities) {
    if (config.selfTile === game.current) {
        flashingImage(entities.selfIcon, config.selfTile + 'Icon');
    } else {
        flashingImage(entities.enemyIcon, config.enemyTile + 'Icon');
    }
};

Crafty.scene('pickFirst', function () {
    var entities = {};
    renderAll(entities);
    flashingIcon(entities);

    _.each(game.listActions(), function (action) {
        var tileEntity = entities[action.from.x + ',' + action.from.y];
        flashingImage(tileEntity, game.at(action.from) + 'Tile');
        tileEntity.requires('Mouse')
            .bind('Click', function () {
                picked = action.from;
                Crafty.scene('pickSecond');
            });
    });
});

Crafty.scene('pickSecond', function () {
    var entities = {};
    renderAll(entities);
    flashingIcon(entities);

    _.each(game.listActions(), function (action) {
        if (_.isEqual(picked, action.from)) {
            var tileEntity = entities[action.to.x + ',' + action.to.y];
            flashingImage(tileEntity, game.at(action.to) + 'Tile');
            tileEntity.requires('Mouse')
                .bind('Click', function () {
                    game.act(action);
                    if (game.result) {
                        Crafty.scene('gameover');
                    } else {
                        picked = null;
                        Crafty.scene('pickFirst');
                    }
                });
        }
    });

    var pickedEntity = entities[picked.x + ',' + picked.y];
    pickedEntity.requires('Mouse')
        .image(image(game.at(picked) + 'TileEm'))
        .bind('Click', function () {
            picked = null;
            Crafty.scene('pickFirst');
        });
});

Crafty.scene('gameover', function () {
    var entities = {};
    renderAll(entities);

    Crafty.e("2D, DOM, Image")
        .attr(layout.panel)
        .image('assets/images/panel.png');

    var message;

    if (config.selfTile === game.result) {
        message = config.selfName + ' wins';
    } else {
        message = config.enemyName + ' wins';
    }

    Crafty.e("2D, DOM, Text, Delay")
        .attr(layout.panelText)
        .text(message)
        .textFont({size: '50px', family: 'grobedeutschmeister'})
        .css({'text-align': 'center'})
        .delay(function () {
            game = Kono();
            picked = null;
            Crafty.scene('pickFirst');
        }, 5000);
});

Crafty.load(res, function () {
    console.log("crafty loaded");
    Crafty.init(1024, 768, 'stage');
    Crafty.scene('pickFirst');
});
<commit_msg>Update main.js<commit_after>
/*jshint browser: true */
'use strict';
var _ = require('underscore');
var Crafty = require('craftyjs');
var Kono = require('./kono');

var res = require('./res');
var layout = require('./layout');

var config = {
    selfTile: 'red',
    selfName: 'Player 1',
    enemyTile: 'blue',
    enemyName: 'Player 2',
};

var game = Kono();
var picked = null;

var image = function (imageName) {
    return 'assets/images/' + imageName + '.png';
};

var flashingImage = function (entity, imageName) {
    var emphasized = true;
    entity.requires('Image, Delay')
        .image(image(imageName + 'Em'))
        .delay(function () {
            if (emphasized) {
                this.image(image(imageName + 'Em'));
            } else {
                this.image(image(imageName));
            }
            emphasized = !emphasized;
        }, 400, -1);
};

var renderAll = function (memo) {
    memo.enemyIcon = Crafty.e("2D, DOM, Image")
        .attr(layout.enemyIcon)
        .image(image('blueIcon'));

    memo.enemyName = Crafty.e("2D, DOM, Text")
        .attr(layout.enemyName)
        .text(config.enemyName)
        .textFont({size: '50px', family: 'grobedeutschmeister'});

    memo.selfIcon = Crafty.e("2D, DOM, Image")
        .attr(layout.selfIcon)
        .image(image('redIcon'));

    memo.selfName = Crafty.e("2D, DOM, Text")
        .attr(layout.selfName)
        .text(config.selfName)
        .textFont({size: '50px', family: 'grobedeutschmeister'})
        .css({'text-align': 'right'});

    memo.board = Crafty.e("2D, DOM, Image")
        .attr(layout.board)
        .image(image('board'));

    _.times(4, function (i) {
        _.times(4, function (j) {
            var tile = game.at({x: i, y: j});
            memo[i + ',' + j] = Crafty.e("2D, DOM, Image")
                .attr(layout['grid:' + i + ',' + j])
                .image(image(tile + 'Tile'));
        });
    });
};

var flashingIcon = function (entities) {
    if (config.selfTile === game.current) {
        flashingImage(entities.selfIcon, config.selfTile + 'Icon');
    } else {
        flashingImage(entities.enemyIcon, config.enemyTile + 'Icon');
    }
};

Crafty.scene('pickFirst', function () {
    var entities = {};
    renderAll(entities);
    flashingIcon(entities);

    _.each(game.listActions(), function (action) {
        var tileEntity = entities[action.from.x + ',' + action.from.y];
        flashingImage(tileEntity, game.at(action.from) + 'Tile');
        tileEntity.requires('Mouse')
            .bind('Click', function () {
                picked = action.from;
                Crafty.scene('pickSecond');
            });
    });
});

Crafty.scene('pickSecond', function () {
    var entities = {};
    renderAll(entities);
    flashingIcon(entities);

    _.each(game.listActions(), function (action) {
        if (_.isEqual(picked, action.from)) {
            var tileEntity = entities[action.to.x + ',' + action.to.y];
            flashingImage(tileEntity, game.at(action.to) + 'Tile');
            tileEntity.requires('Mouse')
                .bind('Click', function () {
                    game.act(action);
                    if (game.result) {
                        Crafty.scene('gameover');
                    } else {
                        picked = null;
                        Crafty.scene('pickFirst');
                    }
                });
        }
    });

    var pickedEntity = entities[picked.x + ',' + picked.y];
    pickedEntity.requires('Mouse')
        .image(image(game.at(picked) + 'TileEm'))
        .bind('Click', function () {
            picked = null;
            Crafty.scene('pickFirst');
        });
});

Crafty.scene('gameover', function () {
    var entities = {};
    renderAll(entities);

    Crafty.e("2D, DOM, Image")
        .attr(layout.panel)
        .image('assets/images/panel.png');

    var message;

    if (config.selfTile === game.result) {
        message = config.selfName + ' wins';
    } else {
        message = config.enemyName + ' wins';
    }

    Crafty.e("2D, DOM, Text, Delay")
        .attr(layout.panelText)
        .text(message)
        .textFont({size: '50px', family: 'grobedeutschmeister'})
        .css({'text-align': 'center'})
        .delay(function () {
            game = Kono();
            picked = null;
            Crafty.scene('pickFirst');
        }, 5000);
});

Crafty.load(res, function () {
    console.log("crafty loaded");
    Crafty.init(1024, 768, 'stage');
    Crafty.scene('pickFirst');
});
<|endoftext|>


<commit_before>const electron = require('electron');
const session = require('electron').session;

// Module to control application life.
const {app} = electron
// Module to create native browser window.
const {BrowserWindow} = electron;
require(`${__dirname}/application-menu.js`);
const autoUpdater = require(`${__dirname}/auto-updater.js`);
var http = require("http");
const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
const dialog = require('electron').dialog;
const dir = path.join(app.getPath('temp'), '..', 'Autographa');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
let willQuitApp = false;

let getLatestRelease = () => {
  const versionsDesc = fs.readdirSync(dir).filter((file) => {
    if(file.startsWith("app-")){
      const filePath = path.join(dir, file);
      return fs.statSync(filePath).isDirectory();
    }
  }).reverse();
  return versionsDesc[0];
}

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                          console.log(err)
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};

function createWindow() {
    // Create the browser window.
    win = new BrowserWindow({
    width: 800,
    height: 600,
    'min-width': 600,
    'min-height': 300,
    'accept-first-mouse': true,
    'title-bar-style': 'hidden',
    'webPreferences': {'session': session},
    show: false
    });

    // and load the index.html of the app.
    win.loadURL(`file:${__dirname}/app/views/index.html`);
    //loading window gracefully
    win.once('ready-to-show', () => {
  // Open the DevTools.
   win.webContents.openDevTools();
  win.maximize();
        win.show();
    });

    // Emitted when the window is closed.
    win.on('close', (e) => {
          /* the user tried to quit the app */
          if(autoUpdaterOptions.updateDownloaded) {
              win.refDb.close();
              win.targetDb.close();
              win.lookupsDb.close();
              if(fs.existsSync(path.join(`${__dirname}`, 'db'))){
                copyFolderRecursiveSync(path.join(`${__dirname}`, 'db'), app.getPath('userData'));
                var ds = (new Date()).toISOString().replace(/[^0-9]/g, "");
                copyFolderRecursiveSync(path.join(`${__dirname}`, 'db'), path.join(app.getPath('userData'), "DB_backups", "db_backup_"+ds));
              }
              win = null;
          }
          else{
            win = null;
          }
      app.quit();
    });
}

function preProcess() {
    return new Promise(
    function (resolve, reject) {

        // If DB does not exist in the application dir
        if(!fs.existsSync(path.join(`${__dirname}`, 'db'))){
            if(fs.existsSync(app.getPath('userData')+'/db')){
                copyFolderRecursiveSync((app.getPath('userData')+'/db'), path.join(`${__dirname}`));
                resolve("db copied");
                return
            }else{
                resolve('new installation');
                return;
            }

        }else{
          resolve("db already exist");
        }

    })
     .then((response) => {

         //return dbSetup;
    return new Promise(
        function (resolve, reject) {
        // Setup database.
        var dbUtil = require(`${__dirname}/app/util/DbUtil.js`);
        dbUtil.setupTargetDb
            .then((response) => {
              return dbUtil.setupRefDb;
            })
            .then((response) => {
              resolve(response);
            }).then((response) => {
              return dbUtil.setupLookupsDb;
            })
            .then((response)=>{
                resolve(response)
            })
            .catch((err) => {
              console.log('Error while DB setup. ' + err);
              reject(err);
            });
        });
     })
     .then((response) => {
            if (fs.existsSync(dir)){
              fs.readdirSync(dir).filter((file) => {
                  if(file.startsWith("app-") && file != getLatestRelease()){
                    const filePath = path.join(dir, file);
                    deleteDirectory(filePath).then(function(res){
                      console.log(res)
                    }).catch(function(err){
                      console.log(err);
                    })
                  }
              });
            }
            createWindow();
            win.refDb = require(`${__dirname}/app/util/data-provider`).referenceDb();
            win.targetDb =  require(`${__dirname}/app/util/data-provider`).targetDb();
            win.lookupsDb = require(`${__dirname}/app/util/data-provider`).lookupsDb();
            autoUpdaterOptions = {refDb: win.refDb, updateDownloaded: false}
            autoUpdater.initialize(autoUpdaterOptions);
     })
     .catch((err) => {
         console.log('Error while App initialization.' + err);
     });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', preProcess);

app.on('before-quit', () => willQuitApp = true);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
    app.quit();
    }
});

app.on('activate', () => {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
    createWindow();
    }
});

const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
    // Someone tried to run a second instance, we should focus our window.
    if (win) {
        if (win.isMinimized()) win.restore()
        win.focus()
    }
})

if (isSecondInstance) {
    app.quit()
}

function copyFileSync( source, target ) {

    var targetFile = target;

    //if target is a directory a new file with the same name will be created
    if ( fs.existsSync( target ) ) {
        if ( fs.lstatSync( target ).isDirectory() ) {
            targetFile = path.join( target, path.basename( source ) );
        }
    }

    fs.writeFileSync(targetFile, fs.readFileSync(source));
}

function copyFolderRecursiveSync( source, target ) {
    var files = [];

    if ( !fs.existsSync( target ) ) {
        fs.mkdirSync( target );
    }
    //check if folder needs to be created or integrated
    var targetFolder = path.join( target, path.basename( source ) );
    if ( !fs.existsSync( targetFolder ) ) {
        mkdirp.sync( targetFolder );
    }
    if(!fs.existsSync(app.getPath('userData')+'/DB_backups')){
      mkdirp.sync(app.getPath('userData')+"/DB_backups")
    }

    //copy
    if ( fs.existsSync( source ) && fs.lstatSync( source ).isDirectory() ) {
        files = fs.readdirSync( source );
        files.forEach( function ( file ) {
            var curSource = path.join( source, file );
            if ( fs.lstatSync( curSource ).isDirectory() ) {
                copyFolderRecursiveSync( curSource, targetFolder );
            } else {
                copyFileSync( curSource, targetFolder );
            }
            return
        } );
    }
}
<commit_msg>Comment-out devTools from UI.<commit_after>const electron = require('electron');
const session = require('electron').session;

// Module to control application life.
const {app} = electron
// Module to create native browser window.
const {BrowserWindow} = electron;
require(`${__dirname}/application-menu.js`);
const autoUpdater = require(`${__dirname}/auto-updater.js`);
var http = require("http");
const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
const dialog = require('electron').dialog;
const dir = path.join(app.getPath('temp'), '..', 'Autographa');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
let willQuitApp = false;

let getLatestRelease = () => {
  const versionsDesc = fs.readdirSync(dir).filter((file) => {
    if(file.startsWith("app-")){
      const filePath = path.join(dir, file);
      return fs.statSync(filePath).isDirectory();
    }
  }).reverse();
  return versionsDesc[0];
}

function deleteFile(dir, file) {
    return new Promise(function (resolve, reject) {
        var filePath = path.join(dir, file);
        fs.lstat(filePath, function (err, stats) {
            if (err) {
                return reject(err);
            }
            if (stats.isDirectory()) {
                resolve(deleteDirectory(filePath));
            } else {
                fs.unlink(filePath, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    resolve();
                });
            }
        });
    });
};

function deleteDirectory(dir) {
    return new Promise(function (resolve, reject) {
        fs.access(dir, function (err) {
            if (err) {
                return reject(err);
            }
            fs.readdir(dir, function (err, files) {
                if (err) {
                    return reject(err);
                }
                Promise.all(files.map(function (file) {
                    return deleteFile(dir, file);
                })).then(function () {
                    fs.rmdir(dir, function (err) {
                        if (err) {
                          console.log(err)
                            return reject(err);
                        }
                        resolve();
                    });
                }).catch(reject);
            });
        });
    });
};

function createWindow() {
    // Create the browser window.
    win = new BrowserWindow({
    width: 800,
    height: 600,
    'min-width': 600,
    'min-height': 300,
    'accept-first-mouse': true,
    'title-bar-style': 'hidden',
    'webPreferences': {'session': session},
    show: false
    });

    // and load the index.html of the app.
    win.loadURL(`file:${__dirname}/app/views/index.html`);
    //loading window gracefully
    win.once('ready-to-show', () => {
  // Open the DevTools.
  // win.webContents.openDevTools();
  win.maximize();
        win.show();
    });

    // Emitted when the window is closed.
    win.on('close', (e) => {
          /* the user tried to quit the app */
          if(autoUpdaterOptions.updateDownloaded) {
              win.refDb.close();
              win.targetDb.close();
              win.lookupsDb.close();
              if(fs.existsSync(path.join(`${__dirname}`, 'db'))){
                copyFolderRecursiveSync(path.join(`${__dirname}`, 'db'), app.getPath('userData'));
                var ds = (new Date()).toISOString().replace(/[^0-9]/g, "");
                copyFolderRecursiveSync(path.join(`${__dirname}`, 'db'), path.join(app.getPath('userData'), "DB_backups", "db_backup_"+ds));
              }
              win = null;
          }
          else{
            win = null;
          }
      app.quit();
    });
}

function preProcess() {
    return new Promise(
    function (resolve, reject) {

        // If DB does not exist in the application dir
        if(!fs.existsSync(path.join(`${__dirname}`, 'db'))){
            if(fs.existsSync(app.getPath('userData')+'/db')){
                copyFolderRecursiveSync((app.getPath('userData')+'/db'), path.join(`${__dirname}`));
                resolve("db copied");
                return
            }else{
                resolve('new installation');
                return;
            }

        }else{
          resolve("db already exist");
        }

    })
     .then((response) => {

         //return dbSetup;
    return new Promise(
        function (resolve, reject) {
        // Setup database.
        var dbUtil = require(`${__dirname}/app/util/DbUtil.js`);
        dbUtil.setupTargetDb
            .then((response) => {
              return dbUtil.setupRefDb;
            })
            .then((response) => {
              resolve(response);
            }).then((response) => {
              return dbUtil.setupLookupsDb;
            })
            .then((response)=>{
                resolve(response)
            })
            .catch((err) => {
              console.log('Error while DB setup. ' + err);
              reject(err);
            });
        });
     })
     .then((response) => {
            if (fs.existsSync(dir)){
              fs.readdirSync(dir).filter((file) => {
                  if(file.startsWith("app-") && file != getLatestRelease()){
                    const filePath = path.join(dir, file);
                    deleteDirectory(filePath).then(function(res){
                      console.log(res)
                    }).catch(function(err){
                      console.log(err);
                    })
                  }
              });
            }
            createWindow();
            win.refDb = require(`${__dirname}/app/util/data-provider`).referenceDb();
            win.targetDb =  require(`${__dirname}/app/util/data-provider`).targetDb();
            win.lookupsDb = require(`${__dirname}/app/util/data-provider`).lookupsDb();
            autoUpdaterOptions = {refDb: win.refDb, updateDownloaded: false}
            autoUpdater.initialize(autoUpdaterOptions);
     })
     .catch((err) => {
         console.log('Error while App initialization.' + err);
     });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', preProcess);

app.on('before-quit', () => willQuitApp = true);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
    app.quit();
    }
});

app.on('activate', () => {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
    createWindow();
    }
});

const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
    // Someone tried to run a second instance, we should focus our window.
    if (win) {
        if (win.isMinimized()) win.restore()
        win.focus()
    }
})

if (isSecondInstance) {
    app.quit()
}

function copyFileSync( source, target ) {

    var targetFile = target;

    //if target is a directory a new file with the same name will be created
    if ( fs.existsSync( target ) ) {
        if ( fs.lstatSync( target ).isDirectory() ) {
            targetFile = path.join( target, path.basename( source ) );
        }
    }

    fs.writeFileSync(targetFile, fs.readFileSync(source));
}

function copyFolderRecursiveSync( source, target ) {
    var files = [];

    if ( !fs.existsSync( target ) ) {
        fs.mkdirSync( target );
    }
    //check if folder needs to be created or integrated
    var targetFolder = path.join( target, path.basename( source ) );
    if ( !fs.existsSync( targetFolder ) ) {
        mkdirp.sync( targetFolder );
    }
    if(!fs.existsSync(app.getPath('userData')+'/DB_backups')){
      mkdirp.sync(app.getPath('userData')+"/DB_backups")
    }

    //copy
    if ( fs.existsSync( source ) && fs.lstatSync( source ).isDirectory() ) {
        files = fs.readdirSync( source );
        files.forEach( function ( file ) {
            var curSource = path.join( source, file );
            if ( fs.lstatSync( curSource ).isDirectory() ) {
                copyFolderRecursiveSync( curSource, targetFolder );
            } else {
                copyFileSync( curSource, targetFolder );
            }
            return
        } );
    }
}
<|endoftext|>


<commit_before>'use strict';

const render = require('./renderer');
const chromeLauncher = require('chrome-launcher');
const express = require('express');
const compression = require('compression');
const commandLineArgs = require('command-line-args');
const app = express();
const cache = require('./cache');

// Set up app command line flag options.
const optionsDefinitions = [
  {name: 'cache', type: Boolean, defaultValue: false},
  {name: 'debug', type: Boolean, defaultValue: false}
];

const config = commandLineArgs(optionsDefinitions);

if (!module.parent) {
  if (config.cache) {
    app.get('/', cache.middleware());
    // Always clear the cache for now, while things are changing.
    cache.clearCache();
  }
}

app.use(compression());

app.get('/', async function(request, response) {
  const injectShadyDom = !!request.query['wc-inject-shadydom'];
  const html = await render(request.query.url, injectShadyDom, config).catch((err) => console.error(err));
  response.send(html);
});

app.get('/_ah/health', (request, response) => response.send('OK'));

const appPromise = chromeLauncher.launch({
  chromeFlags: ['--headless', '--disable-gpu', '--remote-debugging-address=0.0.0.0'],
  port: 9222
}).then((chrome) => {
  // Don't open a port when running from inside a module (eg. tests). Importing
  // module can control this.
  const port = process.env.PORT || '3000';
  if (!module.parent) {
    app.listen(port, function() {
      console.log('Listening on port', port);
    });
  }
  return app;
});

module.exports = appPromise;
<commit_msg>fix tests<commit_after>'use strict';

const render = require('./renderer');
const chromeLauncher = require('chrome-launcher');
const express = require('express');
const compression = require('compression');
const commandLineArgs = require('command-line-args');
const app = express();
const cache = require('./cache');

// Set up app command line flag options.
let config = {};
const optionsDefinitions = [
  {name: 'cache', type: Boolean, defaultValue: false},
  {name: 'debug', type: Boolean, defaultValue: false}
];

if (!module.parent) {
  config = commandLineArgs(optionsDefinitions);
  if (config.cache) {
    app.get('/', cache.middleware());
    // Always clear the cache for now, while things are changing.
    cache.clearCache();
  }
}

app.use(compression());

app.get('/', async function(request, response) {
  const injectShadyDom = !!request.query['wc-inject-shadydom'];
  const html = await render(request.query.url, injectShadyDom, config).catch((err) => console.error(err));
  response.send(html);
});

app.get('/_ah/health', (request, response) => response.send('OK'));

const appPromise = chromeLauncher.launch({
  chromeFlags: ['--headless', '--disable-gpu', '--remote-debugging-address=0.0.0.0'],
  port: 9222
}).then((chrome) => {
  // Don't open a port when running from inside a module (eg. tests). Importing
  // module can control this.
  const port = process.env.PORT || '3000';
  if (!module.parent) {
    app.listen(port, function() {
      console.log('Listening on port', port);
    });
  }
  return app;
});

module.exports = appPromise;
<|endoftext|>


<commit_before>import Helmet from 'react-helmet';

export default Helmet;
<commit_msg>chore: remove unused code<commit_after><|endoftext|>


<commit_before>// ==UserScript==
// @name        {@title}
// @author      Horza
// @date        {@date}
// @description Add new features to voat.co
// @license     MIT; https://github.com/HorzaGobuchul/{@repoName}/blob/master/LICENSE
// @match       *://voat.co/*
// @match       *://*.voat.co/*
// @include     file:///C:/Users/eri/Desktop/projets/AVE/test%20pages/Amateur%20Voat%20Enhancements.htm
// @include     file:///C:/Users/eri/Desktop/projets/AVE/test%20pages/light%20theme.html
// @version     2.0.0.0
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @run-at      document-end
// @updateURL   https://github.com/HorzaGobuchul/{@repoName}/raw/master/{@fileName}_meta.user.js
// @downloadURL https://github.com/HorzaGobuchul/{@repoName}/raw/master/{@fileName}.user.js
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require     https://github.com/domchristie/to-markdown/raw/master/dist/to-markdown.js
// ==/UserScript==<commit_msg>Update meta.js<commit_after>// ==UserScript==
// @name        {@title}
// @author      Horza
// @date        {@date}
// @description Add new features to voat.co
// @license     MIT; https://github.com/HorzaGobuchul/{@repoName}/blob/master/LICENSE
// @match       *://voat.co/*
// @match       *://*.voat.co/*
// @version     2.0.0.0
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @run-at      document-end
// @updateURL   https://github.com/HorzaGobuchul/{@repoName}/raw/master/{@fileName}_meta.user.js
// @downloadURL https://github.com/HorzaGobuchul/{@repoName}/raw/master/{@fileName}.user.js
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require     https://github.com/domchristie/to-markdown/raw/master/dist/to-markdown.js
// ==/UserScript==
<|endoftext|>


<commit_before>/* global jQuery */

// disable all events
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.trigger = function (options, parent) {
		this.init = function (el, options) {
			// do not forget parent
			parent.init.call(this, el, options);
			this._data.trigger.disabled = false;
		};
		this.trigger = function (ev, data) {
			if(!this._data.trigger.disabled) {
				parent.trigger.call(this, ev, data);
			}
		};
		this.disable_events = function () { this._data.trigger.disabled = true; };
		this.enable_events = function () { this._data.trigger.disabled = false; };
	};
})(jQuery);

// no hover
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nohover = function () {
		this.hover_node = $.noop;
	};
})(jQuery);

// conditional select
(function ($, undefined) {
	"use strict";
	$.jstree.defaults.conditionalselect = function () { return true; };
	$.jstree.plugins.conditionalselect = function (options, parent) {
		// own function
		this.activate_node = function (obj, e) {
			if(this.settings.conditionalselect.call(this, this.get_node(obj))) {
				parent.activate_node.call(this, obj, e);
			}
		};
	};
})(jQuery);

// real checkboxes
(function ($, undefined) {
	"use strict";

	var inp = document.createElement("INPUT");
	inp.type = "checkbox";
	inp.className = "jstree-realcheckbox";

	$.jstree.defaults.realcheckboxes = {};

	$.jstree.plugins.realcheckboxes = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this._data.realcheckboxes.uto = false;
			this.element
				.on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree ready.jstree loaded.jstree', $.proxy(function () {
						// only if undetermined is in setting
						if(this._data.realcheckboxes.uto) { clearTimeout(this._data.realcheckboxes.uto); }
						this._data.realcheckboxes.uto = setTimeout($.proxy(this._realcheckboxes, this), 50);
					}, this));
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				var i, j, tmp = null, chk = inp.cloneNode(true);
				for(i = 0, j = obj.childNodes.length; i < j; i++) {
					if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
						tmp = obj.childNodes[i];
						break;
					}
				}
				if(tmp) {
					for(i = 0, j = tmp.childNodes.length; i < j; i++) {
						if(tmp.childNodes[i] && tmp.childNodes[i].className && tmp.childNodes[i].className.indexOf("jstree-checkbox") !== -1) {
							tmp = tmp.childNodes[i];
							break;
						}
					}
				}
				if(tmp && tmp.tagName === "I") {
					tmp.style.backgroundColor = "transparent";
					tmp.style.backgroundImage = "none";
					tmp.appendChild(chk);
				}
			}
			return obj;
		};
		this._realcheckboxes = function () {
			var ts = this.settings.checkbox.tie_selection;
			$('.jstree-realcheckbox').each(function () {
				this.checked = (!ts && this.parentNode.className.indexOf("jstree-checked") !== -1) || (ts && this.parentNode.parentNode.className.indexOf('jstree-clicked') !== -1);
				this.indeterminate = this.parentNode.className.indexOf("jstree-undetermined") !== -1;
				this.disabled = this.parentNode.parentNode.className.indexOf("disabled") !== -1;
			});
		};
	};
})(jQuery);

// no state
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nostate = function () {
		this.set_state = function (state, callback) {
			if(callback) { callback.call(this); }
			this.trigger('set_state');
		};
	};
})(jQuery);

// no selected in state
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.noselectedstate = function (options, parent) {
		this.get_state = function () {
			var state = parent.get_state.call(this);
			delete state.core.selected;
			return state;
		};
	};
})(jQuery);

// allow search results expanding
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.show_matches_children = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element
				.on('search.jstree before_open.jstree', function (e, data) {
					if(data.instance.settings.search && data.instance.settings.search.show_only_matches) {
						data.instance._data.search.dom.find('.jstree-node')
							.show().filter('.jstree-last').filter(function() { return this.nextSibling; }).removeClass('jstree-last')
							.end().end().end().find(".jstree-children").each(function () { $(this).children(".jstree-node:visible").eq(-1).addClass("jstree-last"); });
					}
				});
		};
	};
})(jQuery);

// additional icon on node (outside of anchor)
(function ($, undefined) {
	"use strict";
	var img = document.createElement('IMG');
	//img.src = "http://www.dpcd.vic.gov.au/__data/assets/image/0004/30667/help.gif";
	img.className = "jstree-questionmark";

	$.jstree.defaults.questionmark = $.noop;
	$.jstree.plugins.questionmark = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element
				.on("click.jstree", ".jstree-questionmark", $.proxy(function (e) {
						e.stopImmediatePropagation();
						this.settings.questionmark.call(this, this.get_node(e.target));
					}, this));
		};
		this.teardown = function () {
			if(this.settings.questionmark) {
				this.element.find(".jstree-questionmark").remove();
			}
			parent.teardown.call(this);
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				var tmp = img.cloneNode(true);
				obj.insertBefore(tmp, obj.childNodes[2]);
			}
			return obj;
		};
	};
})(jQuery);

// auto numbering
(function ($, undefined) {
	"use strict";
	var span = document.createElement('SPAN');
	span.className = "jstree-numbering";

	$.jstree.defaults.numbering = {};
	$.jstree.plugins.numbering = function (options, parent) {
		this.teardown = function () {
			if(this.settings.questionmark) {
				this.element.find(".jstree-numbering").remove();
			}
			parent.teardown.call(this);
		};
		this.get_number = function (obj) {
			obj = this.get_node(obj);
			var ind = $.inArray(obj.id, this.get_node(obj.parent).children) + 1;
			return obj.parent === '#' ? ind : this.get_number(obj.parent) + '.' + ind;
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			var i, j, tmp = null, elm = null, org = this.get_number(obj);
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				for(i = 0, j = obj.childNodes.length; i < j; i++) {
					if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
						tmp = obj.childNodes[i];
						break;
					}
				}
				if(tmp) {
					elm = span.cloneNode(true);
					elm.innerHTML = org + '. ';
					tmp.insertBefore(elm, tmp.childNodes[tmp.childNodes.length - 1]);
				}
			}
			return obj;
		};
	};
})(jQuery);

// selecting a node opens it
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.selectopens = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element.on('select_node.jstree', function (e, data) { data.instance.open_node(data.node); });
		};
	};
})(jQuery);

// massloading
(function ($, undefined) {
	"use strict";
	$.jstree.defaults.massload = function (nodes, callback) {
		callback(false);
	};
	$.jstree.plugins.massload = function (options, parent) {
		this.init = function (el, options) {
			parent.init.call(this, el, options);
			this._data.massload = {};
		};
		this._load_nodes = function (nodes, callback, is_callback) {
			if(is_callback && !$.isEmptyObject(this._data.massload)) {
				return parent._load_nodes.call(this, nodes, callback, is_callback);
			}
			this.settings.massload.call(this, nodes, $.proxy(function (data) {
				if(data) {
					for(var i in data) {
						if(data.hasOwnProperty(i)) {
							this._data.massload[i] = data[i];
						}
					}
				}
				parent._load_nodes.call(this, nodes, callback, is_callback);
			}, this));
		};
		this._load_node = function (obj, callback) {
			var d = this._data.massload[obj.id];
			if(d) {
				return this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d, function (status) {
					callback.call(this, status);
					delete this._data.massload[obj.id];
				});
			}
			return parent._load_node.call(this, obj, callback);
		};
	};
})(jQuery);

// paste override
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nohover = function () {
		this.paste = function (obj, pos) {
			obj = this.get_node(obj);
			if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }
			if(this[ccp_mode](ccp_node, obj, pos)) {
				this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode });
			}
			ccp_node = false;
			ccp_mode = false;
			ccp_inst = false;
		};
	};
})(jQuery);<commit_msg>added data as object plugin (must implement interface) - ref #1005<commit_after>/* global jQuery */

// disable all events
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.trigger = function (options, parent) {
		this.init = function (el, options) {
			// do not forget parent
			parent.init.call(this, el, options);
			this._data.trigger.disabled = false;
		};
		this.trigger = function (ev, data) {
			if(!this._data.trigger.disabled) {
				parent.trigger.call(this, ev, data);
			}
		};
		this.disable_events = function () { this._data.trigger.disabled = true; };
		this.enable_events = function () { this._data.trigger.disabled = false; };
	};
})(jQuery);

// no hover
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nohover = function () {
		this.hover_node = $.noop;
	};
})(jQuery);

// conditional select
(function ($, undefined) {
	"use strict";
	$.jstree.defaults.conditionalselect = function () { return true; };
	$.jstree.plugins.conditionalselect = function (options, parent) {
		// own function
		this.activate_node = function (obj, e) {
			if(this.settings.conditionalselect.call(this, this.get_node(obj))) {
				parent.activate_node.call(this, obj, e);
			}
		};
	};
})(jQuery);

// real checkboxes
(function ($, undefined) {
	"use strict";

	var inp = document.createElement("INPUT");
	inp.type = "checkbox";
	inp.className = "jstree-realcheckbox";

	$.jstree.defaults.realcheckboxes = {};

	$.jstree.plugins.realcheckboxes = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this._data.realcheckboxes.uto = false;
			this.element
				.on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree ready.jstree loaded.jstree', $.proxy(function () {
						// only if undetermined is in setting
						if(this._data.realcheckboxes.uto) { clearTimeout(this._data.realcheckboxes.uto); }
						this._data.realcheckboxes.uto = setTimeout($.proxy(this._realcheckboxes, this), 50);
					}, this));
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				var i, j, tmp = null, chk = inp.cloneNode(true);
				for(i = 0, j = obj.childNodes.length; i < j; i++) {
					if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
						tmp = obj.childNodes[i];
						break;
					}
				}
				if(tmp) {
					for(i = 0, j = tmp.childNodes.length; i < j; i++) {
						if(tmp.childNodes[i] && tmp.childNodes[i].className && tmp.childNodes[i].className.indexOf("jstree-checkbox") !== -1) {
							tmp = tmp.childNodes[i];
							break;
						}
					}
				}
				if(tmp && tmp.tagName === "I") {
					tmp.style.backgroundColor = "transparent";
					tmp.style.backgroundImage = "none";
					tmp.appendChild(chk);
				}
			}
			return obj;
		};
		this._realcheckboxes = function () {
			var ts = this.settings.checkbox.tie_selection;
			$('.jstree-realcheckbox').each(function () {
				this.checked = (!ts && this.parentNode.className.indexOf("jstree-checked") !== -1) || (ts && this.parentNode.parentNode.className.indexOf('jstree-clicked') !== -1);
				this.indeterminate = this.parentNode.className.indexOf("jstree-undetermined") !== -1;
				this.disabled = this.parentNode.parentNode.className.indexOf("disabled") !== -1;
			});
		};
	};
})(jQuery);

// no state
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nostate = function () {
		this.set_state = function (state, callback) {
			if(callback) { callback.call(this); }
			this.trigger('set_state');
		};
	};
})(jQuery);

// no selected in state
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.noselectedstate = function (options, parent) {
		this.get_state = function () {
			var state = parent.get_state.call(this);
			delete state.core.selected;
			return state;
		};
	};
})(jQuery);

// allow search results expanding
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.show_matches_children = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element
				.on('search.jstree before_open.jstree', function (e, data) {
					if(data.instance.settings.search && data.instance.settings.search.show_only_matches) {
						data.instance._data.search.dom.find('.jstree-node')
							.show().filter('.jstree-last').filter(function() { return this.nextSibling; }).removeClass('jstree-last')
							.end().end().end().find(".jstree-children").each(function () { $(this).children(".jstree-node:visible").eq(-1).addClass("jstree-last"); });
					}
				});
		};
	};
})(jQuery);

// additional icon on node (outside of anchor)
(function ($, undefined) {
	"use strict";
	var img = document.createElement('IMG');
	//img.src = "http://www.dpcd.vic.gov.au/__data/assets/image/0004/30667/help.gif";
	img.className = "jstree-questionmark";

	$.jstree.defaults.questionmark = $.noop;
	$.jstree.plugins.questionmark = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element
				.on("click.jstree", ".jstree-questionmark", $.proxy(function (e) {
						e.stopImmediatePropagation();
						this.settings.questionmark.call(this, this.get_node(e.target));
					}, this));
		};
		this.teardown = function () {
			if(this.settings.questionmark) {
				this.element.find(".jstree-questionmark").remove();
			}
			parent.teardown.call(this);
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				var tmp = img.cloneNode(true);
				obj.insertBefore(tmp, obj.childNodes[2]);
			}
			return obj;
		};
	};
})(jQuery);

// auto numbering
(function ($, undefined) {
	"use strict";
	var span = document.createElement('SPAN');
	span.className = "jstree-numbering";

	$.jstree.defaults.numbering = {};
	$.jstree.plugins.numbering = function (options, parent) {
		this.teardown = function () {
			if(this.settings.questionmark) {
				this.element.find(".jstree-numbering").remove();
			}
			parent.teardown.call(this);
		};
		this.get_number = function (obj) {
			obj = this.get_node(obj);
			var ind = $.inArray(obj.id, this.get_node(obj.parent).children) + 1;
			return obj.parent === '#' ? ind : this.get_number(obj.parent) + '.' + ind;
		};
		this.redraw_node = function(obj, deep, callback, force_draw) {
			var i, j, tmp = null, elm = null, org = this.get_number(obj);
			obj = parent.redraw_node.call(this, obj, deep, callback, force_draw);
			if(obj) {
				for(i = 0, j = obj.childNodes.length; i < j; i++) {
					if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
						tmp = obj.childNodes[i];
						break;
					}
				}
				if(tmp) {
					elm = span.cloneNode(true);
					elm.innerHTML = org + '. ';
					tmp.insertBefore(elm, tmp.childNodes[tmp.childNodes.length - 1]);
				}
			}
			return obj;
		};
	};
})(jQuery);

// selecting a node opens it
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.selectopens = function (options, parent) {
		this.bind = function () {
			parent.bind.call(this);
			this.element.on('select_node.jstree', function (e, data) { data.instance.open_node(data.node); });
		};
	};
})(jQuery);

// massloading
(function ($, undefined) {
	"use strict";
	$.jstree.defaults.massload = function (nodes, callback) {
		callback(false);
	};
	$.jstree.plugins.massload = function (options, parent) {
		this.init = function (el, options) {
			parent.init.call(this, el, options);
			this._data.massload = {};
		};
		this._load_nodes = function (nodes, callback, is_callback) {
			if(is_callback && !$.isEmptyObject(this._data.massload)) {
				return parent._load_nodes.call(this, nodes, callback, is_callback);
			}
			this.settings.massload.call(this, nodes, $.proxy(function (data) {
				if(data) {
					for(var i in data) {
						if(data.hasOwnProperty(i)) {
							this._data.massload[i] = data[i];
						}
					}
				}
				parent._load_nodes.call(this, nodes, callback, is_callback);
			}, this));
		};
		this._load_node = function (obj, callback) {
			var d = this._data.massload[obj.id];
			if(d) {
				return this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(function () { return this.nodeType !== 3; }) : d, function (status) {
					callback.call(this, status);
					delete this._data.massload[obj.id];
				});
			}
			return parent._load_node.call(this, obj, callback);
		};
	};
})(jQuery);

// object as data
(function ($, undefined) {
	"use strict";
	$.jstree.defaults.datamodel = {};
	$.jstree.plugins.datamodel = function (options, parent) {
		this.init = function (el, options) {
			this._data.datamodel = {};
			parent.init.call(this, el, options);
		};
		this._datamodel = function (id, nodes, callback) {
			var i = 0, j = nodes.length, tmp = [], obj = null;
			for(; i < j; i++) {
				this._data.datamodel[nodes[i].getID()] = nodes[i];
				obj = {
					id : nodes[i].getID(),
					text : nodes[i].getText(),
					children : nodes[i].hasChildren()
				};
				if(nodes[i].getExtra) {
					obj = nodes[i].getExtra(obj); // icon, type
				}
				tmp.push(obj);
			}
			return this._append_json_data(id, tmp, $.proxy(function (status) {
				callback.call(this, status);
			}, this));
		};
		this._load_node = function (obj, callback) {
			var id = obj.id;
			var nd = obj.id === "#" ? this.settings.core.data : this._data.datamodel[obj.id].getChildren($.proxy(function (nodes) {
				this._datamodel(id, nodes, callback);
			}, this));
			if($.isArray(nd)) {
				this._datamodel(id, nd, callback);
			}
		};
	};
})(jQuery);
/* demo of the above
function treeNode(val) {
	var id = ++treeNode.counter;
	this.getID = function () {
		return id;
	};
	this.getText = function () {
		return val.toString();
	};
	this.getExtra = function (obj) {
		obj.icon = false;
		return obj;
	};
	this.hasChildren = function () {
		return true;
	};
	this.getChildren = function () {
		return [
			new treeNode(Math.pow(val, 2)),
			new treeNode(Math.sqrt(val)),
		];
	};
}
treeNode.counter = 0;

$('#jstree').jstree({
	'core': {
		'data': [
					new treeNode(2),
					new treeNode(3),
					new treeNode(4),
					new treeNode(5)
				]
	},
	plugins : ['datamodel']
});
*/

// paste override
(function ($, undefined) {
	"use strict";
	$.jstree.plugins.nohover = function () {
		this.paste = function (obj, pos) {
			obj = this.get_node(obj);
			if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }
			if(this[ccp_mode](ccp_node, obj, pos)) {
				this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode });
			}
			ccp_node = false;
			ccp_mode = false;
			ccp_inst = false;
		};
	};
})(jQuery);<|endoftext|>


<commit_before>#!/usr/bin/env node
var fs = require('fs');

var source = process.argv.splice(2)[0];
var target = source.substring(0, source.lastIndexOf('.')) + '.css';

fs.readFile(source, 'utf-8', function(err, data) {
    if (err) throw err;

    var analyze = function(input) {
        return input.map(function(line) {
            var rPart = line.trimLeft();

            return {l: line.replace(rPart, ''), r: rPart};
        });
    };

    var treeify = function(input) {
        var parents = [];

        return input.filter(function(elem) {return elem.l || elem.r;}).
                map(function(line) {
            elem = {name: line.r, children: [], ind: line.l.length};
            parent = parents[parents.length - 1] || {ind: 0};

            if (parent.ind == elem.ind) {
                parents.pop();
                parent = parents[parents.length -1] || {ind: 0};
            }
            elem.parent = parent;

            if (elem.ind == 0) {
                parents = [elem];
                elem.parent = null;
                return elem;
            }
            else if (parent.ind < elem.ind) {
                parents.push(elem);
                parent.children.push(elem);
            }
        }).filter(function(elem) {return elem;});
    };

    var printTree = function(tree, i) {
        i = i || 0;

        tree.forEach(function(k) {
            console.log('depth: ' + i);
            console.log('name: ' + k.name);
            console.log('parent: ' + k.parent);

            printTree(k.children, i + 1);
        });
    };

    var chars = function(c, n) {
        var ret = '';

        for(var i = 0; i < n; i++) {
            ret += c;
        }

        return ret;
    };

    var transform = function(tree) {
        var variables;
        var mixins;
        var nested = [];

        var findVariables = function(tree) {
            var ret = {};

            tree.forEach(function(child) {
                if (child.name[0] == '@') {
                    var parts = child.name.split(':');
                    ret[parts[0]] = parts[1].trim();
                }
            });

            return ret;
        };
        variables = findVariables(tree);

        var replaceVariables = function(line) {
            for(var k in variables) {
                var v = variables[k];

                line = line.replace(k, v);
            }

            return line;
        };

        var findMixins = function(tree) {
            var ret = {};

            var parseParams = function(line) {
                var ret = {};
                var parts;

                line = line.trim();
                line = line.substr(1, line.length - 2);
                parts = line.split(',');

                parts.forEach(function(k) {
                    var segments = k.split(':');

                    ret[segments[0]] = segments[1];
                });

                return ret;
            };

            tree.forEach(function(child) {
                if (child.name.search('mixin ') == 0) {
                    var parts = child.name.split(' ');
                    var name = parts[1];
                    var params = parseParams(parts.slice(2).join(''));

                    ret[child.name.split(' ')[1].trim()] = {
                        children: child.children,
                        params: params
                    };

                    // mark as deleted so we can avoid this later
                    child.deleted = true;
                }
            });

            return ret;
        };
        mixins = findMixins(tree);
        console.log(mixins.roundedCorners);

        var recursion = function(tree, i) {
            return tree.map(function(child) {
                if (child.deleted) {
                    return '';
                }

                var ret = '';
                var parts = child.name.split(':');
                var begin = parts[0];
                if (begin in mixins) {
                    var params = parts[1];
                    var mixin = mixins[begin];

                    // TODO: local parameter scope
                    ret += recursion(mixin.children, i).join('\n');

                    return ret;
                }

                var prefix = chars(' ', i * 4);
                ret = prefix + replaceVariables(child.name);

                if (child.children.length) {
                    if (child.parent) {
                        // going to handle these later separately
                        // we'll lose positional data but it's
                        // a bit easier this way
                        // alternatively could try to sort attributes
                        // within blocks and then render nesting here
                        nested.push(child);
                        ret = '';
                    }
                    else {
                        ret += ' {\n' + recursion(child.children, i + 1).join('\n') + '\n}\n';
                    }
                }
                else {
                    ret += ';';
                }

                return ret;
            });
        };
        var ret = recursion(tree, 0);

        var getFullName = function(child) {
            return child.parent? getFullName(child.parent) + ' ' + child.name: child.name;
        };

        if (nested.length) {
            nested = nested.map(function(child) {
                child.name = getFullName(child);
                child.parent = null;

                return child;
            });

            ret.push(transform(nested));
        }

        return ret.filter(function(o) {return o;});
    };

    var parts = analyze(data.split('\n'));
    var tree = treeify(parts);
    var output = transform(tree).join('\n');
    console.log(output);

    fs.writeFile(target, output, function(err) {
        if (err) throw err;

        console.log('Wrote ' + target + '!');
    });
});

<commit_msg>Make mixin parameter passing work<commit_after>#!/usr/bin/env node
var fs = require('fs');

var source = process.argv.splice(2)[0];
var target = source.substring(0, source.lastIndexOf('.')) + '.css';

fs.readFile(source, 'utf-8', function(err, data) {
    if (err) throw err;

    var analyze = function(input) {
        return input.map(function(line) {
            var rPart = line.trimLeft();

            return {l: line.replace(rPart, ''), r: rPart};
        });
    };

    var treeify = function(input) {
        var parents = [];

        return input.filter(function(elem) {return elem.l || elem.r;}).
                map(function(line) {
            elem = {name: line.r, children: [], ind: line.l.length};
            parent = parents[parents.length - 1] || {ind: 0};

            if (parent.ind == elem.ind) {
                parents.pop();
                parent = parents[parents.length -1] || {ind: 0};
            }
            elem.parent = parent;

            if (elem.ind == 0) {
                parents = [elem];
                elem.parent = null;
                return elem;
            }
            else if (parent.ind < elem.ind) {
                parents.push(elem);
                parent.children.push(elem);
            }
        }).filter(function(elem) {return elem;});
    };

    var printTree = function(tree, i) {
        i = i || 0;

        tree.forEach(function(k) {
            console.log('depth: ' + i);
            console.log('name: ' + k.name);
            console.log('parent: ' + k.parent);

            printTree(k.children, i + 1);
        });
    };

    var chars = function(c, n) {
        var ret = '';

        for(var i = 0; i < n; i++) {
            ret += c;
        }

        return ret;
    };

    var transform = function(tree) {
        var variables;
        var mixins;
        var nested = [];

        var findVariables = function(tree) {
            var ret = {};

            tree.forEach(function(child) {
                if (child.name[0] == '@') {
                    var parts = child.name.split(':');
                    ret[parts[0]] = parts[1].trim();
                }
            });

            return ret;
        };
        variables = findVariables(tree);

        var replaceVariables = function(line, vars) {
            for(var k in vars) {
                var v = vars[k];

                line = line.replace(k, v);
            }

            return line;
        };

        var findMixins = function(tree) {
            var ret = {};

            var parseParams = function(line) {
                var ret = {};
                var parts;

                line = line.trim();
                line = line.substr(1, line.length - 2);
                parts = line.split(',');

                parts.forEach(function(k) {
                    var segments = k.split(':');

                    ret[segments[0]] = segments[1];
                });

                return ret;
            };

            tree.forEach(function(child) {
                if (child.name.search('mixin ') == 0) {
                    var parts = child.name.split(' ');
                    var name = parts[1];
                    var params = parseParams(parts.slice(2).join(''));

                    ret[child.name.split(' ')[1].trim()] = {
                        children: child.children,
                        params: params
                    };

                    // mark as deleted so we can avoid this later
                    child.deleted = true;
                }
            });

            return ret;
        };
        mixins = findMixins(tree);

        var setParams = function(params, values) {
            values = values || [];
            var ret = {};
            var i = 0;

            for(var k in params) {
                ret[k] = values[i] || params[k];

                i++;
            };

            return ret;
        };

        var recursion = function(tree, i, vars) {
            return tree.map(function(child) {
                if (child.deleted) {
                    return '';
                }

                var ret = '';
                var parts = child.name.split(':');
                var begin = parts[0];
                if (begin in mixins) {
                    var values = parts[1] || '';
                    values = values.trim().split(' ');
                    var mixin = mixins[begin];
                    var mixinParams = setParams(mixin.params, values);

                    ret += recursion(mixin.children, i, mixinParams).join('\n');

                    return ret;
                }

                var prefix = chars(' ', i * 4);
                var name = replaceVariables(replaceVariables(child.name, vars), variables);
                ret = prefix + name;

                if (child.children.length) {
                    if (child.parent) {
                        // going to handle these later separately
                        // we'll lose positional data but it's
                        // a bit easier this way
                        // alternatively could try to sort attributes
                        // within blocks and then render nesting here
                        nested.push(child);
                        ret = '';
                    }
                    else {
                        ret += ' {\n' + recursion(child.children, i + 1).join('\n') + '\n}\n';
                    }
                }
                else {
                    ret += ';';
                }

                return ret;
            });
        };
        var ret = recursion(tree, 0);

        var getFullName = function(child) {
            return child.parent? getFullName(child.parent) + ' ' + child.name: child.name;
        };

        if (nested.length) {
            nested = nested.map(function(child) {
                child.name = getFullName(child);
                child.parent = null;

                return child;
            });

            ret.push(transform(nested));
        }

        return ret.filter(function(o) {return o;});
    };

    var parts = analyze(data.split('\n'));
    var tree = treeify(parts);
    var output = transform(tree).join('\n');
    console.log(output);

    fs.writeFile(target, output, function(err) {
        if (err) throw err;

        console.log('Wrote ' + target + '!');
    });
});

<|endoftext|>


<commit_before>'use strict';

import _ from 'lodash'

const placeHolderRegex = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;

const trimLeadingColon = _.ary(_.partialRight(_.trimStart, ':'), 1);

function assembleRoute(segment, routes = {}) {
    const renderPath = _.template(segment, {interpolate: placeHolderRegex});
    return _.assign(renderPath, assemble(routes, segment));
}

function assemble(routes, prefix = '') {
    return _.fromPairs(_.map(routes, (val, key) => {
        const name = _.isObject(val) ? val.name : val;
        return [name, assembleRoute(prefix + key, _.isObject(val) ? val.routes : null)];
    }));
}

function matchPath(path, segment) {
  const re = new RegExp('^' + segment.replace(placeHolderRegex, '([^/]+)') + '(/.+)?$');
  return path.match(re);
}

function findMatch(routes, path) {
  for (const segment in routes) {
    if (!routes.hasOwnProperty(segment)) continue
    const match = matchPath(path, segment)
    if (match) return [match, segment]
  }
  return []
}

function match(routes, path, parent = null) {
    const [matched, segment] = findMatch(routes, path)
    if (!matched) return null
    const spec = routes[segment];
    const name = _.isObject(spec) ? spec.name : spec;
    const props = _.isObject(spec) ? spec.props : {};
    const childRoutes = _.isObject(spec) ? spec.routes : null;
    const paramKeys = (segment.match(placeHolderRegex) || []).map(trimLeadingColon);
    const remainingPath = _.last(matched);
    const params = _.zipObject(paramKeys, _.dropRight(_.tail(matched)));
    if (childRoutes && remainingPath) {
        const childMatch = match(childRoutes, remainingPath, {name, props, params});
        if (childMatch) {
            const [childName, childParams, childRemaining, childProps, childParent] = childMatch
            return [
              [name].concat(childName),
              _.assign({}, params, childParams),
              childRemaining,
              childProps,
              childParent.concat(parent)
            ];
        }
    }
    return [[name], params, remainingPath, props, [parent]];
}

function resolve(routes, resolveRoute, path) {
    const matched = match(routes, _.trimEnd(path, '/'));
    if (matched) {
        const [keys, params, remaining, props, parents] = matched
        const parent = parents.reduceRight((parent, child) => _.assign({parent}, child), null)
        return {
            params,
            remaining,
            props,
            parent,
            route: resolveRoute(keys),
            name: keys.join('.')
        }
    }
    return null;
}

function myro(routes) {
    const routeFns = assemble(routes);
    const resolveRoute = path => path.reduce((obj, key) => obj[key], routeFns)
    return _.assign(_.partial(resolve, routes, resolveRoute), routeFns);
}

export default myro
<commit_msg>import lodash functions individually.<commit_after>'use strict';

import trimStart from 'lodash/trimStart'
import trimEnd from 'lodash/trimEnd'
import template from 'lodash/template'
import fromPairs from 'lodash/fromPairs'
import isObject from 'lodash/isObject'
import map from 'lodash/map'
import assign from 'lodash/assign'
import last from 'lodash/last'
import tail from 'lodash/tail'
import dropRight from 'lodash/dropRight'
import zipObject from 'lodash/zipObject'
import partial from 'lodash/partial'

const placeHolderRegex = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;

const trimLeadingColon = s => trimStart(s, ':')

function assembleRoute(segment, routes = {}) {
    const renderPath = template(segment, {interpolate: placeHolderRegex});
    return assign(renderPath, assemble(routes, segment));
}

function assemble(routes, prefix = '') {
    return fromPairs(map(routes, (val, key) => {
        const name = isObject(val) ? val.name : val;
        return [name, assembleRoute(prefix + key, isObject(val) ? val.routes : null)];
    }));
}

function matchPath(path, segment) {
  const re = new RegExp('^' + segment.replace(placeHolderRegex, '([^/]+)') + '(/.+)?$');
  return path.match(re);
}

function findMatch(routes, path) {
  for (const segment in routes) {
    if (!routes.hasOwnProperty(segment)) continue
    const match = matchPath(path, segment)
    if (match) return [match, segment]
  }
  return []
}

function match(routes, path, parent = null) {
    const [matched, segment] = findMatch(routes, path)
    if (!matched) return null
    const spec = routes[segment];
    const name = isObject(spec) ? spec.name : spec;
    const props = isObject(spec) ? spec.props : {};
    const childRoutes = isObject(spec) ? spec.routes : null;
    const paramKeys = (segment.match(placeHolderRegex) || []).map(trimLeadingColon);
    const remainingPath = last(matched);
    const params = zipObject(paramKeys, dropRight(tail(matched)));
    if (childRoutes && remainingPath) {
        const childMatch = match(childRoutes, remainingPath, {name, props, params});
        if (childMatch) {
            const [childName, childParams, childRemaining, childProps, childParent] = childMatch
            return [
              [name].concat(childName),
              assign({}, params, childParams),
              childRemaining,
              childProps,
              childParent.concat(parent)
            ];
        }
    }
    return [[name], params, remainingPath, props, [parent]];
}

function resolve(routes, resolveRoute, path) {
    const matched = match(routes, trimEnd(path, '/'));
    if (matched) {
        const [keys, params, remaining, props, parents] = matched
        const parent = parents.reduceRight((parent, child) => assign({parent}, child), null)
        return {
            params,
            remaining,
            props,
            parent,
            route: resolveRoute(keys),
            name: keys.join('.')
        }
    }
    return null;
}

function myro(routes) {
    const routeFns = assemble(routes);
    const resolveRoute = path => path.reduce((obj, key) => obj[key], routeFns)
    return assign(partial(resolve, routes, resolveRoute), routeFns);
}

export default myro
<|endoftext|>


<commit_before>/*!
 * Nami core class.
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * 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.
 *
 */

/**
 * @fileoverview Nami client code.
 *
 * @author Marcelo Gornstein - http://marcelog.github.com
 * Website: http://marcelog.github.com/Nami
 */

var net = require('net');
var events = require('events');
var action = require('./message/action.js');
var namiResponse = require('./message/response.js');
var util = require('util');
var namiEvents = require('./message/event.js');
var timer = require('timers');

/**
 * Nami client.
 * @constructor
 * @param amiData The configuration for ami.
 * @augments EventEmitter
 */
function Nami(amiData) {
    Nami.super_.call(this);
    this.logger = require('log4js').getLogger('Nami.Client');
    this.connected = false;
    this.amiData = amiData;
    this.EOL = "\r\n";
    this.EOM = this.EOL + this.EOL;
    this.welcomeMessage = "Asterisk Call Manager/1.1" + this.EOL;
    this.received = false;
    this.responses = { };
    this.callbacks = { };
};
// Nami inherits from the EventEmitter so Nami itself can throw events.
util.inherits(Nami, events.EventEmitter);

/**
 * Called when a message arrives and is decoded as an event (namiRawEvent event).
 * This will actually instantiate an Event. If the event has an ActionID,
 * the corresponding response is looked up and will have this event appended.
 * Otherwise, the event "namiEvent" is fired. Also, the event "namiEvent<EventName>"
 * is fired (i.e: on event Dial, namiEventDial will be fired).
 *
 * @see Nami#onRawMessage(String)
 * @param {Event} response An Event message.
 * @returns void
 */
Nami.prototype.onRawEvent = function (event) {
    this.logger.debug('Got event: ' + util.inspect(event));
	if (typeof (event.actionid) !== 'undefined') {
		this.responses[event.actionid].events.push(event);
	}
	if (
		event.event.indexOf('Complete') !== -1
		|| ((typeof(event.eventlist) !== 'undefined') && event.eventlist.indexOf('Complete') != -1)
	) {
		this.callbacks[event.actionid](this.responses[event.actionid]);
	} else {
		this.emit('namiEvent', event);
		this.emit('namiEvent' + event.event, event);
	}
};

/**
 * Called when a message arrives and is decoded as a response (namiRawResponse event).
 * This will actually instantiate a Response. If this response has associated events
 * (still to be received), it is buffered.
 * Otherwise, the callback used in send() will be called with the response.
 * @see Nami#onRawMessage(String)
 * @see Nami#send(String)
 * @param {Response} response A Response message.
 * @returns void
 */
Nami.prototype.onRawResponse = function (response) {
    this.logger.debug('Got response: ' + util.inspect(response));
	if (
        (typeof(response.message) !== 'undefined')
        && (response.message.indexOf('follow') != -1)
    ) {
		this.responses[response.actionid] = response;
	} else if (typeof (this.callbacks[response.actionid]) !== 'undefined') {
		this.callbacks[response.actionid](response);
	}
};

/**
 * Called by onData() whenever a raw message has been read.
 * Will fire "namiRawEvent" if the raw message represents an event.
 * Will fire "namiRawResponse" if the raw message represents a response.
 * @see Nami#onData(String)
 * @param {String} buffer The raw message read from server.
 * @returns void
 */
Nami.prototype.onRawMessage = function (buffer) {
    this.logger.debug('Building raw message: ' + util.inspect(buffer));
	if (buffer.match(/^Event: /) !== null) {
		var event = new namiEvents.Event(buffer);
		this.emit('namiRawEvent', event);
	} else if (buffer.match(/^Response: /) !== null) {
		var response = new namiResponse.Response(buffer);
		this.emit('namiRawResponse', response);
	} else {
		this.logger.warn("Discarded: |" + buffer + "|");
	}
};

/**
 * Called by node whenever data is available to be read from AMI.
 * Will fire "namiRawMessage" for every complete message read.
 * @param {String} data The data read from server.
 * @see Nami#onRawMessage(String)
 * @returns void
 */
Nami.prototype.onData = function (data) {
    this.logger.debug('Got data: ' + util.inspect(data));
    this.received = this.received.concat(data);
    while ((theEOM = this.received.indexOf(this.EOM)) != -1) {
        var msg = this.received.substr(0, theEOM);
        this.emit('namiRawMessage', msg);
        this.received = this.received.substr(theEOM + this.EOM.length);
    }
};
/**
 * Called when the connection is established to AMI.
 * @returns void
 */
Nami.prototype.onConnect = function () {
    this.connected = true;
};

/**
 * Called when the first line is received from the server. It will check that
 * the other peer is a valid AMI server. If not valid, the event "namiInvalidPeer"
 * will be fired. If not, a login is tried, and onData() is set as the new handler
 * for incoming data. An anonymous function will handle the login response, firing
 * "namiLoginIncorrect" if the username/password were not correctly validated.
 * On successfull connection, "namiConnected" is emitted.
 * @param {String} data The data read from server.
 * @see Nami#onData(String)
 * @see Login(String, String)
 * @returns void
 */
Nami.prototype.onWelcomeMessage = function (data) {
    this.logger.debug('Got welcome message: ' + util.inspect(data));
    var welcome = data.indexOf(this.welcomeMessage);
	var self = this;
    if (welcome == -1) {
        this.emit('namiInvalidPeer', data);
    } else {
        this.socket.on('data', function (data) {
        	self.onData(data);
        });
        this.send(
        	new action.Login(this.amiData.username, this.amiData.secret),
        	function (response) {
        		if (response.response != 'Success') {
        			self.emit('namiLoginIncorrect');
        		} else {
        			self.emit('namiConnected');
                }
        	}
        );
    }
};
/**
 * Closes the connection to AMI.
 * @returns void
 */
Nami.prototype.close = function () {
    this.logger.info('Closing connection');
    this.removeAllListeners();
    this.socket.removeAllListeners();
    this.socket.end();
};

/**
 * Opens the connection to AMI.
 * @returns void
 */
Nami.prototype.open = function () {
    this.logger.debug('Opening connection');
    this.socket = new net.Socket();
    var self = this;
    this.socket.on('connect', this.onConnect);
    this.on('namiRawMessage', this.onRawMessage);
    this.on('namiRawResponse', this.onRawResponse);
    this.on('namiRawEvent', this.onRawEvent);
    this.socket.once('data', function (data) {
    	self.onWelcomeMessage(data);
    });
    this.socket.setEncoding('ascii');
    this.received = "";
    this.socket.connect(this.amiData.port, this.amiData.host);
};

/**
 * Sends an action to AMI.
 *
 * @param {Action} action The action to be sent.
 * @param {function} callback The optional callback to be invoked when the
 * responses arrives.
 *
 * @returns void
 */
Nami.prototype.send = function (action, callback) {
    this.logger.debug('Sending: ' + util.inspect(action));
    this.callbacks[action.ActionID] = callback;
    this.responses[action.ActionID] = "";
    this.socket.write(action.marshall());
};

exports.Nami = Nami;
exports.Actions = action;
exports.Event = namiEvents;
exports.Response = namiResponse;

<commit_msg>now sends logoff action when closing connection<commit_after>/*!
 * Nami core class.
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * 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.
 *
 */

/**
 * @fileoverview Nami client code.
 *
 * @author Marcelo Gornstein - http://marcelog.github.com
 * Website: http://marcelog.github.com/Nami
 */

var net = require('net');
var events = require('events');
var action = require('./message/action.js');
var namiResponse = require('./message/response.js');
var util = require('util');
var namiEvents = require('./message/event.js');
var timer = require('timers');

/**
 * Nami client.
 * @constructor
 * @param amiData The configuration for ami.
 * @augments EventEmitter
 */
function Nami(amiData) {
    Nami.super_.call(this);
    this.logger = require('log4js').getLogger('Nami.Client');
    this.connected = false;
    this.amiData = amiData;
    this.EOL = "\r\n";
    this.EOM = this.EOL + this.EOL;
    this.welcomeMessage = "Asterisk Call Manager/1.1" + this.EOL;
    this.received = false;
    this.responses = { };
    this.callbacks = { };
};
// Nami inherits from the EventEmitter so Nami itself can throw events.
util.inherits(Nami, events.EventEmitter);

/**
 * Called when a message arrives and is decoded as an event (namiRawEvent event).
 * This will actually instantiate an Event. If the event has an ActionID,
 * the corresponding response is looked up and will have this event appended.
 * Otherwise, the event "namiEvent" is fired. Also, the event "namiEvent<EventName>"
 * is fired (i.e: on event Dial, namiEventDial will be fired).
 *
 * @see Nami#onRawMessage(String)
 * @param {Event} response An Event message.
 * @returns void
 */
Nami.prototype.onRawEvent = function (event) {
    this.logger.debug('Got event: ' + util.inspect(event));
	if (typeof (event.actionid) !== 'undefined') {
		this.responses[event.actionid].events.push(event);
	}
	if (
		event.event.indexOf('Complete') !== -1
		|| ((typeof(event.eventlist) !== 'undefined') && event.eventlist.indexOf('Complete') != -1)
	) {
		this.callbacks[event.actionid](this.responses[event.actionid]);
	} else {
		this.emit('namiEvent', event);
		this.emit('namiEvent' + event.event, event);
	}
};

/**
 * Called when a message arrives and is decoded as a response (namiRawResponse event).
 * This will actually instantiate a Response. If this response has associated events
 * (still to be received), it is buffered.
 * Otherwise, the callback used in send() will be called with the response.
 * @see Nami#onRawMessage(String)
 * @see Nami#send(String)
 * @param {Response} response A Response message.
 * @returns void
 */
Nami.prototype.onRawResponse = function (response) {
    this.logger.debug('Got response: ' + util.inspect(response));
	if (
        (typeof(response.message) !== 'undefined')
        && (response.message.indexOf('follow') != -1)
    ) {
		this.responses[response.actionid] = response;
	} else if (typeof (this.callbacks[response.actionid]) !== 'undefined') {
		this.callbacks[response.actionid](response);
	}
};

/**
 * Called by onData() whenever a raw message has been read.
 * Will fire "namiRawEvent" if the raw message represents an event.
 * Will fire "namiRawResponse" if the raw message represents a response.
 * @see Nami#onData(String)
 * @param {String} buffer The raw message read from server.
 * @returns void
 */
Nami.prototype.onRawMessage = function (buffer) {
    this.logger.debug('Building raw message: ' + util.inspect(buffer));
	if (buffer.match(/^Event: /) !== null) {
		var event = new namiEvents.Event(buffer);
		this.emit('namiRawEvent', event);
	} else if (buffer.match(/^Response: /) !== null) {
		var response = new namiResponse.Response(buffer);
		this.emit('namiRawResponse', response);
	} else {
		this.logger.warn("Discarded: |" + buffer + "|");
	}
};

/**
 * Called by node whenever data is available to be read from AMI.
 * Will fire "namiRawMessage" for every complete message read.
 * @param {String} data The data read from server.
 * @see Nami#onRawMessage(String)
 * @returns void
 */
Nami.prototype.onData = function (data) {
    this.logger.debug('Got data: ' + util.inspect(data));
    this.received = this.received.concat(data);
    while ((theEOM = this.received.indexOf(this.EOM)) != -1) {
        var msg = this.received.substr(0, theEOM);
        this.emit('namiRawMessage', msg);
        this.received = this.received.substr(theEOM + this.EOM.length);
    }
};
/**
 * Called when the connection is established to AMI.
 * @returns void
 */
Nami.prototype.onConnect = function () {
    this.connected = true;
};

/**
 * Called when the first line is received from the server. It will check that
 * the other peer is a valid AMI server. If not valid, the event "namiInvalidPeer"
 * will be fired. If not, a login is tried, and onData() is set as the new handler
 * for incoming data. An anonymous function will handle the login response, firing
 * "namiLoginIncorrect" if the username/password were not correctly validated.
 * On successfull connection, "namiConnected" is emitted.
 * @param {String} data The data read from server.
 * @see Nami#onData(String)
 * @see Login(String, String)
 * @returns void
 */
Nami.prototype.onWelcomeMessage = function (data) {
    this.logger.debug('Got welcome message: ' + util.inspect(data));
    var welcome = data.indexOf(this.welcomeMessage);
	var self = this;
    if (welcome == -1) {
        this.emit('namiInvalidPeer', data);
    } else {
        this.socket.on('data', function (data) {
        	self.onData(data);
        });
        this.send(
        	new action.Login(this.amiData.username, this.amiData.secret),
        	function (response) {
        		if (response.response != 'Success') {
        			self.emit('namiLoginIncorrect');
        		} else {
        			self.emit('namiConnected');
                }
        	}
        );
    }
};
/**
 * Closes the connection to AMI.
 * @returns void
 */
Nami.prototype.close = function () {
    var self = this;
    this.send(new action.Logoff(), function () { self.logger.info('Logged out'); });
    this.logger.info('Closing connection');
    this.removeAllListeners();
    this.socket.removeAllListeners();
    this.socket.end();
};

/**
 * Opens the connection to AMI.
 * @returns void
 */
Nami.prototype.open = function () {
    this.logger.debug('Opening connection');
    this.socket = new net.Socket();
    var self = this;
    this.socket.on('connect', this.onConnect);
    this.on('namiRawMessage', this.onRawMessage);
    this.on('namiRawResponse', this.onRawResponse);
    this.on('namiRawEvent', this.onRawEvent);
    this.socket.once('data', function (data) {
    	self.onWelcomeMessage(data);
    });
    this.socket.setEncoding('ascii');
    this.received = "";
    this.socket.connect(this.amiData.port, this.amiData.host);
};

/**
 * Sends an action to AMI.
 *
 * @param {Action} action The action to be sent.
 * @param {function} callback The optional callback to be invoked when the
 * responses arrives.
 *
 * @returns void
 */
Nami.prototype.send = function (action, callback) {
    this.logger.debug('Sending: ' + util.inspect(action));
    this.callbacks[action.ActionID] = callback;
    this.responses[action.ActionID] = "";
    this.socket.write(action.marshall());
};

exports.Nami = Nami;
exports.Actions = action;
exports.Event = namiEvents;
exports.Response = namiResponse;

<|endoftext|>