if (typeof cave !== "undefined") {
    window.addEventListener("error", handleError, true);

    function handleError(evt) {
        if (evt.message) { // Chrome sometimes provides this
            alert("error: " + evt.message + " at linenumber: " + evt.lineno + " of file: " + evt.filename);
        } else {
            if((evt.srcElement || evt.target).toString().indexOf('HTMLImageElement') !== -1)
                return;
            alert("error: " + evt.type + " from element: " + (evt.srcElement || evt.target));
        }
    }
}
if (typeof cave === "undefined") {
    window.cave = {
        /**
         * Enables or disabled the back button on the toolbar
         * @param enable
         */
        toolbar_enableBackBtnFunc: function (enable) {
            console.log('cave.toolbar_enableBackBtnFunc(enable = ' + enable + ')');
        },
        /**
         * Sets the callback function for a given toolbar button
         * @param btnType
         * @param callback
         */
        toolbar_setCallback: function (btnType, callback) {
            console.log('cave.toolbar_setCallback(btnType = ' + btnType + ', callback = ' + typeof callback + ')');
        },
        /**
         * Toggles the visibility of the toolbar
         * @param visibility
         */
        toolbar_setVisible: function (visibility) {
            console.log('cave.toolbar_setVisible(visibility = ' + visibility + ')');
        },
        /**
         * (untested) Appears to set the "corner button" type.
         * @param type
         */
        toolbar_setButtonType: function (type) {
            console.log('cave.toolbar_setButtonType(type = ' + type + ')');
        },
        /**
         * Highlight a button - usually done in a button's own callback function to show that it has been activated.
         * @param btnType
         */
        toolbar_setActiveButton: function (btnType) {
            console.log('cave.toolbar_setActiveButton(btnType = ' + btnType + ')');
        },
        /**
         * Open the drawing tool interface.
         */
        memo_open: function () {
            console.log('cave.memo_open()');
        },
        /**
         * Clear the current drawing.
         */
        memo_clear: function () {
            console.log('cave.memo_clear()');
        },
        /**
         * Unknown
         * @returns {boolean}
         */
        memo_hasValidImage: function () {
            console.log('cave.memo_hasValidImage()');
            return true;
        },
        /**
         * Returns the drawing as base64 encoded BMP image.
         * @returns {string}
         */
        memo_getImageBmp: function () {
            console.log('cave.memo_getImageBmp()');
            return ' ';
        },
        /**
         * (untested) Returns the drawing as base64 encoded TGA image.
         * @returns {string}
         */
        memo_getImageRawTga: function () {
            console.log('cave.memo_getImageRawTga()');
            return 'Qk1CAAAAAAAAAD4AAAAoAAAAAQAAAAEAAAABAAEAAAAAAAQAAADEDgAAxA4AAAAAAAAAAAAAAAAAAP///wCAAAAAAAAA';
        },
        /**
         * Set the current notification count
         * @param count
         */
        toolbar_setNotificationCount: function (count) {
            console.log('cave.toolbar_setNotificationCount(' + count + ')');
        },
        /**
         * Calls system error viewer with specified error code
         * @param errorCode
         */
        error_callErrorViewer: function (errorCode) {
            var fakeMessage = 'Message for error code ' + errorCode;
            alert(errorCode + '\n\n' + fakeMessage);
        },
        /**
         * Calls system error viewer with specified error code and message
         * @param errorCode
         */
        error_callFreeErrorViewer: function (errorCode, message) {
            alert(errorCode + '\n\n' + message);
        },
        /**
         * Begins transition to new page
         */
        transition_begin: function () {
            console.log('cave.transition_begin()');
        },
        /**
         * Ends transition to new page
         */
        transition_end: function () {
            console.log('cave.transition_end()');
        },
        /**
         * Begins transition to new page without grey screen
         */
        transition_beginWithoutEffect: function () {
            console.log('cave.transition_beginWithoutEffect()');
        },
        /**
         * Ends transition to new page without grey screen
         */
        transition_endWithoutEffect: function () {
            console.log('cave.transition_endWithoutEffect()');
        },
        /**
         * Session storage of key value pair
         * @param key
         * @param value
         * @returns {undefined}
         */
        ls_setItem: function (key, value) {
            console.log('cave.ls_setItem()');
            // string でないと実機でエラー出るので挙動を合わせる
            // If it is not string, an error will occur on the actual machine, so match the behavior
            for (var i = 0; i < 2; i++) {
                if (typeof arguments[i] !== 'string') {
                    console.error('Argument ' + i + ' should be string');
                    console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.');
                    return undefined;
                }
            }
            sessionStorage.setItem(key, value);
        },
        /**
         * Fetches value for key from session storage
         * @param key
         * @returns {undefined|string}
         */
        ls_getItem: function (key) {
            console.log('cave.ls_getItem()');
            if (typeof key !== 'string') {
                console.error('Argument 0 should be string');
                console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.');
                return undefined;
            }
            return sessionStorage.getItem(key);
        },
        /**
         * Removes value for key from session storage
         * @param key
         */
        ls_removeItem: function (key) {
            console.log('cave.ls_removeItem()');
            sessionStorage.removeItem(key);
        },
        /**
         * ゲスト初回起動済みフラグをセットする (Set guest first started flag)
         * Puts the applet into guest mode
         * @param bool
         */
        ls_setGuestModeLaunched: function (bool) {
            console.log('cave.ls_setGuestModeLaunched( ' + bool + ' )');
        },
        /**
         * Sets value for specified key in local storage
         * @param key
         * @param value
         */
        lls_setItem: function (key, value) {
            console.log('cave.lls_setItem()');
            localStorage.setItem(key, value);
        },
        /**
         * Fetches the number of values in local storage
         * @returns {number}
         */
        lls_getCount: function () {
            console.log('cave.lls_getCount()');
            return localStorage.length;
        },
        /**
         * Fetches value for key from local storage
         * @param key
         * @returns {string}
         */
        lls_getItem: function (key) {
            console.log('cave.lls_getItem()');
            return localStorage.getItem(key);
        },
        /**
         * Removes value for key from local storage
         * @param key
         */
        lls_removeItem: function (key) {
            console.log('cave.lls_removeItem()');
            return localStorage.removeItem(key);
        },
        /**
         * Returns if key 'custk' has value '1'
         * @returns {boolean}
         */
        ls_canUseCachedServiceToken: function () {
            console.log('cave.ls_canUseCachedServiceToken()');
            return sessionStorage.getItem('custk') === '1' ? true : false;
        },
        /**
         * Sets 'custk' key to specified value
         * @param can_use
         */
        ls_setCanUseCachedServiceToken: function (can_use) {
            console.log('cave.ls_setCanUseCachedServiceToken');
            var flag = can_use ? '1' : '0';
            sessionStorage.setItem('custk', flag);
        },
        /**
         * Shows a dialog box with one button
         * @param title
         * @param message
         * @param button_text
         * @returns {number}
         */
        dialog_oneButton: function (title, message, button_text) {
            console.log('cave.dialog_oneButton()');
            alert("タイトル:" + title + "\n" + "メッセージ:" + message + "\n\n[ " + button_text + " ]");
            return 0;
        },
        /**
         * Returns key at specified index
         * @param index
         * @returns {string}
         */
        lls_getKeyAt: function (index) {
            console.log('cave.lls_getKeyAt index = ' + index);
            return localStorage.key(index);
        },
        /**
         * Returns B64 encoded user Mii image with requested expression
         * @param expression {number} 0-5
         * @returns {string}
         */
        mii_getIconBase64: function (expression) {
            console.log('cave.mii_getIconBase64 expression = ' + expression);
            // 本来表情毎に出し分けるべきだが、とりあえず固定のものを出す
            // Originally, it should be displayed separately for each expression, but for the time being, a fixed one should be displayed
            return 'data:image/png;base64, ';
        },
        /**
         * Returns name of user Mii
         * @returns {string}
         */
        mii_getName: function () {
            console.log('cave.mii_getName');
            return 'chris4403';
        },
        /**
         * Shows a dialog box with two buttons
         * @param title
         * @param message
         * @param buttonL_text
         * @param buttonR_text
         * @returns {number}
         */
        dialog_twoButton: function (title, message, buttonL_text, buttonR_text) {
            console.log('cave.dialog_twoButton()');
            var result = confirm("タイトル:" + title + "\n" + "メッセージ:" + message + "\n\n[ " + buttonL_text + " (Cancel) ] [ " + buttonR_text + " (OK) ]");
            return result ? 1 : 0;
        },

        brw_scrollImmediately: function (posX, posY) {
            console.log('cave.brw_scrollImmediately(posX=' + posX + ', posY=' + posY + ')');
            window.scroll(posX, posY);
        },

        brw_getScrollTopX: function () {
            console.log('cave.brw_getScrollTopX()');
            return window.pageXOffset;
        },

        brw_getScrollTopY: function () {
            console.log('cave.brw_getScrollTopY()');
            return window.pageYOffset;
        },

        brw_notifyPageMoving: function () {
            console.log('cave.brw_notifyPageMoving()');
        },

        requestGc: function () {
            console.log('requestGc');
        },
        /**
         * Exits the app
         */
        exitApp: function () {
            console.log('cave.exitApp()');
        },
        /**
         * Returns a JSON string, containing app IDs
         * @param minutes
         * @returns {string}
         */
        plog_getPlayTitlesFilteredByPlayTime: function (minutes) {
            console.log('cave.plog_getPlayTitlesFilteredByPlayTime(' + minutes + ')');
            return '{"IDs":["0004001000020700"]}'; // Miiスタジオ (Mii studio)
        },
        /**
         * Jumps to user account settings
         */
        jump_toAccount: function () {
            console.log('cave.jump_toAccount()');
        },
        /**
         * play (predefined) sound effect
         * @param label
         */
        /* Values:
        SE_OLV_CANCEL
        SE_OLV_OK
        SE_OLV_OK_SUB
        SE_OLV_CHECKBOX_CHECK
        SE_OLV_CHECKBOX_UNCHECK
        SE_OLV_MII_ADD
        SE_OLV_RELOAD
        SE_OLV_BALLOON_OPEN
        SE_OLV_BALLOON_CLOSE
        SE_WAVE_SCROLL_PAGE
        SE_WAVE_SCROLL_PAGE_LR
        SE_WAVE_SCROLL_LIMIT_LR
        SE_CTR_COMMON_TOUCH
        SE_CTR_COMMON_TOUCHOUT
        SE_CTR_COMMON_TOUCHOUT_S
        SE_CTR_COMMON_TOUCHIN
        SE_CTR_COMMON_TOGGLE
        SE_CTR_COMMON_SILENT
        SE_CTR_COMMON_BUTTON
        SE_CTR_COMMON_OK
        SE_CTR_COMMON_RETURN
        SE_CTR_COMMON_CANCEL
        SE_CTR_COMMON_WAIT
        SE_CTR_COMMON_WAIT_END
        SE_CTR_COMMON_CONNECT
        SE_CTR_COMMON_ERROR
        SE_CTR_COMMON_NOTICE
        SE_CTR_COMMON_NOMOVE
        SE_CTR_COMMON_SCROLL
        SE_CTR_COMMON_SCROLL_LIST
        SE_CTR_COMMON_SCROLL_TEXT
        SE_CTR_COMMON_SCROLL_END
        SE_CTR_COMMON_DIALOG
        SE_CTR_COMMON_SYSAPPLET_END
        SE_CTR_SPIDER_HG_Prev
        SE_CTR_SPIDER_HG_Next
        SE_CTR_SPIDER_MV_START
        SE_CTR_SPIDER_LINK
        SE_CTR_SPIDER_YOMIKOMI
        SE_CTR_SPIDER_YOMIEND
        SE_CTR_SPIDER_YomiCancel
        SE_CTR_SPIDER_MV_KAKTEI
        SE_CTR_SPIDER_MV_LINE
        SE_CTR_SPIDER_MV_CURSOR
        SE_CTR_SPIDER_FavCursor
        SE_CTR_SPIDER_Navi
        SE_CTR_SPIDER_Navi_On
        SE_CTR_SPIDER_Navi_Off
        SE_CTR_SPIDER_ZOOM2BIG
        SE_CTR_SPIDER_ZOOM2SMALL
        SE_CTR_SPIDER_LINK_CLICK
        SE_CTR_SPIDER_BTN_CLICK
        SE_CTR_SPIDER_Ticker
        SE_CTR_SPIDER_SEL_START
        SE_CTR_SPIDER_SEL_CURSOR
        SE_CTR_SPIDER_InfoOn
        SE_CTR_SPIDER_SEL_END
         */
        snd_playSe: function (label) {
            console.log('cave.snd_playSe(' + label + ')');
        },
        /**
         * Play (predefined) background music
         * @param label
         */
        /* Values:
        BGM_ACCOUNT_OP
        BGM_CAVE_MAIN
        BGM_CAVE_MAIN_LOOP
        BGM_CAVE_MAIN_LOOP_NOWAIT
        BGM_CAVE_WORLD_MAP_MINT
        BGM_CAVE_WORLD_MAP
        BGM_CAVE_MAIN_OFFLINE
        BGM_CAVE_SETTING
        BGM_CAVE_SYOKAI
        BGM_CAVE_SYOKAI2
         */
        snd_playBgm: function (label) {
            console.log('cave.snd_playBgm(' + label + ')');
        },
        /**
         * Checks if suspended application is running
         * @returns {boolean}
         */
        sap_exists: function () {
            console.log('cave.sap_exists()');
            return false;
        },
        /**
         * Fetches the program ID for the suspended application
         * @returns {undefined}
         */
        sap_programId: function () {
            console.log('cave.sap_programId()');
            return undefined;
        },
        /**
         * Captures the lower screen of the suspended app. Returns as a base64 encoded image (todo: clarify format).
         */
        capture_getLowerImage: function () {
            console.log('cave.capture_getLowerImage()');
        },
        /**
         * Captures the left-eye image from the upper screen of the suspended app. Returns as a base64 encoded image (todo: clarify format).
         */
        capture_getUpperImageLeft: function () {
            console.log('cave.capture_getUpperImageLeft()');
        },
        /**
         * Captures the right-eye image from the upper screen of the suspended app. Returns as a base64 encoded image (todo: clarify format)
         */
        capture_getUpperImageRight: function () {
            console.log('cave.capture_getUpperImageRight()');
        },
        /**
         * (untested) Presumably captures both left and right images from the top screen.
         */
        capture_getUpperImage3D: function () {
            console.log('cave.capture_getUpperImage3D()');
        },
        /**
         * More advanced version of capture_getUpperImage3D that allows for customisation.
         * @param width
         * @param height
         * @param quality
         * @param format
         */
        capture_getLowerImageDetail: function (width, height, quality, format) {
            console.log('cave.capture_getLowerImageDetail(' +
                [width, height, quality, format].join(', ')
                + ')');
        },
        /**
         * More advanced version of capture_getUpperImageLeft that allows for customisation.
         * @param width
         * @param height
         * @param quality
         * @param format
         */
        capture_getUpperImageLeftDetail: function (width, height, quality, format) {
            console.log('cave.capture_getUpperImageLeftDetail(' +
                [width, height, quality, format].join(', ')
                + ')');
        },
        /**
         * More advanced version of capture_getUpperImageRight that allows for customisation.
         * @param width
         * @param height
         * @param quality
         * @param format
         */
        capture_getUpperImageRightDetail: function (width, height, quality, format) {
            console.log('cave.capture_getUpperImageRightDetail(' +
                [width, height, quality, format].join(', ')
                + ')');
        },
        /**
         * More advanced version of capture_getUpperImage3D that allows for customisation.
         * @param width
         * @param height
         * @param quality
         * @param format
         */
        capture_getUpperImage3DDetail: function (width, height, quality, format) {
            console.log('cave.capture_getUpperImage3DDetail(' +
                [width, height, quality, format].join(', ')
                + ')');
        },
        /**
         * Unknown
         */
        capture_notifyUpdatedLocalList: function () {
            console.log('cave.capture_notifyUpdatedLocalList()');
        },
        /**
         * Check if the suspended software allows screenshots.
         * @returns {boolean}
         */
        capture_isEnabled: function () {
            console.log('cave.capture_isEnabled()');
            return false;
        },
        /**
         * Same as capture_isEnabled, but instead checks the capture status of a single screen. Set screenIndex to 0 to check the top screen, or 1 to check the lower screen.
         * @param screen
         * @returns {boolean|boolean}
         */
        capture_isEnabledEx: function (screen) {
            console.log('cave.capture_isEnabledEx(' + screen + ')');

            // テスト等でスクショの有無を固定したい場合にクッキーで指定できるようにする, 実機ではこの挙動は存在しない
            // If you want to fix the presence or absence of screenshots in tests, etc., make it possible to specify with cookies, this behavior does not exist on the actual device
            var force = Olv.Cookie.get('force_screenshot_for_test');
            if (force === 'true') return true;
            if (force === 'false') return false;

            return Math.random() >= 0.5 ? true : false;
        },

        boss_regist: function (interval_hour) {
            console.log('cave.boss_regist(' + interval_hour + ')');
            return true;
        },
        boss_registEx: function (interval_hour, count) {
            console.log('cave.boss_regist(' + interval_hour + ',' + count + ')');
            return true;
        },

        boss_unregist: function () {
            console.log('cave.boss_unregist()');
        },

        boss_isRegisted: function () {
            console.log('cave.boss_isRegisted()');
            return true;
        },

        boss_clearNewArrival: function () {
            console.log('cave.boss_clearNewArrival()');
        },
        lls_clear: function () {
            console.log('cave.lls_clear()');
            localStorage.clear();
        },
        lls_setCaptureImage: function (key, value) {
            console.log('cave.setCaptureImage(' + [key, value].join(', ') + ')');
            localStorage.setItem(key, value);
            return 0;
        },
        lls_getPath: function (key) {
            console.log('cave.getPath(' + key + ')');
            var value = localStorage.getItem(key);
            if (value == 0) {
                return '/img/dummy-image/screenshot-dummy-3ds-low.jpeg';
            } else {
                return '/img/dummy-image/screenshot-dummy-3ds-upper.jpeg';
            }
        },
        /**
         * Fetches history from previoys
         * @returns {string}
         */
        history_getPrev: function () {
            console.log('cave.history_getPrev()');
            // 直前の履歴項目の URL を返す API だが、
            // とりあえずは referrer で代用しておく。
            // An API that returns the URL of the previous history item, but
            // Substitute with referrer for the time being.
            return document.referrer;
        },
        /**
         * Removes previous entry from history
         */
        history_removePrev: function () {
            console.log('cave.history_removePrev()');
        },
        /**
         * Fetches URL at provided index
         * @param backIndex
         * @returns {string}
         */
        history_getAt: function (backIndex) {
            console.log('cave.history_getAt(' + backIndex + ')');
            return '';
        },
        /**
         * Removes history at provided index
         * @param backIndex
         */
        history_removeAt: function (backIndex) {
            console.log('cave.history_removeAt(' + backIndex + ')');
        },
        /**
         * Determines if browser can navigate back
         * @returns {number}
         */
        history_getBackCount: function () {
            console.log('cave.history_getBackCount()');
            // 戻ることのできるページ数を返すAPIだが、
            // とりあえずは referrer で代用しておく。
            // API that returns the number of pages that can be returned,
            // Substitute with referrer for the time being.
            return !!document.referrer ? 1 : 0;
        },

        toolbar_setMode: function (mode) {
            console.log('cave.toolbar_setMode(' + mode + ')');
        },
        toolbar_setWideButtonMessage: function (message) {
            console.log('cave.toolbar_setWideButtonMessage(' + message + ')')
        },
        jump_existsWebbrs: function () {
            console.log('jump_existsWebbrs()');
            return true;
        },
        jump_toSystemUpdate: function (type) {
            console.log('jump_toSystemUpdate(' + type + ')')
        },
        jump_toWebbrs: function (url) {
            console.log('cave.jump_toWebbrs(' + url + ')');
            window.location.href = url;
            return true;
        },
        jump_toShop: function (dialog, titleId) {
            console.log('cave.jump_toShop(' + dialog + ', ' + titleId + ')');
            return true;
        },
        jump_getYoutubeVersion: function () {
            console.log('cave.jump_getYoutubeVersion()');
            return -1;
        },
        jump_suspendedToutube: function (query) {
            console.log('cave.jump_suspendedToutube(' + query + ')');
            return 1;
        },
        jump_toYoutube: function (dialog, query) {
            console.log('cave.jump_toYoutube(' + dialog + ', ' + query + ')');
            return 1;
        },
        jump_existsApplication: function (titleId) {
            console.log('jump_existsApplication(' + titleId + ')');
            return true;
        },
        jump_resetParamToApp: function () {
            console.log('jump_resetParamToApp');
        },
        jump_canUseQuery: function (titleId) {
            console.log('jump_canUseQuery(' + titleId + ')');
            return true;
        },
        jump_setModeToApp: function (mode) {
            console.log('jump_setModeToApp(' + mode + ')');
        },
        jump_setDataUTF8ToApp: function (type, data) {
            console.log('jump_setDataUTF8ToApp(' + type + ', ' + data + ')');
        },
        jump_setNumberDataToApp: function (type, data) {
            console.log('jump_setNumberDataToApp(' + type + ', ' + data + ')');
        },
        jump_setBase64DataToApp: function (type, data) {
            console.log('jump_setBase64DataToApp(' + type + ', ' + data + ')');
        },
        jump_toApplication: function (dialog, titleId) {
            console.log('cave.jump_toApplication(' + dialog + ', ' + titleId + ')');
            return true;
        },
        convertTimeToString: function (unixTime) {
            console.log('cave.convertTimeToString(' + unixTime + ')');
            var date = new Date(unixTime * 1000);
            return date.getFullYear() + '-' + this._toDoubleDigits(date.getMonth() + 1) + '-' + this._toDoubleDigits(date.getDate())
                + ' ' + this._toDoubleDigits(date.getHours()) + ':' + this._toDoubleDigits(date.getMinutes()) + ':' + this._toDoubleDigits(date.getSeconds());
        },
        _toDoubleDigits: function (num) {
            num += "";
            if (num.length === 1) {
                num = "0" + num;
            }
            return num;
        },
        getLocalTimeSeconds: function () {
            console.log('cave.getLocalTimeSeconds()');
            return parseInt(new Date().getTime() / 1000);
        },
        effect_scrollGuide: function (flag) {
            console.log('cave.effect_scrollGuide( ' + flag + ' )');
        },
        effect_setScrollGuideOffsetPos: function (x, y) {
            console.log('effect_setScrollGuideOffsetPos( x = ' + x + ', y = ' + ' )');
        },
        // キーボードアプレットに入力した文字列が返り値になるので window.prompt でエミュレートする
        // Since the string entered in the keyboard applet will be the return value, emulate it with window.prompt
        swkbd_callFullKeyboard: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible) {
            console.log('cave.callFullKeyboard( ' + Array.prototype.join.call(arguments, ', ') + ' )');
            return window.prompt('cave.callFullKeyboard', text);
        },
        swkbd_callFullKeyboardWithGuide: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible, guide) {
            console.log('cave.callFullKeyboardWithGuide( ' + Array.prototype.join.call(arguments, ', ') + ' )');
            return window.prompt('cave.callFullKeyboardWithGuide [' + guide + ']', text);
        },
        viewer_setOnCloseCallback: function (callback) {
            console.log('cave.viewer_setOnCloseCallback(callback = ' + typeof callback + ')');
        },
        home_setEnabled: function (flag) {
            console.log('cave.home_setEnabled', flag);
        },
    };
}