头条小程序 jssdk 代码详细注释 - 压缩变量已重命名

46 阅读6分钟

头条 jssdk 地址: static-002-gtm.msxf.com/market/stat…

/**
 * 头条小程序JSSDK - 提供与原生环境交互的能力
 */
var tt = function() {
    "use strict";
    
    /**
     * 将ArrayBuffer转换为Base64编码字符串
     * @param {ArrayBuffer} buffer - 二进制数据缓冲区
     * @returns {string} Base64编码字符串
     */
    function arrayBufferToBase64(buffer) {
        let result = "";
        const uint8Array = new Uint8Array(buffer);
        const bufferLength = uint8Array.byteLength;
        
        for (let i = 0; i < bufferLength; i++) {
            result += String.fromCharCode(uint8Array[i]);
        }
        
        return base64Encode(result);
    }
    
    /**
     * 将Base64编码字符串转换为ArrayBuffer
     * @param {string} base64String - Base64编码字符串
     * @returns {ArrayBuffer} 二进制数据缓冲区
     */
    function base64ToArrayBuffer(base64String) {
        const decodedString = base64Decode(base64String);
        const stringLength = decodedString.length;
        const uint8Array = new Uint8Array(stringLength);
        
        for (let i = 0; i < stringLength; i++) {
            uint8Array[i] = decodedString.charCodeAt(i);
        }
        
        return uint8Array.buffer;
    }
    
    /**
     * 将ArrayBuffer包装为包含Base64编码的对象
     * @param {ArrayBuffer} buffer - 二进制数据缓冲区
     * @returns {Object} 包含Base64编码的对象
     */
    function wrapBufferAsBase64(buffer) {
        return {
            base64: arrayBufferToBase64(buffer)
        };
    }
    
    /**
     * 从包装对象中提取并转换Base64为ArrayBuffer
     * @param {Object} wrappedObject - 包含Base64属性的对象
     * @returns {ArrayBuffer|undefined} 二进制数据缓冲区或undefined
     */
    function unwrapBase64AsBuffer(wrappedObject) {
        if (wrappedObject !== null) {
            return wrappedObject.base64 
                ? base64ToArrayBuffer(wrappedObject.base64) 
                : undefined;
        }
    }
    
    /**
     * 类型检查函数工厂 - 生成检查特定类型的函数
     * @param {string} typeName - 类型名称
     * @returns {Function} 类型检查函数
     */
    function createTypeChecker(typeName) {
        return function(value) {
            return Object.prototype.toString.call(value).toLowerCase() === 
                ("[object " + typeName + "]").toLowerCase();
        };
    }
    
    /**
     * 处理对象中的ArrayBuffer,将其转换为Base64编码
     * @param {Object} data - 待处理的对象
     * @returns {Object} 处理后的对象
     */
    function processObjectWithBuffers(data) {
        if (!createTypeChecker("Object")(data)) {
            return data;
        }
        
        const bufferList = [];
        
        for (const key in data) {
            const value = data[key];
            
            if (
                value !== undefined && 
                value instanceof ArrayBuffer && 
                value.byteLength !== undefined
            ) {
                const wrappedBuffer = wrapBufferAsBase64(value);
                wrappedBuffer.key = key;
                bufferList.push(wrappedBuffer);
            }
        }
        
        if (bufferList.length > 0) {
            for (let i = 0; i < bufferList.length; i++) {
                delete data[bufferList[i].key];
            }
            
            data.__nativeBuffers__ = bufferList;
        }
        
        return data;
    }
    
    /**
     * 处理包含Base64编码的对象,将其还原为ArrayBuffer
     * @param {Object} data - 待处理的对象
     * @returns {Object} 处理后的对象
     */
    function restoreObjectWithBuffers(data) {
        if (
            !createTypeChecker("Object")(data) || 
            data.__nativeBuffers__ === null
        ) {
            return data;
        }
        
        const bufferList = data.__nativeBuffers__;
        delete data.__nativeBuffers__;
        
        for (let i = 0; i < bufferList.length; i++) {
            const bufferItem = bufferList[i];
            
            if (bufferItem !== null) {
                const buffer = unwrapBase64AsBuffer(bufferItem);
                
                if (
                    buffer !== undefined && 
                    buffer instanceof ArrayBuffer
                ) {
                    data[bufferItem.key] = buffer;
                }
            }
        }
        
        return data;
    }
    
    /**
     * 处理原生回调消息
     * @param {string} message - JSON格式的消息字符串
     * @param {string} callbackId - 回调函数ID
     */
    function handleNativeCallback(message, callbackId) {
        if (message !== undefined && 
            typeof nativeCallbackHandlers[callbackId] === "function" && 
            message !== "" && 
            message !== null
        ) {
            try {
                message = JSON.parse(message);
                message = serializationHelper.unpack(message);
            } catch (error) {
                message = {};
            }
            
            nativeCallbackHandlers[callbackId](message);
            delete nativeCallbackHandlers[callbackId];
        }
    }
    
    /**
     * 调用原生方法
     * @param {string} methodName - 方法名称
     * @param {string} paramsString - 参数JSON字符串
     * @param {string} callbackId - 回调函数ID
     */
    function invokeNativeMethod(methodName, paramsString, callbackId) {
        if (isAppBackground && 
            config.needCacheMethods.find(method => method === methodName)
        ) {
            return pendingNativeCalls.push([methodName, paramsString, callbackId]);
        }
        
        if (nativeCore) {
            nativeCore.invoke(methodName, paramsString, callbackId);
        } else {
            const message = {
                event: methodName,
                paramsString: paramsString,
                callbackId: callbackId
            };
            
            webkitBridge.messageHandlers.invoke.postMessage(message);
        }
    }
    
    /**
     * 发布事件到原生端
     * @param {string} eventName - 事件名称
     * @param {string} paramsString - 参数JSON字符串
     * @param {Array} webviewIds - WebView ID列表
     */
    function publishNativeEvent(eventName, paramsString, webviewIds) {
        if (nativeCore) {
            nativeCore.publish(eventName, paramsString, webviewIds);
        } else {
            webkitBridge.messageHandlers.publish.postMessage({
                event: eventName,
                paramsString: paramsString,
                webviewIds: webviewIds
            });
        }
    }
    
    /**
     * 调用原生API(带回调)
     * @param {string} methodName - 方法名称
     * @param {Object} params - 参数对象
     * @param {Function} callback - 回调函数
     */
    function callNativeApi(methodName, params, callback) {
        params = serializationHelper.pack(params);
        const paramsString = JSON.stringify(params || {});
        const callbackId = ++callbackCounter;
        
        nativeCallbackHandlers[callbackId] = callback;
        
        if (methodName === "openSchema") {
            schemaCallbackIds.push(callbackId);
        }
        
        invokeNativeMethod(methodName, paramsString, callbackId);
    }
    
    /**
     * 处理JS桥接回调
     * @param {string} callbackId - 回调函数ID
     * @param {string|Object} response - 响应数据
     */
    function handleJsBridgeCallback(callbackId, response) {
        if (typeof response === "string") {
            response = JSON.parse(response);
        }
        
        response = serializationHelper.unpack(response);
        const callback = nativeCallbackHandlers[callbackId];
        
        if (typeof callback === "function") {
            callback(response);
        }
        
        if (schemaCallbackIds.indexOf(callbackId) === -1) {
            delete nativeCallbackHandlers[callbackId];
        }
    }
    
    /**
     * 注册事件监听器
     * @param {string} eventName - 事件名称
     * @param {Function} handler - 事件处理函数
     */
    function registerEventListener(eventName, handler) {
        eventListeners[eventName] = handler;
    }
    
    /**
     * 发布自定义事件
     * @param {string} eventName - 事件名称
     * @param {Object} data - 事件数据
     * @param {Array} targetWebviews - 目标WebView列表
     */
    function publishCustomEvent(eventName, data, targetWebviews) {
        targetWebviews = targetWebviews || [];
        targetWebviews = JSON.stringify(targetWebviews);
        
        publishNativeEvent(
            "custom_event_" + eventName, 
            JSON.stringify(data), 
            targetWebviews
        );
    }
    
    /**
     * 订阅自定义事件
     * @param {string} eventName - 事件名称
     * @param {Function} handler - 事件处理函数
     */
    function subscribeCustomEvent(eventName, handler) {
        customEventListeners["custom_event_" + eventName] = handler;
    }
    
    /**
     * 处理原生事件
     * @param {string} eventName - 事件名称
     * @param {string|Object} data - 事件数据
     * @param {*} param1 - 附加参数1
     * @param {*} param2 - 附加参数2
     */
    function handleNativeEvent(eventName, data, param1, param2) {
        // 应用进入后台状态
        if (eventName === AppStateEvents.onAppEnterBackground) {
            isAppBackground = true;
        } 
        // 应用进入前台状态
        else if (eventName === AppStateEvents.onAppEnterForeground) {
            isAppBackground = false;
            
            // 处理挂起的原生调用
            while (pendingNativeCalls.length) {
                invokeNativeMethod.apply(null, pendingNativeCalls.shift());
            }
        }
        
        // 如果不在后台或者事件不需要禁用,则处理事件
        if (
            !isAppBackground || 
            !config.disabledEvents.find(event => event === eventName)
        ) {
            if (typeof data === "string") {
                data = JSON.parse(data);
            }
            
            data = serializationHelper.unpack(data);
            
            const eventHandler = 
                eventName.indexOf("custom_event_") > -1 
                    ? customEventListeners[eventName] 
                    : eventListeners[eventName];
            
            if (typeof eventHandler === "function") {
                eventHandler(data, param1, param2);
            }
        }
    }
    
    /**
     * 空函数,用作默认回调
     */
    function noop() {}
    
    /**
     * 发布消息到事件总线
     */
    function publishToEventBus() {
        const args = Array.prototype.slice.call(arguments);
        args[1] = {
            data: args[1],
            options: {
                timestamp: Date.now()
            }
        };
        
        invokeAsync(function() {
            eventBus.publish.apply(eventBus, args);
        });
    }
    
    /**
     * 调用应用服务方法
     * @param {Object} options - 调用选项
     */
    function callAppServiceMethod(options) {
        const methodName = options.name;
        const methodType = options.type || "sdk";
        const methodArgs = options.args || {};
        const methodExt = options.ext || {};
        
        // 存储回调函数和扩展处理函数
        serviceCallbacks[callbackIdCounter] = {
            success: methodArgs.success || noop,
            fail: methodArgs.fail || noop,
            complete: methodArgs.complete || noop
        };
        
        serviceHandlers[callbackIdCounter] = {
            beforeAll: methodExt.beforeAll || noop,
            beforeSuccess: methodExt.beforeSuccess || noop,
            afterSuccess: methodExt.afterSuccess || noop,
            beforeFail: methodExt.beforeFail || noop,
            afterFail: methodExt.afterFail || noop,
            afterAll: methodExt.afterAll || noop
        };
        
        // 发布消息到事件总线
        publishToEventBus("invokeAppServiceMethod", {
            name: methodName,
            type: methodType,
            args: methodArgs,
            callbackId: callbackIdCounter
        });
        
        callbackIdCounter += 1;
    }
    
    // 获取全局对象
    const globalObject = new Function("return this;")();
    
    // 原生核心接口
    let nativeCore = globalObject.ttJSCore;
    
    // WebKit桥接对象
    let webkitBridge = globalObject.webkit;
    
    // 兼容处理
    if (nativeCore) {
        globalObject.ttJSCore = {
            onDocumentReady: function() {
                return nativeCore.onDocumentReady();
            }
        };
    }
    
    // 检测是否在头条小程序环境中
    const isInToutiaoApp = navigator.userAgent.toLocaleLowerCase().includes("toutiaomicroapp");
    
    // 进一步兼容处理
    if (webkitBridge && isInToutiaoApp) {
        webkitBridge = {
            messageHandlers: {
                onDocumentReady: {
                    postMessage: function() {
                        return webkitBridge.messageHandlers.onDocumentReady.postMessage("");
                    }
                }
            }
        };
    }
    
    // Base64编码表
    const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    
    // Base64编码函数
    const base64Encode = function(str) {
        let output = "";
        let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        let i = 0;
        
        str = String(str);
        
        while (i < str.length) {
            chr1 = str.charCodeAt(i++);
            chr2 = str.charCodeAt(i++);
            chr3 = str.charCodeAt(i++);
            
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            
            output = output +
                base64Chars.charAt(enc1) + base64Chars.charAt(enc2) +
                base64Chars.charAt(enc3) + base64Chars.charAt(enc4);
        }
        
        return output;
    };
    
    // Base64解码函数
    const base64Decode = function(str) {
        str = String(str).replace(/=+$/, "");
        
        if (str.length % 4 === 1) {
            throw new Error('"atob" failed: The string to be decoded is not correctly encoded.');
        }
        
        let output = "";
        let chr1, chr2, chr3;
        let enc1, enc2, enc3, enc4;
        let i = 0;
        
        // 为了兼容,先转换为UTF-8
        str = str.replace(/-/g, "+").replace(/_/g, "/");
        
        while (i < str.length) {
            enc1 = base64Chars.indexOf(str.charAt(i++));
            enc2 = base64Chars.indexOf(str.charAt(i++));
            enc3 = base64Chars.indexOf(str.charAt(i++));
            enc4 = base64Chars.indexOf(str.charAt(i++));
            
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            
            output += String.fromCharCode(chr1);
            
            if (enc3 !== 64) {
                output += String.fromCharCode(chr2);
            }
            
            if (enc4 !== 64) {
                output += String.fromCharCode(chr3);
            }
        }
        
        return output;
    };
    
    // 序列化助手
    const serializationHelper = {
        new: wrapBufferAsBase64,
        get: unwrapBase64AsBuffer,
        pack: processObjectWithBuffers,
        unpack: restoreObjectWithBuffers
    };
    
    // 原生回调处理函数集合
    const nativeCallbackHandlers = {};
    
    // 回调计数器
    let callbackCounter = 0;
    
    // 事件监听器集合
    const eventListeners = {};
    
    // 自定义事件监听器集合
    const customEventListeners = {};
    
    // 挂起的原生调用队列
    const pendingNativeCalls = [];
    
    // 应用是否处于后台状态
    let isAppBackground = false;
    
    // Schema回调ID列表
    const schemaCallbackIds = [];
    
    // 应用状态事件
    const AppStateEvents = {
        onAppEnterBackground: "onAppEnterBackground",
        onAppEnterForeground: "onAppEnterForeground"
    };
    
    // 配置信息
    const config = {
        needCacheMethods: [
            "showModal", 
            "showToast", 
            "showActionSheet", 
            "hideToast"
        ],
        disabledEvents: [
            "onAccelerometerChange", 
            "onCompassChange"
        ]
    };
    
    // 事件总线
    const eventBus = {
        on: registerEventListener,
        publish: publishCustomEvent,
        invoke: callNativeApi,
        subscribe: subscribeCustomEvent
    };
    
    // JS桥接接口
    globalObject.ttJSBridge = {
        get invokeHandler() {
            return handleJsBridgeCallback;
        },
        get subscribeHandler() {
            return handleNativeEvent;
        }
    };
    
    // 回调ID计数器
    let callbackIdCounter = 0;
    
    // 服务回调集合
    const serviceCallbacks = [];
    
    // 服务处理函数集合
    const serviceHandlers = [];
    
    // 异步执行函数
    const invokeAsync = function(callback) {
        callback();
    };
    
    // 订阅应用服务方法回调
    subscribeCustomEvent("callbackAppServiceMethod", function(eventData) {
        const response = eventData.res;
        const isSuccess = eventData.isSuccess;
        const callbackId = eventData.callbackId;
        
        const callback = serviceCallbacks[callbackId];
        const handler = serviceHandlers[callbackId];
        
        // 执行前置处理
        handler.beforeAll(response);
        
        if (isSuccess) {
            handler.beforeSuccess(response);
            callback.success(response);
            handler.afterSuccess(response);
        } else {
            handler.beforeFail(response);
            callback.fail(response);
            handler.afterFail(response);
        }
        
        callback.complete(response);
        handler.afterAll(response);
    });
    
    // 返回小程序API
    return {
        miniProgram: {
            /**
             * 重定向到指定页面
             * @param {Object} options - 重定向选项
             */
            redirectTo: function(options) {
                callAppServiceMethod({
                    name: "redirectTo",
                    args: options,
                    type: "jssdk"
                });
            },
            
            /**
             * 导航到新页面
             * @param {Object} options - 导航选项
             */
            navigateTo: function(options) {
                callAppServiceMethod({
                    name: "navigateTo",
                    args: options,
                    type: "jssdk"
                });
            },
            
            /**
             * 切换到指定标签页
             * @param {Object} options - 标签页选项
             */
            switchTab: function(options) {
                callAppServiceMethod({
                    name: "switchTab",
                    args: options,
                    type: "jssdk"
                });
            },
            
            /**
             * 重新启动应用并跳转到指定页面
             * @param {Object} options - 重启选项
             */
            reLaunch: function(options) {
                callAppServiceMethod({
                    name: "reLaunch",
                    args: options,
                    type: "jssdk"
                });
            },
            
            /**
             * 返回上一页
             * @param {Object} options - 返回选项
             */
            navigateBack: function(options) {
                callAppServiceMethod({
                    name: "navigateBack",
                    args: options,
                    type: "jssdk"
                });
            },
            
            /**
             * 向小程序web-view组件发送消息
             * @param {Object} options - 消息选项
             */
            postMessage: function(options) {
                if (options) {
                    callAppServiceMethod({
                        name: "postMessage",
                        args: options,
                        type: "jssdk"
                    });
                }
            },
            
            /**
             * 同步设置滑动返回模式
             * @param {number} mode - 滑动返回模式(0-禁用,1-启用)
             */
            setSwipeBackModeSync: function(mode) {
                callAppServiceMethod({
                    name: "setSwipeBackMode",
                    args: {
                        mode: arguments.length > 0 && mode !== undefined ? mode : 0
                    },
                    type: "jssdk"
                });
            }
        }
    };
}();