<commit_before>// ==UserScript==
// @name        SycBox
// @namespace   Elitepvpers
// @description customized ShoutBox
// @include     *//www.elitepvpers.com/forum/
// @author      Syc
// @version     1.0.9
// @downloadURL https://github.com/epvpsyc/SycBox/raw/master/SycBox.user.js
// @updateURL   https://github.com/epvpsyc/SycBox/raw/master/SycBox.user.js
// @grant       none
// ==/UserScript==
(function ($) {
    // refreshActive will be false for the beginning
    // this is supposed to prevent refreshing the SB before the SB is even properly initialized
    let refreshActive = false;

    let chatHistory = [];
    let settings;

    // adding css style this way
    // since github did not like the way of splitting the css into a separate file
    // and loading it from github
    //
    // thanks to Der-Eddy
    function appendStyleRaw(style) {
        if (style) {
            $('').appendTo('head');
        }
    }

    function updateChatHistory(changes) {
        refreshActive = false;

        // 'changes' will indicate if there is a new message that needs to be added to the SB
        $.ajax({
            url: '//www.elitepvpers.com/forum/mgc_cb_evo_ajax.php',
            type: 'POST',
            dataType: 'xml',
            data: {
                'do': 'ajax_refresh_chat',
                'status': 'open',
                'channel_id': (settings.useEnglishChannel ? 1 : 0),
                'location': 'full',
                'first_load': 1,
                'securitytoken': SECURITYTOKEN,
            }
        }).done(function (data) {
            data = $.parseHTML($(data).find('chatbox_content').text());

            $(data).find('tr.alt2').each(function () {
                let id = parseInt($(this).find('td:first').attr('id').replace('chat_', ''));

                if (!getMessageById(id)) {
                    let $username = $(this).find('td:nth-last-child(2) > span > a');
                    let color = ($username.find('span').length) ? $username.find('span').css('color') : 'black';
                    let username = ($username.find('span').length) ? $username.find('span').html() : $username.text().slice(1, -1);
                    let text = $(this).find('td').last().html().trim();
                    let url = $(this).find('td:nth-last-child(2) > span > a').attr('href');
                    let userid = parseInt(url.split('/').pop().split('-')[0]);

                    text = (settings.removeSmileys) ? removeSmileys(text) : text;

                    let message = {
                        'id': id,
                        'time': $(this).find('.mgc_cb_evo_date').text().trim(),
                        'user': {
                            'id': userid,
                            'url': url,
                            'name': username,
                            'color': color,
                        },
                        'text': text,
                        'appended': false,
                    };

                    chatHistory.push(message);
                    changes = true;
                }
            });

            if (changes) {
                appendToSB();
            }

            refreshActive = true;
        });
    }

    function initTable() {
        let $oldSB = $('tbody#mgc_cb_evo_opened');
        let $sycBoxTable = $('#sycBoxTable');
        let sbWidth = $oldSB.width();
        let sbHeight = $oldSB.height();

        $oldSB.parent().html('

');
        $('#sycBoxTbody').parent().attr('id', 'sycBoxTable');

        // title
        $sycBoxTable.before(
            '
e*pvp Premium Shoutbox ' +
            '[Archiv]' +
            '[SycBox]' +
            '
'
        );

        // input
        $sycBoxTable.after(
            '
' +
            '<input type="text" id="sycBoxSend" tabindex="1">' +
            '
'
        );

        // set sizes for sb
        $sycBoxTable.width(sbWidth + 'px');
        $sycBoxTable.css('max-height', sbHeight + 'px');

        updateChatHistory(true);
        initMenu();
    }

    function appendToSB() {
        let appended = false;

        chatHistory.reverse().forEach(function (chat, i) {
            if (!chat.appended) {
                chatHistory[i].appended = true;

                // layout:
                // date | username | message text
                // 13:37 Syc This is an example message

                let line = '
' +
                    '' +
                    chat.time +
                    '	' +
                    '' +
                    chat.user.name +
                    '	' +
                    '' + chat.text + '	' +
                    '
';
                $('#sycBoxTbody').append(line);
                appended = true;
            }
        });

        chatHistory.reverse();

        // in the case of a message actually being added to the SB, scroll down
        if (appended) {
            let $sycBoxTable = $('#sycBoxTable');
            $sycBoxTable.animate({
                scrollTop: $sycBoxTable.prop('scrollHeight')
            });
        }
    }

    function clearSB() {
        chatHistory = [];
        $('#sycBoxTable').find('tr').remove();

        updateChatHistory(true);
    }

    function initMenu() {
        let removeSmileysHtml = (settings.removeSmileys) ? 'checked' : '';
        let useEnglishChannelHtml = (settings.useEnglishChannel) ? 'checked' : '';

        $(
            '
' +
            '
sycBox Settings' +
            '
x
' +
            '
' +
            '
' +
            '' +
            '<input type="checkbox" data-sycbox-id="removeSmileys" class="sycBoxSetToggle"' + removeSmileysHtml + '>' +
            'remove smiley images' +
            '' +
            '' +
            '<input type="checkbox" data-sycbox-id="useEnglishChannel" class="sycBoxSetToggle"' + useEnglishChannelHtml + '>' +
            'use english channel' +
            '' +
            '
' +
            '
' +
            '
' +
            'v' + GM_info['script']['version'] +
            '
' +
            'Github | ©Syc' +
            '
' +
            '
'
        ).appendTo('body');
    }

    function removeSmileys(text) {
        let smileys = [
            ['smile.gif', ':)'],
            ['confused.gif', ':confused:'],
            ['rtfm.gif', ':rtfm:'],
            ['pimp.gif', ':pimp:'],
            ['mofo.gif', ':mofo:'],
            ['handsdown.gif', ':handsdown:'],
            ['bandit.gif', ':bandit:'],
            ['cool.gif', ':cool:'],
            ['facepalm.gif', ':facepalm:'],
            ['frown.gif', ':('],
            ['mad.gif', ':mad:'],
            ['tongue.gif', ':p'],
            ['wink.gif', ';)'],
            ['biggrin.gif', ':D'],
            ['redface.gif', ':o'],
            ['rolleyes.gif', ':rolleyes:'],
            ['eek.gif', ':eek:'],
            ['awesome.gif', ':awesome:'],
        ];

        smileys.forEach(function (smiley, i) {
            let html = $('
').html(text);
            html.find('img[src$="' + smileys[i][0] + '"]').replaceWith(smileys[i][1]);
            text = html.html();
        });

        return $(text).html();
    }

    function getMessageById(id) {
        let message = undefined;

        chatHistory.forEach(function (chat) {
            if (chat.id === id) {
                message = chat;
            }
        });

        return message;
    }

    function sendMessage(message) {
        refreshActive = false;
        let channelID = (settings.useEnglishChannel ? 1 : 0);

        let sendAjax = new vB_AJAX_Handler(true);
        sendAjax.send('//www.elitepvpers.com/forum/mgc_cb_evo_ajax.php', 'do=ajax_chat&channel_id=' + channelID + '&chat=' + PHP.urlencode(message) + '&securitytoken=' + SECURITYTOKEN);
        setTimeout(function () {
            updateChatHistory();
        }, 1000);
    }

    function getStorage(key) {
        const settingsDefault = {
            'removeSmileys': true,
            'useEnglishChannel': false,
        };

        let prefix = "sycBox_";
        let storage = (localStorage.getItem(prefix + key));

        if (storage === null) {
            storage = setStorage(key, settingsDefault[key]);
        } else {
            storage = localStorage.getItem(prefix + key);
        }

        storage = (storage === 'false') ? false : storage;
        storage = (storage === 'true') ? true : storage;

        return storage;
    }

    function setStorage(key, value) {
        let prefix = "sycBox_";
        localStorage.setItem(prefix + key, value);
        return value;
    }

    function updateSettings() {
        settings = {
            'removeSmileys': getStorage('removeSmileys'),
            'useEnglishChannel': getStorage('useEnglishChannel'),
        };
    }

    updateSettings();
    initTable();

    $('#sycBoxSend').keypress(function (e) {
        let key = e.which;
        if (key === 13) {
            sendMessage($(this).val());
            $(this).val('');
        }
    });

    $('#sycBoxMenuBtn').on('click', function () {
        $('#sycBoxMenu').toggle();
    });

    $('#sycBoxMenuClose').on('click', function () {
        $('#sycBoxMenu').toggle();
    });

    $('input.sycBoxSetToggle').on('change', function () {
        setStorage($(this).attr('data-sycbox-id'), $(this).is(':checked'));
        updateSettings();

        if ($(this).attr('data-sycbox-id') === 'useEnglishChannel') {
            clearSB();
        }
    });

    $('#sycBoxTbody').on('click', 'span.sycBoxTime', function () {
        let message = getMessageById(parseInt($(this).attr('data-sycbox-id')));
        let bburl = '@[URL="' + message.user.url + '"]' +
            message.user.name + '[/URL] ';

        let $input = $('#sycBoxSend');
        $input.val($input.val() + bburl);
        $input.focus();
    });

    window.setInterval(function () {
        window.clearTimeout(idleTimeout);
    }, 30000);

    window.setInterval(function () {
        if (refreshActive) {
            updateChatHistory();
        }
    }, 5000);

    // apply css
    appendStyleRaw(
        '.sycBoxTitle { \
            padding: 4px; \
            color: white; \
            background: #1c1e20; \
            text-align: center; \
            font: bold 11px tahoma,verdana,geneva,lucida,arial,helvetica,sans-serif; \
        } \
        #sycBoxTable { \
            padding: 2px; \
            border: 1px solid #CCCCCC; \
            background-color: #EDEDED; \
            border-collapse: collapse; \
            border-spacing: 0; \
            display: block; \
            table-layout: fixed; \
            overflow: auto; \
        } \
        #sycBoxTbody { \
            width: 100%; \
        } \
        #sycBoxTbody td { \
            padding: 1px 2px 1px 2px; \
            vertical-align: top; \
            white-space: nowrap; \
            font-size: 11px; \
        } \
        #sycBoxTbody tr td:last-child { \
            width: 100%; \
            word-wrap: break-word; \
            white-space: normal; \
        } \
        #sycBoxSend { \
            height: 15px; \
            width: 100%; \
            margin-right: -3px; \
            padding: 0 2px 0 2px; \
            border: 1px solid #CCCCCC; \
            border-top: 0; \
            font-size: 11px; \
            box-sizing: border-box; \
            -moz-box-sizing: border-box; \
        } \
        span.sycBoxTime { \
            cursor: pointer; \
        } \
        #sycBoxMenu { \
            position: absolute; \
            top: 20%; \
            left: 50%; \
            min-height: 50px; \
            width: 350px; \
            margin-left: -175px; \
            display: none; \
            border: 1px solid #CCCCCC; \
            background-color: #EDEDED; \
        } \
        #sycBoxMenuContent { \
            padding: 3px 3px 15px 3px; \
        } \
        #sycBoxMenuContent > label { \
            display: block; \
            padding: 0 0 3px 4px; \
        } \
        input[type="checkbox"].sycBoxSetToggle { \
            position: relative; \
            top: -1px; \
            width: 13px; \
            height: 13px; \
            margin:0; \
            margin-right: 3px; \
            padding: 0; \
            vertical-align: bottom; \
            *overflow: hidden; \
        } \
        #sycBoxMenuClose { \
            position: absolute; \
            top: 0; \
            right: 0; \
            padding: 1px 4px 0 0; \
            color: white; \
            font-size: 14px; \
            cursor: pointer; \
        } \
        .sycBoxMenuFooter { \
            position: absolute; \
            left: 0; \
            right: 0; \
            bottom: 0; \
            padding: 2px; \
            text-align: right; \
            font-size: 10px; \
        } \
        .sycBoxMenuFooter > #sycBoxMenuFooterLeft { \
            position: absolute; \
            bottom: 0; \
            text-align: left; \
        } \
        a:link.sycBox { \
            text-decoration: none; \
        } \
        a:visited.sycBox { \
            text-decoration: none; \
        } \
        a:hover.sycBox { \
            text-decoration: none; \
        } \
        a:active.sycBox { \
            text-decoration: none; \
        }'
    );
})(jQuery);
<commit_msg>jquery selector / changed css<commit_after>// ==UserScript==
// @name        SycBox
// @namespace   Elitepvpers
// @description customized ShoutBox
// @include     *//www.elitepvpers.com/forum/
// @author      Syc
// @version     1.0.9
// @downloadURL https://github.com/epvpsyc/SycBox/raw/master/SycBox.user.js
// @updateURL   https://github.com/epvpsyc/SycBox/raw/master/SycBox.user.js
// @grant       none
// ==/UserScript==
(function ($) {
    // refreshActive will be false for the beginning
    // this is supposed to prevent refreshing the SB before the SB is even properly initialized
    let refreshActive = false;

    let chatHistory = [];
    let settings;

    // adding css style this way
    // since github did not like the way of splitting the css into a separate file
    // and loading it from github
    //
    // thanks to Der-Eddy
    function appendStyleRaw(style) {
        if (style) {
            $('').appendTo('head');
        }
    }

    function updateChatHistory(changes) {
        refreshActive = false;

        // 'changes' will indicate if there is a new message that needs to be added to the SB
        $.ajax({
            url: '//www.elitepvpers.com/forum/mgc_cb_evo_ajax.php',
            type: 'POST',
            dataType: 'xml',
            data: {
                'do': 'ajax_refresh_chat',
                'status': 'open',
                'channel_id': (settings.useEnglishChannel ? 1 : 0),
                'location': 'full',
                'first_load': 1,
                'securitytoken': SECURITYTOKEN,
            }
        }).done(function (data) {
            data = $.parseHTML($(data).find('chatbox_content').text());

            $(data).find('tr.alt2').each(function () {
                let id = parseInt($(this).find('td:first').attr('id').replace('chat_', ''));

                if (!getMessageById(id)) {
                    let $username = $(this).find('td:nth-last-child(2) > span > a');
                    let color = ($username.find('span').length) ? $username.find('span').css('color') : 'black';
                    let username = ($username.find('span').length) ? $username.find('span').html() : $username.text().slice(1, -1);
                    let text = $(this).find('td').last().html().trim();
                    let url = $(this).find('td:nth-last-child(2) > span > a').attr('href');
                    let userid = parseInt(url.split('/').pop().split('-')[0]);

                    text = (settings.removeSmileys) ? removeSmileys(text) : text;

                    let message = {
                        'id': id,
                        'time': $(this).find('.mgc_cb_evo_date').text().trim(),
                        'user': {
                            'id': userid,
                            'url': url,
                            'name': username,
                            'color': color,
                        },
                        'text': text,
                        'appended': false,
                    };

                    chatHistory.push(message);
                    changes = true;
                }
            });

            if (changes) {
                appendToSB();
            }

            refreshActive = true;
        });
    }

    function initTable() {
        let $oldSB = $('tbody#mgc_cb_evo_opened');
        let sbWidth = $oldSB.width();
        let sbHeight = $oldSB.height();

        $oldSB.parent().html('

');
        $('#sycBoxTbody').parent().attr('id', 'sycBoxTable');
        let $sycBoxTable = $('#sycBoxTable');

        // title
        $sycBoxTable.before(
            '
e*pvp Premium Shoutbox ' +
            '[Archiv]' +
            '[SycBox]' +
            '
'
        );

        // input
        $sycBoxTable.after(
            '
' +
            '<input type="text" id="sycBoxSend" tabindex="1">' +
            '
'
        );

        // set sizes for sb
        $sycBoxTable.width(sbWidth + 'px');
        $sycBoxTable.css('max-height', sbHeight + 'px');

        updateChatHistory(true);
        initMenu();
    }

    function appendToSB() {
        let appended = false;

        chatHistory.reverse().forEach(function (chat, i) {
            if (!chat.appended) {
                chatHistory[i].appended = true;

                // layout:
                // date | username | message text
                // 13:37 Syc this is an example message

                let line = '
' +
                    '' +
                    chat.time +
                    '	' +
                    '' +
                    chat.user.name +
                    '	' +
                    '' + chat.text + '	' +
                    '
';
                $('#sycBoxTbody').append(line);
                appended = true;
            }
        });

        chatHistory.reverse();

        // in the case of a message actually being added to the SB, scroll down
        if (appended) {
            let $sycBoxTable = $('#sycBoxTable');
            $sycBoxTable.animate({
                scrollTop: $sycBoxTable.prop('scrollHeight')
            });
        }
    }

    function clearSB() {
        chatHistory = [];
        $('#sycBoxTable').find('tr').remove();

        updateChatHistory(true);
    }

    function initMenu() {
        let removeSmileysHtml = (settings.removeSmileys) ? 'checked' : '';
        let useEnglishChannelHtml = (settings.useEnglishChannel) ? 'checked' : '';

        $(
            '
' +
            '
sycBox Settings' +
            '
x
' +
            '
' +
            '
' +
            '' +
            '<input type="checkbox" data-sycbox-id="removeSmileys" class="sycBoxSetToggle"' + removeSmileysHtml + '>' +
            'remove smiley images' +
            '' +
            '' +
            '<input type="checkbox" data-sycbox-id="useEnglishChannel" class="sycBoxSetToggle"' + useEnglishChannelHtml + '>' +
            'use english channel' +
            '' +
            '
' +
            '
' +
            '
' +
            'v' + GM_info['script']['version'] +
            '
' +
            'Github | ©Syc' +
            '
' +
            '
'
        ).appendTo('body');
    }

    function removeSmileys(text) {
        let smileys = [
            ['smile.gif', ':)'],
            ['confused.gif', ':confused:'],
            ['rtfm.gif', ':rtfm:'],
            ['pimp.gif', ':pimp:'],
            ['mofo.gif', ':mofo:'],
            ['handsdown.gif', ':handsdown:'],
            ['bandit.gif', ':bandit:'],
            ['cool.gif', ':cool:'],
            ['facepalm.gif', ':facepalm:'],
            ['frown.gif', ':('],
            ['mad.gif', ':mad:'],
            ['tongue.gif', ':p'],
            ['wink.gif', ';)'],
            ['biggrin.gif', ':D'],
            ['redface.gif', ':o'],
            ['rolleyes.gif', ':rolleyes:'],
            ['eek.gif', ':eek:'],
            ['awesome.gif', ':awesome:'],
        ];

        smileys.forEach(function (smiley, i) {
            let html = $('
').html(text);
            html.find('img[src$="' + smileys[i][0] + '"]').replaceWith(smileys[i][1]);
            text = html.html();
        });

        return $(text).html();
    }

    function getMessageById(id) {
        let message = undefined;

        chatHistory.forEach(function (chat) {
            if (chat.id === id) {
                message = chat;
            }
        });

        return message;
    }

    function sendMessage(message) {
        refreshActive = false;
        let channelID = (settings.useEnglishChannel ? 1 : 0);

        let sendAjax = new vB_AJAX_Handler(true);
        sendAjax.send('//www.elitepvpers.com/forum/mgc_cb_evo_ajax.php', 'do=ajax_chat&channel_id=' + channelID + '&chat=' + PHP.urlencode(message) + '&securitytoken=' + SECURITYTOKEN);
        setTimeout(function () {
            updateChatHistory();
        }, 1000);
    }

    function getStorage(key) {
        const settingsDefault = {
            'removeSmileys': true,
            'useEnglishChannel': false,
        };

        let prefix = "sycBox_";
        let storage = (localStorage.getItem(prefix + key));

        if (storage === null) {
            storage = setStorage(key, settingsDefault[key]);
        } else {
            storage = localStorage.getItem(prefix + key);
        }

        storage = (storage === 'false') ? false : storage;
        storage = (storage === 'true') ? true : storage;

        return storage;
    }

    function setStorage(key, value) {
        let prefix = "sycBox_";
        localStorage.setItem(prefix + key, value);
        return value;
    }

    function updateSettings() {
        settings = {
            'removeSmileys': getStorage('removeSmileys'),
            'useEnglishChannel': getStorage('useEnglishChannel'),
        };
    }

    updateSettings();
    initTable();

    $('#sycBoxSend').keypress(function (e) {
        let key = e.which;
        if (key === 13) {
            sendMessage($(this).val());
            $(this).val('');
        }
    });

    $('#sycBoxMenuBtn').on('click', function () {
        $('#sycBoxMenu').toggle();
    });

    $('#sycBoxMenuClose').on('click', function () {
        $('#sycBoxMenu').toggle();
    });

    $('input.sycBoxSetToggle').on('change', function () {
        setStorage($(this).attr('data-sycbox-id'), $(this).is(':checked'));
        updateSettings();

        if ($(this).attr('data-sycbox-id') === 'useEnglishChannel') {
            clearSB();
        }
    });

    $('#sycBoxTbody').on('click', 'span.sycBoxTime', function () {
        let message = getMessageById(parseInt($(this).attr('data-sycbox-id')));
        let bburl = '@[URL="' + message.user.url + '"]' +
            message.user.name + '[/URL] ';

        let $input = $('#sycBoxSend');
        $input.val($input.val() + bburl);
        $input.focus();
    });

    window.setInterval(function () {
        window.clearTimeout(idleTimeout);
    }, 30000);

    window.setInterval(function () {
        if (refreshActive) {
            updateChatHistory();
        }
    }, 5000);

    // apply css
    appendStyleRaw(
        '.sycBoxTitle {\
            padding: 4px;\
            color: white;\
            background: #1c1e20;\
            text-align: center;\
            font: bold 11px tahoma,verdana,geneva,lucida,arial,helvetica,sans-serif;\
        }\
        .sycBoxTitle > span {\
            text-decoration: underline;\
            cursor: pointer;\
        }\
        #sycBoxTable {\
            padding: 2px;\
            border: 1px solid #CCCCCC;\
            background-color: #EDEDED;\
            border-collapse: collapse;\
            border-spacing: 0;\
            display: block;\
            table-layout: fixed;\
            overflow: auto;\
        }\
        #sycBoxTbody {\
            width: 100%;\
        }\
        #sycBoxTbody td {\
            padding: 1px 2px 1px 2px;\
            vertical-align: top;\
            white-space: nowrap;\
            font-size: 11px;\
        }\
        #sycBoxTbody tr td:last-child {\
            width: 100%;\
            word-wrap: break-word;\
            white-space: normal;\
        }\
        #sycBoxSend {\
            height: 15px;\
            width: 100%;\
            margin-right: -3px;\
            padding: 0 2px 0 2px;\
            border: 1px solid #CCCCCC;\
            border-top: 0;\
            font-size: 11px;\
            box-sizing: border-box;\
            -moz-box-sizing: border-box;\
        }\
        span.sycBoxTime {\
            cursor: pointer;\
        }\
        #sycBoxMenu {\
            position: absolute;\
            top: 20%;\
            left: 50%;\
            min-height: 50px;\
            width: 350px;\
            margin-left: -175px;\
            display: none;\
            border: 1px solid #CCCCCC;\
            background-color: #EDEDED;\
        }\
        #sycBoxMenuContent {\
            padding: 3px 3px 15px 3px;\
        }\
        #sycBoxMenuContent > label {\
            display: block;\
            padding: 0 0 3px 4px;\
        }\
        input[type="checkbox"].sycBoxSetToggle {\
            position: relative;\
            top: -1px;\
            width: 13px;\
            height: 13px;\
            margin:0;\
            margin-right: 3px;\
            padding: 0;\
            vertical-align: bottom;\
            *overflow: hidden;\
        }\
        #sycBoxMenuClose {\
            position: absolute;\
            top: 0;\
            right: 0;\
            padding: 1px 4px 0 0;\
            color: white;\
            font-size: 14px;\
            cursor: pointer;\
        }\
        .sycBoxMenuFooter {\
            position: absolute;\
            left: 0;\
            right: 0;\
            bottom: 0;\
            padding: 2px;\
            text-align: right;\
            font-size: 10px;\
        }\
        .sycBoxMenuFooter > #sycBoxMenuFooterLeft {\
            position: absolute;\
            bottom: 0;\
            text-align: left;\
        }\
        a:link.sycBox {\
            text-decoration: none;\
        }\
        a:visited.sycBox {\
            text-decoration: none;\
        }\
        a:hover.sycBox {\
            text-decoration: none;\
        }\
        a:active.sycBox {\
            text-decoration: none;\
        }'
    );
})(jQuery);
<|endoftext|>


<commit_before>describe('Backbone.Validator', function() {
  'use strict';

  var Validator = Backbone.Validator,
    fn, model, view, spy;

  var create = function(base, classOpts, instanceOpts) {
    var Model = base.extend(classOpts || {});
    return new Model(instanceOpts || {});
  };

  Validator.apply();

  describe('Model', function() {
    beforeEach(function() {
      model = create(Backbone.Model, {
        validation: {
          field_1: {
            required: true,
            message: '#1 required'
          },

          field_2: {
            required: true,
            format: 'email',
            message: '#2 format'
          },

          field_3: [
            {
              required: true,
              message: '#3 required'
            },
            {
              message: '#3 from fn',
              fn: function(value) {
                return !!value ? true : '#3 overridden';
              }
            },
            {
              message: '#3 from config',
              fn: function(value) {
                return !!value;
              }
            }
          ]
        }
      });

      spyOn(model, 'sync');
    });

    describe('via #save', function() {
      it('validates passed attributes', function() {
        model.save({field_1: null});
        expect(model.validationError).toEqual({
          field_1: ['#1 required']
        });
      });

      it('validates passed attributes even if no validation for it', function() {
        model.save({field_8: 1, field_9: 1});
        expect(model.validationError).toBeNull();
      });

      it('validates entire model', function() {
        model.save();
        expect(model.validationError).toEqual({
          field_1: ['#1 required'],
          field_2: ['#2 format'],
          field_3: ['#3 required', '#3 from fn', '#3 from config']
        });
      });

      it('passes validation with proper values', function() {
        model.save({field_1: 1, field_2: 'user@example.com', field_3: 1 });
        expect(model.validationError).toBeNull();
      });
    });

    describe('via #isValid', function() {
      it('validates passed attributes', function() {
        expect(model.isValid('field_1')).toBeFalsy();
      });

      it('validates passed attributes even if no validation for it', function() {
        expect(model.isValid()).toBeFalsy();
      });

      it('validates entire model', function() {
        expect(model.isValid()).toBeFalsy();
      });

      it('passes validation with proper values', function() {
        model.set({field_1: 1, field_2: 'user@example.com', field_3: 1 });
        expect(model.isValid()).toBeTruthy();
      });
    });

    describe('via #set', function() {
      it('validates passed attributes', function() {
        model.set({field_1: null}, {validate: true});
        expect(model.validationError).toEqual({
          field_1: ['#1 required']
        });
      });

      it('validates passed attributes even if no validation for it', function() {
        model.set({field_8: 1, field_9: 1}, {validate: true});
        expect(model.validationError).toBeNull();
      });

      it('passes validation with proper values', function() {
        model.set({field_1: 1, field_2: 'user@example.com', field_3: 1 }, {validate: true});
        expect(model.validationError).toBeNull();
      });
    });
  });

  describe('#addValidator', function() {
    beforeEach(function() {
      spy = jasmine.createSpy('validator');
      Validator.addValidator('custom', spy);
    });

    it('passes expected value', function() {
      model = create(Backbone.Model, {
        validation: {
          field_1: {
            custom: 10
          }
        }
      });

      model.set({field_1: 1}, {validate: true});
      expect(spy).toHaveBeenCalledWith(1, 10);
    });
  });

  describe('validators', function() {
    var expectToFail = function(validatorName, value, expectation, errorMessage) {
      var validations = { attr: {} };
      validations.attr[validatorName] = expectation;

      it('fails with ' + jasmine.pp(value) + ' and returns ' + errorMessage, function() {
        expect(Validator.validate({attr: value}, validations).attr[0]).toBeDefined();
      });
    };

    var expectToPass = function(validatorName, value, expectation) {
      it('passes with ' + jasmine.pp(value), function() {
        var validations = { attr: {} };
        validations.attr[validatorName] = expectation;
        expect(Validator.validate({attr: value}, validations)).toEqual(null);
      });
    };

    describe('required', function() {
      expectToPass('required', 'Hello', true);
      expectToPass('required', true, true);
      expectToPass('required', 1, true);
      expectToFail('required', false, true);
      expectToFail('required', '', true);
      expectToFail('required', null, true);
      expectToFail('required', undefined, true);
    });

    describe('minLength', function() {
      expectToPass('minLength', 'Sam', 3);
      expectToFail('minLength', 'S', 3);
    });

    describe('maxLength', function() {
      expectToPass('maxLength', 'Sam', 3);
      expectToFail('maxLength', 'Samuel', 3);
    });

    describe('fn', function() {
      expectToPass('fn', 'Sam', function(value) {
        return value.length === 3;
      });

      expectToFail('fn', 'Samuel', function(value) {
        return value.length === 3;
      });

      expectToFail('fn', 'Samuel', function(value) {
        return value.length === 3 ? null : 'custom message';
      }, 'custom message');
    });

    describe('format', function() {
      expectToPass('format', 'a_b_c', /^([abc_]*)$/);
      expectToFail('format', 'a_b_c_d', /^([abc_]*)$/);

      expectToPass('format', '', 'email');
      expectToPass('format', 'user@example.com', 'email');
      expectToFail('format', 'user_example.com', 'email');

      expectToPass('format', 'http://example.com', 'url');
      expectToFail('format', 'http_example_com', 'url');

      expectToPass('format', '1234', 'digits');
      expectToFail('format', '1234a', 'digits');

      expectToPass('format', '123.789', 'number');
      expectToFail('format', '123.789a', 'number');
    });

    describe('collection', function() {
      var User = Backbone.Model.extend({
        validation: {
          name: {
            required: true
          }
        }
      });

      var Users = Backbone.Collection.extend({ model: User });

      expectToPass('collection', new Users([
        { name: 'Sam' },
        { name: 'Tom' },
        { name: 'Dan' }
      ]));

      expectToFail('collection', new Users([
        { name: 'Sam' },
        { name: '' },
        { name: '' }
      ]), true, [
        [1, { name: ['Is required'] }],
        [2, { name: ['Is required'] }]
      ]);

      expectToPass('collection', [
        new User({ name: 'Sam' }),
        new User({ name: 'Tom' }),
        new User({ name: 'Dan' })
      ]);

      expectToFail('collection', [
        new User({ name: 'Sam' }),
        new User({ name: '' }),
        new User({ name: '' })
      ], true, [
        [1, { name: ['Is required'] }],
        [2, { name: ['Is required'] }]
      ]);
    });
  });
});
<commit_msg>Add view specs<commit_after>describe('Backbone.Validator', function() {
  'use strict';

  var Validator = Backbone.Validator,
    fn, model, view, spy;

  var create = function(base, classOpts, instanceOpts) {
    var Model = base.extend(classOpts || {});
    return new Model(instanceOpts || {});
  };

  Validator.apply();

  describe('#validators', function() {
    var pp = function(value) {
      var str = jasmine.pp(value);
      return str.length > 100 ? str.substr(0, 100) + '...' : str;
    };

    var expectToFail = function(validatorName, value, expectation, errorMessage) {
      var validations = { attr: {} };
      validations.attr[validatorName] = expectation;

      it('fails with ' + pp(value) + ' and returns ' + pp(errorMessage), function() {
        expect(Validator.validate({attr: value}, validations).attr[0]).toBeDefined();
      });
    };

    var expectToPass = function(validatorName, value, expectation) {
      it('passes with ' + pp(value), function() {
        var validations = { attr: {} };
        validations.attr[validatorName] = expectation;
        expect(Validator.validate({attr: value}, validations)).toEqual(null);
      });
    };

    describe('add', function() {
      beforeEach(function() {
        spy = jasmine.createSpy('validator');
        Validator.addValidator('custom', spy);
      });

      it('passes expected value', function() {
        model = create(Backbone.Model, {
          validation: {
            field_1: {
              custom: 10
            }
          }
        });

        model.set({field_1: 1}, {validate: true});
        expect(spy).toHaveBeenCalledWith(1, 10);
      });
    });

    describe('required', function() {
      expectToPass('required', 'Hello', true);
      expectToPass('required', true, true);
      expectToPass('required', 1, true);
      expectToFail('required', false, true);
      expectToFail('required', '', true);
      expectToFail('required', null, true);
      expectToFail('required', undefined, true);
    });

    describe('minLength', function() {
      expectToPass('minLength', 'Sam', 3);
      expectToFail('minLength', 'S', 3);
    });

    describe('maxLength', function() {
      expectToPass('maxLength', 'Sam', 3);
      expectToFail('maxLength', 'Samuel', 3);
    });

    describe('fn', function() {
      expectToPass('fn', 'Sam', function(value) {
        return value.length === 3;
      });

      expectToFail('fn', 'Samuel', function(value) {
        return value.length === 3;
      });

      expectToFail('fn', 'Samuel', function(value) {
        return value.length === 3 ? null : 'custom message';
      }, 'custom message');
    });

    describe('format', function() {
      expectToPass('format', 'a_b_c', /^([abc_]*)$/);
      expectToFail('format', 'a_b_c_d', /^([abc_]*)$/);

      expectToPass('format', '', 'email');
      expectToPass('format', 'user@example.com', 'email');
      expectToFail('format', 'user_example.com', 'email');

      expectToPass('format', 'http://example.com', 'url');
      expectToFail('format', 'http_example_com', 'url');

      expectToPass('format', '1234', 'digits');
      expectToFail('format', '1234a', 'digits');

      expectToPass('format', '123.789', 'number');
      expectToFail('format', '123.789a', 'number');
    });

    describe('collection', function() {
      var User = Backbone.Model.extend({
        validation: {
          name: {
            required: true
          }
        }
      });

      var Users = Backbone.Collection.extend({ model: User });

      expectToPass('collection', new Users([
        { name: 'Sam' },
        { name: 'Tom' },
        { name: 'Dan' }
      ]));

      expectToFail('collection', new Users([
        { name: 'Sam' },
        { name: '' },
        { name: '' }
      ]), true, [
        [1, { name: ['Is required'] }],
        [2, { name: ['Is required'] }]
      ]);

      expectToPass('collection', [
        new User({ name: 'Sam' }),
        new User({ name: 'Tom' }),
        new User({ name: 'Dan' })
      ]);

      expectToFail('collection', [
        new User({ name: 'Sam' }),
        new User({ name: '' }),
        new User({ name: '' })
      ], true, [
        [1, { name: ['Is required'] }],
        [2, { name: ['Is required'] }]
      ]);
    });
  });

  describe('Model', function() {
    beforeEach(function() {
      model = create(Backbone.Model, {
        validation: {
          field_1: {
            required: true,
            message: '#1 required'
          },

          field_2: {
            required: true,
            format: 'email',
            message: '#2 format'
          },

          field_3: [
            {
              required: true,
              message: '#3 required'
            },
            {
              message: '#3 from fn',
              fn: function(value) {
                return !!value ? true : '#3 overridden';
              }
            },
            {
              message: '#3 from config',
              fn: function(value) {
                return !!value;
              }
            }
          ]
        }
      });

      spyOn(model, 'sync');
    });

    describe('via #save', function() {
      it('validates passed attributes', function() {
        model.save({field_1: null});
        expect(model.validationError).toEqual({
          field_1: ['#1 required']
        });
      });

      it('validates passed attributes even if no validation for it', function() {
        model.save({field_8: 1, field_9: 1});
        expect(model.validationError).toBeNull();
      });

      it('validates entire model', function() {
        model.save();
        expect(model.validationError).toEqual({
          field_1: ['#1 required'],
          field_2: ['#2 format'],
          field_3: ['#3 required', '#3 from fn', '#3 from config']
        });
      });

      it('passes validation with proper values', function() {
        model.save({field_1: 1, field_2: 'user@example.com', field_3: 1 });
        expect(model.validationError).toBeNull();
      });
    });

    describe('via #isValid', function() {
      it('validates passed attributes', function() {
        expect(model.isValid('field_1')).toBeFalsy();
      });

      it('validates passed attributes even if no validation for it', function() {
        expect(model.isValid()).toBeFalsy();
      });

      it('validates entire model', function() {
        expect(model.isValid()).toBeFalsy();
      });

      it('passes validation with proper values', function() {
        model.set({field_1: 1, field_2: 'user@example.com', field_3: 1 });
        expect(model.isValid()).toBeTruthy();
      });
    });

    describe('via #set', function() {
      it('validates passed attributes', function() {
        model.set({field_1: null}, {validate: true});
        expect(model.validationError).toEqual({
          field_1: ['#1 required']
        });
      });

      it('validates passed attributes even if no validation for it', function() {
        model.set({field_8: 1, field_9: 1}, {validate: true});
        expect(model.validationError).toBeNull();
      });

      it('passes validation with proper values', function() {
        model.set({field_1: 1, field_2: 'user@example.com', field_3: 1 }, {validate: true});
        expect(model.validationError).toBeNull();
      });
    });
  });

  describe('View', function() {
    var valid, invalid;

    beforeEach(function() {
      valid = jasmine.createSpy('valid');
      invalid = jasmine.createSpy('invalid');

      model = create(Backbone.Model, {
        validation: {
          email: {
            format: 'email',
            message: 'Invalid email'
          },

          name: {
            minLength: 4
          }
        }
      });

      view = create(Backbone.View, {
        model: model
      });

      jasmine.Clock.useMock();
    });

    afterEach(function() {
      view.remove();
    });

    it('raises error if model does not exist', function() {
      view.model = null;
      expect(function() {
        view.bindValidation();
      }).toThrow('Model is not provided');
    });

    describe('when validating model', function() {
      var expectBinding = function(setup) {
        setup();

        model.validate({ email: 'user_example_com', name: 'Valid name' });
        jasmine.Clock.tick(50);

        expect(valid).toHaveBeenCalledWith('name', 'Valid name', model);
        expect(invalid).toHaveBeenCalledWith('email', 'user_example_com', ['Invalid email'], model);
      };

      it('runs callbacks from options if specified', function() {
        expectBinding(function() {
          view.bindValidation(model, {
            onValidField: valid,
            onInvalidField: invalid
          });
        });
      });

      it('runs callbacks from view instance if specified', function() {
        expectBinding(function() {
          view.bindValidation(model);
          _.extend(view, {
            onValidField: valid,
            onInvalidField: invalid
          });
        });
      });

      it('runs callbacks fallback from Backbone.Validator.ViewCallbacks', function() {
        expectBinding(function() {
          view.bindValidation(model);
          Validator.ViewCallbacks = {
            onValidField: valid,
            onInvalidField: invalid
          };
        });
      });
    });

    describe('when view removed', function() {
      it('unbinds `validated` events', function() {
        view.bindValidation(model, {
          onValidField: valid,
          onInvalidField: invalid

        });

        view.remove();
        model.validate();
        jasmine.Clock.tick(50);

        expect(valid).not.toHaveBeenCalled();
        expect(invalid).not.toHaveBeenCalled();
      });

      it('unbinds only `validated` event', function() {
        spy = jasmine.createSpy('spy');
        model.on('validated', spy);
        view.bindValidation(model, {
          onValidField: valid,
          onInvalidField: invalid
        });
        view.remove();

        model.validate({ email: 'user@example.com' });
        jasmine.Clock.tick(50);

        expect(spy).toHaveBeenCalled();
        expect(valid).not.toHaveBeenCalled();
        expect(invalid).not.toHaveBeenCalled();
      });
    });
  });
});
<|endoftext|>


<commit_before>window.BTCPayShopifyIntegrationModule = function () {
    if (!window.btcpay) {
        throw new Error("The BTCPay modal js was not loaded on this page.");
    }
    if (!window.Shopify) {
        throw new Error("The Shopify global object was not loaded on this page.");
    }
    if (!window.BTCPAYSERVER_URL || !window.STORE_ID) {
        throw new Error("The BTCPAYSERVER_URL STORE_ID global vars were not set on this page.");
    }
    const shopify_order_id = Shopify.checkout.order_id;
    const btcPayServerUrl = window.BTCPAYSERVER_URL;
    const storeId = window.STORE_ID;
    var currentInvoiceData;
    var modalShown = false;

    const pageElements = document.querySelector.bind(document);
    const insertElement = (document.querySelectorAll.bind(document),
        (e,
         n) => {
            n.parentNode.insertBefore(e,
                n.nextSibling)
        });

    let buttonElement = null;

    const pageItems = {
        mainHeader: pageElements("#main-header"),
        orderConfirmed: pageElements(".os-step__title"),
        orderConfirmedDescription: pageElements(".os-step__description"),
        continueButton: pageElements(".step__footer__continue-btn"),
        checkMarkIcon: pageElements(".os-header__hanging-icon"),
        orderStatus: pageElements(".os-header__title"),
        paymentMethod: pageElements(".payment-method-list__item__info"),
        price: pageElements(".payment-due__price"),
        finalPrice: pageElements(".total-recap__final-price"),
        orderNumber: pageElements(".os-order-number"),
    }

    function setOrderAsPaid() {
        pageItems.mainHeader.innerText = "Thank you!",
        pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "block"),
        pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "block"),
        pageItems.continueButton && (pageItems.continueButton.style.visibility = "visible"),
        pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "visible"),
        buttonElement && (buttonElement.style.display = "none");
    }

    function showPaymentInstructions() {
        pageItems.mainHeader && (pageItems.mainHeader.innerText = "Review and pay!"),
        pageItems.continueButton && (pageItems.continueButton.style.visibility = "hidden"),
        pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "hidden"),
        pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "none"),
        pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "none");
    }

    function getOrCheckInvoice(backgroundCheck) {
        const url = btcPayServerUrl + "/stores/" + storeId + "/integrations/shopify/" + shopify_order_id + (backgroundCheck ? "?checkonly=true" : "");
        return fetch(url, {
            method: "GET",
            mode: "cors", // no-cors, cors, *same-origin,
            headers: {
                "Content-Type": "application/json",
                "accept": "application/json",
            }
        })
            .then(function (response) {
                return response.json();
            }).catch(function () {
                if (!backgroundCheck)
                    alert("Could not initiate BTCPay Server payment method, try again later.");
            })
    }

    function onPayButtonClicked() {
        buttonElement.innerHTML = "Displaying Invoice...";

        getOrCheckInvoice().then(handleInvoiceData).catch(fail.bind(this));
    }

    function handleInvoiceData(data, opts) {
        currentInvoiceData = data;
        if (!currentInvoiceData) {
            if (modalShown) {
                window.btcpay.hideFrame();
                fail();
            }else if(opts && opts.backgroundCheck){
                injectPaymentButtonHtml();
            }else{
                fail();
            }
            return;
        }
        if (["complete", "confirmed", "paid"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
            setOrderAsPaid();
        } else if (["invalid", "expired"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
            fail();
        } else if (!opts || !opts.backgroundCheck) {
            showModal();
        }
    }

    function showModal() {
        if (currentInvoiceData && !modalShown) {
            modalShown = true;
            window.btcpay.setApiUrlPrefix(btcPayServerUrl);

            window.btcpay.onModalReceiveMessage(function (evt) {
                if (evt && evt.invoiceId && evt.status) {
                    currentInvoiceData = evt;
                }
            });

            window.btcpay.onModalWillEnter(function () {
                modalShown = true;
            });

            window.btcpay.onModalWillLeave(function () {
                modalShown = false;
                getOrCheckInvoice(true).then(function (d) {
                    handleInvoiceData(d, {backgroundCheck: true})
                });
            });
            window.btcpay.showInvoice(currentInvoiceData.invoiceId);
        }
    }

    function fail() {
        currentInvoiceData = null;
        buttonElement.innerHTML = payButtonHtml;
    }

    const payButtonHtml = '<button class="" onclick="onPayButtonClicked()" style="width:210px; border: none; outline: none;"></button>';

    function injectPaymentButtonHtml() {
        // Payment button that opens modal
        buttonElement = document.getElementById("btcpayserver-pay");
        if (buttonElement) {
            return;
        }
        buttonElement = document.createElement("div");
        buttonElement.id = "btcpayserver-pay";
        buttonElement.innerHTML = payButtonHtml;
        insertElement(buttonElement, pageItems.orderConfirmed);
    }

    if (["bitcoin", "btc", "btcpayserver"].filter(value => pageItems.paymentMethod.innerText.toLowerCase().indexOf(value) !== -1).length === 0) {
        return;
    }
    showPaymentInstructions();
    window.onPayButtonClicked = onPayButtonClicked.bind(this);
    getOrCheckInvoice(true).then(function (d) {
        handleInvoiceData(d, {backgroundCheck: true})
    });

};

window.addEventListener("load", BTCPayShopifyIntegrationModule);
<commit_msg>Ensuring that elements on page are refreshed as soon as possible<commit_after>window.BTCPayShopifyIntegrationModule = function () {
    const pageElements = document.querySelector.bind(document);
    const insertElement = (document.querySelectorAll.bind(document),
        (e,
            n) => {
            n.parentNode.insertBefore(e,
                n.nextSibling)
        });

    // execute BTCPayShopifyIntegrationModule as soon as possible
    var paymentMethod = pageElements(".payment-method-list__item__info");
    if (null === paymentMethod) {
        return void setTimeout(() => {
            window.BTCPayShopifyIntegrationModule();
        }, 10);
    }

    if (!window.btcpay) {
        throw new Error("The BTCPay modal js was not loaded on this page.");
    }
    if (!window.Shopify) {
        throw new Error("The Shopify global object was not loaded on this page.");
    }
    if (!window.BTCPAYSERVER_URL || !window.STORE_ID) {
        throw new Error("The BTCPAYSERVER_URL STORE_ID global vars were not set on this page.");
    }
    const shopify_order_id = Shopify.checkout.order_id;
    const btcPayServerUrl = window.BTCPAYSERVER_URL;
    const storeId = window.STORE_ID;
    var currentInvoiceData;
    var modalShown = false;

    let buttonElement = null;

    var pageItems = {
        mainHeader: pageElements("#main-header"),
        orderConfirmed: pageElements(".os-step__title"),
        orderConfirmedDescription: pageElements(".os-step__description"),
        continueButton: pageElements(".step__footer__continue-btn"),
        checkMarkIcon: pageElements(".os-header__hanging-icon"),
        orderStatus: pageElements(".os-header__title"),
        paymentMethod: pageElements(".payment-method-list__item__info"),
        price: pageElements(".payment-due__price"),
        finalPrice: pageElements(".total-recap__final-price"),
        orderNumber: pageElements(".os-order-number"),
    };

    function setOrderAsPaid() {
        pageItems.mainHeader.innerText = "Thank you!",
        pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "block"),
        pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "block"),
        pageItems.continueButton && (pageItems.continueButton.style.visibility = "visible"),
        pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "visible"),
        buttonElement && (buttonElement.style.display = "none");
    }

    function showPaymentInstructions() {
        pageItems.mainHeader && (pageItems.mainHeader.innerText = "Review and pay!"),
        pageItems.continueButton && (pageItems.continueButton.style.visibility = "hidden"),
        pageItems.checkMarkIcon && (pageItems.checkMarkIcon.style.visibility = "hidden"),
        pageItems.orderConfirmed && (pageItems.orderConfirmed.style.display = "none"),
        pageItems.orderConfirmedDescription && (pageItems.orderConfirmedDescription.style.display = "none");
    }

    function getOrCheckInvoice(backgroundCheck) {
        const url = btcPayServerUrl + "/stores/" + storeId + "/integrations/shopify/" + shopify_order_id + (backgroundCheck ? "?checkonly=true" : "");
        return fetch(url, {
            method: "GET",
            mode: "cors", // no-cors, cors, *same-origin,
            headers: {
                "Content-Type": "application/json",
                "accept": "application/json",
            }
        })
            .then(function (response) {
                return response.json();
            }).catch(function () {
                if (!backgroundCheck)
                    alert("Could not initiate BTCPay Server payment method, try again later.");
            })
    }

    function onPayButtonClicked() {
        buttonElement.innerHTML = "Displaying Invoice...";

        getOrCheckInvoice().then(handleInvoiceData).catch(fail.bind(this));
    }

    function handleInvoiceData(data, opts) {
        currentInvoiceData = data;
        if (!currentInvoiceData) {
            if (modalShown) {
                window.btcpay.hideFrame();
                fail();
            }else if(opts && opts.backgroundCheck){
                injectPaymentButtonHtml();
            }else{
                fail();
            }
            return;
        }
        if (["complete", "confirmed", "paid"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
            setOrderAsPaid();
        } else if (["invalid", "expired"].indexOf(currentInvoiceData.status.toLowerCase()) >= 0) {
            fail();
        } else if (!opts || !opts.backgroundCheck) {
            showModal();
        }
    }

    function showModal() {
        if (currentInvoiceData && !modalShown) {
            modalShown = true;
            window.btcpay.setApiUrlPrefix(btcPayServerUrl);

            window.btcpay.onModalReceiveMessage(function (evt) {
                if (evt && evt.invoiceId && evt.status) {
                    currentInvoiceData = evt;
                }
            });

            window.btcpay.onModalWillEnter(function () {
                modalShown = true;
            });

            window.btcpay.onModalWillLeave(function () {
                modalShown = false;
                getOrCheckInvoice(true).then(function (d) {
                    handleInvoiceData(d, {backgroundCheck: true})
                });
            });
            window.btcpay.showInvoice(currentInvoiceData.invoiceId);
        }
    }

    function fail() {
        currentInvoiceData = null;
        buttonElement.innerHTML = payButtonHtml;
    }

    const payButtonHtml = '<button class="" onclick="onPayButtonClicked()" style="width:210px; border: none; outline: none;"></button>';

    function injectPaymentButtonHtml() {
        // Payment button that opens modal
        buttonElement = document.getElementById("btcpayserver-pay");
        if (buttonElement) {
            return;
        }
        buttonElement = document.createElement("div");
        buttonElement.id = "btcpayserver-pay";
        buttonElement.innerHTML = payButtonHtml;
        insertElement(buttonElement, pageItems.orderConfirmed);
    }

    if (["bitcoin", "btc", "btcpayserver"].filter(value => pageItems.paymentMethod.innerText.toLowerCase().indexOf(value) !== -1).length === 0) {
        return;
    }
    showPaymentInstructions();
    window.onPayButtonClicked = onPayButtonClicked.bind(this);
    getOrCheckInvoice(true).then(function (d) {
        handleInvoiceData(d, {backgroundCheck: true})
    });

};

window.BTCPayShopifyIntegrationModule();
<|endoftext|>


<commit_before>function var_dump () {
    // http://kevin.vanzonneveld.net
    // +   original by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Zahlii
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // -    depends on: echo
    // %        note 1: For returning a string, use var_export() with the second argument
    // *     example 1: var_dump(1);
    // *     returns 1: 'int(1)'

    var output = '', pad_char = ' ', pad_val = 4, lgth = 0, i = 0, d = this.window.document;
    var _getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }
        return name[1];
    };

    var _repeat_char = function (len, pad_char) {
        var str = '';
        for (var i = 0; i < len; i++) {
            str += pad_char;
        }
        return str;
    };
    var _getInnerVal = function (val, thick_pad) {
        var ret = '';
        if (val === null) {
            ret = 'NULL';
        }
        else if (typeof val === 'boolean') {
            ret = 'bool(' + val + ')';
        }
        else if (typeof val === 'string') {
            ret = 'string(' + val.length+') "' + val + '"';
        }
        else if (typeof val === 'number') {
            if (parseFloat(val) == parseInt(val, 10)) {
                ret = 'int(' + val + ')';
            }
            else {
                ret = 'float('+val+')';
            }
        }
        // The remaining are not PHP behavior because these values only exist in this exact form in JavaScript
        else if (typeof val === 'undefined') {
            ret = 'undefined';
        }
        else if (typeof val === 'function') {
            var funcLines = val.toString().split('\n');
            ret = '';
            for (var i = 0, fll = funcLines.length; i < fll; i++) {
                ret += (i !== 0 ? '\n'+thick_pad : '') + funcLines[i];
            }
        }
        else if (val instanceof Date) {
            ret = 'Date('+val+')';
        }
        else if (val instanceof RegExp) {
            ret = 'RegExp('+val+')';
        }
        else if (val.nodeName) { // Different than PHP's DOMElement
            switch(val.nodeType) {
                case 1:
                    if (typeof val.namespaceURI === 'undefined' || val.namespaceURI === 'http://www.w3.org/1999/xhtml') { // Undefined namespace could be plain XML, but namespaceURI not widely supported
                        ret = 'HTMLElement("' + val.nodeName + '")';
                    }
                    else {
                        ret = 'XML Element("' + val.nodeName + '")';
                    }
                    break;
                case 2:
                    ret = 'ATTRIBUTE_NODE(' + val.nodeName + ')';
                    break;
                case 3:
                    ret = 'TEXT_NODE(' + val.nodeValue + ')';
                    break;
                case 4:
                    ret = 'CDATA_SECTION_NODE(' + val.nodeValue + ')';
                    break;
                case 5:
                    ret = 'ENTITY_REFERENCE_NODE';
                    break;
                case 6:
                    ret = 'ENTITY_NODE';
                    break;
                case 7:
                    ret = 'PROCESSING_INSTRUCTION_NODE(' + val.nodeName + ':' + val.nodeValue+')';
                    break;
                case 8:
                    ret = 'COMMENT_NODE(' + val.nodeValue + ')';
                    break;
                case 9:
                    ret = 'DOCUMENT_NODE';
                    break;
                case 10:
                    ret = 'DOCUMENT_TYPE_NODE';
                    break;
                case 11:
                    ret = 'DOCUMENT_FRAGMENT_NODE';
                    break;
                case 12:
                    ret = 'NOTATION_NODE';
                    break;
            }
        }
        return ret;
    };

    var _formatArray = function (obj, cur_depth, pad_val, pad_char) {
        var someProp = '';
        if (cur_depth > 0) {
            cur_depth++;
        }

        var base_pad = _repeat_char(pad_val * (cur_depth - 1), pad_char);
        var thick_pad = _repeat_char(pad_val * (cur_depth + 1), pad_char);
        var str = '';
        var val = '';

        if (typeof obj === 'object' && obj !== null) {
            if (obj.constructor && _getFuncName(obj.constructor) === 'PHPJS_Resource') {
                return obj.var_dump();
            }
            lgth = 0;
            for (someProp in obj) {
                lgth++;
            }
            str += 'array('+lgth+') {\n';
            for (var key in obj) {
                var objVal = obj[key];
                if (typeof objVal === 'object' && objVal !== null &&
                    !(objVal instanceof Date) && !(objVal instanceof RegExp) && !objVal.nodeName) {
                    str += thick_pad + '[' + key + '] =>\n' + thick_pad + _formatArray(objVal, cur_depth + 1, pad_val, pad_char);
                } else {
                    val = _getInnerVal(objVal, thick_pad);
                    str += thick_pad + '[' + key + '] =>\n' + thick_pad + val + '\n';
                }
            }
            str += base_pad + '}\n';
        } else {
            str = _getInnerVal(obj, thick_pad);
        }
        return str;
    };

    output = _formatArray(arguments[0], 0, pad_val, pad_char);
    for (i=1; i < arguments.length; i++) {
        output += '\n' + _formatArray(arguments[i], 0, pad_val, pad_char);
    }

    if (d.body) {
        this.echo(output);
    }
    else {
        try {
            d = XULDocument; // We're in XUL, so appending as plain text won't work
            this.echo('
'+output+'
');
        }
        catch (e) {
            this.echo(output); // Outputting as plain text may work in some plain XML
        }
    }
}<commit_msg>Improve note in var_dump()<commit_after>function var_dump () {
    // http://kevin.vanzonneveld.net
    // +   original by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Zahlii
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // -    depends on: echo
    // %        note 1: For returning a string, use var_export() with the second argument set to true
    // *     example 1: var_dump(1);
    // *     returns 1: 'int(1)'

    var output = '', pad_char = ' ', pad_val = 4, lgth = 0, i = 0, d = this.window.document;
    var _getFuncName = function (fn) {
        var name = (/\W*function\s+([\w\$]+)\s*\(/).exec(fn);
        if (!name) {
            return '(Anonymous)';
        }
        return name[1];
    };

    var _repeat_char = function (len, pad_char) {
        var str = '';
        for (var i = 0; i < len; i++) {
            str += pad_char;
        }
        return str;
    };
    var _getInnerVal = function (val, thick_pad) {
        var ret = '';
        if (val === null) {
            ret = 'NULL';
        }
        else if (typeof val === 'boolean') {
            ret = 'bool(' + val + ')';
        }
        else if (typeof val === 'string') {
            ret = 'string(' + val.length+') "' + val + '"';
        }
        else if (typeof val === 'number') {
            if (parseFloat(val) == parseInt(val, 10)) {
                ret = 'int(' + val + ')';
            }
            else {
                ret = 'float('+val+')';
            }
        }
        // The remaining are not PHP behavior because these values only exist in this exact form in JavaScript
        else if (typeof val === 'undefined') {
            ret = 'undefined';
        }
        else if (typeof val === 'function') {
            var funcLines = val.toString().split('\n');
            ret = '';
            for (var i = 0, fll = funcLines.length; i < fll; i++) {
                ret += (i !== 0 ? '\n'+thick_pad : '') + funcLines[i];
            }
        }
        else if (val instanceof Date) {
            ret = 'Date('+val+')';
        }
        else if (val instanceof RegExp) {
            ret = 'RegExp('+val+')';
        }
        else if (val.nodeName) { // Different than PHP's DOMElement
            switch(val.nodeType) {
                case 1:
                    if (typeof val.namespaceURI === 'undefined' || val.namespaceURI === 'http://www.w3.org/1999/xhtml') { // Undefined namespace could be plain XML, but namespaceURI not widely supported
                        ret = 'HTMLElement("' + val.nodeName + '")';
                    }
                    else {
                        ret = 'XML Element("' + val.nodeName + '")';
                    }
                    break;
                case 2:
                    ret = 'ATTRIBUTE_NODE(' + val.nodeName + ')';
                    break;
                case 3:
                    ret = 'TEXT_NODE(' + val.nodeValue + ')';
                    break;
                case 4:
                    ret = 'CDATA_SECTION_NODE(' + val.nodeValue + ')';
                    break;
                case 5:
                    ret = 'ENTITY_REFERENCE_NODE';
                    break;
                case 6:
                    ret = 'ENTITY_NODE';
                    break;
                case 7:
                    ret = 'PROCESSING_INSTRUCTION_NODE(' + val.nodeName + ':' + val.nodeValue+')';
                    break;
                case 8:
                    ret = 'COMMENT_NODE(' + val.nodeValue + ')';
                    break;
                case 9:
                    ret = 'DOCUMENT_NODE';
                    break;
                case 10:
                    ret = 'DOCUMENT_TYPE_NODE';
                    break;
                case 11:
                    ret = 'DOCUMENT_FRAGMENT_NODE';
                    break;
                case 12:
                    ret = 'NOTATION_NODE';
                    break;
            }
        }
        return ret;
    };

    var _formatArray = function (obj, cur_depth, pad_val, pad_char) {
        var someProp = '';
        if (cur_depth > 0) {
            cur_depth++;
        }

        var base_pad = _repeat_char(pad_val * (cur_depth - 1), pad_char);
        var thick_pad = _repeat_char(pad_val * (cur_depth + 1), pad_char);
        var str = '';
        var val = '';

        if (typeof obj === 'object' && obj !== null) {
            if (obj.constructor && _getFuncName(obj.constructor) === 'PHPJS_Resource') {
                return obj.var_dump();
            }
            lgth = 0;
            for (someProp in obj) {
                lgth++;
            }
            str += 'array('+lgth+') {\n';
            for (var key in obj) {
                var objVal = obj[key];
                if (typeof objVal === 'object' && objVal !== null &&
                    !(objVal instanceof Date) && !(objVal instanceof RegExp) && !objVal.nodeName) {
                    str += thick_pad + '[' + key + '] =>\n' + thick_pad + _formatArray(objVal, cur_depth + 1, pad_val, pad_char);
                } else {
                    val = _getInnerVal(objVal, thick_pad);
                    str += thick_pad + '[' + key + '] =>\n' + thick_pad + val + '\n';
                }
            }
            str += base_pad + '}\n';
        } else {
            str = _getInnerVal(obj, thick_pad);
        }
        return str;
    };

    output = _formatArray(arguments[0], 0, pad_val, pad_char);
    for (i=1; i < arguments.length; i++) {
        output += '\n' + _formatArray(arguments[i], 0, pad_val, pad_char);
    }

    if (d.body) {
        this.echo(output);
    }
    else {
        try {
            d = XULDocument; // We're in XUL, so appending as plain text won't work
            this.echo('
'+output+'
');
        }
        catch (e) {
            this.echo(output); // Outputting as plain text may work in some plain XML
        }
    }
}<|endoftext|>


<commit_before>var tSquareModule = (function () {

	var theModule = {};
	var resolutions = [{
			name : 'thumbnailSystem',
			width : 260,
			height : 130
		}, {
			name : 'xgaSystem',
			width : 1024,
			height : 768
		}, {
			name : 'wxgaSystem',
			width : 1280,
			height : 800
		}, {
			name : 'hdSystem',
			width : 1366,
			height : 768
		}, {
			name : 'fhdSystem',
			width : 1920,
			height : 1080
		}, {
			name : 'originalSystem'
		}
	],
	resolutionsNo = 6,
	scrollEvents = 0,
	numberOfElemnts = 3,
	resizeWindowElementHeightBc = 0,
	resizeWindowElementWidthBc = 0,
	resizeWindowPageHeight = 0,
	resizeWindowResolutionIndex = -1,
	translationStartTime = null,
	translationFrom = 0,
	translationTo = 0,
	animationSpeed = 0,
	animationDuration = 500,
	animationBusy = false,
	touchStartTime = null,
	touchMinSpeed = 0.9, // Pixels / Millisecond
	touchStartX = 0,
	touchStartY = 0;

	function translate(poz) {
		$("#content_left").css({
			'top' : poz + "px"
		});
		$("#content_right").css({
			'bottom' : (resizeWindowPageHeight + poz) + "px"
		});
	};

	function renderTranslation(timestamp) {
		var pozY = null;
		var progress = 0;

		if (!translationStartTime) {
			translationStartTime = Date.now();
		} else {
			//We want to make sure that pozY is going to reach the translation.translateTo value.
			//Otherwise there is the chance that for the animation to not stop :(.
			progress = Math.min(Date.now() - translationStartTime, animationDuration);

			pozY = translationFrom + (animationSpeed * progress);
			translate(pozY);
		}
		if (pozY != translationTo) {
			window.requestAnimationFrame(renderTranslation);
		} else {
			translationStartTime = null;
			animationBusy = false;
		}
	};

	function scroll(deltaY) {
		if (!animationBusy) {
			var scrollDirection = 0;
			animationBusy = true;
			translationFrom = translationTo;
			if (deltaY < 0) {
				if (scrollEvents == 0) {
					animationBusy = false;
					return;
				}
				scrollDirection = 1;
				scrollEvents--;
			} else {
				if (scrollEvents == numberOfElemnts) {
					animationBusy = false;
					return;
				}
				scrollDirection = -1;
				scrollEvents++;
			}
			translationTo += (resizeWindowElementHeightBc * scrollDirection);
			// Here I'm getting the magnitude and the direction of the speed vector.
			animationSpeed = (translationTo - translationFrom) / animationDuration;

			window.requestAnimationFrame(renderTranslation);

			// Set to he position indicators accordingly.
			$($(".positionIndicator")[scrollEvents + scrollDirection]).removeClass("currentPosition");
			$($(".positionIndicator")[scrollEvents]).addClass("currentPosition");
		}
	};

	function resizeDiv() {
		var tmpResolutionIndex = resizeWindowResolutionIndex;
		var resizeWindowElementHeight = $(window).height();
		var resizeWindowElementWidth = $(window).width();

		var borderThickness = Math.floor((0.015 * resizeWindowElementWidth));
		borderThickness -= borderThickness % 2;

		resizeWindowElementHeightBc = resizeWindowElementHeight - (borderThickness * 2);
		resizeWindowElementWidthBc = resizeWindowElementWidth - (borderThickness * 2);

		resizeWindowPageHeight = numberOfElemnts * resizeWindowElementHeightBc;

		function searchInterval(resolutionIndex) {
			if (resolutions[resolutionIndex].width === resizeWindowElementWidth ||
				resolutionIndex === 0 || resolutionIndex === 5) {
				resizeWindowResolutionIndex = resolutionIndex;
			}
			if (resolutions[resolutionIndex].width > resizeWindowElementWidth) {
				if (resolutions[resolutionIndex - 1].width < resizeWindowElementWidth) {
					resizeWindowResolutionIndex = resolutionIndex;
				} else {
					searchInterval(resolutionIndex - 1);
				}
			}
			if (resolutions[resolutionIndex].width < resizeWindowElementWidth) {
				if (resolutions[resolutionIndex + 1].width > resizeWindowElementWidth) {
					resizeWindowResolutionIndex = resolutionIndex + 1;
				} else {
					searchInterval(resolutionIndex + 1);
				}
			}
		};
		searchInterval(3);
		console.log(resizeWindowResolutionIndex);

		if (tmpResolutionIndex !== resizeWindowResolutionIndex) {
			var imageURL = "pictures/" + resolutions[resizeWindowResolutionIndex].name + "/";
			$(".image").each(function () {
				var imageElement = this;
				// Using the core $.ajax() method
				$.ajax({

					// The URL for the request
					url : imageURL + imageElement.id,

					// Whether this is a POST or GET request
					type : "GET",

					cache : true,

					// Set process data to false
					processData : false,

					// Code to run if the request succeeds;
					// the response is passed to the function
					success : function (response) {
						imageElement.style.backgroundImage = 'url(' + imageURL + imageElement.id + ')';
					},

					// Code to run if the request fails; the raw request and
					// status codes are passed to the function
					error : function (xhr, status, errorThrown) {
						console.log("Error : " + errorThrown);
					}
				});
			});
		};

		$(".resizable").css({
			'height' : resizeWindowElementHeightBc,
			'width' : resizeWindowElementWidthBc
		});
		$(".content").width(resizeWindowElementWidthBc / 2);

		$(".social").css({
			'height' : resizeWindowElementHeightBc / 2,
			'width' : resizeWindowElementWidthBc / 2
		});
		$("#content_right").css({
			'bottom' : resizeWindowPageHeight + "px"
		});
		$(".resizableWindow").css({
			'height' : resizeWindowElementHeight,
			'width' : resizeWindowElementWidthBc
		});
		$(".borderVertical, .positionIndicatorsContainer").css({
			'width' : borderThickness
		});
		$(".borderHorizontal").css({
			'height' : borderThickness
		});
		$(".globalMargin").css({
			'margin' : borderThickness
		});
		$(".border").css({
			'margin-left' : borderThickness,
			'margin-right' : borderThickness
		});
	};

	theModule.init = function () {

		// We first want to resize the div to fit the screen.
		resizeDiv();

		// Setting up the position indicators.
		var positionIndicatorsContainer = $(".positionIndicatorsContainer");
		for (var i = 0; i <= 3; i++) {
			positionIndicatorsContainer.append($("
"));
		}
		$($(".positionIndicator")[0]).addClass("currentPosition");

		// On window resize, we get the new height. Then we calculate and re-position everything that depends on it.
		$(window).resize(function () {
			resizeDiv();
			translationTo = -1 * scrollEvents * resizeWindowElementHeightBc;
			translate(translationTo);
		})
		// We listen for wheel events and update the scene acordingly.
		$(window).bind("wheel", function (event) {
			console.log("wheel");
			scroll(event.originalEvent.deltaY);
			return false;
		})
		// We listen for key events and update the scene accordingly.
		$(window).keydown(function (event) {
			console.log("keydown");
			switch (event.originalEvent.key) {
			case "PageDown":
				scroll(1);
				return false;
			case "ArrowDown":
				scroll(1);
				return false;
			case "ArrowUp":
				scroll(-1);
				return false;
			case "PageUp":
				scroll(-1);
				return false;
			}
		});
		// We listen for touch events and update the scene accordingly.
		$(".resizeable").bind("touchstart", function (event) {
			console.log("touchstart");
			console.log(event.originalEvent.changedTouches.length);
			if (event.originalEvent.changedTouches.length == 1) {
				touchObj = event.originalEvent.changedTouches[0];
				touchStartTime = Date.now();
				touchStartX = touchObj.pageX;
				touchStartY = touchObj.pageY;
			}
		});
		// We listen for touch events and update the scene accordingly.
		$(".resizeable").bind("touchend", function (event) {
			console.log("touchend");
			if (event.originalEvent.changedTouches.length == 1) {
				touchObj = event.originalEvent.changedTouches[0];
				var deltaTouchTime = Date.now() - touchStartTime;
				var deltaTouchX = touchStartX - touchObj.pageX;
				var deltaTouchY = touchStartY - touchObj.pageY;

				if (Math.abs(deltaTouchX) < Math.abs(deltaTouchY)) {
					var touchSpeed = Math.abs(deltaTouchY) / deltaTouchTime;
					if (touchSpeed >= touchMinSpeed)
						scroll(deltaTouchY);
				}
			}
		});

		// We listen for mouse events and update the scene accordingly.
		$(".resizeable").bind("mousedown", function (event) {
			console.log("mousedown");
			touchObj = event.originalEvent;
			touchStartTime = Date.now();
			touchStartX = touchObj.clientX;
			touchStartY = touchObj.clientY;
		});
		// We listen for mouse events and update the scene accordingly.
		$(".resizeable").bind("mouseup", function (event) {
			console.log("mouseup");
			touchObj = event.originalEvent;
			var deltaTouchTime = Date.now() - touchStartTime;
			var deltaTouchX = touchStartX - touchObj.clientX; ;
			var deltaTouchY = touchStartY - touchObj.clientY;

			if (Math.abs(deltaTouchX) < Math.abs(deltaTouchY)) {
				var touchSpeed = Math.abs(deltaTouchY) / deltaTouchTime;
				if (touchSpeed >= touchMinSpeed)
					scroll(deltaTouchY);
			}
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("mousemove", function (event) {
			event.preventDefault();
			console.log("mousemove");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("drag", function (event) {
			console.log("drag");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("touchmove", function (event) {
			event.preventDefault();
			console.log("touchmove");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("scroll", function (event) {
			event.preventDefault();
			console.log("scroll");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("MSPointerMove", function (event) {
			console.log("MSPointerMove");
		});
	};

	function initGallery (galleryItems) {
		var galleryContainer = $("
");
		var itemWidth = resizeWindowElementWidthBc / 4;
		var index;
		var galleryItemsLength = galleryItems.length;
		for (index = 0; index < galleryItemsLength; index++) {
			var galleryItem = galleryItems[index];
			console.log(galleryItem);
			var listItem = $("
");
			var thumbnail = new Image();

			listItem.append(thumbnail);
			thumbnail.src = "gallery/thumbnail/" + galleryItem.filename;
			thumbnail.css({
				'width' : itemWidth * galleryItem.metadata.occupiedWidthCells
			});
			galleryContainer.append(listItem);
		}
		$(".resizableWindow").append(galleryContainer);
	};

	theModule.galleryFilter = function (filterTag) {
		// Using the core $.ajax() method
		$.ajax({

			// The URL for the request
			url : "gallery/list/" + filterTag,

			// Whether this is a POST or GET request
			type : "GET",

			// The type of data we expect back
			dataType : "json",

			cache : true,

			// Set process data to false
			processData : false,

			// Code to run if the request succeeds;
			// the response is passed to the function
			success : function (response) {
				console.log(response);
				initGallery(response);
			},

			// Code to run if the request fails; the raw request and
			// status codes are passed to the function
			error : function (xhr, status, errorThrown) {
				console.log("Error : " + errorThrown);
			}
		});
	};

	return theModule;
}
	());
<commit_msg>Bug Fix 6<commit_after>var tSquareModule = (function () {

	var theModule = {};
	var resolutions = [{
			name : 'thumbnailSystem',
			width : 260,
			height : 130
		}, {
			name : 'xgaSystem',
			width : 1024,
			height : 768
		}, {
			name : 'wxgaSystem',
			width : 1280,
			height : 800
		}, {
			name : 'hdSystem',
			width : 1366,
			height : 768
		}, {
			name : 'fhdSystem',
			width : 1920,
			height : 1080
		}, {
			name : 'originalSystem'
		}
	],
	resolutionsNo = 6,
	scrollEvents = 0,
	numberOfElemnts = 3,
	resizeWindowElementHeightBc = 0,
	resizeWindowElementWidthBc = 0,
	resizeWindowPageHeight = 0,
	resizeWindowResolutionIndex = -1,
	translationStartTime = null,
	translationFrom = 0,
	translationTo = 0,
	animationSpeed = 0,
	animationDuration = 500,
	animationBusy = false,
	touchStartTime = null,
	touchMinSpeed = 0.9, // Pixels / Millisecond
	touchStartX = 0,
	touchStartY = 0;

	function translate(poz) {
		$("#content_left").css({
			'top' : poz + "px"
		});
		$("#content_right").css({
			'bottom' : (resizeWindowPageHeight + poz) + "px"
		});
	};

	function renderTranslation(timestamp) {
		var pozY = null;
		var progress = 0;

		if (!translationStartTime) {
			translationStartTime = Date.now();
		} else {
			//We want to make sure that pozY is going to reach the translation.translateTo value.
			//Otherwise there is the chance that for the animation to not stop :(.
			progress = Math.min(Date.now() - translationStartTime, animationDuration);

			pozY = translationFrom + (animationSpeed * progress);
			translate(pozY);
		}
		if (pozY != translationTo) {
			window.requestAnimationFrame(renderTranslation);
		} else {
			translationStartTime = null;
			animationBusy = false;
		}
	};

	function scroll(deltaY) {
		if (!animationBusy) {
			var scrollDirection = 0;
			animationBusy = true;
			translationFrom = translationTo;
			if (deltaY < 0) {
				if (scrollEvents == 0) {
					animationBusy = false;
					return;
				}
				scrollDirection = 1;
				scrollEvents--;
			} else {
				if (scrollEvents == numberOfElemnts) {
					animationBusy = false;
					return;
				}
				scrollDirection = -1;
				scrollEvents++;
			}
			translationTo += (resizeWindowElementHeightBc * scrollDirection);
			// Here I'm getting the magnitude and the direction of the speed vector.
			animationSpeed = (translationTo - translationFrom) / animationDuration;

			window.requestAnimationFrame(renderTranslation);

			// Set to he position indicators accordingly.
			$($(".positionIndicator")[scrollEvents + scrollDirection]).removeClass("currentPosition");
			$($(".positionIndicator")[scrollEvents]).addClass("currentPosition");
		}
	};

	function resizeDiv() {
		var tmpResolutionIndex = resizeWindowResolutionIndex;
		var resizeWindowElementHeight = $(window).height();
		var resizeWindowElementWidth = $(window).width();

		var borderThickness = Math.floor((0.015 * resizeWindowElementWidth));
		borderThickness -= borderThickness % 2;

		resizeWindowElementHeightBc = resizeWindowElementHeight - (borderThickness * 2);
		resizeWindowElementWidthBc = resizeWindowElementWidth - (borderThickness * 2);

		resizeWindowPageHeight = numberOfElemnts * resizeWindowElementHeightBc;

		function searchInterval(resolutionIndex) {
			if (resolutions[resolutionIndex].width === resizeWindowElementWidth ||
				resolutionIndex === 0 || resolutionIndex === 5) {
				resizeWindowResolutionIndex = resolutionIndex;
			}
			if (resolutions[resolutionIndex].width > resizeWindowElementWidth) {
				if (resolutions[resolutionIndex - 1].width < resizeWindowElementWidth) {
					resizeWindowResolutionIndex = resolutionIndex;
				} else {
					searchInterval(resolutionIndex - 1);
				}
			}
			if (resolutions[resolutionIndex].width < resizeWindowElementWidth) {
				if (resolutions[resolutionIndex + 1].width > resizeWindowElementWidth) {
					resizeWindowResolutionIndex = resolutionIndex + 1;
				} else {
					searchInterval(resolutionIndex + 1);
				}
			}
		};
		searchInterval(3);
		console.log(resizeWindowResolutionIndex);

		if (tmpResolutionIndex !== resizeWindowResolutionIndex) {
			var imageURL = "pictures/" + resolutions[resizeWindowResolutionIndex].name + "/";
			$(".image").each(function () {
				var imageElement = this;
				// Using the core $.ajax() method
				$.ajax({

					// The URL for the request
					url : imageURL + imageElement.id,

					// Whether this is a POST or GET request
					type : "GET",

					cache : true,

					// Set process data to false
					processData : false,

					// Code to run if the request succeeds;
					// the response is passed to the function
					success : function (response) {
						imageElement.style.backgroundImage = 'url(' + imageURL + imageElement.id + ')';
					},

					// Code to run if the request fails; the raw request and
					// status codes are passed to the function
					error : function (xhr, status, errorThrown) {
						console.log("Error : " + errorThrown);
					}
				});
			});
		};

		$(".resizable").css({
			'height' : resizeWindowElementHeightBc,
			'width' : resizeWindowElementWidthBc
		});
		$(".content").width(resizeWindowElementWidthBc / 2);

		$(".social").css({
			'height' : resizeWindowElementHeightBc / 2,
			'width' : resizeWindowElementWidthBc / 2
		});
		$("#content_right").css({
			'bottom' : resizeWindowPageHeight + "px"
		});
		$(".resizableWindow").css({
			'height' : resizeWindowElementHeight,
			'width' : resizeWindowElementWidthBc
		});
		$(".borderVertical, .positionIndicatorsContainer").css({
			'width' : borderThickness
		});
		$(".borderHorizontal").css({
			'height' : borderThickness
		});
		$(".globalMargin").css({
			'margin' : borderThickness
		});
		$(".border").css({
			'margin-left' : borderThickness,
			'margin-right' : borderThickness
		});
	};

	theModule.init = function () {

		// We first want to resize the div to fit the screen.
		resizeDiv();

		// Setting up the position indicators.
		var positionIndicatorsContainer = $(".positionIndicatorsContainer");
		for (var i = 0; i <= 3; i++) {
			positionIndicatorsContainer.append($("
"));
		}
		$($(".positionIndicator")[0]).addClass("currentPosition");

		// On window resize, we get the new height. Then we calculate and re-position everything that depends on it.
		$(window).resize(function () {
			resizeDiv();
			translationTo = -1 * scrollEvents * resizeWindowElementHeightBc;
			translate(translationTo);
		})
		// We listen for wheel events and update the scene acordingly.
		$(window).bind("wheel", function (event) {
			console.log("wheel");
			scroll(event.originalEvent.deltaY);
			return false;
		})
		// We listen for key events and update the scene accordingly.
		$(window).keydown(function (event) {
			console.log("keydown");
			switch (event.originalEvent.key) {
			case "PageDown":
				scroll(1);
				return false;
			case "ArrowDown":
				scroll(1);
				return false;
			case "ArrowUp":
				scroll(-1);
				return false;
			case "PageUp":
				scroll(-1);
				return false;
			}
		});
		// We listen for touch events and update the scene accordingly.
		$(".resizeable").bind("touchstart", function (event) {
			console.log("touchstart");
			console.log(event.originalEvent.changedTouches.length);
			if (event.originalEvent.changedTouches.length == 1) {
				touchObj = event.originalEvent.changedTouches[0];
				touchStartTime = Date.now();
				touchStartX = touchObj.pageX;
				touchStartY = touchObj.pageY;
			}
		});
		// We listen for touch events and update the scene accordingly.
		$(".resizeable").bind("touchend", function (event) {
			console.log("touchend");
			if (event.originalEvent.changedTouches.length == 1) {
				touchObj = event.originalEvent.changedTouches[0];
				var deltaTouchTime = Date.now() - touchStartTime;
				var deltaTouchX = touchStartX - touchObj.pageX;
				var deltaTouchY = touchStartY - touchObj.pageY;

				if (Math.abs(deltaTouchX) < Math.abs(deltaTouchY)) {
					var touchSpeed = Math.abs(deltaTouchY) / deltaTouchTime;
					if (touchSpeed >= touchMinSpeed)
						scroll(deltaTouchY);
				}
			}
		});

		// We listen for mouse events and update the scene accordingly.
		$(".resizeable").bind("mousedown", function (event) {
			console.log("mousedown");
			touchObj = event.originalEvent;
			touchStartTime = Date.now();
			touchStartX = touchObj.clientX;
			touchStartY = touchObj.clientY;
		});
		// We listen for mouse events and update the scene accordingly.
		$(".resizeable").bind("mouseup", function (event) {
			console.log("mouseup");
			touchObj = event.originalEvent;
			var deltaTouchTime = Date.now() - touchStartTime;
			var deltaTouchX = touchStartX - touchObj.clientX; ;
			var deltaTouchY = touchStartY - touchObj.clientY;

			if (Math.abs(deltaTouchX) < Math.abs(deltaTouchY)) {
				var touchSpeed = Math.abs(deltaTouchY) / deltaTouchTime;
				if (touchSpeed >= touchMinSpeed)
					scroll(deltaTouchY);
			}
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("mousemove", function (event) {
			event.preventDefault();
			console.log("mousemove");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("drag", function (event) {
			console.log("drag");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("touchmove", function (event) {
			event.preventDefault();
			console.log("touchmove");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("scroll", function (event) {
			event.preventDefault();
			console.log("scroll");
		});

		// We listen for events for test purposes.
		$(".resizeable").bind("MSPointerMove", function (event) {
			console.log("MSPointerMove");
		});
	};

	function initGallery (galleryItems) {
		var galleryContainer = $("
");
		var itemWidth = resizeWindowElementWidthBc / 4;
		var index;
		var galleryItemsLength = galleryItems.length;
		for (index = 0; index < galleryItemsLength; index++) {
			var galleryItem = galleryItems[index];
			console.log(galleryItem);
			var listItem = $("
");
			var thumbnail = new Image();

			listItem.append(thumbnail);
			thumbnail.src = "gallery/thumbnail/" + galleryItem.filename;
			listItem.css({
				'width' : itemWidth * galleryItem.metadata.occupiedWidthCells
			});
			galleryContainer.append(listItem);
		}
		$(".resizableWindow").append(galleryContainer);
	};

	theModule.galleryFilter = function (filterTag) {
		// Using the core $.ajax() method
		$.ajax({

			// The URL for the request
			url : "gallery/list/" + filterTag,

			// Whether this is a POST or GET request
			type : "GET",

			// The type of data we expect back
			dataType : "json",

			cache : true,

			// Set process data to false
			processData : false,

			// Code to run if the request succeeds;
			// the response is passed to the function
			success : function (response) {
				console.log(response);
				initGallery(response);
			},

			// Code to run if the request fails; the raw request and
			// status codes are passed to the function
			error : function (xhr, status, errorThrown) {
				console.log("Error : " + errorThrown);
			}
		});
	};

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