<commit_before>(function (window) {
    'use strict';

    var document = window.document,
        KEY_CHAR_MAP,
        ARABIC_START = 0x0600, // Starting Unicode point of Arabic range
        ARABIC_END = 0x06FF,   // Ending Unicode point of Arabic range
        UYGHUR_VOWELS,
        ARABIC_PUNCTUATION_MARKS,
        HAMZA,
        CTRL_KEY_LISTENERS,
        DELIMITER = ':',
        inputMode = {}, // 0: Uyghur, 1: Latin
        addToAll = window.attachAll || false,
        whitelist = window.bedit_allow || [],
        blacklist = window.bedit_deny || [];

    function getChar(unicode) {
        return String.fromCharCode(unicode);
    }

    function indexOf(array, element) {
        var i;

        if (array && array.length) {
            for (i = 0; i < array.length; i++) {
                if (array[i] === element) {
                    return i;
                }
            }
        }

        return -1;
    }

    function initialize() {
        // ASCII -> Unicode of Uyghur characters
        KEY_CHAR_MAP = {};

        KEY_CHAR_MAP.a    = getChar(0x06BE); // h
        KEY_CHAR_MAP.b    = getChar(0x0628); // b
        KEY_CHAR_MAP.c    = getChar(0x063A); // gh
        KEY_CHAR_MAP.D    = getChar(0x0698); // zh
        KEY_CHAR_MAP.d    = getChar(0x062F); // d
        KEY_CHAR_MAP.e    = getChar(0x06D0); // :e
        KEY_CHAR_MAP.F    = getChar(0x0641); // f
        KEY_CHAR_MAP.f    = getChar(0x0627); // a
        KEY_CHAR_MAP.G    = getChar(0x06AF); // g
        KEY_CHAR_MAP.g    = getChar(0x06D5); // e
        KEY_CHAR_MAP.H    = getChar(0x062E); // x
        KEY_CHAR_MAP.h    = getChar(0x0649); // i
        KEY_CHAR_MAP.i    = getChar(0x06AD); // ng
        KEY_CHAR_MAP.J    = getChar(0x062C); // j
        KEY_CHAR_MAP.j    = getChar(0x0642); // q
        KEY_CHAR_MAP.K    = getChar(0x06C6); // :o
        KEY_CHAR_MAP.k    = getChar(0x0643); // k
        KEY_CHAR_MAP.l    = getChar(0x0644); // l
        KEY_CHAR_MAP.m    = getChar(0x0645); // m
        KEY_CHAR_MAP.n    = getChar(0x0646); // n
        KEY_CHAR_MAP.o    = getChar(0x0648); // o
        KEY_CHAR_MAP.p    = getChar(0x067E); // p
        KEY_CHAR_MAP.q    = getChar(0x0686); // ch
        KEY_CHAR_MAP.r    = getChar(0x0631); // r
        KEY_CHAR_MAP.s    = getChar(0x0633); // s
        KEY_CHAR_MAP.t    = getChar(0x062A); // t
        KEY_CHAR_MAP.u    = getChar(0x06C7); // u
        KEY_CHAR_MAP.v    = getChar(0x06C8); // :u
        KEY_CHAR_MAP.w    = getChar(0x06CB); // w
        KEY_CHAR_MAP.x    = getChar(0x0634); // sh
        KEY_CHAR_MAP.y    = getChar(0x064A); // y
        KEY_CHAR_MAP.z    = getChar(0x0632); // z
        KEY_CHAR_MAP['/'] = getChar(0x0626); // hamza

        // Uyghur punctuation marks
        KEY_CHAR_MAP[';'] = getChar(0x061B);
        KEY_CHAR_MAP['?'] = getChar(0x061F);
        KEY_CHAR_MAP[','] = getChar(0x060C);

        // Invert parentheses, square brackets, and curly braces for RTL layout.
        KEY_CHAR_MAP['('] = ')';
        KEY_CHAR_MAP[')'] = '(';
        KEY_CHAR_MAP['['] = ']';
        KEY_CHAR_MAP[']'] = '[';
        KEY_CHAR_MAP['}'] = getChar(0x00AB);
        KEY_CHAR_MAP['{'] = getChar(0x00BB);
        KEY_CHAR_MAP['<'] = '>'; // Sticking to the standard.
        KEY_CHAR_MAP['>'] = '<'; // Sticking to the standard.

        UYGHUR_VOWELS = [
            KEY_CHAR_MAP.f,
            KEY_CHAR_MAP.g,
            KEY_CHAR_MAP.e,
            KEY_CHAR_MAP.h,
            KEY_CHAR_MAP.o,
            KEY_CHAR_MAP.u,
            KEY_CHAR_MAP.K,
            KEY_CHAR_MAP.v
        ];

        HAMZA = KEY_CHAR_MAP['/'];

        ARABIC_PUNCTUATION_MARKS = [
            KEY_CHAR_MAP[';'],
            KEY_CHAR_MAP['?'],
            KEY_CHAR_MAP[',']
        ];

        CTRL_KEY_LISTENERS = {};

        CTRL_KEY_LISTENERS.K = toggleInputMode;
        // [Ctrl-T] can no longer be used for inverting the input direction in WebKit (Blink), see:
        //   https://code.google.com/p/chromium/issues/detail?id=33056
        // Therefore, use 'Y' ('Y' as in the Uyghur word 'Yönilish')
        CTRL_KEY_LISTENERS.Y = toggleInputDirection;
    }

    function toggleInputMode(event) {
        var element = event.srcElement || event.target;

        inputMode[element.name] = 1 - inputMode[element.name];
    }

    function toggleInputDirection(event) {
        var element = event.srcElement || event.target;

        element.style.direction = (element.style.direction === 'ltr' ? 'rtl' : 'ltr');
    }

    function isArabicLetter(ch) {
        var unicode = ch.charCodeAt(0);

        return (unicode >= ARABIC_START && unicode < ARABIC_END) && indexOf(ARABIC_PUNCTUATION_MARKS, ch) < 0;
    }

    function isUyghurVowel(ch) {
        return isArabicLetter(ch) && indexOf(UYGHUR_VOWELS, ch) >= 0;
    }

    function prependHamzaConditionally(ch, element) {
        var result = ch,
            start = element.selectionStart;

        if (isUyghurVowel(ch)) {
            if (start === 0) { // cursor is at the begginning of the input area
                result = HAMZA + ch;
            } else {
                // by using 'start' only, this if statement takes care of both cases of empty-selection and non-empty selection.
                if ((!isArabicLetter(element.value[start - 1]) || isUyghurVowel(element.value[start - 1]))) {
                    result = HAMZA + ch;
                } else {
                    result = ch;
                }
            }
        }

        return result;
    }

    function insert(element, ch) {
        var previousSelectionStart,
            currentSelectionStart,
            previousScrollTop,
            previousScrollLeft;

        if (document.selection && document.selection.createRange) { // Trident 5.0+
            document.selection.createRange().text = ch;
        } else { // W3C
            previousSelectionStart = element.selectionStart;

            // Gecko scrolls to top in textarea after input, fix this.
            if (element.type === 'textarea' && element.scrollTop) {
                previousScrollTop = element.scrollTop;
                previousScrollLeft = element.scrollLeft;
            }

            // Automatically prepend hamza if ch is an Uyghur vowel and the conditions hold true.
            ch = prependHamzaConditionally(ch, element);

            element.value = element.value.substring(0, element.selectionStart) +
                ch + element.value.substring(element.selectionEnd);

            if (previousScrollTop) {
                element.scrollTop = previousScrollTop;
                element.scrollLeft = previousScrollLeft;
            }

            currentSelectionStart = previousSelectionStart + ch.length;
            element.setSelectionRange(currentSelectionStart, currentSelectionStart);
        }
    }

    function keydownListener(e) {
        var event = e || window.event,
            isMetaKey = event.ctrlKey || event.metaKey,
            keyCode = event.keyCode || event.which,
            c = getChar(keyCode).toUpperCase();

        if (isMetaKey && CTRL_KEY_LISTENERS[c]) {
            CTRL_KEY_LISTENERS[c](event);

            if (event.preventDefault) {
                event.preventDefault();
                event.stopPropagation();
            } else {
                event.returnValue = false;
                event.cancelBubble = true;
            }
        }
    }

    function keypressListener(e) {
        var event = e || window.event,
            target = event.srcElement || event.target,
            isMetaKey = event.ctrlKey || event.metaKey,
            keyCode = event.keyCode || event.which,
            c = getChar(keyCode),
            isAlphabetic = /^[A-Z]{1}$/.test(c.toUpperCase()),
            preventDefaultAndStopPropagation = false;

        // The extra check for the meta key ([Ctrl]) is because:
        //   https://bugzilla.mozilla.org/show_bug.cgi?id=501496
        if (!isMetaKey && inputMode[target.name] === 0) {
            if (KEY_CHAR_MAP[c]) {
                if (event.keyCode && !event.which) { // Trident 4.0-
                    event.keyCode = KEY_CHAR_MAP[c].charCodeAt(0);
                } else {                             // W3C event is read-only.
                    insert(target, KEY_CHAR_MAP[c]);
                }
                preventDefaultAndStopPropagation = true;
            } else if (isAlphabetic) {
                event.returnValue = false;
                preventDefaultAndStopPropagation = true;
            }
        }

        if (preventDefaultAndStopPropagation) {
            if (event.preventDefault) {
                event.preventDefault();
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        }
    }

    function getAllEditBoxes() {
        var inputs,
            textAreas,
            all = [],
            i;

        inputs = document.getElementsByTagName('input');
        textAreas = document.getElementsByTagName('textarea');

        for (i = 0; i < inputs.length; i++) {
            if (inputs[i].type.toLowerCase() === 'text') {
                all.push(inputs[i]);
            }
        }

        for (i = 0; i < textAreas.length; i++) {
            all.push(textAreas[i]);
        }

        return all;
    }

    function addEventListener(element, event, listener) {
        if (element.addEventListener) { // W3C
            element.removeEventListener(event, listener, false);
            element.addEventListener(event, listener, false);
        } else if (element.attachEvent) { // Trident 4.0-
            element.detachEvent('on' + event, listener);
            element.attachEvent('on' + event, listener);
        }
    }

    function addKeyboardEventListeners() {
        var all,
            element,
            cancel = true,
            i;

        if (!addToAll) {
            if (whitelist.length) {
                cancel = false;
                whitelist = whitelist.split(DELIMITER);
            }

            if (blacklist.length) {
                cancel = false;
                addToAll = true; // A blacklist by itself implies 'addToAll'.
                blacklist = blacklist.split(DELIMITER);
            }
        } else {
            if (blacklist.length) {
                cancel = false;
                blacklist = blacklist.split(DELIMITER);
            }
        }

        if (cancel) {
            return;
        }

        all = getAllEditBoxes();

        if (addToAll) {
            for (i = 0; i < all.length; i++) {
                element = all[i];

                if (indexOf(blacklist, element.name) < 0) {
                    addEventListener(element, 'keydown', keydownListener);
                    addEventListener(element, 'keypress', keypressListener);

                    // Initialize the input mode for this element.
                    inputMode[element.name] = 0;
                }
            }
        } else {
            for (i = 0; i < all.length; i++) {
                element = all[i];

                if (indexOf(whitelist, element.name) >= 0) {
                    addEventListener(element, 'keydown', keydownListener);
                    addEventListener(element, 'keypress', keypressListener);

                    // Initialize the input mode for this element.
                    inputMode[element.name] = 0;
                }
            }
        }
    }

    function load() {
        initialize();
        addKeyboardEventListeners();
    }

    (function onDomReady() {
        var isDomReadyCallbackCalled = false,
            isDomReadyListenerAdded = false;

        function callDomReadyCallback() {
            if (!isDomReadyCallbackCalled) {
                if (!document.body) { // In case IE gets a little overzealous.
                    return setTimeout(callDomReadyCallback, 1);
                }

                isDomReadyCallbackCalled = true;
                load();
            }
        }

        function domReadyListener() {
            if (document.addEventListener) {
                document.removeEventListener('DOMContentLoaded', domReadyListener);
            } else if (document.attachEvent) {
                // Execution gets here only when document.readyState !== 'loading'
                document.detachEvent('onreadystatechange', domReadyListener);
            } else {
                return;
            }

            callDomReadyCallback();
        }

        function ieScrollCheck() {
            if (isDomReadyCallbackCalled) {
                return;
            }

            try {
                document.documentElement.doScroll('left');
            } catch (e) {
                setTimeout(ieScrollCheck, 1);
                return;
            }

            callDomReadyCallback();
        }

        function addDomReadyListener() {
            var isTopLevel = false;

            if (isDomReadyListenerAdded) {
                return;
            }
            isDomReadyListenerAdded = true;

            // In case DOM has already been loaded, call the function right away.
            if (document.readyState !== 'loading') {
                callDomReadyCallback();
                return;
            }

            if (document.addEventListener) {
                document.addEventListener('DOMContentLoaded', domReadyListener, false);
                window.addEventListener('load', domReadyListener, false); // Fallback.
            } else if (document.attachEvent) {
                document.attachEvent('onreadystatechange', domReadyListener);
                document.attachEvent('onload', domReadyListener); // Fallback.

                // http://javascript.nwbox.com/IEContentLoaded/
                try {
                    isTopLevel = !window.frameElement;
                } catch (e) {}

                if (document.documentElement.doScroll && isTopLevel) {
                    ieScrollCheck();
                }
            }
        }

        addDomReadyListener();
    }) ();
}) (window);

<commit_msg>removed a pair of redundant parentheses.<commit_after>(function (window) {
    'use strict';

    var document = window.document,
        KEY_CHAR_MAP,
        ARABIC_START = 0x0600, // Starting Unicode point of Arabic range
        ARABIC_END = 0x06FF,   // Ending Unicode point of Arabic range
        UYGHUR_VOWELS,
        ARABIC_PUNCTUATION_MARKS,
        HAMZA,
        CTRL_KEY_LISTENERS,
        DELIMITER = ':',
        inputMode = {}, // 0: Uyghur, 1: Latin
        addToAll = window.attachAll || false,
        whitelist = window.bedit_allow || [],
        blacklist = window.bedit_deny || [];

    function getChar(unicode) {
        return String.fromCharCode(unicode);
    }

    function indexOf(array, element) {
        var i;

        if (array && array.length) {
            for (i = 0; i < array.length; i++) {
                if (array[i] === element) {
                    return i;
                }
            }
        }

        return -1;
    }

    function initialize() {
        // ASCII -> Unicode of Uyghur characters
        KEY_CHAR_MAP = {};

        KEY_CHAR_MAP.a    = getChar(0x06BE); // h
        KEY_CHAR_MAP.b    = getChar(0x0628); // b
        KEY_CHAR_MAP.c    = getChar(0x063A); // gh
        KEY_CHAR_MAP.D    = getChar(0x0698); // zh
        KEY_CHAR_MAP.d    = getChar(0x062F); // d
        KEY_CHAR_MAP.e    = getChar(0x06D0); // :e
        KEY_CHAR_MAP.F    = getChar(0x0641); // f
        KEY_CHAR_MAP.f    = getChar(0x0627); // a
        KEY_CHAR_MAP.G    = getChar(0x06AF); // g
        KEY_CHAR_MAP.g    = getChar(0x06D5); // e
        KEY_CHAR_MAP.H    = getChar(0x062E); // x
        KEY_CHAR_MAP.h    = getChar(0x0649); // i
        KEY_CHAR_MAP.i    = getChar(0x06AD); // ng
        KEY_CHAR_MAP.J    = getChar(0x062C); // j
        KEY_CHAR_MAP.j    = getChar(0x0642); // q
        KEY_CHAR_MAP.K    = getChar(0x06C6); // :o
        KEY_CHAR_MAP.k    = getChar(0x0643); // k
        KEY_CHAR_MAP.l    = getChar(0x0644); // l
        KEY_CHAR_MAP.m    = getChar(0x0645); // m
        KEY_CHAR_MAP.n    = getChar(0x0646); // n
        KEY_CHAR_MAP.o    = getChar(0x0648); // o
        KEY_CHAR_MAP.p    = getChar(0x067E); // p
        KEY_CHAR_MAP.q    = getChar(0x0686); // ch
        KEY_CHAR_MAP.r    = getChar(0x0631); // r
        KEY_CHAR_MAP.s    = getChar(0x0633); // s
        KEY_CHAR_MAP.t    = getChar(0x062A); // t
        KEY_CHAR_MAP.u    = getChar(0x06C7); // u
        KEY_CHAR_MAP.v    = getChar(0x06C8); // :u
        KEY_CHAR_MAP.w    = getChar(0x06CB); // w
        KEY_CHAR_MAP.x    = getChar(0x0634); // sh
        KEY_CHAR_MAP.y    = getChar(0x064A); // y
        KEY_CHAR_MAP.z    = getChar(0x0632); // z
        KEY_CHAR_MAP['/'] = getChar(0x0626); // hamza

        // Uyghur punctuation marks
        KEY_CHAR_MAP[';'] = getChar(0x061B);
        KEY_CHAR_MAP['?'] = getChar(0x061F);
        KEY_CHAR_MAP[','] = getChar(0x060C);

        // Invert parentheses, square brackets, and curly braces for RTL layout.
        KEY_CHAR_MAP['('] = ')';
        KEY_CHAR_MAP[')'] = '(';
        KEY_CHAR_MAP['['] = ']';
        KEY_CHAR_MAP[']'] = '[';
        KEY_CHAR_MAP['}'] = getChar(0x00AB);
        KEY_CHAR_MAP['{'] = getChar(0x00BB);
        KEY_CHAR_MAP['<'] = '>'; // Sticking to the standard.
        KEY_CHAR_MAP['>'] = '<'; // Sticking to the standard.

        UYGHUR_VOWELS = [
            KEY_CHAR_MAP.f,
            KEY_CHAR_MAP.g,
            KEY_CHAR_MAP.e,
            KEY_CHAR_MAP.h,
            KEY_CHAR_MAP.o,
            KEY_CHAR_MAP.u,
            KEY_CHAR_MAP.K,
            KEY_CHAR_MAP.v
        ];

        HAMZA = KEY_CHAR_MAP['/'];

        ARABIC_PUNCTUATION_MARKS = [
            KEY_CHAR_MAP[';'],
            KEY_CHAR_MAP['?'],
            KEY_CHAR_MAP[',']
        ];

        CTRL_KEY_LISTENERS = {};

        CTRL_KEY_LISTENERS.K = toggleInputMode;
        // [Ctrl-T] can no longer be used for inverting the input direction in WebKit (Blink), see:
        //   https://code.google.com/p/chromium/issues/detail?id=33056
        // Therefore, use 'Y' ('Y' as in the Uyghur word 'Yönilish')
        CTRL_KEY_LISTENERS.Y = toggleInputDirection;
    }

    function toggleInputMode(event) {
        var element = event.srcElement || event.target;

        inputMode[element.name] = 1 - inputMode[element.name];
    }

    function toggleInputDirection(event) {
        var element = event.srcElement || event.target;

        element.style.direction = (element.style.direction === 'ltr' ? 'rtl' : 'ltr');
    }

    function isArabicLetter(ch) {
        var unicode = ch.charCodeAt(0);

        return (unicode >= ARABIC_START && unicode < ARABIC_END) && indexOf(ARABIC_PUNCTUATION_MARKS, ch) < 0;
    }

    function isUyghurVowel(ch) {
        return isArabicLetter(ch) && indexOf(UYGHUR_VOWELS, ch) >= 0;
    }

    function prependHamzaConditionally(ch, element) {
        var result = ch,
            start = element.selectionStart;

        if (isUyghurVowel(ch)) {
            if (start === 0) { // cursor is at the begginning of the input area
                result = HAMZA + ch;
            } else {
                // by using 'start' only, this if statement takes care of both cases of empty-selection and non-empty selection.
                if (!isArabicLetter(element.value[start - 1]) || isUyghurVowel(element.value[start - 1])) {
                    result = HAMZA + ch;
                } else {
                    result = ch;
                }
            }
        }

        return result;
    }

    function insert(element, ch) {
        var previousSelectionStart,
            currentSelectionStart,
            previousScrollTop,
            previousScrollLeft;

        if (document.selection && document.selection.createRange) { // Trident 5.0+
            document.selection.createRange().text = ch;
        } else { // W3C
            previousSelectionStart = element.selectionStart;

            // Gecko scrolls to top in textarea after input, fix this.
            if (element.type === 'textarea' && element.scrollTop) {
                previousScrollTop = element.scrollTop;
                previousScrollLeft = element.scrollLeft;
            }

            // Automatically prepend hamza if ch is an Uyghur vowel and the conditions hold true.
            ch = prependHamzaConditionally(ch, element);

            element.value = element.value.substring(0, element.selectionStart) +
                ch + element.value.substring(element.selectionEnd);

            if (previousScrollTop) {
                element.scrollTop = previousScrollTop;
                element.scrollLeft = previousScrollLeft;
            }

            currentSelectionStart = previousSelectionStart + ch.length;
            element.setSelectionRange(currentSelectionStart, currentSelectionStart);
        }
    }

    function keydownListener(e) {
        var event = e || window.event,
            isMetaKey = event.ctrlKey || event.metaKey,
            keyCode = event.keyCode || event.which,
            c = getChar(keyCode).toUpperCase();

        if (isMetaKey && CTRL_KEY_LISTENERS[c]) {
            CTRL_KEY_LISTENERS[c](event);

            if (event.preventDefault) {
                event.preventDefault();
                event.stopPropagation();
            } else {
                event.returnValue = false;
                event.cancelBubble = true;
            }
        }
    }

    function keypressListener(e) {
        var event = e || window.event,
            target = event.srcElement || event.target,
            isMetaKey = event.ctrlKey || event.metaKey,
            keyCode = event.keyCode || event.which,
            c = getChar(keyCode),
            isAlphabetic = /^[A-Z]{1}$/.test(c.toUpperCase()),
            preventDefaultAndStopPropagation = false;

        // The extra check for the meta key ([Ctrl]) is because:
        //   https://bugzilla.mozilla.org/show_bug.cgi?id=501496
        if (!isMetaKey && inputMode[target.name] === 0) {
            if (KEY_CHAR_MAP[c]) {
                if (event.keyCode && !event.which) { // Trident 4.0-
                    event.keyCode = KEY_CHAR_MAP[c].charCodeAt(0);
                } else {                             // W3C event is read-only.
                    insert(target, KEY_CHAR_MAP[c]);
                }
                preventDefaultAndStopPropagation = true;
            } else if (isAlphabetic) {
                event.returnValue = false;
                preventDefaultAndStopPropagation = true;
            }
        }

        if (preventDefaultAndStopPropagation) {
            if (event.preventDefault) {
                event.preventDefault();
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        }
    }

    function getAllEditBoxes() {
        var inputs,
            textAreas,
            all = [],
            i;

        inputs = document.getElementsByTagName('input');
        textAreas = document.getElementsByTagName('textarea');

        for (i = 0; i < inputs.length; i++) {
            if (inputs[i].type.toLowerCase() === 'text') {
                all.push(inputs[i]);
            }
        }

        for (i = 0; i < textAreas.length; i++) {
            all.push(textAreas[i]);
        }

        return all;
    }

    function addEventListener(element, event, listener) {
        if (element.addEventListener) { // W3C
            element.removeEventListener(event, listener, false);
            element.addEventListener(event, listener, false);
        } else if (element.attachEvent) { // Trident 4.0-
            element.detachEvent('on' + event, listener);
            element.attachEvent('on' + event, listener);
        }
    }

    function addKeyboardEventListeners() {
        var all,
            element,
            cancel = true,
            i;

        if (!addToAll) {
            if (whitelist.length) {
                cancel = false;
                whitelist = whitelist.split(DELIMITER);
            }

            if (blacklist.length) {
                cancel = false;
                addToAll = true; // A blacklist by itself implies 'addToAll'.
                blacklist = blacklist.split(DELIMITER);
            }
        } else {
            if (blacklist.length) {
                cancel = false;
                blacklist = blacklist.split(DELIMITER);
            }
        }

        if (cancel) {
            return;
        }

        all = getAllEditBoxes();

        if (addToAll) {
            for (i = 0; i < all.length; i++) {
                element = all[i];

                if (indexOf(blacklist, element.name) < 0) {
                    addEventListener(element, 'keydown', keydownListener);
                    addEventListener(element, 'keypress', keypressListener);

                    // Initialize the input mode for this element.
                    inputMode[element.name] = 0;
                }
            }
        } else {
            for (i = 0; i < all.length; i++) {
                element = all[i];

                if (indexOf(whitelist, element.name) >= 0) {
                    addEventListener(element, 'keydown', keydownListener);
                    addEventListener(element, 'keypress', keypressListener);

                    // Initialize the input mode for this element.
                    inputMode[element.name] = 0;
                }
            }
        }
    }

    function load() {
        initialize();
        addKeyboardEventListeners();
    }

    (function onDomReady() {
        var isDomReadyCallbackCalled = false,
            isDomReadyListenerAdded = false;

        function callDomReadyCallback() {
            if (!isDomReadyCallbackCalled) {
                if (!document.body) { // In case IE gets a little overzealous.
                    return setTimeout(callDomReadyCallback, 1);
                }

                isDomReadyCallbackCalled = true;
                load();
            }
        }

        function domReadyListener() {
            if (document.addEventListener) {
                document.removeEventListener('DOMContentLoaded', domReadyListener);
            } else if (document.attachEvent) {
                // Execution gets here only when document.readyState !== 'loading'
                document.detachEvent('onreadystatechange', domReadyListener);
            } else {
                return;
            }

            callDomReadyCallback();
        }

        function ieScrollCheck() {
            if (isDomReadyCallbackCalled) {
                return;
            }

            try {
                document.documentElement.doScroll('left');
            } catch (e) {
                setTimeout(ieScrollCheck, 1);
                return;
            }

            callDomReadyCallback();
        }

        function addDomReadyListener() {
            var isTopLevel = false;

            if (isDomReadyListenerAdded) {
                return;
            }
            isDomReadyListenerAdded = true;

            // In case DOM has already been loaded, call the function right away.
            if (document.readyState !== 'loading') {
                callDomReadyCallback();
                return;
            }

            if (document.addEventListener) {
                document.addEventListener('DOMContentLoaded', domReadyListener, false);
                window.addEventListener('load', domReadyListener, false); // Fallback.
            } else if (document.attachEvent) {
                document.attachEvent('onreadystatechange', domReadyListener);
                document.attachEvent('onload', domReadyListener); // Fallback.

                // http://javascript.nwbox.com/IEContentLoaded/
                try {
                    isTopLevel = !window.frameElement;
                } catch (e) {}

                if (document.documentElement.doScroll && isTopLevel) {
                    ieScrollCheck();
                }
            }
        }

        addDomReadyListener();
    }) ();
}) (window);

<|endoftext|>


<commit_before>var roles = {
	abstract: [
		'command',
		'composite',
		'input',
		'landmark',
		'range',
		'roletype',
		'section',
		'sectionhead',
		'select',
		'structure',
		'widget',
		'window',
	],
	widget: {
		standalone: [
			'alert',
			'alertdialog',
			'button',
			'checkbox',
			'dialog',
			'gridcell',
			'link',
			'log',
			'marquee',
			'menuitem',
			'menuitemcheckbox',
			'menuitemradio',
			'option',
			'progressbar',
			'radio',
			'scrollbar',
			'slider',
			'spinbutton',
			'status',
			'tab',
			'tabpanel',
			'textbox',
			'timer',
			'tooltip',
			'treeitem',
		],
		composite: [
			'combobox',
			'grid',
			'listbox',
			'menu',
			'menubar',
			'radiogroup',
			'tablist',
			'tree',
			'treegrid',
		],
	},
	structure: [
		'article',
		'columnheader',
		'definition',
		'directory',
		'document',
		'group',
		'heading',
		'img',
		'list',
		'listitem',
		'math',
		'note',
		'presentation',
		'region',
		'row',
		'rowgroup',
		'rowheader',
		'separator',
		'toolbar',
	],
	landmark: [
		'application',
		'banner',
		'complementary',
		'contentinfo',
		'form',
		'main',
		'navigation',
		'search',
	],
};

// https://www.w3.org/TR/html-aria/#docconformance
var extraSelectors = {
	article: ['article'],
	banner: ['body > header'],
	button: [
		'button',
		'input[type="button"]',
		'input[type="image"]',
		'input[type="reset"]',
		'input[type="submit"]',
		'summary',
	],
	cell: ['td'],
	checkbox: ['input[type="checkbox"]'],
	columnheader: ['th[scope="col"]'],
	combobox: [
		'input[type="email"][list]',
		'input[type="search"][list]',
		'input[type="tel"][list]',
		'input[type="text"][list]',
		'input[type="url"][list]',
	],
	complementary: ['aside'],
	contentinfo: ['body > footer'],
	definition: ['dd'],
	dialog: ['dialog'],
	document: ['body'],
	figure: ['figure'],
	form: ['form'],
	group: ['details', 'optgroup'],
	heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
	img: ['img:not([alt=""])'],
	link: ['a[href]', 'area[href]', 'link[href]'],
	listbox: ['datalist', 'select'],
	list: ['dl', 'ol', 'ul'],
	listitem: ['ul > li', 'ol > li'],
	main: ['main'],
	math: ['math'],
	menuitemcheckbox: ['menuitem[type="checkbox"]'],
	menuitem: ['menuitem[type="command"]'],
	menuitemradio: ['menuitem[type="radio"]'],
	menu: ['menu[type="context"]'],
	navigation: ['nav'],
	option: ['option'],
	progressbar: ['progress'],
	radio: ['input[type="radio"]'],
	region: ['section'],
	rowgroup: ['tbody', 'thead', 'tfoot'],
	rowheader: ['th[scope="row"]'],
	row: ['tr'],
	searchbox: ['input[type="search"]:not([list])'],
	separator: ['hr'],
	slider: ['input[type="range"]'],
	spinbutton: ['input[type="number"]'],
	status: ['output'],
	table: ['table'],
	textbox: [
		'input[type="email"]:not([list])',
		'input[type="tel"]:not([list])',
		'input[type="text"]:not([list])',
		'input[type="url"]:not([list])',
		'textarea',
	],
};

// https://www.w3.org/TR/wai-aria/roles
var subRoles = {
	command: ['button', 'link', 'menuitem'],
	composite: ['grid', 'select', 'tablist'],
	input: ['checkbox', 'option', 'select', 'spinbutton', 'textbox'],
	landmark: [
		'application',
		'banner',
		'complementary',
		'contentinfo',
		'form',
		'main',
		'navigation',
		'search',
	],
	range: ['progressbar', 'spinbutton'],
	roletype: ['structure', 'widget', 'window'],
	section: [
		'definition',
		'gridcell',
		'group',
		'img',
		'listitem',
		'marquee',
		'math',
		'note',
		'region',
		'tooltip',
	],
	sectionhead: ['columnheader', 'heading', 'rowheader', 'tab'],
	select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],
	structure: [
		'document',
		'presentation',
		'section',
		'sectionhead',
		'separator',
	],
	widget: [
		'columnheader',
		'command',
		'composite',
		'gridcell',
		'input',
		'range',
		'row',
		'rowheader',
		'tab',
	],
	window: ['dialog'],
	alert: ['alertdialog'],
	checkbox: ['menuitemcheckbox'],
	dialog: ['alertdialog'],
	gridcell: ['columnheader', 'rowheader'],
	menuitem: ['menuitemcheckbox'],
	menuitemcheckbox: ['menuitemradio'],
	option: ['treeitem'],
	radio: ['menuitemradio'],
	status: ['timer'],
	grid: ['treegrid'],
	menu: ['menubar'],
	tree: ['treegrid'],
	directory: ['tablist'],
	document: ['article'],
	group: ['row', 'rowgroup', 'select', 'toolbar'],
	list: ['directory', 'listbox', 'menu'],
	listitem: ['treeitem'],
	region: [
		'alert',
		'article',
		'grid',
		'landmark',
		'list',
		'log',
		'status',
		'tabpanel',
	],
};

var getChildRoles = function(role) {
	var children = subRoles[role] || [];
	var descendents = children.map(getChildRoles);

	var result = [role];

	children.forEach(function(r) {
		if (result.indexOf(r) == -1) {
			result.push(r);
		}
	});
	descendents.forEach(function(list) {
		list.forEach(function(r) {
			if (result.indexOf(r) == -1) {
				result.push(r);
			}
		});
	});

	return result;
};

var _getSelector = function(role) {
	var selector = '[role="' + role + '"]';
	var extra = (extraSelectors[role] || []).map(sel => sel + ':not([role])');
	return [selector].concat(extra).join(',');
};

var getSelector = function(role) {
	var roles = getChildRoles(role);
	return roles.map(_getSelector).join(',');
};

var getRole = function(el) {
	for (var i = 0; i < roles.landmark.length; i++) {
		var role = roles.landmark[i];
		if (el.matches(_getSelector(role))) {
			return role;
		}
	}
};

// http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
var getName = function(el) {
	var ret = '';

	if (el.matches('[aria-labelledby]')) {
		var id = el.getAttribute('aria-labelledby');
		var label = document.getElementById(id);
		ret = getName(label);
	} else if (el.matches('[aria-label]')) {
		ret = el.getAttribute('aria-label');
	} else if (el.label && el.labels.length > 0) {
		ret = getName(el.labels[0]);
	} else if (el.placeholder) {
		ret = el.placeholder;
	// figcaption
	} else if (el.alt) {
		ret = el.alt;
	// caption
	// table
	} else if (el.textContent) {
		ret = el.textContent;
	} else if (el.title) {
		ret = el.title;
	} else if (el.value) {
		ret = el.value;
	}

	return ret.trim().replace(/\s+/g, ' ');
};

var createDialog = function() {
	var dialog = document.createElement('dialog');
	dialog.addEventListener('close', function() {
		dialog.parentNode.removeChild(dialog);
	});
	document.body.appendChild(dialog);
	return dialog;
};

var createList = function(items) {
	var ul = document.createElement('ul');
	Array.prototype.forEach.call(items, function(item) {
		var li = document.createElement('li');
		li.appendChild(item);
		ul.appendChild(li);
	});
	return ul;
};

document.addEventListener('keyup', function(event) {
	if (event.ctrlKey && !event.altKey && event.key == 'm') {
		var dialog = createDialog();

		var landmarks = document.querySelectorAll(getSelector('landmark'));
		var links = Array.prototype.map.call(landmarks, function(el) {
			var a = document.createElement('a');
			a.href = '#';
			a.addEventListener('click', function(event) {
				event.preventDefault();
				dialog.close();
				el.tabIndex = -1;
				el.focus();
			});
			a.textContent = getRole(el);
			return a;
		});

		dialog.appendChild(createList(links));
		dialog.showModal();
	}
});
<commit_msg>highlight incorrect selectors<commit_after>var roles = {
	abstract: [
		'command',
		'composite',
		'input',
		'landmark',
		'range',
		'roletype',
		'section',
		'sectionhead',
		'select',
		'structure',
		'widget',
		'window',
	],
	widget: {
		standalone: [
			'alert',
			'alertdialog',
			'button',
			'checkbox',
			'dialog',
			'gridcell',
			'link',
			'log',
			'marquee',
			'menuitem',
			'menuitemcheckbox',
			'menuitemradio',
			'option',
			'progressbar',
			'radio',
			'scrollbar',
			'slider',
			'spinbutton',
			'status',
			'tab',
			'tabpanel',
			'textbox',
			'timer',
			'tooltip',
			'treeitem',
		],
		composite: [
			'combobox',
			'grid',
			'listbox',
			'menu',
			'menubar',
			'radiogroup',
			'tablist',
			'tree',
			'treegrid',
		],
	},
	structure: [
		'article',
		'columnheader',
		'definition',
		'directory',
		'document',
		'group',
		'heading',
		'img',
		'list',
		'listitem',
		'math',
		'note',
		'presentation',
		'region',
		'row',
		'rowgroup',
		'rowheader',
		'separator',
		'toolbar',
	],
	landmark: [
		'application',
		'banner',
		'complementary',
		'contentinfo',
		'form',
		'main',
		'navigation',
		'search',
	],
};

// https://www.w3.org/TR/html-aria/#docconformance
var extraSelectors = {
	article: ['article'],
	button: [
		'button',
		'input[type="button"]',
		'input[type="image"]',
		'input[type="reset"]',
		'input[type="submit"]',
		'summary',
	],
	cell: ['td'],
	checkbox: ['input[type="checkbox"]'],
	combobox: [
		'input[type="email"][list]',
		'input[type="search"][list]',
		'input[type="tel"][list]',
		'input[type="text"][list]',
		'input[type="url"][list]',
	],
	complementary: ['aside'],
	definition: ['dd'],
	dialog: ['dialog'],
	document: ['body'],
	figure: ['figure'],
	form: ['form'],
	group: ['details', 'optgroup'],
	heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
	img: ['img:not([alt=""])'],
	link: ['a[href]', 'area[href]', 'link[href]'],
	listbox: ['datalist', 'select'],
	list: ['dl', 'ol', 'ul'],
	listitem: ['ul > li', 'ol > li'],
	main: ['main'],
	math: ['math'],
	menuitemcheckbox: ['menuitem[type="checkbox"]'],
	menuitem: ['menuitem[type="command"]'],
	menuitemradio: ['menuitem[type="radio"]'],
	menu: ['menu[type="context"]'],
	navigation: ['nav'],
	option: ['option'],
	progressbar: ['progress'],
	radio: ['input[type="radio"]'],
	region: ['section'],
	rowgroup: ['tbody', 'thead', 'tfoot'],
	row: ['tr'],
	searchbox: ['input[type="search"]:not([list])'],
	separator: ['hr'],
	slider: ['input[type="range"]'],
	spinbutton: ['input[type="number"]'],
	status: ['output'],
	table: ['table'],
	textbox: [
		'input[type="email"]:not([list])',
		'input[type="tel"]:not([list])',
		'input[type="text"]:not([list])',
		'input[type="url"]:not([list])',
		'textarea',
	],

	// `body >` is not correct
	banner: ['body > header'],
	contentinfo: ['body > footer'],

	// if scope is missing, it is calculated automatically
	rowheader: ['th[scope="row"]'],
	columnheader: ['th[scope="col"]'],
};

// https://www.w3.org/TR/wai-aria/roles
var subRoles = {
	command: ['button', 'link', 'menuitem'],
	composite: ['grid', 'select', 'tablist'],
	input: ['checkbox', 'option', 'select', 'spinbutton', 'textbox'],
	landmark: [
		'application',
		'banner',
		'complementary',
		'contentinfo',
		'form',
		'main',
		'navigation',
		'search',
	],
	range: ['progressbar', 'spinbutton'],
	roletype: ['structure', 'widget', 'window'],
	section: [
		'definition',
		'gridcell',
		'group',
		'img',
		'listitem',
		'marquee',
		'math',
		'note',
		'region',
		'tooltip',
	],
	sectionhead: ['columnheader', 'heading', 'rowheader', 'tab'],
	select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],
	structure: [
		'document',
		'presentation',
		'section',
		'sectionhead',
		'separator',
	],
	widget: [
		'columnheader',
		'command',
		'composite',
		'gridcell',
		'input',
		'range',
		'row',
		'rowheader',
		'tab',
	],
	window: ['dialog'],
	alert: ['alertdialog'],
	checkbox: ['menuitemcheckbox'],
	dialog: ['alertdialog'],
	gridcell: ['columnheader', 'rowheader'],
	menuitem: ['menuitemcheckbox'],
	menuitemcheckbox: ['menuitemradio'],
	option: ['treeitem'],
	radio: ['menuitemradio'],
	status: ['timer'],
	grid: ['treegrid'],
	menu: ['menubar'],
	tree: ['treegrid'],
	directory: ['tablist'],
	document: ['article'],
	group: ['row', 'rowgroup', 'select', 'toolbar'],
	list: ['directory', 'listbox', 'menu'],
	listitem: ['treeitem'],
	region: [
		'alert',
		'article',
		'grid',
		'landmark',
		'list',
		'log',
		'status',
		'tabpanel',
	],
};

var getChildRoles = function(role) {
	var children = subRoles[role] || [];
	var descendents = children.map(getChildRoles);

	var result = [role];

	children.forEach(function(r) {
		if (result.indexOf(r) == -1) {
			result.push(r);
		}
	});
	descendents.forEach(function(list) {
		list.forEach(function(r) {
			if (result.indexOf(r) == -1) {
				result.push(r);
			}
		});
	});

	return result;
};

var _getSelector = function(role) {
	var selector = '[role="' + role + '"]';
	var extra = (extraSelectors[role] || []).map(sel => sel + ':not([role])');
	return [selector].concat(extra).join(',');
};

var getSelector = function(role) {
	var roles = getChildRoles(role);
	return roles.map(_getSelector).join(',');
};

var getRole = function(el) {
	for (var i = 0; i < roles.landmark.length; i++) {
		var role = roles.landmark[i];
		if (el.matches(_getSelector(role))) {
			return role;
		}
	}
};

// http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
var getName = function(el) {
	var ret = '';

	if (el.matches('[aria-labelledby]')) {
		var id = el.getAttribute('aria-labelledby');
		var label = document.getElementById(id);
		ret = getName(label);
	} else if (el.matches('[aria-label]')) {
		ret = el.getAttribute('aria-label');
	} else if (el.label && el.labels.length > 0) {
		ret = getName(el.labels[0]);
	} else if (el.placeholder) {
		ret = el.placeholder;
	// figcaption
	} else if (el.alt) {
		ret = el.alt;
	// caption
	// table
	} else if (el.textContent) {
		ret = el.textContent;
	} else if (el.title) {
		ret = el.title;
	} else if (el.value) {
		ret = el.value;
	}

	return ret.trim().replace(/\s+/g, ' ');
};

var createDialog = function() {
	var dialog = document.createElement('dialog');
	dialog.addEventListener('close', function() {
		dialog.parentNode.removeChild(dialog);
	});
	document.body.appendChild(dialog);
	return dialog;
};

var createList = function(items) {
	var ul = document.createElement('ul');
	Array.prototype.forEach.call(items, function(item) {
		var li = document.createElement('li');
		li.appendChild(item);
		ul.appendChild(li);
	});
	return ul;
};

document.addEventListener('keyup', function(event) {
	if (event.ctrlKey && !event.altKey && event.key == 'm') {
		var dialog = createDialog();

		var landmarks = document.querySelectorAll(getSelector('landmark'));
		var links = Array.prototype.map.call(landmarks, function(el) {
			var a = document.createElement('a');
			a.href = '#';
			a.addEventListener('click', function(event) {
				event.preventDefault();
				dialog.close();
				el.tabIndex = -1;
				el.focus();
			});
			a.textContent = getRole(el);
			return a;
		});

		dialog.appendChild(createList(links));
		dialog.showModal();
	}
});
<|endoftext|>


<commit_before>var asBars = function () {
	this.redraw = function (d, quantifier) {
		var data = d.data;
		d.scale.range ([0, this.height]);

		var barWidth = this.width / data.length;
		var chartHeight = this.height;
		//TODO migrate this to the new quantifierCallback method
		var qn = quantifier ? $.proxy (
				function (selector, d) {
					var attrs = quantifier.fn.apply (quantifier.context, [d, quantifier.args, quantifier.data])
					if (!attrs) throw "Quantifier did not respond";
					var height = attrs === Number (attrs) ? attrs : attrs.height;
					d3.select (selector).attr (attrs)
						.attr ("y", chartHeight - height)
						.attr ("height", height + 1);
					if (!attrs.width) d3.select (selector).attr ("width", barWidth );
				},
			quantifier) : function (selector, d) { d3.select (selector).attr ("class", ""); };
		var margin = this.margin;
		this.svg.selectAll ("g").remove (); //HACK. Sucks.
		var bar = this.svg.selectAll ("g")
			.data (data.items ());

		bar.enter ().append ("g")
			.attr ("transform", function (d, i) { return "translate(" + i * barWidth + ", " + margin.top + ")"; });

		bar.append ("rect")
			.each (function (d) { qn (this, d); })
	}
	return this;
}
ant.charts.bars = function (container, conf) { this.init (container, conf); }
//charts.bars.prototype.constructor = asChart.init;
asChart.call (ant.charts.bars.prototype);
asBars.call (ant.charts.bars.prototype);
<commit_msg>Reset callbacks on redraw. Huge performance improvement.<commit_after>var asBars = function () {
	this.redraw = function (d, quantifier) {
		this.callbacks = {};
		var data = d.data;
		d.scale.range ([0, this.height]);

		var barWidth = this.width / data.length;
		var chartHeight = this.height;
		//TODO migrate this to the new quantifierCallback method
		var qn = quantifier ? $.proxy (
				function (selector, d) {
					var attrs = quantifier.fn.apply (quantifier.context, [d, quantifier.args, quantifier.data])
					var height = attrs === Number (attrs) ? attrs : attrs.height;
					var data = attrs.data;
					attrs.data = null;
					if (isNaN (height)) height = 0;
					var calcY = chartHeight - height;
					if (isNaN (calcY)) calcY = 0;
					d3.select (selector)
						.attr ("y", calcY)
						.attr ("height", height + 1)
						.attr (attrs);
					if (!attrs.width) d3.select (selector).attr ("width", barWidth );

					if (data) {
						for (var d in data) {
							var val = data [d];
							if (val === Object (val)) {
								val = JSON.stringify (val);
							}
							d3.select (selector).attr ("data-" + d, val);
						}
					}
				},
			quantifier) : function (selector, d) { d3.select (selector).attr ("class", ""); };
		var margin = this.margin;
		this.svg.selectAll ("g").remove (); //HACK. Sucks.
		var items = data;
		if (typeof data.items === "function") {
			items = data.items ();
		}
		var bar = this.svg.selectAll ("g")
			.data (items);

		bar.enter ().append ("g")
			.attr ("transform", function (d, i) { return "translate(" + i * barWidth + ", " + margin.top + ")"; });
		bar.append ("rect")
			.each (function (d) { qn (this, d); })
			.on ("click", this.createCallback ("click"))
			.on ("mouseover", this.createCallback ("mouseover"))
			.on ("mouseout", this.createCallback ("mouseout"))

	}
	return this;
}
ant.charts.bars = function (container, conf) { this.init (container, conf); }
//charts.bars.prototype.constructor = asChart.init;
asChart.call (ant.charts.bars.prototype);
asBars.call (ant.charts.bars.prototype);
<|endoftext|>


<commit_before>import _ from "lodash";
import Router from "koa-66";
import passport from "koa-passport";
import AuthenticationApi from "./AuthenticationApi";
import jwt from "jsonwebtoken";
import config from "config";

let log = require("logfilename")(__filename);

const errorMsg = (err, info) => {
  if (info && info.message) {
    return info.message;
  } else if (err && err.message) {
    return err.message;
  } else {
    return "Unknown error";
  }
};

const localAuthCB = ctx => (err, user, info = {}) => {
  const jwtConfig = _.defaults(config.jwt, { secret: "secret" });
  log.debug(
    "localAuthCB %s, %s, %s",
    JSON.stringify(user),
    JSON.stringify(info),
    JSON.stringify(err)
  );
  if (user) {
    ctx.body = {
      user,
      token: jwt.sign(user, jwtConfig.secret, jwtConfig.options)
    };
    ctx.login(user, error => {
      if (error) {
        log.error("login ", error);
        throw error;
      } else {
        log.debug("login ok ");
      }
    });
  } else {
    ctx.status = 401;
    ctx.body = {
      error: {
        message: errorMsg(err, info)
      }
    };
  }
};

export function AuthenticationHttpController(app) {
  log.debug("AuthenticationHttpController");
  let authApi = AuthenticationApi(app);
  let respond = app.utils.http.respond;

  return {
    login(ctx, next) {
      return passport.authenticate("local", localAuthCB(ctx))(ctx, next);
    },
    logout(ctx) {
      log.debug("logout");
      ctx.logout();
      ctx.body = {
        success: true
      };
    },
    async register(context) {
      return respond(context, authApi, authApi.createPending, [
        context.request.body
      ]);
    },
    async verifyEmailCode(context) {
      return respond(context, authApi, authApi.verifyEmailCode, [
        context.request.body
      ]);
    },
    async resetPassword(context) {
      return respond(context, authApi, authApi.resetPassword, [
        context.request.body
      ]);
    },
    async verifyResetPasswordToken(context) {
      return respond(context, authApi, authApi.verifyResetPasswordToken, [
        context.request.body
      ]);
    },
    async loginFacebook(context, next) {
      return passport.authenticate("facebook_mobile", localAuthCB(context))(
        context,
        next
      );
    },
    async loginGoogle(context, next) {
      return passport.authenticate("google_mobile", localAuthCB(context))(
        context,
        next
      );
    },
    async loginGoogleIdToken(context, next) {
      return passport.authenticate("google-id-token", localAuthCB(context))(
        context,
        next
      );
    }
  };
}

export default function AuthenticationRouter(app) {
  let router = new Router();
  let authHttpController = AuthenticationHttpController(app);
  router.post("/login", authHttpController.login);
  router.post("/logout", authHttpController.logout);
  router.post("/register", authHttpController.register);
  router.post("/reset_password", authHttpController.resetPassword);
  router.post("/verify_email_code", authHttpController.verifyEmailCode);
  router.post(
    "/verify_reset_password_token",
    authHttpController.verifyResetPasswordToken
  );

  // Facebook Auth from web
  router.get(
    "/facebook",
    passport.authenticate("facebook", { scope: ["email"] })
  );
  router.get(
    "/facebook/callback",
    passport.authenticate("facebook", {
      failureRedirect: "/login",
      successRedirect: "login"
    })
  );
  // Facebook Auth from mobile
  router.post("/login_facebook", authHttpController.loginFacebook);

  //Google
  router.get(
    "/google",
    passport.authenticate("google", { scope: ["email", "profile"] })
  );
  router.get(
    "/google/callback",
    passport.authenticate("google", {
      failureRedirect: "/login",
      successRedirect: "login"
    })
  );
  router.post("/login_google", authHttpController.loginGoogle);

  router.post("/login_google_id_token", authHttpController.loginGoogleIdToken);

  app.server.baseRouter().mount("auth", router);

  return router;
}
<commit_msg>redirect to /user/auth/login<commit_after>import _ from "lodash";
import Router from "koa-66";
import passport from "koa-passport";
import AuthenticationApi from "./AuthenticationApi";
import jwt from "jsonwebtoken";
import config from "config";

let log = require("logfilename")(__filename);

const errorMsg = (err, info) => {
  if (info && info.message) {
    return info.message;
  } else if (err && err.message) {
    return err.message;
  } else {
    return "Unknown error";
  }
};

const localAuthCB = ctx => (err, user, info = {}) => {
  const jwtConfig = _.defaults(config.jwt, { secret: "secret" });
  log.debug(
    "localAuthCB %s, %s, %s",
    JSON.stringify(user),
    JSON.stringify(info),
    JSON.stringify(err)
  );
  if (user) {
    ctx.body = {
      user,
      token: jwt.sign(user, jwtConfig.secret, jwtConfig.options)
    };
    ctx.login(user, error => {
      if (error) {
        log.error("login ", error);
        throw error;
      } else {
        log.debug("login ok ");
      }
    });
  } else {
    ctx.status = 401;
    ctx.body = {
      error: {
        message: errorMsg(err, info)
      }
    };
  }
};

export function AuthenticationHttpController(app) {
  log.debug("AuthenticationHttpController");
  let authApi = AuthenticationApi(app);
  let respond = app.utils.http.respond;

  return {
    login(ctx, next) {
      return passport.authenticate("local", localAuthCB(ctx))(ctx, next);
    },
    logout(ctx) {
      log.debug("logout");
      ctx.logout();
      ctx.body = {
        success: true
      };
    },
    async register(context) {
      return respond(context, authApi, authApi.createPending, [
        context.request.body
      ]);
    },
    async verifyEmailCode(context) {
      return respond(context, authApi, authApi.verifyEmailCode, [
        context.request.body
      ]);
    },
    async resetPassword(context) {
      return respond(context, authApi, authApi.resetPassword, [
        context.request.body
      ]);
    },
    async verifyResetPasswordToken(context) {
      return respond(context, authApi, authApi.verifyResetPasswordToken, [
        context.request.body
      ]);
    },
    async loginFacebook(context, next) {
      return passport.authenticate("facebook_mobile", localAuthCB(context))(
        context,
        next
      );
    },
    async loginGoogle(context, next) {
      return passport.authenticate("google_mobile", localAuthCB(context))(
        context,
        next
      );
    },
    async loginGoogleIdToken(context, next) {
      return passport.authenticate("google-id-token", localAuthCB(context))(
        context,
        next
      );
    }
  };
}

export default function AuthenticationRouter(app) {
  let router = new Router();
  let authHttpController = AuthenticationHttpController(app);
  router.post("/login", authHttpController.login);
  router.post("/logout", authHttpController.logout);
  router.post("/register", authHttpController.register);
  router.post("/reset_password", authHttpController.resetPassword);
  router.post("/verify_email_code", authHttpController.verifyEmailCode);
  router.post(
    "/verify_reset_password_token",
    authHttpController.verifyResetPasswordToken
  );

  // Facebook Auth from web
  router.get(
    "/facebook",
    passport.authenticate("facebook", { scope: ["email"] })
  );
  router.get(
    "/facebook/callback",
    passport.authenticate("facebook", {
      failureRedirect: "/user/auth/login",
      successRedirect: "/user/auth/login"
    })
  );
  // Facebook Auth from mobile
  router.post("/login_facebook", authHttpController.loginFacebook);

  //Google
  router.get(
    "/google",
    passport.authenticate("google", { scope: ["email", "profile"] })
  );
  router.get(
    "/google/callback",
    passport.authenticate("google", {
      failureRedirect: "/user/auth/login",
      successRedirect: "/user/auth/login"
    })
  );
  router.post("/login_google", authHttpController.loginGoogle);

  router.post("/login_google_id_token", authHttpController.loginGoogleIdToken);

  app.server.baseRouter().mount("auth", router);

  return router;
}
<|endoftext|>


<commit_before>/* global document, window */
/* eslint-disable max-len */
import {CompositeLayer, IconLayer, WebMercatorViewport} from 'deck.gl';
import {makeTextureAtlasFromLabels} from './label-utils';
import {tagmapLayout} from './tagmap-utils';
import colorbrewer from 'colorbrewer';

/* Constants */
const defaultProps = {
  getLabel: x => x.label,
  getWeight: x => x.weight,
  getPosition: x => x.coordinates,
  // ['#1d91c0', '#41b6c4', '#7fcdbb', '#c7e9b4', '#edf8b1']
  colorScheme: colorbrewer.YlGnBu[9].slice(1, 6).reverse(),
  minFontSize: 14,
  maxFontSize: 32,
  weightThreshold: 1
};

export default class TagmapLayer extends CompositeLayer {

  initializeState() {
    // create canvas for measuring the text size
    const canvas = document.createElement('canvas');
    this.state = {canvas};
  }

  updateState({props, oldProps, changeFlags}) {
    // super.updateState({props, oldProps, changeFlags});

    if (changeFlags.dataChanged) {
      this.updateLabelAtlas();
      this.updateLayout();
    } else if (changeFlags.viewportChanged ||
        props.minFontSize !== oldProps.minFontSize ||
        props.maxFontSize !== oldProps.maxFontSize ||
        props.weightThreshold !== oldProps.weightThreshold) {
      this.updateLayout();
    }
  }

  updateLabelAtlas() {
    const {data, getLabel} = this.props;
    if (!data || data.length === 0) {
      return;
    }

    const {gl} = this.context;
    // font size for texture generation
    const fontSize = 32;
    // avoid generating duplicate labels
    const labels = Array.from(new Set(data.map(getLabel)));
    // use the texture generator from label layer
    // need to be optimized
    const {texture, mapping} = makeTextureAtlasFromLabels(gl, {data: labels, fontSize});

    // mappingDict -- key: label, val: mapping box in the texture
    const mappingDict = {};
    mapping.forEach((x, i) => {
      mappingDict[labels[i]] = x;
    });
    this.setState({data, texture, mapping: mappingDict});
  }

  updateLayout() {
    const {data} = this.state;
    if (!data || data.length === 0) {
      return;
    }

    const {viewport} = this.context;
    // data accessor
    const {getPosition, getLabel, getWeight} = this.props;
    // vis param
    const {minFontSize, maxFontSize, weightThreshold, colorScheme} = this.props;
    const visParam = {minFontSize, maxFontSize, weightThreshold, colorScheme};

    // filter to only visualize labels in the viewport
    const transform = new WebMercatorViewport(Object.assign({}, viewport));
    const labelsInViewport = this.state.data.map(x => {
      const pCoords = transform.project(getPosition(x));
      return Object.assign({}, {pCoords}, x);
    }).filter(x => {
      return x.pCoords[0] >= 0 && x.pCoords[0] <= viewport.width && x.pCoords[1] >= 0 && x.pCoords[1] <= viewport.height;
    });

    // generate tagmap
    const {tags} = tagmapLayout(labelsInViewport, transform, this.state.canvas, {getLabel, getPosition: x => x.pCoords, getWeight}, visParam);
    this.setState({tags});
  }

  renderLayers() {
    const {tags} = this.state;

    return [
      new IconLayer(Object.assign({}, this.props, {
        id: 'tag-map',
        iconAtlas: this.state.texture,
        iconMapping: this.state.mapping,
        data: tags,
        // weird scaling related to texture mapping and viewport change (pitch)
        sizeScale: window.devicePixelRatio * 1.25,
        getIcon: d => d.term,
        getPosition: d => d.position,
        getColor: d => d.color,
        getSize: d => d.size
      }))
    ];
  }
}

TagmapLayer.layerName = 'TagmapLayer';
TagmapLayer.defaultProps = defaultProps;
<commit_msg>Fix updateState issue: add shouldUpdateState function (#762)<commit_after>/* global document, window */
/* eslint-disable max-len */
import {CompositeLayer, IconLayer, WebMercatorViewport} from 'deck.gl';
import {makeTextureAtlasFromLabels} from './label-utils';
import {tagmapLayout} from './tagmap-utils';
import colorbrewer from 'colorbrewer';

/* Constants */
const defaultProps = {
  getLabel: x => x.label,
  getWeight: x => x.weight,
  getPosition: x => x.coordinates,
  // ['#1d91c0', '#41b6c4', '#7fcdbb', '#c7e9b4', '#edf8b1']
  colorScheme: colorbrewer.YlGnBu[9].slice(1, 6).reverse(),
  minFontSize: 14,
  maxFontSize: 32,
  weightThreshold: 1
};

export default class TagmapLayer extends CompositeLayer {

  initializeState() {
    // create canvas for measuring the text size
    const canvas = document.createElement('canvas');
    this.state = {canvas};
  }

  shouldUpdateState({changeFlags}) {
    return changeFlags.somethingChanged;
  }

  updateState({props, oldProps, changeFlags}) {
    super.updateState({props, oldProps, changeFlags});

    if (changeFlags.dataChanged) {
      this.updateLabelAtlas();
      this.updateLayout();
    } else if (changeFlags.viewportChanged ||
        props.minFontSize !== oldProps.minFontSize ||
        props.maxFontSize !== oldProps.maxFontSize ||
        props.weightThreshold !== oldProps.weightThreshold) {
      this.updateLayout();
    }
  }

  updateLabelAtlas() {
    const {data, getLabel} = this.props;
    if (!data || data.length === 0) {
      return;
    }

    const {gl} = this.context;
    // font size for texture generation
    const fontSize = 32;
    // avoid generating duplicate labels
    const labels = Array.from(new Set(data.map(getLabel)));
    // use the texture generator from label layer
    // need to be optimized
    const {texture, mapping} = makeTextureAtlasFromLabels(gl, {data: labels, fontSize});

    // mappingDict -- key: label, val: mapping box in the texture
    const mappingDict = {};
    mapping.forEach((x, i) => {
      mappingDict[labels[i]] = x;
    });
    this.setState({data, texture, mapping: mappingDict});
  }

  updateLayout() {
    const {data} = this.state;
    if (!data || data.length === 0) {
      return;
    }

    const {viewport} = this.context;
    // data accessor
    const {getPosition, getLabel, getWeight} = this.props;
    // vis param
    const {minFontSize, maxFontSize, weightThreshold, colorScheme} = this.props;
    const visParam = {minFontSize, maxFontSize, weightThreshold, colorScheme};

    // filter to only visualize labels in the viewport
    const transform = new WebMercatorViewport(Object.assign({}, viewport));
    const labelsInViewport = this.state.data.map(x => {
      const pCoords = transform.project(getPosition(x));
      return Object.assign({}, {pCoords}, x);
    }).filter(x => {
      return x.pCoords[0] >= 0 && x.pCoords[0] <= viewport.width && x.pCoords[1] >= 0 && x.pCoords[1] <= viewport.height;
    });

    // generate tagmap
    const {tags} = tagmapLayout(labelsInViewport, transform, this.state.canvas, {getLabel, getPosition: x => x.pCoords, getWeight}, visParam);
    this.setState({tags});
  }

  renderLayers() {
    const {tags} = this.state;

    return [
      new IconLayer(Object.assign({}, this.props, {
        id: 'tag-map',
        iconAtlas: this.state.texture,
        iconMapping: this.state.mapping,
        data: tags,
        // weird scaling related to texture mapping and viewport change (pitch)
        sizeScale: window.devicePixelRatio * 1.25,
        getIcon: d => d.term,
        getPosition: d => d.position,
        getColor: d => d.color,
        getSize: d => d.size
      }))
    ];
  }
}

TagmapLayer.layerName = 'TagmapLayer';
TagmapLayer.defaultProps = defaultProps;
<|endoftext|>


<commit_before>/*global FormplayerFrontend, Util */

FormplayerFrontend.module("SessionNavigate", function (SessionNavigate, FormplayerFrontend, Backbone, Marionette) {
    SessionNavigate.Router = Marionette.AppRouter.extend({
        appRoutes: {
            "apps": "listApps", // list all apps available to this user
            "single_app/:id": "singleApp", // Show app in phone mode (SingleAppView)
            "sessions": "listSessions", //list all this user's current sessions (incomplete forms)
            "sessions/:id": "getSession",
            ":session": "listMenus",  // Default route
        },
    });

    var API = {
        listApps: function () {
            FormplayerFrontend.request("clearForm");
            SessionNavigate.AppList.Controller.listApps();
        },
        singleApp: function(appId) {
            SessionNavigate.AppList.Controller.singleApp(appId);
        },
        selectApp: function (appId) {
            SessionNavigate.MenuList.Controller.selectMenu(appId);
        },
        listMenus: function (sessionObject) {
            FormplayerFrontend.request("clearForm");
            var urlObject = Util.CloudcareUrl.fromJson(
                Util.encodedUrlToObject(sessionObject || Backbone.history.getFragment())
            );
            var appId = urlObject.appId;
            var sessionId = urlObject.sessionId;
            var steps = urlObject.steps;
            var page = urlObject.page;
            var search = urlObject.search;
            var queryDict = urlObject.queryDict;
            var previewMode = urlObject.previewMode;
            var singleApp = urlObject.singleApp;
            SessionNavigate.MenuList.Controller.selectMenu(
                appId,
                sessionId,
                steps,
                page,
                search,
                queryDict,
                previewMode,
                singleApp
            );
        },
        showDetail: function (model, index) {
            SessionNavigate.MenuList.Controller.showDetail(model, index);
        },
        listSessions: function() {
            FormplayerFrontend.request("clearForm");
            SessionNavigate.SessionList.Controller.listSessions();
        },

        getSession: function(sessionId) {
            FormplayerFrontend.request("getSession", sessionId);
        },
        renderResponse: function (menuResponse) {
            FormplayerFrontend.request("clearForm");
            var NextScreenCollection = Backbone.Collection.extend({});
            var nextScreenCollection;
            //TODO: clean up this hackiness
            if (menuResponse.commands) {
                nextScreenCollection = new NextScreenCollection(menuResponse.commands);
                nextScreenCollection.type = "commands";
            } else {
                nextScreenCollection = new NextScreenCollection(menuResponse.entities);
                nextScreenCollection.type = "entities";
            }
            nextScreenCollection.title = menuResponse.title;
            nextScreenCollection.locales = menuResponse.locales;
            nextScreenCollection.sessionId = menuResponse.menuSessionId;
            var currentFragment = Backbone.history.getFragment();
            var urlObject = Util.CloudcareUrl.fromJson(Util.encodedUrlToObject(currentFragment));
            urlObject.setSessionId(nextScreenCollection.sessionId);
            var encodedUrl = Util.objectToEncodedUrl(urlObject.toJson());
            FormplayerFrontend.navigate(encodedUrl);
            SessionNavigate.MenuList.Controller.showMenu(nextScreenCollection);
        },
    };

    FormplayerFrontend.on("apps:currentApp", function () {
        var urlObject = Util.currentUrlToObject();
        urlObject.clearExceptApp();
        Util.setUrlToObject(urlObject);
        API.selectApp(urlObject.appId);
    });

    FormplayerFrontend.on("apps:list", function () {
        FormplayerFrontend.navigate("apps");
        API.listApps();
    });

    FormplayerFrontend.on("app:select", function (appId) {
        var urlObject = new Util.CloudcareUrl(appId);
        Util.setUrlToObject(urlObject);
        API.selectApp(appId);
    });

    FormplayerFrontend.on('app:singleApp', function(appId) {
        FormplayerFrontend.navigate("/phone_mode/" + appId);
        API.singleApp(appId);
    });

    FormplayerFrontend.on("menu:select", function (index) {
        var urlObject = Util.currentUrlToObject();
        urlObject.addStep(index);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:paginate", function (page) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setPage(page);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:search", function (search) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setSearch(search);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:query", function (queryDict) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setQuery(queryDict);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:show:detail", function (model, index) {
        API.showDetail(model, index);
    });

    FormplayerFrontend.on("sessions", function () {
        FormplayerFrontend.navigate("/sessions");
        API.listSessions();
    });

    FormplayerFrontend.on("getSession", function (sessionId) {
        FormplayerFrontend.navigate("/sessions/" + sessionId);
        API.getSession(sessionId);
    });

    FormplayerFrontend.on("renderResponse", function (menuResponse) {
        API.renderResponse(menuResponse);
    });

    SessionNavigate.on("start", function () {
        new SessionNavigate.Router({
            controller: API,
        });
    });

    FormplayerFrontend.on("breadcrumbSelect", function (index) {
        FormplayerFrontend.request("clearForm");
        var urlObject = Util.currentUrlToObject();
        urlObject.spliceSteps(index);
        Util.setUrlToObject(urlObject);
        SessionNavigate.MenuList.Controller.selectMenu(urlObject.appId, null, urlObject.steps);
    });

});
<commit_msg>fix up bad merge<commit_after>/*global FormplayerFrontend, Util */

FormplayerFrontend.module("SessionNavigate", function (SessionNavigate, FormplayerFrontend, Backbone, Marionette) {
    SessionNavigate.Router = Marionette.AppRouter.extend({
        appRoutes: {
            "apps": "listApps", // list all apps available to this user
            "single_app/:id": "singleApp", // Show app in phone mode (SingleAppView)
            "sessions": "listSessions", //list all this user's current sessions (incomplete forms)
            "sessions/:id": "getSession",
            ":session": "listMenus",  // Default route
        },
    });

    var API = {
        listApps: function () {
            FormplayerFrontend.request("clearForm");
            SessionNavigate.AppList.Controller.listApps();
        },
        singleApp: function(appId) {
            SessionNavigate.AppList.Controller.singleApp(appId);
        },
        selectApp: function (appId) {
            SessionNavigate.MenuList.Controller.selectMenu(appId);
        },
        listMenus: function (sessionObject) {
            FormplayerFrontend.request("clearForm");
            var urlObject = Util.CloudcareUrl.fromJson(
                Util.encodedUrlToObject(sessionObject || Backbone.history.getFragment())
            );
            var appId = urlObject.appId;
            var sessionId = urlObject.sessionId;
            var steps = urlObject.steps;
            var page = urlObject.page;
            var search = urlObject.search;
            var queryDict = urlObject.queryDict;
            SessionNavigate.MenuList.Controller.selectMenu(
                appId,
                sessionId,
                steps,
                page,
                search,
                queryDict
            );
        },
        showDetail: function (model, index) {
            SessionNavigate.MenuList.Controller.showDetail(model, index);
        },
        listSessions: function() {
            FormplayerFrontend.request("clearForm");
            SessionNavigate.SessionList.Controller.listSessions();
        },

        getSession: function(sessionId) {
            FormplayerFrontend.request("getSession", sessionId);
        },
        renderResponse: function (menuResponse) {
            FormplayerFrontend.request("clearForm");
            var NextScreenCollection = Backbone.Collection.extend({});
            var nextScreenCollection;
            //TODO: clean up this hackiness
            if (menuResponse.commands) {
                nextScreenCollection = new NextScreenCollection(menuResponse.commands);
                nextScreenCollection.type = "commands";
            } else {
                nextScreenCollection = new NextScreenCollection(menuResponse.entities);
                nextScreenCollection.type = "entities";
            }
            nextScreenCollection.title = menuResponse.title;
            nextScreenCollection.locales = menuResponse.locales;
            nextScreenCollection.sessionId = menuResponse.menuSessionId;
            var currentFragment = Backbone.history.getFragment();
            var urlObject = Util.CloudcareUrl.fromJson(Util.encodedUrlToObject(currentFragment));
            urlObject.setSessionId(nextScreenCollection.sessionId);
            var encodedUrl = Util.objectToEncodedUrl(urlObject.toJson());
            FormplayerFrontend.navigate(encodedUrl);
            SessionNavigate.MenuList.Controller.showMenu(nextScreenCollection);
        },
    };

    FormplayerFrontend.on("apps:currentApp", function () {
        var urlObject = Util.currentUrlToObject();
        urlObject.clearExceptApp();
        Util.setUrlToObject(urlObject);
        API.selectApp(urlObject.appId);
    });

    FormplayerFrontend.on("apps:list", function () {
        FormplayerFrontend.navigate("apps");
        API.listApps();
    });

    FormplayerFrontend.on("app:select", function (appId) {
        var urlObject = new Util.CloudcareUrl(appId);
        Util.setUrlToObject(urlObject);
        API.selectApp(appId);
    });

    FormplayerFrontend.on('app:singleApp', function(appId) {
        FormplayerFrontend.navigate("/single_app/" + appId);
        API.singleApp(appId);
    });

    FormplayerFrontend.on("menu:select", function (index) {
        var urlObject = Util.currentUrlToObject();
        urlObject.addStep(index);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:paginate", function (page) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setPage(page);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:search", function (search) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setSearch(search);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:query", function (queryDict) {
        var urlObject = Util.currentUrlToObject();
        urlObject.setQuery(queryDict);
        Util.setUrlToObject(urlObject);
        API.listMenus();
    });

    FormplayerFrontend.on("menu:show:detail", function (model, index) {
        API.showDetail(model, index);
    });

    FormplayerFrontend.on("sessions", function () {
        FormplayerFrontend.navigate("/sessions");
        API.listSessions();
    });

    FormplayerFrontend.on("getSession", function (sessionId) {
        FormplayerFrontend.navigate("/sessions/" + sessionId);
        API.getSession(sessionId);
    });

    FormplayerFrontend.on("renderResponse", function (menuResponse) {
        API.renderResponse(menuResponse);
    });

    SessionNavigate.on("start", function () {
        new SessionNavigate.Router({
            controller: API,
        });
    });

    FormplayerFrontend.on("breadcrumbSelect", function (index) {
        FormplayerFrontend.request("clearForm");
        var urlObject = Util.currentUrlToObject();
        urlObject.spliceSteps(index);
        Util.setUrlToObject(urlObject);
        SessionNavigate.MenuList.Controller.selectMenu(urlObject.appId, null, urlObject.steps);
    });

});
<|endoftext|>


<commit_before>var util = (function(parent, global) {
    var mAccessToken = null;
    var mSessionKey = "test-session-key";
    var mHost = "localhost";
    var mScopes = Array(
            'servicediscovery',
            'serviceinformation',
            'system',
            'battery',
            'connect',
            'deviceorientation',
            'filedescriptor',
            'file',
            'mediaplayer',
            'mediastreamrecording',
            'notification',
            'phone',
            'proximity',
            'settings',
            'vibration',
            'light',
            'remotecontroller',
            'drivecontroller',
            'mhealth',
            'sphero',
            'dice',
            'temperature',
            'camera',
            'canvas',
            'health',
            'touch',
            'humandetect',
            'keyevent',
            'omnidirectionalimage',
            'tv',
            'powermeter',
            'humidity',
            'illuminance',
            'videochat',
            'airconditioner',
            'atmosphericpressure',
            'ecg',
            'poseEstimation',
            'stressEstimation',
            'walkState',
            'gpio');

    function init(callback) {
        dConnect.setHost(mHost);
        dConnect.setExtendedOrigin("file://");
        checkDeviceConnect(callback);
    }
    parent.init = init;

    function startManager(onAvailable) {
        var errorCallback = function(errorCode, errorMessage) {
            switch (errorCode) {
            case dConnect.constants.ErrorCode.ACCESS_FAILED:
                alert('Device Connect Managerが起動していません。');
                break;
            case dConnect.constants.ErrorCode.INVALID_SERVER:
                alert('WARNING: Device Connect Manager may be spoofed.');
                break;
            case dConnect.constants.ErrorCode.INVALID_ORIGIN:
                alert('WARNING: Origin of this app is invalid. Maybe the origin is not registered in whiteList.');
                break;
            default:
                alert(errorMessage);
                break;
            }
        };
        dConnect.checkDeviceConnect(onAvailable, errorCallback);
    }

    function checkDeviceConnect(callback) {
        startManager(function(apiVersion) {
            console.log('Device Connect API version: ' + apiVersion);

            mAccessToken = getCookie('accessToken');

            serviceDiscovery(function(services) {
                var serviceId = getServiceId();
                for (var i = 0; i < services.length; i++) {
                    if (serviceId === services[i].id) {
                        var service = services[i];
                        serviceInformation(function(json) {
                            openWebSocketIfNeeded();
                            callback(service.name, json);
                        });
                        return;
                    }
                }
                alert('指定されたサービスが見つかりません。\n serviceId=' + serviceId);
            });
        });
    }

    function authorization(callback) {
        dConnect.authorization(mScopes, 'デバイス確認画面',
            function(clientId, accessToken) {
                mAccessToken = accessToken;
                openWebSocketIfNeeded();
                setCookie('accessToken', mAccessToken);
                callback();
            },
            function(errorCode, errorMessage) {
                showAlert("認証に失敗しました", errorCode, errorMessage);
            });
    }

    function serviceDiscovery(callback) {
        dConnect.discoverDevices(mAccessToken, function(json) {
            callback(json.services);
        }, function(errorCode, errorMessage) {
            if (errorCode == 11 || errorCode == 12 || errorCode == 13 || errorCode == 15) {
                authorization(function() {
                    serviceDiscovery(callback);
                });
            } else {
                showAlert("デバイスの情報取得に失敗しました。", errorCode, errorMessage)
            }
        });
    }

    function serviceInformation(callback) {
        dConnect.getSystemDeviceInfo(getServiceId(), getAccessToken(), function(json) {
            callback(json);
        }, function(errorCode, errorMessage) {
            if (errorCode == 11 || errorCode == 12 || errorCode == 13 || errorCode == 15) {
                authorization(function() {
                    serviceInformation(callback);
                });
            } else {
                showAlert("デバイスの情報取得に失敗しました。", errorCode, errorMessage)
            }
        });
    }

    function openWebSocketIfNeeded() {
        if (!dConnect.isConnectedWebSocket()) {
            var accessToken = mAccessToken ? mAccessToken : mSessionKey;
            dConnect.connectWebSocket(accessToken, function(code, message) {
                if (code > 0) {
                    alert('WebSocketが切れました。\n code=' + code + " message=" + message);
                }
                console.log("WebSocket: code=" + code + " message=" +message);
            });
        }
    }

    function setCookieInternal(key, value) {
        document.cookie = key + '=' + value;
    }

    function getCookieInternal(name) {
        var result = null;
        var cookieName = name + '=';
        var allCookies = document.cookie;
        var position = allCookies.indexOf(cookieName);
        if (position != -1) {
            var startIndex = position + cookieName.length;
            var endIndex = allCookies.indexOf(';', startIndex);
            if (endIndex == -1) {
                endIndex = allCookies.length;
            }
            result = decodeURIComponent(allCookies.substring(startIndex, endIndex));
        }
        return result;
    }

    function setCookie(key, value) {
        if (window.Android) {
            Android.setCookie(key, value);
        } else {
            setCookieInternal(key, value);
        }
    }

    function getCookie(name) {
        if (window.Android) {
            return Android.getCookie(name);
        } else {
            return getCookieInternal(name);
        }
    }

    function getQuery(name) {
        if (1 < document.location.search.length) {
            var query = document.location.search.substring(1);
            var parameters = query.split('&');
            for (var i = 0; i < parameters.length; i++) {
                var element = parameters[i].split('=');
                var paramName = decodeURIComponent(element[0]);
                var paramValue = decodeURIComponent(element[1]);
                if (paramName == name) {
                    return paramValue;
                }
            }
        }
        return null;
    }

    function containsScope(profile) {
        for (var i = 0; i < mScopes.length; i++) {
            if (mScopes[i] == profile) {
                return true;
            }
        }
        return false;
    }

    function appendScope(uri) {
        var elm = document.createElement('a');
        elm.href = uri;

        var p = elm.pathname.split('/');
        for (var i = 0; i < p.length; i++) {
            if (p[i] != '' && p[i] != 'gotapi') {
                if (!containsScope(p[i])) {
                    mScopes.push(p[i]);
                }
                break;
            }
        }
    }

    function rebuildUri(uri) {
        var elm = document.createElement('a');
        elm.href = uri;

        var u = elm.origin + elm.pathname + "?";

        var parameters = elm.search.substring(1).split('&');
        for (var i = 0; i < parameters.length; i++) {
            var element = parameters[i].split('=');
            var paramName = decodeURIComponent(element[0]);
            var paramValue = decodeURIComponent(element[1]);
            if (i > 0) {
                u += '&';
            }
            if (paramName == 'accessToken') {
                u += element[0] + "=" + mAccessToken;
            } else {
                u += element[0] + "=" + element[1];
            }
        }
        return u;
    }

    function createXMLHttpRequest() {
        try {
            return new XMLHttpRequest();
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.6.0');
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP');
        } catch(e) {}
        return null;
    }

    function sendRequest(method, uri, body, callback) {
         var xhr = createXMLHttpRequest();
         xhr.onerror = function (e) {
             console.log("onerror:" + xhr.statusText);
         };

         xhr.onload = function (e) {
             console.log("onload:" + xhr.readyState);
         };

         xhr.onreadystatechange = function() {
             switch (xhr.readyState) {
             case 1: {
                 try {
                     xhr.setRequestHeader("X-GotAPI-Origin".toLowerCase(), "file://");
                 } catch (e) {
                     return;
                 }

                 if (method.toUpperCase() === 'DELETE' && (body === undefined || body === null)) {
                     body = '';
                 }
                 xhr.send(body);
                 break;
             }
             case 2:
                 break;
             case 3:
                break;
             case 4: {
                 if (xhr.status == 200) {
                    var json = JSON.parse(xhr.responseText);
                    if (json.result == 1 && json.errorCode == 14) {
                        appendScope(uri);
                        authorization(function() {
                            if (method.toUpperCase() == 'GET' || method.toUpperCase() == 'DELETE') {
                                uri = rebuildUri(uri);
                            } else {
                                body.set('accessToken', mAccessToken);
                            }
                            sendRequest(method, uri, body, callback);
                        });
                        return;
                    }
                 }
                 callback(xhr.status, xhr.responseText);
                 break;
             }
             default:
                 break;
             }
        };
        xhr.open(method, uri);
    }
    parent.sendRequest = sendRequest;

    function getUri(path) {
        return 'http://' + mHost + ':4035' + path;
    }
    parent.getUri = getUri;

    function getProfile() {
        return getQuery('profile');
    }
    parent.getProfile = getProfile;

    function getServiceId() {
        return getQuery('serviceId');
    }
    parent.getServiceId = getServiceId;

    function getResourceUri() {
        return getQuery('resource');
    }
    parent.getResourceUri = getResourceUri;

    function getMimeType() {
        return getQuery('mimeType');
    }
    parent.getMimeType = getMimeType;

    function getAccessToken() {
        return mAccessToken;
    }
    parent.getAccessToken = getAccessToken;

    function getSessionKey() {
        return mSessionKey;
    }
    parent.getSessionKey = getSessionKey;

    function showAlert(message, errorCode, errorMessage) {
        alert(message + "\n errorCode: " + errorCode + " \n " + errorMessage);
    }
    parent.showAlert = showAlert;

    function createTemplate(templateId, data) {
        var template = document.getElementById(templateId).text;
        var html = template.replace(/{#(\w+)}/g, function(m, key) {
            var text = data[key] || '';
            return text;
        });
        return html;
    }
    parent.createTemplate = createTemplate;

    function replaceUri(jsonObject) {
        var mimeType = 'image/png';
        for (var key in jsonObject) {
            var value = jsonObject[key];
            if (value instanceof Object && !(value instanceof Array)) {
                replaceUri(value);
            } else {
                if (key == 'uri') {
                    if (jsonObject['mimeType']) {
                        mimeType = jsonObject['mimeType'];
                    }
                    jsonObject[key] = '' + value + "";
                }
            }
        }
    }

    function formatJSON(jsonText) {
        var jsonBefore = JSON.parse(jsonText);
        replaceUri(jsonBefore);
        var json = JSON.stringify(jsonBefore, null, "    ");
        return json.replace(/\r?\n/g, "
").replace(/\s{2}/g, "  ");
    }
    parent.formatJSON = formatJSON;

    function escapeText(text) {
        return text.replace(/</g, "<").replace(/>/g, ">");
    }
    parent.escapeText = escapeText;

    return parent;
})(util || {}, this.self || global);
<commit_msg>WebSocketがサポートされていない場合にエラーダイアログを表示するように修正<commit_after>var util = (function(parent, global) {
    var mAccessToken = null;
    var mSessionKey = "test-session-key";
    var mHost = "localhost";
    var mScopes = Array(
            'servicediscovery',
            'serviceinformation',
            'system',
            'battery',
            'connect',
            'deviceorientation',
            'filedescriptor',
            'file',
            'mediaplayer',
            'mediastreamrecording',
            'notification',
            'phone',
            'proximity',
            'settings',
            'vibration',
            'light',
            'remotecontroller',
            'drivecontroller',
            'mhealth',
            'sphero',
            'dice',
            'temperature',
            'camera',
            'canvas',
            'health',
            'touch',
            'humandetect',
            'keyevent',
            'omnidirectionalimage',
            'tv',
            'powermeter',
            'humidity',
            'illuminance',
            'videochat',
            'airconditioner',
            'atmosphericpressure',
            'ecg',
            'poseEstimation',
            'stressEstimation',
            'walkState',
            'gpio');

    function init(callback) {
        dConnect.setHost(mHost);
        dConnect.setExtendedOrigin("file://");
        checkDeviceConnect(callback);
    }
    parent.init = init;

    function startManager(onAvailable) {
        var errorCallback = function(errorCode, errorMessage) {
            switch (errorCode) {
            case dConnect.constants.ErrorCode.ACCESS_FAILED:
                alert('Device Connect Managerが起動していません。');
                break;
            case dConnect.constants.ErrorCode.INVALID_SERVER:
                alert('WARNING: Device Connect Manager may be spoofed.');
                break;
            case dConnect.constants.ErrorCode.INVALID_ORIGIN:
                alert('WARNING: Origin of this app is invalid. Maybe the origin is not registered in whiteList.');
                break;
            default:
                alert(errorMessage);
                break;
            }
        };
        dConnect.checkDeviceConnect(onAvailable, errorCallback);
    }

    function checkDeviceConnect(callback) {
        startManager(function(apiVersion) {
            console.log('Device Connect API version: ' + apiVersion);

            mAccessToken = getCookie('accessToken');

            serviceDiscovery(function(services) {
                var serviceId = getServiceId();
                for (var i = 0; i < services.length; i++) {
                    if (serviceId === services[i].id) {
                        var service = services[i];
                        serviceInformation(function(json) {
                            openWebSocketIfNeeded();
                            callback(service.name, json);
                        });
                        return;
                    }
                }
                alert('指定されたサービスが見つかりません。\n serviceId=' + serviceId);
            });
        });
    }

    function authorization(callback) {
        dConnect.authorization(mScopes, 'デバイス確認画面',
            function(clientId, accessToken) {
                mAccessToken = accessToken;
                openWebSocketIfNeeded();
                setCookie('accessToken', mAccessToken);
                callback();
            },
            function(errorCode, errorMessage) {
                showAlert("認証に失敗しました", errorCode, errorMessage);
            });
    }

    function serviceDiscovery(callback) {
        dConnect.discoverDevices(mAccessToken, function(json) {
            callback(json.services);
        }, function(errorCode, errorMessage) {
            if (errorCode == 11 || errorCode == 12 || errorCode == 13 || errorCode == 15) {
                authorization(function() {
                    serviceDiscovery(callback);
                });
            } else {
                showAlert("デバイスの情報取得に失敗しました。", errorCode, errorMessage)
            }
        });
    }

    function serviceInformation(callback) {
        dConnect.getSystemDeviceInfo(getServiceId(), getAccessToken(), function(json) {
            callback(json);
        }, function(errorCode, errorMessage) {
            if (errorCode == 11 || errorCode == 12 || errorCode == 13 || errorCode == 15) {
                authorization(function() {
                    serviceInformation(callback);
                });
            } else {
                showAlert("デバイスの情報取得に失敗しました。", errorCode, errorMessage)
            }
        });
    }

    function openWebSocketIfNeeded() {
        try {
            if (!dConnect.isConnectedWebSocket()) {
                var accessToken = mAccessToken ? mAccessToken : mSessionKey;
                dConnect.connectWebSocket(accessToken, function(code, message) {
                    if (code > 0) {
                        alert('WebSocketが切れました。\n code=' + code + " message=" + message);
                    }
                    console.log("WebSocket: code=" + code + " message=" +message);
                });
            }
        } catch (e) {
            alert("この端末は、WebSocketをサポートしていません。");
        }
    }

    function setCookieInternal(key, value) {
        document.cookie = key + '=' + value;
    }

    function getCookieInternal(name) {
        var result = null;
        var cookieName = name + '=';
        var allCookies = document.cookie;
        var position = allCookies.indexOf(cookieName);
        if (position != -1) {
            var startIndex = position + cookieName.length;
            var endIndex = allCookies.indexOf(';', startIndex);
            if (endIndex == -1) {
                endIndex = allCookies.length;
            }
            result = decodeURIComponent(allCookies.substring(startIndex, endIndex));
        }
        return result;
    }

    function setCookie(key, value) {
        if (window.Android) {
            Android.setCookie(key, value);
        } else {
            setCookieInternal(key, value);
        }
    }

    function getCookie(name) {
        if (window.Android) {
            return Android.getCookie(name);
        } else {
            return getCookieInternal(name);
        }
    }

    function getQuery(name) {
        if (1 < document.location.search.length) {
            var query = document.location.search.substring(1);
            var parameters = query.split('&');
            for (var i = 0; i < parameters.length; i++) {
                var element = parameters[i].split('=');
                var paramName = decodeURIComponent(element[0]);
                var paramValue = decodeURIComponent(element[1]);
                if (paramName == name) {
                    return paramValue;
                }
            }
        }
        return null;
    }

    function containsScope(profile) {
        for (var i = 0; i < mScopes.length; i++) {
            if (mScopes[i] == profile) {
                return true;
            }
        }
        return false;
    }

    function appendScope(uri) {
        var elm = document.createElement('a');
        elm.href = uri;

        var p = elm.pathname.split('/');
        for (var i = 0; i < p.length; i++) {
            if (p[i] != '' && p[i] != 'gotapi') {
                if (!containsScope(p[i])) {
                    mScopes.push(p[i]);
                }
                break;
            }
        }
    }

    function rebuildUri(uri) {
        var elm = document.createElement('a');
        elm.href = uri;

        var u = elm.origin + elm.pathname + "?";

        var parameters = elm.search.substring(1).split('&');
        for (var i = 0; i < parameters.length; i++) {
            var element = parameters[i].split('=');
            var paramName = decodeURIComponent(element[0]);
            var paramValue = decodeURIComponent(element[1]);
            if (i > 0) {
                u += '&';
            }
            if (paramName == 'accessToken') {
                u += element[0] + "=" + mAccessToken;
            } else {
                u += element[0] + "=" + element[1];
            }
        }
        return u;
    }

    function createXMLHttpRequest() {
        try {
            return new XMLHttpRequest();
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.6.0');
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        } catch(e) {}
        try {
            return new ActiveXObject('MSXML2.XMLHTTP');
        } catch(e) {}
        return null;
    }

    function sendRequest(method, uri, body, callback) {
         var xhr = createXMLHttpRequest();
         xhr.onerror = function (e) {
             console.log("onerror:" + xhr.statusText);
         };

         xhr.onload = function (e) {
             console.log("onload:" + xhr.readyState);
         };

         xhr.onreadystatechange = function() {
             switch (xhr.readyState) {
             case 1: {
                 try {
                     xhr.setRequestHeader("X-GotAPI-Origin".toLowerCase(), "file://");
                 } catch (e) {
                     return;
                 }

                 if (method.toUpperCase() === 'DELETE' && (body === undefined || body === null)) {
                     body = '';
                 }
                 xhr.send(body);
                 break;
             }
             case 2:
                 break;
             case 3:
                break;
             case 4: {
                 if (xhr.status == 200) {
                    var json = JSON.parse(xhr.responseText);
                    if (json.result == 1 && json.errorCode == 14) {
                        appendScope(uri);
                        authorization(function() {
                            if (method.toUpperCase() == 'GET' || method.toUpperCase() == 'DELETE') {
                                uri = rebuildUri(uri);
                            } else {
                                body.set('accessToken', mAccessToken);
                            }
                            sendRequest(method, uri, body, callback);
                        });
                        return;
                    }
                 }
                 callback(xhr.status, xhr.responseText);
                 break;
             }
             default:
                 break;
             }
        };
        xhr.open(method, uri);
    }
    parent.sendRequest = sendRequest;

    function getUri(path) {
        return 'http://' + mHost + ':4035' + path;
    }
    parent.getUri = getUri;

    function getProfile() {
        return getQuery('profile');
    }
    parent.getProfile = getProfile;

    function getServiceId() {
        return getQuery('serviceId');
    }
    parent.getServiceId = getServiceId;

    function getResourceUri() {
        return getQuery('resource');
    }
    parent.getResourceUri = getResourceUri;

    function getMimeType() {
        return getQuery('mimeType');
    }
    parent.getMimeType = getMimeType;

    function getAccessToken() {
        return mAccessToken;
    }
    parent.getAccessToken = getAccessToken;

    function getSessionKey() {
        return mSessionKey;
    }
    parent.getSessionKey = getSessionKey;

    function showAlert(message, errorCode, errorMessage) {
        alert(message + "\n errorCode: " + errorCode + " \n " + errorMessage);
    }
    parent.showAlert = showAlert;

    function createTemplate(templateId, data) {
        var template = document.getElementById(templateId).text;
        var html = template.replace(/{#(\w+)}/g, function(m, key) {
            var text = data[key] || '';
            return text;
        });
        return html;
    }
    parent.createTemplate = createTemplate;

    function replaceUri(jsonObject) {
        var mimeType = 'image/png';
        for (var key in jsonObject) {
            var value = jsonObject[key];
            if (value instanceof Object && !(value instanceof Array)) {
                replaceUri(value);
            } else {
                if (key == 'uri') {
                    if (jsonObject['mimeType']) {
                        mimeType = jsonObject['mimeType'];
                    }
                    jsonObject[key] = '' + value + "";
                }
            }
        }
    }

    function formatJSON(jsonText) {
        var jsonBefore = JSON.parse(jsonText);
        replaceUri(jsonBefore);
        var json = JSON.stringify(jsonBefore, null, "    ");
        return json.replace(/\r?\n/g, "
").replace(/\s{2}/g, "  ");
    }
    parent.formatJSON = formatJSON;

    function escapeText(text) {
        return text.replace(/</g, "<").replace(/>/g, ">");
    }
    parent.escapeText = escapeText;

    return parent;
})(util || {}, this.self || global);
<|endoftext|>