<commit_before>const entities = require('entities');
const defaults = {
    fg: '#FFF',
    bg: '#000',
    newline: false,
    escapeXML: false,
    stream: false,
    colors: getDefaultColors()
};

function getDefaultColors() {
    const colors = {
        0: '#000',
        1: '#A00',
        2: '#0A0',
        3: '#A50',
        4: '#00A',
        5: '#A0A',
        6: '#0AA',
        7: '#AAA',
        8: '#555',
        9: '#F55',
        10: '#5F5',
        11: '#FF5',
        12: '#55F',
        13: '#F5F',
        14: '#5FF',
        15: '#FFF'
    };

    range(0, 5).forEach(red => {
        range(0, 5).forEach(green => {
            range(0, 5).forEach(blue => setStyleColor(red, green, blue, colors));
        });
    });

    range(0, 23).forEach(function (gray) {
        const c = gray + 232;
        const l = toHexString(gray * 10 + 8);

        colors[c] = '#' + l + l + l;
    });

    return colors;
}

/**
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 * @param {object} colors
 */
function setStyleColor(red, green, blue, colors) {
    const c = 16 + (red * 36) + (green * 6) + blue;
    const r = red > 0 ? red * 40 + 55 : 0;
    const g = green > 0 ? green * 40 + 55 : 0;
    const b = blue > 0 ? blue * 40 + 55 : 0;

    colors[c] = toColorHexString([r, g, b]);
}

/**
 * Converts from a number like 15 to a hex string like 'F'
 * @param {number} num
 * @returns {string}
 */
function toHexString(num) {
    let str = num.toString(16);

    while (str.length < 2) {
        str = '0' + str;
    }

    return str;
}

/**
 * Converts from an array of numbers like [15, 15, 15] to a hex string like 'FFF'
 * @param {[red, green, blue]} ref
 * @returns {string}
 */
function toColorHexString(ref) {
    const results = [];

    for (const r of ref) {
        results.push(toHexString(r));
    }

    return '#' + results.join('');
}

/**
 * @param {Array} stack
 * @param {string} token
 * @param {*} data
 * @param {object} options
 */
function generateOutput(stack, token, data, options) {
    let result;

    if (token === 'text') {
        result = pushText(data, options);
    } else if (token === 'display') {
        result = handleDisplay(stack, data, options);
    } else if (token === 'xterm256') {
        result = pushForegroundColor(stack, options.colors[data]);
    } else if (token === 'rgb') {
        result = handleRgb(stack, data);
    }

    return result;
}

/**
 * @param {Array} stack
 * @param {string} data
 * @returns {*}
 */
function handleRgb(stack, data) {
    data = data.substring(2).slice(0, -1);
    const operation = +data.substr(0,2);

    const color = data.substring(5).split(';');
    const rgb = color.map(function(value) {
        return ('0' + Number(value).toString(16)).substr(-2);
    }).join('');

    return pushStyle(stack, (operation === 38 ? 'color:#' : 'background-color:#') + rgb);
}

/**
 * @param {Array} stack
 * @param {number} code
 * @param {object} options
 * @returns {*}
 */
function handleDisplay(stack, code, options) {
    code = parseInt(code, 10);

    const codeMap = {
        '-1': () => '
',
        0: () => stack.length && resetStyles(stack),
        1: () => pushTag(stack, 'b'),
        3: () => pushTag(stack, 'i'),
        4: () => pushTag(stack, 'u'),
        8: () => pushStyle(stack, 'display:none'),
        9: () => pushTag(stack, 'strike'),
        22: () => pushStyle(stack, 'font-weight:normal;text-decoration:none;font-style:normal'),
        23: () => closeTag(stack, 'i'),
        24: () => closeTag(stack, 'u'),
        39: () => pushForegroundColor(stack, options.fg),
        49: () => pushBackgroundColor(stack, options.bg),
        53: () => pushStyle(stack, 'text-decoration:overline')
    };

    let result;
    if (codeMap[code]) {
        result = codeMap[code]();
    } else if (4 < code && code < 7) {
        result = pushTag(stack, 'blink');
    } else if (29 < code && code < 38) {
        result = pushForegroundColor(stack, options.colors[code - 30]);
    } else if ((39 < code && code < 48)) {
        result = pushBackgroundColor(stack, options.colors[code - 40]);
    } else if ((89 < code && code < 98)) {
        result = pushForegroundColor(stack, options.colors[8 + (code - 90)]);
    } else if ((99 < code && code < 108)) {
        result = pushBackgroundColor(stack, options.colors[8 + (code - 100)]);
    }

    return result;
}

/**
 * Clear all the styles
 * @returns {string}
 */
function resetStyles(stack) {
    const stackClone = stack.slice(0);

    stack.length = 0;

    return stackClone.reverse().map(function (tag) {
        return '</' + tag + '>';
    }).join('');
}

/**
 * Creates an array of numbers ranging from low to high
 * @param {number} low
 * @param {number} high
 * @returns {Array}
 * @example range(3, 7); // creates [3, 4, 5, 6, 7]
 */
function range(low, high) {
    const results = [];

    for (let j = low; j <= high; j++) {
        results.push(j);
    }

    return results;
}

/**
 * Returns a new function that is true if value is NOT the same category
 * @param {string} category
 * @returns {function}
 */
function notCategory(category) {
    return function (e) {
        return (category === null || e.category !== category) && category !== 'all';
    };
}

/**
 * Converts a code into an ansi token type
 * @param {number} code
 * @returns {string}
 */
function categoryForCode(code) {
    code = parseInt(code, 10);
    let result = null;

    if (code === 0) {
        result = 'all';
    } else if (code === 1) {
        result = 'bold';
    } else if ((2 < code && code < 5)) {
        result = 'underline';
    } else if ((4 < code && code < 7)) {
        result = 'blink';
    } else if (code === 8) {
        result = 'hide';
    } else if (code === 9) {
        result = 'strike';
    } else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) {
        result = 'foreground-color';
    } else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) {
        result = 'background-color';
    }

    return result;
}

/**
 * @param {string} text
 * @param {object} options
 * @returns {string}
 */
function pushText(text, options) {
    if (options.escapeXML) {
        return entities.encodeXML(text);
    }

    return text;
}

/**
 * @param {Array} stack
 * @param {string} tag
 * @param {string} [style='']
 * @returns {string}
 */
function pushTag(stack, tag, style) {
    if (!style) {
        style = '';
    }

    stack.push(tag);

    return ['<' + tag, (style ? ' style="' + style + '"' : void 0), '>'].join('');
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function pushStyle (stack, style) {
    return pushTag(stack, 'span', style);
}

function pushForegroundColor(stack, color) {
    return pushTag(stack, 'span', 'color:' + color);
}

function pushBackgroundColor(stack, color) {
    return pushTag(stack, 'span', 'background-color:' + color);
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function closeTag(stack, style) {
    let last;

    if (stack.slice(-1)[0] === style) {
        last = stack.pop();
    }

    if (last) {
        return '</' + style + '>';
    }
}

/**
 * @param {string} text
 * @param {object} options
 * @param {function} callback
 * @returns {Array}
 */
function tokenize(text, options, callback) {
    let ansiMatch = false;
    const ansiHandler = 3;

    function remove() {
        return '';
    }

    function removeXterm256(m, g1) {
        callback('xterm256', g1);
        return '';
    }

    function newline(m) {
        if (options.newline) {
            callback('display', -1);
        } else {
            callback('text', m);
        }

        return '';
    }

    function ansiMess(m, g1) {
        ansiMatch = true;
        if (g1.trim().length === 0) {
            g1 = '0';
        }

        g1 = g1.trimRight(';').split(';');

        for (const g of g1) {
            callback('display', g);
        }

        return '';
    }

    function realText(m) {
        callback('text', m);

        return '';
    }

    function rgb(m) {
        callback('rgb', m);

        return '';
    }

    /* eslint no-control-regex:0 */
    const tokens = [{
        pattern: /^\x08+/,
        sub: remove
    }, {
        pattern: /^\x1b\[[012]?K/,
        sub: remove
    }, {
        pattern: /^\x1b\[\(B/,
        sub: remove
    }, {
        pattern: /^\x1b\[[34]8;2;\d+;\d+;\d+m/,
        sub: rgb
    }, {
        pattern: /^\x1b\[38;5;(\d+)m/,
        sub: removeXterm256
    }, {
        pattern: /^\n/,
        sub: newline
    }, {
        pattern: /^\x1b\[((?:\d{1,3};?)+|)m/,
        sub: ansiMess
    }, {
        // CSI n J
        // ED - Erase in Display Clears part of the screen.
        // If n is 0 (or missing), clear from cursor to end of screen.
        // If n is 1, clear from cursor to beginning of the screen.
        // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
        // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer
        //   (this feature was added for xterm and is supported by other terminal applications).
        pattern: /^\x1b\[\d?J/,
        sub: remove
    }, {
        // CSI n ; m f
        // HVP - Horizontal Vertical Position Same as CUP
        pattern: /^\x1b\[\d{0,3};\d{0,3}f/,
        sub: remove
    }, {
        // catch-all for CSI sequences?
        pattern: /^\x1b\[?[\d;]{0,3}/,
        sub: remove
    }, {
        pattern: /^([^\x1b\x08\n]+)/,
        sub: realText
    }];

    function process(handler, i) {
        if (i > ansiHandler && ansiMatch) {
            return;
        }

        ansiMatch = false;

        text = text.replace(handler.pattern, handler.sub);
    }

    const results1 = [];
    let {length} = text;

    outer:
    while (length > 0) {
        for (let i = 0, o = 0, len = tokens.length; o < len; i = ++o) {
            const handler = tokens[i];
            process(handler, i);

            if (text.length !== length) {
                // We matched a token and removed it from the text. We need to
                // start matching *all* tokens against the new text.
                length = text.length;
                continue outer;
            }
        }

        if (text.length === length) {
            break;
        }
        results1.push(0);

        length = text.length;
    }

    return results1;
}

/**
 * If streaming, then the stack is "sticky"
 *
 * @param {Array} stickyStack
 * @param {string} token
 * @param {*} data
 * @returns {Array}
 */
function updateStickyStack(stickyStack, token, data) {
    if (token !== 'text') {
        stickyStack = stickyStack.filter(notCategory(categoryForCode(data)));
        stickyStack.push({token, data, category: categoryForCode(data)});
    }

    return stickyStack;
}

class Filter {
    constructor (options) {
        options = options || {};

        if (options.colors) {
            options.colors = Object.assign({}, defaults.colors, options.colors);
        }

        this.options = Object.assign({}, defaults, options);
        this.stack = [];
        this.stickyStack = [];
    }
    toHtml (input) {
        input = typeof input === 'string' ? [input] : input;
        const {stack, options} = this;
        const buf = [];

        this.stickyStack.forEach(element => {
            const output = generateOutput(stack, element.token, element.data, options);

            if (output) {
                buf.push(output);
            }
        });

        tokenize(input.join(''), options, (token, data) => {
            const output = generateOutput(stack, token, data, options);

            if (output) {
                buf.push(output);
            }

            if (options.stream) {
                this.stickyStack = updateStickyStack(this.stickyStack, token, data);
            }
        });

        if (stack.length) {
            buf.push(resetStyles(stack));
        }

        return buf.join('');
    }
}

module.exports = Filter;
<commit_msg>Add JSDoc types to Filter<commit_after>const entities = require('entities');
const defaults = {
    fg: '#FFF',
    bg: '#000',
    newline: false,
    escapeXML: false,
    stream: false,
    colors: getDefaultColors()
};

function getDefaultColors() {
    const colors = {
        0: '#000',
        1: '#A00',
        2: '#0A0',
        3: '#A50',
        4: '#00A',
        5: '#A0A',
        6: '#0AA',
        7: '#AAA',
        8: '#555',
        9: '#F55',
        10: '#5F5',
        11: '#FF5',
        12: '#55F',
        13: '#F5F',
        14: '#5FF',
        15: '#FFF'
    };

    range(0, 5).forEach(red => {
        range(0, 5).forEach(green => {
            range(0, 5).forEach(blue => setStyleColor(red, green, blue, colors));
        });
    });

    range(0, 23).forEach(function (gray) {
        const c = gray + 232;
        const l = toHexString(gray * 10 + 8);

        colors[c] = '#' + l + l + l;
    });

    return colors;
}

/**
 * @param {number} red
 * @param {number} green
 * @param {number} blue
 * @param {object} colors
 */
function setStyleColor(red, green, blue, colors) {
    const c = 16 + (red * 36) + (green * 6) + blue;
    const r = red > 0 ? red * 40 + 55 : 0;
    const g = green > 0 ? green * 40 + 55 : 0;
    const b = blue > 0 ? blue * 40 + 55 : 0;

    colors[c] = toColorHexString([r, g, b]);
}

/**
 * Converts from a number like 15 to a hex string like 'F'
 * @param {number} num
 * @returns {string}
 */
function toHexString(num) {
    let str = num.toString(16);

    while (str.length < 2) {
        str = '0' + str;
    }

    return str;
}

/**
 * Converts from an array of numbers like [15, 15, 15] to a hex string like 'FFF'
 * @param {[red, green, blue]} ref
 * @returns {string}
 */
function toColorHexString(ref) {
    const results = [];

    for (const r of ref) {
        results.push(toHexString(r));
    }

    return '#' + results.join('');
}

/**
 * @param {Array} stack
 * @param {string} token
 * @param {*} data
 * @param {object} options
 */
function generateOutput(stack, token, data, options) {
    let result;

    if (token === 'text') {
        result = pushText(data, options);
    } else if (token === 'display') {
        result = handleDisplay(stack, data, options);
    } else if (token === 'xterm256') {
        result = pushForegroundColor(stack, options.colors[data]);
    } else if (token === 'rgb') {
        result = handleRgb(stack, data);
    }

    return result;
}

/**
 * @param {Array} stack
 * @param {string} data
 * @returns {*}
 */
function handleRgb(stack, data) {
    data = data.substring(2).slice(0, -1);
    const operation = +data.substr(0,2);

    const color = data.substring(5).split(';');
    const rgb = color.map(function(value) {
        return ('0' + Number(value).toString(16)).substr(-2);
    }).join('');

    return pushStyle(stack, (operation === 38 ? 'color:#' : 'background-color:#') + rgb);
}

/**
 * @param {Array} stack
 * @param {number} code
 * @param {object} options
 * @returns {*}
 */
function handleDisplay(stack, code, options) {
    code = parseInt(code, 10);

    const codeMap = {
        '-1': () => '
',
        0: () => stack.length && resetStyles(stack),
        1: () => pushTag(stack, 'b'),
        3: () => pushTag(stack, 'i'),
        4: () => pushTag(stack, 'u'),
        8: () => pushStyle(stack, 'display:none'),
        9: () => pushTag(stack, 'strike'),
        22: () => pushStyle(stack, 'font-weight:normal;text-decoration:none;font-style:normal'),
        23: () => closeTag(stack, 'i'),
        24: () => closeTag(stack, 'u'),
        39: () => pushForegroundColor(stack, options.fg),
        49: () => pushBackgroundColor(stack, options.bg),
        53: () => pushStyle(stack, 'text-decoration:overline')
    };

    let result;
    if (codeMap[code]) {
        result = codeMap[code]();
    } else if (4 < code && code < 7) {
        result = pushTag(stack, 'blink');
    } else if (29 < code && code < 38) {
        result = pushForegroundColor(stack, options.colors[code - 30]);
    } else if ((39 < code && code < 48)) {
        result = pushBackgroundColor(stack, options.colors[code - 40]);
    } else if ((89 < code && code < 98)) {
        result = pushForegroundColor(stack, options.colors[8 + (code - 90)]);
    } else if ((99 < code && code < 108)) {
        result = pushBackgroundColor(stack, options.colors[8 + (code - 100)]);
    }

    return result;
}

/**
 * Clear all the styles
 * @returns {string}
 */
function resetStyles(stack) {
    const stackClone = stack.slice(0);

    stack.length = 0;

    return stackClone.reverse().map(function (tag) {
        return '</' + tag + '>';
    }).join('');
}

/**
 * Creates an array of numbers ranging from low to high
 * @param {number} low
 * @param {number} high
 * @returns {Array}
 * @example range(3, 7); // creates [3, 4, 5, 6, 7]
 */
function range(low, high) {
    const results = [];

    for (let j = low; j <= high; j++) {
        results.push(j);
    }

    return results;
}

/**
 * Returns a new function that is true if value is NOT the same category
 * @param {string} category
 * @returns {function}
 */
function notCategory(category) {
    return function (e) {
        return (category === null || e.category !== category) && category !== 'all';
    };
}

/**
 * Converts a code into an ansi token type
 * @param {number} code
 * @returns {string}
 */
function categoryForCode(code) {
    code = parseInt(code, 10);
    let result = null;

    if (code === 0) {
        result = 'all';
    } else if (code === 1) {
        result = 'bold';
    } else if ((2 < code && code < 5)) {
        result = 'underline';
    } else if ((4 < code && code < 7)) {
        result = 'blink';
    } else if (code === 8) {
        result = 'hide';
    } else if (code === 9) {
        result = 'strike';
    } else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) {
        result = 'foreground-color';
    } else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) {
        result = 'background-color';
    }

    return result;
}

/**
 * @param {string} text
 * @param {object} options
 * @returns {string}
 */
function pushText(text, options) {
    if (options.escapeXML) {
        return entities.encodeXML(text);
    }

    return text;
}

/**
 * @param {Array} stack
 * @param {string} tag
 * @param {string} [style='']
 * @returns {string}
 */
function pushTag(stack, tag, style) {
    if (!style) {
        style = '';
    }

    stack.push(tag);

    return ['<' + tag, (style ? ' style="' + style + '"' : void 0), '>'].join('');
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function pushStyle (stack, style) {
    return pushTag(stack, 'span', style);
}

function pushForegroundColor(stack, color) {
    return pushTag(stack, 'span', 'color:' + color);
}

function pushBackgroundColor(stack, color) {
    return pushTag(stack, 'span', 'background-color:' + color);
}

/**
 * @param {Array} stack
 * @param {string} style
 * @returns {string}
 */
function closeTag(stack, style) {
    let last;

    if (stack.slice(-1)[0] === style) {
        last = stack.pop();
    }

    if (last) {
        return '</' + style + '>';
    }
}

/**
 * @param {string} text
 * @param {object} options
 * @param {function} callback
 * @returns {Array}
 */
function tokenize(text, options, callback) {
    let ansiMatch = false;
    const ansiHandler = 3;

    function remove() {
        return '';
    }

    function removeXterm256(m, g1) {
        callback('xterm256', g1);
        return '';
    }

    function newline(m) {
        if (options.newline) {
            callback('display', -1);
        } else {
            callback('text', m);
        }

        return '';
    }

    function ansiMess(m, g1) {
        ansiMatch = true;
        if (g1.trim().length === 0) {
            g1 = '0';
        }

        g1 = g1.trimRight(';').split(';');

        for (const g of g1) {
            callback('display', g);
        }

        return '';
    }

    function realText(m) {
        callback('text', m);

        return '';
    }

    function rgb(m) {
        callback('rgb', m);

        return '';
    }

    /* eslint no-control-regex:0 */
    const tokens = [{
        pattern: /^\x08+/,
        sub: remove
    }, {
        pattern: /^\x1b\[[012]?K/,
        sub: remove
    }, {
        pattern: /^\x1b\[\(B/,
        sub: remove
    }, {
        pattern: /^\x1b\[[34]8;2;\d+;\d+;\d+m/,
        sub: rgb
    }, {
        pattern: /^\x1b\[38;5;(\d+)m/,
        sub: removeXterm256
    }, {
        pattern: /^\n/,
        sub: newline
    }, {
        pattern: /^\x1b\[((?:\d{1,3};?)+|)m/,
        sub: ansiMess
    }, {
        // CSI n J
        // ED - Erase in Display Clears part of the screen.
        // If n is 0 (or missing), clear from cursor to end of screen.
        // If n is 1, clear from cursor to beginning of the screen.
        // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
        // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer
        //   (this feature was added for xterm and is supported by other terminal applications).
        pattern: /^\x1b\[\d?J/,
        sub: remove
    }, {
        // CSI n ; m f
        // HVP - Horizontal Vertical Position Same as CUP
        pattern: /^\x1b\[\d{0,3};\d{0,3}f/,
        sub: remove
    }, {
        // catch-all for CSI sequences?
        pattern: /^\x1b\[?[\d;]{0,3}/,
        sub: remove
    }, {
        pattern: /^([^\x1b\x08\n]+)/,
        sub: realText
    }];

    function process(handler, i) {
        if (i > ansiHandler && ansiMatch) {
            return;
        }

        ansiMatch = false;

        text = text.replace(handler.pattern, handler.sub);
    }

    const results1 = [];
    let {length} = text;

    outer:
    while (length > 0) {
        for (let i = 0, o = 0, len = tokens.length; o < len; i = ++o) {
            const handler = tokens[i];
            process(handler, i);

            if (text.length !== length) {
                // We matched a token and removed it from the text. We need to
                // start matching *all* tokens against the new text.
                length = text.length;
                continue outer;
            }
        }

        if (text.length === length) {
            break;
        }
        results1.push(0);

        length = text.length;
    }

    return results1;
}

/**
 * If streaming, then the stack is "sticky"
 *
 * @param {Array} stickyStack
 * @param {string} token
 * @param {*} data
 * @returns {Array}
 */
function updateStickyStack(stickyStack, token, data) {
    if (token !== 'text') {
        stickyStack = stickyStack.filter(notCategory(categoryForCode(data)));
        stickyStack.push({token, data, category: categoryForCode(data)});
    }

    return stickyStack;
}

class Filter {
    /**
     * @param {object} options
     * @param {string=} options.fg The default foreground color used when reset color codes are encountered.
     * @param {string=} options.bg The default background color used when reset color codes are encountered.
     * @param {boolean=} options.newline Convert newline characters to `<br/>`.
     * @param {boolean=} options.escapeXML Generate HTML/XML entities.
     * @param {boolean=} options.stream Save style state across invocations of `toHtml()`.
     * @param {(string[] | {[code: number]: string})=} options.colors Can override specific colors or the entire ANSI palette.
     */
    constructor (options) {
        options = options || {};

        if (options.colors) {
            options.colors = Object.assign({}, defaults.colors, options.colors);
        }

        this.options = Object.assign({}, defaults, options);
        this.stack = [];
        this.stickyStack = [];
    }
    /**
     * @param {string | string[]} input
     * @returns {string}
     */
    toHtml (input) {
        input = typeof input === 'string' ? [input] : input;
        const {stack, options} = this;
        const buf = [];

        this.stickyStack.forEach(element => {
            const output = generateOutput(stack, element.token, element.data, options);

            if (output) {
                buf.push(output);
            }
        });

        tokenize(input.join(''), options, (token, data) => {
            const output = generateOutput(stack, token, data, options);

            if (output) {
                buf.push(output);
            }

            if (options.stream) {
                this.stickyStack = updateStickyStack(this.stickyStack, token, data);
            }
        });

        if (stack.length) {
            buf.push(resetStyles(stack));
        }

        return buf.join('');
    }
}

module.exports = Filter;
<|endoftext|>


<commit_before>const https = require('https');
const querystring = require('querystring');

const request = (host, path, data) => new Promise((resolve, reject) => {
  const qs = querystring.stringify(data);

  const req = https.request(
    {
      host,
      path: path + '?' + qs
    },
    res => {
      let content = '';
      res.setEncoding('utf8');
      res.on('data', chunk => content += chunk);
      res.on('end', () => resolve(JSON.parse(content)), console.log(content));
    }
  );

  req.on('error', reject);
  req.end();
});

function apiFactory(request) {
  return class Api {
    constructor(telegram_key) {
      this.telegram_key = telegram_key;
      this.hostname = 'api.telegram.org';
    }
    getUpdates(offset = 0) {
      return request(this.hostname, `/bot${this.telegram_key}/getUpdates`, {
        offset
      });
    }
    sendFullMessage(message) {
      return request(
        this.hostname,
        `/bot${this.telegram_key}/sendMessage`,
        message
      );
    }
    sendMessage(chat_id, text, options) {
      return this.sendFullMessage(Object.assign({ chat_id, text }, options));
    }
  };
}

module.exports = {
  apiFactory,
  request
};
<commit_msg>using post to make request<commit_after>const https = require('https');
const querystring = require('querystring');

const request = (host, path, data) => new Promise((resolve, reject) => {
  const qs = querystring.stringify(data);

  const req = https.request(
    {
      host,
      path: path,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-length': Buffer.byteLength(payload)
      }
    },
    res => {
      let content = '';
      res.setEncoding('utf8');
      res.on('data', chunk => content += chunk);
      res.on('end', () => resolve(JSON.parse(content)), console.log(content));
    }
  );

  req.write(payload);
  req.on('error', reject);
  req.end();
});

function apiFactory(request) {
  return class Api {
    constructor(telegram_key) {
      this.telegram_key = telegram_key;
      this.hostname = 'api.telegram.org';
    }
    getUpdates(offset = 0) {
      return request(this.hostname, `/bot${this.telegram_key}/getUpdates`, {
        offset
      });
    }
    sendFullMessage(message) {
      return request(
        this.hostname,
        `/bot${this.telegram_key}/sendMessage`,
        message
      );
    }
    sendMessage(chat_id, text, options) {
      return this.sendFullMessage(Object.assign({ chat_id, text }, options));
    }
  };
}

module.exports = {
  apiFactory,
  request
};
<|endoftext|>


<commit_before>/* global angular, describe, beforeEach, module, inject, it, expect, _, moment */

(function () {
    'use strict';

    var $ = function (haystack, query) {
        return angular.element(haystack[0].querySelectorAll(query));
    };

    var $compile;
    var $scope;
    var $rootScope;
    var template;
    var compiledTemplate;
    var $templateCache;

    var compile = function (template) {
        return $compile(template)($scope);
    };

    describe('Input types directives', function () {

        beforeEach(module('vr.directives.slider'));
        beforeEach(module('angular-bind-html-compile'));
        beforeEach(module('angular-input-fields'));

        beforeEach(inject(function (_$compile_, _$rootScope_, _$templateCache_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            $scope = $rootScope.$new();
            $templateCache = _$templateCache_;

        }));

        describe('single-choice-input', function () {
            it('should compile and have a input element and a select element', function () {

                $scope.choices = [1];

                $scope.questionName = 'question';

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input').length).toBe(1);
                expect($(compiledTemplate, 'select').length).toBe(1);
            });
        });

        describe('number input', function () {
            it('should compile and have a number input element', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="number"]').length).toBe(1);

            });
        });

        describe('text input', function () {
            it('should compile and have a text input element', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="text"]').length).toBe(1);
            });
        });

        describe('slider input', function () {
            it('should have a slider', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="range"]').length).toBe(1);
            });

            it('should have default labels for lowest and highest values', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '.step.perc-0').html()).toContain('none');
                expect($(compiledTemplate, '.step.perc-100').html()).toContain('very severe');

            });

            it('should have a default value of ceiling', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '[slider]').attr('ceiling')).toBe('10');
            });

            it('should have a default I=O translate-fn', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();
                var translateFn = compiledTemplate.isolateScope().translateFn;

                expect(translateFn).toBeDefined();
                expect(translateFn(10)).toBe(10);
                expect(translateFn(2)).toBe(2);

            });

            it('should accept a translate-fn attribute and change the default translate-fn', function () {
                $scope.translateFunction = function (input) {
                    return input * 2;
                };

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();
                var translateFn = compiledTemplate.isolateScope().translateFn;

                expect(translateFn).toBeDefined();
                expect(translateFn(10)).toBe(20);
                expect(translateFn(2)).toBe(4);

            });

            it('should accept a className attribute and attach it as a class', function () {
                template = '

';
                compiledTemplate = compile(template);
                $rootScope.$digest();
                expect($(compiledTemplate, '.slider-wrapper > div').attr('class')).toContain('someClass');

            });

            it('should have changable labels for lowest and highest values', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '.step.perc-0').html()).toContain('No problem');
                expect($(compiledTemplate, '.step.perc-100').html()).toContain('Worst problem');
            });

        });

        describe('boolean input', function () {
            it('should compile and have a input element and a select element', function () {
                $scope.model = {
                    question: true
                };

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input').length).toBe(2);
                expect($(compiledTemplate, 'option').length).toBe(3);

            });
        });

        describe('switch input', function () {

            it('should generate a div.switch-wrapper with two btn-radio directives with correct values', function () {
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                $scope.switchValues = [
                    {
                        value: 'switch1',
                        display_name: 'Switch 1'
                    },
                    {
                        value: 'switch2',
                        display_name: 'Switch 2'
                    }
                ];
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'div.switch-wrapper').length).toBe(1);
                expect($(compiledTemplate, '[btn-radio]').length).toBe(2);

                expect($(compiledTemplate, '[btn-radio]:first-child').html()).toBe('Switch 1');
                expect($(compiledTemplate, '[btn-radio]:nth-child(2)').html()).toBe('Switch 2');

            });

            it('should generate a div.switch-wrapper with two btn-radio directives with correct values when in a group', function () {
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                $scope.switchValues = [
                    {
                        value: 'switch1',
                        display_name: 'Switch 1'
                    },
                    {
                        value: 'switch2',
                        display_name: 'Switch 2'
                    }
                ];
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'div.switch-wrapper').length).toBe(1);
                expect($(compiledTemplate, '[btn-radio]').length).toBe(2);

                expect($(compiledTemplate, '[btn-radio]:first-child').html()).toBe('Switch 1');
                expect($(compiledTemplate, '[btn-radio]:nth-child(2)').html()).toBe('Switch 2');

            });
        });

        describe('checkbox input', function () {
            it('should compile and have a checkbox input element and a label', function () {
                $scope.label = 'A label';

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="checkbox"]').length).toBe(1);
                expect($(compiledTemplate, 'label span').html()).toContain('A label');

            });
        });

        describe('moment-select ', function () {
            it('should compile and have 2 select elements', function () {

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'select').length).toBe(2);
            });

            it('should be disabled if `disable` is set', function () {

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'select[disabled]').length).toBe(2);
            });

            it('should set hour and minute based on the model of duration type', function () {

                $scope.duration = moment.duration({
                    hours: 2,
                    minutes: 5
                });
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(2);
                expect(isolated.minutes).toBe(5);
            });

            it('should set hour and minute based on the model of duration string', function () {

                $scope.duration = '01:30:00';
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(1);
                expect(isolated.minutes).toBe(30);
                expect($scope.duration.toString()).toBe('PT1H30M');
            });

            it('should set hour and minute based on the model of date string', function () {

                $scope.date = '2013-02-08 09:30:26';
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(9);
                expect(isolated.minutes).toBe(30);
            });

            it('should set hour and minute based on the model of date type', function () {

                $scope.date = moment('2013-02-08 09:30:26');
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(9);
                expect(isolated.minutes).toBe(30);
            });

            it('should return a duration object with hours and minutes', function () {
                $scope.duration = {};
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                isolated.hours = 9;
                isolated.minutes = 30;

                $rootScope.$digest();

                expect($scope.duration.toString()).toBe('PT9H30M');
            });

            it('should return a moment object with hours and minutes', function () {
                $scope.date = {};
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                isolated.hours = 9;
                isolated.minutes = 30;

                $rootScope.$digest();

                expect($scope.date.toString()).toContain('09:30:00');
            });

            describe('should have minute choices ', function () {
                it('that default to 0 .. 59', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(60);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 60));

                });

                it('that depended on minute-step', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(6);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 60, 10));
                });

                it('that depended on minute-min', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(50);
                    expect(isolated.minuteChoices).toEqual(_.range(10, 60));
                });

                it('that depended on minute-max', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(10);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 10));
                });
            });

            describe('should have hour choices ', function () {
                it('that default to 0 .. 24', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(24);
                    expect(isolated.hourChoices).toEqual(_.range(0, 24));
                });

                it('that depended on hour-step', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(12);
                    expect(isolated.hourChoices).toEqual(_.range(0, 24, 2));
                });

                it('that depended on hour-min', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(14);
                    expect(isolated.hourChoices).toEqual(_.range(10, 24));
                });

                it('that depended on hour-max', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(10);
                    expect(isolated.hourChoices).toEqual(_.range(0, 10));
                });
            });

        });
    });
}());
<commit_msg>Add minutes attribute to moment-select<commit_after>/* global angular, describe, beforeEach, module, inject, it, expect, _, moment */

(function () {
    'use strict';

    var $ = function (haystack, query) {
        return angular.element(haystack[0].querySelectorAll(query));
    };

    var $compile;
    var $scope;
    var $rootScope;
    var template;
    var compiledTemplate;
    var $templateCache;

    var compile = function (template) {
        return $compile(template)($scope);
    };

    describe('Input types directives', function () {

        beforeEach(module('vr.directives.slider'));
        beforeEach(module('angular-bind-html-compile'));
        beforeEach(module('angular-input-fields'));

        beforeEach(inject(function (_$compile_, _$rootScope_, _$templateCache_) {
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            $scope = $rootScope.$new();
            $templateCache = _$templateCache_;

        }));

        describe('single-choice-input', function () {
            it('should compile and have a input element and a select element', function () {

                $scope.choices = [1];

                $scope.questionName = 'question';

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input').length).toBe(1);
                expect($(compiledTemplate, 'select').length).toBe(1);
            });
        });

        describe('number input', function () {
            it('should compile and have a number input element', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="number"]').length).toBe(1);

            });
        });

        describe('text input', function () {
            it('should compile and have a text input element', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="text"]').length).toBe(1);
            });
        });

        describe('slider input', function () {
            it('should have a slider', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="range"]').length).toBe(1);
            });

            it('should have default labels for lowest and highest values', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '.step.perc-0').html()).toContain('none');
                expect($(compiledTemplate, '.step.perc-100').html()).toContain('very severe');

            });

            it('should have a default value of ceiling', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '[slider]').attr('ceiling')).toBe('10');
            });

            it('should have a default I=O translate-fn', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();
                var translateFn = compiledTemplate.isolateScope().translateFn;

                expect(translateFn).toBeDefined();
                expect(translateFn(10)).toBe(10);
                expect(translateFn(2)).toBe(2);

            });

            it('should accept a translate-fn attribute and change the default translate-fn', function () {
                $scope.translateFunction = function (input) {
                    return input * 2;
                };

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();
                var translateFn = compiledTemplate.isolateScope().translateFn;

                expect(translateFn).toBeDefined();
                expect(translateFn(10)).toBe(20);
                expect(translateFn(2)).toBe(4);

            });

            it('should accept a className attribute and attach it as a class', function () {
                template = '

';
                compiledTemplate = compile(template);
                $rootScope.$digest();
                expect($(compiledTemplate, '.slider-wrapper > div').attr('class')).toContain('someClass');

            });

            it('should have changable labels for lowest and highest values', function () {
                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, '.step.perc-0').html()).toContain('No problem');
                expect($(compiledTemplate, '.step.perc-100').html()).toContain('Worst problem');
            });

        });

        describe('boolean input', function () {
            it('should compile and have a input element and a select element', function () {
                $scope.model = {
                    question: true
                };

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input').length).toBe(2);
                expect($(compiledTemplate, 'option').length).toBe(3);

            });
        });

        describe('switch input', function () {

            it('should generate a div.switch-wrapper with two btn-radio directives with correct values', function () {
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                $scope.switchValues = [
                    {
                        value: 'switch1',
                        display_name: 'Switch 1'
                    },
                    {
                        value: 'switch2',
                        display_name: 'Switch 2'
                    }
                ];
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'div.switch-wrapper').length).toBe(1);
                expect($(compiledTemplate, '[btn-radio]').length).toBe(2);

                expect($(compiledTemplate, '[btn-radio]:first-child').html()).toBe('Switch 1');
                expect($(compiledTemplate, '[btn-radio]:nth-child(2)').html()).toBe('Switch 2');

            });

            it('should generate a div.switch-wrapper with two btn-radio directives with correct values when in a group', function () {
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                $scope.switchValues = [
                    {
                        value: 'switch1',
                        display_name: 'Switch 1'
                    },
                    {
                        value: 'switch2',
                        display_name: 'Switch 2'
                    }
                ];
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'div.switch-wrapper').length).toBe(1);
                expect($(compiledTemplate, '[btn-radio]').length).toBe(2);

                expect($(compiledTemplate, '[btn-radio]:first-child').html()).toBe('Switch 1');
                expect($(compiledTemplate, '[btn-radio]:nth-child(2)').html()).toBe('Switch 2');

            });
        });

        describe('checkbox input', function () {
            it('should compile and have a checkbox input element and a label', function () {
                $scope.label = 'A label';

                template = '

';
                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'input[type="checkbox"]').length).toBe(1);
                expect($(compiledTemplate, 'label span').html()).toContain('A label');

            });
        });

        describe('moment-select ', function () {
            it('should compile and have 2 select elements', function () {

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'select').length).toBe(2);
            });

            it('should be disabled if `disable` is set', function () {

                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                expect($(compiledTemplate, 'select[disabled]').length).toBe(2);
            });

            it('should set hour and minute based on the model of duration type', function () {

                $scope.duration = moment.duration({
                    hours: 2,
                    minutes: 5
                });
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(2);
                expect(isolated.minutes).toBe(5);
            });

            it('should set hour and minute based on the model of duration string', function () {

                $scope.duration = '01:30:00';
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(1);
                expect(isolated.minutes).toBe(30);
                expect($scope.duration.toString()).toBe('PT1H30M');
            });

            it('should set hour and minute based on the model of date string', function () {

                $scope.date = '2013-02-08 09:30:26';
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(9);
                expect(isolated.minutes).toBe(30);
            });

            it('should set hour and minute based on the model of date type', function () {

                $scope.date = moment('2013-02-08 09:30:26');
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                expect(isolated.hours).toBe(9);
                expect(isolated.minutes).toBe(30);
            });

            it('should return a duration object with hours and minutes', function () {
                $scope.duration = {};
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                isolated.hours = 9;
                isolated.minutes = 30;

                $rootScope.$digest();

                expect($scope.duration.toString()).toBe('PT9H30M');
            });

            it('should return a moment object with hours and minutes', function () {
                $scope.date = {};
                template = '

';

                compiledTemplate = compile(template);

                $rootScope.$digest();

                var isolated = compiledTemplate.isolateScope();
                isolated.hours = 9;
                isolated.minutes = 30;

                $rootScope.$digest();

                expect($scope.date.toString()).toContain('09:30:00');
            });

            describe('should have minute choices ', function () {
                it('that default to 0 .. 59', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(60);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 60));

                });

                it('that depended on minute-step', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(6);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 60, 10));
                });

                it('that depended on minute-min', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(50);
                    expect(isolated.minuteChoices).toEqual(_.range(10, 60));
                });

                it('that depended on minute-max', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.minuteChoices.length).toBe(10);
                    expect(isolated.minuteChoices).toEqual(_.range(0, 10));
                });
            });

            describe('should have hour choices ', function () {
                it('that default to 0 .. 24', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(24);
                    expect(isolated.hourChoices).toEqual(_.range(0, 24));
                });

                it('that depended on hour-step', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(12);
                    expect(isolated.hourChoices).toEqual(_.range(0, 24, 2));
                });

                it('that depended on hour-min', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(14);
                    expect(isolated.hourChoices).toEqual(_.range(10, 24));
                });

                it('that depended on hour-max', function () {

                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    var isolated = compiledTemplate.isolateScope();

                    expect(isolated.hourChoices.length).toBe(10);
                    expect(isolated.hourChoices).toEqual(_.range(0, 10));
                });

                it('with a default min suffix in the template', function () {
                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    expect($(compiledTemplate, 'select[ng-model="minutes"] option:nth-child(2)').attr('label')).toContain('min');
                });

                it('with a overwritable min suffix in the template', function () {
                    template = '

';

                    compiledTemplate = compile(template);

                    $rootScope.$digest();

                    expect($(compiledTemplate, 'select[ng-model="minutes"] option:nth-child(2)').attr('label')).toContain('minutes');
                });

            });

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


<commit_before>// dependencies
var EventEmitter = require('events').EventEmitter;
var WebSocketServer = require('ws').Server;
var http = require('http');

// the global engine object
var engine = global.engine = new EventEmitter();

// prototypal inheritance
engine.clone = clone;

// flatten object
engine.flat = flat;

// undlatten object
engine.deep = deep;

// get a value from an object with a dot notation path
engine.path = path;

// require dependencies
engine.session = require('./session');

// http request handler
engine.http = {
    request: require('./request')
};

// socket connection handler
engine.socket = {
    connection: require('./socket')
};

// module factory
engine.module = require('./module');

// caches
engine.cache = require('./cache');

// static files
engine.static = require('./static');

/**
 * Start the HTTP and WebSocket servers.
 *
 * @public
 * @param {string} The absolute path to the repo.
 * @param {number} The port number.
 * @param {string} The user id.
 * @param {string} The API key.
 * @param {boolean} Indicates if engine should run in production mode.
 */
module.exports = function (repo, port, user, key, production) {

    engine.repo = repo;
    engine.port = port;
    engine.user = user;
    engine.key = key;
    engine.production = production;

    // create a http server
    engine.http.server = http.createServer(engine.session(engine.http.request));

    // create a websocket server
    engine.socket.server = new WebSocketServer({server: engine.http.server});

    // listen to websocket connections
    engine.socket.server.on('connection', engine.session(engine.socket.connection));

    // start http server
    engine.server.http.listen(port);

    console.log(engine);
};

/**
 * Clone object. True prototypal inheritance.
 *
 * @public
 * @param {object} The, to be cloned, object.
 */
function clone (object) {

    // create an empty function
    function O() {}

    // set prototype to given object
    O.prototype = object;

    // create new instance of empty function
    return new O();
}

/**
 * Create a flat object {key1: {key2: "value"}} => {"key1.key2": "value"}
 *
 * @public
 * @param {string} The object, which is flattened.
 */
function flat (object) {
    var output = {};
    var value;
    var newKey;

    // recusrive handler
    function step (obj, prev) {
        for (var key in obj) {
            value = obj[key];
            newKey = prev + key;

            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {

                if (Object.keys(value).length) {
                    step(value, newKey + '.');
                    continue;
                }
            }

            output[newKey] = value;
        }
    }

    // start recursive loop
    step(object, '');

    return output;
}

/**
 * Unflatten dot-notation keys {"key1.key2": "value"} => {key1: {key2: "value"}}
 *
 * @public
 * @param {string} The object, which is unflattened.
 */
function deep (object) {
    var result = {};
    var parentObj = result;
    var key;
    var subkeys;
    var subkey;
    var last;
    var keys = Object.keys(object);

    for (var i = 0; i < keys.length; ++i) {

        key = keys[i];
        subkeys = key.split('.');
        last = subkeys.pop();

        for (var ii = 0; ii < subkeys.length; ++ii) {
            subkey = subkeys[ii];
            parentObj[subkey] = typeof parentObj[subkey] === 'undefined' ? {} : parentObj[subkey];
            parentObj = parentObj[subkey];
        }

        parentObj[last] = object[key];
        parentObj = result;
    }

    return result;
}

/**
 * Get a value from a property "path" (dot.notation).
 * path('Object.key.key.value'[, {'key': {'key': {'value': 123}}}]);
 *
 * @public
 * @param {string} The path in "dot" notation.
 * @param {object} The data object, which is used to search the path.
 */
function path (key, scope) {

    if (!path) {
        return;
    }

    var o = key;
    key = key.split('.');
    scope = scope || this;

    // find keys in paths or return
    for (var i = 0; i < key.length; ++i) {
        if (!(scope = scope[key[i]])) {
            return;
        }
    }

    return scope;
}
<commit_msg>fixed wrong object prop access<commit_after>// dependencies
var EventEmitter = require('events').EventEmitter;
var WebSocketServer = require('ws').Server;
var httpServer = require('http');

// the global engine object
var engine = global.engine = new EventEmitter();

// prototypal inheritance
engine.clone = clone;

// flatten object
engine.flat = flat;

// undlatten object
engine.deep = deep;

// get a value from an object with a dot notation path
engine.path = path;

// require dependencies
engine.session = require('./session');

// http request handler
engine.http = {
    request: require('./request')
};

// socket connection handler
engine.socket = {
    connection: require('./socket')
};

// module factory
engine.module = require('./module');

// caches
engine.cache = require('./cache');

// static files
engine.static = require('./static');

/**
 * Start the HTTP and WebSocket servers.
 *
 * @public
 * @param {string} The absolute path to the repo.
 * @param {number} The port number.
 * @param {string} The user id.
 * @param {string} The API key.
 * @param {boolean} Indicates if engine should run in production mode.
 */
module.exports = function (repo, port, user, key, production) {

    engine.repo = repo;
    engine.port = port;
    engine.user = user;
    engine.key = key;
    engine.production = production;

    // create a http server
    engine.http.server = httpServer.createServer(engine.session(engine.http.request));

    // create a websocket server
    engine.socket.server = new WebSocketServer({server: engine.http.server});

    // listen to websocket connections
    engine.socket.server.on('connection', engine.session(engine.socket.connection));

    // start http server
    engine.http.server.listen(port);

    console.log(engine);
};

/**
 * Clone object. True prototypal inheritance.
 *
 * @public
 * @param {object} The, to be cloned, object.
 */
function clone (object) {

    // create an empty function
    function O() {}

    // set prototype to given object
    O.prototype = object;

    // create new instance of empty function
    return new O();
}

/**
 * Create a flat object {key1: {key2: "value"}} => {"key1.key2": "value"}
 *
 * @public
 * @param {string} The object, which is flattened.
 */
function flat (object) {
    var output = {};
    var value;
    var newKey;

    // recusrive handler
    function step (obj, prev) {
        for (var key in obj) {
            value = obj[key];
            newKey = prev + key;

            if (typeof value === 'object' && value !== null && !Array.isArray(value)) {

                if (Object.keys(value).length) {
                    step(value, newKey + '.');
                    continue;
                }
            }

            output[newKey] = value;
        }
    }

    // start recursive loop
    step(object, '');

    return output;
}

/**
 * Unflatten dot-notation keys {"key1.key2": "value"} => {key1: {key2: "value"}}
 *
 * @public
 * @param {string} The object, which is unflattened.
 */
function deep (object) {
    var result = {};
    var parentObj = result;
    var key;
    var subkeys;
    var subkey;
    var last;
    var keys = Object.keys(object);

    for (var i = 0; i < keys.length; ++i) {

        key = keys[i];
        subkeys = key.split('.');
        last = subkeys.pop();

        for (var ii = 0; ii < subkeys.length; ++ii) {
            subkey = subkeys[ii];
            parentObj[subkey] = typeof parentObj[subkey] === 'undefined' ? {} : parentObj[subkey];
            parentObj = parentObj[subkey];
        }

        parentObj[last] = object[key];
        parentObj = result;
    }

    return result;
}

/**
 * Get a value from a property "path" (dot.notation).
 * path('Object.key.key.value'[, {'key': {'key': {'value': 123}}}]);
 *
 * @public
 * @param {string} The path in "dot" notation.
 * @param {object} The data object, which is used to search the path.
 */
function path (key, scope) {

    if (!path) {
        return;
    }

    var o = key;
    key = key.split('.');
    scope = scope || this;

    // find keys in paths or return
    for (var i = 0; i < key.length; ++i) {
        if (!(scope = scope[key[i]])) {
            return;
        }
    }

    return scope;
}
<|endoftext|>


<commit_before>(function () {
	module('keypath');

	var object = {
		simple: 'okay',
		food: {
			eggs: 'good',
			cheese: 'good',
			cottage_cheese: 'bad'
		},
		turtles: ['Michelangelo', 'Leonardo', 'Donatello', 'Raphael']
	};

	Object.prototype.bad = 'argh';

	test('keypath(object, path)', function () {
		equals(keypath(object, 'simple'), 'okay', 'Should look up a value by keypath');
		equals(keypath(object, 'food.eggs'), 'good', 'Should handle multiple keys');
		equals(keypath(object, 'turtles.2'), 'Donatello', 'Should handle array indexes');
		equals(keypath(object, 'nothing'), null, 'Should return null when not found');
		equals(keypath(object, 'bad'), null, 'Should not look up the prototype chain');
		equals(keypath(object), null, 'Should not break when no path is provided');
		equals(keypath(), null, 'Should not break when no object is provided');
	});

	test('keypath(object, path, fallback)', function () {
		equals(keypath(object, 'my.crazy.path', true), true, 'Should return the fallback value');
		equals(keypath(object, 'my.crazy.path', undefined), undefined, 'Should allow undefined as the fallback value');
	});

	test('keypath(object, path, fallback, prototype)', function () {
		equals(keypath(object, 'bad', null, true), 'argh', 'Should traverse the prototype chain');
	});

	module('flatten.noConflict()');

	test('it should return the keypath function and restore state', function () {
		var _keypath = keypath;
		equal(keypath.noConflict(), _keypath);
		equal(this.keypath, undefined);
	});

}());
<commit_msg>Removed whitespace from test file<commit_after>(function () {
  module('keypath');

  var object = {
    simple: 'okay',
    food: {
      eggs: 'good',
      cheese: 'good',
      cottage_cheese: 'bad'
    },
    turtles: ['Michelangelo', 'Leonardo', 'Donatello', 'Raphael']
  };

  Object.prototype.bad = 'argh';

  test('keypath(object, path)', function () {
    equals(keypath(object, 'simple'), 'okay', 'Should look up a value by keypath');
    equals(keypath(object, 'food.eggs'), 'good', 'Should handle multiple keys');
    equals(keypath(object, 'turtles.2'), 'Donatello', 'Should handle array indexes');
    equals(keypath(object, 'nothing'), null, 'Should return null when not found');
    equals(keypath(object, 'bad'), null, 'Should not look up the prototype chain');
    equals(keypath(object), null, 'Should not break when no path is provided');
    equals(keypath(), null, 'Should not break when no object is provided');
  });

  test('keypath(object, path, fallback)', function () {
    equals(keypath(object, 'my.crazy.path', true), true, 'Should return the fallback value');
    equals(keypath(object, 'my.crazy.path', undefined), undefined, 'Should allow undefined as the fallback value');
  });

  test('keypath(object, path, fallback, prototype)', function () {
    equals(keypath(object, 'bad', null, true), 'argh', 'Should traverse the prototype chain');
  });

  module('keypath.noConflict()');

  test('it should return the keypath function and restore state', function () {
    var _keypath = keypath;
    equal(keypath.noConflict(), _keypath);
    equal(this.keypath, undefined);
  });

}());
<|endoftext|>


<commit_before>console.log("Loading Tridion Extension");

/*
	Listen to load of Tridion Dashboard and inject the extension
*/
chrome.webNavigation.onCompleted.addListener(
	function(tab)
	{
		var details = {runAt: "document_end"};
		console.debug('Loading Tridion Extension');

		details["file"] = "popup.js";
		chrome.tabs.executeScript( tab.tabId, details, msg_log("loaded tridion_ext"));

		chrome.tabs.sendMessage(tab.tabId, {"action": "dashboard_load"}, function (){
			console.debug("Finished load of tridion!");
		});
	}
	// URLfilter
	, {url:[{pathContains:"ListFilters/SearchListBar.aspx"}]}
);

/*
	Click listener for extension button
*/
chrome.browserAction.onClicked.addListener(function(tab) {
  // No tabs or host permissions needed!
  console.log('browserAction');
  chrome.tabs.executeScript({
    code: 'document.body.style.backgroundColor="red"'
  });
});

// UTILS
function msg_log(str)
{
	console.info(str);
}<commit_msg>Handle messages from page<commit_after>// Author: Daniel G (oscar-daniel.gonzalez@hp.com)

console.log("Loading Tridion Extension");
/*
	Listen to load of Tridion Dashboard and inject the extension
*/
chrome.webNavigation.onCompleted.addListener(
	function(tab)
	{
		console.debug('Loading Tridion Extension');

		var details = {runAt: "document_end"};
		details["file"] = "popup.js";
		chrome.tabs.executeScript( tab.tabId, details, msg_log("loaded tridion_ext"));
	}
	// URLfilter
	, {url:[{pathContains:"ListFilters/SearchListBar.aspx"}]}
);

/*
	Click listener for extension button
*/
chrome.browserAction.onClicked.addListener(function(tab) {
	// No tabs or host permissions needed!
	console.log('browserAction');
	chrome.tabs.executeScript({
		code: 'document.body.style.backgroundColor="red"'
	});
});

/*
	Handle messages from page
*/
chrome.runtime.onMessage.addListener(function(msg)
{
	console.debug("Background - Yo I got a message!");
	console.info(msg);
	chrome.tabs.create({ url: msg.url, active: false});
});

// UTILS
function msg_log(str)
{
  	console.info(str);
}<|endoftext|>


<commit_before>test( "compact", function(){

  var custom = 42;
  var major = -1;
  var minor = -2;
  var patch = -3;
  var container = "#container";
  var path = "p[3]/ul/li[2]";
  var activity = -9;
  var left = 1234;
  var top = 5678;
  var time1 = 1000;
  var time2 = 10000;
  var details = "#any";
  var specifics = 34;

  deepEqual( compact( [] ), [],
                         "empty array expected when empty array is compacted");

  var input0 = [
    custom,
    major,minor,patch,
    container,
    path,
    activity,
    left,
    top,
    time1
  ];
  var json0 = JSON.stringify( input0 );
  var result0 = compact( input0 );
  strictEqual( JSON.stringify( input0 ), json0,
                                   "input ghost data 0 must not be modified");
  deepEqual( result0, input0,      "a clone is expected for a single record");

  var input1 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time2
    ]
  ];
  var json1 = JSON.stringify( input1 );
  var output1 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      [
        time1
      ],
      [
        time2
      ]
    ]
  ];
  var result1 = compact( input1 );
  strictEqual( JSON.stringify( input1 ), json1,
                                   "input ghost data 1  must not be modified");
  deepEqual( result1, output1,                    "nesting expected at times");

  var input2 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      2000,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      4000,
      time2
    ]
  ];
  var output2 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      [
        2000,
        time1
      ],
      [
        4000,
        time2
      ]
    ]
  ];
  var result2 = compact( input2 );
  deepEqual( result2, output2,                      "nesting expected at top");

  var input3 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      3000,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      4000,
      top,
      time2
    ]
  ];
  var output3 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      [
        3000,
        top,
        time1
      ],
      [
        4000,
        top,
        time2
      ]
    ]
  ];
  var result3 = compact( input3 );
  deepEqual( result3, output3,                     "nesting expected at left");

  var input4 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      -1,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -2,
      left,
      top,
      time2
    ]
  ];
  var output4 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      [
        -1,
        left,
        top,
        time1
      ],
      [
        -2,
        left,
        top,
        time2
      ]
    ]
  ];
  var result4 = compact( input4 );
  deepEqual( result4, output4,            "nesting expected at activity type");

  var input5 = [
    custom,
    major,minor,patch,
    [
      container,
      "p[1]",
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      "p[2]",
      activity,
      left,
      top,
      time2
    ]
  ];
  var output5 = [
    custom,
    major,minor,patch,
    [
      container,
      [
        "p[1]",
        activity,
        left,
        top,
        time1
      ],
      [
        "p[2]",
        activity,
        left,
        top,
        time2
      ]
    ]
  ];
  var result5 = compact( input5 );
  deepEqual( result5, output5,                     "nesting expected at path");

  var input6 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      time2
    ]
  ];
  var json6 = JSON.stringify( input6 );
  var result6 = compact( input6 );
  strictEqual( JSON.stringify( result6 ), json6,
                "no extra nesting expected with different ancestor selectors");

  var input7 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      "p[1]",
      activity,
      left,
      top,
      0
    ],
    [
      "#container1",
      "p[2]",
      -1,
      6000,
      top,
      1000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      6000,
      top,
      2000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      7000,
      4000,
      3000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      7000,
      3000,
      4000
    ],
    [
      "#container1",
      "p[2]",
      -2,
      left,
      top,
      5000
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      6000
    ]
  ];
  var output7 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      [
        "p[1]",
        activity,
        left,
        top,
        0
      ],
      [
        "p[2]",
        [
          -1,
          [
            6000,
            top,
            [
              1000
            ],
            [
              2000
            ]
          ],
          [
            7000,
            [
              4000
            ],
            [
              3000
            ],
            [
              3000
            ],
            [
              4000
            ]
          ]
        ],
        [
          -2,
          left,
          top,
          5000
        ]
      ]
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      6000
    ]
  ];
  var result7 = compact( input7 );
  deepEqual( result7, output7,              "five levels of nesting expected");

  var input8 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      1000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      2000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      3000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      4000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      5000
    ]
  ];
  var output8 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      [
        1000
      ],
      [
        2000
      ],
      [
        3000
      ],
      [
        4000
      ],
      [
        5000
      ]
    ]
  ];
  var result8 = compact( input8 );
  deepEqual( result8, output8,   "nesting at times expected with five values");

  var input9 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      -5,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -4,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -3,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -2,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -1,
      left,
      top,
      time2
    ]
  ];
  var output9 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      [
        -5,
        left,
        top,
        time1
      ],
      [
        -4,
        left,
        top,
        time1
      ],
      [
        -3,
        left,
        top,
        time1
      ],
      [
        -2,
        left,
        top,
        time1
      ],
      [
        -1,
        left,
        top,
        time2
      ]
    ]
  ];
  var result9 = compact( input9 );
  deepEqual( result9, output9,
                         "nesting expected at activity type with five values");

  var input10 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ]
  ];
  var output10 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [],
      []
    ]
  ];
  var result10 = compact( input10 );
  deepEqual( result10, output10,
     "nesting expected at details with empty arrays when details are omitted");

  var input11 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      details,
      specifics
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      details,
      specifics
    ],
  ];
  var output11 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [
        details,
        specifics
      ],
      [
        details,
        specifics
      ]
    ]
  ];
  var result11 = compact( input11 );
  deepEqual( result11, output11,
                                     "nesting expected at details even with " +
                                      "common value of details and specifics");

  var input12 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [1,2,3],
      specifics
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [1,5,9]
    ],
  ];
  var output12 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [
        [1,2,3],
        specifics
      ],
      [
        [1,5,9]
      ]
    ]
  ];
  var result12 = compact( input12 );
  deepEqual( result12, output12,
                      "nesting expected at details with or without specifics");
});
<commit_msg>Update minimal tests based on updated specification of format<commit_after>test( "compact", function(){

  var custom = 42;
  var major = -1;
  var minor = -2;
  var patch = -3;
  var container = "#container";
  var path = "p[3]/ul/li[2]";
  var activity = -9;
  var left = 1234;
  var top = 5678;
  var time1 = 1000;
  var time2 = 10000;
  var details = "#any";
  var specifics = 34;

  deepEqual(
    compact( [custom,major,minor,patch,[]] ),
    [custom,major,minor,patch,[]],
                   "General Custom field and version fields must be copied, " +
                                         "even with an empty list of records");

  var input0 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ]
  ];
  var json0 = JSON.stringify( input0 );
  var result0 = compact( input0 );
  strictEqual( JSON.stringify( input0 ), json0,
                                   "input ghost data 0 must not be modified");
  deepEqual( result0, input0,      "a clone is expected for a single record");

  var input1 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time2
    ]
  ];
  var json1 = JSON.stringify( input1 );
  var output1 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      [
        time1
      ],
      [
        time2
      ]
    ]
  ];
  var result1 = compact( input1 );
  strictEqual( JSON.stringify( input1 ), json1,
                                   "input ghost data 1  must not be modified");
  deepEqual( result1, output1,                    "nesting expected at times");

  var input2 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      2000,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      4000,
      time2
    ]
  ];
  var output2 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      [
        2000,
        time1
      ],
      [
        4000,
        time2
      ]
    ]
  ];
  var result2 = compact( input2 );
  deepEqual( result2, output2,                      "nesting expected at top");

  var input3 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      3000,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      4000,
      top,
      time2
    ]
  ];
  var output3 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      [
        3000,
        top,
        time1
      ],
      [
        4000,
        top,
        time2
      ]
    ]
  ];
  var result3 = compact( input3 );
  deepEqual( result3, output3,                     "nesting expected at left");

  var input4 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      -1,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -2,
      left,
      top,
      time2
    ]
  ];
  var output4 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      [
        -1,
        left,
        top,
        time1
      ],
      [
        -2,
        left,
        top,
        time2
      ]
    ]
  ];
  var result4 = compact( input4 );
  deepEqual( result4, output4,            "nesting expected at activity type");

  var input5 = [
    custom,
    major,minor,patch,
    [
      container,
      "p[1]",
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      "p[2]",
      activity,
      left,
      top,
      time2
    ]
  ];
  var output5 = [
    custom,
    major,minor,patch,
    [
      container,
      [
        "p[1]",
        activity,
        left,
        top,
        time1
      ],
      [
        "p[2]",
        activity,
        left,
        top,
        time2
      ]
    ]
  ];
  var result5 = compact( input5 );
  deepEqual( result5, output5,                     "nesting expected at path");

  var input6 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      time2
    ]
  ];
  var json6 = JSON.stringify( input6 );
  var result6 = compact( input6 );
  strictEqual( JSON.stringify( result6 ), json6,
                "no extra nesting expected with different ancestor selectors");

  var input7 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      "p[1]",
      activity,
      left,
      top,
      0
    ],
    [
      "#container1",
      "p[2]",
      -1,
      6000,
      top,
      1000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      6000,
      top,
      2000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      7000,
      4000,
      3000
    ],
    [
      "#container1",
      "p[2]",
      -1,
      7000,
      3000,
      4000
    ],
    [
      "#container1",
      "p[2]",
      -2,
      left,
      top,
      5000
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      6000
    ]
  ];
  var output7 = [
    custom,
    major,minor,patch,
    [
      "#container1",
      [
        "p[1]",
        activity,
        left,
        top,
        0
      ],
      [
        "p[2]",
        [
          -1,
          [
            6000,
            top,
            [
              1000
            ],
            [
              2000
            ]
          ],
          [
            7000,
            [
              4000
            ],
            [
              3000
            ],
            [
              3000
            ],
            [
              4000
            ]
          ]
        ],
        [
          -2,
          left,
          top,
          5000
        ]
      ]
    ],
    [
      "#container2",
      path,
      activity,
      left,
      top,
      6000
    ]
  ];
  var result7 = compact( input7 );
  deepEqual( result7, output7,              "five levels of nesting expected");

  var input8 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      1000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      2000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      3000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      4000
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      5000
    ]
  ];
  var output8 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      [
        1000
      ],
      [
        2000
      ],
      [
        3000
      ],
      [
        4000
      ],
      [
        5000
      ]
    ]
  ];
  var result8 = compact( input8 );
  deepEqual( result8, output8,   "nesting at times expected with five values");

  var input9 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      -5,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -4,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -3,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -2,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      -1,
      left,
      top,
      time2
    ]
  ];
  var output9 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      [
        -5,
        left,
        top,
        time1
      ],
      [
        -4,
        left,
        top,
        time1
      ],
      [
        -3,
        left,
        top,
        time1
      ],
      [
        -2,
        left,
        top,
        time1
      ],
      [
        -1,
        left,
        top,
        time2
      ]
    ]
  ];
  var result9 = compact( input9 );
  deepEqual( result9, output9,
                         "nesting expected at activity type with five values");

  var input10 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1
    ]
  ];
  var output10 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [],
      []
    ]
  ];
  var result10 = compact( input10 );
  deepEqual( result10, output10,
     "nesting expected at details with empty arrays when details are omitted");

  var input11 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      details,
      specifics
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      details,
      specifics
    ],
  ];
  var output11 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [
        details,
        specifics
      ],
      [
        details,
        specifics
      ]
    ]
  ];
  var result11 = compact( input11 );
  deepEqual( result11, output11,
                                     "nesting expected at details even with " +
                                      "common value of details and specifics");

  var input12 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [1,2,3],
      specifics
    ],
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [1,5,9]
    ],
  ];
  var output12 = [
    custom,
    major,minor,patch,
    [
      container,
      path,
      activity,
      left,
      top,
      time1,
      [
        [1,2,3],
        specifics
      ],
      [
        [1,5,9]
      ]
    ]
  ];
  var result12 = compact( input12 );
  deepEqual( result12, output12,
                      "nesting expected at details with or without specifics");
});
<|endoftext|>