头条 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"
});
}
}
};
}();