<commit_before>on('ready', function() {
    if (!_.has(state, 'Supernotes')) {
        state.Supernotes = {
            sheet: 'Default',
            template: 'default',
            title: 'name',
            theText: '',
            sendToPlayers: true

        };
        message = 'Welcome to Supernotes! If this is your first time running it, the script is set to use the Default Roll Template. You can choose a different sheet template below, as well as decide whether you want the script to display a "Send to Players" footer at the end of every GM message. It is currently set to true.

[Default Template - any sheet](!gmnote --config|default)
[D&D 5th Edition by Roll20](!gmnote --config|dnd5e)
[DnD 5e Shaped](!gmnote --config|5eshaped)
[Pathfinder by Roll20](!gmnote --config|pfofficial)
[Pathfinder Community](!gmnote --config|pfcommunity)
[Pathfinder 2e by Roll20](!gmnote --config|pf2e)
[Starfinder by Roll20](!gmnote --config|starfinder)

[Toggle Send to Players](!gmnote --config|sendtoPlayers)';
        sendChat('Supernotes', '/w gm &{template:' + state.Supernotes.template + '}{{' + state.Supernotes.title + '=' + 'Config' + '}} {{' + state.Supernotes.theText + '=' + message + '}}');
    }
});

on('ready', () => {

    const decodeUnicode = (str) => str.replace(/%u[0-9a-fA-F]{2,4}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));

    const version = '0.0.5';
    log('Supernotes v' + version + ' is ready!  To set the template of choice or to toggle the send to players option, Use the command !gmnote --config');

    on('chat:message', function(msg) {
        if ('api' === msg.type && msg.content.match(/^!(gm|pc)note\b/) && playerIsGM(msg.playerid)) {
            let match = msg.content.match(/^!gmnote-(.*)$/);

            //define command
            let command = msg.content.split(/\s+--/)[0];

            let messagePrefix = '/w gm ';
            if (command === '!pcnote') {
                messagePrefix = '';
            }

            //define option
            let option = msg.content.split(/\s+--/)[1];

            const template = state.Supernotes.template;
            const title = state.Supernotes.title;
            const theText = state.Supernotes.theText;
            const sendToPlayers = state.Supernotes.sendToPlayers;

            if (option !== undefined && option.includes('config')) {
                let templateChoice = option.split('|')[1]

                if (templateChoice === undefined) {
                    message = 'Current sheet template:
' + state.Supernotes.sheet + '
Send to Players:
' + state.Supernotes.sendToPlayers + '

Choose a template for Supernotes to use.

[Default Template - any sheet](!gmnote --config|default)
[D&D 5th Edition by Roll20](!gmnote --config|dnd5e)
[DnD 5e Shaped](!gmnote --config|5eshaped)
[Pathfinder Community](!gmnote --config|pfcommunity)
[Pathfinder by Roll20](!gmnote --config|pfofficial)
[Pathfinder 2e by Roll20](!gmnote --config|pf2e)
[Starfinder by Roll20](!gmnote --config|starfinder)

[Toggle Send to Players](!gmnote --config|sendtoPlayers)'
                    sendChat('Supernotes', messagePrefix + '&{template:' + template + '}{{' + title + '=' + 'Config' + '}} {{' + theText + '=' + message + '}}');
                }

                switch (templateChoice) {
                    case 'default':
                        state.Supernotes.sheet = 'Default';
                        state.Supernotes.template = 'default';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = '';
                        sendChat('Supernotes', '/w gm Supernotes set to Default roll template');
                        break;
                    case 'dnd5e':
                        state.Supernotes.sheet = 'D&D 5th Edition by Roll20';
                        state.Supernotes.template = 'npcaction';
                        state.Supernotes.title = 'rname';
                        state.Supernotes.theText = 'description';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case '5eshaped':
                        state.Supernotes.sheet = 'DnD 5e Shaped';
                        state.Supernotes.template = '5e-shaped';
                        state.Supernotes.title = 'title';
                        state.Supernotes.theText = 'text_big';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pfcommunity':
                        state.Supernotes.sheet = 'Pathfinder Community';
                        state.Supernotes.template = 'pf_generic';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = 'description';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pfofficial':
                        state.Supernotes.sheet = 'Pathfinder by Roll20';
                        state.Supernotes.template = 'npc';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = 'descflag=1}} {{desc';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pf2e':
                        state.Supernotes.sheet = 'Pathefinder 2e';
                        state.Supernotes.template = 'rolls';
                        state.Supernotes.title = 'header';
                        state.Supernotes.theText = 'notes_show=[[1]]}} {{notes';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'starfinder':
                        state.Supernotes.sheet = 'Starfinder';
                        state.Supernotes.template = 'sf_generic';
                        state.Supernotes.title = 'title';
                        state.Supernotes.theText = 'buttons0';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'sendtoPlayers':
                        if (state.Supernotes.sendToPlayers) {
                            state.Supernotes.sendToPlayers = false
                        } else {
                            state.Supernotes.sendToPlayers = true
                        };
                        sendChat('Supernotes', '/w gm Send to Players set to ' + state.Supernotes.sendToPlayers);
                        break;
                }
            } else {
                if (option !== undefined && option.includes('help')) {
                    message = 'Supernotes pulls the contents from a token's GM Notes field. If the token represents a character, you can optionally pull in the Bio or GM notes from the character, as well as the avatar, or extract just the image from the bio field. The user can decide whether to whisper the notes to the GM or broadcast them to all players. Finally, there is the option to add a footer to notes whispered to the GM. This footer creates a chat button to give the option of sending the notes on to the players.
This script as written is optimized for the D&D 5th Edition by Roll20 sheet, but can be adapted easily suing the Configuration section below.

Commands:
!gmnote whispers the note to the GM
!pcnote sends the note to all players

Paramaters

--token Pulls notes from the selected token's gm notes field. This is optional. If it is missing, the script assumes --token

--charnote Pulls notes from the gm notes field of the character assigned to a token.

--bio Pulls notes from the bio field of the character assigned to a token.

--avatar Pulls the image from the avatar field of the character assigned to a token.

--image Pulls first image from the bio field of the character assigned to a token, if any exists. Otherwise returns notice that no artwork is available

--images Pulls all images from the bio field of the character assigned to a token, if any exist. Otherwise returns notice that no artwork is available

--image[number] Pulls indexed image from the bio field of the character assigned to a token, if any exist. --image1 will pull the first image, --image2 the second and so on. Otherwise returns first image if available. If no images are available, returns notice that no artwork is available.

--help Displays help.

--config Returns a configuration dialog box that allows you to set which sheet's roll template to use, and to toggle the "Send to Players" footer.

Configuration
When first installed, Supernotes is configured for the default roll template. It will display a config dialog box at startup that will allow you to choose a roll template based on your character sheet of choice, as well as the option  to toggle whether you want the "Send to Players" footer button to appear.
You will need to edit the code of the script to create a custom configuration. The pre-installed sheets are:

Default Template
D&D 5th Edition by Roll20
5e Shaped
Pathfinder by Roll20
Pathfinder Community
Pathfinder 2e by Roll20
Starfinder
';
                    sendChat('Supernotes', messagePrefix + '&{template:' + template + '}{{' + title + '=' + 'Supernotes Help' + '}} {{' + theText + '=' + message + '}}');

                } else {
                    if (!(option + '').match(/^(bio|charnote|avatar|imag(e|es|e[1-9]))/)) {
                        option = 'token';
                    }

                    let playerButton = '';
                    if (sendToPlayers && command === '!gmnote') {
                        playerButton = '\n[Send to Players](!pcnote --' + option + ')';
                    }

                    let regex;
                    if (match && match[1]) {
                        regex = new RegExp(`^${match[1]}`, 'i');
                    }

                    let message = '';
                    let whom = '';

                    if (option === 'avatar') {
                        (msg.selected || [])
                        .map(o => getObj('graphic', o._id))
                            .filter(g => undefined !== g)
                            .map(t => getObj('character', t.get('represents')))
                            .filter(c => undefined !== c)
                            .forEach(c => {
                                message = "";
                                whom = c.get('name');
                                sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');
                            });
                    } else {

                        if (option.match(/^imag(e|es|e[1-9])/)) {

                            (msg.selected || [])
                            .map(o => getObj('graphic', o._id))
                                .filter(g => undefined !== g)
                                .map(t => getObj('character', t.get('represents')))
                                .filter(c => undefined !== c)
                                .forEach(c => c.get('bio', (val) => {
                                    if (null !== val && 'null' !== val && val.length > 0) {
                                        if (regex) {
                                            message = _.filter(
                                                decodeUnicode(val).split(/(?:[\n\r]+|
)/),
                                                (l) => regex.test(l.replace(/<[^>]*>/g, ''))
                                            ).join('\r');
                                        } else {
                                            message = decodeUnicode(val);
                                        }
                                        if (option === "images") {
                                            artwork = message.match(/\/g)
                                        } else {
                                            artwork = message.match(/\/g);
                                            artwork = String(artwork);

                                            imageIndex = option.match(/\d+/g);

                                            if (isNaN(imageIndex) || !imageIndex) {
                                                imageIndex = 1
                                            }

                                            if (imageIndex > (artwork.split(",")).length) {
                                                imageIndex = 1
                                            }

                                            imageIndex = imageIndex - 1; //corrects from human readable

                                            artwork = artwork.split(",")[imageIndex];

                                        }
                                        log("no artwork? =" + artwork);
                                        if (artwork.length > 1) {
                                            message = artwork;
                                        } else {
                                            message = 'No artwork exists for this character.';
                                        }

                                        whom = c.get('name');
                                        //Sends the final message
                                        sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                                    }
                                }));
                        } else {

                            if ((option === 'bio') || (option === 'charnote')) {
                                let suboption = (option === 'charnote') ? 'gmnotes' : 'bio';

                                (msg.selected || [])
                                .map(o => getObj('graphic', o._id))
                                    .filter(g => undefined !== g)
                                    .map(t => getObj('character', t.get('represents')))
                                    .filter(c => undefined !== c)
                                    .forEach(c => c.get(suboption, (val) => {
                                        if (null !== val && 'null' !== val && val.length > 0) {
                                            if (regex) {
                                                message = _.filter(
                                                    decodeUnicode(val).split(/(?:[\n\r]+|
)/),
                                                    (l) => regex.test(l.replace(/<[^>]*>/g, ''))
                                                ).join('\r');
                                            } else {
                                                message = decodeUnicode(val);
                                            }
                                            whom = c.get('name');
                                            //Sends the final message
                                            sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                                        }
                                    }));
                            } else {
                                (msg.selected || [])
                                .map(o => getObj('graphic', o._id))
                                    .filter(g => undefined !== g)
                                    .filter((o) => o.get('gmnotes').length > 0)
                                    .forEach(o => {
                                        if (regex) {
                                            message = _.filter(decodeURIComponent(decodeUnicode(o.get('gmnotes'))).split(/(?:[\n\r]+|
)/), (l) => regex.test(l)).join('\r');
                                        } else {
                                            message = decodeURIComponent(decodeUnicode(o.get('gmnotes')));
                                        }
                                        whom = o.get('name');

                                    });
                                //Sends the final message
                                sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                            }

                            [
                                `### REPORT###`,
                                `THE MESSAGE =${message}`,
                                `command = ${command}`,
                                `option = ${option}`,
                                `messagePrefix = ${messagePrefix}`,
                                `message =${message}`
                            ].forEach(m => log(m));
                        }
                    }
                }
            }
        }
    });
});
<commit_msg>Update Supernotes.js<commit_after>on('ready', function() {
    if (!_.has(state, 'Supernotes')) {
        state.Supernotes = {
            sheet: 'Default',
            template: 'default',
            title: 'name',
            theText: '',
            sendToPlayers: true

        };
        message = 'Welcome to Supernotes! If this is your first time running it, the script is set to use the Default Roll Template. You can choose a different sheet template below, as well as decide whether you want the script to display a "Send to Players" footer at the end of every GM message. It is currently set to true.

[Default Template - any sheet](!gmnote --config|default)
[D&D 5th Edition by Roll20](!gmnote --config|dnd5e)
[DnD 5e Shaped](!gmnote --config|5eshaped)
[Pathfinder by Roll20](!gmnote --config|pfofficial)
[Pathfinder Community](!gmnote --config|pfcommunity)
[Pathfinder 2e by Roll20](!gmnote --config|pf2e)
[Starfinder by Roll20](!gmnote --config|starfinder)

[Toggle Send to Players](!gmnote --config|sendtoPlayers)';
        sendChat('Supernotes', '/w gm &{template:' + state.Supernotes.template + '}{{' + state.Supernotes.title + '=' + 'Config' + '}} {{' + state.Supernotes.theText + '=' + message + '}}');
    }
});

on('ready', () => {

    const decodeUnicode = (str) => str.replace(/%u[0-9a-fA-F]{2,4}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));

    const version = '0.0.5';
    log('Supernotes v' + version + ' is ready!  To set the template of choice or to toggle the send to players option, Use the command !gmnote --config');

    on('chat:message', function(msg) {
        if ('api' === msg.type && msg.content.match(/^!(gm|pc)note\b/) && playerIsGM(msg.playerid)) {
            let match = msg.content.match(/^!gmnote-(.*)$/);

            //define command
            let command = msg.content.split(/\s+--/)[0];

            let messagePrefix = '/w gm ';
            if (command === '!pcnote') {
                messagePrefix = '';
            }

            //define option
            let option = msg.content.split(/\s+--/)[1];

            const template = state.Supernotes.template;
            const title = state.Supernotes.title;
            const theText = state.Supernotes.theText;
            const sendToPlayers = state.Supernotes.sendToPlayers;

            if (option !== undefined && option.includes('config')) {
                let templateChoice = option.split('|')[1]

                if (templateChoice === undefined) {
                    message = 'Current sheet template:
' + state.Supernotes.sheet + '
Send to Players:
' + state.Supernotes.sendToPlayers + '

Choose a template for Supernotes to use.

[Default Template - any sheet](!gmnote --config|default)
[D&D 5th Edition by Roll20](!gmnote --config|dnd5e)
[DnD 5e Shaped](!gmnote --config|5eshaped)
[Pathfinder Community](!gmnote --config|pfcommunity)
[Pathfinder by Roll20](!gmnote --config|pfofficial)
[Pathfinder 2e by Roll20](!gmnote --config|pf2e)
[Starfinder by Roll20](!gmnote --config|starfinder)

[Toggle Send to Players](!gmnote --config|sendtoPlayers)'
                    sendChat('Supernotes', messagePrefix + '&{template:' + template + '}{{' + title + '=' + 'Config' + '}} {{' + theText + '=' + message + '}}');
                }

                switch (templateChoice) {
                    case 'default':
                        state.Supernotes.sheet = 'Default';
                        state.Supernotes.template = 'default';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = '';
                        sendChat('Supernotes', '/w gm Supernotes set to Default roll template');
                        break;
                    case 'dnd5e':
                        state.Supernotes.sheet = 'D&D 5th Edition by Roll20';
                        state.Supernotes.template = 'npcaction';
                        state.Supernotes.title = 'rname';
                        state.Supernotes.theText = 'description';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case '5eshaped':
                        state.Supernotes.sheet = 'DnD 5e Shaped';
                        state.Supernotes.template = '5e-shaped';
                        state.Supernotes.title = 'title';
                        state.Supernotes.theText = 'text_big';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pfcommunity':
                        state.Supernotes.sheet = 'Pathfinder Community';
                        state.Supernotes.template = 'pf_generic';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = 'description';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pfofficial':
                        state.Supernotes.sheet = 'Pathfinder by Roll20';
                        state.Supernotes.template = 'npc';
                        state.Supernotes.title = 'name';
                        state.Supernotes.theText = 'descflag=1}} {{desc';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'pf2e':
                        state.Supernotes.sheet = 'Pathefinder 2e';
                        state.Supernotes.template = 'rolls';
                        state.Supernotes.title = 'header';
                        state.Supernotes.theText = 'notes_show=[[1]]}} {{notes';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'starfinder':
                        state.Supernotes.sheet = 'Starfinder';
                        state.Supernotes.template = 'sf_generic';
                        state.Supernotes.title = 'title';
                        state.Supernotes.theText = 'buttons0';
                        sendChat('Supernotes', '/w gm Supernotes set to ' + state.Supernotes.sheet);
                        break;
                    case 'sendtoPlayers':
                        if (state.Supernotes.sendToPlayers) {
                            state.Supernotes.sendToPlayers = false
                        } else {
                            state.Supernotes.sendToPlayers = true
                        };
                        sendChat('Supernotes', '/w gm Send to Players set to ' + state.Supernotes.sendToPlayers);
                        break;
                }
            } else {
                if (option !== undefined && option.includes('help')) {
                    message = 'Supernotes pulls the contents from a token's GM Notes field. If the token represents a character, you can optionally pull in the Bio or GM notes from the character, as well as the avatar, or extract just the image from the bio field. The user can decide whether to whisper the notes to the GM or broadcast them to all players. Finally, there is the option to add a footer to notes whispered to the GM. This footer creates a chat button to give the option of sending the notes on to the players.
This script as written is optimized for the D&D 5th Edition by Roll20 sheet, but can be adapted easily suing the Configuration section below.

Commands:
!gmnote whispers the note to the GM
!pcnote sends the note to all players

Paramaters

--token Pulls notes from the selected token's gm notes field. This is optional. If it is missing, the script assumes --token

--charnote Pulls notes from the gm notes field of the character assigned to a token.

--bio Pulls notes from the bio field of the character assigned to a token.

--avatar Pulls the image from the avatar field of the character assigned to a token.

--image Pulls first image from the bio field of the character assigned to a token, if any exists. Otherwise returns notice that no artwork is available

--images Pulls all images from the bio field of the character assigned to a token, if any exist. Otherwise returns notice that no artwork is available

--image[number] Pulls indexed image from the bio field of the character assigned to a token, if any exist. --image1 will pull the first image, --image2 the second and so on. Otherwise returns first image if available. If no images are available, returns notice that no artwork is available.

--help Displays help.

--config Returns a configuration dialog box that allows you to set which sheet's roll template to use, and to toggle the "Send to Players" footer.

Configuration
When first installed, Supernotes is configured for the default roll template. It will display a config dialog box at startup that will allow you to choose a roll template based on your character sheet of choice, as well as the option  to toggle whether you want the "Send to Players" footer button to appear.
You will need to edit the code of the script to create a custom configuration. The pre-installed sheets are:

Default Template
D&D 5th Edition by Roll20
5e Shaped
Pathfinder by Roll20
Pathfinder Community
Pathfinder 2e by Roll20
Starfinder
';
                    sendChat('Supernotes', messagePrefix + '&{template:' + template + '}{{' + title + '=' + 'Supernotes Help' + '}} {{' + theText + '=' + message + '}}');

                } else {
                    if (!(option + '').match(/^(bio|charnote|avatar|imag(e|es|e[1-9]))/)) {
                        option = 'token';
                    }

                    let playerButton = '';
                    if (sendToPlayers && command === '!gmnote') {
                        playerButton = '\n[Send to Players](!pcnote --' + option + ')';
                    }

                    let regex;
                    if (match && match[1]) {
                        regex = new RegExp(`^${match[1]}`, 'i');
                    }

                    let message = '';
                    let whom = '';

                    if (option === 'avatar') {
                        (msg.selected || [])
                        .map(o => getObj('graphic', o._id))
                            .filter(g => undefined !== g)
                            .map(t => getObj('character', t.get('represents')))
                            .filter(c => undefined !== c)
                            .forEach(c => {
                                message = "";
                                whom = c.get('name');
                                sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');
                            });
                    } else {

                        if (option.match(/^imag(e|es|e[1-9])/)) {

                            (msg.selected || [])
                            .map(o => getObj('graphic', o._id))
                                .filter(g => undefined !== g)
                                .map(t => getObj('character', t.get('represents')))
                                .filter(c => undefined !== c)
                                .forEach(c => c.get('bio', (val) => {
                                    if (null !== val && 'null' !== val && val.length > 0) {
                                        if (regex) {
                                            message = _.filter(
                                                decodeUnicode(val).split(/(?:[\n\r]+|
)/),
                                                (l) => regex.test(l.replace(/<[^>]*>/g, ''))
                                            ).join('\r');
                                        } else {
                                            message = decodeUnicode(val);
                                        }
                                        if (option === "images") {
                                            artwork = message.match(/\/g)
                                        } else {
                                            artwork = message.match(/\/g);
                                            artwork = String(artwork);

                                            imageIndex = option.match(/\d+/g);

                                            if (isNaN(imageIndex) || !imageIndex) {
                                                imageIndex = 1
                                            }

                                            if (imageIndex > (artwork.split(",")).length) {
                                                imageIndex = 1
                                            }

                                            imageIndex = imageIndex - 1; //corrects from human readable

                                            artwork = artwork.split(",")[imageIndex];

                                        }
                                        log("artwork string =" + artwork);
                                        if ((''+artwork).length > 3) {
                                            message = artwork;
                                        } else {
                                            message = 'No artwork exists for this character.';
                                        }

                                        whom = c.get('name');
                                        //Sends the final message
                                        sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                                    }
                                }));
                        } else {

                            if ((option === 'bio') || (option === 'charnote')) {
                                let suboption = (option === 'charnote') ? 'gmnotes' : 'bio';

                                (msg.selected || [])
                                .map(o => getObj('graphic', o._id))
                                    .filter(g => undefined !== g)
                                    .map(t => getObj('character', t.get('represents')))
                                    .filter(c => undefined !== c)
                                    .forEach(c => c.get(suboption, (val) => {
                                        if (null !== val && 'null' !== val && val.length > 0) {
                                            if (regex) {
                                                message = _.filter(
                                                    decodeUnicode(val).split(/(?:[\n\r]+|
)/),
                                                    (l) => regex.test(l.replace(/<[^>]*>/g, ''))
                                                ).join('\r');
                                            } else {
                                                message = decodeUnicode(val);
                                            }
                                            whom = c.get('name');
                                            //Sends the final message
                                            sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                                        }
                                    }));
                            } else {
                                (msg.selected || [])
                                .map(o => getObj('graphic', o._id))
                                    .filter(g => undefined !== g)
                                    .filter((o) => o.get('gmnotes').length > 0)
                                    .forEach(o => {
                                        if (regex) {
                                            message = _.filter(decodeURIComponent(decodeUnicode(o.get('gmnotes'))).split(/(?:[\n\r]+|
)/), (l) => regex.test(l)).join('\r');
                                        } else {
                                            message = decodeURIComponent(decodeUnicode(o.get('gmnotes')));
                                        }
                                        whom = o.get('name');

                                    });
                                //Sends the final message
                                sendChat(whom, messagePrefix + '&{template:' + template + '}{{' + title + '=' + whom + '}} {{' + theText + '=' + message + playerButton + '}}');

                            }

                            [
                                `### REPORT###`,
                                `THE MESSAGE =${message}`,
                                `command = ${command}`,
                                `option = ${option}`,
                                `messagePrefix = ${messagePrefix}`,
                                `message =${message}`
                            ].forEach(m => log(m));
                        }
                    }
                }
            }
        }
    });
});
<|endoftext|>


<commit_before>const express = require('express');
const model = require('../../model');
const router = express.Router();
module.exports = router;

const has = (obj, ...keys) => keys.every(checkey => Object.keys(obj).some(objkey => objkey === checkey));

router.use('/article/', (req, res, next) => {
	req.articleid = '???';
	next();
});
router.get('/article/:articleid', (req, res, next) => {
	req.articleid = req.params.articleid;
	next();
});
router.get('/article/*', (req, res, next) => {
	res.status(404);
	res.end('articleid: ' + req.articleid);
});

router.use('/', (req, res, next) => {
	req.tags = [];
	next();
})

const tagMiddleware = (req, res, next) => {
	req.pagecode = 1;
	req.tags = req.params.tagraw.split(',').map(str => str.trim());
	next();
};
router.use('/tag/:tagraw', tagMiddleware);
router.use('/*/tag/:tagraw', tagMiddleware);

router.use('/:pagecode', (req, res, next) => {
	let pagecode = Number(req.params.pagecode);
	if (!isNaN(pagecode)) {
		req.pagecode = pagecode;
	}
	next();
});

let render = (req, res, next) => {
	res.end('page: ' + req.pagecode);
}
router.get('/', (req, res, next) => {
	req.pagecode = 1;
	render(req, res, next);
});
router.get('/*', (req, res, next) => {
	if (req.pagecode) {
		render(req, res, next);
	} else {
		next();
	}
})
<commit_msg>Home router<commit_after>const express = require('express');
const envir = require('../../envir');
const article = require('../../lib/article');
const router = express.Router();
module.exports = router;

const has = (obj, ...keys) => keys.every(checkey => Object.keys(obj).some(objkey => objkey === checkey));

router.use('/article/', (req, res, next) => {
	req.articleid = '???';
	next();
});
router.get('/article/:articleid', (req, res, next) => {
	req.articleid = req.params.articleid;
	next();
});
router.get('/article/*', (req, res, next) => {
	res.status(404);
	res.end('articleid: ' + req.articleid);
});

router.use('/', (req, res, next) => {
	req.tags = [];
	next();
})

const tagMiddleware = (req, res, next) => {
	req.pagecode = 1;
	req.tags = req.params.tagraw.split(',').map(str => str.trim());
	next();
};
router.use('/tag/:tagraw', tagMiddleware);
router.use('/*/tag/:tagraw', tagMiddleware);

router.use('/:pagecode', (req, res, next) => {
	let pagecode = Number(req.params.pagecode);
	if (!isNaN(pagecode)) {
		req.pagecode = pagecode;
	}
	next();
});

const render = (req, res, next) => {
	let list;
	article.getlist(req.pagecode)
		.then(listResult => {
			list = listResult;
			return article.count();
		})
		.then(count => {
		res.render('home', {
				code: 0,
				tags: req.tags,
				limit: envir.limit,
				page: req.pagecode,
				count,
				list,
			});
		}, err => {
			console.error(err);
			res.json({
				code: 2
			})
		})
		.catch(err => {
			console.error(err);
			res.json({
				code: 1
			})
		});
};

router.get('/', (req, res, next) => {
	req.pagecode = 1;
	render(req, res, next);
});
router.get('/*', (req, res, next) => {
	if (req.pagecode) {
		render(req, res, next);
	} else {
		next();
	}
});
<|endoftext|>


<commit_before>/**
 * @file entries.js
 * @author Y3G
 * @fileoverview
 * 接口文件
 */

'use strict';

var checkEntries = require('./src/check');
var check = checkEntries.check;

check.Checker = checkEntries.Checker;
check.setCheckFailureCallback = checkEntries.setCheckFailureCallback;
check.setIsExternalSourceFileCallback = checkEntries.setIsExternalSourceFileCallback;

module.exports = check;<commit_msg>refactor codes<commit_after>/**
 * @file entries.js
 * @author Y3G
 * @fileoverview
 * 接口文件
 */

'use strict';

var check = require('./src/check');
var makePolicy = require('./policy');

var exportModule = check.check;

exportModule.Checker = check.Checker;
exportModule.NotChecker = check.NotChecker;
exportModule.policy = makePolicy(check.Checker.prototype, ['not', 'owner']);

module.exports = exportModule;<|endoftext|>


<commit_before>import Reflux from 'reflux'
import parseSchema from 'components/organisms/ufadmin/utils/parseSchema'
import d3 from 'd3'

import _ from 'lodash'
import moment from 'moment'

var DataBrowserTableStore = Reflux.createStore({
  listenables: [require('actions/DataBrowserTableActions')],

  table: {
    data: null,
    schema: null,
    fields: null,
    columns: null
  },

  getInitialState: function () {
    return this.table
  },

  _format: function (value) {
    if (_.isFinite(value)) {
      var format = d3.format('n')
      if (Math.abs(value) < 1 && value !== 0) {
        format = d3.format('.4f')
      }
      return format(value)
    }
    return ''
  },

  _buildSchema: function (schema, fields) {
    schema.items.properties = fields
    return schema
  },

  _extractItemsFromData: function (datapoints) {
    return datapoints.map(item => {
      let result = _.pick(item, 'location')
      result.campaign = moment(item.campaign.start_date).format('MMM YYYY')
      result.location_id = item.location
      result.campaign_id = item.campaign
      item.indicators.forEach(indicator => {
        // result[indicator.indicator] = this._format(indicator.value) // indicator.indicator is the id
        result[indicator.indicator] = {
          value: this._format(indicator.value), // indicator.indicator is the id
          computed: indicator.computed
        }
      })
      return result
    })
  },

  _getPickValue: function (items, locations) {
    let pickValue = []
    items.forEach(item => {
      locations.forEach(location => {
        if (item.location === location.id) {
          item.location = location.name
          pickValue.push(item)
          return
        }
      })
    })
    return pickValue
  },

  onGetTableData: function (locations, indicators, datapoints) {
    console.log('datapoints', datapoints)
    let fields = {location: {title: 'Location', name: 'location'}, campaign: {title: 'Campaign', name: 'campaign'}}
    let columns = ['location', 'campaign']
    let items = this._extractItemsFromData(datapoints)
    indicators.forEach(indicator => {
      fields[indicator.id] = {title: indicator.name, name: indicator.id, 'computed': indicator.computed}
      columns.push(indicator.id)
    })
    this.table.data = this._getPickValue(items, locations)
    this.table.schema = this._buildSchema(parseSchema(datapoints), fields)
    this.table.fields = fields
    this.table.columns = columns
    this.trigger(this.table)
  }
})

export default DataBrowserTableStore
<commit_msg>remove log<commit_after>import Reflux from 'reflux'
import parseSchema from 'components/organisms/ufadmin/utils/parseSchema'
import d3 from 'd3'

import _ from 'lodash'
import moment from 'moment'

var DataBrowserTableStore = Reflux.createStore({
  listenables: [require('actions/DataBrowserTableActions')],

  table: {
    data: null,
    schema: null,
    fields: null,
    columns: null
  },

  getInitialState: function () {
    return this.table
  },

  _format: function (value) {
    if (_.isFinite(value)) {
      var format = d3.format('n')
      if (Math.abs(value) < 1 && value !== 0) {
        format = d3.format('.4f')
      }
      return format(value)
    }
    return ''
  },

  _buildSchema: function (schema, fields) {
    schema.items.properties = fields
    return schema
  },

  _extractItemsFromData: function (datapoints) {
    return datapoints.map(item => {
      let result = _.pick(item, 'location')
      result.campaign = moment(item.campaign.start_date).format('MMM YYYY')
      result.location_id = item.location
      result.campaign_id = item.campaign
      item.indicators.forEach(indicator => {
        // result[indicator.indicator] = this._format(indicator.value) // indicator.indicator is the id
        result[indicator.indicator] = {
          value: this._format(indicator.value), // indicator.indicator is the id
          computed: indicator.computed
        }
      })
      return result
    })
  },

  _getPickValue: function (items, locations) {
    let pickValue = []
    items.forEach(item => {
      locations.forEach(location => {
        if (item.location === location.id) {
          item.location = location.name
          pickValue.push(item)
          return
        }
      })
    })
    return pickValue
  },

  onGetTableData: function (locations, indicators, datapoints) {
    let fields = {location: {title: 'Location', name: 'location'}, campaign: {title: 'Campaign', name: 'campaign'}}
    let columns = ['location', 'campaign']
    let items = this._extractItemsFromData(datapoints)
    indicators.forEach(indicator => {
      fields[indicator.id] = {title: indicator.name, name: indicator.id, 'computed': indicator.computed}
      columns.push(indicator.id)
    })
    this.table.data = this._getPickValue(items, locations)
    this.table.schema = this._buildSchema(parseSchema(datapoints), fields)
    this.table.fields = fields
    this.table.columns = columns
    this.trigger(this.table)
  }
})

export default DataBrowserTableStore
<|endoftext|>


<commit_before>import { assert } from 'chai';
import { TimeWarp, listFixtures, loadFixture, loadPayload } from './utils';

import { scrape, scrapeUserData } from '../src/scrape';

describe('Scrape', function () {
    const fixtures = listFixtures('posts/');
    const timeWarp = new TimeWarp();

    afterEach(() => {
        timeWarp.reset();
    });

    fixtures.forEach((path) => {
        const fixture = loadFixture(path);
        const payload = loadPayload(path);

        it(`parses fixture "${path}"`, function () {
            timeWarp.set(2016, 5, 6, 15, 0, 10, -120);
            assert.deepEqual(scrape(fixture), payload);
        });
    });

    it('ignores a post that contains another post', function () {
        timeWarp.set(2016, 5, 6, 15, 0, 10, -120);

        const post0 = loadFixture('posts/postCommentedByFriend').find('.userContentWrapper');
        const post1 = loadFixture('posts/sponsoredPostLikedByFriends').find('.userContentWrapper');

        assert.equal(scrape(post0), null);
        assert.equal(scrape(post1), null);
    });

    it('parses basic user info', function () {
        const userInfo = loadFixture('basicInfo');
        assert.deepEqual(scrapeUserData(userInfo), {
            href: 'https://www.facebook.com/giangigino',
            id: '123456789'
        });
    });
});
<commit_msg>Use payload for basic user info test<commit_after>import { assert } from 'chai';
import { TimeWarp, listFixtures, loadFixture, loadPayload } from './utils';

import { scrape, scrapeUserData } from '../src/scrape';

describe('Scrape', function () {
    const fixtures = listFixtures('posts/');
    const timeWarp = new TimeWarp();

    afterEach(() => {
        timeWarp.reset();
    });

    fixtures.forEach((path) => {
        const fixture = loadFixture(path);
        const payload = loadPayload(path);

        it(`parses fixture "${path}"`, function () {
            timeWarp.set(2016, 5, 6, 15, 0, 10, -120);
            assert.deepEqual(scrape(fixture), payload);
        });
    });

    it('ignores a post that contains another post', function () {
        timeWarp.set(2016, 5, 6, 15, 0, 10, -120);

        const post0 = loadFixture('posts/postCommentedByFriend').find('.userContentWrapper');
        const post1 = loadFixture('posts/sponsoredPostLikedByFriends').find('.userContentWrapper');

        assert.equal(scrape(post0), null);
        assert.equal(scrape(post1), null);
    });

    it('parses basic user info', function () {
        const userInfo = loadFixture('basicInfo');
        const payload = loadPayload('basicInfo');

        assert.deepEqual(scrapeUserData(userInfo), payload);
    });
});
<|endoftext|>


<commit_before>var _ = require('underscore'),
    bower = require('bower'),
    childProcess = require('child_process'),
    fs = require('fs'),
    request = require('request'),
    util = require('util'),
    semver = require('semver'),
    yeoman = require('yeoman-generator');

var CollectVersions = module.exports = function CollectVersions(args, options, config) {

  yeoman.generators.Base.apply(this, arguments);

  this.argument('type', {desc: 'Version type. May be one of {scotsman, bower, hapi, npm}', required: true});

  if (this.type === 'scotsman' || this.type === 'hapi') {
    this.argument('url', {desc: 'Remote version list url', required: true});
  } else if (this.type !== 'bower' && this.type !== 'npm') {
    throw new Error('First argument must be one of {scotsman, bower, hapi, npm}');
  }
};

util.inherits(CollectVersions, yeoman.generators.Base);

CollectVersions.prototype.exec = function() {
  if (this.type === 'scotsman') {
    this._scotsman();
  } else if (this.type === 'hapi') {
    this._hapi();
  } else if (this.type === 'bower') {
    this._bowerLS();
  } else if (this.type === 'npm') {
    this._npmLS();
  }
};

CollectVersions.prototype._scotsman = function() {
  var done = this.async(),
      self = this;

  function process(err, response, body) {
    if (err) {
      throw new Error('Failed to load scotsman url "' + self.url + '" ' + err);
    }

    var ls = (/<pre>((?:\n|.)*?)<\/pre>/.exec(body) || [])[1];
    self.versions = parseVersions(ls);
    self.versions[0].bowerRoot = true;

    done();
  }

  if (this.url.indexOf('//') >= 0) {
    request(this.url, process);
  } else {
    fs.readFile(this.url, function(err, data) {
      process(err, undefined, data.toString());
    });
  }
};

CollectVersions.prototype._bowerLS = function() {
  var done = this.async(),
      self = this;
  childProcess.exec('bower ls --offline', function(err, stdout, stderr) {
    self.versions = parseVersions(stdout);
    self.versions[0].bowerRoot = true;

    done();
  });
};

CollectVersions.prototype._hapi = function() {
  var done = this.async(),
      self = this;
  request(this.url, function(err, response, body) {
    if (err) {
      throw err;
    }

    var versions = JSON.parse(body);
    self.versions = versions;

    done();
  });
};

CollectVersions.prototype._npmLS = function() {
  var done = this.async(),
      self = this;
  childProcess.exec('npm ls --json', function(err, stdout, stderr) {
    var versions = [];

    function deepPluck(name, obj, root) {
      versions.push({
        name: name,
        version: obj.version
      });

      if (obj.dependencies) {
        _.each(obj.dependencies, function(info, child) {
          return deepPluck((root ? '' : name + '/') + child, info);
        });
      }
    }

    var list = JSON.parse(stdout);
    deepPluck(list.name, list, true);
    versions[0].npmRoot = true;

    self.versions = versions;

    done();
  });

};

CollectVersions.prototype.output = function() {
  if (!this.quiet) {
    console.log(JSON.stringify(this.versions, undefined, 2));
  }
};

function parseVersions(ls) {
  /*jshint boss:true */
  var versions = [],
      matcher = /(\S+)#(\S+)/g,
      match;
  while (match = matcher.exec(ls)) {
    versions.push({
      name: match[1],
      version: match[2]
    });
  }
  return versions;
}
<commit_msg>Drop hapi version collector<commit_after>var _ = require('underscore'),
    bower = require('bower'),
    childProcess = require('child_process'),
    fs = require('fs'),
    request = require('request'),
    util = require('util'),
    semver = require('semver'),
    yeoman = require('yeoman-generator');

var CollectVersions = module.exports = function CollectVersions(args, options, config) {

  yeoman.generators.Base.apply(this, arguments);

  this.argument('type', {desc: 'Version type. May be one of {scotsman, bower, hapi, npm}', required: true});

  if (this.type === 'scotsman' || this.type === 'hapi') {
    this.argument('url', {desc: 'Remote version list url', required: true});
  } else if (this.type !== 'bower' && this.type !== 'npm') {
    throw new Error('First argument must be one of {scotsman, bower, hapi, npm}');
  }
};

util.inherits(CollectVersions, yeoman.generators.Base);

CollectVersions.prototype.exec = function() {
  if (this.type === 'scotsman') {
    this._scotsman();
  } else if (this.type === 'hapi') {
    this._hapi();
  } else if (this.type === 'bower') {
    this._bowerLS();
  } else if (this.type === 'npm') {
    this._npmLS();
  }
};

CollectVersions.prototype._scotsman = function() {
  var done = this.async(),
      self = this;

  function process(err, response, body) {
    if (err) {
      throw new Error('Failed to load scotsman url "' + self.url + '" ' + err);
    }

    var ls = (/<pre>((?:\n|.)*?)<\/pre>/.exec(body) || [])[1];
    self.versions = parseVersions(ls);
    self.versions[0].bowerRoot = true;

    done();
  }

  if (this.url.indexOf('//') >= 0) {
    request(this.url, process);
  } else {
    fs.readFile(this.url, function(err, data) {
      process(err, undefined, data.toString());
    });
  }
};

CollectVersions.prototype._bowerLS = function() {
  var done = this.async(),
      self = this;
  childProcess.exec('bower ls --offline', function(err, stdout, stderr) {
    self.versions = parseVersions(stdout);
    self.versions[0].bowerRoot = true;

    done();
  });
};

CollectVersions.prototype._npmLS = function() {
  var done = this.async(),
      self = this;
  childProcess.exec('npm ls --json', function(err, stdout, stderr) {
    var versions = [];

    function deepPluck(name, obj, root) {
      versions.push({
        name: name,
        version: obj.version
      });

      if (obj.dependencies) {
        _.each(obj.dependencies, function(info, child) {
          return deepPluck((root ? '' : name + '/') + child, info);
        });
      }
    }

    var list = JSON.parse(stdout);
    deepPluck(list.name, list, true);
    versions[0].npmRoot = true;

    self.versions = versions;

    done();
  });

};

CollectVersions.prototype.output = function() {
  if (!this.quiet) {
    console.log(JSON.stringify(this.versions, undefined, 2));
  }
};

function parseVersions(ls) {
  /*jshint boss:true */
  var versions = [],
      matcher = /(\S+)#(\S+)/g,
      match;
  while (match = matcher.exec(ls)) {
    versions.push({
      name: match[1],
      version: match[2]
    });
  }
  return versions;
}
<|endoftext|>


<commit_before>var irc = require('irc');

var sockets = new Array();

var client = new irc.Client('chat.freenode.net', 'nodedc_bot', {
        channels: ['#node.dc'],
});

var lastMessages = [];

client.addListener('message#node.dc', function (from, message) {
    onsole.log("message from irc: " + from + ' => #node.dc: ' + message);
    var message = {time:new Date(), message:message, user:from};
	lastMessages.push(message);
	if(lastMessages.length > 24){
		lastMessages.shift();
	}
    sockets.forEach(function(socket){
    	socket.emit('message', message);
    });
});

client.addListener('error', function(message) {
    console.log('error: ', message);
});

module.exports = function(server){

	server.sockets.on('connection', function(socket) {
		console.log('got socket connection: ');
		var index = sockets.push(socket);
		socket.on('close', function () {
			sockets.splice(index, 1);
		});
    	socket.emit('init', {lastMessages:lastMessages});
	});

}

<commit_msg>Fix Reference Error<commit_after>var irc = require('irc');

var sockets = new Array();

var client = new irc.Client('chat.freenode.net', 'nodedc_bot', {
        channels: ['#node.dc'],
});

var lastMessages = [];

client.addListener('message#node.dc', function (from, message) {
    console.log("message from irc: " + from + ' => #node.dc: ' + message);
    var message = {time:new Date(), message:message, user:from};
	lastMessages.push(message);
	if(lastMessages.length > 24){
		lastMessages.shift();
	}
    sockets.forEach(function(socket){
    	socket.emit('message', message);
    });
});

client.addListener('error', function(message) {
    console.log('error: ', message);
});

module.exports = function(server){

	server.sockets.on('connection', function(socket) {
		console.log('got socket connection: ');
		var index = sockets.push(socket);
		socket.on('close', function () {
			sockets.splice(index, 1);
		});
    	socket.emit('init', {lastMessages:lastMessages});
	});

}

<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

var utils = require('../../utils');

var models = require('../../models');
var User = models.User;

router.all('/register', utils.require_login);
router.get('/register', function(req, res, next) {
  res.render('auth/register', { origin: req.query.origin });
});

router.post('/register', function(req, res, next) {
  var fullname = req.body.fullname.trim();
  var origin = req.body.origin;
  if(origin == null || origin[0] != '/') {
    origin = '/';
  };

  if(fullname == '') {
    res.redirect(302, '/auth/register?origin=' + origin);
    return;
  }

  User.find({
    where: {
      email: req.session.currentUser
    }
  }).catch(function(error) {
      res.status(500).send('Error retrieving user object');
  }).then(function(user) {
    if(!user) {
      // Create the user
      var user = User.create({
        email: req.session.currentUser,
        name: fullname
      }).then(function(user) {
        res.redirect(origin);
      });
    } else {
      // The user already existed...
      res.redirect(302, '/');
    }
  });

});

module.exports = router;
<commit_msg>Fix auth global import paths<commit_after>var express = require('express');
var router = express.Router();

var utils = require('../utils');

var models = require('../models');
var User = models.User;

router.all('/register', utils.require_login);
router.get('/register', function(req, res, next) {
  res.render('auth/register', { origin: req.query.origin });
});

router.post('/register', function(req, res, next) {
  var fullname = req.body.fullname.trim();
  var origin = req.body.origin;
  if(origin == null || origin[0] != '/') {
    origin = '/';
  };

  if(fullname == '') {
    res.redirect(302, '/auth/register?origin=' + origin);
    return;
  }

  User.find({
    where: {
      email: req.session.currentUser
    }
  }).catch(function(error) {
      res.status(500).send('Error retrieving user object');
  }).then(function(user) {
    if(!user) {
      // Create the user
      var user = User.create({
        email: req.session.currentUser,
        name: fullname
      }).then(function(user) {
        res.redirect(origin);
      });
    } else {
      // The user already existed...
      res.redirect(302, '/');
    }
  });

});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';
var extend = require('xtend');
var util = require('util');
var when = require('when');
var pipeline = require('when/pipeline');
var _ = require('lodash');
var chalk = require('chalk');
var inquirer = require('inquirer');
var prompt = inquirer.prompt;
var dfu = require('../oldlib/dfu.js');
var BaseCommand = require('./BaseCommand');

var EarlyReturnError = function () {
};
var SkipStepError = function () {
};

var DoctorCommand = function (cli, options) {
	DoctorCommand.super_.call(this, cli, options);
	this.options = extend({}, this.options, options);

	this.init();
};
util.inherits(DoctorCommand, BaseCommand);
DoctorCommand.prototype = extend(BaseCommand.prototype, {
	options: null,
	name: 'doctor',
	description: 'Puts your device back into a healthy state.',
	deviceTimeout: 3000,
	serialTimeout: 1000,

	init: function () {
		this.addOption('*', this.deviceDoctor.bind(this), 'Puts your device back into a healthy state');
	},

	deviceDoctor: function () {
		return pipeline([
			this._showDoctorWelcome.bind(this),
			this._findDevice.bind(this),
			this._nameDevice.bind(this),
			this._updateSystemFirmware.bind(this),
			this._updateCC3000.bind(this),
			this._flashDoctor.bind(this),
			this._selectAntenna.bind(this),
			this._selectIP.bind(this),
			this._resetSoftAPPrefix.bind(this),
			this._clearEEPROM.bind(this),
			this._setupWiFi.bind(this),
			this._resetKeys.bind(this),
			this._flashTinker.bind(this),
			this._showDoctorGoodbye.bind(this)
		]).catch(this._showDoctorError.bind(this));
	},

	_showDoctorWelcome: function() {
		console.log(chalk.bold.white('The Device Doctor will put your device back into a healthy state'));
		console.log('It will:');
		_.map([
			'Upgrade system firmware',
			'Flash the default Tinker app',
			'Reset the device and server keys',
			'Clear the Wi-Fi settings',
		], function (line) {
			console.log('  - ' + line);
		});
	},

	_findDevice: function() {
		var serialCommand = this.cli.getCommandModule('serial');
		return when.promise(function (resolve) {
			// Try to find a "normal" mode device through the serial port
			serialCommand.findDevices(resolve);
		}).then(function (devices) {
			if (devices.length === 0) {
				// Try to find a "DFU" mode device through dfu-util
				return dfu.listDFUDevices();
			} else {
				return devices;
			}
		}).then(function (devices) {
			this.device = devices && devices[0];
			return devices;
		}.bind(this));
	},

	_nameDevice: function(devices) {
		if (devices.length === 0) {
			console.log('');
			console.log(chalk.cyan('>'), 'Connect a Particle device to a USB port and run the command again.');
			throw new EarlyReturnError();
		}

		if (devices.length > 1) {
			console.log('');
			console.log(chalk.cyan('!'), 'You have ' + devices.length + ' devices connected to USB ports.');
			console.log(chalk.cyan('!'), 'To avoid confusion, disconnect all but the one device and run the command again.');
			throw new EarlyReturnError();
		}

		var deviceName = this.device.type || 'Device';
		console.log('');
		console.log('The Doctor will operate on your ' + deviceName + ' connected over USB');
	},

	_enterDfuMode: function() {
		console.log('Put the device in ' + chalk.bold.yellow('DFU mode'));
		console.log('Tap ' + chalk.bold.cyan('RESET/RST') + ' while holding ' + chalk.bold.cyan('MODE/SETUP') +
			' until the device blinks ' + chalk.bold.yellow('yellow.'));
		return this._promptReady();
	},

	_promptReady: function() {
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select Continue when ready',
			choices: ['Continue', 'Skip step', 'Exit']
		}]).then(function (ans) {
			switch (ans.choice) {
				case 'Skip step':
					throw new SkipStepError();
				case 'Exit':
					throw new EarlyReturnError();
				default:
					return;
			}
		});
	},

	_catchSkipStep: function(e) {
		if (e instanceof SkipStepError) {
			return;
		} else {
			throw e;
		}
	},

	_displayStepTitle: function(title) {
		console.log(chalk.bold.white('\n' + title + '\n'));
	},

	_updateSystemFirmware: function() {
		if (!this._deviceHasFeature('system-firmware')) {
			return;
		}

		this._displayStepTitle('Updating system firmware');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('update');
			}.bind(this)).catch(this._catchSkipStep);
	},

	_updateCC3000: function() {
		if (!this._deviceHasFeature('cc3000')) {
			return;
		}

		this._displayStepTitle('Updating CC3000 firmware');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'cc3000']);
			}.bind(this)).then(function() {
				console.log('Applying update...');
				console.log('Wait until the device stops blinking ' + chalk.bold.magenta('magenta') + ' and starts blinking ' + chalk.bold.yellow('yellow'));
				return this.promptDfd(chalk.cyan('>') + ' Press ENTER when ready');
			}.bind(this)).catch(this._catchSkipStep);
	},

	_flashDoctor: function() {
		this._displayStepTitle('Flashing the Device Doctor app');
		console.log('This app allows changing more settings on your device\n');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'doctor']);
			}.bind(this))
			.then(this._waitForSerialDevice.bind(this, this.deviceTimeout))
			.then(function (device) {
				if (!device) {
					throw new Error('Could not find serial device. Ensure the Device Doctor app was flashed');
				}
			}).catch(this._catchSkipStep);
	},

	_selectAntenna: function() {
		if (!this._deviceHasFeature('antenna-selection')) {
			return;
		}

		this._displayStepTitle('Select antenna');
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select the antenna to use to connect to Wi-Fi',
			choices: ['Internal', 'External', 'Skip step', 'Exit']
		}])
			.then(function (ans) {
				switch (ans.choice) {
					case 'Skip step':
						throw new SkipStepError();
					case 'Exit':
						throw new EarlyReturnError();
					default:
						return ans.choice;
				}
			}).then(function (antenna) {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorAntenna(this.device, antenna, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_selectIP: function() {
		if (!this._deviceHasFeature('wifi')) {
			return;
		}

		this._displayStepTitle('Configure IP address');
		var mode;
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select how the device will be assigned an IP address',
			choices: ['Dynamic IP', 'Static IP', 'Skip step', 'Exit']
		}])
			.then(function (ans) {
				switch (ans.choice) {
					case 'Skip step':
						throw new SkipStepError();
					case 'Exit':
						throw new EarlyReturnError();
					default:
						mode = ans.choice;
				}
			}).then(function () {
				if (mode === 'Static IP') {
					return this._promptIPAddresses({
						device_ip: 'Device IP',
						netmask: 'Netmask',
						gateway: 'Gateway',
						dns: 'DNS'
					});
				}
			}.bind(this)).then(function (ipAddresses) {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorIP(this.device, mode, ipAddresses, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_promptIPAddresses: function(ips) {
		return prompt(_.map(ips, function (label, key) {
			return {
				type: 'input',
				name: key,
				message: label,
				validate: function (val) {
					var parts = val.split('.');
					var allNumbers = _.every(parts, function (n) { return (+n).toString() === n });
					return parts.length === 4 && allNumbers;
				}
			}
		})).then(function (ans) {
			return _.mapValues(ips, function (label, key) {
				return ans[key];
			});
		});
	},

	_resetSoftAPPrefix: function() {
		if (!this._deviceHasFeature('softap')) {
			return;
		}

		this._displayStepTitle('Reset Wi-Fi hotspot name in listening mode');

		return this._promptReady()
			.then(function () {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorSoftAPPrefix(this.device, '', this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_clearEEPROM: function() {
		this._displayStepTitle('Clear all data in EEPROM storage');

		return this._promptReady()
			.then(function () {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorClearEEPROM(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_flashTinker: function() {
		this._displayStepTitle('Flashing the default Particle Tinker app');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'tinker']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_resetKeys: function() {
		this._displayStepTitle('Reseting server and device keys');

		// do this again to refresh the device data with latest firmware
		return this._waitForSerialDevice(this.deviceTimeout)
			.then(this._enterDfuMode.bind(this))
			.then(function() {
				return this.cli.runCommand('keys', ['server']);
				// keys servers doesn't cause the device to reset so it is still in DFU mode
			}.bind(this))
			.then(function () {
				if (!this.device || !this.device.deviceId) {
					console.log(chalk.red('!'), 'Skipping device key because it does not report its device ID over USB');
					return;
				}
				return this.cli.runCommand('keys', ['doctor', this.device.deviceId, '--force']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_waitForSerialDevice: function(timeout) {
		var timeoutReached = false;
		when().delay(timeout).then(function() {
			timeoutReached = true;
		});

		var tryFindDevice = function() {
			return this._findDevice().then(function () {
				if (this.device && this.device.port) {
					return this.device;
				} else if (timeoutReached) {
					return null;
				} else {
					return when().delay(250).then(tryFindDevice);
				}
			}.bind(this));
		}.bind(this);

		return tryFindDevice();
	},

	_setupWiFi: function() {
		if (!this._deviceHasFeature('wifi')) {
			return;
		}
		var serialCommand = this.cli.getCommandModule('serial');

		this._displayStepTitle('Clearing and setting up Wi-Fi settings');
		return this._promptReady()
			.then(function () {
				return serialCommand.sendDoctorClearWiFi(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).then(function () {
				return serialCommand.sendDoctorListenMode(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).then(function() {
				return this.cli.runCommand('serial', ['wifi']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_deviceHasFeature: function(feature) {
		var features = (this.device && this.device.specs && this.device.specs.features) || [];
		return _.includes(features, feature);
	},

	_showDoctorGoodbye: function() {
		this._displayStepTitle('The Doctor has restored your device!');
		console.log(chalk.cyan('>'), "Please visit our community forums if your device still can't connect to the Particle cloud");
		console.log(chalk.bold.white('https://community.particle.io/'));
	},

	_showDoctorError: function(e) {
		if (e instanceof EarlyReturnError) {
			return;
		}
		console.log("The Doctor didn't complete sucesfully. " + e.message);
		console.log(chalk.cyan('>'), 'Please visit our community forums for help with this error:');
		console.log(chalk.bold.white('https://community.particle.io/'));
	},
});

module.exports = DoctorCommand;
<commit_msg>Warn before resetting keys for a device you do not own<commit_after>'use strict';
var extend = require('xtend');
var util = require('util');
var when = require('when');
var pipeline = require('when/pipeline');
var _ = require('lodash');
var chalk = require('chalk');
var inquirer = require('inquirer');
var prompt = inquirer.prompt;
var dfu = require('../oldlib/dfu.js');
var ApiClient = require('../oldlib/ApiClient.js');
var BaseCommand = require('./BaseCommand');

var EarlyReturnError = function () {
};
var SkipStepError = function () {
};

var DoctorCommand = function (cli, options) {
	DoctorCommand.super_.call(this, cli, options);
	this.options = extend({}, this.options, options);

	this.init();
};
util.inherits(DoctorCommand, BaseCommand);
DoctorCommand.prototype = extend(BaseCommand.prototype, {
	options: null,
	name: 'doctor',
	description: 'Puts your device back into a healthy state.',
	deviceTimeout: 3000,
	serialTimeout: 1000,

	init: function () {
		this.addOption('*', this.deviceDoctor.bind(this), 'Puts your device back into a healthy state');
	},

	deviceDoctor: function () {
		return pipeline([
			this._showDoctorWelcome.bind(this),
			this._setupApi.bind(this),
			this._findDevice.bind(this),
			this._nameDevice.bind(this),
			this._updateSystemFirmware.bind(this),
			this._updateCC3000.bind(this),
			this._flashDoctor.bind(this),
			this._selectAntenna.bind(this),
			this._selectIP.bind(this),
			this._resetSoftAPPrefix.bind(this),
			this._clearEEPROM.bind(this),
			this._setupWiFi.bind(this),
			this._resetKeys.bind(this),
			this._flashTinker.bind(this),
			this._showDoctorGoodbye.bind(this)
		]).catch(this._showDoctorError.bind(this));
	},

	_showDoctorWelcome: function() {
		console.log(chalk.bold.white('The Device Doctor will put your device back into a healthy state'));
		console.log('It will:');
		_.map([
			'Upgrade system firmware',
			'Flash the default Tinker app',
			'Reset the device and server keys',
			'Clear the Wi-Fi settings',
		], function (line) {
			console.log('  - ' + line);
		});
	},

	_setupApi: function() {
		this.api = new ApiClient();
		if (!this.api.ready()) {
			throw new EarlyReturnError();
		}
	},

	_findDevice: function() {
		var serialCommand = this.cli.getCommandModule('serial');
		return when.promise(function (resolve) {
			// Try to find a "normal" mode device through the serial port
			serialCommand.findDevices(resolve);
		}).then(function (devices) {
			if (devices.length === 0) {
				// Try to find a "DFU" mode device through dfu-util
				return dfu.listDFUDevices();
			} else {
				return devices;
			}
		}).then(function (devices) {
			this.device = devices && devices[0];
			return devices;
		}.bind(this));
	},

	_nameDevice: function(devices) {
		if (devices.length === 0) {
			console.log('');
			console.log(chalk.cyan('>'), 'Connect a Particle device to a USB port and run the command again.');
			throw new EarlyReturnError();
		}

		if (devices.length > 1) {
			console.log('');
			console.log(chalk.cyan('!'), 'You have ' + devices.length + ' devices connected to USB ports.');
			console.log(chalk.cyan('!'), 'To avoid confusion, disconnect all but the one device and run the command again.');
			throw new EarlyReturnError();
		}

		var deviceName = this.device.type || 'Device';
		console.log('');
		console.log('The Doctor will operate on your ' + deviceName + ' connected over USB');
	},

	_enterDfuMode: function() {
		console.log('Put the device in ' + chalk.bold.yellow('DFU mode'));
		console.log('Tap ' + chalk.bold.cyan('RESET/RST') + ' while holding ' + chalk.bold.cyan('MODE/SETUP') +
			' until the device blinks ' + chalk.bold.yellow('yellow.'));
		return this._promptReady();
	},

	_promptReady: function() {
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select Continue when ready',
			choices: ['Continue', 'Skip step', 'Exit']
		}]).then(function (ans) {
			switch (ans.choice) {
				case 'Skip step':
					throw new SkipStepError();
				case 'Exit':
					throw new EarlyReturnError();
				default:
					return;
			}
		});
	},

	_catchSkipStep: function(e) {
		if (e instanceof SkipStepError) {
			return;
		} else {
			throw e;
		}
	},

	_displayStepTitle: function(title) {
		console.log(chalk.bold.white('\n' + title + '\n'));
	},

	_updateSystemFirmware: function() {
		if (!this._deviceHasFeature('system-firmware')) {
			return;
		}

		this._displayStepTitle('Updating system firmware');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('update');
			}.bind(this)).catch(this._catchSkipStep);
	},

	_updateCC3000: function() {
		if (!this._deviceHasFeature('cc3000')) {
			return;
		}

		this._displayStepTitle('Updating CC3000 firmware');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'cc3000']);
			}.bind(this)).then(function() {
				console.log('Applying update...');
				console.log('Wait until the device stops blinking ' + chalk.bold.magenta('magenta') + ' and starts blinking ' + chalk.bold.yellow('yellow'));
				return this.promptDfd(chalk.cyan('>') + ' Press ENTER when ready');
			}.bind(this)).catch(this._catchSkipStep);
	},

	_flashDoctor: function() {
		this._displayStepTitle('Flashing the Device Doctor app');
		console.log('This app allows changing more settings on your device\n');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'doctor']);
			}.bind(this))
			.then(this._waitForSerialDevice.bind(this, this.deviceTimeout))
			.then(function (device) {
				if (!device) {
					throw new Error('Could not find serial device. Ensure the Device Doctor app was flashed');
				}
			}).catch(this._catchSkipStep);
	},

	_selectAntenna: function() {
		if (!this._deviceHasFeature('antenna-selection')) {
			return;
		}

		this._displayStepTitle('Select antenna');
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select the antenna to use to connect to Wi-Fi',
			choices: ['Internal', 'External', 'Skip step', 'Exit']
		}])
			.then(function (ans) {
				switch (ans.choice) {
					case 'Skip step':
						throw new SkipStepError();
					case 'Exit':
						throw new EarlyReturnError();
					default:
						return ans.choice;
				}
			}).then(function (antenna) {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorAntenna(this.device, antenna, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_selectIP: function() {
		if (!this._deviceHasFeature('wifi')) {
			return;
		}

		this._displayStepTitle('Configure IP address');
		var mode;
		return prompt([{
			type: 'list',
			name: 'choice',
			message: 'Select how the device will be assigned an IP address',
			choices: ['Dynamic IP', 'Static IP', 'Skip step', 'Exit']
		}])
			.then(function (ans) {
				switch (ans.choice) {
					case 'Skip step':
						throw new SkipStepError();
					case 'Exit':
						throw new EarlyReturnError();
					default:
						mode = ans.choice;
				}
			}).then(function () {
				if (mode === 'Static IP') {
					return this._promptIPAddresses({
						device_ip: 'Device IP',
						netmask: 'Netmask',
						gateway: 'Gateway',
						dns: 'DNS'
					});
				}
			}.bind(this)).then(function (ipAddresses) {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorIP(this.device, mode, ipAddresses, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_promptIPAddresses: function(ips) {
		return prompt(_.map(ips, function (label, key) {
			return {
				type: 'input',
				name: key,
				message: label,
				validate: function (val) {
					var parts = val.split('.');
					var allNumbers = _.every(parts, function (n) { return (+n).toString() === n });
					return parts.length === 4 && allNumbers;
				}
			}
		})).then(function (ans) {
			return _.mapValues(ips, function (label, key) {
				return ans[key];
			});
		});
	},

	_resetSoftAPPrefix: function() {
		if (!this._deviceHasFeature('softap')) {
			return;
		}

		this._displayStepTitle('Reset Wi-Fi hotspot name in listening mode');

		return this._promptReady()
			.then(function () {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorSoftAPPrefix(this.device, '', this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_clearEEPROM: function() {
		this._displayStepTitle('Clear all data in EEPROM storage');

		return this._promptReady()
			.then(function () {
				var serialCommand = this.cli.getCommandModule('serial');
				return serialCommand.sendDoctorClearEEPROM(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).catch(this._catchSkipStep);
	},

	_flashTinker: function() {
		this._displayStepTitle('Flashing the default Particle Tinker app');
		return this._enterDfuMode()
			.then(function() {
				return this.cli.runCommand('flash', ['--usb', 'tinker']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_resetKeys: function() {
		this._displayStepTitle('Reseting server and device keys');

		// do this again to refresh the device data with latest firmware
		return this._waitForSerialDevice(this.deviceTimeout)
			.then(this._verifyDeviceOwnership.bind(this))
			.then(this._enterDfuMode.bind(this))
			.then(function() {
				return this.cli.runCommand('keys', ['server']);
				// keys servers doesn't cause the device to reset so it is still in DFU mode
			}.bind(this))
			.then(function () {
				if (!this.device || !this.device.deviceId) {
					console.log(chalk.red('!'), 'Skipping device key because it does not report its device ID over USB');
					return;
				}
				return this.cli.runCommand('keys', ['doctor', this.device.deviceId, '--force']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_waitForSerialDevice: function(timeout) {
		var timeoutReached = false;
		when().delay(timeout).then(function() {
			timeoutReached = true;
		});

		var tryFindDevice = function() {
			return this._findDevice().then(function () {
				if (this.device && this.device.port) {
					return this.device;
				} else if (timeoutReached) {
					return null;
				} else {
					return when().delay(250).then(tryFindDevice);
				}
			}.bind(this));
		}.bind(this);

		return tryFindDevice();
	},

	_verifyDeviceOwnership: function() {
		return when().then(function() {
			if (!this.device || !this.device.deviceId) {
				return false;
			}

			return this.api.getAttributes(this.device.deviceId).then(function (attributes) {
				if (attributes.error === 'Permission Denied') {
					return false;
				}
				return true;
			}, function (error) {
				return false;
			});
		}.bind(this)).then(function (ownsDevice) {
			if (ownsDevice) {
				return;
			}
			console.log(chalk.red('!'), "This device is not claimed to your Particle account.");
			console.log(chalk.red('!'), 'Resetting keys for a device you do not own may permanently prevent it from connecting to the Particle cloud.');
			return prompt([{
				type: 'confirm',
				name: 'choice',
				message: 'Skip resetting keys?',
				default: true
			}]).then(function (ans) {
				if (ans.choice) {
					throw new SkipStepError();
				}
			});
		});
	},

	_setupWiFi: function() {
		if (!this._deviceHasFeature('wifi')) {
			return;
		}
		var serialCommand = this.cli.getCommandModule('serial');

		this._displayStepTitle('Clearing and setting up Wi-Fi settings');
		return this._promptReady()
			.then(function () {
				return serialCommand.sendDoctorClearWiFi(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).then(function () {
				return serialCommand.sendDoctorListenMode(this.device, this.serialTimeout);
			}.bind(this)).then(function (message) {
				console.log(message);
			}).then(function() {
				return this.cli.runCommand('serial', ['wifi']);
			}.bind(this)).catch(this._catchSkipStep);
	},

	_deviceHasFeature: function(feature) {
		var features = (this.device && this.device.specs && this.device.specs.features) || [];
		return _.includes(features, feature);
	},

	_showDoctorGoodbye: function() {
		this._displayStepTitle('The Doctor has restored your device!');
		console.log(chalk.cyan('>'), "Please visit our community forums if your device still can't connect to the Particle cloud");
		console.log(chalk.bold.white('https://community.particle.io/'));
	},

	_showDoctorError: function(e) {
		if (e instanceof EarlyReturnError) {
			return;
		}
		console.log("The Doctor didn't complete sucesfully. " + e.message);
		console.log(chalk.cyan('>'), 'Please visit our community forums for help with this error:');
		console.log(chalk.bold.white('https://community.particle.io/'));
	},
});

module.exports = DoctorCommand;
<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var Backbone = require('backbone');
var StepsView = require('./steps_view');
var RowModel = require('./row_model');
var DefaultFooterView = require('./default_footer_view');
var ViewFactory = require('../../view_factory');

/**
 * Model for the administrative regions georeference option.
 */
module.exports = cdb.core.Model.extend({

  TAB_LABEL: 'Admin. Regions',
  KIND: 'admin1',

  defaults: {
    step: 0,
    columns: []
  },

  initialize: function(attrs) {
    if (!attrs.geocodeStuff) throw new Error('geocodeStuff is required');
    if (!attrs.columnsNames) throw new Error('columnsNames is required');
    if (!attrs.columns) throw new Error('columns is required');
  },

  createView: function() {
    this._initRows();
    this._setStateForFirstStep();

    return ViewFactory.createByList([
      new StepsView({
        title: 'Select the column that has Administrative Regions',
        desc: "Georeference your data by country’s, state's, province's or municipality’s",
        model: this
      }),
      new DefaultFooterView({
        model: this
      })
    ]);
  },

  assertIfCanContinue: function() {
    var value = this.get('step') === 0 ? this._columnNameValue() : this.get('geometryType');
    this.set('canContinue', !!value);
  },

  continue: function() {
    if (this.get('step') === 0) {
      this._setStateForSecondStep();
    } else {
      this._geocode();
    }
  },

  goBack: function() {
    this._setStateForFirstStep();
  },

  availableGeometriesFetchData: function() {
    return this.get('geocodeStuff').availableGeometriesFetchData(this.KIND, this._locationValue(), this._isLocationFreeText());
  },

  _setStateForFirstStep: function() {
    this.set({
      step: 0,
      canGoBack: false,
      canContinue: false,
      hideFooter: false
    });
    this.get('rows').invoke('unset', 'value');
  },

  _setStateForSecondStep: function() {
    this.set({
      step: 1,
      canGoBack: true,
      canContinue: false,
      hideFooter: true,
      geometryType: ''
    });
  },

  _initRows: function() {
    var rows = new Backbone.Collection([
      new RowModel({
        comboViewClass: 'Combo',
        label: 'In which column are your region names stored?',
        placeholder: 'Select column',
        data: this.get('columnsNames')
      }),
      new RowModel({
        label: "If your column isn't country data, where's it?",
        data: this.get('columns')
      })
    ]);
    this.set('rows', rows);
  },

  _geocode: function() {
    var geocodeStuff = this.get('geocodeStuff');
    var locationValue = this._locationValue();
    var isLocationFreeText = this._isLocationFreeText();

    var d = geocodeStuff.geocodingChosenData({
      type: 'admin',
      kind: geocodeStuff.isLocationWorld(locationValue, isLocationFreeText, true) ? 'admin0' : this.KIND, // migrated from old code, unclear why this is needed
      location: locationValue,
      column_name: this._columnNameValue(),
      geometry_type: this.get('geometryType')
    }, isLocationFreeText, true);

    this.set('geocodeData', d);
  },

  _columnNameValue: function() {
    return this.get('rows').first().get('value');
  },

  _locationValue: function() {
    return this._location().get('value');
  },

  _isLocationFreeText: function() {
    return this._location().get('isFreeText');
  },

  _location: function() {
    return this.get('rows').last();
  }

});
<commit_msg>Update admin_regions_model.js<commit_after>var cdb = require('cartodb.js');
var Backbone = require('backbone');
var StepsView = require('./steps_view');
var RowModel = require('./row_model');
var DefaultFooterView = require('./default_footer_view');
var ViewFactory = require('../../view_factory');

/**
 * Model for the administrative regions georeference option.
 */
module.exports = cdb.core.Model.extend({

  TAB_LABEL: 'Admin. Regions',
  KIND: 'admin1',

  defaults: {
    step: 0,
    columns: []
  },

  initialize: function(attrs) {
    if (!attrs.geocodeStuff) throw new Error('geocodeStuff is required');
    if (!attrs.columnsNames) throw new Error('columnsNames is required');
    if (!attrs.columns) throw new Error('columns is required');
  },

  createView: function() {
    this._initRows();
    this._setStateForFirstStep();

    return ViewFactory.createByList([
      new StepsView({
        title: 'Select the column that has Administrative Regions',
        desc: "Georeference your data by country, state, province or municipality name",
        model: this
      }),
      new DefaultFooterView({
        model: this
      })
    ]);
  },

  assertIfCanContinue: function() {
    var value = this.get('step') === 0 ? this._columnNameValue() : this.get('geometryType');
    this.set('canContinue', !!value);
  },

  continue: function() {
    if (this.get('step') === 0) {
      this._setStateForSecondStep();
    } else {
      this._geocode();
    }
  },

  goBack: function() {
    this._setStateForFirstStep();
  },

  availableGeometriesFetchData: function() {
    return this.get('geocodeStuff').availableGeometriesFetchData(this.KIND, this._locationValue(), this._isLocationFreeText());
  },

  _setStateForFirstStep: function() {
    this.set({
      step: 0,
      canGoBack: false,
      canContinue: false,
      hideFooter: false
    });
    this.get('rows').invoke('unset', 'value');
  },

  _setStateForSecondStep: function() {
    this.set({
      step: 1,
      canGoBack: true,
      canContinue: false,
      hideFooter: true,
      geometryType: ''
    });
  },

  _initRows: function() {
    var rows = new Backbone.Collection([
      new RowModel({
        comboViewClass: 'Combo',
        label: 'In which column are your region names stored?',
        placeholder: 'Select column',
        data: this.get('columnsNames')
      }),
      new RowModel({
        label: "If the above column isn't country name data, which column is?",
        data: this.get('columns')
      })
    ]);
    this.set('rows', rows);
  },

  _geocode: function() {
    var geocodeStuff = this.get('geocodeStuff');
    var locationValue = this._locationValue();
    var isLocationFreeText = this._isLocationFreeText();

    var d = geocodeStuff.geocodingChosenData({
      type: 'admin',
      kind: geocodeStuff.isLocationWorld(locationValue, isLocationFreeText, true) ? 'admin0' : this.KIND, // migrated from old code, unclear why this is needed
      location: locationValue,
      column_name: this._columnNameValue(),
      geometry_type: this.get('geometryType')
    }, isLocationFreeText, true);

    this.set('geocodeData', d);
  },

  _columnNameValue: function() {
    return this.get('rows').first().get('value');
  },

  _locationValue: function() {
    return this._location().get('value');
  },

  _isLocationFreeText: function() {
    return this._location().get('isFreeText');
  },

  _location: function() {
    return this.get('rows').last();
  }

});
<|endoftext|>


<commit_before>'use strict';

/*
* The original C++ code and copyright notice is from the [Boost library]{@link http://www.boost.org/doc/libs/1_37_0/boost/math/special_functions/gamma.hpp}.
*
* The implementation has been modified for JavaScript.
*/

/*
* (C) Copyright John Maddock 2006.
* (C) Copyright Paul A. Bristow 2007.
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
*/

// MODULES //

var continuedFraction = require( '@stdlib/math/base/tools/continued-fraction' );
var upper_incomplete_gamma_fract = require( './upper_incomplete_gamma_fract.js' );

// MAIN //

/**
* Evaluate the lower incomplete gamma integral via a series expansion and divide by gamma(z) to normalise.
*
* @param {number} a - function parameter
* @param {number} z - function parameter
* @returns {number} function value
*/
function upper_gamma_fraction( a, z ) {
	var f = upper_incomplete_gamma_fract( a, z );
	return 1.0 / ( z - a + 1.0 + continuedFraction( f ) );
} // end FUNCTION upper_gamma_fraction()

// EXPORTS //

module.exports = upper_gamma_fraction;
<commit_msg>Fix lint errors<commit_after>'use strict';

/*
* The original C++ code and copyright notice is from the [Boost library]{@link http://www.boost.org/doc/libs/1_37_0/boost/math/special_functions/gamma.hpp}.
*
* The implementation has been modified for JavaScript.
*/

/*
* (C) Copyright John Maddock 2006.
* (C) Copyright Paul A. Bristow 2007.
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0. (See accompanying file
* LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
*/

// MODULES //

var continuedFraction = require( '@stdlib/math/base/tools/continued-fraction' );
var upperIncompleteGammaFract = require( './upper_incomplete_gamma_fract.js' );

// MAIN //

/**
* Evaluate the lower incomplete gamma integral via a series expansion and divide by gamma(z) to normalise.
*
* @param {number} a - function parameter
* @param {number} z - function parameter
* @returns {number} function value
*/
function upperGammaFraction( a, z ) {
	var f = upperIncompleteGammaFract( a, z );
	return 1.0 / ( z - a + 1.0 + continuedFraction( f ) );
} // end FUNCTION upperGammaFraction()

// EXPORTS //

module.exports = upperGammaFraction;
<|endoftext|>


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

// The nature of the web being what it is, we often will need to use Polyfills
// to get support for what we want. This will pull in babel-polyfill which will
// ensure we have an ES6 like environment.
import "babel-polyfill";

// We'll use docReady as a modern replacement for $(document).ready() which
// does not require all of jQuery to use. This will let us use it without
// having to load all of jQuery, which will make things faster.
import docReady from "warehouse/utils/doc-ready";

// Import our utility functions
import HTMLInclude from "warehouse/utils/html-include";
import * as formUtils from "warehouse/utils/forms";
import Clipboard from "clipboard";

// Kick off the client side HTML includes.
docReady(HTMLInclude);

// Handle the JS based automatic form submission.
docReady(formUtils.submitTriggers);

// Copy handler for the pip command on package detail page
docReady(() => {
  let clipboard = new Clipboard(".copy-pip-command");
  clipboard.on('success', (e) => {
    e.trigger.setAttribute('aria-label', 'Copied!');
    e.trigger.setAttribute('aria-label', 'Copy to clipboard');
    e.clearSelection();
  });
});
<commit_msg>remove the second label change line<commit_after>/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// The nature of the web being what it is, we often will need to use Polyfills
// to get support for what we want. This will pull in babel-polyfill which will
// ensure we have an ES6 like environment.
import "babel-polyfill";

// We'll use docReady as a modern replacement for $(document).ready() which
// does not require all of jQuery to use. This will let us use it without
// having to load all of jQuery, which will make things faster.
import docReady from "warehouse/utils/doc-ready";

// Import our utility functions
import HTMLInclude from "warehouse/utils/html-include";
import * as formUtils from "warehouse/utils/forms";
import Clipboard from "clipboard";

// Kick off the client side HTML includes.
docReady(HTMLInclude);

// Handle the JS based automatic form submission.
docReady(formUtils.submitTriggers);

// Copy handler for the pip command on package detail page
docReady(() => {
  let clipboard = new Clipboard(".copy-pip-command");
  clipboard.on('success', (e) => {
    e.trigger.setAttribute('aria-label', 'Copied!');
    e.clearSelection();
  });
});
<|endoftext|>


<commit_before>'use strict';

var db                  = require('mano').db
  , ensureStringifiable = require('es5-ext/object/validate-stringifiable-value');
/**
 * Test if given property of a given object is cardinalPropertyKey of NestedMap
 * @param object
 * @param property - expected cardinal propery
 * @returns {boolean}
 */
module.exports = function (object, property) {
	var isCardinal = false, keyPath = [property];
	ensureStringifiable(property);

	while (object) {
		if (object.owner && object.owner.owner && object.owner.owner instanceof db.NestedMap) {
			isCardinal = (object.owner.owner.cardinalPropertyKey === keyPath.join('/'));
			break;
		}
		keyPath.unshift(object.key);
		object = object.owner;
	}

	return isCardinal;
};
<commit_msg>#982 Fix tests<commit_after>'use strict';

var db                  = require('mano').db
  , ensureStringifiable = require('es5-ext/object/validate-stringifiable-value');
/**
 * Tests if given property of a given object is cardinalPropertyKey of NestedMap
 * @param object
 * @param property - expected cardinal propery
 * @returns {boolean}
 */
module.exports = function (object, property) {
	var isCardinal = false, keyPath = [property];
	ensureStringifiable(property);

	while (object) {
		if (object.owner && object.owner.owner && object.owner.owner instanceof db.NestedMap) {
			isCardinal = (object.owner.owner.cardinalPropertyKey === keyPath.join('/'));
			break;
		}
		keyPath.unshift(object.key);
		object = object.owner;
	}

	return isCardinal;
};
<|endoftext|>


<commit_before>var express = require('express'),
    path = require('path'),
    favicon = require('serve-favicon'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    session = require('express-session');
// PS：session与cookie是分开保存的.但是session中的数据经过加密处理后默认保存在一个cookie中.因此在使用session中间件之前必须使用cookieParser中间件

module.exports = function (app) {
    // ===================登陆校验===================
    // session生命周期设置
    app.use(session({
        secret: 'secret',
        cookie:{
            maxAge: 1000*60*30 // 毫秒
        }
    }));

    /*
     * 登录拦截器，在请求未流向路由前先进行判断
     */
    app.use(function (req, res, next) {
        if (req.originalUrl != "/login" && !req.session.user) {
            return res.redirect("/login");
        }
        next();
    });

    // 存储req.session到res.locals，模板引擎可以直接获取res.locals的数据进行渲染
    app.use(function (req, res, next) {
        // 这部步骤用于持续保证每次访问刷新本地跟服务端身份信息
        res.locals.user = req.session.user;
        var err = req.session.error;
        delete req.session.error;
        res.locals.message = "";
        if(err){
            res.locals.message = err;
        }
        next();
    });
    // ==============================================
    app.set('view engine', 'html');// 路由可以省去文件后缀名
    app.use(logger('dev')); // 在控制台中，显示req请求的信息
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(cookieParser());

    // 设置Angular入口静态路由
    app.use(express.static(path.join(root, 'app')));
    app.use(favicon(root + '/app/assets/favicon.ico'));
};
<commit_msg>update<commit_after>var express = require('express'),
    path = require('path'),
    favicon = require('serve-favicon'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    session = require('express-session');
// PS：session与cookie是分开保存的.但是session中的数据经过加密处理后默认保存在一个cookie中.因此在使用session中间件之前必须使用cookieParser中间件

module.exports = function (app) {
    // ===================登陆校验===================
    // session生命周期设置
    app.use(session({
        secret: 'secret',
        cookie:{
            maxAge: 1000*60*30 // 毫秒
        }
    }));

    // 设置Angular入口静态路由
    app.use(express.static(path.join(root, 'app')));
    app.use(favicon(root + '/app/assets/favicon.ico'));

    /*
     * 登录拦截器，在请求未流向路由前先进行判断
     */
    app.use(function (req, res, next) {
        if (req.originalUrl != "/login" && !req.session.user) {
            return res.redirect("/login");
        }
        next();
    });

    // 存储req.session到res.locals，模板引擎可以直接获取res.locals的数据进行渲染
    app.use(function (req, res, next) {
        // 这部步骤用于持续保证每次访问刷新本地跟服务端身份信息
        res.locals.user = req.session.user;
        var err = req.session.error;
        delete req.session.error;
        res.locals.message = "";
        if(err){
            res.locals.message = err;
        }
        next();
    });
    // ==============================================
    app.set('view engine', 'html');// 路由可以省去文件后缀名
    app.use(logger('dev')); // 在控制台中，显示req请求的信息
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(cookieParser());
};
<|endoftext|>