{{JS_PREFIX}}_load_messages = function (messages, set_func) {
    const requestOptions = { method: 'GET' };
    const find = Array.isArray(messages) ? messages.join(",") : 'prefix:' + messages;
    fetch('/__bubble/api/filter/messages/{{BUBBLE_REQUEST_ID}}/{{BUBBLE_APP_NAME}}/{{ACCOUNT_LOCALE}}/find/'+find, requestOptions)
        .then(response => response.json())
        .then(appMessages => {
            const map = {};
            for (let i=0; i<appMessages.messages.length; i++) {
                const m = appMessages.messages[i];
                map[m.name] = m.value;
            }
            set_func(map);
        })
        .catch((error) => {
            console.error('Error getting messages ('+messages+'): '+error);
        });
}

{{JS_PREFIX}}_link_message_url = function (link) {
    return '/__bubble/api/filter/messages/{{BUBBLE_REQUEST_ID}}/{{BUBBLE_APP_NAME}}/{{ACCOUNT_LOCALE}}/link/'+link+'_url';
}

{{JS_PREFIX}}_asset_img_url = function (icon) {
    return '/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/{{BUBBLE_APP_NAME}}/'+icon+'?raw=true';
}

{{JS_PREFIX}}_load_link_message = function (link, set_func) {
    const requestOptions = { method: 'GET' };
    fetch({{JS_PREFIX}}_link_message_url(link), requestOptions)
        .then(response => response.text())
        .then(data => set_func(data))
        .catch((error) => {
            console.error('Error getting link message ('+link+'): '+error);
        });
}

{{JS_PREFIX}}_app_entry_div = function () {
    const entry = document.createElement('div');
    entry.style.all = 'revert';
    entry.style.fontSize = 'small';
    return entry;
}

{{JS_PREFIX}}_app_header_element = function () {
    const header = document.createElement('strong');
    header.style.all = 'revert';
    header.style.fontSize = 'medium';
    return header;
}

{{JS_PREFIX}}_app_header = function (title, defaultTitle, formatFunc) {
    const labelText = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, title, defaultTitle);
    const label = {{JS_PREFIX}}_app_header_element();
    label.appendChild(document.createTextNode(formatFunc ? formatFunc(labelText) : labelText));
    return label;
}

{{JS_PREFIX}}_app_title_span = function (defaultName) {
    const appTitleSpan = document.createElement('span');
    appTitleSpan.style.fontSize = 'large';
    const appImage = document.createElement('img');
    appImage.src = {{JS_PREFIX}}_asset_img_url('icon');
    appImage.style.all = 'revert';
    appImage.style.width = '16px';
    appImage.width = 16;
    const appTitle = document.createElement('strong');
    const appName = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, 'web_appName', defaultName);
    appTitle.appendChild(document.createTextNode(appName));
    appTitleSpan.appendChild(appImage);
    appTitleSpan.appendChild(appTitle);
    return appTitleSpan;
}

function {{JS_PREFIX}}_create_button_element() {
    const btn = document.createElement('button');
    btn.style.all = 'revert';
    btn.style.color = 'black';
    btn.style.fontSize = 'x-small';
    return btn;
}

function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelFormat) {
    const label = {{PAGE_PREFIX}}_msg_or_default({{JS_PREFIX}}_messages, labelKey, labelDefault);
    const btn = {{JS_PREFIX}}_create_button_element();
    btn.addEventListener('click', onclick);
    btn.appendChild(document.createTextNode(labelFormat ? labelFormat(label) : label));
    return btn;
}

{{JS_PREFIX}}_follow_url = '/__bubble/api/filter/follow/{{BUBBLE_REQUEST_ID}}';
{{JS_PREFIX}}_follow_and_apply_regex_url = '/__bubble/api/filter/followAndApplyRegex/{{BUBBLE_REQUEST_ID}}';

{{JS_PREFIX}}_url_chasers = {};

if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') {

    {{PAGE_PREFIX}}_screenWidth = function () { return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth };

    let {{PAGE_PREFIX}}_doc_ready = false;

    let {{PAGE_PREFIX}}_icon_status = [];

    {{PAGE_PREFIX}}_msg_or_default = function (messages, msg, def) {
        return msg && msg in messages ? messages[msg] : def;
    }

    {{PAGE_PREFIX}}_get_fqdn_tld = function () {
        const host = window.location.hostname;
        const fqdnParts = host.split('.');
        switch (fqdnParts.length) {
            case 0: return host;
            case 1: return host;
            default: return fqdnParts[fqdnParts.length-2] + '.' + fqdnParts[fqdnParts.length-1];
        }
    }

    function {{PAGE_PREFIX}}_remove_param(href, param) {
        const cid = href.indexOf(param+'=');
        if (cid !== -1) {
            const regex = new RegExp('&?'+param+'=[^?&]+', 'g');
            href = href.replace(regex, '');
            const qPos = href.indexOf('?');
            if (href[qPos+1] === '&') {
                href = href.substring(0, qPos) + href.substring(qPos+2);
            }
        }
        return href;
    }

    {{PAGE_PREFIX}}_log = function (data) {
        const logData = JSON.stringify(data);
        const requestOptions = {
            method: 'POST',
            body: logData
        };
        // console.log('Logging to server: '+logData);
        fetch('/__bubble/api/filter/logs/{{BUBBLE_REQUEST_ID}}', requestOptions)
            .then(() => {
                // console.log('Logged to server: '+logData);
            })
            .catch((error) => {
                console.error('Error logging "'+logData+'" to server: '+error);
            });
    }

    {{PAGE_PREFIX}}_createDetailsDiv = function (id) {
        const detailsDiv = document.createElement('div');
        detailsDiv.id = id;
        detailsDiv.style.backgroundColor = '#ffffff';
        detailsDiv.style.position = 'fixed';
        detailsDiv.style.bottom = '10px';
        detailsDiv.style.right = '5px';
        detailsDiv.style.maxHeight = '80%';
        detailsDiv.style.maxWidth = '80%';
        detailsDiv.style.overflowX = 'scroll';
        detailsDiv.style.overflowY = 'scroll';
        detailsDiv.style.border = '1px solid black';
        detailsDiv.style.padding = '6px'
        detailsDiv.style.zIndex = '{{expr APP_CONTROLS_Z_INDEX '+' 1}}';
        return detailsDiv;
    }

    {{PAGE_PREFIX}}_addBubbleApp = function (app) {
        if (window.self === window.top) {
            if ({{PAGE_PREFIX}}_icon_status.find(a => a.app === app.app)) {
                {{PAGE_PREFIX}}_log('addBubbleApp: NOT adding app (already added): '+app.app);
            } else {
                {{PAGE_PREFIX}}_log('addBubbleApp: adding app: '+app.app);
                {{PAGE_PREFIX}}_icon_status.push(app);
            }
        }
    }

    {{PAGE_PREFIX}}_getAppIconImgSrc = function (app) {
        return '/__bubble/api/filter/assets/{{BUBBLE_REQUEST_ID}}/' + app.app + '/' + app.icon + '?raw=true';
    }

    {{PAGE_PREFIX}}_getAppIconImgId = function (app) {
        return app.jsPrefix + '_app_icon_img';
    }

    {{PAGE_PREFIX}}_setAppIconImg = function (app) {
        const imgId = {{PAGE_PREFIX}}_getAppIconImgId(app);
        const img = document.getElementById(imgId);
        if (img) {
            img.src = {{PAGE_PREFIX}}_getAppIconImgSrc(app);
        } else {
            console.warn('setAppIconImg: img element not found: '+imgId)
        }
    }

    function {{PAGE_PREFIX}}_onReady(callback) {
        const intervalId = window.setInterval(function() {
            if (document.getElementsByTagName('body')[0] !== undefined) {
                {{PAGE_PREFIX}}_doc_ready = true;
                window.clearInterval(intervalId);
                callback.call(this);
            }
        }, {{PAGE_ONREADY_INTERVAL}});
    }

    {{PAGE_PREFIX}}_onReady(function() {
        const controlDivId = '{{PAGE_PREFIX}}_controlDiv';
        let bubbleControlDiv = document.getElementById(controlDivId);
        if (bubbleControlDiv === null) {
            bubbleControlDiv = document.createElement('div');
            bubbleControlDiv.id = controlDivId;
            bubbleControlDiv.style.position = 'fixed';
            bubbleControlDiv.style.bottom = '0';
            bubbleControlDiv.style.right = '0';
            bubbleControlDiv.style.zIndex = '{{APP_CONTROLS_Z_INDEX}}';
            document.getElementsByTagName('body')[0].appendChild(bubbleControlDiv);
        }
        for (let i=0; i<{{PAGE_PREFIX}}_icon_status.length; i++) {
            const iconSpecs = {{PAGE_PREFIX}}_icon_status[i];
            const br = document.createElement('br');
            const link = document.createElement('a');
            if (typeof iconSpecs.link === 'function') {
                link.onclick = function (ev) { iconSpecs.link(ev); return false; }
            } else {
                link.href = '{{{BUBBLE_HOME}}}/app/' + iconSpecs.app + '/' + iconSpecs.link;
            }
            const screenWidth  = {{PAGE_PREFIX}}_screenWidth();
            const img = document.createElement('img');
            img.id = {{PAGE_PREFIX}}_getAppIconImgId(iconSpecs);
            img.src = {{PAGE_PREFIX}}_getAppIconImgSrc(iconSpecs);
            img.width = Math.min(128, screenWidth/8);
            link.appendChild(img);
            bubbleControlDiv.appendChild(br);
            bubbleControlDiv.appendChild(link);
            if (typeof iconSpecs.onReady === 'function') {
                iconSpecs.onReady();
            }
        }
    });
}

function {{JS_PREFIX}}_chase_redirects (a, removeParams, regex, groups, callback) {
    const initial_href = a.href;
    if (initial_href in {{JS_PREFIX}}_url_chasers) {
        a.href = {{JS_PREFIX}}_url_chasers[initial_href];
        return;
    }
    if (a.className && a.className.indexOf('{{JS_PREFIX}}_followed') !== -1) return;
    if (a.className) {
        a.className = a.className + ' {{JS_PREFIX}}_followed';
    } else {
        a.className = '{{JS_PREFIX}}_followed';
    }

    a.rel = 'noopener noreferrer nofollow';

    let is_regex = (typeof regex !== 'undefined');
    const follow_body = !is_regex ? initial_href :
        {
            'url': initial_href,
            'regex': regex,
            'groups': (typeof groups === 'undefined' || groups === null ? null : groups)
        };
    const request_opts = {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(follow_body)
    }
    const follow_url = is_regex ? {{JS_PREFIX}}_follow_and_apply_regex_url : {{JS_PREFIX}}_follow_url;
    fetch(follow_url, request_opts)
        .then(response => is_regex ? response.json() : response.text())
        .then(data => {
            if (is_regex) {
                callback(data);
            } else {
                if (data && (data.startsWith('http://') || data.startsWith('https://'))) {
                    if (typeof removeParams === 'undefined' || removeParams === null || removeParams) {
                        const qPos = data.indexOf('?');
                        a.href = qPos === -1 ? data : data.substring(0, qPos);
                    } else {
                        a.href = data;
                    }
                    {{JS_PREFIX}}_url_chasers[initial_href] = a.href;
                    // console.log('chase_redirect: rewrote '+initial_href+' -> '+a.href);
                } else {
                    console.warn('chase_redirects: ' + a.href + ' returned non-URL response: ' + data);
                }
            }
        })
        .catch((error) => {
            console.error('chase_redirects: error following: '+a.href+': '+error);
        });
}
