<commit_before>(function() {
    /* global cooObjectBase */
    /* global cooGetScopeVariablesDecl */
    /* global cooGetScopeRet */
    /* global COO_COMMAND_PART_STRING */
    /* global COO_COMMAND_PART_JS */
    /* global COO_COMMAND_PART_IDENTIFIER */
    /* global cooExtractParamNames */
    /* global cooPushScopeVariable */
    /* global INDENT */
    /* global cooRunGenerators */
    /* global cooCreateScope */
    /* global cooMatchCommand */
    /* global cooGetParamsDecl */

    var routeId = 0;

    function createRouteMethod(actualName) {
        return {
            actualName: actualName,
            allowValues: true,

            extractParams: function(cmd) {
                var params;

                if (cmd.name === 'NOMATCH') {
                    if (cmd.parts.length > 1) {
                        cmd.file.errorUnexpectedPart(cmd.parts[1]);
                    }

                    if (Object.keys(cmd.root.data.methods).length) {
                        cmd.parts[0].error = 'Either combination of PATHNAME, SEARCH and HASH or NOMATCH is possible';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }
                } else {
                    if ('nomatch' in cmd.root.data.methods) {
                        cmd.parts[0].error = 'Either NOMATCH or combination of PATHNAME, SEARCH and HASH is possible';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }
                }

                if (cmd.parts.length > 1) {
                    if (cmd.parts[1].type === COO_COMMAND_PART_STRING ||
                        cmd.parts[1].type === COO_COMMAND_PART_JS)
                    {
                        params = {};
                    } else if (cmd.parts[1].type === COO_COMMAND_PART_IDENTIFIER) {
                        params = cooExtractParamNames(cmd, cmd.parts, 1);

                        for (var p in params) {
                            cooPushScopeVariable(cmd, p, false);
                        }
                    } else {
                        cmd.file.errorUnexpectedPart(cmd.parts[1]);
                    }
                }

                if (cmd.parts.length > 2) {
                    cmd.file.errorUnexpectedPart(cmd.parts[2]);
                }

                return params;
            },

            tuneCommand: function(cmd) {
                if (cmd.parts[1] && cmd.parts[1].type !== COO_COMMAND_PART_IDENTIFIER) {
                    cmd.hasSubblock = false;
                }

                if (cmd.name === 'NOMATCH') {
                    cmd.hasSubblock = false;
                }
            },

            getCodeBefore: function(cmd) {
                var tmp;

                if (cmd.name === 'NOMATCH') {
                    return actualName + ': true' + (cmd.last ? '' : ',');
                } else if (cmd.hasSubblock) {
                    tmp = cooGetScopeVariablesDecl(cmd);

                    if (tmp === '') {
                        cmd.parts[0].error = 'No value';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    return actualName + ': function(' + (cmd.parts[1] ? cmd.parts[1].value : '') + ') {' + tmp;
                } else {
                    var ret = [];

                    ret.push(actualName);
                    ret.push(': ');

                    if (cmd.parts[1].type !== COO_COMMAND_PART_JS) {
                        ret.push('new RegExp(');
                    }

                    ret.push(cmd.parts[1].value);

                    if (cmd.parts[1].type !== COO_COMMAND_PART_JS) {
                        ret.push(')');
                    }

                    if (!cmd.last) {
                        ret.push(',')
                    }

                    return ret.join('');
                }
            },

            getCodeAfter: function(cmd) {
                if (cmd.hasSubblock) {
                    return cooGetScopeRet(cmd) + '}' + (cmd.last ? '' : ',');
                }
            }
        };
    }

    cooObjectBase(
        {
            cmdName: 'ROUTE',
            cmdStorage: 'CooCoo.Route',
            baseClass: {name: 'CooCoo.RouteBase'},
            getCodeBeforeAfter: function(cmd) {
                var ret = [];

                if (cmd.debug) {
                    ret.push('\n');
                }

                ret.push(INDENT);
                ret.push('id: ');
                ret.push(++routeId);
                ret.push(',');

                return ret.join('');
            },

            getCodeAfterAfter: function(cmd) {
                var ret = [];

                if (!Object.keys(cmd.data.methods).length) {
                    cmd.parts[0].error = 'Route should have at least one of PATHNAME, SEARCH, HASH or NOMATCH';
                    cmd.file.errorUnexpectedPart(cmd.parts[0]);
                }

                ret.push('new CooCoo.Route.');
                ret.push(cmd.parts[1].value);
                ret.push('();\n');

                return ret.join('');
            }
        },
        {
            specialMethods: {
                PATHNAME: createRouteMethod('pathname'),
                SEARCH: createRouteMethod('search'),
                HASH: createRouteMethod('hash'),
                NOMATCH: createRouteMethod('nomatch')
            }
        },
        {
            'ROUTE': {
                '': function(cmd) {
                    cmd.hasSubblock = true;

                    cmd.processChild = matchOnOff;

                    cmd.getCodeBefore = function() {
                        if (!cmd.data.on) {
                            cmd.parts[0].error = 'Missing ON declaration';
                            cmd.file.errorUnexpectedPart(cmd.parts[0]);
                        }

                        if (!cmd.data.off) {
                            cmd.parts[0].error = 'Missing OFF declaration';
                            cmd.file.errorUnexpectedPart(cmd.parts[0]);
                        }

                        var ret = [];

                        ret.push('new CooCoo.Route.' + cmd.parts[1].value + '(this, ');

                        cmd.data.on.ignore = false;
                        cooRunGenerators(cmd.data.on, ret, 1);
                        cmd.data.on.ignore = true;

                        ret[ret.length - 1] += ',';

                        cmd.data.off.ignore = false;
                        cooRunGenerators(cmd.data.off, ret, 1);
                        cmd.data.off.ignore = true;

                        ret.push(');');

                        return ret.join('\n');
                    };

                    cmd.getCodeAfter = function() {

                    };
                }
            }
        }
    );

    function processOnOff(cmd) {
        cmd.ignore = true;
        cmd.hasSubblock = true;
        cmd.valueRequired = false;

        cooCreateScope(cmd);

        var params = cooExtractParamNames(cmd, cmd.parts, 1),
            p;

        for (p in params) {
            cooPushScopeVariable(cmd, p, false);
        }

        cmd.getCodeBefore = function() {
            var ret = [];

            ret.push('function(');
            ret.push(cooGetParamsDecl(params));
            ret.push(') {');
            ret.push(cooGetScopeVariablesDecl(cmd));

            return ret.join('');
        };

        cmd.getCodeAfter = function() {
            return '}';
        };
    }

    function matchOnOff(cmd) {
        return cooMatchCommand(cmd, {
            'ON': {
                '*': function() {
                    if (cmd.parent.data.on) {
                        cmd.parts[0].error = 'Duplicate ON declaration';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    cmd.parent.data.on = cmd;

                    processOnOff(cmd);
                }
            },

            'OFF': {
                '*': function() {
                    if (cmd.parent.data.off) {
                        cmd.parts[0].error = 'Duplicate OFF declaration';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    cmd.parent.data.off = cmd;

                    processOnOff(cmd);
                }
            }
        });
    }
})();
<commit_msg>Missing semicolon.<commit_after>(function() {
    /* global cooObjectBase */
    /* global cooGetScopeVariablesDecl */
    /* global cooGetScopeRet */
    /* global COO_COMMAND_PART_STRING */
    /* global COO_COMMAND_PART_JS */
    /* global COO_COMMAND_PART_IDENTIFIER */
    /* global cooExtractParamNames */
    /* global cooPushScopeVariable */
    /* global INDENT */
    /* global cooRunGenerators */
    /* global cooCreateScope */
    /* global cooMatchCommand */
    /* global cooGetParamsDecl */

    var routeId = 0;

    function createRouteMethod(actualName) {
        return {
            actualName: actualName,
            allowValues: true,

            extractParams: function(cmd) {
                var params;

                if (cmd.name === 'NOMATCH') {
                    if (cmd.parts.length > 1) {
                        cmd.file.errorUnexpectedPart(cmd.parts[1]);
                    }

                    if (Object.keys(cmd.root.data.methods).length) {
                        cmd.parts[0].error = 'Either combination of PATHNAME, SEARCH and HASH or NOMATCH is possible';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }
                } else {
                    if ('nomatch' in cmd.root.data.methods) {
                        cmd.parts[0].error = 'Either NOMATCH or combination of PATHNAME, SEARCH and HASH is possible';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }
                }

                if (cmd.parts.length > 1) {
                    if (cmd.parts[1].type === COO_COMMAND_PART_STRING ||
                        cmd.parts[1].type === COO_COMMAND_PART_JS)
                    {
                        params = {};
                    } else if (cmd.parts[1].type === COO_COMMAND_PART_IDENTIFIER) {
                        params = cooExtractParamNames(cmd, cmd.parts, 1);

                        for (var p in params) {
                            cooPushScopeVariable(cmd, p, false);
                        }
                    } else {
                        cmd.file.errorUnexpectedPart(cmd.parts[1]);
                    }
                }

                if (cmd.parts.length > 2) {
                    cmd.file.errorUnexpectedPart(cmd.parts[2]);
                }

                return params;
            },

            tuneCommand: function(cmd) {
                if (cmd.parts[1] && cmd.parts[1].type !== COO_COMMAND_PART_IDENTIFIER) {
                    cmd.hasSubblock = false;
                }

                if (cmd.name === 'NOMATCH') {
                    cmd.hasSubblock = false;
                }
            },

            getCodeBefore: function(cmd) {
                var tmp;

                if (cmd.name === 'NOMATCH') {
                    return actualName + ': true' + (cmd.last ? '' : ',');
                } else if (cmd.hasSubblock) {
                    tmp = cooGetScopeVariablesDecl(cmd);

                    if (tmp === '') {
                        cmd.parts[0].error = 'No value';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    return actualName + ': function(' + (cmd.parts[1] ? cmd.parts[1].value : '') + ') {' + tmp;
                } else {
                    var ret = [];

                    ret.push(actualName);
                    ret.push(': ');

                    if (cmd.parts[1].type !== COO_COMMAND_PART_JS) {
                        ret.push('new RegExp(');
                    }

                    ret.push(cmd.parts[1].value);

                    if (cmd.parts[1].type !== COO_COMMAND_PART_JS) {
                        ret.push(')');
                    }

                    if (!cmd.last) {
                        ret.push(',');
                    }

                    return ret.join('');
                }
            },

            getCodeAfter: function(cmd) {
                if (cmd.hasSubblock) {
                    return cooGetScopeRet(cmd) + '}' + (cmd.last ? '' : ',');
                }
            }
        };
    }

    cooObjectBase(
        {
            cmdName: 'ROUTE',
            cmdStorage: 'CooCoo.Route',
            baseClass: {name: 'CooCoo.RouteBase'},
            getCodeBeforeAfter: function(cmd) {
                var ret = [];

                if (cmd.debug) {
                    ret.push('\n');
                }

                ret.push(INDENT);
                ret.push('id: ');
                ret.push(++routeId);
                ret.push(',');

                return ret.join('');
            },

            getCodeAfterAfter: function(cmd) {
                var ret = [];

                if (!Object.keys(cmd.data.methods).length) {
                    cmd.parts[0].error = 'Route should have at least one of PATHNAME, SEARCH, HASH or NOMATCH';
                    cmd.file.errorUnexpectedPart(cmd.parts[0]);
                }

                ret.push('new CooCoo.Route.');
                ret.push(cmd.parts[1].value);
                ret.push('();\n');

                return ret.join('');
            }
        },
        {
            specialMethods: {
                PATHNAME: createRouteMethod('pathname'),
                SEARCH: createRouteMethod('search'),
                HASH: createRouteMethod('hash'),
                NOMATCH: createRouteMethod('nomatch')
            }
        },
        {
            'ROUTE': {
                '': function(cmd) {
                    cmd.hasSubblock = true;

                    cmd.processChild = matchOnOff;

                    cmd.getCodeBefore = function() {
                        if (!cmd.data.on) {
                            cmd.parts[0].error = 'Missing ON declaration';
                            cmd.file.errorUnexpectedPart(cmd.parts[0]);
                        }

                        if (!cmd.data.off) {
                            cmd.parts[0].error = 'Missing OFF declaration';
                            cmd.file.errorUnexpectedPart(cmd.parts[0]);
                        }

                        var ret = [];

                        ret.push('new CooCoo.Route.' + cmd.parts[1].value + '(this, ');

                        cmd.data.on.ignore = false;
                        cooRunGenerators(cmd.data.on, ret, 1);
                        cmd.data.on.ignore = true;

                        ret[ret.length - 1] += ',';

                        cmd.data.off.ignore = false;
                        cooRunGenerators(cmd.data.off, ret, 1);
                        cmd.data.off.ignore = true;

                        ret.push(');');

                        return ret.join('\n');
                    };

                    cmd.getCodeAfter = function() {

                    };
                }
            }
        }
    );

    function processOnOff(cmd) {
        cmd.ignore = true;
        cmd.hasSubblock = true;
        cmd.valueRequired = false;

        cooCreateScope(cmd);

        var params = cooExtractParamNames(cmd, cmd.parts, 1),
            p;

        for (p in params) {
            cooPushScopeVariable(cmd, p, false);
        }

        cmd.getCodeBefore = function() {
            var ret = [];

            ret.push('function(');
            ret.push(cooGetParamsDecl(params));
            ret.push(') {');
            ret.push(cooGetScopeVariablesDecl(cmd));

            return ret.join('');
        };

        cmd.getCodeAfter = function() {
            return '}';
        };
    }

    function matchOnOff(cmd) {
        return cooMatchCommand(cmd, {
            'ON': {
                '*': function() {
                    if (cmd.parent.data.on) {
                        cmd.parts[0].error = 'Duplicate ON declaration';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    cmd.parent.data.on = cmd;

                    processOnOff(cmd);
                }
            },

            'OFF': {
                '*': function() {
                    if (cmd.parent.data.off) {
                        cmd.parts[0].error = 'Duplicate OFF declaration';
                        cmd.file.errorUnexpectedPart(cmd.parts[0]);
                    }

                    cmd.parent.data.off = cmd;

                    processOnOff(cmd);
                }
            }
        });
    }
})();
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use babel';
'use strict';

import Discord from 'discord.js';
import config from './config';
import logger from './logger';

// Version info
export const VERSION = { code: 10101, string: '1.1.1' };

// Commands
import HelpCommand from './commands/help';
import AboutCommand from './commands/about';
import DiceRollCommand from './commands/dice-roll';
import MaxDiceRollCommand from './commands/max-roll';
import MinDiceRollCommand from './commands/min-roll';
import ViewCharacterCommand from './commands/view-character';
import ListCharactersCommand from './commands/list-characters';
import AddCharacterCommand from './commands/add-character';
import DeleteCharacterCommand from './commands/delete-character';
export const commands = [
	HelpCommand,
	AboutCommand,
	DiceRollCommand,
	MaxDiceRollCommand,
	MinDiceRollCommand,
	ViewCharacterCommand,
	ListCharactersCommand,
	AddCharacterCommand,
	DeleteCharacterCommand
];

logger.info('RPBot v' + VERSION.string + ' (' + VERSION.code + ') is starting...');

// Verify that the credentials are usable
if(!config.token && (!config.email || !config.password)) {
	logger.error('Both "email" and "password" must be specified if not using a token.');
	process.exit(1);
}

// Create client
const clientOptions = { autoReconnect: config.autoReconnect, forceFetchUsers: true, disableEveryone: true };
export const client = new Discord.Client(clientOptions);
logger.info('Client created.', clientOptions);
client.on('ready', () => {
	logger.info('Bot is ready; logged in as ' + client.user.username + '#' + client.user.discriminator + ' (ID ' + client.user.id + ')');
});
client.on('error', e => {
	logger.error(e);
});
client.on('warn', e => {
	logger.warn(e);
});
client.on('debug', e => {
	logger.debug(e);
});
client.on('disconnected', () => {
	logger.error('Disconnected.');
});

// Set up command recognition
client.on('message', message => {
	if(message.author !== client.user) {
		commandLoop: for(const command of commands) {
			for(const match of command.triggers) {
				const matches = match.exec(message.content);
				if(matches) {
					logger.info('Running ' + command.name + '.', {
						message: message.toString(),
						matches: matches.toString(),
						user: message.author.username + '#' + message.author.discriminator,
						userID: message.author.id,
						server: message.server.toString(),
						serverID: message.server.id
					});
					command.run(message, matches);
					break commandLoop;
				}
			}
		}
	}
});

// Log in
const loginCallback = e => {
	if(e) logger.error('Failed to login.', e);
}
if(config.token) {
	logger.info('Logging in with token...');
	client.loginWithToken(config.token, config.email, config.password, loginCallback);
} else {
	logger.info('Logging in with email and password...');
	client.login(config.email, config.password, loginCallback);
}
<commit_msg>Added logging for config<commit_after>#!/usr/bin/env node
'use babel';
'use strict';

import Discord from 'discord.js';
import config from './config';
import logger from './logger';

// Version info
export const VERSION = { code: 10101, string: '1.1.1' };

// Commands
import HelpCommand from './commands/help';
import AboutCommand from './commands/about';
import DiceRollCommand from './commands/dice-roll';
import MaxDiceRollCommand from './commands/max-roll';
import MinDiceRollCommand from './commands/min-roll';
import ViewCharacterCommand from './commands/view-character';
import ListCharactersCommand from './commands/list-characters';
import AddCharacterCommand from './commands/add-character';
import DeleteCharacterCommand from './commands/delete-character';
export const commands = [
	HelpCommand,
	AboutCommand,
	DiceRollCommand,
	MaxDiceRollCommand,
	MinDiceRollCommand,
	ViewCharacterCommand,
	ListCharactersCommand,
	AddCharacterCommand,
	DeleteCharacterCommand
];

logger.info('RPBot v' + VERSION.string + ' (' + VERSION.code + ') is starting...');
logger.debug('Configuration:', config);

// Verify that the credentials are usable
if(!config.token && (!config.email || !config.password)) {
	logger.error('Both "email" and "password" must be specified if not using a token.');
	process.exit(1);
}

// Create client
const clientOptions = { autoReconnect: config.autoReconnect, forceFetchUsers: true, disableEveryone: true };
export const client = new Discord.Client(clientOptions);
logger.info('Client created.', clientOptions);
client.on('ready', () => {
	logger.info('Bot is ready; logged in as ' + client.user.username + '#' + client.user.discriminator + ' (ID ' + client.user.id + ')');
});
client.on('error', e => {
	logger.error(e);
});
client.on('warn', e => {
	logger.warn(e);
});
client.on('debug', e => {
	logger.debug(e);
});
client.on('disconnected', () => {
	logger.error('Disconnected.');
});

// Set up command recognition
client.on('message', message => {
	if(message.author !== client.user) {
		commandLoop: for(const command of commands) {
			for(const match of command.triggers) {
				const matches = match.exec(message.content);
				if(matches) {
					logger.info('Running ' + command.name + '.', {
						message: message.toString(),
						matches: matches.toString(),
						user: message.author.username + '#' + message.author.discriminator,
						userID: message.author.id,
						server: message.server.toString(),
						serverID: message.server.id
					});
					command.run(message, matches);
					break commandLoop;
				}
			}
		}
	}
});

// Log in
const loginCallback = e => {
	if(e) logger.error('Failed to login.', e);
}
if(config.token) {
	logger.info('Logging in with token...');
	client.loginWithToken(config.token, config.email, config.password, loginCallback);
} else {
	logger.info('Logging in with email and password...');
	client.login(config.email, config.password, loginCallback);
}
<|endoftext|>


<commit_before>//handle setupevents as quickly as possible
const setupEvents = require('./setupWinEvents');
if (setupEvents.handleSquirrelEvent()) {
   // squirrel event handled and app will exit in 1000ms, so don't do anything else
   return;
}

const electron = require('electron');
// Module to control application life.
const {app, Menu} = require('electron');
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const os = require('os');
const path = require('path');
const url = require('url');

// 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 mainWindow;

function createWindow() {
    // Create the browser window.
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 768,
        minWidth: 1024,
        minHeight: 768,
        webPreferences: {
            webSecurity: false
        }
    });

    // mainWindow.setResizable(false);

    // and load the index.html of the app.
    const startUrl = process.env.ELECTRON_START_URL || url.format({
            pathname: path.join(__dirname, '/../build/index.html'),
            protocol: 'file:',
            slashes: true
        });
    mainWindow.loadURL(startUrl);

    mainWindow.webContents.openDevTools();

    // Open the DevTools.
    if (process.env.ELECTRON_DEV) {
        mainWindow.webContents.openDevTools();
    }

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null
    });

    if (os.platform() !== 'win32') {
        const template = [{
            label: "Safex Wallet 0.0.7",
            submenu: [
                { label: "About Safex Wallet v0.0.7", selector: "orderFrontStandardAboutPanel:" },
                { type: "separator" },
                { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
            ]}, {
            label: "Edit",
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
            ]}
        ];

        Menu.setApplicationMenu(Menu.buildFromTemplate(template));
    }
}

// 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.

require('electron-context-menu')({
	prepend: (params, browserWindow) => [{
        label: 'Safex Wallet',
    }],
    shouldShowMenu: (event, params) => params.mediaType!=='image',
    showInspectElement: false,
});

let win;

app.on('ready', createWindow, () => {
	win = new BrowserWindow();
});

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // 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', function () {
    // 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 (mainWindow === null) {
        createWindow()
    }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>window size fix for windows<commit_after>//handle setupevents as quickly as possible
const setupEvents = require('./setupWinEvents');
if (setupEvents.handleSquirrelEvent()) {
   // squirrel event handled and app will exit in 1000ms, so don't do anything else
   return;
}

const electron = require('electron');
// Module to control application life.
const {app, Menu} = require('electron');
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const os = require('os');
const path = require('path');
const url = require('url');

// 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 mainWindow;

function createWindow() {
    // Create the browser window.
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 768,
        minWidth: 1024,
        minHeight: 768,
        webPreferences: {
            webSecurity: false
        },
        // resizable: false,
        maximizable: false,
        useContentSize: true
    });

    // mainWindow.setResizable(false);

    // and load the index.html of the app.
    const startUrl = process.env.ELECTRON_START_URL || url.format({
            pathname: path.join(__dirname, '/../build/index.html'),
            protocol: 'file:',
            slashes: true
        });
    mainWindow.loadURL(startUrl);

    mainWindow.webContents.openDevTools();

    // Open the DevTools.
    if (process.env.ELECTRON_DEV) {
        mainWindow.webContents.openDevTools();
    }

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null
    });

    if (os.platform() !== 'win32') {
        const template = [{
            label: "Safex Wallet 0.0.7",
            submenu: [
                { label: "About Safex Wallet v0.0.7", selector: "orderFrontStandardAboutPanel:" },
                { type: "separator" },
                { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); }}
            ]}, {
            label: "Edit",
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
            ]}
        ];

        Menu.setApplicationMenu(Menu.buildFromTemplate(template));
    }
}

// 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.

require('electron-context-menu')({
	prepend: (params, browserWindow) => [{
        label: 'Safex Wallet',
    }],
    shouldShowMenu: (event, params) => params.mediaType!=='image',
    showInspectElement: false,
});

let win;

app.on('ready', createWindow, () => {
	win = new BrowserWindow();
});

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // 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', function () {
    // 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 (mainWindow === null) {
        createWindow()
    }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>


<commit_before>/**
 * Player.js
 *
 * Foofighter.js — Player entity
 *
 * This class represents the data model and display entity for a
 * Player's startship in the game environment.
 */

;(function( FooFighter ){

'use strict';

function Player ( gameState, group ) {
    this.gameState = gameState;
    this.config = {};
    this.sprite = {};
    this.movementVelocity = 250;
    this.displayStates = {
        neutral: 'player.png',
        left: 'playerLeft.png',
        right: 'playerRight.png'
    };
    this.fireTimer = 250;
    this.canFire = true;
    this.group = group;
    this.config.laser = {
        velocity: -500,
        fireTimer: 250
    };
    this.lasers = gameState.groups.lasers;
    this.bindEvents();
}

var proto = Player.prototype;

proto.bindEvents = function(){
    var vent = this.gameState.vent;

    vent.on('create', this.create.bind(this));
    vent.on('update', function(){
        this.movementHandler();
        this.weaponHandler();
    }.bind(this));
    return this;
};

proto.create = function(){
    var game = this.gameState.game;
    this.sprite = game.add.sprite(
        game.world.centerX, game.world.centerY,
        'sprites', this.displayStates.neutral
    );
    this.sprite.anchor = {
        x: 0.5,
        y: 0.5
    };
    this.group.add(this.sprite);
    return this;
};

proto.movementHandler = function(){
    var game = this.gameState.game,
        magnitude = this.movementVelocity,
        modifier = 1,
        axis,
        cursors = this.gameState.cursors;

    this.sprite.body.velocity.setTo(0, 0);

    if ( cursors.up.isDown ) {
        modifier = -1;
        axis = 'y';
    } else if ( cursors.down.isDown ) {
        modifier = 1;
        axis = 'y';
    } else if (cursors.left.isDown) {
        this.sprite.loadTexture('sprites', this.displayStates.left);
        modifier = -1;
        axis = 'x';
    } else if (cursors.right.isDown) {
        this.sprite.loadTexture('sprites', this.displayStates.right);
        modifier = 1;
        axis = 'x';
    } else {
        this.sprite.loadTexture('sprites', this.displayStates.neutral);
    }

    this.sprite.body.velocity[axis] = (modifier * magnitude);
    return this;
};

proto.weaponHandler = function(){
    var game = this.gameState.game,
        cursors = this.gameState.cursors,
        keyboard = this.gameState.keyboard,
        laser;

    if ( !this.canFire ) {
        return false;
    }

    if ( keyboard.isDown(Phaser.Keyboard.SPACEBAR) ) {
        this.createLaser();
        this.canFire = false;
        setTimeout(function(){
            this.canFire = true;
        }.bind(this), this.config.laser.fireTimer);
    }
    return this;
};

proto.createLaser = function(){
    var game = this.gameState.game,
        velocity = this.config.laser.velocity,
        laser;

    laser = game.add.sprite(
        this.sprite.body.x + this.sprite.body.width/2,
        this.sprite.body.y - 10,
        'sprites', 'laserGreen.png'
    );
    this.lasers.add(laser);
    laser.anchor = {
        x: 0.5,
        y: 0.5
    };
    laser.body.velocity.y = velocity;
    laser.events.onOutOfBounds.add(laser.kill, laser);
    return this;
};

FooFighter.Player = Player;

})(FooFighter);
<commit_msg>yknow what they say: dead players shoot no lasers<commit_after>/**
 * Player.js
 *
 * Foofighter.js — Player entity
 *
 * This class represents the data model and display entity for a
 * Player's startship in the game environment.
 */

;(function( FooFighter ){

'use strict';

function Player ( gameState, group ) {
    this.gameState = gameState;
    this.config = {};
    this.sprite = {};
    this.movementVelocity = 250;
    this.displayStates = {
        neutral: 'player.png',
        left: 'playerLeft.png',
        right: 'playerRight.png'
    };
    this.fireTimer = 250;
    this.canFire = true;
    this.group = group;
    this.config.laser = {
        velocity: -500,
        fireTimer: 250
    };
    this.lasers = gameState.groups.lasers;
    this.bindEvents();
}

var proto = Player.prototype;

proto.bindEvents = function(){
    var vent = this.gameState.vent;

    vent.on('create', this.create.bind(this));
    vent.on('update', function(){
        this.movementHandler();
        this.weaponHandler();
    }.bind(this));
    return this;
};

proto.create = function(){
    var game = this.gameState.game;
    this.sprite = game.add.sprite(
        game.world.centerX, game.world.centerY,
        'sprites', this.displayStates.neutral
    );
    this.sprite.anchor = {
        x: 0.5,
        y: 0.5
    };
    this.group.add(this.sprite);
    return this;
};

proto.movementHandler = function(){
    var game = this.gameState.game,
        magnitude = this.movementVelocity,
        modifier = 1,
        axis,
        cursors = this.gameState.cursors;

    this.sprite.body.velocity.setTo(0, 0);

    if ( cursors.up.isDown ) {
        modifier = -1;
        axis = 'y';
    } else if ( cursors.down.isDown ) {
        modifier = 1;
        axis = 'y';
    } else if (cursors.left.isDown) {
        this.sprite.loadTexture('sprites', this.displayStates.left);
        modifier = -1;
        axis = 'x';
    } else if (cursors.right.isDown) {
        this.sprite.loadTexture('sprites', this.displayStates.right);
        modifier = 1;
        axis = 'x';
    } else {
        this.sprite.loadTexture('sprites', this.displayStates.neutral);
    }

    this.sprite.body.velocity[axis] = (modifier * magnitude);
    return this;
};

proto.weaponHandler = function(){
    var game = this.gameState.game,
        cursors = this.gameState.cursors,
        keyboard = this.gameState.keyboard,
        laser;

    if ( !this.canFire || !this.sprite.alive ) {
        return false;
    }

    if ( keyboard.isDown(Phaser.Keyboard.SPACEBAR) ) {
        this.createLaser();
        this.canFire = false;
        setTimeout(function(){
            this.canFire = true;
        }.bind(this), this.config.laser.fireTimer);
    }
    return this;
};

proto.createLaser = function(){
    var game = this.gameState.game,
        velocity = this.config.laser.velocity,
        laser;

    laser = game.add.sprite(
        this.sprite.body.x + this.sprite.body.width/2,
        this.sprite.body.y - 10,
        'sprites', 'laserGreen.png'
    );
    this.lasers.add(laser);
    laser.anchor = {
        x: 0.5,
        y: 0.5
    };
    laser.body.velocity.y = velocity;
    laser.events.onOutOfBounds.add(laser.kill, laser);
    return this;
};

FooFighter.Player = Player;

})(FooFighter);
<|endoftext|>


<commit_before>'use strict';

const Lines=require('./html-lines.js');
const UnescapedLines=require('crnx-base/lines');
const CollectionFeature=require('./collection-feature.js');

function capitalize(s) {
	return s.charAt(0).toUpperCase()+s.slice(1);
}
function toCamelCase(s) {
	return s.split('.').map((w,i)=>i>0?capitalize(w):w).join('');
}

class Filter {
	constructor(options,n) {
		this.options=options;
		this.n=n;
	}
	get nSuffix() {
		if (this.n!==undefined) {
			return '.'+this.n;
		} else {
			return '';
		}
	}
	getPropertyOption(property) {
		return this.options[property.name];
	}
	getPropertyOptionName(property) {
		return 'options.filters.'+this.type+'.'+property.name;
	}
	get nodeJsName() {
		return toCamelCase(this.type+this.nSuffix+'.node');
	}
	getPropertyInputHtmlName(propertyName) {
		return 'my.'+this.type+this.nSuffix+'.'+propertyName;
	}
	//getPropertyInputJsName(propertyName) {
	//	return toCamelCase(this.type+this.nSuffix+'.'+propertyName+'.input');
	//}
	requestFeatureContext(featureContext) {
	}
	getHtmlPropertyLines(i18n,property) {
		const lines=new Lines;
		const option=this.getPropertyOption(property);
		const inputHtmlName=this.getPropertyInputHtmlName(property.name);
		if (property.type=='range' && option.input) {
			lines.a(
				""+i18n(this.getPropertyOptionName(property))+"",
				"<input id='"+inputHtmlName+"' type='range' value='"+option+"' min='"+option.min+"' max='"+option.max+"'"+(option.step!=1?" step='"+option.step+"'":"")+" />"
			);
		} else if (property.type=='select' && option.input) {
			lines.a(
				""+i18n(this.getPropertyOptionName(property))+"",
				(
					new Lines(...option.availableValues.map(value=>"<option>"+value+"</option>"))
				).wrap(
					"<select id='"+inputHtmlName+"'>","</select>"
				)
			);
		} else if (property.type=='xhr') {
			lines.a(
				""+i18n(this.getPropertyOptionName(property)+'.loading')+""
			);
		}
		return lines;
	}
	getHtmlLines(featureContext,i18n) {
		const lines=new Lines(
			...this.nodeProperties.map(property=>this.getHtmlPropertyLines(i18n,property))
		);
		return lines.wrapIfNotEmpty("
","
");
	}
	getJsInitLines(i18n,prevNodeJsNames) {
		return new Lines(
			new UnescapedLines("// "+i18n('comment.filters.'+this.type)),
			"var "+this.nodeJsName+"=ctx."+this.ctxCreateMethodName+"();",
			...prevNodeJsNames.map(
				prevNodeJsName=>prevNodeJsName+".connect("+this.nodeJsName+");"
			)
		);
	}
	// abstract:
	// get type()
	// get ctxCreateMethodName()
	// get nodeJsNames()
	// get nodeProperties()
}

class SinglePathFilter extends Filter {
	get nodeJsNames() {
		return [this.nodeJsName];
	}
	getJsInitLines(i18n,prevNodeJsNames) {
		const lines=super.getJsInitLines(i18n,prevNodeJsNames);
		this.nodeProperties.forEach(property=>{
			const option=this.getPropertyOption(property);
			const nodePropertyJsName=this.nodeJsName+"."+property.name+(property.type=='range'?".value":"");
			//if (property.type=='range' || property.type=='select') {
				let value=option.value;
				if (property.type=='select') {
					value="'"+value+"'";
				}
				if (property.fn) {
					value=property.fn(value);
				}
				if (option.value!=option.defaultValue) {
					lines.a(
						nodePropertyJsName+"="+value+";"
					);
				}
			//}
			if (option.input) {
				//const inputJsName=this.getPropertyInputJsName(property.name);
				const inputHtmlName=this.getPropertyInputHtmlName(property.name);
				let value="this.value";
				if (property.fn) {
					value=property.fn(value);
				}
				const eventProp=(property.type=='range'?'oninput':'onchange'); //
				lines.a(
					//"var "+inputJsName+"=document.getElementById('"+inputHtmlName+"');",
					//(property.type=='range'?inputJsName+".oninput=":"")+inputJsName+".onchange=function(){",
					"document.getElementById('"+inputHtmlName+"')."+eventProp+"=function(){",
					//
					"	"+nodePropertyJsName+"="+value+";",
					"};"
				);
			}
		});
		return lines;
	}
}

const filterClasses={
	gain: class extends SinglePathFilter {
		get type()                { return 'gain'; }
		get ctxCreateMethodName() { return 'createGain'; }
		get nodeProperties() {
			return [
				{
					name:'gain',
					type:'range',
				}
			];
		}
	},
	panner: class extends SinglePathFilter {
		get type()                { return 'panner'; }
		get ctxCreateMethodName() { return 'createStereoPanner'; }
		get nodeProperties() {
			return [
				{
					name:'pan',
					type:'range',
				}
			];
		}
	},
	biquad: class extends SinglePathFilter {
		get type()                { return 'biquad'; }
		get ctxCreateMethodName() { return 'createBiquadFilter'; }
		get nodeProperties() {
			return [
				{
					name:'type',
					type:'select',
				},{
					name:'frequency',
					type:'range',
				},{
					name:'detune',
					type:'range',
				},{
					name:'Q',
					type:'range',
					fn:x=>`Math.pow(10,${x})`,
				}
			];
		}
	},
	convolver: class extends Filter {
		get type()                { return 'convolver'; }
		get ctxCreateMethodName() { return 'createConvolver'; }
		get nodeProperties() {
			return [
				{
					name:'reverb',
					type:'range',
				},{
					name:'buffer',
					type:'xhr',
				}
			];
		}
		get nodeJsNames() {
			return [this.wetGainNodeJsName,this.dryGainNodeJsName];
		}
		get dryGainNodeJsName() {
			return toCamelCase(this.type+this.nSuffix+'.dry.gain.node');
		}
		get wetGainNodeJsName() {
			return toCamelCase(this.type+this.nSuffix+'.wet.gain.node');
		}
		getJsInitLines(i18n,prevNodeJsNames) {
			const lines=super.getJsInitLines(i18n,prevNodeJsNames);
			const inputHtmlName=this.getPropertyInputHtmlName('reverb');
			const messageHtmlName=this.getPropertyInputHtmlName('buffer');
			lines.a(
				"var "+this.wetGainNodeJsName+"=ctx.createGain();",
				this.nodeJsName+".connect("+this.wetGainNodeJsName+");",
				"var "+this.dryGainNodeJsName+"=ctx.createGain();",
				...prevNodeJsNames.map(
					prevNodeJsName=>prevNodeJsName+".connect("+this.dryGainNodeJsName+");"
				)
			);
			if (this.options.reverb!=1) {
				lines.a(
					this.wetGainNodeJsName+".gain.value="+this.options.reverb+";"
				);
			}
			if (this.options.reverb!=0) {
				lines.a(
					this.dryGainNodeJsName+".gain.value="+(1-this.options.reverb)+";"
				);
			}
			if (this.options.reverb.input) {
				lines.a(
					"document.getElementById('"+inputHtmlName+"').oninput=function(){",
					"	"+this.wetGainNodeJsName+".gain.value=this.value;",
					"	"+this.dryGainNodeJsName+".gain.value=1-this.value;",
					"};"
				);
			}
			lines.a(
				"var xhr=new XMLHttpRequest();",
				"xhr.open('GET','"+this.options.url+"');", // TODO html escape
				"xhr.responseType='arraybuffer';",
				"xhr.onload=function(){",
				"	if (this.status==200) {", // TODO we are checking status here, but what if <audio>'s status is an error?
				"		ctx.decodeAudioData(this.response,function(buffer){",
				"			"+this.nodeJsName+".buffer=buffer;",
				"			document.getElementById('"+messageHtmlName+"').textContent='';",
				"		});",
				"	} else {",
				"		this.onerror();",
				"	}",
				"};",
				"xhr.onerror=function(){",
				"	document.getElementById('"+messageHtmlName+"').textContent='"+i18n('options.filters.convolver.buffer.error')+"';",
				"};",
				"xhr.send();"
			);
			return lines;
		}
	},
	equalizer: class extends Filter {
		get type()                { return 'equalizer'; }
		get ctxCreateMethodName() { return 'createBiquadFilter'; }
		get frequencies()         { return [60,170,350,1000,3500,10000]; }
		get nodeProperties() {
			return this.frequencies.map(freq=>({
				name:'gain'+freq,
				type:'range',
			}));
		}
		get nodeJsNames() {
			return [this.nodeJsName];
		}
		requestFeatureContext(featureContext) {
			if (!this.allGainsConstant) {
				featureContext.alignedInputs=true;
			}
		}
		getHtmlPropertyLines(i18n,property) {
			const lines=super.getHtmlPropertyLines(i18n,property);
			return lines.wrapIfNotEmpty("
","
");
		}
		getJsInitLines(i18n,prevNodeJsNames) {
			const affectedFreqs=[];
			const affectedOptions=[];
			this.frequencies.forEach((freq,i)=>{
				const option=this.getPropertyOption(this.nodeProperties[i]);
				if (option.input!=false || option.value!=0) {
					affectedFreqs.push(freq);
					affectedOptions.push(option);
				}
			});
			// { hack: make at least one node
			if (affectedFreqs.length==0) {
				affectedFreqs.push(this.frequencies[0]);
				affectedOptions.push(this.getPropertyOption(this.nodeProperties[0]));
			}
			// }
			const allGainsConstant=affectedOptions.every(option=>option.input==false);
			const noGainsConstant=affectedOptions.every(option=>option.input==true);
			const getJsData=()=>{
				const data=[];
				affectedFreqs.forEach((freq,i)=>{
					const option=affectedOptions[i];
					if (noGainsConstant) {
						data.push(freq);
					} else {
						data.push("["+freq+","+(option.input ? 'null' : option.value)+"]");
					}
				});
				return data.join();
			};
			const getJsDataItem=()=>{
				if (noGainsConstant) {
					return "freq";
				} else {
					return "freqData";
				}
			};
			const getJsLoopLines=()=>{
				const nodeJsName=(allGainsConstant ? this.nodeJsName : 'node');
				const lines=new Lines;
				if (!noGainsConstant) {
					lines.a("var freq=freqData[0], gain=freqData[1];");
				}
				if (allGainsConstant) {
					lines.a("");
				} else {
					lines.a("var ");
				}
				lines.t(nodeJsName+"=ctx."+this.ctxCreateMethodName+"();");
				if (prevNodeJsNames.length==1) {
					lines.a("prevNode.connect("+nodeJsName+");");
				} else {
					lines.a(
						"prevNodes.forEach(function(prevNode){",
						"	prevNode.connect("+nodeJsName+");",
						"});"
					);
				}
				lines.a(
					nodeJsName+".type='peaking';",
					nodeJsName+".frequency.value=freq;"
				);
				if (!noGainsConstant) {
					let gainLines=new Lines(nodeJsName+".gain.value=gain;");
					if (!allGainsConstant) {
						gainLines.wrap("if (gain!==null) {","}");
					}
					lines.a(gainLines);
				}
				if (!allGainsConstant) {
					const inputHtmlNamePrefix=this.getPropertyInputHtmlName('gain');
					let listenerLines=new Lines(
						"var element=document.getElementById('"+inputHtmlNamePrefix+"'+freq);",
						"(element.oninput=function(){",
						"	"+nodeJsName+".gain.value=element.value;",
						"})();"
					);
					if (!noGainsConstant) {
						listenerLines.wrap("if (gain===null) {","}");
					}
					lines.a(listenerLines);
				}
				const outerNodeJsName=(allGainsConstant ? nodeJsName : this.nodeJsName+"="+nodeJsName);
				if (prevNodeJsNames.length==1) {
					lines.a("prevNode="+outerNodeJsName+";");
				} else {
					lines.a("prevNodes=["+outerNodeJsName+"];");
				}
				return lines;
			};
			const lines=new Lines(
				new UnescapedLines("// "+i18n('comment.filters.'+this.type))
			);
			if (prevNodeJsNames.length==1) {
				lines.a("var prevNode="+prevNodeJsNames[0]+";");
			} else {
				lines.a("var prevNodes=["+prevNodeJsNames.join()+"];");
			}
			lines.a(
				"var "+this.nodeJsName+";",
				"["+getJsData()+"].forEach(function("+getJsDataItem()+"){",
				getJsLoopLines().indent(),
				"});"
			);
			return lines;
		}
	},
};

class FilterSequence extends CollectionFeature {
	getEntryClass(entryOption) {
		return filterClasses[entryOption.filter];
	}
	requestFeatureContext(featureContext) {
		if (this.entries.length>0) {
			featureContext.audioContext=true;
		}
		this.entries.forEach(entry=>{
			entry.requestFeatureContext(featureContext);
		});
	}
	getJsInitLines(featureContext,i18n,prevNodeJsNames) {
		const lines=super.getJsInitLines(...arguments);
		if (this.entries.length==0) {
			return lines;
		}
		lines.interleave(...this.entries.map(entry=>{
			const lines=entry.getJsInitLines(i18n,prevNodeJsNames);
			prevNodeJsNames=entry.nodeJsNames;
			return lines;
		}));
		return lines;
	}
	getNodeJsNames(featureContext,prevNodeJsNames) {
		if (this.entries.length>0) {
			return this.entries[this.entries.length-1].nodeJsNames;
		} else {
			return prevNodeJsNames;
		}
	}
}

module.exports=FilterSequence;
<commit_msg>don't output equalizer node if no freqs affected<commit_after>'use strict';

const Lines=require('./html-lines.js');
const UnescapedLines=require('crnx-base/lines');
const CollectionFeature=require('./collection-feature.js');

function capitalize(s) {
	return s.charAt(0).toUpperCase()+s.slice(1);
}
function toCamelCase(s) {
	return s.split('.').map((w,i)=>i>0?capitalize(w):w).join('');
}

class Filter {
	constructor(options,n) {
		this.options=options;
		this.n=n;
	}
	get nSuffix() {
		if (this.n!==undefined) {
			return '.'+this.n;
		} else {
			return '';
		}
	}
	getPropertyOptionName(property) {
		return 'options.filters.'+this.type+'.'+property.name;
	}
	get nodeJsName() {
		return toCamelCase(this.type+this.nSuffix+'.node');
	}
	getPropertyInputHtmlName(propertyName) {
		return 'my.'+this.type+this.nSuffix+'.'+propertyName;
	}
	//getPropertyInputJsName(propertyName) {
	//	return toCamelCase(this.type+this.nSuffix+'.'+propertyName+'.input');
	//}
	requestFeatureContext(featureContext) {
	}
	getHtmlPropertyLines(i18n,property) {
		const lines=new Lines;
		const option=this.options[property.name];
		const inputHtmlName=this.getPropertyInputHtmlName(property.name);
		if (property.type=='range' && option.input) {
			lines.a(
				""+i18n(this.getPropertyOptionName(property))+"",
				"<input id='"+inputHtmlName+"' type='range' value='"+option+"' min='"+option.min+"' max='"+option.max+"'"+(option.step!=1?" step='"+option.step+"'":"")+" />"
			);
		} else if (property.type=='select' && option.input) {
			lines.a(
				""+i18n(this.getPropertyOptionName(property))+"",
				(
					new Lines(...option.availableValues.map(value=>"<option>"+value+"</option>"))
				).wrap(
					"<select id='"+inputHtmlName+"'>","</select>"
				)
			);
		} else if (property.type=='xhr') {
			lines.a(
				""+i18n(this.getPropertyOptionName(property)+'.loading')+""
			);
		}
		return lines;
	}
	getHtmlLines(featureContext,i18n) {
		const lines=new Lines(
			...this.nodeProperties.map(property=>this.getHtmlPropertyLines(i18n,property))
		);
		return lines.wrapIfNotEmpty("
","
");
	}
	getJsInitLines(i18n,prevNodeJsNames) {
		return new Lines(
			new UnescapedLines("// "+i18n('comment.filters.'+this.type)),
			"var "+this.nodeJsName+"=ctx."+this.ctxCreateMethodName+"();",
			...prevNodeJsNames.map(
				prevNodeJsName=>prevNodeJsName+".connect("+this.nodeJsName+");"
			)
		);
	}
	getNodeJsNames(prevNodeJsNames) {
		return [this.nodeJsName];
	}
	// abstract:
	// get type()
	// get ctxCreateMethodName()
	// getNodeJsNames(prevNodeJsNames)
	// get nodeProperties()
}

class SinglePathFilter extends Filter {
	getJsInitLines(i18n,prevNodeJsNames) {
		const lines=super.getJsInitLines(i18n,prevNodeJsNames);
		this.nodeProperties.forEach(property=>{
			const option=this.options[property.name];
			const nodePropertyJsName=this.nodeJsName+"."+property.name+(property.type=='range'?".value":"");
			//if (property.type=='range' || property.type=='select') {
				let value=option.value;
				if (property.type=='select') {
					value="'"+value+"'";
				}
				if (property.fn) {
					value=property.fn(value);
				}
				if (option.value!=option.defaultValue) {
					lines.a(
						nodePropertyJsName+"="+value+";"
					);
				}
			//}
			if (option.input) {
				//const inputJsName=this.getPropertyInputJsName(property.name);
				const inputHtmlName=this.getPropertyInputHtmlName(property.name);
				let value="this.value";
				if (property.fn) {
					value=property.fn(value);
				}
				const eventProp=(property.type=='range'?'oninput':'onchange'); //
				lines.a(
					//"var "+inputJsName+"=document.getElementById('"+inputHtmlName+"');",
					//(property.type=='range'?inputJsName+".oninput=":"")+inputJsName+".onchange=function(){",
					"document.getElementById('"+inputHtmlName+"')."+eventProp+"=function(){",
					//
					"	"+nodePropertyJsName+"="+value+";",
					"};"
				);
			}
		});
		return lines;
	}
}

const filterClasses={
	gain: class extends SinglePathFilter {
		get type()                { return 'gain'; }
		get ctxCreateMethodName() { return 'createGain'; }
		get nodeProperties() {
			return [
				{
					name:'gain',
					type:'range',
				}
			];
		}
	},
	panner: class extends SinglePathFilter {
		get type()                { return 'panner'; }
		get ctxCreateMethodName() { return 'createStereoPanner'; }
		get nodeProperties() {
			return [
				{
					name:'pan',
					type:'range',
				}
			];
		}
	},
	biquad: class extends SinglePathFilter {
		get type()                { return 'biquad'; }
		get ctxCreateMethodName() { return 'createBiquadFilter'; }
		get nodeProperties() {
			return [
				{
					name:'type',
					type:'select',
				},{
					name:'frequency',
					type:'range',
				},{
					name:'detune',
					type:'range',
				},{
					name:'Q',
					type:'range',
					fn:x=>`Math.pow(10,${x})`,
				}
			];
		}
	},
	convolver: class extends Filter {
		get type()                { return 'convolver'; }
		get ctxCreateMethodName() { return 'createConvolver'; }
		get nodeProperties() {
			return [
				{
					name:'reverb',
					type:'range',
				},{
					name:'buffer',
					type:'xhr',
				}
			];
		}
		getNodeJsNames(prevNodeJsNames) {
			return [this.wetGainNodeJsName,this.dryGainNodeJsName];
		}
		get dryGainNodeJsName() {
			return toCamelCase(this.type+this.nSuffix+'.dry.gain.node');
		}
		get wetGainNodeJsName() {
			return toCamelCase(this.type+this.nSuffix+'.wet.gain.node');
		}
		getJsInitLines(i18n,prevNodeJsNames) {
			const lines=super.getJsInitLines(i18n,prevNodeJsNames);
			const inputHtmlName=this.getPropertyInputHtmlName('reverb');
			const messageHtmlName=this.getPropertyInputHtmlName('buffer');
			lines.a(
				"var "+this.wetGainNodeJsName+"=ctx.createGain();",
				this.nodeJsName+".connect("+this.wetGainNodeJsName+");",
				"var "+this.dryGainNodeJsName+"=ctx.createGain();",
				...prevNodeJsNames.map(
					prevNodeJsName=>prevNodeJsName+".connect("+this.dryGainNodeJsName+");"
				)
			);
			if (this.options.reverb!=1) {
				lines.a(
					this.wetGainNodeJsName+".gain.value="+this.options.reverb+";"
				);
			}
			if (this.options.reverb!=0) {
				lines.a(
					this.dryGainNodeJsName+".gain.value="+(1-this.options.reverb)+";"
				);
			}
			if (this.options.reverb.input) {
				lines.a(
					"document.getElementById('"+inputHtmlName+"').oninput=function(){",
					"	"+this.wetGainNodeJsName+".gain.value=this.value;",
					"	"+this.dryGainNodeJsName+".gain.value=1-this.value;",
					"};"
				);
			}
			lines.a(
				"var xhr=new XMLHttpRequest();",
				"xhr.open('GET','"+this.options.url+"');", // TODO html escape
				"xhr.responseType='arraybuffer';",
				"xhr.onload=function(){",
				"	if (this.status==200) {", // TODO we are checking status here, but what if <audio>'s status is an error?
				"		ctx.decodeAudioData(this.response,function(buffer){",
				"			"+this.nodeJsName+".buffer=buffer;",
				"			document.getElementById('"+messageHtmlName+"').textContent='';",
				"		});",
				"	} else {",
				"		this.onerror();",
				"	}",
				"};",
				"xhr.onerror=function(){",
				"	document.getElementById('"+messageHtmlName+"').textContent='"+i18n('options.filters.convolver.buffer.error')+"';",
				"};",
				"xhr.send();"
			);
			return lines;
		}
	},
	equalizer: class extends Filter {
		get type()                { return 'equalizer'; }
		get ctxCreateMethodName() { return 'createBiquadFilter'; }
		get frequencies()         { return [60,170,350,1000,3500,10000]; }
		get nodeProperties() {
			return this.frequencies.map(freq=>({
				name:'gain'+freq,
				type:'range',
			}));
		}
		getFrequencyOption(i) {
			return this.options[this.nodeProperties[i].name];
		}
		get affectedFreqsAndOptions() {
			return this.frequencies.map((freq,i)=>{
				const option=this.getFrequencyOption(i);
				return {freq,option};
			}).filter(fo=>(fo.option.input!=false || fo.option.value!=0));
		}
		getNodeJsNames(prevNodeJsNames) {
			if (this.affectedFreqsAndOptions.length>0) {
				return [this.nodeJsName];
			} else {
				return prevNodeJsNames;
			}
		}
		requestFeatureContext(featureContext) {
			if (!this.allGainsConstant) {
				featureContext.alignedInputs=true;
			}
		}
		getHtmlPropertyLines(i18n,property) {
			const lines=super.getHtmlPropertyLines(i18n,property);
			return lines.wrapIfNotEmpty("
","
");
		}
		getJsInitLines(i18n,prevNodeJsNames) {
			if (this.affectedFreqsAndOptions.length==0) {
				return new Lines;
			}
			const allGainsConstant=this.affectedFreqsAndOptions.every(fo=>fo.option.input==false);
			const noGainsConstant=this.affectedFreqsAndOptions.every(fo=>fo.option.input==true);
			const getJsData=()=>{
				const data=[];
				this.affectedFreqsAndOptions.forEach(fo=>{
					if (noGainsConstant) {
						data.push(fo.freq);
					} else {
						data.push("["+fo.freq+","+(fo.option.input ? 'null' : fo.option.value)+"]");
					}
				});
				return data.join();
			};
			const getJsDataItem=()=>{
				if (noGainsConstant) {
					return "freq";
				} else {
					return "freqData";
				}
			};
			const getJsLoopLines=()=>{
				const nodeJsName=(allGainsConstant ? this.nodeJsName : 'node');
				const lines=new Lines;
				if (!noGainsConstant) {
					lines.a("var freq=freqData[0], gain=freqData[1];");
				}
				if (allGainsConstant) {
					lines.a("");
				} else {
					lines.a("var ");
				}
				lines.t(nodeJsName+"=ctx."+this.ctxCreateMethodName+"();");
				if (prevNodeJsNames.length==1) {
					lines.a("prevNode.connect("+nodeJsName+");");
				} else {
					lines.a(
						"prevNodes.forEach(function(prevNode){",
						"	prevNode.connect("+nodeJsName+");",
						"});"
					);
				}
				lines.a(
					nodeJsName+".type='peaking';",
					nodeJsName+".frequency.value=freq;"
				);
				if (!noGainsConstant) {
					let gainLines=new Lines(nodeJsName+".gain.value=gain;");
					if (!allGainsConstant) {
						gainLines.wrap("if (gain!==null) {","}");
					}
					lines.a(gainLines);
				}
				if (!allGainsConstant) {
					const inputHtmlNamePrefix=this.getPropertyInputHtmlName('gain');
					let listenerLines=new Lines(
						"var element=document.getElementById('"+inputHtmlNamePrefix+"'+freq);",
						"(element.oninput=function(){",
						"	"+nodeJsName+".gain.value=element.value;",
						"})();"
					);
					if (!noGainsConstant) {
						listenerLines.wrap("if (gain===null) {","}");
					}
					lines.a(listenerLines);
				}
				const outerNodeJsName=(allGainsConstant ? nodeJsName : this.nodeJsName+"="+nodeJsName);
				if (prevNodeJsNames.length==1) {
					lines.a("prevNode="+outerNodeJsName+";");
				} else {
					lines.a("prevNodes=["+outerNodeJsName+"];");
				}
				return lines;
			};
			const lines=new Lines(
				new UnescapedLines("// "+i18n('comment.filters.'+this.type))
			);
			if (prevNodeJsNames.length==1) {
				lines.a("var prevNode="+prevNodeJsNames[0]+";");
			} else {
				lines.a("var prevNodes=["+prevNodeJsNames.join()+"];");
			}
			lines.a(
				"var "+this.nodeJsName+";",
				"["+getJsData()+"].forEach(function("+getJsDataItem()+"){",
				getJsLoopLines().indent(),
				"});"
			);
			return lines;
		}
	},
};

class FilterSequence extends CollectionFeature {
	getEntryClass(entryOption) {
		return filterClasses[entryOption.filter];
	}
	requestFeatureContext(featureContext) {
		if (this.entries.length>0) {
			featureContext.audioContext=true;
		}
		this.entries.forEach(entry=>{
			entry.requestFeatureContext(featureContext);
		});
	}
	getJsInitLines(featureContext,i18n,prevNodeJsNames) {
		const lines=super.getJsInitLines(...arguments);
		if (this.entries.length==0) {
			return lines;
		}
		lines.interleave(...this.entries.map(entry=>{
			const lines=entry.getJsInitLines(i18n,prevNodeJsNames);
			prevNodeJsNames=entry.getNodeJsNames(prevNodeJsNames);
			return lines;
		}));
		return lines;
	}
	getNodeJsNames(featureContext,prevNodeJsNames) {
		this.entries.forEach(entry=>{
			prevNodeJsNames=entry.getNodeJsNames(prevNodeJsNames);
		});
		return prevNodeJsNames;
	}
}

module.exports=FilterSequence;
<|endoftext|>


<commit_before>import * as I          from 'immutable';
import * as fw         from './freeWords';
import * as generators from '../common/generators';

import { Partition } from '../common/unionFind';

const range = (from, to) => {
  if (to < from)
    return new Array(from - to).fill(0).map((_, i) => from - i);
  else
    return new Array(to - from).fill(0).map((_, i) => from + i);
};

const allGens = function*(nrGens) {
  for (let i = 1; i <= nrGens; ++i)
    yield i;
  for (let i = 1; i <= nrGens; ++i)
    yield -i;
}

class CosetTable {
  constructor(nrGens) {
    this.nrGens = nrGens;
    this.table = [new Array(2 * nrGens + 1)];
    this.part = new Partition();
  }

  clone() {
    const t = new CosetTable(this.nrGens);
    t.table = this.table.slice().map(row => row.slice());
    t.part = this.part.clone();
    return t;
  }

  allGens() {
    return allGens(this.nrGens)
  }

  get size() {
    return this.table.length;
  }

  get(c, g) {
    return (this.table[c] || [])[g + this.nrGens];
  }

  set(c, g, d) {
    if (c == null)
      throw new Error('oops!');
    if (this.table[c] == null)
      this.table[c] = new Array(2 * this.nrGens + 1);
    this.table[c][g + this.nrGens] = d;
  }

  join(c, d, g) {
    this.set(c, g, d);
    this.set(d, -g, c);
  }

  canon(c) {
    return this.part.find(c);
  }

  identify(a, b) {
    const queue = [[a, b]];

    while (queue.length) {
      const [a, b] = queue.shift().map(x => this.canon(x));

      if (a != b) {
        this.part.union(a, b);

        for (const g of this.allGens()) {
          const ag = this.get(a, g);
          const bg = this.get(b, g);
          if (ag == null)
            this.set(a, g, bg);
          else {
            if (bg != null && this.canon(ag) != this.canon(bg))
              queue.push([ag, bg]);
            this.set(b, g, ag);
          }
        }
      }
    }
  }

  asCompactMatrix() {
    const toIdx = {};
    let i = 0;
    for (const k of range(0, this.table.length)) {
      if (this.canon(k) == k) {
        toIdx[k] = i;
        ++i;
      }
    }

    const result = [];
    for (const k of range(0, this.table.length)) {
      if (toIdx[k] != null) {
        const row = {};
        for (const g of this.allGens())
          row[g] = toIdx[this.canon(this.get(k, g))];
        result.push(row);
      }
    }

    return result;
  }
}

const scan = (table, w, start, limit) => {
  let row = start;
  let i = 0;

  while (i < limit) {
    const next = table.get(row, w.get(i));
    if (next == null)
      break;
    else {
      ++i;
      row = next;
    }
  }

  return { row, index: i };
};

const scanAndIdentify = (table, w, start) => {
  const n = w.size;
  const { row: head, index: i } = scan(table, w, start, n);
  const { row: tail, index: j } = scan(table, fw.inverse(w), start, n - i);

  if (i + j == n - 1) {
    table.join(head, tail, w.get(i));
    return head;
  }
  else if (i + j == n && head != tail)
    table.identify(head, tail);
  else
    return -1;
};

const _insertInOrderedSet = (elm, set, cmp) => {
  let i = 0;
  while (i < set.length && cmp(elm, set[i]) < 0)
    ++i;
  if (i >= set.length || cmp(elm, set[i]) != 0)
    set.splice(i, 0, elm);
};

const _expandRelators = relators => {
  const out = [];
  for (const rel of relators) {
    for (const w of fw.relatorPermutations(rel))
      _insertInOrderedSet(w, out, fw.compare);
  }
  return out;
};

export const cosetTable = (nrGens, relators, subgroupGens) => {
  const rels = _expandRelators(relators);
  const table = new CosetTable(nrGens);

  for (let i = 0; i < table.size; ++i) {
    if (i != table.canon(i))
      continue;

    for (const g of table.allGens()) {
      if (table.get(i, g) == null) {
        const n = table.size;
        if (n >= 10000)
          throw new Error('maximum coset table size reached');

        table.join(i, n, g);
        for (const w of rels)
          scanAndIdentify(table, w, n);
        for (const w of subgroupGens)
          scanAndIdentify(table, fw.word(w), table.canon(0));
      }
    }
  }

  return table.asCompactMatrix();
};

export const cosetRepresentatives = table => {
  const queue = [0];
  const reps = [fw.empty];

  while (queue.length) {
    const i = queue.shift();
    const w = reps[i];

    for (const g of Object.keys(table[i])) {
      const k = table[i][g];
      if (reps[k] == null) {
        reps[k] = fw.product([w, [parseInt(g)]]);
        queue.push(k);
      }
    }
  }

  return reps;
};

const _firstFreeInTable = table => {
  for (let k = 0; k < table.size; ++k) {
    for (const g of table.allGens()) {
      if (table.get(k, g) == null)
        return [k, g];
    }
  }
};

const _scanRecursively = (rels, table, index) => {
  const q = [index];

  while (q.length) {
    const row = q.shift();

    for (const rel of rels) {
      const next = scanAndIdentify(table, rel, row);
      if (next == null)
        return false;
      else if (next >= 0)
        q.push(next);
    }
  }

  return true;
};

const _potentialChildren = (table, rels, maxCosets) => {
  const [k, g] = _firstFreeInTable(table) || [];
  const result = [];

  if (k != null) {
    const ginv = -g;
    const limit = Math.min(table.size + 1, maxCosets);

    for (let pos = k; pos < limit; ++pos) {
      if (table.get(pos, ginv) == null) {
        const t = table.clone();
        t.join(k, pos, g);
        if (_scanRecursively(rels, t, k))
          result.push(t);
      }
    }
  }

  return result;
};

const _compareRenumberedFom = (table, start) => {
  const n2o = [start];
  const o2n = { [start]: 0 };

  for (let row = 0; row < table.size; ++row) {
    if (row >= n2o.length)
      throw new Error("coset table is not transitive");

    for (const g of table.allGens()) {
      const t = table.get(n2o[row], g);
      if (t != null && o2n[t] == null) {
        o2n[t] = n2o.length;
        n2o.push(t);
      }

      const nval = o2n[t];
      const oval = table.get(row, g);
      if (oval != nval)
        return oval == null ? -1 : nval == null ? 1 : nval - oval;
    }
  }

  return 0;
};

const _isCanonical = table => range(1, table.size)
  .every(start => _compareRenumberedFom(table, start) >= 0);

export const tables = (nrGens, relators, maxCosets) => {
  const rels = _expandRelators(relators);

  return generators.backtracker({
    root: new CosetTable(nrGens),

    extract(table) {
      return _firstFreeInTable(table) == null ? table.asCompactMatrix() : null;
    },

    children(table) {
      return _potentialChildren(table, rels, maxCosets)
        .filter(t => t.size && _isCanonical(t));
    }
  });
};

const _inducedTable = (nrGens, img, img0) => {
  const table = new CosetTable(nrGens);
  const o2n = { [img0]: 0 };
  const n2o = [img0];

  for (let i = 0; i < table.size; ++i) {
    for (const g of table.allGens()) {
      const k = img(n2o[i], g);
      if (o2n[k] == null)
        o2n[k] = table.size;
      const n = o2n[k];
      n2o[n] = k;
      table.join(i, n, g);
    }
  }

  return table.asCompactMatrix();
};

export const intersectionTable = (tableA, tableB) =>
  _inducedTable(
    tableA.first().keySeq(),
    (es, g) => [tableA.getIn([es[0], g]), tableB.getIn([es[1], g])],
    [0, 0]
  );

export const coreTable = base =>
  _inducedTable(
    Object.keys(base[0]).length / 2,
    (es, g) => es.map(e => base[e][g]),
    range(0, base.length)
  );

const _sgn = x => (x > 0) - (x < 0);
const _sum = a => a.reduce((x, y) => x + y, 0);

export const relatorAsVector = (rel, nrgens) => {
  const out = new Array(nrgens).fill(0);

  for (const w of rel) {
    if (w < 0)
      --out[-w - 1];
    else if (w > 0)
      ++out[w - 1];
  }

  return out;
};

export const relatorMatrix = (nrgens, relators) =>
  relators.map(rel => relatorAsVector(rel, nrgens));

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x && x.toString()).join(', ') + ' ]';
  };

  const test = table => {
    const reps = cosetRepresentatives(table);
    console.log(JSON.stringify(reps), Object.keys(reps).length);
  };

  const base = cosetTable(
    3,
    [[1,1], [2,2], [3,3], [1,2,1,2,1,2], [1,3,1,3], fw.raisedTo(3, [2,3])],
    [[1,2]]);

  test(base);
  test(coreTable(base));

  console.log(_expandRelators([[1,2,-3]]));

  for (const x of generators.results(tables(2, [[1,1],[2,2],[1,2,1,2]], 8)))
    console.log(JSON.stringify(x));
}
<commit_msg>finish rewriting coset table code<commit_after>import * as I          from 'immutable';
import * as fw         from './freeWords';
import * as generators from '../common/generators';

import { Partition } from '../common/unionFind';

const range = (from, to) => {
  if (to < from)
    return new Array(from - to).fill(0).map((_, i) => from - i);
  else
    return new Array(to - from).fill(0).map((_, i) => from + i);
};

const allGens = function*(nrGens) {
  for (let i = 1; i <= nrGens; ++i)
    yield i;
  for (let i = 1; i <= nrGens; ++i)
    yield -i;
}

class CosetTable {
  constructor(nrGens) {
    this.nrGens = nrGens;
    this.table = [new Array(2 * nrGens + 1)];
    this.part = new Partition();
  }

  clone() {
    const t = new CosetTable(this.nrGens);
    t.table = this.table.slice().map(row => row.slice());
    t.part = this.part.clone();
    return t;
  }

  allGens() {
    return allGens(this.nrGens)
  }

  get size() {
    return this.table.length;
  }

  get(c, g) {
    return (this.table[c] || [])[g + this.nrGens];
  }

  set(c, g, d) {
    if (c == null)
      throw new Error('oops!');
    if (this.table[c] == null)
      this.table[c] = new Array(2 * this.nrGens + 1);
    this.table[c][g + this.nrGens] = d;
  }

  join(c, d, g) {
    this.set(c, g, d);
    this.set(d, -g, c);
  }

  canon(c) {
    return this.part.find(c);
  }

  identify(a, b) {
    const queue = [[a, b]];

    while (queue.length) {
      const [a, b] = queue.shift().map(x => this.canon(x));

      if (a != b) {
        this.part.union(a, b);

        for (const g of this.allGens()) {
          const ag = this.get(a, g);
          const bg = this.get(b, g);
          if (ag == null)
            this.set(a, g, bg);
          else {
            if (bg != null && this.canon(ag) != this.canon(bg))
              queue.push([ag, bg]);
            this.set(b, g, ag);
          }
        }
      }
    }
  }

  asCompactMatrix() {
    const toIdx = {};
    let i = 0;
    for (const k of range(0, this.table.length)) {
      if (this.canon(k) == k) {
        toIdx[k] = i;
        ++i;
      }
    }

    const result = [];
    for (const k of range(0, this.table.length)) {
      if (toIdx[k] != null) {
        const row = {};
        for (const g of this.allGens())
          row[g] = toIdx[this.canon(this.get(k, g))];
        result.push(row);
      }
    }

    return result;
  }
}

const scan = (table, w, start, limit) => {
  let row = start;
  let i = 0;

  while (i < limit) {
    const next = table.get(row, w.get(i));
    if (next == null)
      break;
    else {
      ++i;
      row = next;
    }
  }

  return { row, index: i };
};

const scanAndIdentify = (table, w, start) => {
  const n = w.size;
  const { row: head, index: i } = scan(table, w, start, n);
  const { row: tail, index: j } = scan(table, fw.inverse(w), start, n - i);

  if (i + j == n - 1) {
    table.join(head, tail, w.get(i));
    return head;
  }
  else if (i + j == n && head != tail)
    table.identify(head, tail);
  else
    return -1;
};

const _insertInOrderedSet = (elm, set, cmp) => {
  let i = 0;
  while (i < set.length && cmp(elm, set[i]) < 0)
    ++i;
  if (i >= set.length || cmp(elm, set[i]) != 0)
    set.splice(i, 0, elm);
};

const _expandRelators = relators => {
  const out = [];
  for (const rel of relators) {
    for (const w of fw.relatorPermutations(rel))
      _insertInOrderedSet(w, out, fw.compare);
  }
  return out;
};

export const cosetTable = (nrGens, relators, subgroupGens) => {
  const rels = _expandRelators(relators);
  const table = new CosetTable(nrGens);

  for (let i = 0; i < table.size; ++i) {
    if (i != table.canon(i))
      continue;

    for (const g of table.allGens()) {
      if (table.get(i, g) == null) {
        const n = table.size;
        if (n >= 10000)
          throw new Error('maximum coset table size reached');

        table.join(i, n, g);
        for (const w of rels)
          scanAndIdentify(table, w, n);
        for (const w of subgroupGens)
          scanAndIdentify(table, fw.word(w), table.canon(0));
      }
    }
  }

  return table.asCompactMatrix();
};

export const cosetRepresentatives = table => {
  const queue = [0];
  const reps = [fw.empty];

  while (queue.length) {
    const i = queue.shift();
    const w = reps[i];

    for (const g of Object.keys(table[i])) {
      const k = table[i][g];
      if (reps[k] == null) {
        reps[k] = fw.product([w, [parseInt(g)]]);
        queue.push(k);
      }
    }
  }

  return reps;
};

const _firstFreeInTable = table => {
  for (let k = 0; k < table.size; ++k) {
    for (const g of table.allGens()) {
      if (table.get(k, g) == null)
        return [k, g];
    }
  }
};

const _scanRecursively = (rels, table, index) => {
  const q = [index];

  while (q.length) {
    const row = q.shift();

    for (const rel of rels) {
      const next = scanAndIdentify(table, rel, row);
      if (next == null)
        return false;
      else if (next >= 0)
        q.push(next);
    }
  }

  return true;
};

const _potentialChildren = (table, rels, maxCosets) => {
  const [k, g] = _firstFreeInTable(table) || [];
  const result = [];

  if (k != null) {
    const ginv = -g;
    const limit = Math.min(table.size + 1, maxCosets);

    for (let pos = k; pos < limit; ++pos) {
      if (table.get(pos, ginv) == null) {
        const t = table.clone();
        t.join(k, pos, g);
        if (_scanRecursively(rels, t, k))
          result.push(t);
      }
    }
  }

  return result;
};

const _compareRenumberedFom = (table, start) => {
  const n2o = [start];
  const o2n = { [start]: 0 };

  for (let row = 0; row < table.size; ++row) {
    if (row >= n2o.length)
      throw new Error("coset table is not transitive");

    for (const g of table.allGens()) {
      const t = table.get(n2o[row], g);
      if (t != null && o2n[t] == null) {
        o2n[t] = n2o.length;
        n2o.push(t);
      }

      const nval = o2n[t];
      const oval = table.get(row, g);
      if (oval != nval)
        return oval == null ? -1 : nval == null ? 1 : nval - oval;
    }
  }

  return 0;
};

const _isCanonical = table => range(1, table.size)
  .every(start => _compareRenumberedFom(table, start) >= 0);

export const tables = (nrGens, relators, maxCosets) => {
  const rels = _expandRelators(relators);

  return generators.backtracker({
    root: new CosetTable(nrGens),

    extract(table) {
      return _firstFreeInTable(table) == null ? table.asCompactMatrix() : null;
    },

    children(table) {
      return _potentialChildren(table, rels, maxCosets)
        .filter(t => t.size && _isCanonical(t));
    }
  });
};

const _inducedTable = (nrGens, img, img0) => {
  const table = new CosetTable(nrGens);
  const o2n = { [img0]: 0 };
  const n2o = [img0];

  for (let i = 0; i < table.size; ++i) {
    for (const g of table.allGens()) {
      const k = img(n2o[i], g);
      if (o2n[k] == null)
        o2n[k] = table.size;
      const n = o2n[k];
      n2o[n] = k;
      table.join(i, n, g);
    }
  }

  return table.asCompactMatrix();
};

export const intersectionTable = (tableA, tableB) =>
  _inducedTable(
    Math.max(...Object.keys(tableA[0])),
    ([a, b], g) => [tableA.get(a, g), tableB.get(b, g)],
    [0, 0]
  );

export const coreTable = base =>
  _inducedTable(
    Math.max(...Object.keys(base[0])),
    (es, g) => es.map(e => base[e][g]),
    range(0, base.length)
  );

const _sgn = x => (x > 0) - (x < 0);
const _sum = a => a.reduce((x, y) => x + y, 0);

export const relatorAsVector = (rel, nrgens) => {
  const out = new Array(nrgens).fill(0);

  for (const w of rel) {
    if (w < 0)
      --out[-w - 1];
    else if (w > 0)
      ++out[w - 1];
  }

  return out;
};

export const relatorMatrix = (nrgens, relators) =>
  relators.map(rel => relatorAsVector(rel, nrgens));

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x && x.toString()).join(', ') + ' ]';
  };

  const test = table => {
    const reps = cosetRepresentatives(table);
    console.log(JSON.stringify(reps), Object.keys(reps).length);
  };

  const base = cosetTable(
    3,
    [[1,1], [2,2], [3,3], [1,2,1,2,1,2], [1,3,1,3], fw.raisedTo(3, [2,3])],
    [[1,2]]);

  test(base);
  test(coreTable(base));

  console.log(_expandRelators([[1,2,-3]]));

  for (const x of generators.results(tables(2, [[1,1],[2,2],[1,2,1,2]], 8)))
    console.log(JSON.stringify(x));
}
<|endoftext|>


<commit_before>(function() {

  'use strict';

  // Global Variables
  // ----------------

  var head = document.getElementsByTagName('head')[0];
  var hiddenRules = document.createElement('style');
  var classname = '_skate';
  var skateAdapter = mutationObserverAdapter() || mutationEventAdapter();
  var domPrefixes = [
      'moz',
      'ms',
      'o',
      'webkit',
    ];
  var matchesSelector = (function() {
      var matcher = Element.prototype.matches;

      domPrefixes.some(function(prefix) {
        var method = prefix + 'MatchesSelector';

        if (Element.prototype[method]) {
          matcher = method;
          return true;
        }
      });

      return function (element, selector) {
        return element && element.nodeType === 1 && element[matcher](selector);
      };
    }());

  // Factory
  // -------

  function skate(selector, component) {
    return new Skate(selector, component);
  }

  // Default configuration.
  skate.defaults = {
    extend: {},
    listen: true
  };

  // All active instances.
  skate.instances = [];

  // Initialises the elements against all skate instances.
  skate.init = function(elements) {
    skate.instances.forEach(function(instance) {
      instance.init(elements);
    });

    return skate;
  };

  // Destroys all active instances.
  skate.destroy = function() {
    for (var a = skate.instances.length - 1; a >= 0; a--) {
      skate.instances[a].deafen();
    }

    return skate;
  };

  // Inserts hide rules for the specified selector. This is useful if you pass
  // a matching function instead of a selector to `skate()`. If you pass a
  // function and specify a `ready()` callback, your element will not be hidden
  // because there was no selector that could be used to autohide them. Calling
  // this manually gets around that problem.
  skate.hide = function(selector) {
    var ensureHideRules = [
      'position: absolute !important',
      'clip: rect(0, 0, 0, 0)'
    ];

    hiddenRules.sheet.insertRule(
      appendSelector(selector, ':not(.' + classname + ')') + '{' + ensureHideRules.join(';') + '}',
      hiddenRules.sheet.cssRules.length
    );

    return skate;
  };

  // Finds instances matching the specified node.
  skate.find = function(element) {
    var instances = [];

    skate.instances.forEach(function(instance) {
      if (instance.matches(element)) {
        instances.push(instance);
      }
    });

    return instances;
  };

  // Common Interface
  // ----------------

  function Skate(matcher, component) {
    this.component = component
    this.elements = [];
    this.matcher = matcher;

    inherit(this.component, skate.defaults);

    // Emulate the web components ready callback.
    if (this.component.ready && typeof this.matcher === 'string') {
      skate.hide(this.matcher);
    }

    if (this.component.listen) {
      this.listen();
    }
  }

  Skate.prototype = {
    // Initialises an element, or elements.
    init: function(elements, force) {
      // Defaults to using the current selector, or elements if the selector is
      // not a string. This is because a matching function can be provided
      // instead of a selector.
      if (typeof elements === 'undefined') {
        elements = document.querySelectorAll(typeof this.selector === 'string' ? this.selector : '*');
      }

      if (elements.nodeType === 1) {
        initElement(this, elements, force);
      } else if (typeof elements === 'string') {
        initSelector(this, elements, force);
      } else if (typeof elements.length === 'number') {
        initTraversable(this, elements, force);
      }

      return this;
    },

    // Returns whether or not the instance can be applied to the element.
    matches: function(element) {
      return typeof this.matcher === 'function' ?
        this.matcher(element) :
        matchesSelector(element, this.matcher);
    },

    // Starts listening for new elements.
    listen: function() {
      if (skate.instances.indexOf(this) === -1) {
        skate.instances.push(this);
        this.init();
      }

      return this;
    },

    // Stops listening for new elements.
    deafen: function() {
      if (skate.instances.indexOf(this) !== -1) {
        skate.instances.splice(skate.instances.indexOf(this), 1);
      }

      return this;
    }
  };

  // Initialisers
  // ------------

  // Initialises an element directly by searching for any matching components.
  function initElement(skate, element, force) {
    if (force || skate.matches(element)) {
      triggerReady(skate, element);
    }
  }

  // Initialises elements matching the specified selector.
  function initSelector(skate, selector, force) {
    initTraversable(skate, document.querySelectorAll(selector), force);
  }

  // Initialises anything that is enumerable / traversable.
  function initTraversable(skate, elements, force) {
    [].slice.call(elements).forEach(function(element) {
      initElement(skate, element, force);
    });
  }

  // Lifecycle Triggers
  // ------------------

  // Triggers the ready callback and continues execution to the insert callback.
  function triggerReady(skate, target) {
    var definedMultipleArgs = /^[^(]+\([^,)]+,/;
    var readyFn = skate.component.ready;

    // If it's already been setup, don't do anything.
    if (skate.elements.indexOf(target) > -1) {
      return;
    }

    // Adds to the list of registered elements so the remove check knows which elements to check.
    skate.elements.push(target);

    // Extend element properties and methods with those provided.
    inherit(target, skate.component.extend);

    // If an async callback is defined make it async, sync or do nothing if no ready method is defined.
    if (readyFn && definedMultipleArgs.test(readyFn)) {
      readyFn(target, done);
    } else if (readyFn) {
      readyFn(target);
      done();
    } else {
      done();
    }

    // Async callback that continues execution.
    function done() {
      triggerInsert(skate, target);
    }
  }

  // Triggers insert on the target.
  function triggerInsert(skate, target) {
    var insertFn = skate.component.insert;

    // Ensures that the element is no longer hidden.
    addClass(target, classname);

    if (insertFn) {
      insertFn(target);
    }
  }

  // Triggers remove on the target.
  function triggerRemove(target) {
    skate.find(target).forEach(function(inst) {
      if (inst.component.remove) {
        inst.component.remove(target);
      }
    });
  }

  // MutationObserver Adapter
  // ------------------------

  function mutationObserverAdapter() {
    var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;

    if (!MutationObserver) {
      return;
    }

    var observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        skate.init(mutation.addedNodes);

        if (mutation.removedNodes) {
          [].slice.call(mutation.removedNodes).forEach(function(removedNode) {
            triggerRemove(removedNode);
          });
        }
      });
    });

    observer.observe(document, {
      childList: true,
      subtree: true
    });

    return true;
  }

  // Mutation Events Adapter
  // -----------------------

  function mutationEventAdapter() {
    document.addEventListener('DOMNodeInserted', function(e) {
      skate.init(e.target);
    });

    document.addEventListener('DOMNodeRemoved', function(e) {
      triggerRemove(e.target);
    });

    return true;
  }

  // Utilities
  // ---------

  // Adds the specified class to the element.
  function addClass(element, classname) {
    if (element.classList) {
      element.classList.add(classname);
    } else {
      element.className +=  ' ' + classname;
    }
  }

  // Appends the selector to the original selector or selectors.
  function appendSelector(original, addition) {
    var parts = splitSelector(original);

    parts.forEach(function(part, index) {
      parts[index] += addition;
    });

    return parts.join(',');
  }

  // Merges the second argument into the first.
  function inherit(base, from) {
    for (var a in from) {
      if (typeof base[a] === 'undefined') {
        base[a] = from[a];
      }
    }
  }

  // Splits a selector by commas.
  function splitSelector(selector) {
    var selectors = selector.split(',');

    selectors.forEach(function(item, index) {
      selectors[index] = item.replace(/^\s+/, '').replace(/\s+$/, '');
    });

    return selectors;
  }

  // Global Setup
  // ------------

  // Rules that hide elements as they're inserted so that elements are hidden
  // prior to calling the ready callback to prevent FOUC if the component
  // modifies the element in which it is bound.
  head.appendChild(hiddenRules);

  // Exporting
  // ---------

  if (typeof define === 'function' && define.amd) {
    define('skate', function() {
      return skate;
    });
  } else {
    window.skate = skate;
  }

})();
<commit_msg>Default function to insert callback.<commit_after>(function() {

  'use strict';

  // Global Variables
  // ----------------

  var head = document.getElementsByTagName('head')[0];
  var hiddenRules = document.createElement('style');
  var classname = '_skate';
  var skateAdapter = mutationObserverAdapter() || mutationEventAdapter();
  var domPrefixes = [
      'moz',
      'ms',
      'o',
      'webkit',
    ];
  var matchesSelector = (function() {
      var matcher = Element.prototype.matches;

      domPrefixes.some(function(prefix) {
        var method = prefix + 'MatchesSelector';

        if (Element.prototype[method]) {
          matcher = method;
          return true;
        }
      });

      return function (element, selector) {
        return element && element.nodeType === 1 && element[matcher](selector);
      };
    }());

  // Factory
  // -------

  function skate(selector, component) {
    return new Skate(selector, component);
  }

  // Default configuration.
  skate.defaults = {
    extend: {},
    listen: true
  };

  // All active instances.
  skate.instances = [];

  // Initialises the elements against all skate instances.
  skate.init = function(elements) {
    skate.instances.forEach(function(instance) {
      instance.init(elements);
    });

    return skate;
  };

  // Destroys all active instances.
  skate.destroy = function() {
    for (var a = skate.instances.length - 1; a >= 0; a--) {
      skate.instances[a].deafen();
    }

    return skate;
  };

  // Inserts hide rules for the specified selector. This is useful if you pass
  // a matching function instead of a selector to `skate()`. If you pass a
  // function and specify a `ready()` callback, your element will not be hidden
  // because there was no selector that could be used to autohide them. Calling
  // this manually gets around that problem.
  skate.hide = function(selector) {
    var ensureHideRules = [
      'position: absolute !important',
      'clip: rect(0, 0, 0, 0)'
    ];

    hiddenRules.sheet.insertRule(
      appendSelector(selector, ':not(.' + classname + ')') + '{' + ensureHideRules.join(';') + '}',
      hiddenRules.sheet.cssRules.length
    );

    return skate;
  };

  // Finds instances matching the specified node.
  skate.find = function(element) {
    var instances = [];

    skate.instances.forEach(function(instance) {
      if (instance.matches(element)) {
        instances.push(instance);
      }
    });

    return instances;
  };

  // Common Interface
  // ----------------

  function Skate(matcher, component) {
    // Can specify a function that defaults to the insert callback.
    if (typeof component === 'function') {
      component = {
        insert: function(){}
      };
    }

    this.component = component
    this.elements = [];
    this.matcher = matcher;

    inherit(this.component, skate.defaults);

    // Emulate the web components ready callback.
    if (this.component.ready && typeof this.matcher === 'string') {
      skate.hide(this.matcher);
    }

    if (this.component.listen) {
      this.listen();
    }
  }

  Skate.prototype = {
    // Initialises an element, or elements.
    init: function(elements, force) {
      // Defaults to using the current selector, or elements if the selector is
      // not a string. This is because a matching function can be provided
      // instead of a selector.
      if (typeof elements === 'undefined') {
        elements = document.querySelectorAll(typeof this.selector === 'string' ? this.selector : '*');
      }

      if (elements.nodeType === 1) {
        initElement(this, elements, force);
      } else if (typeof elements === 'string') {
        initSelector(this, elements, force);
      } else if (typeof elements.length === 'number') {
        initTraversable(this, elements, force);
      }

      return this;
    },

    // Returns whether or not the instance can be applied to the element.
    matches: function(element) {
      return typeof this.matcher === 'function' ?
        this.matcher(element) :
        matchesSelector(element, this.matcher);
    },

    // Starts listening for new elements.
    listen: function() {
      if (skate.instances.indexOf(this) === -1) {
        skate.instances.push(this);
        this.init();
      }

      return this;
    },

    // Stops listening for new elements.
    deafen: function() {
      if (skate.instances.indexOf(this) !== -1) {
        skate.instances.splice(skate.instances.indexOf(this), 1);
      }

      return this;
    }
  };

  // Initialisers
  // ------------

  // Initialises an element directly by searching for any matching components.
  function initElement(skate, element, force) {
    if (force || skate.matches(element)) {
      triggerReady(skate, element);
    }
  }

  // Initialises elements matching the specified selector.
  function initSelector(skate, selector, force) {
    initTraversable(skate, document.querySelectorAll(selector), force);
  }

  // Initialises anything that is enumerable / traversable.
  function initTraversable(skate, elements, force) {
    [].slice.call(elements).forEach(function(element) {
      initElement(skate, element, force);
    });
  }

  // Lifecycle Triggers
  // ------------------

  // Triggers the ready callback and continues execution to the insert callback.
  function triggerReady(skate, target) {
    var definedMultipleArgs = /^[^(]+\([^,)]+,/;
    var readyFn = skate.component.ready;

    // If it's already been setup, don't do anything.
    if (skate.elements.indexOf(target) > -1) {
      return;
    }

    // Adds to the list of registered elements so the remove check knows which elements to check.
    skate.elements.push(target);

    // Extend element properties and methods with those provided.
    inherit(target, skate.component.extend);

    // If an async callback is defined make it async, sync or do nothing if no ready method is defined.
    if (readyFn && definedMultipleArgs.test(readyFn)) {
      readyFn(target, done);
    } else if (readyFn) {
      readyFn(target);
      done();
    } else {
      done();
    }

    // Async callback that continues execution.
    function done() {
      triggerInsert(skate, target);
    }
  }

  // Triggers insert on the target.
  function triggerInsert(skate, target) {
    var insertFn = skate.component.insert;

    // Ensures that the element is no longer hidden.
    addClass(target, classname);

    if (insertFn) {
      insertFn(target);
    }
  }

  // Triggers remove on the target.
  function triggerRemove(target) {
    skate.find(target).forEach(function(inst) {
      if (inst.component.remove) {
        inst.component.remove(target);
      }
    });
  }

  // MutationObserver Adapter
  // ------------------------

  function mutationObserverAdapter() {
    var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;

    if (!MutationObserver) {
      return;
    }

    var observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        skate.init(mutation.addedNodes);

        if (mutation.removedNodes) {
          [].slice.call(mutation.removedNodes).forEach(function(removedNode) {
            triggerRemove(removedNode);
          });
        }
      });
    });

    observer.observe(document, {
      childList: true,
      subtree: true
    });

    return true;
  }

  // Mutation Events Adapter
  // -----------------------

  function mutationEventAdapter() {
    document.addEventListener('DOMNodeInserted', function(e) {
      skate.init(e.target);
    });

    document.addEventListener('DOMNodeRemoved', function(e) {
      triggerRemove(e.target);
    });

    return true;
  }

  // Utilities
  // ---------

  // Adds the specified class to the element.
  function addClass(element, classname) {
    if (element.classList) {
      element.classList.add(classname);
    } else {
      element.className +=  ' ' + classname;
    }
  }

  // Appends the selector to the original selector or selectors.
  function appendSelector(original, addition) {
    var parts = splitSelector(original);

    parts.forEach(function(part, index) {
      parts[index] += addition;
    });

    return parts.join(',');
  }

  // Merges the second argument into the first.
  function inherit(base, from) {
    for (var a in from) {
      if (typeof base[a] === 'undefined') {
        base[a] = from[a];
      }
    }
  }

  // Splits a selector by commas.
  function splitSelector(selector) {
    var selectors = selector.split(',');

    selectors.forEach(function(item, index) {
      selectors[index] = item.replace(/^\s+/, '').replace(/\s+$/, '');
    });

    return selectors;
  }

  // Global Setup
  // ------------

  // Rules that hide elements as they're inserted so that elements are hidden
  // prior to calling the ready callback to prevent FOUC if the component
  // modifies the element in which it is bound.
  head.appendChild(hiddenRules);

  // Exporting
  // ---------

  if (typeof define === 'function' && define.amd) {
    define('skate', function() {
      return skate;
    });
  } else {
    window.skate = skate;
  }

})();
<|endoftext|>