<commit_before>// Rest and Recovery
// A Roll20 API script to handle recovery on the resource attributes on the D&D 5th Edition by Roll20 sheet.
on('ready', () => {
    const version = '0.0.2';
    const sheetVersion = 'D&D 5th Edition by Roll20';
    log('Rest and Recovery v' + version + ' is ready!  Designed for use with the ' + sheetVersion + ' character sheet!');
                const shortRestList = ['Spell Slots', 'Warlock Spell Slots','Spell Points','Channel Divinity', 'Wild Shapes', 'Second Wind','Action Surge','Superiority Dice', 'Ki', 'Ki Points','Visions of the Past','Enthralling Performance','Words of Terror','Unbreakable Majesty','Spirit Totem','Indomitable','Arcane Shot','Detect Portal','Ethereal Step','Magic-Users Nemesis','Favored by the Gods','Wind Soul','Blade Song','Arcane Abeyance','Illusory Step','Shapechanger'];
                const longRestList = ['Rages', 'Lay on Hands', 'Sorcery Points','Flash of Genius','Divine Intervention','Eyes of the Grave','Warding Flare','Wrath of the Storm','Cleansing Touch','War Priest Attack','Sentinel at Deaths Door','Embodiment of the Law','Universal Speech','Mantle of Majesty','Infectious Inspiration','Shadow Lore','Balm of the Summer Court','Natural Recovery','Fungal Infestation','Hidden Paths','Walker in Dreams','Faithful Summons','Fighting Spirit','Warding Maneuver','Strength Before Death','Unwavering Mark','Wholeness of Body','Glorious Defense','Undying Sentinel','Invincible Conqueror','Holy Nimbus','Living Legend','Emissary of Redemption','Elder Champion','Avenging Angel','Dread Lord','Hunters Sense','Unerring Eye','Spell Thief','Strength of the Grave','Tides of Chaos','Unearthly Recovery','Mystic Arcanum-6th','Mystic Arcanum-7th','Mystic Arcanum-8th','Mystic Arcanum-9th','Eldritch Master','Chronal Shift','Momentary Stasis','Benign Transposition','Instinctive Charm','Power Surge','Violent Attraction','Event Horizon'];
                const ammoList = ['Crossbow bolts', 'Arrows', 'Bullets', 'Sling Bullets', 'Needles', 'Darts'];

    on('chat:message', (msg) => {
        if ('api' === msg.type && /!r-(short|long|charges|ammo|help|list)\b/i.test(msg.content) && msg.selected) {
            let idList = [];
            let nameList = [];
            var rReport = '';
            const header = "
";
            const footer = '
';

            function sendMessage(messageText) {
                sendChat("Rest and Recovery", "/w " + msg.who + header + messageText + footer);

            }

            function checkRestList(rName) {

                rRecoverName = rName;

                if (ammoList.includes(rName)) {
                    rRecoverName = rName + '+ammo';
                } else if (longRestList.includes(rName)) {
                    rRecoverName = rName + '+LR';
                } else if (shortRestList.includes(rName)) {
                    rRecoverName = rName + '+SR';
                } else {
                    rRecoverName = rName + '+x';
                }

                return rRecoverName;
            }

            const attrLookup = (characterid, name, caseSensitive) => {
                let match = name.match(/^(repeating_.*)_\$(\d+)_.*$/);
                if (match) {
                    let index = match[2],
                        attrMatcher = new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`, (caseSensitive ? '' : 'i')),
                        createOrderKeys = [],
                        attrs = _.chain(findObjs({
                            type: 'attribute',
                            characterid
                        }))
                        .map((a) => {
                            return {
                                attr: a,
                                match: a.get('name').match(attrMatcher)
                            };
                        })
                        .filter((o) => o.match)
                        .each((o) => createOrderKeys.push(o.match[1]))
                        .reduce((m, o) => {
                            m[o.match[1]] = o.attr;
                            return m;
                        }, {})
                        .value(),
                        sortOrderKeys = _.chain(((findObjs({
                            type: 'attribute',
                            characterid,
                            name: `_reporder_${match[1]}`
                        })[0] || {
                            get: _.noop
                        }).get('current') || '').split(/\s*,\s*/))
                        .intersection(createOrderKeys)
                        .union(createOrderKeys)
                        .value();
                    if (index < sortOrderKeys.length && _.has(attrs, sortOrderKeys[index])) {
                        return attrs[sortOrderKeys[index]];
                    }
                    return;
                }
                return findObjs({
                    type: 'attribute',
                    characterid,
                    name: name
                }, {
                    caseInsensitive: !caseSensitive
                })[0];
            };

            //Dice rolling function
            const parse = (txt) => {
                const tokenize = /(\d+d\d+|\d+|\+|-)/ig;
                const dieparts = /^(\d+)?d(\d+)$/i;
                const ops = {
                    '+': (m, n) => m + n,
                    '-': (m, n) => m - n
                };
                let op = '+';

                return (txt.replace(/\s+/g, '').match(tokenize) || []).reduce((m, t) => {
                    let matches = t.match(dieparts);
                    if (matches) {
                        return ops[op](m, [...Array(parseInt(matches[1]) || 1)].reduce(m => m + randomInteger(parseInt(matches[2])), 0));
                    } else if (/^\d+$/.test(t)) {
                        return ops[op](m, parseInt(t));
                    } else {
                        op = t;
                        return m;
                    }
                }, 0);
            };

            //Get character list. Future versions may handle multiple characters
            let TCData = msg.selected
                .map(o => getObj('graphic', o._id))
                .filter(o => undefined !== o)
                .filter(t => t.get('represents').length)
                .map(t => ({
                    token: t,
                    character: getObj('character', t.get('represents'))
                }))
                .filter(o => undefined !== o.character);
            var allIDs = TCData.map(n => n.character.get('_id'));

            var characterID = (allIDs[0]);
            var myCharacter = getObj('character', characterID);

            var resourceName_l = [],
                resourceRecovery_l = [],
                resourceCurrent_l = [],
                resourceMax_l = [],
                resourceName_r = [],
                resourceRecovery_r = [],
                resourceCurrent_r = [],
                resourceMax_r = [];

            resourceName_c = getAttrByName(characterID, "class_resource_name");
            resourceName_c = checkRestList(resourceName_c);
            resourceRecovery_c = resourceName_c.split(/\+(.+)/)[1]; //Regex splits on first occurence of '+', so the dice strings are preserved
            resourceCurrent_c = getAttrByName(characterID, "class_resource");
            resourceMax_c = getAttrByName(characterID, "class_resource", "max");

            resourceName_o = getAttrByName(characterID, "other_resource_name");
            resourceName_o = checkRestList(resourceName_o);
            resourceRecovery_o = resourceName_o.split(/\+(.+)/)[1];
            resourceCurrent_o = getAttrByName(characterID, "other_resource");
            resourceMax_o = getAttrByName(characterID, "other_resource", "max");

            //Get Repeating Resources
            var i = 0
            do {
                if (attrLookup(characterID, 'repeating_resource_$' + i + '_resource_left_name')) {
                    resourceName_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left_name');
                    resourceName_l[i] = checkRestList(resourceName_l[i]);
                    resourceRecovery_l[i] = resourceName_l[i].split(/\+(.+)/)[1];
                    resourceCurrent_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left');
                    resourceMax_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left', 'max');
                }

                if (attrLookup(characterID, 'repeating_resource_$' + i + '_resource_right_name')) {
                    resourceName_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right_name');
                    resourceName_r[i] = checkRestList(resourceName_r[i]);
                    resourceRecovery_r[i] = resourceName_r[i].split(/\+(.+)/)[1];
                    resourceCurrent_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right');
                    resourceMax_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right', 'max');
                }
                i++;
            }
            while (i < 10);

            //recover resources function
            function recoverResource(rFullName, rName, rRecover, rCurrent, rMax) {
                let r = attrLookup(characterID, rCurrent);

                if ((rRecover.includes("SR") && msg.content.includes("short")) || ((rRecover.includes("LR") || rRecover.includes("SR")) && msg.content.includes("long"))) {

                    if (rRecover.includes('d')) {

                        rRecover = rRecover.split(/SR|LR/)[1];
                        let recoverAmount = parse(rRecover);
                        r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                        rReport = rReport + getAttrByName(characterID, rName) + ' added ' + recoverAmount + 'charges.
';
                    } else {
                        r.set('current', rMax);
                        rReport = rReport + getAttrByName(characterID, rName) + ' has been fully restored.
';

                    }
                } else if (rRecover.includes("d") && !rRecover.includes('R') && msg.content.includes("charges")) {
                    let recoverAmount = parse(rRecover);
                    r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                    rReport = rReport + getAttrByName(characterID, rName) + ' added ' + recoverAmount + 'charges.
';
                } else if (rRecover.includes('ammo') && msg.content.includes("ammo")) {
                    if (rMax - Number(r.get('current')) > 0) {
                        let recoverAmount = (rMax - Number(r.get('current'))) + 'd2-' + (rMax - Number(r.get('current')));
                        recoverAmount = parse(recoverAmount);
                        r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                        r.set('max', r.get('current'));
                        rReport = rReport + recoverAmount + ' ' + getAttrByName(characterID, rName) + ' have been recovered.
';
                    }
                }
            }

            //Run Recoveries Here
            recoverResource(resourceName_c, "class_resource_name", resourceRecovery_c, "class_resource", resourceMax_c);
            recoverResource(resourceName_o, "other_resource_name", resourceRecovery_o, "other_resource", resourceMax_o);

            i = 0;
            do {
                if (resourceName_l[i]) {
                    recoverResource(resourceName_l[i], 'repeating_resource_$' + i + '_resource_left_name', resourceRecovery_l[i], 'repeating_resource_$' + i + '_resource_left', resourceMax_l[i]);

                }
                i++;
            }
            while (resourceName_l[i]);

            i = 0;
            do {
                if (resourceName_r[i]) {
                    recoverResource(resourceName_r[i], 'repeating_resource_$' + i + '_resource_right_name', resourceRecovery_r[i], 'repeating_resource_$' + i + '_resource_right', resourceMax_r[i]);
                }
                i++;
            }
            while (resourceName_r[i]);

            if (msg.content.includes("help")) {
                sendMessage('
Rest and Recovery

A Roll20 API script to handle recovery on the resource attributes on the D&D 5th Edition by Roll20 sheet.To use this script, resources must include a code in their name, separated from the name by a plus sign. You can include standard dice expressions as well. "1d6" is used in all examples, but you can do 2d6+3, 3d20, etc. Here are examples of the commands given and the codes that are affected.
!r-short
Used for Short Rest

+SR This resource will return to its maximumm value

+SR1d6 This resource will add 1d6 to the resource up to its maximum value
!r-long
Used for Long Rest

+LR This resource will return to its maximumm value

+LR1d6 This resource will add 1d6 to the resource up to its maximum value
!r-Charges
*used for restoring charges that are user-controlled, such as "at dawn" or "under a full moon".

+1d6
!r-Ammo
no code is used here. The script looks for common ammo types: Crossbow bolts, Arrows, Bullets, etc. It rolls 1d2 for each piece of ammo expended. If the result is a "2", the ammo is recovered. The max and current values are adjusted to reflect the new total.
Special Cases
Finally, the following special cases exist. Class Resources that have any of the following names are recognized and handled appropriately:
These are recovered on a Short or Long Rest:

Spell Slots, Warlock Spell Slots

Channel Divinity

Wild Shape

Superiority Dice

Ki Points, Ki
These are recovered on a Long Rest.

Rages

Lay on Hands

Sorcery Points

Bardic Inspiration needs a +SR or +LR code, since the recovery rate changes at fifth level
');
            } else {
             if (msg.content.includes("list")) {
                sendMessage('
Rest and Recovery

Short Rest Class Resources
'+shortRestList.join('

')+'

Long Rest Class Resources
'+longRestList.join('

')+'

Ammo Types
'+ammoList.join('

')+'
');
            } else {
                if (rReport) {
                    sendMessage(rReport);
                } else {
                    sendMessage('No resources were changed.');
}
                }
                log(rReport);
            }

        }
    });
});
<commit_msg>Update Rest-And-Recovery.js<commit_after>// Rest and Recovery
// A Roll20 API script to handle recovery on the resource attributes on the D&D 5th Edition by Roll20 sheet.
on('ready', () => {
    const version = '0.0.2';
    const sheetVersion = 'D&D 5th Edition by Roll20';
    log('Rest and Recovery v' + version + ' is ready!  Designed for use with the ' + sheetVersion + ' character sheet!');
                const shortRestList = ['Spell Slots', 'Warlock Spell Slots','Spell Points','Channel Divinity', 'Wild Shapes', 'Second Wind','Action Surge','Superiority Dice', 'Ki', 'Ki Points','Visions of the Past','Enthralling Performance','Words of Terror','Unbreakable Majesty','Spirit Totem','Arcane Shot','Detect Portal','Ethereal Step','Magic-Users Nemesis','Favored by the Gods','Wind Soul','Bladesong','Blade Song','Arcane Abeyance','Illusory Self','Shapechanger'];
                const longRestList = ['Rage', 'Rages', 'Lay on Hands', 'Sorcery Points','Flash of Genius','Divine Intervention','Eyes of the Grave','Warding Flare','Wrath of the Storm','Cleansing Touch','War Priest Attack','Sentinel at Deaths Door','Embodiment of the Law','Universal Speech','Mantle of Majesty','Indomitable','Infectious Inspiration','Shadow Lore','Balm of the Summer Court','Natural Recovery','Fungal Infestation','Hidden Paths','Walker in Dreams','Faithful Summons','Fighting Spirit','Warding Maneuver','Strength Before Death','Unwavering Mark','Wholeness of Body','Glorious Defense','Undying Sentinel','Invincible Conqueror','Holy Nimbus','Living Legend','Emissary of Redemption','Elder Champion','Avenging Angel','Dread Lord','Hunters Sense','Unerring Eye','Spell Thief','Strength of the Grave','Tides of Chaos','Unearthly Recovery','Mystic Arcanum-6th','Mystic Arcanum-7th','Mystic Arcanum-8th','Mystic Arcanum-9th','Eldritch Master','Chronal Shift','Momentary Stasis','Benign Transposition','Instinctive Charm','Power Surge','Violent Attraction','Event Horizon'];
                const ammoList = ['Crossbow bolts', 'Arrows', 'Bullets', 'Sling Bullets', 'Needles', 'Darts'];

    on('chat:message', (msg) => {
        if ('api' === msg.type && /!r-(short|long|charges|ammo|help|list)\b/i.test(msg.content) && msg.selected) {
            let idList = [];
            let nameList = [];
            var rReport = '';
            const header = "
";
            const footer = '
';

            function sendMessage(messageText) {
                sendChat("Rest and Recovery", "/w " + msg.who + header + messageText + footer);

            }

            function checkRestList(rName) {

                rRecoverName = rName;

                if (ammoList.includes(rName)) {
                    rRecoverName = rName + '+ammo';
                } else if (longRestList.includes(rName)) {
                    rRecoverName = rName + '+LR';
                } else if (shortRestList.includes(rName)) {
                    rRecoverName = rName + '+SR';
                } else {
                    rRecoverName = rName + '+x';
                }

                return rRecoverName;
            }

            const attrLookup = (characterid, name, caseSensitive) => {
                let match = name.match(/^(repeating_.*)_\$(\d+)_.*$/);
                if (match) {
                    let index = match[2],
                        attrMatcher = new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`, (caseSensitive ? '' : 'i')),
                        createOrderKeys = [],
                        attrs = _.chain(findObjs({
                            type: 'attribute',
                            characterid
                        }))
                        .map((a) => {
                            return {
                                attr: a,
                                match: a.get('name').match(attrMatcher)
                            };
                        })
                        .filter((o) => o.match)
                        .each((o) => createOrderKeys.push(o.match[1]))
                        .reduce((m, o) => {
                            m[o.match[1]] = o.attr;
                            return m;
                        }, {})
                        .value(),
                        sortOrderKeys = _.chain(((findObjs({
                            type: 'attribute',
                            characterid,
                            name: `_reporder_${match[1]}`
                        })[0] || {
                            get: _.noop
                        }).get('current') || '').split(/\s*,\s*/))
                        .intersection(createOrderKeys)
                        .union(createOrderKeys)
                        .value();
                    if (index < sortOrderKeys.length && _.has(attrs, sortOrderKeys[index])) {
                        return attrs[sortOrderKeys[index]];
                    }
                    return;
                }
                return findObjs({
                    type: 'attribute',
                    characterid,
                    name: name
                }, {
                    caseInsensitive: !caseSensitive
                })[0];
            };

            //Dice rolling function
            const parse = (txt) => {
                const tokenize = /(\d+d\d+|\d+|\+|-)/ig;
                const dieparts = /^(\d+)?d(\d+)$/i;
                const ops = {
                    '+': (m, n) => m + n,
                    '-': (m, n) => m - n
                };
                let op = '+';

                return (txt.replace(/\s+/g, '').match(tokenize) || []).reduce((m, t) => {
                    let matches = t.match(dieparts);
                    if (matches) {
                        return ops[op](m, [...Array(parseInt(matches[1]) || 1)].reduce(m => m + randomInteger(parseInt(matches[2])), 0));
                    } else if (/^\d+$/.test(t)) {
                        return ops[op](m, parseInt(t));
                    } else {
                        op = t;
                        return m;
                    }
                }, 0);
            };

            //Get character list. Future versions may handle multiple characters
            let TCData = msg.selected
                .map(o => getObj('graphic', o._id))
                .filter(o => undefined !== o)
                .filter(t => t.get('represents').length)
                .map(t => ({
                    token: t,
                    character: getObj('character', t.get('represents'))
                }))
                .filter(o => undefined !== o.character);
            var allIDs = TCData.map(n => n.character.get('_id'));

            var characterID = (allIDs[0]);
            var myCharacter = getObj('character', characterID);

            var resourceName_l = [],
                resourceRecovery_l = [],
                resourceCurrent_l = [],
                resourceMax_l = [],
                resourceName_r = [],
                resourceRecovery_r = [],
                resourceCurrent_r = [],
                resourceMax_r = [];

            resourceName_c = getAttrByName(characterID, "class_resource_name");
            resourceName_c = checkRestList(resourceName_c);
            resourceRecovery_c = resourceName_c.split(/\+(.+)/)[1]; //Regex splits on first occurence of '+', so the dice strings are preserved
            resourceCurrent_c = getAttrByName(characterID, "class_resource");
            resourceMax_c = getAttrByName(characterID, "class_resource", "max");

            resourceName_o = getAttrByName(characterID, "other_resource_name");
            resourceName_o = checkRestList(resourceName_o);
            resourceRecovery_o = resourceName_o.split(/\+(.+)/)[1];
            resourceCurrent_o = getAttrByName(characterID, "other_resource");
            resourceMax_o = getAttrByName(characterID, "other_resource", "max");

            //Get Repeating Resources
            var i = 0
            do {
                if (attrLookup(characterID, 'repeating_resource_$' + i + '_resource_left_name')) {
                    resourceName_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left_name');
                    resourceName_l[i] = checkRestList(resourceName_l[i]);
                    resourceRecovery_l[i] = resourceName_l[i].split(/\+(.+)/)[1];
                    resourceCurrent_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left');
                    resourceMax_l[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_left', 'max');
                }

                if (attrLookup(characterID, 'repeating_resource_$' + i + '_resource_right_name')) {
                    resourceName_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right_name');
                    resourceName_r[i] = checkRestList(resourceName_r[i]);
                    resourceRecovery_r[i] = resourceName_r[i].split(/\+(.+)/)[1];
                    resourceCurrent_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right');
                    resourceMax_r[i] = getAttrByName(characterID, 'repeating_resource_$' + i + '_resource_right', 'max');
                }
                i++;
            }
            while (i < 10);

            //recover resources function
            function recoverResource(rFullName, rName, rRecover, rCurrent, rMax) {
                let r = attrLookup(characterID, rCurrent);

                if ((rRecover.includes("SR") && msg.content.includes("short")) || ((rRecover.includes("LR") || rRecover.includes("SR")) && msg.content.includes("long"))) {

                    if (rRecover.includes('d')) {

                        rRecover = rRecover.split(/SR|LR/)[1];
                        let recoverAmount = parse(rRecover);
                        r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                        rReport = rReport + getAttrByName(characterID, rName) + ' added ' + recoverAmount + 'charges.
';
                    } else {
                        r.set('current', rMax);
                        rReport = rReport + getAttrByName(characterID, rName) + ' has been fully restored.
';

                    }
                } else if (rRecover.includes("d") && !rRecover.includes('R') && msg.content.includes("charges")) {
                    let recoverAmount = parse(rRecover);
                    r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                    rReport = rReport + getAttrByName(characterID, rName) + ' added ' + recoverAmount + 'charges.
';
                } else if (rRecover.includes('ammo') && msg.content.includes("ammo")) {
                    if (rMax - Number(r.get('current')) > 0) {
                        let recoverAmount = (rMax - Number(r.get('current'))) + 'd2-' + (rMax - Number(r.get('current')));
                        recoverAmount = parse(recoverAmount);
                        r.set('current', Math.min(Number(r.get('current')) + recoverAmount, rMax));
                        r.set('max', r.get('current'));
                        rReport = rReport + recoverAmount + ' ' + getAttrByName(characterID, rName) + ' have been recovered.
';
                    }
                }
            }

            //Run Recoveries Here
            recoverResource(resourceName_c, "class_resource_name", resourceRecovery_c, "class_resource", resourceMax_c);
            recoverResource(resourceName_o, "other_resource_name", resourceRecovery_o, "other_resource", resourceMax_o);

            i = 0;
            do {
                if (resourceName_l[i]) {
                    recoverResource(resourceName_l[i], 'repeating_resource_$' + i + '_resource_left_name', resourceRecovery_l[i], 'repeating_resource_$' + i + '_resource_left', resourceMax_l[i]);

                }
                i++;
            }
            while (resourceName_l[i]);

            i = 0;
            do {
                if (resourceName_r[i]) {
                    recoverResource(resourceName_r[i], 'repeating_resource_$' + i + '_resource_right_name', resourceRecovery_r[i], 'repeating_resource_$' + i + '_resource_right', resourceMax_r[i]);
                }
                i++;
            }
            while (resourceName_r[i]);

            if (msg.content.includes("help")) {
                sendMessage('
Rest and Recovery

A Roll20 API script to handle recovery on the resource attributes on the D&D 5th Edition by Roll20 sheet.To use this script, resources must include a code in their name, separated from the name by a plus sign. You can include standard dice expressions as well. "1d6" is used in all examples, but you can do 2d6+3, 3d20, etc. Here are examples of the commands given and the codes that are affected.
!r-short
Used for Short Rest

+SR This resource will return to its maximumm value

+SR1d6 This resource will add 1d6 to the resource up to its maximum value
!r-long
Used for Long Rest

+LR This resource will return to its maximumm value

+LR1d6 This resource will add 1d6 to the resource up to its maximum value
!r-Charges
*used for restoring charges that are user-controlled, such as "at dawn" or "under a full moon".

+1d6
!r-Ammo
no code is used here. The script looks for common ammo types: Crossbow bolts, Arrows, Bullets, etc. It rolls 1d2 for each piece of ammo expended. If the result is a "2", the ammo is recovered. The max and current values are adjusted to reflect the new total.
Special Cases
Finally, the following special cases exist. Class Resources that have any of the following names are recognized and handled appropriately:
These are recovered on a Short or Long Rest:

Spell Slots, Warlock Spell Slots

Channel Divinity

Wild Shape

Superiority Dice

Ki Points, Ki
These are recovered on a Long Rest.

Rages

Lay on Hands

Sorcery Points

Bardic Inspiration needs a +SR or +LR code, since the recovery rate changes at fifth level
');
            } else {
             if (msg.content.includes("list")) {
                sendMessage('
Rest and Recovery

Short Rest Class Resources
'+shortRestList.join('

')+'

Long Rest Class Resources
'+longRestList.join('

')+'

Ammo Types
'+ammoList.join('

')+'
');
            } else {
                if (rReport) {
                    sendMessage(rReport);
                } else {
                    sendMessage('No resources were changed.');
}
                }
                log(rReport);
            }

        }
    });
});
<|endoftext|>


<commit_before>// flow

import React, {PropTypes, Component} from 'react';
import {StyleSheet, Dimensions, TouchableOpacity, Animated} from 'react-native';

const SCREEN_WIDTH = Dimensions.get('window').width;
const SCREEN_HEIGHT = Dimensions.get('window').height;

class Overlay extends Component {
	constructor(props) {
		super(props);
		this.state = {
			opacity: new Animated.Value(0),
		};
	}

	componentWillReceiveProps(nextProps) {
		if (this.props.showOverlay !== nextProps.showOverlay) {
			if (nextProps.showOverlay) {
				Animated.timing(this.state.opacity, {
					toValue: nextProps.opacity || 0,
					duration: this.props.animationDuration,
				}).start();
			} else {
				Animated.timing(this.state.opacity, {
					toValue: 0,
					duration: this.props.animationDuration,
				}).start();
			}
		}
	}

	render() {
		const backgroundColor = {backgroundColor: this.props.backgroundColor};
		const pointerEvents = this.props.showOverlay ? 'auto' : 'none';
		const onPress = this.props.onPress;
		const opacity = {opacity: this.state.opacity};

		return (
			<Animated.View
				pointerEvents={pointerEvents}
				style={[styles.overlay, backgroundColor, opacity]}
			>
				<TouchableOpacity onPress={onPress} style={[styles.overlay]}/>
			</Animated.View>
		);
	}
}

Overlay.propTypes = {
	onPress: PropTypes.func,
	backgroundColor: PropTypes.string,
	opacity: PropTypes.number,
	animationDuration: PropTypes.number,
	showOverlay: PropTypes.bool,
};

Overlay.defaultProps = {
	backgroundColor: '#000',
	opacity: 0.5,
	animationDuration: 200,
	showOverlay: false,
};

const styles = StyleSheet.create({
	overlay: {
		flex: 1,
		top: 0,
		left: 0,
		width: SCREEN_WIDTH,
		height: SCREEN_HEIGHT,
		position: 'absolute',
	},
});

export default Overlay;
<commit_msg>:rocket: used destructuring rather than explicit assignment<commit_after>// flow

import React, {PropTypes, Component} from 'react';
import {StyleSheet, Dimensions, TouchableOpacity, Animated} from 'react-native';

const {width, height} = Dimensions.get('window');

class Overlay extends Component {
	constructor(props) {
		super(props);
		this.state = {
			opacity: new Animated.Value(0),
		};
	}

	componentWillReceiveProps(nextProps) {
		if (this.props.showOverlay !== nextProps.showOverlay) {
			if (nextProps.showOverlay) {
				Animated.timing(this.state.opacity, {
					toValue: nextProps.opacity || 0,
					duration: this.props.animationDuration,
				}).start();
			} else {
				Animated.timing(this.state.opacity, {
					toValue: 0,
					duration: this.props.animationDuration,
				}).start();
			}
		}
	}

	render() {
		const backgroundColor = {backgroundColor: this.props.backgroundColor};
		const pointerEvents = this.props.showOverlay ? 'auto' : 'none';
		const onPress = this.props.onPress;
		const opacity = {opacity: this.state.opacity};

		return (
			<Animated.View
				pointerEvents={pointerEvents}
				style={[styles.overlay, backgroundColor, opacity]}
			>
				<TouchableOpacity onPress={onPress} style={[styles.overlay]}/>
			</Animated.View>
		);
	}
}

Overlay.propTypes = {
	onPress: PropTypes.func,
	backgroundColor: PropTypes.string,
	opacity: PropTypes.number,
	animationDuration: PropTypes.number,
	showOverlay: PropTypes.bool,
};

Overlay.defaultProps = {
	backgroundColor: '#000',
	opacity: 0.5,
	animationDuration: 200,
	showOverlay: false,
};

const styles = StyleSheet.create({
	overlay: {
		flex: 1,
		top: 0,
		left: 0,
		width: width,
		height: height,
		position: 'absolute',
	},
});

export default Overlay;
<|endoftext|>


<commit_before>import { moduleForModel, test } from 'ember-qunit';
import modelList from '../../helpers/model-list';
import Ember from 'ember';

const { run } = Ember;

moduleForModel('course', 'Unit | Model | Course', {
  needs: modelList
});

test('it exists', function(assert) {
  let model = this.subject();
  assert.ok(!!model);
});

test('check required publication items', function(assert) {
  let model = this.subject();
  let store = this.store();
  assert.equal(model.get('requiredPublicationIssues').length, 3);
  var cohort = store.createRecord('cohort');
  model.get('cohorts').addObject(cohort);
  assert.equal(model.get('requiredPublicationIssues').length, 2);
  model.set('startDate', 'nothing');
  assert.equal(model.get('requiredPublicationIssues').length, 1);
  model.set('endDate', 'nothing');
  assert.equal(model.get('requiredPublicationIssues').length, 0);
});

test('check optional publication items', function(assert) {
  let model = this.subject();
  let store = this.store();
  assert.equal(model.get('optionalPublicationIssues').length, 3);
  model.get('terms').addObject(store.createRecord('term'));
  assert.equal(model.get('optionalPublicationIssues').length, 2);
  model.get('objectives').addObject(store.createRecord('objective'));
  assert.equal(model.get('optionalPublicationIssues').length, 1);
  model.get('meshDescriptors').addObject(store.createRecord('meshDescriptor'));
  assert.equal(model.get('optionalPublicationIssues').length, 0);
});

test('check empty competencies', async function(assert) {
  assert.expect(1);
  let course = this.subject();

  const competencies = await course.get('competencies');
  assert.equal(competencies.length, 0);
});

test('check competencies', async function(assert) {
  assert.expect(4);
  let course = this.subject();
  let store = this.store();

  await run( async () => {
    let competency1 = store.createRecord('competency');
    let competency2 = store.createRecord('competency');
    let competency3 = store.createRecord('competency');

    let objective1 = store.createRecord('objective', {competency: competency1});
    let objective2 = store.createRecord('objective', {competency: competency2});
    let objective3 = store.createRecord('objective', {competency: competency3, courses: [course], parents: [objective1]});
    let objective4 = store.createRecord('objective', {courses: [course], parents: [objective2]});
    objective1.get('children').pushObject(objective3);
    objective2.get('children').pushObject(objective4);
    course.get('objectives').pushObjects([objective3, objective4]);

    const competencies = await course.get('competencies');

    assert.equal(competencies.length, 3);
    assert.ok(competencies.includes(competency1));
    assert.ok(competencies.includes(competency2));
    assert.ok(competencies.includes(competency3));
  });
});

test('check publishedSessionOfferingCounts count', function(assert) {
  assert.expect(2);
  let course = this.subject();
  let store = this.store();

  run(() => {
    let offering1 = store.createRecord('offering');
    let offering2 = store.createRecord('offering');
    let offering3 = store.createRecord('offering');
    let offering4 = store.createRecord('offering');

    let session1 = store.createRecord('session', {offerings: [offering1, offering2], published: true});
    let session2 = store.createRecord('session', {offerings: [offering3], published: true});
    let session3 = store.createRecord('session', {offerings: [offering4], published: false});

    course.get('sessions').pushObjects([session1, session2, session3]);

    assert.equal(course.get('publishedOfferingCount'), 3);
    let offering5 = store.createRecord('offering');
    session1.get('offerings').pushObject(offering5);
    session3.set('published', true);

    assert.equal(course.get('publishedOfferingCount'), 5);
  });
});

test("domains", async function(assert) {
  assert.expect(10);
  let course = this.subject();
  let store = this.store();
  await run( async () => {
    const domain1 = store.createRecord('competency', { id: 1, title: 'Zylinder' });
    const domain2 = store.createRecord('competency', { id: 2, title: 'Anton' });
    const domain3 = store.createRecord('competency', { id: 3, title: 'Lexicon' });
    const competency1 = store.createRecord('competency', { id: 4, title: 'Zeppelin', parent: domain1 });
    const competency2 = store.createRecord('competency', { id: 5, title: 'Aardvark', parent: domain1 });
    const competency3 = store.createRecord('competency', { id: 6, title: 'Geflarknik', parent: domain2 });
    // competencies that are linked to these domains, but not to this course.
    // they should not appear in the output.
    store.createRecord('competency', { id: 7, parent: domain1 });
    store.createRecord('competency', { id: 8, parent: domain2 });
    store.createRecord('competency', { id: 9, parent: domain3 });

    let objective1 = store.createRecord('objective', { competency: competency1 });
    let objective2 = store.createRecord('objective', { competency: competency2 });
    let objective3 = store.createRecord('objective', { competency: competency3 });
    let objective4 = store.createRecord('objective', { competency: domain3 });

    course.get('objectives').pushObjects([ objective1, objective2, objective3, objective4 ]);

    const domainProxies = await course.get('domains');
    assert.equal(domainProxies.length, 3);

    const domainProxy1 = domainProxies[0];
    assert.equal(domainProxy1.get('content'), domain2);
    assert.equal(domainProxy1.get('subCompetencies').length, 1);
    assert.ok(domainProxy1.get('subCompetencies').includes(competency3));

    const domainProxy2 = domainProxies[1];
    assert.equal(domainProxy2.get('content'), domain3);
    assert.equal(domainProxy2.get('subCompetencies').length, 0);

    const domainProxy3 = domainProxies[2];
    assert.equal(domainProxy3.get('content'), domain1);
    assert.equal(domainProxy3.get('subCompetencies').length, 2);
    assert.equal(domainProxy3.get('subCompetencies')[0], competency2);
    assert.equal(domainProxy3.get('subCompetencies')[1], competency1);
  });
});
<commit_msg>test coverage for schools CP.<commit_after>import { moduleForModel, test } from 'ember-qunit';
import modelList from '../../helpers/model-list';
import Ember from 'ember';

const { run } = Ember;

moduleForModel('course', 'Unit | Model | Course', {
  needs: modelList
});

test('it exists', function(assert) {
  let model = this.subject();
  assert.ok(!!model);
});

test('check required publication items', function(assert) {
  let model = this.subject();
  let store = this.store();
  assert.equal(model.get('requiredPublicationIssues').length, 3);
  var cohort = store.createRecord('cohort');
  model.get('cohorts').addObject(cohort);
  assert.equal(model.get('requiredPublicationIssues').length, 2);
  model.set('startDate', 'nothing');
  assert.equal(model.get('requiredPublicationIssues').length, 1);
  model.set('endDate', 'nothing');
  assert.equal(model.get('requiredPublicationIssues').length, 0);
});

test('check optional publication items', function(assert) {
  let model = this.subject();
  let store = this.store();
  assert.equal(model.get('optionalPublicationIssues').length, 3);
  model.get('terms').addObject(store.createRecord('term'));
  assert.equal(model.get('optionalPublicationIssues').length, 2);
  model.get('objectives').addObject(store.createRecord('objective'));
  assert.equal(model.get('optionalPublicationIssues').length, 1);
  model.get('meshDescriptors').addObject(store.createRecord('meshDescriptor'));
  assert.equal(model.get('optionalPublicationIssues').length, 0);
});

test('check empty competencies', async function(assert) {
  assert.expect(1);
  let course = this.subject();

  const competencies = await course.get('competencies');
  assert.equal(competencies.length, 0);
});

test('check competencies', async function(assert) {
  assert.expect(4);
  let course = this.subject();
  let store = this.store();

  await run( async () => {
    let competency1 = store.createRecord('competency');
    let competency2 = store.createRecord('competency');
    let competency3 = store.createRecord('competency');

    let objective1 = store.createRecord('objective', {competency: competency1});
    let objective2 = store.createRecord('objective', {competency: competency2});
    let objective3 = store.createRecord('objective', {competency: competency3, courses: [course], parents: [objective1]});
    let objective4 = store.createRecord('objective', {courses: [course], parents: [objective2]});
    objective1.get('children').pushObject(objective3);
    objective2.get('children').pushObject(objective4);
    course.get('objectives').pushObjects([objective3, objective4]);

    const competencies = await course.get('competencies');

    assert.equal(competencies.length, 3);
    assert.ok(competencies.includes(competency1));
    assert.ok(competencies.includes(competency2));
    assert.ok(competencies.includes(competency3));
  });
});

test('check publishedSessionOfferingCounts count', function(assert) {
  assert.expect(2);
  let course = this.subject();
  let store = this.store();

  run(() => {
    let offering1 = store.createRecord('offering');
    let offering2 = store.createRecord('offering');
    let offering3 = store.createRecord('offering');
    let offering4 = store.createRecord('offering');

    let session1 = store.createRecord('session', {offerings: [offering1, offering2], published: true});
    let session2 = store.createRecord('session', {offerings: [offering3], published: true});
    let session3 = store.createRecord('session', {offerings: [offering4], published: false});

    course.get('sessions').pushObjects([session1, session2, session3]);

    assert.equal(course.get('publishedOfferingCount'), 3);
    let offering5 = store.createRecord('offering');
    session1.get('offerings').pushObject(offering5);
    session3.set('published', true);

    assert.equal(course.get('publishedOfferingCount'), 5);
  });
});

test("domains", async function(assert) {
  assert.expect(10);
  let course = this.subject();
  let store = this.store();
  await run( async () => {
    const domain1 = store.createRecord('competency', { id: 1, title: 'Zylinder' });
    const domain2 = store.createRecord('competency', { id: 2, title: 'Anton' });
    const domain3 = store.createRecord('competency', { id: 3, title: 'Lexicon' });
    const competency1 = store.createRecord('competency', { id: 4, title: 'Zeppelin', parent: domain1 });
    const competency2 = store.createRecord('competency', { id: 5, title: 'Aardvark', parent: domain1 });
    const competency3 = store.createRecord('competency', { id: 6, title: 'Geflarknik', parent: domain2 });
    // competencies that are linked to these domains, but not to this course.
    // they should not appear in the output.
    store.createRecord('competency', { id: 7, parent: domain1 });
    store.createRecord('competency', { id: 8, parent: domain2 });
    store.createRecord('competency', { id: 9, parent: domain3 });

    let objective1 = store.createRecord('objective', { competency: competency1 });
    let objective2 = store.createRecord('objective', { competency: competency2 });
    let objective3 = store.createRecord('objective', { competency: competency3 });
    let objective4 = store.createRecord('objective', { competency: domain3 });

    course.get('objectives').pushObjects([ objective1, objective2, objective3, objective4 ]);

    const domainProxies = await course.get('domains');
    assert.equal(domainProxies.length, 3);

    const domainProxy1 = domainProxies[0];
    assert.equal(domainProxy1.get('content'), domain2);
    assert.equal(domainProxy1.get('subCompetencies').length, 1);
    assert.ok(domainProxy1.get('subCompetencies').includes(competency3));

    const domainProxy2 = domainProxies[1];
    assert.equal(domainProxy2.get('content'), domain3);
    assert.equal(domainProxy2.get('subCompetencies').length, 0);

    const domainProxy3 = domainProxies[2];
    assert.equal(domainProxy3.get('content'), domain1);
    assert.equal(domainProxy3.get('subCompetencies').length, 2);
    assert.equal(domainProxy3.get('subCompetencies')[0], competency2);
    assert.equal(domainProxy3.get('subCompetencies')[1], competency1);
  });
});

test('schools', async function(assert) {
  assert.expect(4);
  let course = this.subject();
  let store = this.store();
  await run( async () => {
    const school1 = store.createRecord('school');
    const school2 = store.createRecord('school');
    const school3 = store.createRecord('school');
    const program1 = store.createRecord('program', { school: school2 });
    const program2 = store.createRecord('program', { school: school2 });
    const program3 = store.createRecord('program', { school: school3 });
    const programYear1 = store.createRecord('programYear', { program: program1 });
    const programYear2 = store.createRecord('programYear', { program: program2 });
    const programYear3 = store.createRecord('programYear', { program: program3 });
    const cohort1 = store.createRecord('cohort', { programYear: programYear1 });
    const cohort2 = store.createRecord('cohort', { programYear: programYear2 });
    const cohort3 = store.createRecord('cohort', { programYear: programYear3 });

    course.get('cohorts').pushObjects([ cohort1, cohort2, cohort3 ]);
    course.set('school', school1);

    const schools = await course.get('schools');

    assert.equal(schools.length, 3);
    assert.ok(schools.includes(school1));
    assert.ok(schools.includes(school2));
    assert.ok(schools.includes(school3));
  });
});
<|endoftext|>


<commit_before>const _isEqual = require('lodash/isEqual');
const keycode = require('keycode');
const PropTypes = require('prop-types');
const Radium = require('radium');
const React = require('react');
const ReactDOM = require('react-dom');

import { withTheme } from './Theme';
const Icon = require('./Icon');
const { Listbox, Option } = require('./accessibility/Listbox');

const { themeShape } = require('../constants/App');

const StyleUtils = require('../utils/Style');
const { deprecatePrimaryColor } = require('../utils/Deprecation');

// returns a function that takes a click event, stops it, then calls the callback
const haltEvent = callback => e => {
  e.preventDefault();
  e.stopPropagation();
  callback();
};

const optionShape = PropTypes.shape({
  displayValue: PropTypes.any.isRequired,
  icon: PropTypes.any,
  value: PropTypes.any.isRequired
});

class Select extends React.Component {
  static propTypes = {
    dropdownStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    elementRef: PropTypes.func,
    onChange: PropTypes.func,
    options: PropTypes.arrayOf(optionShape),
    optionsStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    optionStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    optionTextStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    placeholderText: PropTypes.string,
    primaryColor: PropTypes.string,
    scrimStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    selected: optionShape,
    selectedStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    theme: themeShape,
    valid: PropTypes.bool
  };

  static defaultProps = {
    onChange () {},
    options: [],
    placeholderText: 'Select One',
    valid: true
  };

  constructor (props) {
    super(props);

    this.state = {
      isOpen: false,
      selected: props.selected
    };
  }

  componentDidMount () {
    deprecatePrimaryColor(this.props);
  }

  componentWillReceiveProps (newProps) {
    if (!_isEqual(newProps.selected, this.props.selected)) {
      this.setState({ selected: newProps.selected });
    }
  }

  _handleKeyDown = (e) => {
    switch (keycode(e)) {
      case 'esc':
        e.preventDefault();
        e.stopPropagation();
        this._close();
        break;
      case 'enter':
      case 'space':
        if (this.state.isOpen) return;
        e.preventDefault();
        e.stopPropagation();
        this._open();
        break;
    }
  };

  _close = () => {
    this.setState({ isOpen: false });
    this.elementRef.focus();
  };

  _open = () => {
    this.setState({ isOpen: true });
  };

  _handleOptionClick = (option) => {
    this.setState({ selected: option }, () => {
      this._close();
      this.props.onChange(option);
    });
  };

  _scrollListDown = (nextIndex) => {
    const ul = ReactDOM.findDOMNode(this.optionList);
    const activeLi = ul.children[nextIndex];
    const heightFromTop = nextIndex * activeLi.clientHeight;

    if (heightFromTop > ul.clientHeight) {
      ul.scrollTop = activeLi.offsetTop - activeLi.clientHeight;
    }
  };

  _scrollListUp = (prevIndex) => {
    const ul = ReactDOM.findDOMNode(this.optionList);
    const activeLi = ul.children[prevIndex];
    const heightFromBottom = (this.props.options.length - prevIndex) * activeLi.clientHeight;

    if (heightFromBottom > ul.clientHeight) {
      ul.scrollTop = activeLi.offsetTop - activeLi.clientHeight;
    }
  };

  _renderScrim = (styles) => {
    if (this.state.isOpen) {
      return (

      );
    } else {
      return null;
    }
  };

  _renderOptions = (styles) => {
    if (this.state.isOpen) {
      if (this.props.children) {
        return (

            {this.props.children}

        );
      } else {
        return (
          <Listbox
            aria-label={this.props.placeholderText}
            className='mx-select-options'
            ref={(ref) => this.optionList = ref}
            style={styles.options}
            useGlobalKeyHandler={true}
          >
            {this.props.options.map(option => {
              return (
                <Option
                  className='mx-select-option'
                  isSelected={_isEqual(option, this.state.selected)}
                  key={option.displayValue + option.value}
                  label={option.displayValue}
                  onClick={haltEvent(this._handleOptionClick.bind(null, option))}
                  style={Object.assign({},
                    styles.option,
                    this.props.optionStyle,
                    _isEqual(option, this.state.selected) ? styles.activeOption : null
                  )}
                >
                  {option.icon ? (
                    <Icon
                      size={20}
                      style={styles.optionIcon}
                      type={option.icon}
                    />
                  ) : null}

{option.displayValue}

                  {_isEqual(option, this.state.selected) ? <Icon size={20} type='check' /> : null }
                </Option>
              );
            })}
          </Listbox>
        );
      }
    } else {
      return null;
    }
  };

  render () {
    const theme = StyleUtils.mergeTheme(this.props.theme, this.props.primaryColor);
    const styles = this.styles(theme);
    const selected = this.state.selected || this.props.selected || { displayValue: this.props.placeholderText, value: '' };

    return (

 this.elementRef = ref}
          style={styles.component}
          tabIndex='0'
        >
          {this._renderScrim(styles)}

            {selected.icon ? (
              <Icon
                size={20}
                style={styles.optionIcon}
                type={selected.icon}
              />
            ) : null}

{selected.displayValue}

            <Icon
              size={20}
              type={this.state.isOpen ? 'caret-up' : 'caret-down'}
            />

          {this.props.options.length || this.props.children ? this._renderOptions(styles) : null}

    );
  }

  styles = (theme) => {
    const focusedOption = {
      backgroundColor: theme.Colors.PRIMARY,
      color: theme.Colors.WHITE,
      fill: theme.Colors.WHITE
    };

    return {
      component: Object.assign({},
        {
          backgroundColor: theme.Colors.WHITE,
          borderRadius: 3,
          border: '1px solid ' + theme.Colors.GRAY_300,
          cursor: 'pointer',
          fontFamily: theme.FontFamily,
          fontSize: theme.FontSizes.MEDIUM,
          padding: '8px 10px',
          position: 'relative',
          boxSizing: 'border-box'
        }, this.props.dropdownStyle),
      select: {
        position: 'absolute',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        width: '100%',
        opacity: 0
      },
      selected: Object.assign({},
        {
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'space-between',
          position: 'relative'
        }, this.props.selectedStyle),
      invalid: {
        borderColor: theme.Colors.DANGER
      },
      options: Object.assign({},
        {
          backgroundColor: theme.Colors.WHITE,
          border: '1px solid ' + theme.Colors.GRAY_300,
          borderRadius: '0 0 3px 3px',
          left: -1,
          right: -1,
          margin: '8px 0 0 0',
          padding: 0,
          minWidth: '100%',
          position: 'absolute',
          zIndex: 10,
          fontSize: 12,
          boxShadow: theme.ShadowHigh,
          boxSizing: 'border-box',
          maxHeight: 260,
          overflow: 'auto'
        }, this.props.optionsStyle),
      activeOption: {
        fill: theme.Colors.PRIMARY,
        color: theme.Colors.PRIMARY
      },
      option: {
        display: 'flex',
        alignItems: 'center',
        color: theme.Colors.GRAY_700,
        cursor: 'pointer',
        backgroundColor: theme.Colors.WHITE,
        outline: 'none',
        padding: 10,
        whiteSpace: 'nowrap',

        ':focus': focusedOption,
        ':hover': focusedOption
      },
      optionIcon: {
        marginRight: 5
      },
      optionText: Object.assign({},
        {
          flex: '1 0 0%'
        }, this.props.optionTextStyle),
      scrim: {
        position: 'fixed',
        zIndex: 9,
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      }
    };
  };
}

module.exports = withTheme(Radium(Select));
<commit_msg>Pass onOptionClick handler to child components to set selected option<commit_after>const _isEqual = require('lodash/isEqual');
const keycode = require('keycode');
const PropTypes = require('prop-types');
const Radium = require('radium');
const React = require('react');
const ReactDOM = require('react-dom');

import { withTheme } from './Theme';
const Icon = require('./Icon');
const { Listbox, Option } = require('./accessibility/Listbox');

const { themeShape } = require('../constants/App');

const StyleUtils = require('../utils/Style');
const { deprecatePrimaryColor } = require('../utils/Deprecation');

// returns a function that takes a click event, stops it, then calls the callback
const haltEvent = callback => e => {
  e.preventDefault();
  e.stopPropagation();
  callback();
};

const optionShape = PropTypes.shape({
  displayValue: PropTypes.any.isRequired,
  icon: PropTypes.any,
  value: PropTypes.any.isRequired
});

class Select extends React.Component {
  static propTypes = {
    dropdownStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    elementRef: PropTypes.func,
    onChange: PropTypes.func,
    options: PropTypes.arrayOf(optionShape),
    optionsStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    optionStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    optionTextStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    placeholderText: PropTypes.string,
    primaryColor: PropTypes.string,
    scrimStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    selected: optionShape,
    selectedStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    theme: themeShape,
    valid: PropTypes.bool
  };

  static defaultProps = {
    onChange () {},
    options: [],
    placeholderText: 'Select One',
    valid: true
  };

  constructor (props) {
    super(props);

    this.state = {
      isOpen: false,
      selected: props.selected
    };
  }

  componentDidMount () {
    deprecatePrimaryColor(this.props);
  }

  componentWillReceiveProps (newProps) {
    if (!_isEqual(newProps.selected, this.props.selected)) {
      this.setState({ selected: newProps.selected });
    }
  }

  _handleKeyDown = (e) => {
    switch (keycode(e)) {
      case 'esc':
        e.preventDefault();
        e.stopPropagation();
        this._close();
        break;
      case 'enter':
      case 'space':
        if (this.state.isOpen) return;
        e.preventDefault();
        e.stopPropagation();
        this._open();
        break;
    }
  };

  _close = () => {
    this.setState({ isOpen: false });
    this.elementRef.focus();
  };

  _open = () => {
    this.setState({ isOpen: true });
  };

  _handleOptionClick = (option) => {
    this.setState({ selected: option }, () => {
      this._close();
      this.props.onChange(option);
    });
  };

  _scrollListDown = (nextIndex) => {
    const ul = ReactDOM.findDOMNode(this.optionList);
    const activeLi = ul.children[nextIndex];
    const heightFromTop = nextIndex * activeLi.clientHeight;

    if (heightFromTop > ul.clientHeight) {
      ul.scrollTop = activeLi.offsetTop - activeLi.clientHeight;
    }
  };

  _scrollListUp = (prevIndex) => {
    const ul = ReactDOM.findDOMNode(this.optionList);
    const activeLi = ul.children[prevIndex];
    const heightFromBottom = (this.props.options.length - prevIndex) * activeLi.clientHeight;

    if (heightFromBottom > ul.clientHeight) {
      ul.scrollTop = activeLi.offsetTop - activeLi.clientHeight;
    }
  };

  _renderScrim = (styles) => {
    if (this.state.isOpen) {
      return (

      );
    } else {
      return null;
    }
  };

  _renderOptions = (styles) => {
    if (this.state.isOpen) {
      if (this.props.children) {
        return (

            {typeof this.props.children === 'function' ? this.props.children({ onOptionClick: this._handleOptionClick }) : this.props.children}

        );
      } else {
        return (
          <Listbox
            aria-label={this.props.placeholderText}
            className='mx-select-options'
            ref={(ref) => this.optionList = ref}
            style={styles.options}
            useGlobalKeyHandler={true}
          >
            {this.props.options.map(option => {
              return (
                <Option
                  className='mx-select-option'
                  isSelected={_isEqual(option, this.state.selected)}
                  key={option.displayValue + option.value}
                  label={option.displayValue}
                  onClick={haltEvent(this._handleOptionClick.bind(null, option))}
                  style={Object.assign({},
                    styles.option,
                    this.props.optionStyle,
                    _isEqual(option, this.state.selected) ? styles.activeOption : null
                  )}
                >
                  {option.icon ? (
                    <Icon
                      size={20}
                      style={styles.optionIcon}
                      type={option.icon}
                    />
                  ) : null}

{option.displayValue}

                  {_isEqual(option, this.state.selected) ? <Icon size={20} type='check' /> : null }
                </Option>
              );
            })}
          </Listbox>
        );
      }
    } else {
      return null;
    }
  };

  render () {
    const theme = StyleUtils.mergeTheme(this.props.theme, this.props.primaryColor);
    const styles = this.styles(theme);
    const selected = this.state.selected || this.props.selected || { displayValue: this.props.placeholderText, value: '' };

    return (

 this.elementRef = ref}
          style={styles.component}
          tabIndex='0'
        >
          {this._renderScrim(styles)}

            {selected.icon ? (
              <Icon
                size={20}
                style={styles.optionIcon}
                type={selected.icon}
              />
            ) : null}

{selected.displayValue}

            <Icon
              size={20}
              type={this.state.isOpen ? 'caret-up' : 'caret-down'}
            />

          {this.props.options.length || this.props.children ? this._renderOptions(styles) : null}

    );
  }

  styles = (theme) => {
    const focusedOption = {
      backgroundColor: theme.Colors.PRIMARY,
      color: theme.Colors.WHITE,
      fill: theme.Colors.WHITE
    };

    return {
      component: Object.assign({},
        {
          backgroundColor: theme.Colors.WHITE,
          borderRadius: 3,
          border: '1px solid ' + theme.Colors.GRAY_300,
          cursor: 'pointer',
          fontFamily: theme.FontFamily,
          fontSize: theme.FontSizes.MEDIUM,
          padding: '8px 10px',
          position: 'relative',
          boxSizing: 'border-box'
        }, this.props.dropdownStyle),
      select: {
        position: 'absolute',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        width: '100%',
        opacity: 0
      },
      selected: Object.assign({},
        {
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'space-between',
          position: 'relative'
        }, this.props.selectedStyle),
      invalid: {
        borderColor: theme.Colors.DANGER
      },
      options: Object.assign({},
        {
          backgroundColor: theme.Colors.WHITE,
          border: '1px solid ' + theme.Colors.GRAY_300,
          borderRadius: '0 0 3px 3px',
          left: -1,
          right: -1,
          margin: '8px 0 0 0',
          padding: 0,
          minWidth: '100%',
          position: 'absolute',
          zIndex: 10,
          fontSize: 12,
          boxShadow: theme.ShadowHigh,
          boxSizing: 'border-box',
          maxHeight: 260,
          overflow: 'auto'
        }, this.props.optionsStyle),
      activeOption: {
        fill: theme.Colors.PRIMARY,
        color: theme.Colors.PRIMARY
      },
      option: {
        display: 'flex',
        alignItems: 'center',
        color: theme.Colors.GRAY_700,
        cursor: 'pointer',
        backgroundColor: theme.Colors.WHITE,
        outline: 'none',
        padding: 10,
        whiteSpace: 'nowrap',

        ':focus': focusedOption,
        ':hover': focusedOption
      },
      optionIcon: {
        marginRight: 5
      },
      optionText: Object.assign({},
        {
          flex: '1 0 0%'
        }, this.props.optionTextStyle),
      scrim: {
        position: 'fixed',
        zIndex: 9,
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
      }
    };
  };
}

module.exports = withTheme(Radium(Select));
<|endoftext|>


<commit_before>import React from 'react';
import {Text, View} from 'react-native';
import {NavigationContext} from 'navigation-react';
import {SharedElement as SharedElementWeb} from 'navigation-react-mobile';

class NavigationBarWeb extends React.Component {
  constructor(props) {
    super(props);
    this.setTitle = this.setTitle.bind(this);
  }
  componentDidMount() {
    this.props.stateNavigator.onNavigate(this.setTitle);
    document.title = this.props.title;
  }
  componentWillUnmount() {
    this.props.stateNavigator.offNavigate(this.setTitle);
  }
  setTitle(_oldState, _state, _data, _asyncData, stateContext) {
    const {crumbs} = this.props.stateNavigator.stateContext;
    if (stateContext.crumbs.length === crumbs.length)
      document.title = this.props.title;
  }
  render() {
    return (
      <Text
        accessibilityRole="heading"
        aria-level="1"
        style={{fontSize: 20, marginLeft: 15}}>
        {this.props.title}
      </Text>
    );
  }
}

const NavigationBar = props => (
  <NavigationContext.Consumer>
      {(navigationEvent) => <NavigationBarWeb stateNavigator={navigationEvent.stateNavigator} {...props} />}
  </NavigationContext.Consumer>
)

const RightBar = () => null;
const BarButton = () => null;

class SharedElement extends React.Component{
  setNativeProps() {
  }
  render() {
    const {style, transition, children, ...props} = this.props;
    return (
      <View style={style}>
        <SharedElementWeb {...props}>

{children}

        </SharedElementWeb>
      </View>
    );
  }
}

export { NavigationBar, RightBar, BarButton, SharedElement };
<commit_msg>Rendered navigation bar buttons on web<commit_after>import React from 'react';
import {Text, TouchableHighlight, View} from 'react-native';
import {NavigationContext} from 'navigation-react';
import {SharedElement as SharedElementWeb} from 'navigation-react-mobile';

class NavigationBarWeb extends React.Component {
  constructor(props) {
    super(props);
    this.setTitle = this.setTitle.bind(this);
  }
  componentDidMount() {
    this.props.stateNavigator.onNavigate(this.setTitle);
    document.title = this.props.title;
  }
  componentWillUnmount() {
    this.props.stateNavigator.offNavigate(this.setTitle);
  }
  setTitle(_oldState, _state, _data, _asyncData, stateContext) {
    const {crumbs} = this.props.stateNavigator.stateContext;
    if (stateContext.crumbs.length === crumbs.length)
      document.title = this.props.title;
  }
  render() {
    return (
      <View style={{
        marginLeft: 15,
        marginRight: 5,
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'flex-end'
      }}>
        <Text
          accessibilityRole="heading"
          aria-level="1"
          style={{fontSize: 20}}>
          {this.props.title}
        </Text>
        {this.props.children}
      </View>
    );
  }
}

const NavigationBar = props => (
  <NavigationContext.Consumer>
      {(navigationEvent) => <NavigationBarWeb stateNavigator={navigationEvent.stateNavigator} {...props} />}
  </NavigationContext.Consumer>
)

const RightBar = ({children}) => children;

const BarButton = ({title, onPress}) => (
  <TouchableHighlight
    underlayColor="#fff"
    onPress={onPress}>
    <Text>{title}</Text>
  </TouchableHighlight>

);

class SharedElement extends React.Component{
  setNativeProps() {
  }
  render() {
    const {style, transition, children, ...props} = this.props;
    return (
      <View style={style}>
        <SharedElementWeb {...props}>

{children}

        </SharedElementWeb>
      </View>
    );
  }
}

export { NavigationBar, RightBar, BarButton, SharedElement };
<|endoftext|>


<commit_before>/*
 * widget unit tests
 */
(function($) {

module('widget factory', {
	teardown: function() {
		delete $.ui.testWidget;
	}
});

test('widget creation', function() {
	var myPrototype = {
		_create: function() {},
		creationTest: function() {}
	};

	$.widget('ui.testWidget', myPrototype);
	ok($.isFunction($.ui.testWidget), 'constructor was created');
	equals('object', typeof $.ui.testWidget.prototype, 'prototype was created');
	equals($.ui.testWidget.prototype._create, myPrototype._create, 'create function is copied over');
	equals($.ui.testWidget.prototype.creationTest, myPrototype.creationTest, 'random function is copied over');
	equals($.ui.testWidget.prototype.option, $.Widget.prototype.option, 'option method copied over from base widget');
});

test('jQuery usage', function() {
	expect(10);

	var shouldInit = false;

	$.widget('ui.testWidget', {
		getterSetterVal: 5,
		_create: function() {
			ok(shouldInit, 'init called on instantiation');
		},
		methodWithParams: function(param1, param2) {
			ok(true, 'method called via .pluginName(methodName)');
			equals(param1, 'value1',
				'parameter passed via .pluginName(methodName, param)');
			equals(param2, 'value2',
				'multiple parameters passed via .pluginName(methodName, param, param)');

			return this;
		},
		getterSetterMethod: function(val) {
			if (val) {
				this.getterSetterVal = val;
			} else {
				return this.getterSetterVal;
			}
		}
	});

	shouldInit = true;
	var elem = $('

').testWidget();
	shouldInit = false;

	var instance = elem.data('testWidget');
	equals(typeof instance, 'object', 'instance stored in .data(pluginName)');
	equals(instance.element[0], elem[0], 'element stored on widget');
	var ret = elem.testWidget('methodWithParams', 'value1', 'value2');
	equals(ret, elem, 'jQuery object returned from method call');

	ret = elem.testWidget('getterSetterMethod');
	equals(ret, 5, 'getter/setter can act as getter');
	ret = elem.testWidget('getterSetterMethod', 30);
	equals(ret, elem, 'getter/setter method can be chainable');
	equals(instance.getterSetterVal, 30, 'getter/setter can act as setter');
});

test('direct usage', function() {
	expect(9);

	var shouldInit = false;

	$.widget('ui.testWidget', {
		getterSetterVal: 5,
		_create: function() {
			ok(shouldInit, 'init called on instantiation');
		},
		methodWithParams: function(param1, param2) {
			ok(true, 'method called dirctly');
			equals(param1, 'value1', 'parameter passed via direct call');
			equals(param2, 'value2', 'multiple parameters passed via direct call');

			return this;
		},
		getterSetterMethod: function(val) {
			if (val) {
				this.getterSetterVal = val;
			} else {
				return this.getterSetterVal;
			}
		}
	});

	var elem = $('

')[0];

	shouldInit = true;
	var instance = new $.ui.testWidget({}, elem);
	shouldInit = false;

	equals($(elem).data('testWidget'), instance, 'instance stored in .data(pluginName)');
	equals(instance.element[0], elem, 'element stored on widget');

	var ret = instance.methodWithParams('value1', 'value2');
	equals(ret, instance, 'plugin returned from method call');

	ret = instance.getterSetterMethod();
	equals(ret, 5, 'getter/setter can act as getter');
	instance.getterSetterMethod(30);
	equals(instance.getterSetterVal, 30, 'getter/setter can act as setter');
});

test('merge multiple option arguments', function() {
	expect(1);
	$.widget("ui.testWidget", {
		_create: function() {
			same(this.options, {
				disabled: false,
				option1: "value1",
				option2: "value2",
				option3: "value3",
				option4: {
					option4a: "valuea",
					option4b: "valueb"
				}
			});
		}
	});
	$("

").testWidget({
		option1: "valuex",
		option2: "valuex",
		option3: "value3",
		option4: {
			option4a: "valuex"
		}
	}, {
		option1: "value1",
		option2: "value2",
		option4: {
			option4b: "valueb"
		}
	}, {
		option4: {
			option4a: "valuea"
		}
	});
});

test("re-init", function() {
	var div = $( "

" ),
		actions = [];

	$.widget( "ui.testWidget", {
		_create: function() {
			actions.push( "create" );
		},
		_init: function() {
			actions.push( "init" );
		},
		_setOption: function( key, value ) {
			actions.push( "option" + key );
		}
	});

	actions = [];
	div.testWidget({ foo: "bar" });
	same( actions, [ "create", "init" ], "correct methods called on init" );

	actions = [];
	div.testWidget();
	same( actions, [ "init" ], "correct methods call on re-init" );

	actions = [];
	div.testWidget({ foo: "bar" });
	same( actions, [ "optionfoo", "init" ], "correct methods called on re-init with options" );
});

test( ".option() - getter", function() {
	$.widget( "ui.testWidget", {
		_create: function() {}
	});

	var div = $( "

" ).testWidget({
		foo: "bar",
		baz: 5,
		qux: [ "quux", "quuux" ]
	});

	same( div.testWidget( "option", "foo"), "bar", "single option - string" );
	same( div.testWidget( "option", "baz"), 5, "single option - number" );
	same( div.testWidget( "option", "qux"), [ "quux", "quuux" ],
		"single option - array" );

	var options = div.testWidget( "option" );
	same( options, {
		disabled: false,
		foo: "bar",
		baz: 5,
		qux: [ "quux", "quuux" ]
	}, "full options hash returned" );
	options.foo = "notbar";
	same( div.testWidget( "option", "foo"), "bar",
		"modifying returned options hash does not modify plugin instance" );
});

test( ".option() - setter", function() {
	var calls = [];
	$.widget( "ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			calls.push({
				key: key,
				val: val
			});
		}
	});
	var div = $( "

" ).testWidget();

	calls = [];
	div.testWidget( "option", "foo", "bar" );
	same( calls, [{ key: "foo", val: "bar" }],
		"_setOption called for single option" );

	calls = [];
	div.testWidget( "option", {
		bar: "qux",
		quux: "quuux"
	});
	same( calls, [
		{ key: "bar", val: "qux" },
		{ key: "quux", val: "quuux" }
	], "_setOption called with multiple options" );
});

test( ".enable()", function() {
	expect( 2 );
	$.widget("ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			same( key, "disabled", "_setOption called with disabled option" );
			same( val, false, "disabled set to false" );
		}
	});
	$( "

" ).testWidget().testWidget( "enable" );
});

test( ".disable()", function() {
	expect( 2 );
	$.widget("ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			same( key, "disabled", "_setOption called with disabled option" );
			same( val, true, "disabled set to true" );
		}
	});
	$( "

" ).testWidget().testWidget( "disable" );
});

test(".widget() - base", function() {
	$.widget("ui.testWidget", {
		_create: function() {}
	});
	var div = $("

").testWidget();
	same(div[0], div.testWidget("widget")[0]);
});

test(".widget() - overriden", function() {
	var wrapper = $("

");
	$.widget("ui.testWidget", {
		_create: function() {},
		widget: function() {
			return wrapper;
		}
	});
	same(wrapper[0], $("

").testWidget().testWidget("widget")[0]);
});

})(jQuery);
<commit_msg>Widget Tests: Coding standards.<commit_after>/*
 * widget unit tests
 */
(function( $ ) {

module( "widget factory", {
	teardown: function() {
		delete $.ui.testWidget;
	}
});

test( "widget creation", function() {
	var myPrototype = {
		_create: function() {},
		creationTest: function() {}
	};

	$.widget( "ui.testWidget", myPrototype );
	ok( $.isFunction( $.ui.testWidget ), "constructor was created" );
	equals( "object", typeof $.ui.testWidget.prototype, "prototype was created" );
	equals( $.ui.testWidget.prototype._create, myPrototype._create,
		"create function is copied over" );
	equals( $.ui.testWidget.prototype.creationTest, myPrototype.creationTest,
		"random function is copied over" );
	equals( $.ui.testWidget.prototype.option, $.Widget.prototype.option,
		"option method copied over from base widget" );
});

test( "jQuery usage", function() {
	expect( 10 );

	var shouldInit = false;

	$.widget( "ui.testWidget", {
		getterSetterVal: 5,
		_create: function() {
			ok( shouldInit, "init called on instantiation" );
		},
		methodWithParams: function( param1, param2 ) {
			ok( true, "method called via .pluginName(methodName)" );
			equals( param1, "value1",
				"parameter passed via .pluginName(methodName, param)" );
			equals( param2, "value2",
				"multiple parameters passed via .pluginName(methodName, param, param)" );

			return this;
		},
		getterSetterMethod: function( val ) {
			if ( val ) {
				this.getterSetterVal = val;
			} else {
				return this.getterSetterVal;
			}
		}
	});

	shouldInit = true;
	var elem = $( "

" ).testWidget();
	shouldInit = false;

	var instance = elem.data( "testWidget" );
	equals( typeof instance, "object", "instance stored in .data(pluginName)" );
	equals( instance.element[0], elem[0], "element stored on widget" );
	var ret = elem.testWidget( "methodWithParams", "value1", "value2" );
	equals( ret, elem, "jQuery object returned from method call" );

	ret = elem.testWidget( "getterSetterMethod" );
	equals( ret, 5, "getter/setter can act as getter" );
	ret = elem.testWidget( "getterSetterMethod", 30 );
	equals( ret, elem, "getter/setter method can be chainable" );
	equals( instance.getterSetterVal, 30, "getter/setter can act as setter" );
});

test( "direct usage", function() {
	expect( 9 );

	var shouldInit = false;

	$.widget( "ui.testWidget", {
		getterSetterVal: 5,
		_create: function() {
			ok( shouldInit, "init called on instantiation" );
		},
		methodWithParams: function( param1, param2 ) {
			ok( true, "method called dirctly" );
			equals( param1, "value1", "parameter passed via direct call" );
			equals( param2, "value2", "multiple parameters passed via direct call" );

			return this;
		},
		getterSetterMethod: function( val ) {
			if ( val ) {
				this.getterSetterVal = val;
			} else {
				return this.getterSetterVal;
			}
		}
	});

	var elem = $( "

" )[ 0 ];

	shouldInit = true;
	var instance = new $.ui.testWidget( {}, elem );
	shouldInit = false;

	equals( $( elem ).data( "testWidget" ), instance,
		"instance stored in .data(pluginName)" );
	equals( instance.element[ 0 ], elem, "element stored on widget" );

	var ret = instance.methodWithParams( "value1", "value2" );
	equals( ret, instance, "plugin returned from method call" );

	ret = instance.getterSetterMethod();
	equals( ret, 5, "getter/setter can act as getter" );
	instance.getterSetterMethod( 30 );
	equals( instance.getterSetterVal, 30, "getter/setter can act as setter" );
});

test("merge multiple option arguments", function() {
	expect( 1 );
	$.widget( "ui.testWidget", {
		_create: function() {
			same( this.options, {
				disabled: false,
				option1: "value1",
				option2: "value2",
				option3: "value3",
				option4: {
					option4a: "valuea",
					option4b: "valueb"
				}
			});
		}
	});
	$( "

" ).testWidget({
		option1: "valuex",
		option2: "valuex",
		option3: "value3",
		option4: {
			option4a: "valuex"
		}
	}, {
		option1: "value1",
		option2: "value2",
		option4: {
			option4b: "valueb"
		}
	}, {
		option4: {
			option4a: "valuea"
		}
	});
});

test( "re-init", function() {
	var div = $( "

" ),
		actions = [];

	$.widget( "ui.testWidget", {
		_create: function() {
			actions.push( "create" );
		},
		_init: function() {
			actions.push( "init" );
		},
		_setOption: function( key, value ) {
			actions.push( "option" + key );
		}
	});

	actions = [];
	div.testWidget({ foo: "bar" });
	same( actions, [ "create", "init" ], "correct methods called on init" );

	actions = [];
	div.testWidget();
	same( actions, [ "init" ], "correct methods call on re-init" );

	actions = [];
	div.testWidget({ foo: "bar" });
	same( actions, [ "optionfoo", "init" ], "correct methods called on re-init with options" );
});

test( ".option() - getter", function() {
	$.widget( "ui.testWidget", {
		_create: function() {}
	});

	var div = $( "

" ).testWidget({
		foo: "bar",
		baz: 5,
		qux: [ "quux", "quuux" ]
	});

	same( div.testWidget( "option", "foo"), "bar", "single option - string" );
	same( div.testWidget( "option", "baz"), 5, "single option - number" );
	same( div.testWidget( "option", "qux"), [ "quux", "quuux" ],
		"single option - array" );

	var options = div.testWidget( "option" );
	same( options, {
		disabled: false,
		foo: "bar",
		baz: 5,
		qux: [ "quux", "quuux" ]
	}, "full options hash returned" );
	options.foo = "notbar";
	same( div.testWidget( "option", "foo"), "bar",
		"modifying returned options hash does not modify plugin instance" );
});

test( ".option() - setter", function() {
	var calls = [];
	$.widget( "ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			calls.push({
				key: key,
				val: val
			});
		}
	});
	var div = $( "

" ).testWidget();

	calls = [];
	div.testWidget( "option", "foo", "bar" );
	same( calls, [{ key: "foo", val: "bar" }],
		"_setOption called for single option" );

	calls = [];
	div.testWidget( "option", {
		bar: "qux",
		quux: "quuux"
	});
	same( calls, [
		{ key: "bar", val: "qux" },
		{ key: "quux", val: "quuux" }
	], "_setOption called with multiple options" );
});

test( ".enable()", function() {
	expect( 2 );
	$.widget( "ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			same( key, "disabled", "_setOption called with disabled option" );
			same( val, false, "disabled set to false" );
		}
	});
	$( "

" ).testWidget().testWidget( "enable" );
});

test( ".disable()", function() {
	expect( 2 );
	$.widget( "ui.testWidget", {
		_create: function() {},
		_setOption: function( key, val ) {
			same( key, "disabled", "_setOption called with disabled option" );
			same( val, true, "disabled set to true" );
		}
	});
	$( "

" ).testWidget().testWidget( "disable" );
});

test( ".widget() - base", function() {
	$.widget( "ui.testWidget", {
		_create: function() {}
	});
	var div = $( "

" ).testWidget();
	same( div[0], div.testWidget( "widget" )[0]);
});

test( ".widget() - overriden", function() {
	var wrapper = $( "

" );
	$.widget( "ui.testWidget", {
		_create: function() {},
		widget: function() {
			return wrapper;
		}
	});
	same( wrapper[0], $( "

" ).testWidget().testWidget( "widget" )[0] );
});

})( jQuery );
<|endoftext|>


<commit_before>describe('Promise', function() {
    beforeEach(function(done) {
        Ext.Loader.setPath('Ext.promise', './lib');
        Ext.application({
            name: 'Promise',

            launch: function() {
                Ext.require('Ext.promise.Deferred', function() {
                    done();
                });
            }
        });
    });

    describe('basics', function() {
        it('should be requirable', function(done) {
            Ext.require('Ext.promise.Deferred', function() {
                done();
            });
        });

        it('should exist', function() {
            expect(Ext.promise.Deferred).toBeDefined();
        });

    });

    describe('#then', function() {
        var deferred, callback, spy;

        beforeEach(function() {
            deferred = Ext.create('Ext.promise.Deferred');
            callback = jasmine.createSpy('callback');
            spy = jasmine.createSpyObj('spy', ['after']);
            spy.callback = function() {
                this.after();
            };
        });

        it('should have a function then', function() {
            expect(deferred.promise.then).toBeDefined();
        });

        it('should call the callback, on resolve', function() {
            deferred.promise.then(callback).fail(function(){});
            deferred.resolve();
            expect(callback).toHaveBeenCalled();
        });

        it('should call the callback, only once', function() {
            deferred.resolve();
            deferred.promise.then(callback).fail(function(){});
            deferred.resolve();
            deferred.resolve();
            expect(callback.calls.count()).toEqual(1);
        });

        it('should call it with the scope', function() {
            deferred.promise.then(spy.callback, spy);
            deferred.resolve();
            expect(spy.after).toHaveBeenCalled();
        });

        it('should call it with the scope if there is an onRejected callback', function() {
            deferred.promise.then(spy.callback, Ext.emptyFn, spy);
            deferred.resolve();
            expect(spy.after).toHaveBeenCalled();
        });

        it('should call then even if the promise was resolved before', function() {
            deferred.resolve();
            deferred.promise.then(callback);
            expect(callback).toHaveBeenCalled();
        });

        it('should call then when a async callback is finish', function(done) {
            setTimeout(function() {
                deferred.resolve();
                expect(callback).toHaveBeenCalled();
                done();
            }, 500);
            deferred.promise.then(callback);
        });

        it('should get the resolved value', function() {
            deferred.resolve('foo');
            deferred.promise.then(callback);
            expect(callback).toHaveBeenCalledWith('foo');
        });

        it('should be chainable', function(done) {
            deferred.promise.then(callback).then(callback).then(function() {
                expect(callback.calls.count()).toEqual(2);
                done();
            });
            deferred.resolve();
        });

        xit('should resolve new promise in then', function(done) {
            deferred.promise.then(function() {
                var deferred2 = Ext.create('Ext.promise.Deferred');
                setTimeout(function() {
                    deferred2.resolve('bar');
                }, 500);
                return deferred2.promise;
            }).then(function(value) {
                expect(value).toEqual('bar');
                done();
            });
            deferred.resolve();
        });

    });

    describe('#fail', function() {
        var deferred, callback, spy;
        beforeEach(function() {
            deferred = Ext.create('Ext.promise.Deferred');
            callback = jasmine.createSpy('callback');
            spy = jasmine.createSpyObj('spy', ['after']);
            spy.callback = function() {
                this.after();
            };
        });

        it('should have a function fail', function() {
            expect(deferred.promise.fail).toBeDefined();
        });

        it('should call the callback, on reject', function() {
            deferred.promise.then(Ext.emptyFn).fail(callback);
            deferred.reject();
            expect(callback).toHaveBeenCalled();
        });

        it('should call the onRejected, given to then', function() {
            deferred.promise.then(Ext.emptyFn, callback);
            deferred.reject();
            expect(callback).toHaveBeenCalled();
        });

        it('should not call then callback on reject', function() {
            deferred.promise.then(callback);
            deferred.reject();
            expect(callback).not.toHaveBeenCalled();
        });

        it('should only be called once', function() {
            deferred.reject();
            deferred.promise.then(Ext.emptyFn, callback);
            deferred.reject();
            deferred.reject();
            expect(callback.calls.count()).toEqual(1);
        });

        xit('should resolve new promise in fail', function(done) {
            deferred.promise.fail(function() {
                var deferred2 = Ext.create('Ext.promise.Deferred');
                setTimeout(function() {
                    deferred2.resolve('bar');
                }, 500);
                return deferred2.promise;
            }).then(function(value) {
                expect(value).toEqual('bar');
                done();
            });
            deferred.reject();
        });

    });

    describe('mixin', function() {
        it('should be possible to use it as a mixin', function(done) {
            var munchkin;
            Ext.define('munchkin', {
                mixins: ['Ext.promise.Deferred'],
                loot: function() {
                    var deferred = this.deferred(),
                        me = this;
                    setTimeout(function() {
                        me.resolve('a sword');
                    }, 400);
                    return deferred.promise;
                }
            });
            munchkin = Ext.create('munchkin');
            munchkin.loot()
                .then(function(theLoot) {
                    expect(theLoot).toEqual('a sword');
                    done();
                });
        });
    });
});
<commit_msg>adds some more tests<commit_after>describe('Promise', function() {
    beforeEach(function(done) {
        Ext.Loader.setPath('Ext.promise', './lib');
        Ext.application({
            name: 'Promise',

            launch: function() {
                Ext.require('Ext.promise.Deferred', function() {
                    done();
                });
            }
        });
    });

    describe('basics', function() {
        it('should be requirable', function(done) {
            Ext.require('Ext.promise.Deferred', function() {
                done();
            });
        });

        it('should exist', function() {
            expect(Ext.promise.Deferred).toBeDefined();
        });

    });

    describe('#then', function() {
        var deferred, onFulfilled, onRejected, spy;

        beforeEach(function() {
            deferred = Ext.create('Ext.promise.Deferred');
            onFulfilled = jasmine.createSpy('onFulfilled');
            onRejected = jasmine.createSpy('onRejected');
            spy = jasmine.createSpyObj('spy', ['after']);
            spy.onFulfilled = function() {
                this.after();
            };
        });

        it('should have a function then', function() {
            expect(deferred.promise.then).toBeDefined();
        });

        it('should call the onFulfilled, on resolve', function() {
            deferred.promise.then(onFulfilled).fail(function(){});
            deferred.resolve();
            expect(onFulfilled).toHaveBeenCalled();
        });

        it('should call the onFulfilled, only once', function() {
            deferred.resolve();
            deferred.promise.then(onFulfilled).fail(function(){});
            deferred.resolve();
            deferred.resolve();
            expect(onFulfilled.calls.count()).toEqual(1);
        });

        it('should call it with the scope', function() {
            deferred.promise.then(spy.onFulfilled, spy);
            deferred.resolve();
            expect(spy.after).toHaveBeenCalled();
        });

        it('should call it with the scope if there is an onRejected callback', function() {
            deferred.promise.then(spy.onFulfilled, Ext.emptyFn, spy);
            deferred.resolve();
            expect(spy.after).toHaveBeenCalled();
        });

        it('should call then even if the promise was resolved before', function() {
            deferred.resolve();
            deferred.promise.then(onFulfilled);
            expect(onFulfilled).toHaveBeenCalled();
        });

        it('should call then when a async onFulfilled is finish', function(done) {
            setTimeout(function() {
                deferred.resolve();
                expect(onFulfilled).toHaveBeenCalled();
                done();
            }, 500);
            deferred.promise.then(onFulfilled);
        });

        it('should get the resolved value', function() {
            deferred.resolve('foo');
            deferred.promise.then(onFulfilled);
            expect(onFulfilled).toHaveBeenCalledWith('foo');
        });

        it('should be chainable', function(done) {
            deferred.promise.fail(onRejected).then(onFulfilled).then(onFulfilled).fail(onRejected).then(function() {
                expect(onFulfilled.calls.count()).toEqual(2);
                expect(onRejected.calls.count()).toEqual(0);
                done();
            });
            deferred.resolve();
        });

        xit('should resolve new promise in then', function(done) {
            deferred.promise.then(function() {
                var deferred2 = Ext.create('Ext.promise.Deferred');
                setTimeout(function() {
                    deferred2.resolve('bar');
                }, 500);
                return deferred2.promise;
            }).then(function(value) {
                expect(value).toEqual('bar');
                done();
            });
            deferred.resolve();
        });

    });

    describe('#fail', function() {
        var deferred, callback, spy;
        beforeEach(function() {
            deferred = Ext.create('Ext.promise.Deferred');
            callback = jasmine.createSpy('callback');
            spy = jasmine.createSpyObj('spy', ['after']);
            spy.callback = function() {
                this.after();
            };
        });

        it('should have a function fail', function() {
            expect(deferred.promise.fail).toBeDefined();
        });

        it('should call the callback, on reject', function() {
            deferred.promise.then(Ext.emptyFn).fail(callback);
            deferred.reject();
            expect(callback).toHaveBeenCalled();
        });

        it('should call the onRejected, given to then', function() {
            deferred.promise.then(Ext.emptyFn, callback);
            deferred.reject();
            expect(callback).toHaveBeenCalled();
        });

        it('should not call then callback on reject', function() {
            deferred.promise.then(callback);
            deferred.reject();
            expect(callback).not.toHaveBeenCalled();
        });

        it('should only be called once', function() {
            deferred.reject();
            deferred.promise.then(Ext.emptyFn, callback);
            deferred.reject();
            deferred.reject();
            expect(callback.calls.count()).toEqual(1);
        });

        xit('should resolve new promise in fail', function(done) {
            deferred.promise.fail(function() {
                var deferred2 = Ext.create('Ext.promise.Deferred');
                setTimeout(function() {
                    deferred2.resolve('bar');
                }, 500);
                return deferred2.promise;
            }).then(function(value) {
                expect(value).toEqual('bar');
                done();
            });
            deferred.reject();
        });

    });

    describe('mixin', function() {
        it('should be possible to use it as a mixin', function(done) {
            var munchkin;
            Ext.define('munchkin', {
                mixins: ['Ext.promise.Deferred'],
                loot: function() {
                    var deferred = this.deferred(),
                        me = this;
                    setTimeout(function() {
                        me.resolve('a sword');
                    }, 400);
                    return deferred.promise;
                }
            });
            munchkin = Ext.create('munchkin');
            munchkin.loot()
                .then(function(theLoot) {
                    expect(theLoot).toEqual('a sword');
                    done();
                });
        });
    });
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

const {extractSummary} = require('../utils');
const path = require('path');
const runJest = require('../runJest');
const skipOnWindows = require('skipOnWindows');

const dir = path.resolve(__dirname, '../failures');

skipOnWindows.suite();

test('throwing not Error objects', () => {
  let stderr;
  stderr = runJest(dir, ['throw-number-test.js']).stderr;
  expect(extractSummary(stderr)).toMatchSnapshot();
  stderr = runJest(dir, ['throw-string-test.js']).stderr;
  expect(extractSummary(stderr)).toMatchSnapshot();
  stderr = runJest(dir, ['throw-object-test.js']).stderr;
  expect(extractSummary(stderr)).toMatchSnapshot();
  stderr = runJest(dir, ['assertion-count-test.js']).stderr;
  expect(extractSummary(stderr)).toMatchSnapshot();
});
<commit_msg>Strip inconsistent stack trace lines (#3222)<commit_after>/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

const {extractSummary} = require('../utils');
const path = require('path');
const runJest = require('../runJest');
const skipOnWindows = require('skipOnWindows');

const dir = path.resolve(__dirname, '../failures');

skipOnWindows.suite();

// Some node versions add an extra line to the error stack trace. This makes
// snapshot tests fail on different machines. This function makes sure
// this extra line is always removed.
const stripInconsistentStackLines = summary => {
  summary.rest = summary.rest.replace(/\n^.*process\._tickCallback.*$/gm, '');
  return summary;
};

test('throwing not Error objects', () => {
  let stderr;
  stderr = runJest(dir, ['throw-number-test.js']).stderr;
  expect(stripInconsistentStackLines(
    extractSummary(stderr),
  )).toMatchSnapshot();
  stderr = runJest(dir, ['throw-string-test.js']).stderr;
  expect(stripInconsistentStackLines(
    extractSummary(stderr),
  )).toMatchSnapshot();
  stderr = runJest(dir, ['throw-object-test.js']).stderr;
  expect(stripInconsistentStackLines(
    extractSummary(stderr),
  )).toMatchSnapshot();
  stderr = runJest(dir, ['assertion-count-test.js']).stderr;
  expect(stripInconsistentStackLines(
    extractSummary(stderr),
  )).toMatchSnapshot();
});
<|endoftext|>