<commit_before>#!/usr/bin/env node

"use strict";

const fs = require('fs');
const packageJson = require('./../package.json');
let items = require('./../src/data/items.json');

let args = {}, command = help;

if (process.argv.length > 2) {
    process.argv.map((arg, index) => {
        if (index < 2) {
            return;
        }

        if (index === 2) {
            switch (arg) {
                case 'help':
                    command = help;
                    break;
                case 'set-version':
                    command = setVersion;
                    break;
                case 'check-items':
                    command = checkItems;
                    break;
                case 'save-items':
                    command = saveItems;
                    break;
                case 'import-csv':
                    command = importCsv;
                    break;
                case 'generate-translations':
                    command = generateTranslations;
                    break;
                case 'rename':
                    command = rename;
                    break;
                case 'temp':
                    command = temp;
                    break;
                default:
                    help();
                    process.exit();
                    break;
            }

            return;
        }

        if (arg.indexOf('-') === 0) {
            args[arg[1]] = arg.slice(2);
            if (args[arg[1]] === '') {
                args[arg[1]] = true;
            }
        }
    });
}

command(args);

function error(error, exit = true) {
    console.log(`error: ${error}`);

    if (exit) {
        process.exit();
    }
}

function makeUid(type, string, hero = null) {
    let uid = [type];

    if (hero) {
        uid.push(hero);
    }

    string.toUpperCase().split('').map((c) => uid.push(c.charCodeAt(0)));

    return uid.join('-');
}

// commands
function help(args = {}) {
    console.log(``);
    console.log(`commands:`);
    console.log(`  help: print this message`);
    console.log(`  set-version: set or increment app version`);
    console.log(`    -v%MAJOR.MINOR.PATCH%|patch|minor|major: version`);
    console.log(`  check-items: check items translations and assets`);
    console.log(`    -l%language%: language (if not setted translations check will be skipped)`);
    console.log(`  save-items: save items from json to executable`);
    console.log(`    -c: check before save`);
    console.log(`    -l%language%: language for check if '-c' is setted`);
    console.log(`  import-csv: import items from csv`);
    console.log(`    -f%path%: path to file`);
    console.log(`    -c: check before save`);
    console.log(`    -l%language%: language for check if '-c' is setted`);
    console.log(`  generate-translations: generate/update translations from items names`);
    console.log(`    -l%language%: language`);
    console.log(`  rename: rename files to uid`);
    console.log(`    -p%path%: path to files`);
    console.log(`    -T%TYPE%: item type`);
    console.log(`    -H%HERO%: item hero`);
    console.log(``);
}

function setVersion(args = null) {
    if (!args['v'] || typeof args['v'] !== 'string') {
        error('version is missing');
    }

    let setVersionIOS = function(version) {
        let plist = fs.readFileSync(`./../ios/${packageJson.name}/Info.plist`, 'utf-8');
        plist = plist.replace(
            /<key>CFBundleShortVersionString<\/key>\n([ \t]*)<string>.*<\/string>/,
            `<key>CFBundleShortVersionString</key>\n$1<string>${version}</string>`
        );
        plist = plist.replace(
            /<key>CFBundleVersion<\/key>\n([ \t]*)<string>(\d*)<\/string>/,
            (match, match1, match2) => {
                return `<key>CFBundleVersion</key>\n${match1}<string>${parseInt(match2) + 1}</string>`;
            }
        );
        fs.writeFileSync(`./../ios/${packageJson.name}/Info.plist`, plist);
    };

    let setVersionANDROID = function(version) {
        let buildGradle = fs.readFileSync(`./../android/app/build.gradle`, 'utf-8');
        buildGradle = buildGradle.replace(
            /versionCode (\d*)/,
            (match, match1) => {
                return `versionCode ${parseInt(match1) + 1}`;
            }
        );
        buildGradle = buildGradle.replace(
            /versionName .*/,
            `versionName "${version}"`
        );
        fs.writeFileSync(`./../android/app/build.gradle`, buildGradle);

        let manifest = fs.readFileSync(`./../android/app/src/main/AndroidManifest.xml`, 'utf-8');
        manifest = manifest.replace(
            /android:versionCode="(\d*)"/,
            (match, match1) => {
                return `android:versionCode="${parseInt(match1) + 1}"`;
            }
        );
        manifest = manifest.replace(
            /android:versionName=".*"/,
            `android:versionName="${version}"`
        );
        fs.writeFileSync(`./../android/app/src/main/AndroidManifest.xml`, manifest);
    };

    let platforms = ['ios', 'android',], version, platform;

    if (['patch', 'minor', 'major',].indexOf(args['v']) !== -1) {
        version = args['v'];
    }

    if (!version) {
        if (args['v'] && args['v'].search(/\d+\.\d+\.\d+/) !== -1) {
            version = args['v'];
        } else {
            error('wrong version format. it should be "MAJOR.MINOR.PATCH" or "patch|minor|major" for increment.');
        }
    }

    if (args['p']) {
        if (platforms.indexOf(args['p']) !== -1) {
            platform = [args['p']];
        } else {
            error(`wrong platform. should be one of [${platforms.join(', ')}].`);
        }
    } else {
        platform = platforms;
    }

    switch (version) {
        case 'patch':
            version = packageJson.version.split('.');
            version[2]++;
            version = version.join('.');
            break;
        case 'minor':
            version = packageJson.version.split('.');
            version[1]++;
            version[2] = 0;
            version = version.join('.');
            break;
        case 'major':
            version = packageJson.version.split('.');
            version[0]++;
            version[1] = 0;
            version[2] = 0;
            version = version.join('.');
            break;
    }

    packageJson.version = version;

    platform.map((p) => {
        switch (p) {
            case 'ios':
                setVersionIOS(version);
                break;
            case 'android':
                setVersionANDROID(version);
                break;
        }
    });

    fs.writeFileSync('./../package.json', JSON.stringify(packageJson, null, 2));

    let config = fs.readFileSync('./../src/config.js', 'utf-8');
    config = config.replace(
        /static VERSION = '.*';/,
        `static VERSION = '${version}';`
    );
    fs.writeFileSync('./../src/config.js', config);
}

function checkItems(args = {}, r = false) {
    let translations, result = true;

    if (args['l']) {
        if (typeof args['l'] !== 'string') {
            error('language is missing');
        }

        translations = `./../src/translations/${args['l']}.items.json`;

        if (!fs.existsSync(translations)) {
            error(`'${args['l']}' translations does not exists`);
        }

        translations = require(translations);
    }

    Object.values(items).map((item) => {
        if (["SPRAY", "ICON",].indexOf(item.type) !== -1) {
            if (!fs.existsSync(`./../src/assets/${item.type.toLowerCase()}s/${item.uid}.png`)) {
                result = false;
                console.log(`asset for ${item.type} '${item.name}'(${item.uid}): does not exists`);
            }

            if (translations) {
                if (translations[item.name] === undefined) {
                    result = false;
                    console.log(`${args['l']} translation for${(item.hero ? ` ${item.hero}` : '')} ${item.type} '${item.name}': does not exists`);
                }
            }
        }
    });

    if (r === true) {
        return result;
    }

    console.log(`items checked: ${(result ? 'success' : 'failed')}`);
}

function saveItems(args = {}) {
    if (args['c'] && !checkItems(args, true)) {
        error('save canceled');
    }

    let output = `// THIS FILE GENERATED BY IMPORT COMMAND\n// DO NOT MODIFY\n\n`;

    fs.writeFileSync('./../src/data/items.json', JSON.stringify(items, null, 2));

    output += `import Items from './../Items';\nimport Events from './../Events';\nimport Heroes from './../Heroes';\n\nexport default {\n`;

    for (let i in items) if (items.hasOwnProperty(i)) {
        output += `    '${i}': {\n`;
        output += `        uid: '${items[i].uid}',\n`;
        if (items[i].type === 'ICON') {
            output += `        source: require('./../assets/icons/${items[i].uid}.png'),\n`;
        }
        if (items[i].type === 'SPRAY') {
            output += `        source: require('./../assets/sprays/${items[i].uid}.png'),\n`;
        }
        output += `        default: ${items[i].default},\n`;
        output += `        name: \`${items[i].name}\`,\n`;
        output += `        type: Items.TYPE.${items[i].type},\n`;
        output += `        rarity: Items.RARITY.${items[i].rarity},\n`;
        output += `        hero: ${items[i].hero ? 'Heroes.' + items[i].hero : false},\n`;
        output += `        price: ${items[i].price ? 'Items.PRICE.' + (!items[i].event || items[i].event === 'GENERAL' ? 'DEFAULT' : 'EVENT') + `[Items.RARITY.${items[i].rarity}]` : false},\n`;
        output += `        event: ${items[i].event ? 'Events.' + items[i].event : false},\n`;
        output += `    },\n`;
    }

    output += `};\n`;

    fs.writeFileSync('./../src/data/items.js', output);

    console.log('items saved');
}

function importCsv(args = {}) {
    let addedCount = 0, skippedCount = 0;

    if (!args['f'] || typeof args['f'] !== 'string') {
        error('file is missing');
    }

    if (!fs.existsSync(args['f'])) {
        error(`'${args['f']}' does not exists`);
    }

    fs.readFileSync(args['f'], 'utf-8').split('\n').map((item) => {
        item = item.split(';');

        if (item.length === 0) {
            return;
        }

        item[6] = item[6].replace('\r', '');

        item = {
            uid: makeUid(item[2], item[1], item[4] || null),
            default: item[0] === 'true',
            name: item[1],
            type: item[2],
            rarity: item[3],
            hero: item[4] === 'false' ? false : item[4],
            price: item[5] === 'true',
            event: item[6] === 'false' ? false : item[6],
        };

        if (items[item.uid]) {
            skippedCount++;

            console.log(`${(item.hero ? `${item.hero} ` : '')}${item.type} '${item.name}': already exists`);
        } else {
            addedCount++;

            items[item.uid] = item;

            console.log(`${(item.hero ? `${item.hero} ` : '')}${item.type} '${item.name}': added`);
        }
    });

    console.log(
        `${(addedCount > 0 ? `${addedCount} item(s) added` : '')}${(skippedCount > 0 ? ` / ${skippedCount} items skipped` : '')}`
    );

    saveItems(args);
}

function generateTranslations(args = {}) {
    if (!args['l'] || typeof args['l'] !== 'string') {
        error('language is missing');
    }

    let translations = `./../src/translations/${args['l']}.items.json`, count = 0;

    if (!fs.existsSync(translations)) {
        console.log(`'${args['l']}' translations does not exists and will be created`);

        translations = {};
    } else {
        translations = require(translations);
    }

    Object.values(items).map((item) => {
        if (!translations[item.name]) {
            count++;

            translations[item.name] = item.name;

            console.log(`translation for${(item.hero ? ` ${item.hero}` : '')} ${item.type} '${item.name}': added`);
        }
    });

    if (count > 0) {
        fs.writeFileSync(`./../src/translations/${args['l']}.items.json`, JSON.stringify(translations, null, 2));

        console.log(`${count} translation(s) added`);
    } else {
        console.log('nothing to update');
    }
}

function rename(args = {}) {
    if (!args['p'] || typeof args['p'] !== 'string') {
        error('path is missing');
    }

    if (!fs.existsSync(args['p']) || !fs.lstatSync(args['p']).isDirectory()) {
        error(`'${args['p']}' does not exists or not a directory`);
    }

    if (!args['T'] || typeof args['T'] !== 'string') {
        error('item type is missing');
    }

    let type = args['T'].toUpperCase(), hero, uid, count = 0;

    if (args['H']) {
        if (typeof args['T'] !== 'string') {
            error('hero is missing');
        }

        hero = args['H'].toUpperCase();
    }

    fs.readdirSync(args['p']).map((file) => {
        count++;
        uid = makeUid(type, file.replace('.png', ''), hero);

        fs.renameSync(args['p'] + '/' + file, args['p'] + '/' + uid + '.png');
        console.log(`${file} -> ${uid}.png`);
    });

    console.log(`${count} file(s) renamed`);
}

function temp(args = {}) {
    error('command is not realised');
}
<commit_msg>improve console tools<commit_after>#!/usr/bin/env node

"use strict";

const fs = require('fs');
const packageJson = require('./../package.json');
let items = require('./../src/data/items.json');

let args = {}, command = help;

if (process.argv.length > 2) {
    process.argv.map((arg, index) => {
        if (index < 2) {
            return;
        }

        if (index === 2) {
            switch (arg) {
                case 'help':
                    command = help;
                    break;
                case 'set-version':
                    command = setVersion;
                    break;
                case 'check-items':
                    command = checkItems;
                    break;
                case 'save-items':
                    command = saveItems;
                    break;
                case 'import-csv':
                    command = importCsv;
                    break;
                case 'generate-translations':
                    command = generateTranslations;
                    break;
                case 'rename':
                    command = rename;
                    break;
                case 'temp':
                    command = temp;
                    break;
                default:
                    help();
                    process.exit();
                    break;
            }

            return;
        }

        if (arg.indexOf('-') === 0) {
            args[arg[1]] = arg.slice(2);
            if (args[arg[1]] === '') {
                args[arg[1]] = true;
            }
        }
    });
}

command(args);

function error(error, exit = true) {
    console.log(`error: ${error}`);

    if (exit) {
        process.exit();
    }
}

function makeUid(type, string, hero = null) {
    let uid = [type];

    if (hero) {
        uid.push(hero);
    }

    string.toUpperCase().split('').map((c) => uid.push(c.charCodeAt(0)));

    return uid.join('-');
}

// commands
function help(args = {}) {
    console.log(``);
    console.log(`commands:`);
    console.log(`  help: print this message`);
    console.log(`  set-version: set or increment app version`);
    console.log(`    -v%MAJOR.MINOR.PATCH%|patch|minor|major: version`);
    console.log(`  check-items: check items translations and assets`);
    console.log(`    -l%language%: language (if not setted translations check will be skipped)`);
    console.log(`  save-items: save items from json to executable`);
    console.log(`    -c: check before save`);
    console.log(`    -l%language%: language for check if '-c' is setted`);
    console.log(`  import-csv: import items from csv`);
    console.log(`    -f%path%: path to file`);
    console.log(`    -c: check before save`);
    console.log(`    -l%language%: language for check if '-c' is setted`);
    console.log(`  generate-translations: generate/update translations from items names`);
    console.log(`    -l%language%: language`);
    console.log(`  rename: rename files to uid`);
    console.log(`    -p%path%: path to files`);
    console.log(`    -T%TYPE%: item type`);
    console.log(`    -H%HERO%: item hero`);
    console.log(``);
}

function setVersion(args = null) {
    if (!args['v'] || typeof args['v'] !== 'string') {
        error('version is missing');
    }

    let setVersionIOS = function(version) {
        let plist = fs.readFileSync(`./../ios/${packageJson.name}/Info.plist`, 'utf-8');
        plist = plist.replace(
            /<key>CFBundleShortVersionString<\/key>\n([ \t]*)<string>.*<\/string>/,
            `<key>CFBundleShortVersionString</key>\n$1<string>${version}</string>`
        );
        plist = plist.replace(
            /<key>CFBundleVersion<\/key>\n([ \t]*)<string>(\d*)<\/string>/,
            (match, match1, match2) => {
                return `<key>CFBundleVersion</key>\n${match1}<string>${parseInt(match2) + 1}</string>`;
            }
        );
        fs.writeFileSync(`./../ios/${packageJson.name}/Info.plist`, plist);
    };

    let setVersionANDROID = function(version) {
        let buildGradle = fs.readFileSync(`./../android/app/build.gradle`, 'utf-8');
        buildGradle = buildGradle.replace(
            /versionCode (\d*)/,
            (match, match1) => {
                return `versionCode ${parseInt(match1) + 1}`;
            }
        );
        buildGradle = buildGradle.replace(
            /versionName .*/,
            `versionName "${version}"`
        );
        fs.writeFileSync(`./../android/app/build.gradle`, buildGradle);

        let manifest = fs.readFileSync(`./../android/app/src/main/AndroidManifest.xml`, 'utf-8');
        manifest = manifest.replace(
            /android:versionCode="(\d*)"/,
            (match, match1) => {
                return `android:versionCode="${parseInt(match1) + 1}"`;
            }
        );
        manifest = manifest.replace(
            /android:versionName=".*"/,
            `android:versionName="${version}"`
        );
        fs.writeFileSync(`./../android/app/src/main/AndroidManifest.xml`, manifest);
    };

    let platforms = ['ios', 'android',], version, platform;

    if (['patch', 'minor', 'major',].indexOf(args['v']) !== -1) {
        version = args['v'];
    }

    if (!version) {
        if (args['v'] && args['v'].search(/\d+\.\d+\.\d+/) !== -1) {
            version = args['v'];
        } else {
            error('wrong version format. it should be "MAJOR.MINOR.PATCH" or "patch|minor|major" for increment.');
        }
    }

    if (args['p']) {
        if (platforms.indexOf(args['p']) !== -1) {
            platform = [args['p']];
        } else {
            error(`wrong platform. should be one of [${platforms.join(', ')}].`);
        }
    } else {
        platform = platforms;
    }

    switch (version) {
        case 'patch':
            version = packageJson.version.split('.');
            version[2]++;
            version = version.join('.');
            break;
        case 'minor':
            version = packageJson.version.split('.');
            version[1]++;
            version[2] = 0;
            version = version.join('.');
            break;
        case 'major':
            version = packageJson.version.split('.');
            version[0]++;
            version[1] = 0;
            version[2] = 0;
            version = version.join('.');
            break;
    }

    packageJson.version = version;

    platform.map((p) => {
        switch (p) {
            case 'ios':
                setVersionIOS(version);
                break;
            case 'android':
                setVersionANDROID(version);
                break;
        }
    });

    fs.writeFileSync('./../package.json', JSON.stringify(packageJson, null, 2));

    let config = fs.readFileSync('./../src/config.js', 'utf-8');
    config = config.replace(
        /static VERSION = '.*';/,
        `static VERSION = '${version}';`
    );
    fs.writeFileSync('./../src/config.js', config);
}

function checkItems(args = {}, r = false) {
    let translations, result = true;

    if (args['l']) {
        if (typeof args['l'] !== 'string') {
            error('language is missing');
        }

        translations = `./../src/translations/${args['l']}.items.json`;

        if (!fs.existsSync(translations)) {
            error(`'${args['l']}' translations does not exists`);
        }

        translations = require(translations);
    }

    Object.values(items).map((item) => {
        if (['SPRAY', 'ICON',].indexOf(item.type) !== -1) {
            if (!fs.existsSync(`./../src/assets/${item.type.toLowerCase()}s/${item.uid}.png`)) {
                result = false;
                console.log(`asset for ${item.type} '${item.name}'(${item.uid}): does not exists`);
            }

            if (translations) {
                if (translations[item.name] === undefined) {
                    result = false;
                    console.log(`${args['l']} translation for${(item.hero ? ` ${item.hero}` : '')} ${item.type} '${item.name}': does not exists`);
                }
            }
        }
    });

    if (r === true) {
        return result;
    }

    console.log(`items checked: ${(result ? 'success' : 'failed')}`);
}

function saveItems(args = {}) {
    if (args['c'] && !checkItems(args, true)) {
        error('save canceled');
    }

    let output = `// THIS FILE GENERATED BY IMPORT COMMAND\n// DO NOT MODIFY\n\n`;

    fs.writeFileSync('./../src/data/items.json', JSON.stringify(items, null, 2));

    output += `import Items from './../Items';\nimport Events from './../Events';\nimport Heroes from './../Heroes';\n\nexport default {\n`;

    for (let i in items) if (items.hasOwnProperty(i)) {
        output += `    '${i}': {\n`;
        output += `        uid: '${items[i].uid}',\n`;
        if (items[i].type === 'ICON') {
            output += `        source: require('./../assets/icons/${items[i].uid}.png'),\n`;
        }
        if (items[i].type === 'SPRAY') {
            output += `        source: require('./../assets/sprays/${items[i].uid}.png'),\n`;
        }
        output += `        default: ${items[i].default},\n`;
        output += `        name: \`${items[i].name}\`,\n`;
        output += `        type: Items.TYPE.${items[i].type},\n`;
        output += `        rarity: Items.RARITY.${items[i].rarity},\n`;
        output += `        hero: ${items[i].hero ? 'Heroes.' + items[i].hero : false},\n`;
        output += `        price: ${items[i].price ? 'Items.PRICE.' + (!items[i].event || items[i].event === 'GENERAL' ? 'DEFAULT' : 'EVENT') + `[Items.RARITY.${items[i].rarity}]` : false},\n`;
        output += `        event: ${items[i].event ? 'Events.' + items[i].event : false},\n`;
        output += `    },\n`;
    }

    output += `};\n`;

    fs.writeFileSync('./../src/data/items.js', output);

    console.log('items saved');
}

function importCsv(args = {}) {
    let addedCount = 0, skippedCount = 0;

    if (!args['f'] || typeof args['f'] !== 'string') {
        error('file is missing');
    }

    if (!fs.existsSync(args['f'])) {
        error(`'${args['f']}' does not exists`);
    }

    fs.readFileSync(args['f'], 'utf-8').split('\n').map((item) => {
        item = item.split(';');

        if (item.length === 0) {
            return;
        }

        item[6] = item[6].replace('\r', '');

        item[4] = item[4] === 'false' ? false : item[4];
        item[5] = item[5] === 'true';
        item[6] = item[6] === 'false' ? false : item[6];

        item = {
            uid: makeUid(item[2], item[1], item[4] || null),
            default: item[0] === 'true',
            name: item[1],
            type: item[2],
            rarity: item[3],
            hero: item[4],
            price: item[5],
            event: item[6],
        };

        if (items[item.uid]) {
            skippedCount++;

            console.log(`${(item.hero ? `${item.hero} ` : '')}${item.type} '${item.name}': already exists`);
        } else {
            addedCount++;

            items[item.uid] = item;

            console.log(`${(item.hero ? `${item.hero} ` : '')}${item.type} '${item.name}': added`);
        }
    });

    console.log(
        `${(addedCount > 0 ? `${addedCount} item(s) added` : '')}${(skippedCount > 0 ? ` / ${skippedCount} items skipped` : '')}`
    );

    saveItems(args);
}

function generateTranslations(args = {}) {
    if (!args['l'] || typeof args['l'] !== 'string') {
        error('language is missing');
    }

    let translations = `./../src/translations/${args['l']}.items.json`, count = 0;

    if (!fs.existsSync(translations)) {
        console.log(`'${args['l']}' translations does not exists and will be created`);

        translations = {};
    } else {
        translations = require(translations);
    }

    Object.values(items).map((item) => {
        if (!translations[item.name]) {
            count++;

            translations[item.name] = item.name;

            console.log(`translation for${(item.hero ? ` ${item.hero}` : '')} ${item.type} '${item.name}': added`);
        }
    });

    if (count > 0) {
        fs.writeFileSync(`./../src/translations/${args['l']}.items.json`, JSON.stringify(translations, null, 2));

        console.log(`${count} translation(s) added`);
    } else {
        console.log('nothing to update');
    }
}

function rename(args = {}) {
    if (!args['p'] || typeof args['p'] !== 'string') {
        error('path is missing');
    }

    if (!fs.existsSync(args['p']) || !fs.lstatSync(args['p']).isDirectory()) {
        error(`'${args['p']}' does not exists or not a directory`);
    }

    if (!args['T'] || typeof args['T'] !== 'string') {
        error('item type is missing');
    }

    let type = args['T'].toUpperCase(), hero, uid, count = 0;

    if (args['H']) {
        if (typeof args['T'] !== 'string') {
            error('hero is missing');
        }

        hero = args['H'].toUpperCase();
    }

    fs.readdirSync(args['p']).map((file) => {
        count++;
        uid = makeUid(type, file.replace('.png', ''), hero);

        fs.renameSync(args['p'] + '/' + file, args['p'] + '/' + uid + '.png');
        console.log(`${file} -> ${uid}.png`);
    });

    console.log(`${count} file(s) renamed`);
}

function temp(args = {}) {
    error('command is not realised');
}
<|endoftext|>


<commit_before>module.exports = RedSess

var redis = require("redis")
RedSess.client = null

var Cookies = require('cookies')
var util = require('util')

// optional dependency
try { var KeyGrip = require('keygrip') } catch (e) {}

function RedSess (req, res, opt) {
  if (!(this instanceof RedSess))
    return new RedSess(req, res, opt)

  opt = opt || {}
  if (!RedSess.client && !opt.client) {
    console.error('RedSess: no client yet', req.url, req.headers)
    res.statusCode = 503
    res.setHeader('content-type', 'text/plain')
    res.setHeader('retry-after', '10')
    res.end('Redis client not yet loaded.\n')
    return
  }

  // if we got an array of strings rather than a keygrip, then
  // keygrip it up.
  if (opt.keys) {
    if (!KeyGrip)
      throw new Error('keys provided by KeyGrip not available')
    else if (Array.isArray(opt.keys))
      this.keys = new KeyGrip(opt.keys)
    else if (opt.keys instanceof KeyGrip)
      this.keys = opt.keys
    else
      throw new Error('invalid keys provided')
  }

  // 2 week sessions by default.
  this.expire = opt.expire || 60 * 60 * 24 * 14

  // set up the cookies thingie
  this.cookies = opt.cookies || new Cookies(req, res, this.keys)

  // set the s-cookie
  var name = this.cookieName = opt.cookieName || 's'
  var expireDate = new Date(Date.now() + (this.expire*1000))
  var cookieOptions = opt && opt.cookieOptions || {}
  var copt = util._extend({
    expires: expireDate,
    signed: !!this.keys
  }, cookieOptions)

  var s = this.cookies.get(name, copt)
  if (!s)
    s = opt.token || require('crypto').randomBytes(30).toString('base64')

  if (res)
    this.cookies.set(name, s, copt)

  this.token = s

  if (!this.token)
    throw new Error('could not load session token')

  this.id = "session:" + this.token
  this.client = opt.client || RedSess.client
  this.request = req
  this.response = res
}

RedSess.setClient = function (client) {
  RedSess.client = client
}

RedSess.createClient = function (conf) {
  conf = conf || {}
  RedSess.client = redis.createClient(conf.port, conf.host, conf)
  if (conf.auth)
    RedSess.client.auth(conf.auth)
  return RedSess.client
}

RedSess.quit = RedSess.close = RedSess.end = function (cb) {
  if (!RedSess.client)
    return cb && cb()
  if (cb)
    RedSess.client.once("end", cb)
  RedSess.client.quit()
}

RedSess.destroy = function () {
  RedSess.client.end()
}

RedSess.prototype.del = function (k, cb) {
  if (typeof k === 'function' || !k && !cb)
    return this.delAll(k || cb)

  // actually, delete all keys starting with k:* as well
  this.client.hkeys(this.id, function (er, keys) {
    if (er)
      return cb && cb(er)

    var keys = keys.filter(function (key) {
      return key.split(/:/)[0] === k
    })

    if (!keys.length)
      return cb && cb()

    keys.unshift(this.id)
    this.client.hdel(keys, function (er) {
      this.client.expire(this.id, this.expire)
      if (cb)
        return cb(er)
    }.bind(this))
  }.bind(this))
}

RedSess.prototype.delAll = function (cb) {
  this.client.del(this.id, cb || function(){})
}

// delete all data, and kill the session entirely
RedSess.prototype.destroy = function (cb) {
  this.client.del(this.id, function (er) {
    this.cookies.set(this.cookieName, '', {
      expires: new Date(0),
      signed: !!this.keys
    })
    cb(er)
  }.bind(this))
}

RedSess.prototype.set = function (k, v, cb) {
  var kv = {}

  if (typeof v === 'function')
    cb = v, v = null

  if (v)
    kv[k] = v
  else
    kv = k

  kv = flatten(kv)
  this.client.hmset(this.id, kv, function (er) {
    this.client.expire(this.id, this.expire)
    if (cb)
      return cb(er)
  }.bind(this))
}

RedSess.prototype.get = function (k, cb) {
  if (typeof k === 'function' || !k)
    return this.getAll(k || cb)

  if (!cb)
    return this.client.expire(this.id, this.expire)

  this.getAll(function (er, all) {
    if (er || !all)
      return cb(er, null)
    return cb(null, all.hasOwnProperty(k) ? all[k] : null)
  }.bind(this))
}

RedSess.prototype.getAll = function (cb) {
  // if no cb, then just update the expiration
  if (!cb)
    return this.client.expire(this.id, this.expire)

  this.client.hgetall(this.id, function (er, data) {
    this.client.expire(this.id, this.expire)
    if (!er)
      data = unflatten(data)
    cb(er, data)
  }.bind(this))
}

function flatten (obj, into, prefix) {
  if (!obj || typeof obj !== 'object') return obj
  into = into || {}
  prefix = prefix || ''
  if (prefix) prefix += ':'
  Object.keys(obj).forEach(function (k) {
    if (obj[k] && typeof obj[k] === "object") {
      flatten(obj[k], into, prefix + k)
    } else into[prefix + k] = '' + obj[k]
  })
  return into
}

function unflatten (obj) {
  if (!obj || typeof obj !== "object") return obj
  var into = {}
  Object.keys(obj).forEach(function (k) {
    // actually an object
    var m = k.split(/:/)
    , tail = m.pop()
    , p = into

    m.forEach(function (n) {
      if (!p[n] || typeof p[n] !== "object") p[n] = {}
      p = p[n]
    })
    p[tail] = obj[k]

  })
  return into
}
<commit_msg>straight up get/set instead of hashes<commit_after>module.exports = RedSess

var redis = require("redis")
RedSess.client = null

var Cookies = require('cookies')
var util = require('util')

// optional dependency
try { var KeyGrip = require('keygrip') } catch (e) {}

function RedSess (req, res, opt) {
  if (!(this instanceof RedSess))
    return new RedSess(req, res, opt)

  opt = opt || {}
  if (!RedSess.client && !opt.client) {
    console.error('RedSess: no client yet', req.url, req.headers)
    res.statusCode = 503
    res.setHeader('content-type', 'text/plain')
    res.setHeader('retry-after', '10')
    res.end('Redis client not yet loaded.\n')
    return
  }

  // if we got an array of strings rather than a keygrip, then
  // keygrip it up.
  if (opt.keys) {
    if (!KeyGrip)
      throw new Error('keys provided by KeyGrip not available')
    else if (Array.isArray(opt.keys))
      this.keys = new KeyGrip(opt.keys)
    else if (opt.keys instanceof KeyGrip)
      this.keys = opt.keys
    else
      throw new Error('invalid keys provided')
  }

  // 2 week sessions by default.
  this.expire = opt.expire || 60 * 60 * 24 * 14

  // set up the cookies thingie
  this.cookies = opt.cookies || new Cookies(req, res, this.keys)

  // set the s-cookie
  var name = this.cookieName = opt.cookieName || 's'
  var expireDate = new Date(Date.now() + (this.expire*1000))
  var cookieOptions = opt && opt.cookieOptions || {}
  var copt = util._extend({
    expires: expireDate,
    signed: !!this.keys
  }, cookieOptions)

  var s = this.cookies.get(name, copt)
  if (!s)
    s = opt.token || require('crypto').randomBytes(30).toString('base64')

  if (res)
    this.cookies.set(name, s, copt)

  this.token = s

  if (!this.token)
    throw new Error('could not load session token')

  this.id = "session:" + this.token
  this.client = opt.client || RedSess.client
  this.request = req
  this.response = res
}

RedSess.setClient = function (client) {
  RedSess.client = client
}

RedSess.createClient = function (conf) {
  conf = conf || {}
  RedSess.client = redis.createClient(conf.port, conf.host, conf)
  if (conf.auth)
    RedSess.client.auth(conf.auth)
  return RedSess.client
}

RedSess.quit = RedSess.close = RedSess.end = function (cb) {
  if (!RedSess.client)
    return cb && cb()
  if (cb)
    RedSess.client.once("end", cb)
  RedSess.client.quit()
}

RedSess.destroy = function () {
  RedSess.client.end()
}

RedSess.prototype.del = function (k, cb) {
  if (typeof k === 'function' || !k && !cb)
    return this.delAll(k || cb)

  this.client.get(this.id, function (er, data) {
    if (er)
      return cb && cb(er)

    data = JSON.parse(data)

    delete data[k]

    this.client.set(this.id, JSON.stringify(data), function (er) {
      this.client.expire(this.id, this.expire)
      if (cb)
        return cb(er)
    }.bind(this))
  }.bind(this))
}

RedSess.prototype.delAll = function (cb) {
  this.client.del(this.id, cb || function(){})
}

// delete all data, and kill the session entirely
RedSess.prototype.destroy = function (cb) {
  this.client.del(this.id, function (er) {
    this.cookies.set(this.cookieName, '', {
      expires: new Date(0),
      signed: !!this.keys
    })
    cb(er)
  }.bind(this))
}

RedSess.prototype.set = function (k, v, cb) {
  var kv

  if (typeof v === 'function')
    cb = v, v = null

  this.client.get(this.id, function (er, data) {
    if (er)
      cb(er)

    kv = JSON.parse(data) || {}

    if (v)
      kv[k] = v
    else
      kv = k

    this.client.set(this.id, JSON.stringify(kv), function (er) {
      this.client.expire(this.id, this.expire)
      if (cb)
        return cb(er)
    }.bind(this))
  }.bind(this))
}

RedSess.prototype.get = function (k, cb) {
  if (typeof k === 'function' || !k)
    return this.getAll(k || cb)

  if (!cb)
    return this.client.expire(this.id, this.expire)

  this.getAll(function (er, all) {
    if (er || !all)
      return cb(er, null)
    return cb(null, all.hasOwnProperty(k) ? all[k] : null)
  }.bind(this))
}

RedSess.prototype.getAll = function (cb) {
  // if no cb, then just update the expiration
  if (!cb)
    return this.client.expire(this.id, this.expire)

  this.client.get(this.id, function (er, data) {
    this.client.expire(this.id, this.expire)
    if (!er)
      data = JSON.parse(data)
    cb(er, data)
  }.bind(this))
}
<|endoftext|>


<commit_before>import { parsePath } from './PathUtils'

export const createLocation = (path, state, key) => {
  let location
  if (typeof path === 'string') {
    // Two-arg form: push(path, state)
    location = parsePath(path)
    location.state = state
  } else {
    // One-arg form: push(location)
    location = { ...path }

    if (location.pathname === undefined)
      location.pathname = ''

    if (location.search) {
      if (location.search.charAt(0) !== '?')
        location.search = '?' + location.search
    } else {
      location.search = ''
    }

    if (location.hash) {
      if (location.hash.charAt(0) !== '#')
        location.hash = '#' + location.hash
    } else {
      location.hash = ''
    }

    if (state !== undefined && location.state === undefined)
      location.state = state
  }

  location.key = key

  return location
}

const looseEqual = (a, b) => {
  if (a == null)
    return a == b

  const typeofA = typeof a
  const typeofB = typeof b

  if (typeofA !== typeofB)
    return false

  if (Array.isArray(a)) {
    if (!Array.isArray(b) || a.length !== b.length)
      return false

    return a.every((item, index) => looseEqual(item, b[index]))
  } else if (typeofA === 'object') {
    const keysOfA = Object.keys(a)
    const keysOfB = Object.keys(b)

    if (keysOfA.length !== keysOfB.length)
      return false

    return keysOfA.every(key => looseEqual(a[key], b[key]))
  }

  return a === b
}

export const locationsAreEqual = (a, b) =>
  a.pathname === b.pathname &&
  a.search === b.search &&
  a.hash === b.hash &&
  a.key === b.key &&
  looseEqual(a.state, b.state)
<commit_msg>Minor style tweaks<commit_after>import { parsePath } from './PathUtils'

export const createLocation = (path, state, key) => {
  let location
  if (typeof path === 'string') {
    // Two-arg form: push(path, state)
    location = parsePath(path)
    location.state = state
  } else {
    // One-arg form: push(location)
    location = { ...path }

    if (location.pathname === undefined)
      location.pathname = ''

    if (location.search) {
      if (location.search.charAt(0) !== '?')
        location.search = '?' + location.search
    } else {
      location.search = ''
    }

    if (location.hash) {
      if (location.hash.charAt(0) !== '#')
        location.hash = '#' + location.hash
    } else {
      location.hash = ''
    }

    if (state !== undefined && location.state === undefined)
      location.state = state
  }

  location.key = key

  return location
}

const looseEqual = (a, b) => {
  if (a == null)
    return a == b

  const typeofA = typeof a
  const typeofB = typeof b

  if (typeofA !== typeofB)
    return false

  if (Array.isArray(a)) {
    if (!Array.isArray(b) || a.length !== b.length)
      return false

    return a.every((item, index) => looseEqual(item, b[index]))
  } else if (typeofA === 'object') {
    const aKeys = Object.keys(a)
    const bKeys = Object.keys(b)

    if (aKeys.length !== bKeys.length)
      return false

    return aKeys.every(key => looseEqual(a[key], b[key]))
  }

  return a === b
}

export const locationsAreEqual = (a, b) =>
  a.pathname === b.pathname &&
  a.search === b.search &&
  a.hash === b.hash &&
  a.key === b.key &&
  looseEqual(a.state, b.state)
<|endoftext|>


<commit_before>const test = require('tape');
const Alert = require('../../grafana/alert/alert');
const Condition = require('../../grafana/alert/condition');
const simpleAlert = require('../fixtures/alert/simple_alert');
const alertWithCondition = require('../fixtures/alert/alert_with_condition');

test('simple alert', function t(assert) {
  const alert = new Alert();

  assert.deepEqual(alert.generate(), simpleAlert);
  assert.end();
});

test('alert should be able to add condition', assert => {
  const alert = new Alert();
  const condition = new Condition()
    .onQuery('B')
    .withReducer('min')
    .withEvaluator(1.1, 'gt');

  alert.addCondition(condition);

  assert.deepEqual(alert.generate(), alertWithCondition);
  assert.end();
});

test('alert should be able to receive conditions in the constructor', assert => {
  const overrideConditions = {
    conditions: [{
      type: 'query',
      query: {
        params: [
          'B',
          '5m',
          'now'
        ]
      },
      reducer: {
        type: 'min',
        params: []
      },
      evaluator: {
        type: 'gt',
        params: [1.1]
      },
      operator: {
        type: 'and'
      }
    }]
  };

  const alert = new Alert(overrideConditions);

  assert.deepEqual(alert.generate(), alertWithCondition);
  assert.end();
});
<commit_msg>allow overriding other params in alert<commit_after>const test = require('tape');
const Alert = require('../../grafana/alert/alert');
const Condition = require('../../grafana/alert/condition');
const simpleAlert = require('../fixtures/alert/simple_alert');
const alertWithCondition = require('../fixtures/alert/alert_with_condition');

test('simple alert', function t(assert) {
  const alert = new Alert();

  assert.deepEqual(alert.generate(), simpleAlert);
  assert.end();
});

test('alert should be able to add condition', assert => {
  const alert = new Alert();
  const condition = new Condition()
    .onQuery('B')
    .withReducer('min')
    .withEvaluator(1.1, 'gt');

  alert.addCondition(condition);

  assert.deepEqual(alert.generate(), alertWithCondition);
  assert.end();
});

test('alert should be able to override defaults from the constructor', assert => {
  const overrideOptions = {
    name: 'override name',
    for: '30min',
  };

  const alert = new Alert(overrideOptions);
  const actualAlert = alert.generate();

  assert.deepEqual(actualAlert.name, overrideOptions.name);
  assert.deepEqual(actualAlert.for, overrideOptions.for);
  assert.end();
});

test('alert should be able to receive conditions in the constructor', assert => {
  const overrideConditions = {
    conditions: [{
      type: 'query',
      query: {
        params: [
          'B',
          '5m',
          'now'
        ]
      },
      reducer: {
        type: 'min',
        params: []
      },
      evaluator: {
        type: 'gt',
        params: [1.1]
      },
      operator: {
        type: 'and'
      }
    }]
  };

  const alert = new Alert(overrideConditions);

  assert.deepEqual(alert.generate(), alertWithCondition);
  assert.end();
});
<|endoftext|>


<commit_before>angular.module('fitness.game', [])

// directive for auto-focusing popup input when shown
.directive('focusMe', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      $timeout(function() {
        element[0].focus();
      }, 150);
    }
  };
})
.controller('GameCtrl', function($ionicPopup, $ionicModal, $timeout, $interval, gameService, $scope, $state) {

	console.log('Game Controller loaded');

	var self = this;
	var vowels = ['A', 'E', 'I', 'O', 'U'];
	var numVowels;
	var numCons;
	var vowelsRevealed;
	var consRevealed;
	var vowelStack, consStack, thePhrase, noSpace;
	var thePhrase;

	this.init = function(){
		console.log('init game function');
		var obj = {} ;

		if (gameService.activeGame) {
			console.info("new game!")
			self.loadGame();
		}
		else {
			self.loadGame();
		}

		console.info(thePhrase)
	};

	this.showPopup = function() {
		$scope.data = self.stringifyViewModel();
		var guessPopup = $ionicPopup.show({
			templateUrl: 'pages/game/guess-popup.html',
			title: 'Enter Phrase',
			scope: $scope,
			buttons: [
				{
					text: 'Cancel',
					type: 'button-dark'
				},
				{
					text: 'Solve',
					type: 'button-positive',
					onTap: function(e) {
						if(self.guess && self.guess.toUpperCase() === thePhrase) {
							guessPopup.close();
							self.goodGuess();
						} else {
							self.badGuess();
						}
					}
				}
			]
		});
	};

	this.stringifyViewModel = function() {
		var str = "";
		console.info(self.phrase)
		for(var a = 0; a < self.phrase.length; a++) {
			for(var b = 0; b < self.phrase[a].length; b++) {
				if(self.phrase[a][b].revealed) {
					str += self.phrase[a][b].letter;
				} else {
					str += ".";
				}
			}
			str += " ";
		}

		return str;
	};

	this.goodGuess = function() {
		console.log("good guess!")
		$ionicPopup.alert({
	     	title: 'You are correct!',
	    	 template: 'Great Job! Play another.'
	   });
		self.revealAll();
		self.initModal("pages/game/win-modal.html");
		$state.go('app.home');
	};

	this.badGuess = function() {
		self.guess = "";
		$ionicPopup.alert({
	     title: 'Wrong Guess!',
	     template: 'Try again!'
	   });
	};

	this.timeExpired = function(){
		self.guess = "";
			$ionicPopup.alert({
	  		   title: 'Time Expired!',
	  		   template: 'Uh no! You ran out of time. Better luck next time!'
	   });

	   $state.go('app.home');

	}

	this.initModal = function(template) {
		$ionicModal.fromTemplateUrl(template, {
			scope: $scope,
			animation: 'slide-in-up'
		}).then(function(modal) {
			self.modal = modal;
			self.modal.show();
		});
	};

	// this will replace the existing reveal function
	this.reveal = function(letterType) {
		var curr;
		if(letterType === "V" && vowelStack.length != 0) {
			curr = vowelStack.shift();
			if (curr) {
				curr.model = curr.letter;
				curr.revealed = true;
				vowelsRevealed++;
			}
		} else if(letterType === "C" && consStack.length != 0) {
			curr = consStack.shift();
			if (curr) {
				curr.model = curr.letter;
				curr.revealed = true;
				consRevealed++;
			}
		} else {
			console.error("something went wrong")
			// either we have a bad lettertype or both stacks are empty
		}
	};

	// reveals the entire phrase
	this.revealAll = function() {
		vowelStack.forEach(function(d) {
			d.model = d.letter;
		});

		consStack.forEach(function(d) {
			d.model = d.letter;
		});
	}

	// takes the number of vowels and consonants that should be revealed and reveals them
	this.catchUp = function(vowels, consonants) {
		console.log('catch up to ' + vowels, consonants);
		if(vowels < 0 || consonants < 0) {
			return;
		}

		for(var a = 0; a < vowels; a++) {
			self.reveal("V");
		}

		for(var b = 0; b < consonants; b++) {
			self.reveal("C");
		}
	};

	this.saveGame = function() {
		console.info("game saved!")
		var state = {
			numVowels: numVowels,
			numCons: numCons,
			vowelsRevealed: vowelsRevealed,
			consRevealed: consRevealed,
			vowelStack: vowelStack,
			consStack: consStack,
			viewModel: self.phrase,
			thePhrase: thePhrase,
			noSpace: noSpace
		};

		return localforage.setItem("gameState", state);
	};

	this.loadGame = function() {
		console.info("game loaded!")
		localforage.getItem("gameState").then(function(savedState) {
			if (savedState){
					numVowels = savedState.numVowels;
					numCons = savedState.numCons;
					vowelsRevealed = savedState.vowelsRevealed;
					consRevealed = savedState.consRevealed;
					vowelStack = savedState.vowelStack;
					consStack = savedState.consStack;
					self.phrase = savedState.viewModel;
					thePhrase = savedState.thePhrase;
					noSpace = savedState.noSpace;
			}
			else{
				obj = gameService.startGame();
				self.guess = "" ;

				gameService.activeGame = obj ;
				gameService.activeGameFlag = true ;

				numVowels = obj.numVowels;
				numCons = obj.numCons;
				vowelsRevealed = 0;
				consRevealed = 0;
				vowelStack = obj.vowelStack;
				consStack = obj.consStack;
				self.phrase = obj.viewModel;
				thePhrase = obj.phrase;
				noSpace = obj.noSpace;

			}
			gameService.activeGame = true;
		});
	};

	this.testReveal = function() {
		if(vowelStack.length > 0) {
			self.reveal("V");
		} else {
			self.reveal("C");
		}
	}

	this.drawFriendly = function() {

		console.log('goal steps ' + self.fitbitData.goals.steps);

		jQuery("#friend-progress").empty();

			self.friendlyProgress = jQuery("#friend-progress").radialMultiProgress("init", {
				'fill': 25,
				'font-size': 14,
				'size': 100,
				'data': [
					{'color': "#2DB1E4", 'range': [0, self.fitbitData.goals.steps]}, // steps
					{'color': "#9CCA13", 'range': [0, self.fitbitData.goals.floors]}, // floors
					{'color': "#cf2583", 'range': [0, 24]} // time
				]
			});

		var dh, dm, ds;
	//	setInterval(function() {
			console.log('in the draw circle func.');

			var date = new Date(),
	        h = date.getHours(),
	        m = date.getMinutes(),
	        s = date.getSeconds();

			self.fitbitData.time = gameService.updateTime(h);

			console.log('hours is ' + h);

	    	self.friendlyProgress.radialMultiProgress("to", {
	      		"index": 0, 'perc': self.fitbitData.steps, 'time': 10
	      	});
	    	self.friendlyProgress.radialMultiProgress("to", {
	    		"index": 1, 'perc': self.fitbitData.floors, 'time': 10
	    	});
	    	self.friendlyProgress.radialMultiProgress("to", {
	    		"index": 2, 'perc': h, 'time': 10
	    	});
	  //  }
//	  }, 1000);
	};

	$scope.$on('$ionicView.leave', function(e) {
		self.saveGame();
	});

	$scope.$on('$ionicView.enter', function(e) {

		self.fitbitData = {} ;
		self.fitbitData.goals = {} ;

		self.fitbitData = gameService.data ;

		console.log('steps in game.js: ' + self.fitbitData.steps);

		console.log('getting access token from storage');
		localforage.getItem('fitbitToken').then(function(token){
			if (!token)
				gameService.reload();

			gameService.fitbitToken = token;
			console.log('saving token to game service: ' + token);
			gameService.getFitbitData();
			$timeout( self.computeGameLogic, 4000);
		});

		self.init();

		$interval.cancel(self.queryInterval);

		self.queryInterval = $interval( function(){ self.refreshData(); } , 260000);

		$timeout( self.drawFriendly, 1000);
	});

	this.refreshData = function(){

		gameService.getFitbitData();
		$timeout( self.computeGameLogic, 2000);
		$timeout( self.drawFriendly, 750);
		$scope.$broadcast('scroll.refreshComplete');
	}

	this.computeGameLogic = function(){
		var vowelWorth = parseInt( self.fitbitData.goals.floors /  numVowels);
		var consWorth = parseInt(self.fitbitData.goals.steps /  numCons) ;

		if (consWorth < 200) // min steps required
			consWorth = 200 ;

		if (vowelWorth < 2) // min floors required
			vowelWorth = 2 ;

		var vowelCount = parseInt(self.fitbitData.floors / vowelWorth ) - vowelsRevealed;
		var consCount = parseInt(self.fitbitData.steps / consWorth ) - consRevealed;
		self.catchUp( vowelCount, consCount );
	}

	this.openGiveUpAlert = function() {
		$ionicPopup.confirm({
	     title: 'Give Up?',
	     template: 'Are you sure you want to give up on this puzzle? You will lose all progress on this puzzle.',
		 okText: 'Give Up'
	   }).then(function(res) {
	   	if(res) {
	   		self.closeGame();
	   	} else {
	   		// do nothing
	   	}
	   });
	};

	this.closeGame = function(){
		// needs a prompt

		gameService.activeGame = false;
		$state.transitionTo('app.home');
	}
});
<commit_msg>fixed the give up game<commit_after>angular.module('fitness.game', [])

// directive for auto-focusing popup input when shown
.directive('focusMe', function($timeout) {
  return {
    link: function(scope, element, attrs) {
      $timeout(function() {
        element[0].focus();
      }, 150);
    }
  };
})
.controller('GameCtrl', function($ionicPopup, $ionicModal, $timeout, $interval, gameService, $scope, $state) {

	console.log('Game Controller loaded');

	var self = this;
	var vowels = ['A', 'E', 'I', 'O', 'U'];
	var numVowels;
	var numCons;
	var vowelsRevealed;
	var consRevealed;
	var vowelStack, consStack, thePhrase, noSpace;
	var thePhrase;

	this.init = function(){
		console.log('init game function');
		var obj = {} ;

		if (gameService.activeGame) {
			console.info("new game!")
			self.loadGame();
		}
		else {
			self.loadGame();
		}

		console.info(thePhrase)
	};

	this.showPopup = function() {
		$scope.data = self.stringifyViewModel();
		var guessPopup = $ionicPopup.show({
			templateUrl: 'pages/game/guess-popup.html',
			title: 'Enter Phrase',
			scope: $scope,
			buttons: [
				{
					text: 'Cancel',
					type: 'button-dark'
				},
				{
					text: 'Solve',
					type: 'button-positive',
					onTap: function(e) {
						if(self.guess && self.guess.toUpperCase() === thePhrase) {
							guessPopup.close();
							self.goodGuess();
						} else {
							self.badGuess();
						}
					}
				}
			]
		});
	};

	this.stringifyViewModel = function() {
		var str = "";
		console.info(self.phrase)
		for(var a = 0; a < self.phrase.length; a++) {
			for(var b = 0; b < self.phrase[a].length; b++) {
				if(self.phrase[a][b].revealed) {
					str += self.phrase[a][b].letter;
				} else {
					str += ".";
				}
			}
			str += " ";
		}

		return str;
	};

	this.goodGuess = function() {
		console.log("good guess!")
		$ionicPopup.alert({
	     	title: 'You are correct!',
	    	 template: 'Great Job! Play another.'
	   });
		self.revealAll();
		self.initModal("pages/game/win-modal.html");
		$state.go('app.home');
	};

	this.badGuess = function() {
		self.guess = "";
		$ionicPopup.alert({
	     title: 'Wrong Guess!',
	     template: 'Try again!'
	   });
	};

	this.timeExpired = function(){
		self.guess = "";
			$ionicPopup.alert({
	  		   title: 'Time Expired!',
	  		   template: 'Uh no! You ran out of time. Better luck next time!'
	   });

	   $state.go('app.home');

	}

	this.initModal = function(template) {
		$ionicModal.fromTemplateUrl(template, {
			scope: $scope,
			animation: 'slide-in-up'
		}).then(function(modal) {
			self.modal = modal;
			self.modal.show();
		});
	};

	// this will replace the existing reveal function
	this.reveal = function(letterType) {
		var curr;
		if(letterType === "V" && vowelStack.length != 0) {
			curr = vowelStack.shift();
			if (curr) {
				curr.model = curr.letter;
				curr.revealed = true;
				vowelsRevealed++;
			}
		} else if(letterType === "C" && consStack.length != 0) {
			curr = consStack.shift();
			if (curr) {
				curr.model = curr.letter;
				curr.revealed = true;
				consRevealed++;
			}
		} else {
			console.error("something went wrong")
			// either we have a bad lettertype or both stacks are empty
		}
	};

	// reveals the entire phrase
	this.revealAll = function() {
		vowelStack.forEach(function(d) {
			d.model = d.letter;
		});

		consStack.forEach(function(d) {
			d.model = d.letter;
		});
	}

	// takes the number of vowels and consonants that should be revealed and reveals them
	this.catchUp = function(vowels, consonants) {
		console.log('catch up to ' + vowels, consonants);
		if(vowels < 0 || consonants < 0) {
			return;
		}

		for(var a = 0; a < vowels; a++) {
			self.reveal("V");
		}

		for(var b = 0; b < consonants; b++) {
			self.reveal("C");
		}
	};

	this.saveGame = function() {
		console.info("game saved!")
		var state = {
			numVowels: numVowels,
			numCons: numCons,
			vowelsRevealed: vowelsRevealed,
			consRevealed: consRevealed,
			vowelStack: vowelStack,
			consStack: consStack,
			viewModel: self.phrase,
			thePhrase: thePhrase,
			noSpace: noSpace
		};

		return localforage.setItem("gameState", state);
	};

	this.loadGame = function() {
		console.info("game loaded!")
		localforage.getItem("gameState").then(function(savedState) {
			if (savedState){
					numVowels = savedState.numVowels;
					numCons = savedState.numCons;
					vowelsRevealed = savedState.vowelsRevealed;
					consRevealed = savedState.consRevealed;
					vowelStack = savedState.vowelStack;
					consStack = savedState.consStack;
					self.phrase = savedState.viewModel;
					thePhrase = savedState.thePhrase;
					noSpace = savedState.noSpace;
			}
			else{
				obj = gameService.startGame();
				self.guess = "" ;

				gameService.activeGame = obj ;
				gameService.activeGameFlag = true ;

				numVowels = obj.numVowels;
				numCons = obj.numCons;
				vowelsRevealed = 0;
				consRevealed = 0;
				vowelStack = obj.vowelStack;
				consStack = obj.consStack;
				self.phrase = obj.viewModel;
				thePhrase = obj.phrase;
				noSpace = obj.noSpace;

			}
			gameService.activeGame = true;
		});
	};

	this.testReveal = function() {
		if(vowelStack.length > 0) {
			self.reveal("V");
		} else {
			self.reveal("C");
		}
	}

	this.drawFriendly = function() {

		console.log('goal steps ' + self.fitbitData.goals.steps);

		jQuery("#friend-progress").empty();

			self.friendlyProgress = jQuery("#friend-progress").radialMultiProgress("init", {
				'fill': 25,
				'font-size': 14,
				'size': 100,
				'data': [
					{'color': "#2DB1E4", 'range': [0, self.fitbitData.goals.steps]}, // steps
					{'color': "#9CCA13", 'range': [0, self.fitbitData.goals.floors]}, // floors
					{'color': "#cf2583", 'range': [0, 24]} // time
				]
			});

		var dh, dm, ds;
	//	setInterval(function() {
			console.log('in the draw circle func.');

			var date = new Date(),
	        h = date.getHours(),
	        m = date.getMinutes(),
	        s = date.getSeconds();

			self.fitbitData.time = gameService.updateTime(h);

			console.log('hours is ' + h);

	    	self.friendlyProgress.radialMultiProgress("to", {
	      		"index": 0, 'perc': self.fitbitData.steps, 'time': 10
	      	});
	    	self.friendlyProgress.radialMultiProgress("to", {
	    		"index": 1, 'perc': self.fitbitData.floors, 'time': 10
	    	});
	    	self.friendlyProgress.radialMultiProgress("to", {
	    		"index": 2, 'perc': h, 'time': 10
	    	});
	  //  }
//	  }, 1000);
	};

	$scope.$on('$ionicView.leave', function(e) {
		self.saveGame();
	});

	$scope.$on('$ionicView.enter', function(e) {

		self.fitbitData = {} ;
		self.fitbitData.goals = {} ;

		self.fitbitData = gameService.data ;

		console.log('steps in game.js: ' + self.fitbitData.steps);

		console.log('getting access token from storage');
		localforage.getItem('fitbitToken').then(function(token){
			if (!token)
				gameService.reload();

			gameService.fitbitToken = token;
			console.log('saving token to game service: ' + token);
			gameService.getFitbitData();
			$timeout( self.computeGameLogic, 4000);
		});

		self.init();

		$interval.cancel(self.queryInterval);

		self.queryInterval = $interval( function(){ self.refreshData(); } , 260000);

		$timeout( self.drawFriendly, 1000);
	});

	this.refreshData = function(){

		gameService.getFitbitData();
		$timeout( self.computeGameLogic, 2000);
		$timeout( self.drawFriendly, 750);
		$scope.$broadcast('scroll.refreshComplete');
	}

	this.computeGameLogic = function(){
		var vowelWorth = parseInt( self.fitbitData.goals.floors /  numVowels);
		var consWorth = parseInt(self.fitbitData.goals.steps /  numCons) ;

		if (consWorth < 200) // min steps required
			consWorth = 200 ;

		if (vowelWorth < 2) // min floors required
			vowelWorth = 2 ;

		var vowelCount = parseInt(self.fitbitData.floors / vowelWorth ) - vowelsRevealed;
		var consCount = parseInt(self.fitbitData.steps / consWorth ) - consRevealed;
		self.catchUp( vowelCount, consCount );
	}

	this.openGiveUpAlert = function() {
		$ionicPopup.confirm({
	     title: 'Aww You\'re Ready To Give Up Already?',
	     template: 'Are you sure you want to give up on this puzzle? All progress will be lost.',
		 okText: 'I Give Up'
	   }).then(function(res) {
	   	if(res) {
	   		self.closeGame();
	   	} else {
	   		// do nothing
	   	}
	   });
	};

	this.closeGame = function(){
		// needs a prompt

		localforage.removeItem("gameState");

		gameService.activeGame = false;
		$state.transitionTo('app.home');
	}
});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import Modal from 'react-modal';
import { polygon } from 'turf'
import Map from '../Map'
import Header from '../Header'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
//import * as StatsActions from '../../actions/stats'
import regionToCoords from '../Map/regionToCoords'
import searchHotProjectsInRegion from './searchHotProjects'
import searchFeatures from './searchFeatures'
import style from './style.css'

const hotProjectsModalStyles = {
  overlay: {
    backgroundColor: 'rgba(60,60,60, 0.5)'
  },
  content: {
    top: '50%',
    left: '50%',
    right: 'auto',
    bottom: 'auto',
    marginRight: '-50%',
    transform: 'translate(-50%, -50%)',
    maxHeight: '350px',
    maxWidth: '512px',
    borderRadius: '4px',
    paddingTop: '25px',
    paddingBottom: '35px',
    paddingLeft: '35px',
    paddingRight: '35px'
  }
}

class Stats extends Component {
  state = {
    features: [],
    hotProjects: [],
    hotProjectsModalOpen: false,
    updating: false
  }

  render() {
    // todo: loading animation if region is not yet fully loaded
    return (

{this.state.features.length}
Buildings

{this.state.hotProjects.length}
HOT Projects

        {this.state.updating ? 'updating…' : ''}
        <Modal
          isOpen={this.state.hotProjectsModalOpen}
          onRequestClose={::this.closeHotModal}
          style={hotProjectsModalStyles}>

HOT Projects

          {this.state.hotProjects.map(p =>

{'#'+p.id+' '+p.properties.name}

          )}

        </Modal>

    )
  }

  componentDidMount() {
    if (this.props.map.region) {
      ::this.update(this.props.map.region)
    }
  }

  componentWillReceiveProps(nextProps) {
    // check for changed map parameters
    if (nextProps.map.region !== this.props.map.region) {
      ::this.update(nextProps.map.region)
    }
  }

  update(region) {
    region = polygon(regionToCoords(region))
    this.setState({ updating: true })
    searchFeatures(region, function(err, data) {
      this.setState({
        features: data.features,
        updating: false
      })
    }.bind(this))
    const hotProjects = searchHotProjectsInRegion(region)
    this.setState({ hotProjects })
  }

  openHotModal() {
    this.setState({ hotProjectsModalOpen: true });
  }

  closeHotModal() {
    this.setState({ hotProjectsModalOpen: false });
  }

}

function mapStateToProps(state) {
  return {
    map: state.map
  }
}

function mapDispatchToProps(dispatch) {
  return {
    //actions: bindActionCreators(StatsActions, dispatch)
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Stats)
<commit_msg>add number of contributors to stats bar<commit_after>import React, { Component } from 'react'
import Modal from 'react-modal';
import { polygon } from 'turf'
import Map from '../Map'
import Header from '../Header'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
//import * as StatsActions from '../../actions/stats'
import regionToCoords from '../Map/regionToCoords'
import searchHotProjectsInRegion from './searchHotProjects'
import searchFeatures from './searchFeatures'
import style from './style.css'

const hotProjectsModalStyles = {
  overlay: {
    backgroundColor: 'rgba(60,60,60, 0.5)'
  },
  content: {
    top: '50%',
    left: '50%',
    right: 'auto',
    bottom: 'auto',
    marginRight: '-50%',
    transform: 'translate(-50%, -50%)',
    maxHeight: '350px',
    maxWidth: '512px',
    borderRadius: '4px',
    paddingTop: '25px',
    paddingBottom: '35px',
    paddingLeft: '35px',
    paddingRight: '35px'
  }
}

class Stats extends Component {
  state = {
    features: [],
    hotProjects: [],
    hotProjectsModalOpen: false,
    updating: false
  }

  render() {
    var contributors = {}
    this.state.features.forEach(f => {
      contributors[f.properties._uid] = true
    })
    const numContribuors = Object.keys(contributors).length
    // todo: loading animation if region is not yet fully loaded
    return (

{this.state.features.length}
Buildings

{this.state.hotProjects.length}
HOT Projects

{numContribuors}
Contributors

        {this.state.updating ? 'updating…' : ''}
        <Modal
          isOpen={this.state.hotProjectsModalOpen}
          onRequestClose={::this.closeHotModal}
          style={hotProjectsModalStyles}>

HOT Projects

          {this.state.hotProjects.map(p =>

{'#'+p.id+' '+p.properties.name}

          )}

        </Modal>

    )
  }

  componentDidMount() {
    if (this.props.map.region) {
      ::this.update(this.props.map.region)
    }
  }

  componentWillReceiveProps(nextProps) {
    // check for changed map parameters
    if (nextProps.map.region !== this.props.map.region) {
      ::this.update(nextProps.map.region)
    }
  }

  update(region) {
    region = polygon(regionToCoords(region))
    this.setState({ updating: true })
    searchFeatures(region, function(err, data) {
      this.setState({
        features: data.features,
        updating: false
      })
    }.bind(this))
    const hotProjects = searchHotProjectsInRegion(region)
    this.setState({ hotProjects })
  }

  openHotModal() {
    this.setState({ hotProjectsModalOpen: true });
  }

  closeHotModal() {
    this.setState({ hotProjectsModalOpen: false });
  }

}

function mapStateToProps(state) {
  return {
    map: state.map
  }
}

function mapDispatchToProps(dispatch) {
  return {
    //actions: bindActionCreators(StatsActions, dispatch)
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Stats)
<|endoftext|>


<commit_before>var EOL = require('os').EOL
  , fs = require('fs')
  , assert = require('assert')
  , path = require('path')
  , util = require('util')
  , events = require('events');

var stores = {};

var HistoryStore = function(parent, options, lines) {
  options = options || {};
  console.dir(lines);
  lines = lines.split('\n');
  lines = lines.filter(function(line) {
    line = line.replace(/\r$/, '');
    line = line.trim();
    return line;
  })
  options.flush = options.flush !== undefined ? options.flush : true;
  options.duplicates = options.duplicates !== undefined
    ? options.duplicates : false;
  this.parent = parent;
  this.file = options.file;
  this.options = options;
  this.history = lines;
  console.dir(lines);
  this.stream = fs.createWriteStream(this.file, {flags: 'a'});
  this.offset = this.history.length;
}

HistoryStore.prototype.isFlushed = function() {
  return this.offset === this.history.length;
}

HistoryStore.prototype.write = function(flush, cb) {
  var scope = this;
  if(!flush || this.offset === this.history.length) return cb(null, scope);
  console.log('write to disc %s %s', this.offset, this.history.length);
  var lines = this.history.slice(this.offset, this.history.length);
  // add trailing newline
  if(lines[lines.length - 1]) {
    lines.push('');
  }
  var append = lines.join(EOL);
  console.log('write to disc %j', append);

}

HistoryStore.prototype.add = function(line, options, cb) {
  if(typeof options === 'function') {
    cb = options;
    options = null;
  }
  cb = typeof cb === 'function' ? cb : function noop(){};
  options = options || {};
  var scope = this
    , flush = options.flush || this.options.flush;
  if(!this.options.duplicates && ~this.history.indexOf(line)) {
    //console.log('ignoring duplicate');
    return cb(null, scope);
  }
  console.log('flush %s', flush);
  assert(typeof line === 'string', 'history entry must be a string')
  line = '' + line;
  line = line.replace(/\r?\n$/, '');
  this.history.push(line);
  this.write(flush, cb);
}

//HistoryStore.prototype.close = function

HistoryStore.prototype.clear = function(cb) {
  var scope = this;
  fs.writeFile(this.file, '', function(err) {
    if(err) return cb(err, scope);
    scope.history = [];
    scope.offset = 0;
    cb(null, scope);
  })
}

var History = function(options) {
  options = options || {};
  options.create = options.create !== undefined ? options.create : true;
  this.options = options;
}

util.inherits(History, events.EventEmitter);

History.prototype.load = function(options, cb) {
  var scope = this;
  if(typeof options === 'function') {
    cb = options;
    options = null;
  }
  options = options || {};
  var file = options.file || this.options.file
    , create = options.create !== undefined
      ? options.create : this.options.create;
  assert(file, 'cannot load history with no file');
  file = path.normalize(file);
  if(stores[file] && !options.force) {
    return cb(null, stores[file]);
  }
  function touch() {
    var st = fs.createWriteStream(file, function(err) {
      if(err) return cb(err, null, scope);
      st.end();
      st.destroy();
      load(options, cb);
    });
  }
  fs.readFile(file, function(err, contents) {
    if(err && err.code === 'ENOENT' && create) {
      return touch();
    }
    if(err) return cb(err, null, scope);
    var store = new HistoryStore(scope, scope.options, '' + contents);
    stores[file] = store;
    //console.log('isFlushed: %s', store.isFlushed());
    store.add('line item');
    store.add('line item');
    cb(null, store, scope);
  })
}

/**
 *  Get a history store by file path or all stores.
 */
History.prototype.getStore = function(file) {
  if(file) return stores[file];
  return stores;
}

function history(options, cb) {
  var h = new History(options);
  if(cb) {
    h.load(options, cb);
  }
  return h;
}

history.History = History;

module.exports = history;
<commit_msg>Use private variables in history store.<commit_after>var EOL = require('os').EOL
  , fs = require('fs')
  , assert = require('assert')
  , path = require('path')
  , util = require('util')
  , events = require('events');

var stores = {};

var HistoryStore = function(parent, options, lines) {
  options = options || {};
  console.dir(lines);
  lines = lines.split('\n');
  lines = lines.filter(function(line) {
    line = line.replace(/\r$/, '');
    line = line.trim();
    return line;
  })
  options.flush = options.flush !== undefined ? options.flush : true;
  options.duplicates = options.duplicates !== undefined
    ? options.duplicates : false;
  this.file = options.file;
  this.options = options;
  this._parent = parent;
  this._history = lines;
  //console.dir(lines);
  this._stream = fs.createWriteStream(this.file, {flags: 'a'});
  this._checkpoint = this._history.length;
}

HistoryStore.prototype.isFlushed = function() {
  return this._checkpoint === this._history.length;
}

HistoryStore.prototype.write = function(flush, cb) {
  var scope = this;
  if(!flush || this._checkpoint === this._history.length) return cb(null, scope);
  console.log('write to disc %s %s', this._checkpoint, this._history.length);
  var lines = this._history.slice(this._checkpoint, this._history.length);
  // add trailing newline
  if(lines[lines.length - 1]) {
    lines.push('');
  }
  var append = lines.join(EOL);
  console.log('write to disc %j', append);
  //this._stream =

}

HistoryStore.prototype.add = function(line, options, cb) {
  if(typeof options === 'function') {
    cb = options;
    options = null;
  }
  cb = typeof cb === 'function' ? cb : function noop(){};
  options = options || {};
  var scope = this
    , flush = options.flush || this.options.flush;
  if(!this.options.duplicates && ~this._history.indexOf(line)) {
    //console.log('ignoring duplicate');
    return cb(null, scope);
  }
  console.log('flush %s', flush);
  assert(typeof line === 'string', 'history entry must be a string')
  line = '' + line;
  line = line.replace(/\r?\n$/, '');
  this._history.push(line);
  this.write(flush, cb);
}

//HistoryStore.prototype.close = function

HistoryStore.prototype.clear = function(cb) {
  var scope = this;
  fs.writeFile(this.file, '', function(err) {
    if(err) return cb(err, scope);
    scope.history = [];
    scope.offset = 0;
    cb(null, scope);
  })
}

var History = function(options) {
  options = options || {};
  options.create = options.create !== undefined ? options.create : true;
  this.options = options;
}

util.inherits(History, events.EventEmitter);

History.prototype.load = function(options, cb) {
  var scope = this;
  if(typeof options === 'function') {
    cb = options;
    options = null;
  }
  options = options || {};
  var file = options.file || this.options.file
    , create = options.create !== undefined
      ? options.create : this.options.create;
  assert(file, 'cannot load history with no file');
  file = path.normalize(file);
  if(stores[file] && !options.force) {
    return cb(null, stores[file]);
  }
  function touch() {
    var st = fs.createWriteStream(file, function(err) {
      if(err) return cb(err, null, scope);
      st.end();
      st.destroy();
      load(options, cb);
    });
  }
  fs.readFile(file, function(err, contents) {
    if(err && err.code === 'ENOENT' && create) {
      return touch();
    }
    if(err) return cb(err, null, scope);
    var store = new HistoryStore(scope, scope.options, '' + contents);
    stores[file] = store;
    //console.log('isFlushed: %s', store.isFlushed());
    store.add('line item');
    store.add('line item');
    cb(null, store, scope);
  })
}

/**
 *  Get a history store by file path or all stores.
 */
History.prototype.getStore = function(file) {
  if(file) return stores[file];
  return stores;
}

function history(options, cb) {
  var h = new History(options);
  if(cb) {
    h.load(options, cb);
  }
  return h;
}

history.History = History;

module.exports = history;
<|endoftext|>


<commit_before>'use strict';

var snakeCaseKeys = require('../utils/snake-case-keys');
var backboneFormatter = require('../formatters/backbone');
var bkOptionsFormatter = require('../formatters/bookshelf-options');

// Bookshelf options keys.
var OPTIONS_KEYS = ['withRelated'];

/**
 * Create a record.
 *
 * @param options.model {Object} a Bookshelf Model
 * @return {function} middleware
 */

module.exports = function (options) {
  return function (req, res, next) {
    var bkOpts = bkOptionsFormatter(OPTIONS_KEYS, req.query, options);
    var body = snakeCaseKeys(req.body);

    // Set status to "201 Created".
    res.statusCode = 201;

    options.model.forge()
    .save(body)
    .then(function (model) {
      return model.load(bkOpts.withRelated);
    })
    .then(function (model) {
      return model.fetch();
    })
    .then(function (model) {
      res.body = backboneFormatter.formatModel(model);
    })
    .exec(next);
  };
};
<commit_msg>Error with json postGres, so for fetch I need to know which key to fetch<commit_after>'use strict';

var snakeCaseKeys = require('../utils/snake-case-keys');
var backboneFormatter = require('../formatters/backbone');
var bkOptionsFormatter = require('../formatters/bookshelf-options');

// Bookshelf options keys.
var OPTIONS_KEYS = ['withRelated'];

/**
 * Create a record.
 *
 * @param options.model {Object} a Bookshelf Model
 * @return {function} middleware
 */

module.exports = function (options) {
  return function (req, res, next) {
    var bkOpts = bkOptionsFormatter(OPTIONS_KEYS, req.query, options);
    var body = snakeCaseKeys(req.body);

    // Set status to "201 Created".
    res.statusCode = 201;

    options.model.forge()
    .save(body)
    .then(function (model) {
      return model.load(bkOpts.withRelated);
    })
    .then(function (model) {
      if (options.indexKey) {
        var indexKey = options.indexKey;
        return options.model.forge({ indexKey: model.get(indexKey) }).fetch();
      } else {
        return options.model.forge({ id: model.id }).fetch();
      }
    })
    .then(function (model) {
      res.body = backboneFormatter.formatModel(model);
    })
    .exec(next);
  };
};
<|endoftext|>


<commit_before>import React from 'react'
import Immutable from 'immutable'
import oxford from 'humanize-list'
import plur from 'plur'

import AutosizeInput from 'react-input-autosize'

import studentActions from '../flux/studentActions'

const goodGraduationMessage = "It looks like you'll make it! Just follow the plan, and go over my output with your advisor a few times."
const badGraduationMessage = "You haven't planned everything out yet. Ask your advisor if you need help fitting everything in."

export default class StudentSummary extends React.Component {
	static propTypes = {
		graduatability: React.PropTypes.bool.isRequired,
		student: React.PropTypes.instanceOf(Immutable.Record).isRequired,
	}

	render() {
		// console.log('StudentSummary#render')
		const canGraduate = this.props.graduatability
		const student = this.props.student
		const studies = student.studies

		const name = student.name
		const studentId = student.id
		const NameEl = (<AutosizeInput className='autosize-input'
			value={name}
			onChange={ev => studentActions.updateName(studentId, ev.target.value.trim())} />)

		const degrees = studies.filter(s => s.type === 'degree')
		const majors = studies.filter(s => s.type === 'major')
		const concentrations = studies.filter(s => s.type === 'concentration')
		const emphases = studies.filter(s => s.type === 'emphasis')

		const degreeWord = plur('degree', degrees.size)
		const majorWord = plur('major', majors.size)
		const concentrationWord = plur('concentration', concentrations.size)
		const emphasisWord = plur('emphasis', 'emphases', emphases.size)

		const degreeEmphasizer = degrees.size === 1 ? 'a ' : ''
		const majorEmphasizer = majors.size === 1 ? 'a ' : ''
		const concentrationEmphasizer = concentrations.size === 1 ? 'a ' : ''
		const emphasisEmphasizer = emphases.size === 1 ? 'an ' : ''

		const degreeEl = oxford(degrees.map(s => s.name).toArray())
		const majorEl = oxford(majors.map(s => s.name).toArray())
		const concentrationEl = oxford(concentrations.map(s => s.name).toArray())
		const emphasisEl = oxford(emphases.map(s => s.name).toArray())

		const graduationEl = (<AutosizeInput
			className='autosize-input'
			value={String(student.graduation)}
			onChange={ev => studentActions.changeGraduation(studentId, parseInt(ev.target.value))} />)

		const sinceMatriculationEl = (<AutosizeInput
			className='autosize-input'
			value={String(student.graduation - student.matriculation)}
			onChange={ev => studentActions.changeMatriculation(studentId, student.graduation - parseInt(ev.target.value))} />)

		return (

{name.length ? name[0] : ''}

Hi, {NameEl}

					You are planning on graduating in {graduationEl}, {sinceMatriculationEl}
					{' years after matriculating, with '}
					{(degrees.size > 0) ? `${degreeEmphasizer}${degreeEl} ${degreeWord}` : `no ${degreeWord}`}
					{(majors.size > 0) ? `, ${majorEmphasizer}${majorWord} in ${majorEl}` : null}
					{(concentrations.size > 0) ? `, and ${concentrationEmphasizer}${concentrationWord} in ${concentrationEl}` : null}
					{(emphases.size > 0) ? `, not to mention ${emphasisEmphasizer}${emphasisWord} in ${emphasisEl}` : null}{"."}

					{canGraduate ? goodGraduationMessage : badGraduationMessage}

)
	}
}
<commit_msg>fix StudentSummary once and for all<commit_after>import React from 'react'
import Immutable from 'immutable'
import oxford from 'humanize-list'
import plur from 'plur'

import AutosizeInput from 'react-input-autosize'

import studentActions from '../flux/studentActions'

const goodGraduationMessage = "It looks like you'll make it! Just follow the plan, and go over my output with your advisor a few times."
const badGraduationMessage = "You haven't planned everything out yet. Ask your advisor if you need help fitting everything in."

export default class StudentSummary extends React.Component {
	static propTypes = {
		graduatability: React.PropTypes.bool.isRequired,
		student: React.PropTypes.instanceOf(Immutable.Record).isRequired,
	}

	render() {
		// console.log('StudentSummary#render')
		const canGraduate = this.props.graduatability
		const student = this.props.student
		const studies = student.studies

		const name = student.name
		const studentId = student.id
		const NameEl = (<input className='autosize-input'
			defaultValue={name}
			onChange={ev => studentActions.changeName(studentId, ev.target.value.trim())} />)

		const degrees = studies.filter(s => s.type === 'degree')
		const majors = studies.filter(s => s.type === 'major')
		const concentrations = studies.filter(s => s.type === 'concentration')
		const emphases = studies.filter(s => s.type === 'emphasis')

		const degreeWord = plur('degree', degrees.size)
		const majorWord = plur('major', majors.size)
		const concentrationWord = plur('concentration', concentrations.size)
		const emphasisWord = plur('emphasis', 'emphases', emphases.size)

		const degreeEmphasizer = degrees.size === 1 ? 'a ' : ''
		const majorEmphasizer = majors.size === 1 ? 'a ' : ''
		const concentrationEmphasizer = concentrations.size === 1 ? 'a ' : ''
		const emphasisEmphasizer = emphases.size === 1 ? 'an ' : ''

		const degreeEl = oxford(degrees.map(s => s.name).toArray())
		const majorEl = oxford(majors.map(s => s.name).toArray())
		const concentrationEl = oxford(concentrations.map(s => s.name).toArray())
		const emphasisEl = oxford(emphases.map(s => s.name).toArray())

		const graduationEl = (<AutosizeInput
			className='autosize-input'
			value={String(student.graduation)}
			onChange={ev => studentActions.changeGraduation(studentId, parseInt(ev.target.value || 0))} />)

		const sinceMatriculationEl = (<AutosizeInput
			className='autosize-input'
			value={String(student.matriculation)}
			onChange={ev => studentActions.changeMatriculation(studentId, parseInt(ev.target.value || 0))} />)

		return (

{name.length ? name[0] : ''}

Hi, {NameEl}

					You are planning on graduating in {graduationEl},
					{' '} after matriculating in {sinceMatriculationEl}, with {' '}
					{(degrees.size > 0) ? `${degreeEmphasizer}${degreeEl} ${degreeWord}` : `no ${degreeWord}`}
					{(majors.size > 0) ? `, ${majorEmphasizer}${majorWord} in ${majorEl}` : null}
					{(concentrations.size > 0) ? `, and ${concentrationEmphasizer}${concentrationWord} in ${concentrationEl}` : null}
					{(emphases.size > 0) ? `, not to mention ${emphasisEmphasizer}${emphasisWord} in ${emphasisEl}` : null}{"."}

					{canGraduate ? goodGraduationMessage : badGraduationMessage}

)
	}
}
<|endoftext|>


<commit_before>/*
 *  Test presence of custom Function properties.
 */

/*===
name: true, fileName: true, length: true, caller: false, arguments: false, callee: false, prototype: true
name: true, fileName: true, length: true, caller: true, arguments: true, callee: false, prototype: true
name: true, fileName: true, length: true, caller: false, arguments: false, callee: false, prototype: false
name: true, fileName: true, length: true, caller: false, arguments: false, callee: false, prototype: false
===*/

function test() {
    function f1() {};
    function f2() { 'use strict'; };
    var f3 = f1.bind('mythis');
    var f4 = f2.bind('mythis');

    function dump(f) {
        print('name: ' + f.hasOwnProperty('name') + ', ' +
              'fileName: ' + f.hasOwnProperty('fileName') + ', ' +
              'length: ' + f.hasOwnProperty('length') + ', ' +
              'caller: ' + f.hasOwnProperty('caller') + ', ' +
              'arguments: ' + f.hasOwnProperty('arguments') + ', ' +
              'callee: ' + f.hasOwnProperty('callee') + ', ' +  // never present, just checking
              'prototype: ' + f.hasOwnProperty('prototype'));   // explicitly mentioned in E5.1 that not present for bound functions
    }

    dump(f1);
    dump(f2);
    dump(f3);
    dump(f4);
}

try {
    test();
} catch(e) {
    print(e);
}
<commit_msg>fix testcase expect string to match correct behavior: bound functions should have a 'caller' and 'arguments' property<commit_after>/*
 *  Test presence of custom Function properties.
 */

/*===
name: true, fileName: true, length: true, caller: false, arguments: false, callee: false, prototype: true
name: true, fileName: true, length: true, caller: true, arguments: true, callee: false, prototype: true
name: true, fileName: true, length: true, caller: true, arguments: true, callee: false, prototype: false
name: true, fileName: true, length: true, caller: true, arguments: true, callee: false, prototype: false
===*/

function test() {
    function f1() {};
    function f2() { 'use strict'; };
    var f3 = f1.bind('mythis');
    var f4 = f2.bind('mythis');

    function dump(f) {
        print('name: ' + f.hasOwnProperty('name') + ', ' +
              'fileName: ' + f.hasOwnProperty('fileName') + ', ' +
              'length: ' + f.hasOwnProperty('length') + ', ' +
              'caller: ' + f.hasOwnProperty('caller') + ', ' +
              'arguments: ' + f.hasOwnProperty('arguments') + ', ' +
              'callee: ' + f.hasOwnProperty('callee') + ', ' +  // never present, just checking
              'prototype: ' + f.hasOwnProperty('prototype'));   // explicitly mentioned in E5.1 that not present for bound functions
    }

    dump(f1);
    dump(f2);
    dump(f3);
    dump(f4);
}

try {
    test();
} catch(e) {
    print(e);
}
<|endoftext|>


<commit_before>#! /usr/bin/env node
/* @flow */

import 'babel-polyfill'
import _ from 'lodash'
import chalk from 'chalk'
import path from 'path'
import program from 'commander'
import cliui from 'cliui'

import { DiCy, File } from '@dicy/core'

const ui = cliui({ width: 80 })

function parseStrings (value) {
  return value.split(/\s*,\s*/)
}

function parseNumber (value) {
  return parseInt(value, 10)
}

function parseNumbers (value) {
  return parseStrings(value).map(x => parseNumber(x))
}

function cloneOptions (options) {
  const newOptions = {}

  for (const property in options) {
    const value = options[property]
    if (value !== undefined) {
      newOptions[property] = value
    }
  }

  return newOptions
}

const command = async (inputs, env) => {
  const commands = env.name().split(',')
  const { saveEvents = [], verbose = false, ...options } = cloneOptions(env.opts())
  commands.unshift('load')
  commands.push('save')

  for (const filePath of inputs) {
    const events = []
    const dicy = await DiCy.create(path.resolve(filePath), options)
    dicy
      .on('log', event => {
        const nameText = event.name ? `[${event.name}] ` : ''
        const typeText = event.category ? `${event.category}: ` : ''
        const text = `${nameText}${typeText}${event.text.replace('\n', ' ')}`
        switch (event.severity) {
          case 'error':
            console.error(chalk.red(text))
            break
          case 'warning':
            console.warn(chalk.yellow(text))
            break
          default:
            console.info(text)
            break
        }
      })
      .on('action', event => {
        if (verbose) {
          const triggerText = event.triggers.length !== 0 ? ` triggered by updates to ${event.triggers}` : ''
          console.log(`[${event.rule}] Evaluating ${event.action} action${triggerText}`)
        }
      })
      .on('command', event => {
        console.log(`[${event.rule}] Executing \`${event.command}\``)
      })
      .on('fileDeleted', event => {
        console.log(`Deleting \`${event.file}\``)
      })

    for (const type of saveEvents) {
      dicy.on(type, event => { events.push(event) })
    }

    process.on('SIGTERM', () => dicy.kill())
    process.on('SIGINT', () => dicy.kill())

    await dicy.run(...commands)

    if (saveEvents.length !== 0) {
      const eventFilePath = dicy.resolvePath('$DIR/$NAME-events.yaml')
      await File.safeDump(eventFilePath, { types: saveEvents, events })
    }
  }
}

program
  .version(require('../package.json').version)
  .description('An experimental circular builder for LaTeX')

DiCy.getOptionDefinitions().then(definitions => {
  function loadOptions (pc) {
    for (const option of definitions) {
      // Skip environment variables
      if (option.name.startsWith('$')) continue

      const commands = pc.name().split(',')

      if (!option.commands.some(command => commands.includes(command))) continue

      const prefix = (option.type === 'boolean' && option.defaultValue) ? 'no-' : ''
      const flagList = [].concat((option.aliases || []).filter(alias => alias.length === 1), option.name)
        .map(name => name.length === 1 ? `-${name}` : `--${prefix}${_.kebabCase(name)}`)
        .join(', ')
      const flags = (option.type === 'boolean') ? flagList : `${flagList} <${option.name}>`

      switch (option.type) {
        case 'string':
          if (option.values) {
            pc = pc.option(flags, option.description, new RegExp(`^(${option.values.join('|')})$`))
          } else {
            pc = pc.option(flags, option.description)
          }
          break
        case 'strings':
          pc = pc.option(flags, option.description, parseStrings)
          break
        case 'number':
          pc = pc.option(flags, option.description, parseNumber)
          break
        case 'numbers':
          pc = pc.option(flags, option.description, parseNumbers)
          break
        case 'boolean':
          pc = pc.option(flags, option.description)
          break
      }
    }

    pc = pc.option('--save-events <saveEvents>', 'List of event types to save for test usage.', parseStrings, [])
    pc = pc.option('-v, --verbose', 'Be verbose in command output.')

    return pc
  }

  loadOptions(program
    .command('build [inputs...]')
    .alias('b')
    .description('Build the inputs.'))
    .action(command)

  loadOptions(program
    .command('clean [inputs...]')
    .alias('c')
    .description('Clean up after a previous build.'))
    .action(command)

  loadOptions(program
    .command('scrub [inputs...]')
    .alias('s')
    .description('Clean up generated files after a previous build.'))
    .action(command)

  loadOptions(program
    .command('log [inputs...]')
    .alias('l')
    .description('Report messages from any logs.'))
    .action(command)

  loadOptions(program
    .command('graph [inputs...]')
    .alias('g')
    .description('Create a dependency graph from a previous build.'))
    .action(command)

  loadOptions(program
    .command('build,clean [inputs...]')
    .alias('bc')
    .description('Build the inputs and then clean up.'))
    .action(command)

  loadOptions(program
    .command('build,log [inputs...]')
    .alias('bl')
    .description('Build the inputs and report messages from any logs.'))
    .action(command)

  loadOptions(program
    .command('build,log,clean [inputs...]')
    .alias('blc')
    .description('Build the inputs, report messages from any logs, and then clean up.'))
    .action(command)

  program
    .command('rules')
    .description('List available rules')
    .option('--command <command>', 'List only rules that apply to a specific command.', null)
    .action(async (env) => {
      const { command } = cloneOptions(env.opts())
      const dicy = await DiCy.create('foo.tex')
      ui.div(dicy.getAvailableRules(command).map(rule => `${rule.name}\t  ${rule.description}`).join('\n'))
      console.log(ui.toString())
    })

  program
    .parse(process.argv)
}, error => { console.log(error) })
<commit_msg>Skip rule options and improve descriptions<commit_after>#! /usr/bin/env node
/* @flow */

import 'babel-polyfill'
import _ from 'lodash'
import chalk from 'chalk'
import path from 'path'
import program from 'commander'
import cliui from 'cliui'

import { DiCy, File } from '@dicy/core'

const ui = cliui({ width: 80 })

function parseStrings (value) {
  return value.split(/\s*,\s*/)
}

function parseNumber (value) {
  return parseInt(value, 10)
}

function parseNumbers (value) {
  return parseStrings(value).map(x => parseNumber(x))
}

function cloneOptions (options) {
  const newOptions = {}

  for (const property in options) {
    const value = options[property]
    if (value !== undefined) {
      newOptions[property] = value
    }
  }

  return newOptions
}

const command = async (inputs, env) => {
  const commands = env.name().split(',')
  const { saveEvents = [], verbose = false, ...options } = cloneOptions(env.opts())
  commands.unshift('load')
  commands.push('save')

  for (const filePath of inputs) {
    const events = []
    const dicy = await DiCy.create(path.resolve(filePath), options)
    dicy
      .on('log', event => {
        const nameText = event.name ? `[${event.name}] ` : ''
        const typeText = event.category ? `${event.category}: ` : ''
        const text = `${nameText}${typeText}${event.text.replace('\n', ' ')}`
        switch (event.severity) {
          case 'error':
            console.error(chalk.red(text))
            break
          case 'warning':
            console.warn(chalk.yellow(text))
            break
          default:
            console.info(text)
            break
        }
      })
      .on('action', event => {
        if (verbose) {
          const triggerText = event.triggers.length !== 0 ? ` triggered by updates to ${event.triggers}` : ''
          console.log(`[${event.rule}] Evaluating ${event.action} action${triggerText}`)
        }
      })
      .on('command', event => {
        console.log(`[${event.rule}] Executing \`${event.command}\``)
      })
      .on('fileDeleted', event => {
        console.log(`Deleting \`${event.file}\``)
      })

    for (const type of saveEvents) {
      dicy.on(type, event => { events.push(event) })
    }

    process.on('SIGTERM', () => dicy.kill())
    process.on('SIGINT', () => dicy.kill())

    await dicy.run(...commands)

    if (saveEvents.length !== 0) {
      const eventFilePath = dicy.resolvePath('$DIR/$NAME-events.yaml')
      await File.safeDump(eventFilePath, { types: saveEvents, events })
    }
  }
}

program
  .version(require('../package.json').version)
  .description('An experimental circular builder for LaTeX')

DiCy.getOptionDefinitions().then(definitions => {
  function loadOptions (pc) {
    for (const option of definitions) {
      // Skip environment variables
      if (option.name.startsWith('$') || option.name.includes('_')) continue

      const commands = pc.name().split(',')

      if (!option.commands.some(command => commands.includes(command))) continue

      const prefix = (option.type === 'boolean' && option.defaultValue) ? 'no-' : ''
      const flagList = [].concat((option.aliases || []).filter(alias => alias.length === 1), option.name)
        .map(name => name.length === 1 ? `-${name}` : `--${prefix}${_.kebabCase(name)}`)
        .join(', ')
      const flags = (option.type === 'boolean') ? flagList : `${flagList} <${option.name}>`
      let description = option.description

      if ((option.type === 'string' || option.type === 'number') && (option.values || option.defaultValue)) {
        const parts = []
        if (option.values) parts.push(`values=${option.values.join('|')}`)
        if (option.defaultValue) parts.push(`default value=${option.defaultValue}`)
        description += ` (${parts.join('; ')})`
      }

      switch (option.type) {
        case 'string':
          if (option.values) {
            pc = pc.option(flags, description, new RegExp(`^(${option.values.join('|')})$`))
          } else {
            pc = pc.option(flags, description)
          }
          break
        case 'strings':
          pc = pc.option(flags, description, parseStrings)
          break
        case 'number':
          pc = pc.option(flags, description, parseNumber)
          break
        case 'numbers':
          pc = pc.option(flags, description, parseNumbers)
          break
        case 'boolean':
          pc = pc.option(flags, option.description)
          break
      }
    }

    pc = pc.option('--save-events <saveEvents>', 'List of event types to save for test usage.', parseStrings, [])
    pc = pc.option('-v, --verbose', 'Be verbose in command output.')

    return pc
  }

  loadOptions(program
    .command('build [inputs...]')
    .alias('b')
    .description('Build the inputs.'))
    .action(command)

  loadOptions(program
    .command('clean [inputs...]')
    .alias('c')
    .description('Clean up after a previous build.'))
    .action(command)

  loadOptions(program
    .command('scrub [inputs...]')
    .alias('s')
    .description('Clean up generated files after a previous build.'))
    .action(command)

  loadOptions(program
    .command('log [inputs...]')
    .alias('l')
    .description('Report messages from any logs.'))
    .action(command)

  loadOptions(program
    .command('graph [inputs...]')
    .alias('g')
    .description('Create a dependency graph from a previous build.'))
    .action(command)

  loadOptions(program
    .command('build,clean [inputs...]')
    .alias('bc')
    .description('Build the inputs and then clean up.'))
    .action(command)

  loadOptions(program
    .command('build,log [inputs...]')
    .alias('bl')
    .description('Build the inputs and report messages from any logs.'))
    .action(command)

  loadOptions(program
    .command('build,log,clean [inputs...]')
    .alias('blc')
    .description('Build the inputs, report messages from any logs, and then clean up.'))
    .action(command)

  program
    .command('rules')
    .description('List available rules')
    .option('--command <command>', 'List only rules that apply to a specific command.', null)
    .action(async (env) => {
      const { command } = cloneOptions(env.opts())
      const dicy = await DiCy.create('foo.tex')
      ui.div(dicy.getAvailableRules(command).map(rule => `${rule.name}\t  ${rule.description}`).join('\n'))
      console.log(ui.toString())
    })

  program
    .parse(process.argv)
}, error => { console.log(error) })
<|endoftext|>