From f3a56d622a070bb933dcfeb62674ed3c926c52f4 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Wed, 14 Jul 2021 20:31:55 +0800 Subject: [PATCH] wip(app): uni-app-plus --- .../uni-app-plus/dist/uni-app-service.es.js | 19539 +++++++++++----- .../uni-app-plus/dist/uni-app-view.umd.js | 8 +- .../uni-app-plus/src/service/api/index.ts | 49 +- .../service/api/ui/requestComponentInfo.ts | 7 +- .../src/service/framework/page/setup.ts | 1 + packages/uni-app-plus/src/view/index.ts | 2 + packages/uni-h5/dist/uni-h5.es.js | 5 +- .../service/api/ui/requestComponentInfo.ts | 9 +- 8 files changed, 14094 insertions(+), 5526 deletions(-) diff --git a/packages/uni-app-plus/dist/uni-app-service.es.js b/packages/uni-app-plus/dist/uni-app-service.es.js index b350323b1..76794bb8c 100644 --- a/packages/uni-app-plus/dist/uni-app-service.es.js +++ b/packages/uni-app-plus/dist/uni-app-service.es.js @@ -110,6 +110,9 @@ var serviceContext = (function (vue) { const isFunction = (val) => typeof val === 'function'; const isString = (val) => typeof val === 'string'; const isObject$1 = (val) => val !== null && typeof val === 'object'; + const isPromise = (val) => { + return isObject$1(val) && isFunction(val.then) && isFunction(val.catch); + }; const objectToString = Object.prototype.toString; const toTypeString = (value) => objectToString.call(value); const toRawType = (value) => { @@ -611,6 +614,17 @@ var serviceContext = (function (vue) { }; } + function requestComponentInfo(page, reqs, callback) { + UniServiceJSBridge.invokeViewMethod('requestComponentInfo', { + reqs: reqs.map((req) => { + if (req.component) { + req.component = req.component.$el.nodeId; + } + return req; + }), + }, callback, page.$page.id); + } + function formatLog(module, ...args) { return `[${Date.now()}][${module}]:${args .map((arg) => JSON.stringify(arg)) @@ -1598,6 +1612,15 @@ var serviceContext = (function (vue) { }); } + function getPageIdByVm(vm) { + if (!vm.$) { + return; + } + const rootProxy = vm.$.root.proxy; + if (rootProxy && rootProxy.$page) { + return rootProxy.$page.id; + } + } function getPageById(id) { return getCurrentPages().find((page) => page.$page.id === id); } @@ -1614,6 +1637,19 @@ var serviceContext = (function (vue) { return pages[len - 1]; } } + function getCurrentPageMeta() { + const page = getCurrentPage(); + if (page) { + return page.$page.meta; + } + } + function getCurrentPageId() { + const meta = getCurrentPageMeta(); + if (meta) { + return meta.id; + } + return -1; + } function getCurrentPageVm() { const page = getCurrentPage(); if (page) { @@ -1722,6 +1758,35 @@ var serviceContext = (function (vue) { } } + const callbacks$2 = {}; + function createCallbacks(namespace) { + let scopedCallbacks = callbacks$2[namespace]; + if (!scopedCallbacks) { + scopedCallbacks = { + id: 1, + callbacks: Object.create(null), + }; + callbacks$2[namespace] = scopedCallbacks; + } + return { + get(id) { + return scopedCallbacks.callbacks[id]; + }, + pop(id) { + const callback = scopedCallbacks.callbacks[id]; + if (callback) { + delete scopedCallbacks.callbacks[id]; + } + return callback; + }, + push(callback) { + const id = scopedCallbacks.id++; + scopedCallbacks.callbacks[id] = callback; + return id; + }, + }; + } + const ServiceJSBridge = /*#__PURE__*/ extend(initBridge('view' /* view 指的是 service 层订阅的是 view 层事件 */), { invokeOnCallback(name, res) { return UniServiceJSBridge.emit('api.' + name, res); @@ -1779,13 +1844,13 @@ var serviceContext = (function (vue) { } return []; } - function createSelectorQuery() { + function createSelectorQuery$1() { return uni.createSelectorQuery().in(this); } - function createMediaQueryObserver() { + function createMediaQueryObserver$1() { return uni.createMediaQueryObserver(this); } - function createIntersectionObserver(options) { + function createIntersectionObserver$1(options) { return uni.createIntersectionObserver(this, options); } function selectComponent(selector) { @@ -1797,9 +1862,9 @@ var serviceContext = (function (vue) { var wxInstance = /*#__PURE__*/Object.freeze({ __proto__: null, - createSelectorQuery: createSelectorQuery, - createMediaQueryObserver: createMediaQueryObserver, - createIntersectionObserver: createIntersectionObserver, + createSelectorQuery: createSelectorQuery$1, + createMediaQueryObserver: createMediaQueryObserver$1, + createIntersectionObserver: createIntersectionObserver$1, selectComponent: selectComponent, selectAllComponents: selectAllComponents }); @@ -1869,2873 +1934,4196 @@ var serviceContext = (function (vue) { return false; } - const API_CREATE_INNER_AUDIO_CONTEXT = 'createInnerAudioContext'; + function operateVideoPlayer(videoId, pageId, type, data) { } + function operateMap(id, pageId, type, data) { } + function addIntersectionObserver(args, pageId) { } + function removeIntersectionObserver(args, pageId) { } + function addMediaQueryObserver(args, pageId) { } + function removeMediaQueryObserver(args, pageId) { } + const TEMP_PATH$1 = ''; - //#endregion - /** - * 可以批量设置的监听事件 - */ - const innerAudioContextEventNames = [ - 'onCanplay', - 'onPlay', - 'onPause', - 'onStop', - 'onEnded', - 'onTimeUpdate', - 'onError', - 'onWaiting', - 'onSeeking', - 'onSeeked', - ]; - const innerAudioContextOffEventNames = [ - 'offCanplay', - 'offPlay', - 'offPause', - 'offStop', - 'offEnded', - 'offTimeUpdate', - 'offError', - 'offWaiting', - 'offSeeking', - 'offSeeked', + const API_UPX2PX = 'upx2px'; + const Upx2pxProtocol = [ + { + name: 'upx', + type: [Number, String], + required: true, + }, ]; - const API_GET_BACKGROUND_AUDIO_MANAGER = 'getBackgroundAudioManager'; - - const API_MAKE_PHONE_CALL = 'makePhoneCall'; - const MakePhoneCallProtocol = { - phoneNumber: String, - }; - - const API_ADD_PHONE_CONTACT = 'addPhoneContact'; - - const API_GET_CLIPBOARD_DATA = 'getClipboardData'; - const API_SET_CLIPBOARD_DATA = 'setClipboardData'; - - const API_ON_ACCELEROMETER = 'onAccelerometer'; - const API_OFF_ACCELEROMETER = 'offAccelerometer'; - const API_START_ACCELEROMETER = 'startAccelerometer'; - const API_STOP_ACCELEROMETER = 'stopAccelerometer'; - - const API_ON_COMPASS = 'onCompass'; - const API_OFF_COMPASS = 'offCompass'; - const API_START_COMPASS = 'startCompass'; - const API_STOP_COMPASS = 'stopCompass'; + const EPS = 1e-4; + const BASE_DEVICE_WIDTH = 750; + let isIOS$1 = false; + let deviceWidth = 0; + let deviceDPR = 0; + function checkDeviceWidth() { + const { platform, pixelRatio, windowWidth } = getBaseSystemInfo(); + deviceWidth = windowWidth; + deviceDPR = pixelRatio; + isIOS$1 = platform === 'ios'; + } + function checkValue(value, defaultValue) { + const newValue = Number(value); + return isNaN(newValue) ? defaultValue : newValue; + } + const upx2px = defineSyncApi(API_UPX2PX, (number, newDeviceWidth) => { + if (deviceWidth === 0) { + checkDeviceWidth(); + } + number = Number(number); + if (number === 0) { + return 0; + } + let width = newDeviceWidth || deviceWidth; + { + const config = __uniConfig.globalStyle || {}; + // ignore rpxCalcIncludeWidth + const maxWidth = checkValue(config.rpxCalcMaxDeviceWidth, 960); + const baseWidth = checkValue(config.rpxCalcBaseDeviceWidth, 375); + width = width <= maxWidth ? width : baseWidth; + } + let result = (number / BASE_DEVICE_WIDTH) * width; + if (result < 0) { + result = -result; + } + result = Math.floor(result + EPS); + if (result === 0) { + if (deviceDPR === 1 || !isIOS$1) { + result = 1; + } + else { + result = 0.5; + } + } + return number < 0 ? -result : result; + }, Upx2pxProtocol); - const API_VIBRATE_SHORT = 'vibrateShort'; - const API_VIBRATE_LONG = 'vibrateLong'; + const globalInterceptors = {}; + const scopedInterceptors = {}; - const API_ON_BLUETOOTH_DEVICE_FOUND = 'onBluetoothDeviceFound'; - const API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE = 'onBluetoothAdapterStateChange'; - const API_ON_BLE_CONNECTION_STATE_CHANGE = 'onBLEConnectionStateChange'; - const API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE = 'onBLECharacteristicValueChange'; - const API_START_BLUETOOTH_DEVICES_DISCOVERY = 'startBluetoothDevicesDiscovery'; - const StartBluetoothDevicesDiscoveryProtocol = { - services: Array, - allowDuplicatesKey: Boolean, - interval: Number, - }; - const API_GET_CONNECTED_BLUETOOTH_DEVICES = 'getConnectedBluetoothDevices'; - const GetConnectedBluetoothDevicesProtocol = { - services: { - type: Array, + const API_ADD_INTERCEPTOR = 'addInterceptor'; + const API_REMOVE_INTERCEPTOR = 'removeInterceptor'; + const AddInterceptorProtocol = [ + { + name: 'method', + type: [String, Object], required: true, }, - }; - const API_CREATE_BLE_CONNECTION = 'createBLEConnection'; - const CreateBLEConnectionProtocol = { - deviceId: { - type: String, - required: true, + ]; + const RemoveInterceptorProtocol = AddInterceptorProtocol; + + function mergeInterceptorHook(interceptors, interceptor) { + Object.keys(interceptor).forEach((hook) => { + if (isFunction(interceptor[hook])) { + interceptors[hook] = mergeHook(interceptors[hook], interceptor[hook]); + } + }); + } + function removeInterceptorHook(interceptors, interceptor) { + if (!interceptors || !interceptor) { + return; + } + Object.keys(interceptor).forEach((hook) => { + if (isFunction(interceptor[hook])) { + removeHook(interceptors[hook], interceptor[hook]); + } + }); + } + function mergeHook(parentVal, childVal) { + const res = childVal + ? parentVal + ? parentVal.concat(childVal) + : isArray(childVal) + ? childVal + : [childVal] + : parentVal; + return res ? dedupeHooks(res) : res; + } + function dedupeHooks(hooks) { + const res = []; + for (let i = 0; i < hooks.length; i++) { + if (res.indexOf(hooks[i]) === -1) { + res.push(hooks[i]); + } + } + return res; + } + function removeHook(hooks, hook) { + if (!hooks) { + return; + } + const index = hooks.indexOf(hook); + if (index !== -1) { + hooks.splice(index, 1); + } + } + const addInterceptor = defineSyncApi(API_ADD_INTERCEPTOR, (method, interceptor) => { + if (typeof method === 'string' && isPlainObject(interceptor)) { + mergeInterceptorHook(scopedInterceptors[method] || (scopedInterceptors[method] = {}), interceptor); + } + else if (isPlainObject(method)) { + mergeInterceptorHook(globalInterceptors, method); + } + }, AddInterceptorProtocol); + const removeInterceptor = defineSyncApi(API_REMOVE_INTERCEPTOR, (method, interceptor) => { + if (typeof method === 'string') { + if (isPlainObject(interceptor)) { + removeInterceptorHook(scopedInterceptors[method], interceptor); + } + else { + delete scopedInterceptors[method]; + } + } + else if (isPlainObject(method)) { + removeInterceptorHook(globalInterceptors, method); + } + }, RemoveInterceptorProtocol); + const promiseInterceptor = { + returnValue(res) { + if (!isPromise(res)) { + return res; + } + return res + .then((res) => { + return res[1]; + }) + .catch((res) => { + return res[0]; + }); }, - }; - const API_CLOSE_BLE_CONNECTION = 'closeBLEConnection'; - const CloseBLEConnectionProtocol = { - deviceId: { + }; + + const API_ON = '$on'; + const OnProtocol = [ + { + name: 'event', type: String, required: true, }, - }; - const API_GET_BLE_DEVICE_SERVICES = 'getBLEDeviceServices'; - const GetBLEDeviceServicesProtocol = { - deviceId: { - type: String, + { + name: 'callback', + type: Function, required: true, }, - }; - const API_GET_BLE_DEVICE_CHARACTERISTICS = 'getBLEDeviceCharacteristics'; - const GetBLEDeviceCharacteristicsProtocol = { - deviceId: { - type: String, - required: true, + ]; + const API_ONCE = '$once'; + const OnceProtocol = OnProtocol; + const API_OFF = '$off'; + const OffProtocol = [ + { + name: 'event', + type: [String, Array], }, - serviceId: { - type: String, - required: true, + { + name: 'callback', + type: Function, }, - }; - const API_NOTIFY_BLE_CHARACTERISTIC_VALUE_CHANGE = 'notifyBLECharacteristicValueChange'; - const NotifyBLECharacteristicValueChangeProtocol = { - deviceId: { + ]; + const API_EMIT = '$emit'; + const EmitProtocol = [ + { + name: 'event', type: String, required: true, }, - serviceId: { + ]; + + const emitter = new E(); + const $on = defineSyncApi(API_ON, (name, callback) => { + emitter.on(name, callback); + return () => emitter.off(name, callback); + }, OnProtocol); + const $once = defineSyncApi(API_ONCE, (name, callback) => { + emitter.once(name, callback); + return () => emitter.off(name, callback); + }, OnceProtocol); + const $off = defineSyncApi(API_OFF, (name, callback) => { + if (!name) { + emitter.e = {}; + return; + } + if (!Array.isArray(name)) + name = [name]; + name.forEach((n) => emitter.off(n, callback)); + }, OffProtocol); + const $emit = defineSyncApi(API_EMIT, (name, ...args) => { + emitter.emit(name, ...args); + }, EmitProtocol); + + const validator = [ + { + name: 'id', type: String, required: true, }, - characteristicId: { + ]; + /* export const API_CREATE_AUDIO_CONTEXT = 'createAudioContext' + export type API_TYPE_CREATE_AUDIO_CONTEXT = typeof uni.createAudioContext + export const CreateAudioContextProtocol = validator */ + const API_CREATE_VIDEO_CONTEXT = 'createVideoContext'; + const API_CREATE_MAP_CONTEXT = 'createMapContext'; + const CreateMapContextProtocol = validator; + const API_CREATE_CANVAS_CONTEXT = 'createCanvasContext'; + const CreateCanvasContextProtocol = [ + { + name: 'canvasId', type: String, required: true, }, - state: { - type: Boolean, - required: true, + { + name: 'componentInstance', + type: Object, }, - }; - const API_READ_BLE_CHARACTERISTIC_VALUE = 'readBLECharacteristicValue'; - const ReadBLECharacteristicValueProtocol = { - deviceId: { - type: String, - required: true, + ]; + const API_CREATE_INNER_AUDIO_CONTEXT = 'createInnerAudioContext'; + + class VideoContext { + constructor(id, pageId) { + this.id = id; + this.pageId = pageId; + } + play() { + operateVideoPlayer(this.id, this.pageId); + } + pause() { + operateVideoPlayer(this.id, this.pageId); + } + stop() { + operateVideoPlayer(this.id, this.pageId); + } + seek(position) { + operateVideoPlayer(this.id, this.pageId); + } + sendDanmu(args) { + operateVideoPlayer(this.id, this.pageId); + } + playbackRate(rate) { + operateVideoPlayer(this.id, this.pageId); + } + requestFullScreen(args = {}) { + operateVideoPlayer(this.id, this.pageId); + } + exitFullScreen() { + operateVideoPlayer(this.id, this.pageId); + } + showStatusBar() { + operateVideoPlayer(this.id, this.pageId); + } + hideStatusBar() { + operateVideoPlayer(this.id, this.pageId); + } + } + const createVideoContext = defineSyncApi(API_CREATE_VIDEO_CONTEXT, (id, context) => { + if (context) { + return new VideoContext(id, getPageIdByVm(context)); + } + return new VideoContext(id, getPageIdByVm(getCurrentPageVm())); + }); + + class MapContext { + constructor(id, pageId) { + this.id = id; + this.pageId = pageId; + } + getCenterLocation(options) { + operateMap(this.id, this.pageId); + } + moveToLocation() { + operateMap(this.id, this.pageId); + } + getScale(options) { + operateMap(this.id, this.pageId); + } + getRegion(options) { + operateMap(this.id, this.pageId); + } + includePoints(options) { + operateMap(this.id, this.pageId); + } + translateMarker(options) { + operateMap(this.id, this.pageId); + } + addCustomLayer() { } + removeCustomLayer() { } + addGroundOverlay() { } + removeGroundOverlay() { } + updateGroundOverlay() { } + initMarkerCluster() { } + addMarkers() { } + removeMarkers() { } + moveAlong() { } + openMapAp() { } + $getAppMap() { } + } + const createMapContext = defineSyncApi(API_CREATE_MAP_CONTEXT, (id, context) => { + if (context) { + return new MapContext(id, getPageIdByVm(context)); + } + return new MapContext(id, getPageIdByVm(getCurrentPageVm())); + }, CreateMapContextProtocol); + + function getInt(name, defaultValue) { + return function (value, params) { + if (value) { + params[name] = Math.round(value); + } + else if (typeof defaultValue !== 'undefined') { + params[name] = defaultValue; + } + }; + } + const formatWidth = getInt('width'); + const formatHeight = getInt('height'); + //#region getImageDataOptions + const API_CANVAS_GET_IMAGE_DATA = 'canvasGetImageData'; + const CanvasGetImageDataOptions = { + formatArgs: { + x: getInt('x'), + y: getInt('y'), + width: formatWidth, + height: formatHeight, }, - serviceId: { + }; + const CanvasGetImageDataProtocol = { + canvasId: { type: String, required: true, }, - characteristicId: { - type: String, + x: { + type: Number, required: true, }, - }; - const API_WRITE_BLE_CHARACTERISTIC_VALUE = 'writeBLECharacteristicValue'; - const WriteBLECharacteristicValueProtocol = { - deviceId: { - type: String, + y: { + type: Number, required: true, }, - serviceId: { - type: String, + width: { + type: Number, required: true, }, - characteristicId: { - type: String, - required: true, - }, - value: { - type: Array, + height: { + type: Number, required: true, }, }; - const API_SET_BLE_MTU = 'setBLEMTU'; - const SetBLEMTUProtocol = { - deviceId: { - type: String, + //#endregion + //#region putImageData + const API_CANVAS_PUT_IMAGE_DATA = 'canvasPutImageData'; + const CanvasPutImageDataOptions = CanvasGetImageDataOptions; + const CanvasPutImageDataProtocol = + /*#__PURE__*/ extend({ + data: { + type: Uint8ClampedArray, required: true, }, - mtu: { + }, CanvasGetImageDataProtocol, { + height: { type: Number, - required: true, - }, - }; - const API_GET_BLE_DEVICE_RSSI = 'getBLEDeviceRSSI'; - const GetBLEDeviceRSSIProtocol = { - deviceId: { - type: String, - required: true, - }, - }; - - const API_ON_BEACON_UPDATE = 'onBeaconUpdate'; - const API_ON_BEACON_SERVICE_CHANGE = 'onBeaconServiceChange'; - const API_GET_BEACONS = 'getBeacons'; - const API_START_BEACON_DISCOVERY = 'startBeaconDiscovery'; - const StartBeaconDiscoveryProtocol = { - uuids: { - type: Array, - required: true, }, + }); + //#endregion + //#region toTempFilePath + const fileTypes = { + PNG: 'png', + JPG: 'jpg', + JPEG: 'jpg', }; - const API_STOP_BEACON_DISCOVERY = 'stopBeaconDiscovery'; - - const API_CHECK_IS_SUPPORT_SOTER_AUTHENTICATION = 'soterAuthentication'; - const API_CHECK_IS_SOTER_ENROLLED_IN_DEVICE = 'checkIsSoterEnrolledInDevice'; - const CheckAuthModes = [ - 'fingerPrint', - 'facial', - 'speech', - ]; - const CheckIsSoterEnrolledInDeviceOptions = { + const API_CANVAS_TO_TEMP_FILE_PATH = 'canvasToTempFilePath'; + const CanvasToTempFilePathOptions = { formatArgs: { - checkAuthMode(value, params) { - if (!value || !CheckAuthModes.includes(value)) - return 'checkAuthMode 填写错误'; + x: getInt('x', 0), + y: getInt('y', 0), + width: formatWidth, + height: formatHeight, + destWidth: getInt('destWidth'), + destHeight: getInt('destHeight'), + fileType(value, params) { + value = (value || '').toUpperCase(); + let type = fileTypes[value]; + if (!type) { + type = fileTypes.PNG; + } + params.fileType = type; }, - }, - }; - const CheckIsSoterEnrolledInDeviceProtocols = { - checkAuthMode: String, - }; - const API_START_SOTER_AUTHENTICATION = 'checkIsSoterEnrolledInDevice'; - const StartSoterAuthenticationOptions = { - formatArgs: { - requestAuthModes(value, params) { - if (!value.includes('fingerPrint') && !value.includes('facial')) - return 'requestAuthModes 填写错误'; + quality(value, params) { + params.quality = value && value > 0 && value < 1 ? value : 1; }, }, }; - const StartSoterAuthenticationProtocols = { - requestAuthModes: { - type: Array, + const CanvasToTempFilePathProtocol = { + x: Number, + y: Number, + width: Number, + height: Number, + destWidth: Number, + destHeight: Number, + canvasId: { + type: String, required: true, }, - challenge: String, - authContent: String, + fileType: String, + quality: Number, }; - const API_GET_STORAGE = 'getStorage'; - const GetStorageProtocol = { - key: { - type: String, - required: true, - }, - }; - const API_GET_STORAGE_SYNC = 'getStorageSync'; - const GetStorageSyncProtocol = [ - { - name: 'key', - type: String, - required: true, - }, + //#region import + // import pako from 'pako' + //#endregion + //#region UniServiceJSBridge + const canvasEventCallbacks = createCallbacks('canvasEvent'); + const onCanvasMethodCallback = /*#__PURE__*/ once(() => { + UniServiceJSBridge.subscribe('onCanvasMethodCallback', ({ callbackId, data }) => { + const callback = canvasEventCallbacks.pop(callbackId); + if (callback) { + callback(data); + } + }); + }); + function operateCanvas(canvasId, pageId, type, data) { + UniServiceJSBridge.publishHandler('canvas.' + canvasId, { + canvasId, + type, + data, + }, pageId); + } + //#endregion + //#region methods + var methods1 = ['scale', 'rotate', 'translate', 'setTransform', 'transform']; + var methods2 = [ + 'drawImage', + 'fillText', + 'fill', + 'stroke', + 'fillRect', + 'strokeRect', + 'clearRect', + 'strokeText', ]; - const API_SET_STORAGE = 'setStorage'; - const SetStorageProtocol = { - key: { - type: String, - required: true, - }, - data: { - required: true, - }, - }; - const API_SET_STORAGE_SYNC = 'setStorageSync'; - const SetStorageSyncProtocol = [ - { - name: 'key', - type: String, - required: true, - }, - { - name: 'data', - required: true, - }, + var methods3 = [ + 'setFillStyle', + 'setTextAlign', + 'setStrokeStyle', + 'setGlobalAlpha', + 'setShadow', + 'setFontSize', + 'setLineCap', + 'setLineJoin', + 'setLineWidth', + 'setMiterLimit', + 'setTextBaseline', + 'setLineDash', ]; - const API_REMOVE_STORAGE = 'removeStorage'; - const RemoveStorageProtocol = GetStorageProtocol; - const RemoveStorageSyncProtocol = GetStorageSyncProtocol; - - const API_GET_FILE_INFO = 'getFileInfo'; - const GetFileInfoOptions = { - formatArgs: { - filePath(filePath, params) { - params.filePath = getRealPath(filePath); - }, - }, - }; - const GetFileInfoProtocol = { - filePath: { - type: String, - required: true, - }, - }; - - const API_OPEN_DOCUMENT = 'openDocument'; - const OpenDocumentOptions = { - formatArgs: { - filePath(filePath, params) { - params.filePath = getRealPath(filePath); - }, - }, + function measureText(text, font) { + const canvas = document.createElement('canvas'); + const c2d = canvas.getContext('2d'); + c2d.font = font; + return c2d.measureText(text).width || 0; + } + //#endregion + //#region checkColor + const predefinedColor = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#00ffff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000000', + blanchedalmond: '#ffebcd', + blue: '#0000ff', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#00ffff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgrey: '#a9a9a9', + darkgreen: '#006400', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#ff00ff', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + grey: '#808080', + green: '#008000', + greenyellow: '#adff2f', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgrey: '#d3d3d3', + lightgreen: '#90ee90', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#00ff00', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + rebeccapurple: '#663399', + red: '#ff0000', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#ffffff', + whitesmoke: '#f5f5f5', + yellow: '#ffff00', + yellowgreen: '#9acd32', + transparent: '#00000000', }; - const OpenDocumentProtocol = { - filePath: { - type: String, - required: true, - }, - fileType: String, - }; - - const API_HIDE_KEYBOARD = 'hideKeyboard'; - const API_SHOW_KEYBOARD = 'showKeyboard'; - - const API_GET_LOCATION = 'getLocation'; - const coordTypes = ['WGS84', 'GCJ02']; - const GetLocationOptions = { - formatArgs: { - type(value, params) { - value = (value || '').toUpperCase(); - if (coordTypes.indexOf(value) === -1) { - params.type = coordTypes[0]; + function checkColor(e) { + // 其他开发者适配的echarts会传入一个undefined到这里 + e = e || '#000000'; + var t = null; + if ((t = /^#([0-9|A-F|a-f]{6})$/.exec(e)) != null) { + const n = parseInt(t[1].slice(0, 2), 16); + const o = parseInt(t[1].slice(2, 4), 16); + const r = parseInt(t[1].slice(4), 16); + return [n, o, r, 255]; + } + if ((t = /^#([0-9|A-F|a-f]{3})$/.exec(e)) != null) { + let n = t[1].slice(0, 1); + let o = t[1].slice(1, 2); + let r = t[1].slice(2, 3); + n = parseInt(n + n, 16); + o = parseInt(o + o, 16); + r = parseInt(r + r, 16); + return [n, o, r, 255]; + } + if ((t = /^rgb\((.+)\)$/.exec(e)) != null) { + return t[1] + .split(',') + .map(function (e) { + return Math.min(255, parseInt(e.trim())); + }) + .concat(255); + } + if ((t = /^rgba\((.+)\)$/.exec(e)) != null) { + return t[1].split(',').map(function (e, t) { + return t === 3 + ? Math.floor(255 * parseFloat(e.trim())) + : Math.min(255, parseInt(e.trim())); + }); + } + var i = e.toLowerCase(); + if (hasOwn$1(predefinedColor, i)) { + t = /^#([0-9|A-F|a-f]{6,8})$/.exec(predefinedColor[i]); + const n = parseInt(t[1].slice(0, 2), 16); + const o = parseInt(t[1].slice(2, 4), 16); + const r = parseInt(t[1].slice(4, 6), 16); + let a = parseInt(t[1].slice(6, 8), 16); + a = a >= 0 ? a : 255; + return [n, o, r, a]; + } + console.error('unsupported color:' + e); + return [0, 0, 0, 255]; + } + //#endregion + //#region Class + class CanvasGradient { + constructor(type, data) { + this.type = type; + this.data = data; + this.colorStop = []; + } + addColorStop(position, color) { + this.colorStop.push([position, checkColor(color)]); + } + } + class Pattern { + constructor(image, repetition) { + this.image = image; + this.repetition = repetition; + } + } + class TextMetrics { + constructor(width) { + this.width = width; + } + } + class CanvasContext { + constructor(id, pageId) { + this.id = id; + this.pageId = pageId; + this.actions = []; + this.path = []; + this.subpath = []; + // this.currentTransform = [] + // this.currentStepAnimates = [] + this.drawingState = []; + this.state = { + lineDash: [0, 0], + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowBlur: 0, + shadowColor: [0, 0, 0, 0], + font: '10px sans-serif', + fontSize: 10, + fontWeight: 'normal', + fontStyle: 'normal', + fontFamily: 'sans-serif', + }; + } + draw(reserve = false, callback) { + var actions = [...this.actions]; + this.actions = []; + this.path = []; + var callbackId; + if (typeof callback === 'function') { + callbackId = canvasEventCallbacks.push(callback); + } + operateCanvas(this.id, this.pageId, 'actionsChanged', { + actions, + reserve, + callbackId, + }); + } + createLinearGradient(x0, y0, x1, y1) { + return new CanvasGradient('linear', [x0, y0, x1, y1]); + } + createCircularGradient(x, y, r) { + return new CanvasGradient('radial', [x, y, r]); + } + createPattern(image, repetition) { + if (undefined === repetition) { + console.error("Failed to execute 'createPattern' on 'CanvasContext': 2 arguments required, but only 1 present."); + } + else if (['repeat', 'repeat-x', 'repeat-y', 'no-repeat'].indexOf(repetition) < 0) { + console.error("Failed to execute 'createPattern' on 'CanvasContext': The provided type ('" + + repetition + + "') is not one of 'repeat', 'no-repeat', 'repeat-x', or 'repeat-y'."); + } + else { + return new Pattern(image, repetition); + } + } + measureText(text) { + const font = this.state.font; + let width = 0; + { + const webview = plus.webview + .all() + .find((webview) => webview.getURL().endsWith('www/__uniappview.html')); + if (webview) { + width = Number(webview.evalJSSync(`(${measureText.toString()})(${JSON.stringify(text)},${JSON.stringify(font)})`)); } - else { - params.type = value; + } + return new TextMetrics(width); + } + save() { + this.actions.push({ + method: 'save', + data: [], + }); + this.drawingState.push(this.state); + } + restore() { + this.actions.push({ + method: 'restore', + data: [], + }); + this.state = this.drawingState.pop() || { + lineDash: [0, 0], + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowBlur: 0, + shadowColor: [0, 0, 0, 0], + font: '10px sans-serif', + fontSize: 10, + fontWeight: 'normal', + fontStyle: 'normal', + fontFamily: 'sans-serif', + }; + } + beginPath() { + this.path = []; + this.subpath = []; + } + moveTo(x, y) { + this.path.push({ + method: 'moveTo', + data: [x, y], + }); + this.subpath = [[x, y]]; + } + lineTo(x, y) { + if (this.path.length === 0 && this.subpath.length === 0) { + this.path.push({ + method: 'moveTo', + data: [x, y], + }); + } + else { + this.path.push({ + method: 'lineTo', + data: [x, y], + }); + } + this.subpath.push([x, y]); + } + quadraticCurveTo(cpx, cpy, x, y) { + this.path.push({ + method: 'quadraticCurveTo', + data: [cpx, cpy, x, y], + }); + this.subpath.push([x, y]); + } + bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) { + this.path.push({ + method: 'bezierCurveTo', + data: [cp1x, cp1y, cp2x, cp2y, x, y], + }); + this.subpath.push([x, y]); + } + arc(x, y, r, sAngle, eAngle, counterclockwise = false) { + this.path.push({ + method: 'arc', + data: [x, y, r, sAngle, eAngle, counterclockwise], + }); + this.subpath.push([x, y]); + } + rect(x, y, width, height) { + this.path.push({ + method: 'rect', + data: [x, y, width, height], + }); + this.subpath = [[x, y]]; + } + arcTo(x1, y1, x2, y2, radius) { + this.path.push({ + method: 'arcTo', + data: [x1, y1, x2, y2, radius], + }); + this.subpath.push([x2, y2]); + } + clip() { + this.actions.push({ + method: 'clip', + data: [...this.path], + }); + } + closePath() { + this.path.push({ + method: 'closePath', + data: [], + }); + if (this.subpath.length) { + this.subpath = [this.subpath.shift()]; + } + } + clearActions() { + this.actions = []; + this.path = []; + this.subpath = []; + } + getActions() { + var actions = [...this.actions]; + this.clearActions(); + return actions; + } + set lineDashOffset(value) { + this.actions.push({ + method: 'setLineDashOffset', + data: [value], + }); + } + set globalCompositeOperation(type) { + this.actions.push({ + method: 'setGlobalCompositeOperation', + data: [type], + }); + } + set shadowBlur(level) { + this.actions.push({ + method: 'setShadowBlur', + data: [level], + }); + } + set shadowColor(color) { + this.actions.push({ + method: 'setShadowColor', + data: [color], + }); + } + set shadowOffsetX(x) { + this.actions.push({ + method: 'setShadowOffsetX', + data: [x], + }); + } + set shadowOffsetY(y) { + this.actions.push({ + method: 'setShadowOffsetY', + data: [y], + }); + } + set font(value) { + var self = this; + this.state.font = value; + // eslint-disable-next-line + var fontFormat = value.match(/^(([\w\-]+\s)*)(\d+r?px)(\/(\d+\.?\d*(r?px)?))?\s+(.*)/); + if (fontFormat) { + var style = fontFormat[1].trim().split(/\s/); + var fontSize = parseFloat(fontFormat[3]); + var fontFamily = fontFormat[7]; + var actions = []; + style.forEach(function (value, index) { + if (['italic', 'oblique', 'normal'].indexOf(value) > -1) { + actions.push({ + method: 'setFontStyle', + data: [value], + }); + self.state.fontStyle = value; + } + else if (['bold', 'normal'].indexOf(value) > -1) { + actions.push({ + method: 'setFontWeight', + data: [value], + }); + self.state.fontWeight = value; + } + else if (index === 0) { + actions.push({ + method: 'setFontStyle', + data: ['normal'], + }); + self.state.fontStyle = 'normal'; + } + else if (index === 1) { + pushAction(); + } + }); + if (style.length === 1) { + pushAction(); } - }, - altitude(value, params) { - params.altitude = value ? value : false; - }, - }, - }; - const GetLocationProtocol = { - type: String, - altitude: Boolean, - }; - - const API_CHOOSE_IMAGE = 'chooseImage'; - const ChooseImageOptions = { - formatArgs: { - count(value, params) { - if (!value || value <= 0) { - params.count = 9; + style = actions + .map(function (action) { + return action.data[0]; + }) + .join(' '); + this.state.fontSize = fontSize; + this.state.fontFamily = fontFamily; + this.actions.push({ + method: 'setFont', + data: [`${style} ${fontSize}px ${fontFamily}`], + }); + } + else { + console.warn("Failed to set 'font' on 'CanvasContext': invalid format."); + } + function pushAction() { + actions.push({ + method: 'setFontWeight', + data: ['normal'], + }); + self.state.fontWeight = 'normal'; + } + } + get font() { + return this.state.font; + } + set fillStyle(color) { + this.setFillStyle(color); + } + set strokeStyle(color) { + this.setStrokeStyle(color); + } + set globalAlpha(value) { + value = Math.floor(255 * parseFloat(value)); + this.actions.push({ + method: 'setGlobalAlpha', + data: [value], + }); + } + set textAlign(align) { + this.actions.push({ + method: 'setTextAlign', + data: [align], + }); + } + set lineCap(type) { + this.actions.push({ + method: 'setLineCap', + data: [type], + }); + } + set lineJoin(type) { + this.actions.push({ + method: 'setLineJoin', + data: [type], + }); + } + set lineWidth(value) { + this.actions.push({ + method: 'setLineWidth', + data: [value], + }); + } + set miterLimit(value) { + this.actions.push({ + method: 'setMiterLimit', + data: [value], + }); + } + set textBaseline(type) { + this.actions.push({ + method: 'setTextBaseline', + data: [type], + }); + } + } + const initCanvasContextProperty = /*#__PURE__*/ once(() => { + [...methods1, ...methods2].forEach(function (method) { + function get(method) { + switch (method) { + case 'fill': + case 'stroke': + return function () { + // @ts-ignore + this.actions.push({ + method: method + 'Path', + // @ts-ignore + data: [...this.path], + }); + }; + case 'fillRect': + return function (x, y, width, height) { + // @ts-ignore + this.actions.push({ + method: 'fillPath', + data: [ + { + method: 'rect', + data: [x, y, width, height], + }, + ], + }); + }; + case 'strokeRect': + return function (x, y, width, height) { + // @ts-ignore + this.actions.push({ + method: 'strokePath', + data: [ + { + method: 'rect', + data: [x, y, width, height], + }, + ], + }); + }; + case 'fillText': + case 'strokeText': + return function (text, x, y, maxWidth) { + var data = [text.toString(), x, y]; + if (typeof maxWidth === 'number') { + data.push(maxWidth); + } + // @ts-ignore + this.actions.push({ + method, + data, + }); + }; + case 'drawImage': + return function (imageResource, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) { + if (sHeight === undefined) { + sx = dx; + sy = dy; + sWidth = dWidth; + sHeight = dHeight; + dx = undefined; + dy = undefined; + dWidth = undefined; + dHeight = undefined; + } + var data; + function isNumber(e) { + return typeof e === 'number'; + } + data = + isNumber(dx) && + isNumber(dy) && + isNumber(dWidth) && + isNumber(dHeight) + ? [ + imageResource, + sx, + sy, + sWidth, + sHeight, + dx, + dy, + dWidth, + dHeight, + ] + : isNumber(sWidth) && isNumber(sHeight) + ? [imageResource, sx, sy, sWidth, sHeight] + : [imageResource, sx, sy]; + // @ts-ignore + this.actions.push({ + method, + data, + }); + }; + default: + return function (...data) { + // @ts-ignore + this.actions.push({ + method, + data, + }); + }; } - }, - sizeType(sizeType, params) { - params.sizeType = elemsInArray(sizeType, CHOOSE_SIZE_TYPES); - }, - sourceType(sourceType, params) { - params.sourceType = elemsInArray(sourceType, CHOOSE_SOURCE_TYPES); - }, - extension(extension, params) { - if (extension instanceof Array && extension.length === 0) { - return 'param extension should not be empty.'; + } + CanvasContext.prototype[method] = get(method); + }); + methods3.forEach(function (method) { + function get(method) { + switch (method) { + case 'setFillStyle': + case 'setStrokeStyle': + return function (color) { + if (typeof color !== 'object') { + // @ts-ignore + this.actions.push({ + method, + data: ['normal', checkColor(color)], + }); + } + else { + // @ts-ignore + this.actions.push({ + method, + data: [color.type, color.data, color.colorStop], + }); + } + }; + case 'setGlobalAlpha': + return function (alpha) { + alpha = Math.floor(255 * parseFloat(alpha)); + // @ts-ignore + this.actions.push({ + method, + data: [alpha], + }); + }; + case 'setShadow': + return function (offsetX, offsetY, blur, color) { + color = checkColor(color); + // @ts-ignore + this.actions.push({ + method, + data: [offsetX, offsetY, blur, color], + }); + // @ts-ignore + this.state.shadowBlur = blur; + // @ts-ignore + this.state.shadowColor = color; + // @ts-ignore + this.state.shadowOffsetX = offsetX; + // @ts-ignore + this.state.shadowOffsetY = offsetY; + }; + case 'setLineDash': + return function (pattern, offset) { + pattern = pattern || [0, 0]; + offset = offset || 0; + // @ts-ignore + this.actions.push({ + method, + data: [pattern, offset], + }); + // @ts-ignore + this.state.lineDash = pattern; + }; + case 'setFontSize': + return function (fontSize) { + // @ts-ignore + this.state.font = this.state.font.replace(/\d+\.?\d*px/, fontSize + 'px'); + // @ts-ignore + this.state.fontSize = fontSize; + // @ts-ignore + this.actions.push({ + method, + data: [fontSize], + }); + }; + default: + return function (...data) { + // @ts-ignore + this.actions.push({ + method, + data, + }); + }; } - if (!extension) - params.extension = ['']; - }, - }, + } + CanvasContext.prototype[method] = get(method); + }); + }); + const createCanvasContext = defineSyncApi(API_CREATE_CANVAS_CONTEXT, (canvasId, componentInstance) => { + initCanvasContextProperty(); + if (componentInstance) { + return new CanvasContext(canvasId, getPageIdByVm(componentInstance)); + } + const pageId = getPageIdByVm(getCurrentPageVm()); + if (pageId) { + return new CanvasContext(canvasId, pageId); + } + else { + UniServiceJSBridge.emit('onError', 'createCanvasContext:fail'); + } + }, CreateCanvasContextProtocol); + const canvasGetImageData = defineAsyncApi(API_CANVAS_GET_IMAGE_DATA, ({ canvasId, x, y, width, height }, { resolve, reject }) => { + onCanvasMethodCallback(); + const pageId = getPageIdByVm(getCurrentPageVm()); + if (!pageId) { + reject(); + return; + } + const cId = canvasEventCallbacks.push(function (data) { + let imgData = data.data; + if (imgData && imgData.length) { + if (data.compressed) { + return Promise.resolve().then(function () { return pako_esm; }).then((pako) => { + imgData = pako.inflateRaw(imgData); + delete data.compressed; + data.data = new Uint8ClampedArray(imgData); + resolve(data); + }); + } + data.data = new Uint8ClampedArray(imgData); + } + resolve(data); + }); + operateCanvas(canvasId, pageId, 'getImageData', { + x, + y, + width, + height, + callbackId: cId, + }); + }, CanvasGetImageDataProtocol, CanvasGetImageDataOptions); + const canvasPutImageData = defineAsyncApi(API_CANVAS_PUT_IMAGE_DATA, ({ canvasId, data, x, y, width, height }, { resolve, reject }) => { + onCanvasMethodCallback(); + var pageId = getPageIdByVm(getCurrentPageVm()); + if (!pageId) { + reject(); + return; + } + const cId = canvasEventCallbacks.push(function (data) { + resolve(data); + }); + let compressed; + const operate = () => { + operateCanvas(canvasId, pageId, 'putImageData', { + data, + x, + y, + width, + height, + compressed, + callbackId: cId, + }); + }; + // iOS真机非调试模式压缩太慢暂时排除 + if ((plus.os.name !== 'iOS' || typeof __WEEX_DEVTOOL__ === 'boolean')) { + return Promise.resolve().then(function () { return pako_esm; }).then((pako) => { + data = pako.deflateRaw(data, { to: 'string' }); + compressed = true; + operate(); + }); + } + // fix ... + data = Array.prototype.slice.call(data); + operate(); + }, CanvasPutImageDataProtocol, CanvasPutImageDataOptions); + const canvasToTempFilePath = defineAsyncApi(API_CANVAS_TO_TEMP_FILE_PATH, ({ x = 0, y = 0, width, height, destWidth, destHeight, canvasId, fileType, quality, }, { resolve, reject }) => { + onCanvasMethodCallback(); + var pageId = getPageIdByVm(getCurrentPageVm()); + if (!pageId) { + reject(); + return; + } + const cId = canvasEventCallbacks.push(function (res) { + resolve(res); + }); + const dirname = `${TEMP_PATH$1}/canvas`; + operateCanvas(canvasId, pageId, 'toTempFilePath', { + x, + y, + width, + height, + destWidth, + destHeight, + fileType, + quality, + dirname, + callbackId: cId, + }); + }, CanvasToTempFilePathProtocol, CanvasToTempFilePathOptions); + + //#endregion + /** + * 可以批量设置的监听事件 + */ + const innerAudioContextEventNames = [ + 'onCanplay', + 'onPlay', + 'onPause', + 'onStop', + 'onEnded', + 'onTimeUpdate', + 'onError', + 'onWaiting', + 'onSeeking', + 'onSeeked', + ]; + const innerAudioContextOffEventNames = [ + 'offCanplay', + 'offPlay', + 'offPause', + 'offStop', + 'offEnded', + 'offTimeUpdate', + 'offError', + 'offWaiting', + 'offSeeking', + 'offSeeked', + ]; + + const defaultOptions = { + thresholds: [0], + initialRatio: 0, + observeAll: false, }; - const ChooseImageProtocol = { - count: Number, - sizeType: [Array, String], - sourceType: Array, - extension: Array, - }; + const MARGINS = ['top', 'right', 'bottom', 'left']; + let reqComponentObserverId$1 = 1; + function normalizeRootMargin(margins = {}) { + return MARGINS.map((name) => `${Number(margins[name]) || 0}px`).join(' '); + } + class ServiceIntersectionObserver { + constructor(component, options) { + this._pageId = getPageIdByVm(component); + this._component = component; + this._options = extend({}, defaultOptions, options); + } + relativeTo(selector, margins) { + this._options.relativeToSelector = selector; + this._options.rootMargin = normalizeRootMargin(margins); + return this; + } + relativeToViewport(margins) { + this._options.relativeToSelector = undefined; + this._options.rootMargin = normalizeRootMargin(margins); + return this; + } + observe(selector, callback) { + if (!isFunction(callback)) { + return; + } + this._options.selector = selector; + this._reqId = reqComponentObserverId$1++; + addIntersectionObserver({ + reqId: this._reqId, + component: this._component, + options: this._options, + callback, + }, this._pageId); + } + disconnect() { + this._reqId && + removeIntersectionObserver({ reqId: this._reqId, component: this._component }, this._pageId); + } + } + const createIntersectionObserver = defineSyncApi('createIntersectionObserver', (context, options) => { + if (context && !getPageIdByVm(context)) { + options = context; + context = null; + } + if (context) { + return new ServiceIntersectionObserver(context, options); + } + return new ServiceIntersectionObserver(getCurrentPageVm(), options); + }); - const API_CHOOSE_VIDEO = 'chooseVideo'; - const ChooseVideoOptions = { - formatArgs: { - sourceType(sourceType, params) { - params.sourceType = elemsInArray(sourceType, CHOOSE_SOURCE_TYPES); - }, - compressed: true, - maxDuration: 60, - camera: 'back', - extension(extension, params) { - if (extension instanceof Array && extension.length === 0) { - return 'param extension should not be empty.'; + let reqComponentObserverId = 1; + class ServiceMediaQueryObserver { + constructor(component) { + this._pageId = component.$page && component.$page.id; + this._component = component; + } + observe(options, callback) { + if (!isFunction(callback)) { + return; + } + this._reqId = reqComponentObserverId++; + addMediaQueryObserver({ + reqId: this._reqId, + component: this._component, + options, + callback, + }, this._pageId); + } + disconnect() { + this._reqId && + removeMediaQueryObserver({ + reqId: this._reqId, + component: this._component, + }, this._pageId); + } + } + const createMediaQueryObserver = defineSyncApi('createMediaQueryObserver', (context) => { + if (context && !getPageIdByVm(context)) { + context = null; + } + if (context) { + return new ServiceMediaQueryObserver(context); + } + return new ServiceMediaQueryObserver(getCurrentPageVm()); + }); + + let eventReady = false; + let index$1 = 0; + let optionsCache = {}; + function operateEditor(componentId, pageId, type, options) { + const data = {}; + if (options && + ('success' in options || 'fail' in options || 'complete' in options)) { + const callbackId = String(index$1++); + data.callbackId = callbackId; + optionsCache[callbackId] = options; + if (!eventReady) { + UniServiceJSBridge.subscribe('onEditorMethodCallback', ({ callbackId, data }) => { + callOptions(optionsCache[callbackId], data); + delete optionsCache[callbackId]; + }); + eventReady = true; + } + } + data.options = options; + UniServiceJSBridge.publishHandler('editor.' + componentId, { + componentId, + type, + data, + }, pageId); + } + class EditorContext { + constructor(id, pageId) { + this.id = id; + this.pageId = pageId; + } + format(name, value) { + this._exec('format', { + name, + value, + }); + } + insertDivider() { + this._exec('insertDivider'); + } + insertImage(options) { + this._exec('insertImage', options); + } + insertText(options) { + this._exec('insertText', options); + } + setContents(options) { + this._exec('setContents', options); + } + getContents(options) { + this._exec('getContents', options); + } + clear(options) { + this._exec('clear', options); + } + removeFormat(options) { + this._exec('removeFormat', options); + } + undo(options) { + this._exec('undo', options); + } + redo(options) { + this._exec('redo', options); + } + blur(options) { + this._exec('blur', options); + } + getSelectionText(options) { + this._exec('getSelectionText', options); + } + scrollIntoView(options) { + this._exec('scrollIntoView', options); + } + _exec(method, options) { + operateEditor(this.id, this.pageId, method, options); + } + } + + const ContextClasss = { + canvas: CanvasContext, + map: MapContext, + video: VideoContext, + editor: EditorContext, + }; + function convertContext(result) { + if (result && result.contextInfo) { + const { id, type, page } = result.contextInfo; + const ContextClass = ContextClasss[type]; + result.context = new ContextClass(id, page); + delete result.contextInfo; + } + } + class NodesRef { + constructor(selectorQuery, component, selector, single) { + this._selectorQuery = selectorQuery; + this._component = component; + this._selector = selector; + this._single = single; + } + boundingClientRect(callback) { + this._selectorQuery._push(this._selector, this._component, this._single, { + id: true, + dataset: true, + rect: true, + size: true, + }, callback); + return this._selectorQuery; + } + fields(fields, callback) { + this._selectorQuery._push(this._selector, this._component, this._single, fields, callback); + return this._selectorQuery; + } + scrollOffset(callback) { + this._selectorQuery._push(this._selector, this._component, this._single, { + id: true, + dataset: true, + scrollOffset: true, + }, callback); + return this._selectorQuery; + } + context(callback) { + this._selectorQuery._push(this._selector, this._component, this._single, { + context: true, + }, callback); + return this._selectorQuery; + } + } + class SelectorQuery { + constructor(page) { + this._component = undefined; + this._page = page; + this._queue = []; + this._queueCb = []; + } + exec(callback) { + requestComponentInfo(this._page, this._queue, (res) => { + const queueCbs = this._queueCb; + res.forEach((result, index) => { + if (Array.isArray(result)) { + result.forEach(convertContext); + } + else { + convertContext(result); + } + const queueCb = queueCbs[index]; + if (typeof queueCb === 'function') { + queueCb.call(this, result); + } + }); + // isFn(callback) && + if (typeof callback === 'function') { + callback.call(this, res); } - if (!extension) - params.extension = ['']; - }, + }); + // TODO + return this._nodesRef; + } + in(component) { + this._component = component || undefined; + return this; + } + select(selector) { + return (this._nodesRef = new NodesRef(this, this._component, selector, true)); + } + selectAll(selector) { + return (this._nodesRef = new NodesRef(this, this._component, selector, false)); + } + selectViewport() { + return (this._nodesRef = new NodesRef(this, null, '', true)); + } + _push(selector, component, single, fields, callback) { + this._queue.push({ + component, + selector, + single, + fields, + }); + this._queueCb.push(callback); + } + } + const createSelectorQuery = (defineSyncApi('createSelectorQuery', (context) => { + if (context && !getPageIdByVm(context)) { + context = null; + } + return new SelectorQuery(context || getCurrentPageVm()); + })); + + // import { elemInArray } from '../../helpers/protocol' + const API_CREATE_ANIMATION = 'createAnimation'; + // const timingFunctions: API_TYPE_CREATE_ANIMATION_Timing_Function[] = [ + // 'linear', + // 'ease', + // 'ease-in', + // 'ease-in-out', + // 'ease-out', + // 'step-start', + // 'step-end', + // ] + const CreateAnimationOptions = { + // 目前参数校验不支持此api校验 + formatArgs: { + /* duration: 400, + timingFunction(timingFunction, params) { + params.timingFunction = elemInArray(timingFunction, timingFunctions) + }, + delay: 0, + transformOrigin: '50% 50% 0', */ }, }; - const ChooseVideoProtocol = { - sourceType: Array, - compressed: Boolean, - maxDuration: Number, - camera: String, - extension: Array, + const CreateAnimationProtocol = { + duration: Number, + timingFunction: String, + delay: Number, + transformOrigin: String, }; - const API_GET_IMAGE_INFO = 'getImageInfo'; - const GetImageInfoOptions = { - formatArgs: { - src(src, params) { - params.src = getRealPath(src); - }, - }, + const defaultOption = { + duration: 400, + timingFunction: 'linear', + delay: 0, + transformOrigin: '50% 50% 0', }; - const GetImageInfoProtocol = { - src: { - type: String, - required: true, - }, - }; + class MPAnimation { + constructor(option) { + this.actions = []; + this.currentTransform = {}; + this.currentStepAnimates = []; + this.option = extend({}, defaultOption, option); + } + _getOption(option) { + const _option = { + transition: extend({}, this.option, option), + transformOrigin: '', + }; + _option.transformOrigin = _option.transition.transformOrigin; + delete _option.transition.transformOrigin; + return _option; + } + _pushAnimates(type, args) { + this.currentStepAnimates.push({ + type: type, + args: args, + }); + } + _converType(type) { + return type.replace(/[A-Z]/g, (text) => { + return `-${text.toLowerCase()}`; + }); + } + _getValue(value) { + return typeof value === 'number' ? `${value}px` : value; + } + export() { + const actions = this.actions; + this.actions = []; + return { + actions, + }; + } + step(option) { + this.currentStepAnimates.forEach((animate) => { + if (animate.type !== 'style') { + this.currentTransform[animate.type] = animate; + } + else { + this.currentTransform[`${animate.type}.${animate.args[0]}`] = animate; + } + }); + this.actions.push({ + animates: Object.values(this.currentTransform), + option: this._getOption(option), + }); + this.currentStepAnimates = []; + return this; + } + } + const initAnimationProperty = /*#__PURE__*/ once(() => { + const animateTypes1 = [ + 'matrix', + 'matrix3d', + 'rotate', + 'rotate3d', + 'rotateX', + 'rotateY', + 'rotateZ', + 'scale', + 'scale3d', + 'scaleX', + 'scaleY', + 'scaleZ', + 'skew', + 'skewX', + 'skewY', + 'translate', + 'translate3d', + 'translateX', + 'translateY', + 'translateZ', + ]; + const animateTypes2 = ['opacity', 'backgroundColor']; + const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']; + animateTypes1.concat(animateTypes2, animateTypes3).forEach((type) => { + MPAnimation.prototype[type] = function (...args) { + if (animateTypes2.concat(animateTypes3).includes(type)) { + this._pushAnimates('style', [ + this._converType(type), + animateTypes3.includes(type) ? this._getValue(args[0]) : args[0], + ]); + } + else { + this._pushAnimates(type, args); + } + return this; + }; + }); + }); + const createAnimation = defineSyncApi(API_CREATE_ANIMATION, (option) => { + initAnimationProperty(); + return new MPAnimation(option); + }, CreateAnimationProtocol, CreateAnimationOptions); - const API_PREVIEW_IMAGE = 'previewImage'; - const PreviewImageOptions = { - formatArgs: { - urls(urls, params) { - params.urls = urls.map((url) => typeof url === 'string' && url ? getRealPath(url) : ''); - }, - current(current, params) { - if (typeof current === 'number') { - params.current = - current > 0 && current < params.urls.length ? current : 0; + const API_ON_TAB_BAR_MID_BUTTON_TAP = 'onTabBarMidButtonTap'; + const onTabBarMidButtonTap = defineOnApi(API_ON_TAB_BAR_MID_BUTTON_TAP, () => { + // noop + }); + + const API_GET_SELECTED_TEXT_RANGE = 'getSelectedTextRange'; + + const getSelectedTextRangeEventCallbacks = createCallbacks('getSelectedTextRangeEvent'); + const onGetSelectedTextRange = /*#__PURE__*/ once(() => { + UniServiceJSBridge.subscribe('onGetSelectedTextRange', ({ callbackId, data }) => { + const callback = getSelectedTextRangeEventCallbacks.pop(callbackId); + if (callback) { + callback(data); + } + }); + }); + const getSelectedTextRange = defineAsyncApi(API_GET_SELECTED_TEXT_RANGE, (_, { resolve, reject }) => { + onGetSelectedTextRange(); + const pageId = getCurrentPageId(); + UniServiceJSBridge.publishHandler('getSelectedTextRange', { + pageId, + callbackId: getSelectedTextRangeEventCallbacks.push(function (res) { + if (typeof res.end === 'undefined' && + typeof res.start === 'undefined') { + reject('no focused'); } - else if (typeof current === 'string' && current) { - params.current = getRealPath(current); + else { + resolve(res); } - }, - }, + }), + }, pageId); + }); + + const API_GET_BACKGROUND_AUDIO_MANAGER = 'getBackgroundAudioManager'; + + const API_MAKE_PHONE_CALL = 'makePhoneCall'; + const MakePhoneCallProtocol = { + phoneNumber: String, + }; + + const API_ADD_PHONE_CONTACT = 'addPhoneContact'; + + const API_GET_CLIPBOARD_DATA = 'getClipboardData'; + const API_SET_CLIPBOARD_DATA = 'setClipboardData'; + + const API_ON_ACCELEROMETER = 'onAccelerometer'; + const API_OFF_ACCELEROMETER = 'offAccelerometer'; + const API_START_ACCELEROMETER = 'startAccelerometer'; + const API_STOP_ACCELEROMETER = 'stopAccelerometer'; + + const API_ON_COMPASS = 'onCompass'; + const API_OFF_COMPASS = 'offCompass'; + const API_START_COMPASS = 'startCompass'; + const API_STOP_COMPASS = 'stopCompass'; + + const API_VIBRATE_SHORT = 'vibrateShort'; + const API_VIBRATE_LONG = 'vibrateLong'; + + const API_ON_BLUETOOTH_DEVICE_FOUND = 'onBluetoothDeviceFound'; + const API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE = 'onBluetoothAdapterStateChange'; + const API_ON_BLE_CONNECTION_STATE_CHANGE = 'onBLEConnectionStateChange'; + const API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE = 'onBLECharacteristicValueChange'; + const API_START_BLUETOOTH_DEVICES_DISCOVERY = 'startBluetoothDevicesDiscovery'; + const StartBluetoothDevicesDiscoveryProtocol = { + services: Array, + allowDuplicatesKey: Boolean, + interval: Number, }; - const PreviewImageProtocol = { - urls: { + const API_GET_CONNECTED_BLUETOOTH_DEVICES = 'getConnectedBluetoothDevices'; + const GetConnectedBluetoothDevicesProtocol = { + services: { type: Array, required: true, }, - current: { - type: [Number, String], - }, - }; - - const API_GET_VIDEO_INFO = 'getVideoInfo'; - const GetVideoInfoOptions = { - formatArgs: { - src(src, params) { - params.src = getRealPath(src); - }, + }; + const API_CREATE_BLE_CONNECTION = 'createBLEConnection'; + const CreateBLEConnectionProtocol = { + deviceId: { + type: String, + required: true, }, }; - const GetVideoInfoProtocol = { - src: { + const API_CLOSE_BLE_CONNECTION = 'closeBLEConnection'; + const CloseBLEConnectionProtocol = { + deviceId: { type: String, required: true, }, - }; - - const API_SAVE_IMAGE_TO_PHOTOS_ALBUM = 'saveImageToPhotosAlbum'; - const SaveImageToPhotosAlbumOptions = { - formatArgs: { - filePath(filePath, params) { - params.filePath = getRealPath(filePath); - }, + }; + const API_GET_BLE_DEVICE_SERVICES = 'getBLEDeviceServices'; + const GetBLEDeviceServicesProtocol = { + deviceId: { + type: String, + required: true, }, }; - const SaveImageToPhotosAlbumProtocol = { - filePath: { + const API_GET_BLE_DEVICE_CHARACTERISTICS = 'getBLEDeviceCharacteristics'; + const GetBLEDeviceCharacteristicsProtocol = { + deviceId: { type: String, required: true, }, - }; - - const API_SAVE_VIDEO_TO_PHOTOS_ALBUM = 'saveVideoToPhotosAlbum'; - const SaveVideoToPhotosAlbumOptions = { - formatArgs: { - filePath(filePath, params) { - params.filePath = getRealPath(filePath); - }, + serviceId: { + type: String, + required: true, }, }; - const SaveVideoToPhotosAlbumProtocol = { - filePath: { + const API_NOTIFY_BLE_CHARACTERISTIC_VALUE_CHANGE = 'notifyBLECharacteristicValueChange'; + const NotifyBLECharacteristicValueChangeProtocol = { + deviceId: { type: String, required: true, }, - }; - - const API_GET_RECORDER_MANAGER = 'getRecorderManager'; - - const API_COMPRESS_IMAGE = 'compressImage'; - const CompressImageOptions = { - formatArgs: { - src(src, params) { - params.src = getRealPath(src); - }, + serviceId: { + type: String, + required: true, }, - }; - const CompressImageProtocol = { - src: { + characteristicId: { type: String, required: true, }, - }; - - const API_COMPRESS_VIDEO = 'compressVideo'; - const CompressVideoOptions = { - formatArgs: { - src(src, params) { - params.src = getRealPath(src); - }, + state: { + type: Boolean, + required: true, }, }; - const CompressVideoProtocol = { - src: { + const API_READ_BLE_CHARACTERISTIC_VALUE = 'readBLECharacteristicValue'; + const ReadBLECharacteristicValueProtocol = { + deviceId: { + type: String, + required: true, + }, + serviceId: { + type: String, + required: true, + }, + characteristicId: { type: String, required: true, }, - quality: String, - bitrate: Number, - fps: Number, - resolution: Number, - }; - - const API_REQUEST = 'request'; - const dataType = { - JSON: 'json', }; - const RESPONSE_TYPE = ['text', 'arraybuffer']; - const DEFAULT_RESPONSE_TYPE = 'text'; - const encode$1 = encodeURIComponent; - function stringifyQuery(url, data) { - let str = url.split('#'); - const hash = str[1] || ''; - str = str[0].split('?'); - let query = str[1] || ''; - url = str[0]; - const search = query.split('&').filter((item) => item); - const params = {}; - search.forEach((item) => { - const part = item.split('='); - params[part[0]] = part[1]; - }); - for (const key in data) { - if (hasOwn$1(data, key)) { - let v = data[key]; - if (typeof v === 'undefined' || v === null) { - v = ''; - } - else if (isPlainObject(v)) { - v = JSON.stringify(v); - } - params[encode$1(key)] = encode$1(v); - } - } - query = Object.keys(params) - .map((item) => `${item}=${params[item]}`) - .join('&'); - return url + (query ? '?' + query : '') + (hash ? '#' + hash : ''); - } - const RequestProtocol = { - method: String, - data: [Object, String, Array, ArrayBuffer], - url: { + const API_WRITE_BLE_CHARACTERISTIC_VALUE = 'writeBLECharacteristicValue'; + const WriteBLECharacteristicValueProtocol = { + deviceId: { type: String, required: true, }, - header: Object, - dataType: String, - responseType: String, - withCredentials: Boolean, + serviceId: { + type: String, + required: true, + }, + characteristicId: { + type: String, + required: true, + }, + value: { + type: Array, + required: true, + }, }; - const RequestOptions = { - formatArgs: { - method(value, params) { - params.method = elemInArray((value || '').toUpperCase(), HTTP_METHODS); - }, - data(value, params) { - params.data = value || ''; - }, - url(value, params) { - if (params.method === HTTP_METHODS[0] && - isPlainObject(params.data) && - Object.keys(params.data).length) { - // 将 method,data 校验提前,保证 url 校验时,method,data 已被格式化 - params.url = stringifyQuery(value, params.data); - } - }, - header(value, params) { - const header = (params.header = value || {}); - if (params.method !== HTTP_METHODS[0]) { - if (!Object.keys(header).find((key) => key.toLowerCase() === 'content-type')) { - header['Content-Type'] = 'application/json'; - } - } - }, - dataType(value, params) { - params.dataType = (value || dataType.JSON).toLowerCase(); - }, - responseType(value, params) { - params.responseType = (value || '').toLowerCase(); - if (RESPONSE_TYPE.indexOf(params.responseType) === -1) { - params.responseType = DEFAULT_RESPONSE_TYPE; - } - }, + const API_SET_BLE_MTU = 'setBLEMTU'; + const SetBLEMTUProtocol = { + deviceId: { + type: String, + required: true, + }, + mtu: { + type: Number, + required: true, + }, + }; + const API_GET_BLE_DEVICE_RSSI = 'getBLEDeviceRSSI'; + const GetBLEDeviceRSSIProtocol = { + deviceId: { + type: String, + required: true, }, }; - const API_DOWNLOAD_FILE = 'downloadFile'; - const DownloadFileOptions = { + const API_ON_BEACON_UPDATE = 'onBeaconUpdate'; + const API_ON_BEACON_SERVICE_CHANGE = 'onBeaconServiceChange'; + const API_GET_BEACONS = 'getBeacons'; + const API_START_BEACON_DISCOVERY = 'startBeaconDiscovery'; + const StartBeaconDiscoveryProtocol = { + uuids: { + type: Array, + required: true, + }, + }; + const API_STOP_BEACON_DISCOVERY = 'stopBeaconDiscovery'; + + const API_CHECK_IS_SUPPORT_SOTER_AUTHENTICATION = 'soterAuthentication'; + const API_CHECK_IS_SOTER_ENROLLED_IN_DEVICE = 'checkIsSoterEnrolledInDevice'; + const CheckAuthModes = [ + 'fingerPrint', + 'facial', + 'speech', + ]; + const CheckIsSoterEnrolledInDeviceOptions = { formatArgs: { - header(value, params) { - params.header = value || {}; + checkAuthMode(value, params) { + if (!value || !CheckAuthModes.includes(value)) + return 'checkAuthMode 填写错误'; }, }, }; - const DownloadFileProtocol = { - url: { - type: String, + const CheckIsSoterEnrolledInDeviceProtocols = { + checkAuthMode: String, + }; + const API_START_SOTER_AUTHENTICATION = 'checkIsSoterEnrolledInDevice'; + const StartSoterAuthenticationOptions = { + formatArgs: { + requestAuthModes(value, params) { + if (!value.includes('fingerPrint') && !value.includes('facial')) + return 'requestAuthModes 填写错误'; + }, + }, + }; + const StartSoterAuthenticationProtocols = { + requestAuthModes: { + type: Array, required: true, }, - header: Object, - timeout: Number, + challenge: String, + authContent: String, }; - const API_UPLOAD_FILE = 'uploadFile'; - const UploadFileOptions = { + const API_GET_STORAGE = 'getStorage'; + const GetStorageProtocol = { + key: { + type: String, + required: true, + }, + }; + const API_GET_STORAGE_SYNC = 'getStorageSync'; + const GetStorageSyncProtocol = [ + { + name: 'key', + type: String, + required: true, + }, + ]; + const API_SET_STORAGE = 'setStorage'; + const SetStorageProtocol = { + key: { + type: String, + required: true, + }, + data: { + required: true, + }, + }; + const API_SET_STORAGE_SYNC = 'setStorageSync'; + const SetStorageSyncProtocol = [ + { + name: 'key', + type: String, + required: true, + }, + { + name: 'data', + required: true, + }, + ]; + const API_REMOVE_STORAGE = 'removeStorage'; + const RemoveStorageProtocol = GetStorageProtocol; + const RemoveStorageSyncProtocol = GetStorageSyncProtocol; + + const API_GET_FILE_INFO = 'getFileInfo'; + const GetFileInfoOptions = { formatArgs: { filePath(filePath, params) { - if (filePath) { - params.filePath = getRealPath(filePath); - } - }, - header(value, params) { - params.header = value || {}; - }, - formData(value, params) { - params.formData = value || {}; + params.filePath = getRealPath(filePath); }, }, }; - const UploadFileProtocol = { - url: { + const GetFileInfoProtocol = { + filePath: { type: String, required: true, }, - files: Array, - filePath: String, - name: String, - header: Object, - formData: Object, - timeout: Number, }; - const API_CONNECT_SOCKET = 'connectSocket'; - const ConnectSocketOptions = { + const API_OPEN_DOCUMENT = 'openDocument'; + const OpenDocumentOptions = { formatArgs: { - header(value, params) { - params.header = value || {}; - }, - method(value, params) { - params.method = elemInArray((value || '').toUpperCase(), HTTP_METHODS); - }, - protocols(protocols, params) { - if (typeof protocols === 'string') { - params.protocols = [protocols]; - } + filePath(filePath, params) { + params.filePath = getRealPath(filePath); }, }, }; - const ConnectSocketProtocol = { - url: { + const OpenDocumentProtocol = { + filePath: { type: String, required: true, }, - header: { - type: Object, + fileType: String, + }; + + const API_HIDE_KEYBOARD = 'hideKeyboard'; + const API_SHOW_KEYBOARD = 'showKeyboard'; + + const API_GET_LOCATION = 'getLocation'; + const coordTypes = ['WGS84', 'GCJ02']; + const GetLocationOptions = { + formatArgs: { + type(value, params) { + value = (value || '').toUpperCase(); + if (coordTypes.indexOf(value) === -1) { + params.type = coordTypes[0]; + } + else { + params.type = value; + } + }, + altitude(value, params) { + params.altitude = value ? value : false; + }, }, - method: String, - protocols: [Array, String], }; - const API_SEND_SOCKET_MESSAGE = 'sendSocketMessage'; - const SendSocketMessageProtocol = { - data: [String, ArrayBuffer], + const GetLocationProtocol = { + type: String, + altitude: Boolean, + }; + + const API_CHOOSE_IMAGE = 'chooseImage'; + const ChooseImageOptions = { + formatArgs: { + count(value, params) { + if (!value || value <= 0) { + params.count = 9; + } + }, + sizeType(sizeType, params) { + params.sizeType = elemsInArray(sizeType, CHOOSE_SIZE_TYPES); + }, + sourceType(sourceType, params) { + params.sourceType = elemsInArray(sourceType, CHOOSE_SOURCE_TYPES); + }, + extension(extension, params) { + if (extension instanceof Array && extension.length === 0) { + return 'param extension should not be empty.'; + } + if (!extension) + params.extension = ['']; + }, + }, }; - const API_CLOSE_SOCKET = 'closeSocket'; - const CloseSocketProtocol = { - code: Number, - reason: String, + const ChooseImageProtocol = { + count: Number, + sizeType: [Array, String], + sourceType: Array, + extension: Array, }; - function encodeQueryString(url) { - if (typeof url !== 'string') { - return url; - } - const index = url.indexOf('?'); - if (index === -1) { - return url; - } - const query = url - .substr(index + 1) - .trim() - .replace(/^(\?|#|&)/, ''); - if (!query) { - return url; - } - url = url.substr(0, index); - const params = []; - query.split('&').forEach((param) => { - const parts = param.replace(/\+/g, ' ').split('='); - const key = parts.shift(); - const val = parts.length > 0 ? parts.join('=') : ''; - params.push(key + '=' + encodeURIComponent(val)); - }); - return params.length ? url + '?' + params.join('&') : url; - } - - const ANIMATION_IN = [ - 'slide-in-right', - 'slide-in-left', - 'slide-in-top', - 'slide-in-bottom', - 'fade-in', - 'zoom-out', - 'zoom-fade-out', - 'pop-in', - 'none', - ]; - const ANIMATION_OUT = [ - 'slide-out-right', - 'slide-out-left', - 'slide-out-top', - 'slide-out-bottom', - 'fade-out', - 'zoom-in', - 'zoom-fade-in', - 'pop-out', - 'none', - ]; - const BaseRouteProtocol = { - url: { - type: String, - required: true, + const API_CHOOSE_VIDEO = 'chooseVideo'; + const ChooseVideoOptions = { + formatArgs: { + sourceType(sourceType, params) { + params.sourceType = elemsInArray(sourceType, CHOOSE_SOURCE_TYPES); + }, + compressed: true, + maxDuration: 60, + camera: 'back', + extension(extension, params) { + if (extension instanceof Array && extension.length === 0) { + return 'param extension should not be empty.'; + } + if (!extension) + params.extension = ['']; + }, }, }; - const API_NAVIGATE_TO = 'navigateTo'; - const API_REDIRECT_TO = 'redirectTo'; - const API_SWITCH_TAB = 'switchTab'; - const API_NAVIGATE_BACK = 'navigateBack'; - const API_PRELOAD_PAGE = 'preloadPage'; - const API_UN_PRELOAD_PAGE = 'unPreloadPage'; - const NavigateToProtocol = - /*#__PURE__*/ extend({}, BaseRouteProtocol, createAnimationProtocol(ANIMATION_IN)); - const NavigateBackProtocol = - /*#__PURE__*/ extend({ - delta: { - type: Number, - }, - }, createAnimationProtocol(ANIMATION_OUT)); - const NavigateToOptions = - /*#__PURE__*/ createRouteOptions(API_NAVIGATE_TO); - const NavigateBackOptions = { + const ChooseVideoProtocol = { + sourceType: Array, + compressed: Boolean, + maxDuration: Number, + camera: String, + extension: Array, + }; + + const API_GET_IMAGE_INFO = 'getImageInfo'; + const GetImageInfoOptions = { formatArgs: { - delta(value, params) { - value = parseInt(value + '') || 1; - params.delta = Math.min(getCurrentPages().length - 1, value); + src(src, params) { + params.src = getRealPath(src); }, }, }; - function createAnimationProtocol(animationTypes) { - return { - animationType: { - type: String, - validator(type) { - if (type && animationTypes.indexOf(type) === -1) { - return ('`' + - type + - '` is not supported for `animationType` (supported values are: `' + - animationTypes.join('`|`') + - '`)'); - } - }, - }, - animationDuration: { - type: Number, - }, - }; - } - let navigatorLock; - function beforeRoute() { - navigatorLock = ''; - } - function createRouteOptions(type) { - return { - formatArgs: { - url: createNormalizeUrl(type), + const GetImageInfoProtocol = { + src: { + type: String, + required: true, + }, + }; + + const API_PREVIEW_IMAGE = 'previewImage'; + const PreviewImageOptions = { + formatArgs: { + urls(urls, params) { + params.urls = urls.map((url) => typeof url === 'string' && url ? getRealPath(url) : ''); }, - beforeAll: beforeRoute, - }; - } - function createNormalizeUrl(type) { - return function normalizeUrl(url, params) { - if (!url) { - return `Missing required args: "url"`; - } - // 格式化为绝对路径路由 - url = normalizeRoute(url); - const pagePath = url.split('?')[0]; - // 匹配路由是否存在 - const routeOptions = getRouteOptions(pagePath, true); - if (!routeOptions) { - return 'page `' + url + '` is not found'; - } - // 检测不同类型跳转 - if (type === API_NAVIGATE_TO || type === API_REDIRECT_TO) { - if (routeOptions.meta.isTabBar) { - return `can not ${type} a tabbar page`; - } - } - else if (type === API_SWITCH_TAB) { - if (!routeOptions.meta.isTabBar) { - return 'can not switch to no-tabBar page'; - } - } - // switchTab不允许传递参数,reLaunch到一个tabBar页面是可以的 - if ((type === API_SWITCH_TAB || type === API_PRELOAD_PAGE) && - routeOptions.meta.isTabBar && - params.openType !== 'appLaunch') { - url = pagePath; - } - // 首页自动格式化为`/` - if (routeOptions.meta.isEntry) { - url = url.replace(routeOptions.alias, '/'); - } - // 参数格式化 - params.url = encodeQueryString(url); - if (type === API_UN_PRELOAD_PAGE) { - return; - } - else if (type === API_PRELOAD_PAGE) { - { - if (!routeOptions.meta.isNVue) { - return 'can not preload vue page'; - } + current(current, params) { + if (typeof current === 'number') { + params.current = + current > 0 && current < params.urls.length ? current : 0; } - if (routeOptions.meta.isTabBar) { - const pages = getCurrentPages(); - const tabBarPagePath = routeOptions.path.substr(1); - if (pages.find((page) => page.route === tabBarPagePath)) { - return 'tabBar page `' + tabBarPagePath + '` already exists'; - } + else if (typeof current === 'string' && current) { + params.current = getRealPath(current); } - return; - } - // 主要拦截目标为用户快速点击时触发的多次跳转,该情况,通常前后 url 是一样的 - if (navigatorLock === url && params.openType !== 'appLaunch') { - return `${navigatorLock} locked`; - } - // 至少 onLaunch 之后,再启用lock逻辑(onLaunch之前可能开发者手动调用路由API,来提前跳转) - // enableNavigatorLock 临时开关(不对外开放),避免该功能上线后,有部分情况异常,可以让开发者临时关闭 lock 功能 - if (__uniConfig.ready) { - navigatorLock = url; - } - }; - } - - const API_HIDE_LOADING = 'hideLoading'; - - const API_HIDE_TOAST = 'hideToast'; - - const API_SHOW_ACTION_SHEET = 'showActionSheet'; - const ShowActionSheetProtocol = { - itemList: { + }, + }, + }; + const PreviewImageProtocol = { + urls: { type: Array, required: true, }, - title: String, - alertText: String, - itemColor: String, - popover: Object, - }; - const ShowActionSheetOptions = { - formatArgs: { - itemColor: '#000', + current: { + type: [Number, String], }, }; - const API_SHOW_LOADING = 'showLoading'; - const ShowLoadingProtocol = { - title: String, - mask: Boolean, + const API_GET_VIDEO_INFO = 'getVideoInfo'; + const GetVideoInfoOptions = { + formatArgs: { + src(src, params) { + params.src = getRealPath(src); + }, + }, }; - const ShowLoadingOptions = { + const GetVideoInfoProtocol = { + src: { + type: String, + required: true, + }, + }; + + const API_SAVE_IMAGE_TO_PHOTOS_ALBUM = 'saveImageToPhotosAlbum'; + const SaveImageToPhotosAlbumOptions = { formatArgs: { - title: '', - mask: false, + filePath(filePath, params) { + params.filePath = getRealPath(filePath); + }, + }, + }; + const SaveImageToPhotosAlbumProtocol = { + filePath: { + type: String, + required: true, }, }; - const API_SHOW_MODAL = 'showModal'; - const ShowModalProtocol = { - title: String, - content: String, - showCancel: Boolean, - cancelText: String, - cancelColor: String, - confirmText: String, - confirmColor: String, - }; - const ShowModalOptions = { - beforeInvoke() { - // dynamic init (tree shaking) - initI18nShowModalMsgsOnce(); - }, + const API_SAVE_VIDEO_TO_PHOTOS_ALBUM = 'saveVideoToPhotosAlbum'; + const SaveVideoToPhotosAlbumOptions = { formatArgs: { - title: '', - content: '', - showCancel: true, - cancelText(_value, params) { - if (!hasOwn$1(params, 'cancelText')) { - const { t } = useI18n(); - params.cancelText = t('uni.showModal.cancel'); - } - }, - cancelColor: '#000', - confirmText(_value, params) { - if (!hasOwn$1(params, 'confirmText')) { - const { t } = useI18n(); - params.confirmText = t('uni.showModal.confirm'); - } + filePath(filePath, params) { + params.filePath = getRealPath(filePath); }, - confirmColor: PRIMARY_COLOR, + }, + }; + const SaveVideoToPhotosAlbumProtocol = { + filePath: { + type: String, + required: true, }, }; - const API_SHOW_TOAST = 'showToast'; - const SHOW_TOAST_ICON = [ - 'success', - 'loading', - 'none', - 'error', - ]; - const ShowToastProtocol = { - title: String, - icon: String, - image: String, - duration: Number, - mask: Boolean, - }; - const ShowToastOptions = { + const API_GET_RECORDER_MANAGER = 'getRecorderManager'; + + const API_COMPRESS_IMAGE = 'compressImage'; + const CompressImageOptions = { formatArgs: { - title: '', - icon(type, params) { - params.icon = elemInArray(type, SHOW_TOAST_ICON); - }, - image(value, params) { - if (value) { - params.image = getRealPath(value); - } - else { - params.image = ''; - } + src(src, params) { + params.src = getRealPath(src); }, - duration: 1500, - mask: false, }, - }; - - const API_GET_PROVIDER = 'getProvider'; - const GetProviderProtocol = { - service: { + }; + const CompressImageProtocol = { + src: { type: String, required: true, }, }; - const API_LOGIN = 'login'; - const LoginProtocol = { - provider: String, - scopes: [String, Array], - timeout: Number, - univerifyStyle: Object, - }; - const API_GET_USER_INFO = 'getUserInfo'; - const GetUserInfoProtocol = { - provider: String, - withCredentials: Boolean, - timeout: Number, - lang: String, - }; - const API_GET_USER_PROFILE = 'ggetUserProfilegetUserProfile'; - const GgetUserProfileProtocol = { - provider: String, - withCredentials: Boolean, - timeout: Number, - lang: String, - }; - const API_PRE_LOGIN = 'preLogin'; - const provider = { - UNIVERIFY: 'univerify', - }; - const PreLoginOptions = { + const API_COMPRESS_VIDEO = 'compressVideo'; + const CompressVideoOptions = { formatArgs: { - provider(value, parmas) { - if (Object.values(provider).indexOf(String(value)) < 0) { - return 'provider error'; - } + src(src, params) { + params.src = getRealPath(src); }, }, }; - const PreLoginProtocol = { - provider: { + const CompressVideoProtocol = { + src: { type: String, required: true, }, - }; - const API_CLOSE_AUTH_VIEW = 'closeAuthView'; + quality: String, + bitrate: Number, + fps: Number, + resolution: Number, + }; - const API_SHREA = 'share'; - const SCENE = [ - 'WXSceneSession', - 'WXSenceTimeline', - 'WXSceneFavorite', - ]; - const SahreOptions = { - formatArgs: { - scene(value, params) { - if (params.provider === 'weixin' && (!value || !SCENE.includes(value))) { - return `分享到微信时,scene必须为以下其中一个:${SCENE.join('、')}`; + const API_REQUEST = 'request'; + const dataType = { + JSON: 'json', + }; + const RESPONSE_TYPE = ['text', 'arraybuffer']; + const DEFAULT_RESPONSE_TYPE = 'text'; + const encode$1 = encodeURIComponent; + function stringifyQuery(url, data) { + let str = url.split('#'); + const hash = str[1] || ''; + str = str[0].split('?'); + let query = str[1] || ''; + url = str[0]; + const search = query.split('&').filter((item) => item); + const params = {}; + search.forEach((item) => { + const part = item.split('='); + params[part[0]] = part[1]; + }); + for (const key in data) { + if (hasOwn$1(data, key)) { + let v = data[key]; + if (typeof v === 'undefined' || v === null) { + v = ''; } - }, - summary(value, params) { - if (params.type === 1 && !value) { - return '分享纯文本时,summary必填'; + else if (isPlainObject(v)) { + v = JSON.stringify(v); } + params[encode$1(key)] = encode$1(v); + } + } + query = Object.keys(params) + .map((item) => `${item}=${params[item]}`) + .join('&'); + return url + (query ? '?' + query : '') + (hash ? '#' + hash : ''); + } + const RequestProtocol = { + method: String, + data: [Object, String, Array, ArrayBuffer], + url: { + type: String, + required: true, + }, + header: Object, + dataType: String, + responseType: String, + withCredentials: Boolean, + }; + const RequestOptions = { + formatArgs: { + method(value, params) { + params.method = elemInArray((value || '').toUpperCase(), HTTP_METHODS); }, - href(value, params) { - if (params.type === 0 && !value) { - return '分享图文时,href必填'; - } + data(value, params) { + params.data = value || ''; }, - imageUrl(value, params) { - if ([0, 2, 5].includes(Number(params.type)) && !value) { - return '分享图文、纯图片、小程序时,imageUrl必填,推荐使用小于20Kb的图片'; + url(value, params) { + if (params.method === HTTP_METHODS[0] && + isPlainObject(params.data) && + Object.keys(params.data).length) { + // 将 method,data 校验提前,保证 url 校验时,method,data 已被格式化 + params.url = stringifyQuery(value, params.data); } }, - mediaUrl(value, params) { - if ([3, 4].includes(Number(params.type)) && !value) { - return '分享音乐、视频时,mediaUrl必填'; + header(value, params) { + const header = (params.header = value || {}); + if (params.method !== HTTP_METHODS[0]) { + if (!Object.keys(header).find((key) => key.toLowerCase() === 'content-type')) { + header['Content-Type'] = 'application/json'; + } } }, - miniProgram(value, params) { - if (params.type === 5 && !value) { - return '分享小程序时,miniProgram必填'; + dataType(value, params) { + params.dataType = (value || dataType.JSON).toLowerCase(); + }, + responseType(value, params) { + params.responseType = (value || '').toLowerCase(); + if (RESPONSE_TYPE.indexOf(params.responseType) === -1) { + params.responseType = DEFAULT_RESPONSE_TYPE; } }, }, - }; - const ShareProtocols = { - provider: { - type: String, - required: true, - }, - type: Number, - title: String, - scene: String, - summary: String, - href: String, - imageUrl: String, - mediaUrl: String, - miniProgram: Object, - }; - const API_SHARE_WITH_SYSTEM = 'shareWithSystem'; - const TYPE = [ - 'text', - 'image', - ]; - const ShareWithSystemOptions = { + }; + + const API_DOWNLOAD_FILE = 'downloadFile'; + const DownloadFileOptions = { formatArgs: { - type(value, params) { - if (value && !TYPE.includes(value)) - return '分享参数 type 不正确。只支持text、image'; - params.type = elemInArray(value, TYPE); + header(value, params) { + params.header = value || {}; }, }, }; - const ShareWithSystemProtocols = { - type: String, - summary: String, - href: String, - imageUrl: String, - }; - - const API_REQUEST_PAYMENT = 'requestPayment'; - const RequestPaymentProtocol = { - provider: { + const DownloadFileProtocol = { + url: { type: String, required: true, }, - orderInfo: { - type: [String, Object], - required: true, - }, - timeStamp: String, - nonceStr: String, - package: String, - signType: String, - paySign: String, - }; - - const API_CREATE_REWARDED_VIDEO_AD = 'createRewardedVideoAd'; - const CreateRewardedVideoAdOptions = { - formatArgs: { - adpid: '', - adUnitId: '', - }, - }; - const CreateRewardedVideoAdProtocol = { - adpid: String, - adUnitId: String, + header: Object, + timeout: Number, }; - const API_CREATE_FULL_SCREEN_VIDEO_AD = 'createFullScreenVideoAd'; - const CreateFullScreenVideoAdOptions = { + const API_UPLOAD_FILE = 'uploadFile'; + const UploadFileOptions = { formatArgs: { - adpid: '', + filePath(filePath, params) { + if (filePath) { + params.filePath = getRealPath(filePath); + } + }, + header(value, params) { + params.header = value || {}; + }, + formData(value, params) { + params.formData = value || {}; + }, }, }; - const CreateFullScreenVideoAdProtocol = { - adpid: String, - }; - - const API_CREATE_INTERSTITIAL_AD = 'createInterstitialAd'; - const CreateInterstitialAdOptions = { - formatArgs: { - adpid: '', - adUnitId: '', + const UploadFileProtocol = { + url: { + type: String, + required: true, }, - }; - const CreateInterstitialAdProtocol = { - adpid: String, - adUnitId: String, + files: Array, + filePath: String, + name: String, + header: Object, + formData: Object, + timeout: Number, }; - const API_CREATE_INTERACTIVE_AD = 'createInteractiveAd'; - const CreateInteractiveAdOptions = { + const API_CONNECT_SOCKET = 'connectSocket'; + const ConnectSocketOptions = { formatArgs: { - adpid(value, params) { - if (!value) { - return 'adpid should not be empty.'; - } - if (value) - params.adpid = value; + header(value, params) { + params.header = value || {}; }, - provider(value, params) { - if (!value) { - return 'provider should not be empty.'; + method(value, params) { + params.method = elemInArray((value || '').toUpperCase(), HTTP_METHODS); + }, + protocols(protocols, params) { + if (typeof protocols === 'string') { + params.protocols = [protocols]; } - if (value) - params.provider = value; }, }, }; - const CreateInteractiveAdProtocol = { - adpid: { + const ConnectSocketProtocol = { + url: { type: String, required: true, }, - provider: { - type: String, - required: true, + header: { + type: Object, }, + method: String, + protocols: [Array, String], + }; + const API_SEND_SOCKET_MESSAGE = 'sendSocketMessage'; + const SendSocketMessageProtocol = { + data: [String, ArrayBuffer], + }; + const API_CLOSE_SOCKET = 'closeSocket'; + const CloseSocketProtocol = { + code: Number, + reason: String, }; - function warpPlusSuccessCallback(resolve, after) { - return function successCallback(data) { - delete data.code; - delete data.message; - if (typeof after === 'function') { - data = after(data); - } - resolve(data); + function encodeQueryString(url) { + if (typeof url !== 'string') { + return url; + } + const index = url.indexOf('?'); + if (index === -1) { + return url; + } + const query = url + .substr(index + 1) + .trim() + .replace(/^(\?|#|&)/, ''); + if (!query) { + return url; + } + url = url.substr(0, index); + const params = []; + query.split('&').forEach((param) => { + const parts = param.replace(/\+/g, ' ').split('='); + const key = parts.shift(); + const val = parts.length > 0 ? parts.join('=') : ''; + params.push(key + '=' + encodeURIComponent(val)); + }); + return params.length ? url + '?' + params.join('&') : url; + } + + const ANIMATION_IN = [ + 'slide-in-right', + 'slide-in-left', + 'slide-in-top', + 'slide-in-bottom', + 'fade-in', + 'zoom-out', + 'zoom-fade-out', + 'pop-in', + 'none', + ]; + const ANIMATION_OUT = [ + 'slide-out-right', + 'slide-out-left', + 'slide-out-top', + 'slide-out-bottom', + 'fade-out', + 'zoom-in', + 'zoom-fade-in', + 'pop-out', + 'none', + ]; + const BaseRouteProtocol = { + url: { + type: String, + required: true, + }, + }; + const API_NAVIGATE_TO = 'navigateTo'; + const API_REDIRECT_TO = 'redirectTo'; + const API_SWITCH_TAB = 'switchTab'; + const API_NAVIGATE_BACK = 'navigateBack'; + const API_PRELOAD_PAGE = 'preloadPage'; + const API_UN_PRELOAD_PAGE = 'unPreloadPage'; + const NavigateToProtocol = + /*#__PURE__*/ extend({}, BaseRouteProtocol, createAnimationProtocol(ANIMATION_IN)); + const NavigateBackProtocol = + /*#__PURE__*/ extend({ + delta: { + type: Number, + }, + }, createAnimationProtocol(ANIMATION_OUT)); + const NavigateToOptions = + /*#__PURE__*/ createRouteOptions(API_NAVIGATE_TO); + const NavigateBackOptions = { + formatArgs: { + delta(value, params) { + value = parseInt(value + '') || 1; + params.delta = Math.min(getCurrentPages().length - 1, value); + }, + }, + }; + function createAnimationProtocol(animationTypes) { + return { + animationType: { + type: String, + validator(type) { + if (type && animationTypes.indexOf(type) === -1) { + return ('`' + + type + + '` is not supported for `animationType` (supported values are: `' + + animationTypes.join('`|`') + + '`)'); + } + }, + }, + animationDuration: { + type: Number, + }, }; } - function warpPlusErrorCallback(reject, errMsg) { - return function errorCallback(error) { - error = error || {}; - // 一键登录errorCallback新增 appid、metadata、uid 参数返回 - errMsg = error.message || errMsg || ''; - delete error.message; - reject(errMsg, extend({ code: 0 }, error)); - }; + let navigatorLock; + function beforeRoute() { + navigatorLock = ''; } - function warpPlusEvent(plusObject, event) { - return function () { - const object = plusObject(); - object(function (data) { - if (data) { - delete data.code; - delete data.message; - } - UniServiceJSBridge.invokeOnCallback(event, data); - }); + function createRouteOptions(type) { + return { + formatArgs: { + url: createNormalizeUrl(type), + }, + beforeAll: beforeRoute, }; } - function warpPlusMethod(plusObject, before, after) { - return function (options, { resolve, reject }) { - const object = plusObject(); - object(extend({}, typeof before === 'function' ? before(options) : options, { - success: warpPlusSuccessCallback(resolve, after), - fail: warpPlusErrorCallback(reject), - })); - }; - } - - const STORAGE_DATA_TYPE = '__TYPE'; - const STORAGE_KEYS = 'uni-storage-keys'; - function parseValue(value) { - const types = ['object', 'string', 'number', 'boolean', 'undefined']; - try { - const object = typeof value === 'string' ? JSON.parse(value) : value; - const type = object.type; - if (types.indexOf(type) >= 0) { - const keys = Object.keys(object); - if (keys.length === 2 && 'data' in object) { - // eslint-disable-next-line valid-typeof - if (typeof object.data === type) { - return object.data; - } - // eslint-disable-next-line no-useless-escape - if (type === 'object' && - /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{3}Z$/.test(object.data)) { - // ISO 8601 格式返回 Date - return new Date(object.data); - } - } - else if (keys.length === 1) { - return ''; + function createNormalizeUrl(type) { + return function normalizeUrl(url, params) { + if (!url) { + return `Missing required args: "url"`; + } + // 格式化为绝对路径路由 + url = normalizeRoute(url); + const pagePath = url.split('?')[0]; + // 匹配路由是否存在 + const routeOptions = getRouteOptions(pagePath, true); + if (!routeOptions) { + return 'page `' + url + '` is not found'; + } + // 检测不同类型跳转 + if (type === API_NAVIGATE_TO || type === API_REDIRECT_TO) { + if (routeOptions.meta.isTabBar) { + return `can not ${type} a tabbar page`; } } - } - catch (error) { } - } - const setStorageSync = defineSyncApi(API_SET_STORAGE_SYNC, (key, data) => { - const type = typeof data; - const value = type === 'string' - ? data - : JSON.stringify({ - type, - data: data, - }); - try { - if (type === 'string' && parseValue(value) !== undefined) { - plus.storage.setItem(key + STORAGE_DATA_TYPE, type); + else if (type === API_SWITCH_TAB) { + if (!routeOptions.meta.isTabBar) { + return 'can not switch to no-tabBar page'; + } } - else { - plus.storage.removeItem(key + STORAGE_DATA_TYPE); + // switchTab不允许传递参数,reLaunch到一个tabBar页面是可以的 + if ((type === API_SWITCH_TAB || type === API_PRELOAD_PAGE) && + routeOptions.meta.isTabBar && + params.openType !== 'appLaunch') { + url = pagePath; } - plus.storage.setItem(key, value); - } - catch (error) { } - }, SetStorageSyncProtocol); - const setStorage = defineAsyncApi(API_SET_STORAGE, ({ key, data }, { resolve, reject }) => { - const type = typeof data; - const value = type === 'string' - ? data - : JSON.stringify({ - type, - data: data, - }); - try { - const storage = plus.storage; - if (type === 'string' && parseValue(value) !== undefined) { - storage.setItemAsync(key + STORAGE_DATA_TYPE, type); + // 首页自动格式化为`/` + if (routeOptions.meta.isEntry) { + url = url.replace(routeOptions.alias, '/'); } - else { - storage.removeItemAsync(key + STORAGE_DATA_TYPE); + // 参数格式化 + params.url = encodeQueryString(url); + if (type === API_UN_PRELOAD_PAGE) { + return; } - storage.setItemAsync(key, value, resolve, warpPlusErrorCallback(reject)); - } - catch (error) { - reject(error.message); - } - }, SetStorageProtocol); - function parseGetStorage(type, value) { - let data = value; - if (type !== 'string' || - (type === 'string' && value === '{"type":"undefined"}')) { - try { - // 兼容H5和V3初期历史格式 - let object = JSON.parse(value); - const result = parseValue(object); - if (result !== undefined) { - data = result; + else if (type === API_PRELOAD_PAGE) { + { + if (!routeOptions.meta.isNVue) { + return 'can not preload vue page'; + } } - else if (type) { - // 兼容App端历史格式 - data = object; - if (typeof object === 'string') { - object = JSON.parse(object); - const objectType = typeof object; - if (objectType === 'number' && type === 'date') { - data = new Date(object); - } - else if (objectType === - (['null', 'array'].indexOf(type) < 0 ? type : 'object')) { - data = object; - } + if (routeOptions.meta.isTabBar) { + const pages = getCurrentPages(); + const tabBarPagePath = routeOptions.path.substr(1); + if (pages.find((page) => page.route === tabBarPagePath)) { + return 'tabBar page `' + tabBarPagePath + '` already exists'; } } + return; } - catch (error) { } - } - return data; - } - const getStorageSync = defineSyncApi(API_GET_STORAGE_SYNC, (key, t) => { - const value = plus.storage.getItem(key); - const typeOrigin = plus.storage.getItem(key + STORAGE_DATA_TYPE) || ''; - const type = typeOrigin.toLowerCase(); - if (typeof value !== 'string') { - return ''; - } - return parseGetStorage(type, value); - }, GetStorageSyncProtocol); - const getStorage = defineAsyncApi(API_GET_STORAGE, ({ key }, { resolve, reject }) => { - const storage = plus.storage; - storage.getItemAsync(key, function (res) { - storage.getItemAsync(key + STORAGE_DATA_TYPE, function (typeRes) { - const typeOrigin = typeRes.data || ''; - const type = typeOrigin.toLowerCase(); - resolve({ - data: parseGetStorage(type, res.data), - }); - }, function () { - const type = ''; - resolve({ - data: parseGetStorage(type, res.data), - }); - }); - }, warpPlusErrorCallback(reject)); - }, GetStorageProtocol); - const removeStorageSync = defineSyncApi(API_REMOVE_STORAGE, (key) => { - plus.storage.removeItem(key + STORAGE_DATA_TYPE); - plus.storage.removeItem(key); - }, RemoveStorageSyncProtocol); - const removeStorage = defineAsyncApi(API_REMOVE_STORAGE, ({ key }, { resolve, reject }) => { - // 兼容App端历史格式 - plus.storage.removeItemAsync(key + STORAGE_DATA_TYPE); - plus.storage.removeItemAsync(key, resolve, warpPlusErrorCallback(reject)); - }, RemoveStorageProtocol); - const clearStorageSync = (defineSyncApi('clearStorageSync', () => { - plus.storage.clear(); - })); - const clearStorage = (defineAsyncApi('clearStorage', (_, { resolve, reject }) => { - plus.storage.clearAsync(resolve, warpPlusErrorCallback(reject)); - })); - const getStorageInfoSync = (defineSyncApi('getStorageInfoSync', () => { - const length = plus.storage.getLength() || 0; - const keys = []; - let currentSize = 0; - for (let index = 0; index < length; index++) { - const key = plus.storage.key(index); - if (key !== STORAGE_KEYS && - (key.indexOf(STORAGE_DATA_TYPE) < 0 || - key.indexOf(STORAGE_DATA_TYPE) + STORAGE_DATA_TYPE.length !== - key.length)) { - const value = plus.storage.getItem(key); - currentSize += key.length + value.length; - keys.push(key); + // 主要拦截目标为用户快速点击时触发的多次跳转,该情况,通常前后 url 是一样的 + if (navigatorLock === url && params.openType !== 'appLaunch') { + return `${navigatorLock} locked`; + } + // 至少 onLaunch 之后,再启用lock逻辑(onLaunch之前可能开发者手动调用路由API,来提前跳转) + // enableNavigatorLock 临时开关(不对外开放),避免该功能上线后,有部分情况异常,可以让开发者临时关闭 lock 功能 + if (__uniConfig.ready) { + navigatorLock = url; } - } - return { - keys, - currentSize: Math.ceil((currentSize * 2) / 1024), - limitSize: Number.MAX_VALUE, }; - })); - const getStorageInfo = (defineAsyncApi('getStorageInfo', (_, { resolve }) => { - resolve(getStorageInfoSync()); - })); + } - const getFileInfo$1 = defineAsyncApi(API_GET_FILE_INFO, (options, { resolve, reject }) => { - plus.io.getFileInfo(extend(options, { - success: warpPlusSuccessCallback(resolve), - fail: warpPlusErrorCallback(reject), - })); - }, GetFileInfoProtocol, GetFileInfoOptions); + const API_HIDE_LOADING = 'hideLoading'; - const openDocument = defineAsyncApi(API_OPEN_DOCUMENT, ({ filePath, fileType }, { resolve, reject }) => { - plus.io.resolveLocalFileSystemURL(getRealPath(filePath), (entry) => { - plus.runtime.openFile(getRealPath(filePath)); - resolve(); - }, (err) => { - reject('openDocument:fail ' + err.message); - }); - }, OpenDocumentProtocol, OpenDocumentOptions); + const API_HIDE_TOAST = 'hideToast'; - const DEVICE_FREQUENCY = 200; - const NETWORK_TYPES = [ - 'unknown', - 'none', - 'ethernet', - 'wifi', - '2g', - '3g', - '4g', - '5g', - ]; - const TEMP_PATH_BASE = '_doc/uniapp_temp'; - const TEMP_PATH = `${TEMP_PATH_BASE}_${Date.now()}`; + const API_SHOW_ACTION_SHEET = 'showActionSheet'; + const ShowActionSheetProtocol = { + itemList: { + type: Array, + required: true, + }, + title: String, + alertText: String, + itemColor: String, + popover: Object, + }; + const ShowActionSheetOptions = { + formatArgs: { + itemColor: '#000', + }, + }; - let listener$1 = null; - const onCompassChange = (defineOnApi(API_ON_COMPASS, () => { - startCompass(); - })); - const offCompassChange = (defineOnApi(API_OFF_COMPASS, () => { - stopCompass(); - })); - const startCompass = (defineAsyncApi(API_START_COMPASS, (_, { resolve, reject }) => { - if (!listener$1) { - plus.orientation.watchOrientation((res) => { - UniServiceJSBridge.invokeOnCallback(API_ON_COMPASS, { - direction: res.magneticHeading, - }); - }, (err) => { - reject(err.message); - listener$1 = null; - }, { - frequency: DEVICE_FREQUENCY, - }); - } - setTimeout(resolve, DEVICE_FREQUENCY); - })); - const stopCompass = (defineAsyncApi(API_STOP_COMPASS, (_, { resolve }) => { - if (listener$1) { - plus.orientation.clearWatch(listener$1); - listener$1 = null; - } - resolve(); - })); - - const vibrateShort = defineAsyncApi(API_VIBRATE_SHORT, (_, { resolve }) => { - plus.device.vibrate(15); - resolve(); - }); - const vibrateLong = defineAsyncApi(API_VIBRATE_LONG, (_, { resolve }) => { - plus.device.vibrate(400); - resolve(); - }); + const API_SHOW_LOADING = 'showLoading'; + const ShowLoadingProtocol = { + title: String, + mask: Boolean, + }; + const ShowLoadingOptions = { + formatArgs: { + title: '', + mask: false, + }, + }; - let listener = null; - const onAccelerometerChange = (defineOnApi(API_ON_ACCELEROMETER, () => { - startAccelerometer(); - })); - const offAccelerometerChange = (defineOnApi(API_OFF_ACCELEROMETER, () => { - stopAccelerometer(); - })); - const startAccelerometer = (defineAsyncApi(API_START_ACCELEROMETER, (_, { resolve, reject }) => { - if (!listener) { - listener = plus.accelerometer.watchAcceleration((res) => { - UniServiceJSBridge.invokeOnCallback(API_ON_ACCELEROMETER, { - x: (res && res.xAxis) || 0, - y: (res && res.yAxis) || 0, - z: (res && res.zAxis) || 0, - }); - }, (err) => { - listener = null; - reject(`startAccelerometer:fail ${err.message}`); - }, { - frequency: DEVICE_FREQUENCY, - }); - } - setTimeout(resolve, DEVICE_FREQUENCY); - })); - const stopAccelerometer = (defineAsyncApi(API_STOP_ACCELEROMETER, (_, { resolve }) => { - if (listener) { - plus.accelerometer.clearWatch(listener); - listener = null; - } - resolve(); - })); + const API_SHOW_MODAL = 'showModal'; + const ShowModalProtocol = { + title: String, + content: String, + showCancel: Boolean, + cancelText: String, + cancelColor: String, + confirmText: String, + confirmColor: String, + }; + const ShowModalOptions = { + beforeInvoke() { + // dynamic init (tree shaking) + initI18nShowModalMsgsOnce(); + }, + formatArgs: { + title: '', + content: '', + showCancel: true, + cancelText(_value, params) { + if (!hasOwn$1(params, 'cancelText')) { + const { t } = useI18n(); + params.cancelText = t('uni.showModal.cancel'); + } + }, + cancelColor: '#000', + confirmText(_value, params) { + if (!hasOwn$1(params, 'confirmText')) { + const { t } = useI18n(); + params.confirmText = t('uni.showModal.confirm'); + } + }, + confirmColor: PRIMARY_COLOR, + }, + }; - const onBluetoothDeviceFound = defineOnApi(API_ON_BLUETOOTH_DEVICE_FOUND, warpPlusEvent(() => plus.bluetooth.onBluetoothDeviceFound, API_ON_BLUETOOTH_DEVICE_FOUND)); - const onBluetoothAdapterStateChange = defineOnApi(API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBluetoothAdapterStateChange, API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE)); - const onBLEConnectionStateChange = defineOnApi(API_ON_BLE_CONNECTION_STATE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBLEConnectionStateChange, API_ON_BLE_CONNECTION_STATE_CHANGE)); - const onBLECharacteristicValueChange = defineOnApi(API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBLECharacteristicValueChange, API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE)); - const openBluetoothAdapter = defineAsyncApi('openBluetoothAdapter', warpPlusMethod(() => plus.bluetooth.openBluetoothAdapter)); - const closeBluetoothAdapter = defineAsyncApi('closeBluetoothAdapter', warpPlusMethod(() => plus.bluetooth.closeBluetoothAdapter)); - const getBluetoothAdapterState = defineAsyncApi('getBluetoothAdapterState', warpPlusMethod(() => plus.bluetooth.getBluetoothAdapterState)); - const startBluetoothDevicesDiscovery = defineAsyncApi(API_START_BLUETOOTH_DEVICES_DISCOVERY, warpPlusMethod(() => plus.bluetooth.startBluetoothDevicesDiscovery), StartBluetoothDevicesDiscoveryProtocol); - const stopBluetoothDevicesDiscovery = defineAsyncApi('stopBluetoothDevicesDiscovery', warpPlusMethod(() => plus.bluetooth.stopBluetoothDevicesDiscovery)); - const getBluetoothDevices = defineAsyncApi('getBluetoothDevices', warpPlusMethod(() => plus.bluetooth.getBluetoothDevices)); - const getConnectedBluetoothDevices = defineAsyncApi(API_GET_CONNECTED_BLUETOOTH_DEVICES, warpPlusMethod(() => plus.bluetooth.getConnectedBluetoothDevices), GetConnectedBluetoothDevicesProtocol); - const createBLEConnection = defineAsyncApi(API_CREATE_BLE_CONNECTION, warpPlusMethod(() => plus.bluetooth.createBLEConnection), CreateBLEConnectionProtocol); - const closeBLEConnection = defineAsyncApi(API_CLOSE_BLE_CONNECTION, warpPlusMethod(() => plus.bluetooth.closeBLEConnection), CloseBLEConnectionProtocol); - const getBLEDeviceServices = defineAsyncApi(API_GET_BLE_DEVICE_SERVICES, warpPlusMethod(() => plus.bluetooth.getBLEDeviceServices), GetBLEDeviceServicesProtocol); - const getBLEDeviceCharacteristics = defineAsyncApi(API_GET_BLE_DEVICE_CHARACTERISTICS, warpPlusMethod(() => plus.bluetooth.getBLEDeviceCharacteristics), GetBLEDeviceCharacteristicsProtocol); - const notifyBLECharacteristicValueChange = defineAsyncApi(API_NOTIFY_BLE_CHARACTERISTIC_VALUE_CHANGE, warpPlusMethod(() => plus.bluetooth.notifyBLECharacteristicValueChange), NotifyBLECharacteristicValueChangeProtocol); - const readBLECharacteristicValue = defineAsyncApi(API_READ_BLE_CHARACTERISTIC_VALUE, warpPlusMethod(() => plus.bluetooth.readBLECharacteristicValue), ReadBLECharacteristicValueProtocol); - const writeBLECharacteristicValue = defineAsyncApi(API_WRITE_BLE_CHARACTERISTIC_VALUE, warpPlusMethod(() => plus.bluetooth.writeBLECharacteristicValue), WriteBLECharacteristicValueProtocol); - const setBLEMTU = defineAsyncApi(API_SET_BLE_MTU, warpPlusMethod(() => plus.bluetooth.setBLEMTU), SetBLEMTUProtocol); - const getBLEDeviceRSSI = defineAsyncApi(API_GET_BLE_DEVICE_RSSI, warpPlusMethod(() => plus.bluetooth.getBLEDeviceRSSI), GetBLEDeviceRSSIProtocol); + const API_SHOW_TOAST = 'showToast'; + const SHOW_TOAST_ICON = [ + 'success', + 'loading', + 'none', + 'error', + ]; + const ShowToastProtocol = { + title: String, + icon: String, + image: String, + duration: Number, + mask: Boolean, + }; + const ShowToastOptions = { + formatArgs: { + title: '', + icon(type, params) { + params.icon = elemInArray(type, SHOW_TOAST_ICON); + }, + image(value, params) { + if (value) { + params.image = getRealPath(value); + } + else { + params.image = ''; + } + }, + duration: 1500, + mask: false, + }, + }; - const onBeaconUpdate = defineOnApi(API_ON_BEACON_UPDATE, warpPlusEvent(() => plus.ibeacon.onBeaconUpdate, API_ON_BEACON_UPDATE)); - const onBeaconServiceChange = defineOnApi(API_ON_BEACON_SERVICE_CHANGE, warpPlusEvent(() => plus.ibeacon.onBeaconServiceChange, API_ON_BEACON_SERVICE_CHANGE)); - const getBeacons = defineAsyncApi(API_GET_BEACONS, warpPlusMethod(() => plus.ibeacon.getBeacons)); - const startBeaconDiscovery = defineAsyncApi(API_START_BEACON_DISCOVERY, warpPlusMethod(() => plus.ibeacon.startBeaconDiscovery), StartBeaconDiscoveryProtocol); - const stopBeaconDiscovery = defineAsyncApi(API_STOP_BEACON_DISCOVERY, warpPlusMethod(() => plus.ibeacon.stopBeaconDiscovery)); + const API_GET_PROVIDER = 'getProvider'; + const GetProviderProtocol = { + service: { + type: String, + required: true, + }, + }; - const makePhoneCall = defineAsyncApi(API_MAKE_PHONE_CALL, ({ phoneNumber }, { resolve }) => { - plus.device.dial(phoneNumber); - return resolve(); - }, MakePhoneCallProtocol); + const API_LOGIN = 'login'; + const LoginProtocol = { + provider: String, + scopes: [String, Array], + timeout: Number, + univerifyStyle: Object, + }; + const API_GET_USER_INFO = 'getUserInfo'; + const GetUserInfoProtocol = { + provider: String, + withCredentials: Boolean, + timeout: Number, + lang: String, + }; + const API_GET_USER_PROFILE = 'ggetUserProfilegetUserProfile'; + const GgetUserProfileProtocol = { + provider: String, + withCredentials: Boolean, + timeout: Number, + lang: String, + }; + const API_PRE_LOGIN = 'preLogin'; + const provider = { + UNIVERIFY: 'univerify', + }; + const PreLoginOptions = { + formatArgs: { + provider(value, parmas) { + if (Object.values(provider).indexOf(String(value)) < 0) { + return 'provider error'; + } + }, + }, + }; + const PreLoginProtocol = { + provider: { + type: String, + required: true, + }, + }; + const API_CLOSE_AUTH_VIEW = 'closeAuthView'; - const addPhoneContact = defineAsyncApi(API_ADD_PHONE_CONTACT, ({ photoFilePath = '', nickName, lastName, middleName, firstName, remark, mobilePhoneNumber, weChatNumber, addressCountry, addressState, addressCity, addressStreet, addressPostalCode, organization, title, workFaxNumber, workPhoneNumber, hostNumber, email, url, workAddressCountry, workAddressState, workAddressCity, workAddressStreet, workAddressPostalCode, homeFaxNumber, homePhoneNumber, homeAddressCountry, homeAddressState, homeAddressCity, homeAddressStreet, homeAddressPostalCode, }, { resolve, reject }) => { - plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, (addressbook) => { - const contact = addressbook.create(); - const name = {}; - if (lastName) { - name.familyName = lastName; - } - if (firstName) { - name.givenName = firstName; - } - if (middleName) { - name.middleName = middleName; - } - contact.name = name; - if (nickName) { - contact.nickname = nickName; - } - if (photoFilePath) { - contact.photos = [ - { - type: 'url', - value: photoFilePath, - }, - ]; - } - if (remark) { - contact.note = remark; - } - const mobilePhone = { - type: 'mobile', - }; - const workPhone = { - type: 'work', - }; - const companyPhone = { - type: 'company', - }; - const homeFax = { - type: 'home fax', - }; - const workFax = { - type: 'work fax', - }; - if (mobilePhoneNumber) { - mobilePhone.value = mobilePhoneNumber; - } - if (workPhoneNumber) { - workPhone.value = workPhoneNumber; + const API_SHREA = 'share'; + const SCENE = [ + 'WXSceneSession', + 'WXSenceTimeline', + 'WXSceneFavorite', + ]; + const SahreOptions = { + formatArgs: { + scene(value, params) { + if (params.provider === 'weixin' && (!value || !SCENE.includes(value))) { + return `分享到微信时,scene必须为以下其中一个:${SCENE.join('、')}`; + } + }, + summary(value, params) { + if (params.type === 1 && !value) { + return '分享纯文本时,summary必填'; + } + }, + href(value, params) { + if (params.type === 0 && !value) { + return '分享图文时,href必填'; + } + }, + imageUrl(value, params) { + if ([0, 2, 5].includes(Number(params.type)) && !value) { + return '分享图文、纯图片、小程序时,imageUrl必填,推荐使用小于20Kb的图片'; + } + }, + mediaUrl(value, params) { + if ([3, 4].includes(Number(params.type)) && !value) { + return '分享音乐、视频时,mediaUrl必填'; + } + }, + miniProgram(value, params) { + if (params.type === 5 && !value) { + return '分享小程序时,miniProgram必填'; + } + }, + }, + }; + const ShareProtocols = { + provider: { + type: String, + required: true, + }, + type: Number, + title: String, + scene: String, + summary: String, + href: String, + imageUrl: String, + mediaUrl: String, + miniProgram: Object, + }; + const API_SHARE_WITH_SYSTEM = 'shareWithSystem'; + const TYPE$2 = [ + 'text', + 'image', + ]; + const ShareWithSystemOptions = { + formatArgs: { + type(value, params) { + if (value && !TYPE$2.includes(value)) + return '分享参数 type 不正确。只支持text、image'; + params.type = elemInArray(value, TYPE$2); + }, + }, + }; + const ShareWithSystemProtocols = { + type: String, + summary: String, + href: String, + imageUrl: String, + }; + + const API_REQUEST_PAYMENT = 'requestPayment'; + const RequestPaymentProtocol = { + provider: { + type: String, + required: true, + }, + orderInfo: { + type: [String, Object], + required: true, + }, + timeStamp: String, + nonceStr: String, + package: String, + signType: String, + paySign: String, + }; + + const API_CREATE_REWARDED_VIDEO_AD = 'createRewardedVideoAd'; + const CreateRewardedVideoAdOptions = { + formatArgs: { + adpid: '', + adUnitId: '', + }, + }; + const CreateRewardedVideoAdProtocol = { + adpid: String, + adUnitId: String, + }; + + const API_CREATE_FULL_SCREEN_VIDEO_AD = 'createFullScreenVideoAd'; + const CreateFullScreenVideoAdOptions = { + formatArgs: { + adpid: '', + }, + }; + const CreateFullScreenVideoAdProtocol = { + adpid: String, + }; + + const API_CREATE_INTERSTITIAL_AD = 'createInterstitialAd'; + const CreateInterstitialAdOptions = { + formatArgs: { + adpid: '', + adUnitId: '', + }, + }; + const CreateInterstitialAdProtocol = { + adpid: String, + adUnitId: String, + }; + + const API_CREATE_INTERACTIVE_AD = 'createInteractiveAd'; + const CreateInteractiveAdOptions = { + formatArgs: { + adpid(value, params) { + if (!value) { + return 'adpid should not be empty.'; + } + if (value) + params.adpid = value; + }, + provider(value, params) { + if (!value) { + return 'provider should not be empty.'; + } + if (value) + params.provider = value; + }, + }, + }; + const CreateInteractiveAdProtocol = { + adpid: { + type: String, + required: true, + }, + provider: { + type: String, + required: true, + }, + }; + + function warpPlusSuccessCallback(resolve, after) { + return function successCallback(data) { + delete data.code; + delete data.message; + if (typeof after === 'function') { + data = after(data); } - if (hostNumber) { - companyPhone.value = hostNumber; + resolve(data); + }; + } + function warpPlusErrorCallback(reject, errMsg) { + return function errorCallback(error) { + error = error || {}; + // 一键登录errorCallback新增 appid、metadata、uid 参数返回 + errMsg = error.message || errMsg || ''; + delete error.message; + reject(errMsg, extend({ code: 0 }, error)); + }; + } + function warpPlusEvent(plusObject, event) { + return function () { + const object = plusObject(); + object(function (data) { + if (data) { + delete data.code; + delete data.message; + } + UniServiceJSBridge.invokeOnCallback(event, data); + }); + }; + } + function warpPlusMethod(plusObject, before, after) { + return function (options, { resolve, reject }) { + const object = plusObject(); + object(extend({}, typeof before === 'function' ? before(options) : options, { + success: warpPlusSuccessCallback(resolve, after), + fail: warpPlusErrorCallback(reject), + })); + }; + } + + const STORAGE_DATA_TYPE = '__TYPE'; + const STORAGE_KEYS = 'uni-storage-keys'; + function parseValue(value) { + const types = ['object', 'string', 'number', 'boolean', 'undefined']; + try { + const object = typeof value === 'string' ? JSON.parse(value) : value; + const type = object.type; + if (types.indexOf(type) >= 0) { + const keys = Object.keys(object); + if (keys.length === 2 && 'data' in object) { + // eslint-disable-next-line valid-typeof + if (typeof object.data === type) { + return object.data; + } + // eslint-disable-next-line no-useless-escape + if (type === 'object' && + /^\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{3}Z$/.test(object.data)) { + // ISO 8601 格式返回 Date + return new Date(object.data); + } + } + else if (keys.length === 1) { + return ''; + } } - if (homeFaxNumber) { - homeFax.value = homeFaxNumber; + } + catch (error) { } + } + const setStorageSync = defineSyncApi(API_SET_STORAGE_SYNC, (key, data) => { + const type = typeof data; + const value = type === 'string' + ? data + : JSON.stringify({ + type, + data: data, + }); + try { + if (type === 'string' && parseValue(value) !== undefined) { + plus.storage.setItem(key + STORAGE_DATA_TYPE, type); } - if (workFaxNumber) { - workFax.value = workFaxNumber; + else { + plus.storage.removeItem(key + STORAGE_DATA_TYPE); } - contact.phoneNumbers = [ - mobilePhone, - workPhone, - companyPhone, - homeFax, - workFax, - ]; - if (email) { - contact.emails = [ - { - type: 'home', - value: email, - }, - ]; - } - if (url) { - contact.urls = [ - { - type: 'other', - value: url, - }, - ]; - } - if (weChatNumber) { - contact.ims = [ - { - type: 'other', - value: weChatNumber, - }, - ]; - } - const defaultAddress = { - type: 'other', - preferred: true, - }; - const homeAddress = { - type: 'home', - }; - const companyAddress = { - type: 'company', - }; - if (addressCountry) { - defaultAddress.country = addressCountry; - } - if (addressState) { - defaultAddress.region = addressState; - } - if (addressCity) { - defaultAddress.locality = addressCity; - } - if (addressStreet) { - defaultAddress.streetAddress = addressStreet; - } - if (addressPostalCode) { - defaultAddress.postalCode = addressPostalCode; - } - if (homeAddressCountry) { - homeAddress.country = homeAddressCountry; - } - if (homeAddressState) { - homeAddress.region = homeAddressState; - } - if (homeAddressCity) { - homeAddress.locality = homeAddressCity; - } - if (homeAddressStreet) { - homeAddress.streetAddress = homeAddressStreet; - } - if (homeAddressPostalCode) { - homeAddress.postalCode = homeAddressPostalCode; - } - if (workAddressCountry) { - companyAddress.country = workAddressCountry; - } - if (workAddressState) { - companyAddress.region = workAddressState; - } - if (workAddressCity) { - companyAddress.locality = workAddressCity; + plus.storage.setItem(key, value); + } + catch (error) { } + }, SetStorageSyncProtocol); + const setStorage = defineAsyncApi(API_SET_STORAGE, ({ key, data }, { resolve, reject }) => { + const type = typeof data; + const value = type === 'string' + ? data + : JSON.stringify({ + type, + data: data, + }); + try { + const storage = plus.storage; + if (type === 'string' && parseValue(value) !== undefined) { + storage.setItemAsync(key + STORAGE_DATA_TYPE, type); } - if (workAddressStreet) { - companyAddress.streetAddress = workAddressStreet; + else { + storage.removeItemAsync(key + STORAGE_DATA_TYPE); } - if (workAddressPostalCode) { - companyAddress.postalCode = workAddressPostalCode; + storage.setItemAsync(key, value, resolve, warpPlusErrorCallback(reject)); + } + catch (error) { + reject(error.message); + } + }, SetStorageProtocol); + function parseGetStorage(type, value) { + let data = value; + if (type !== 'string' || + (type === 'string' && value === '{"type":"undefined"}')) { + try { + // 兼容H5和V3初期历史格式 + let object = JSON.parse(value); + const result = parseValue(object); + if (result !== undefined) { + data = result; + } + else if (type) { + // 兼容App端历史格式 + data = object; + if (typeof object === 'string') { + object = JSON.parse(object); + const objectType = typeof object; + if (objectType === 'number' && type === 'date') { + data = new Date(object); + } + else if (objectType === + (['null', 'array'].indexOf(type) < 0 ? type : 'object')) { + data = object; + } + } + } } - contact.addresses = [ - defaultAddress, - homeAddress, - companyAddress, - ]; - contact.save(() => { + catch (error) { } + } + return data; + } + const getStorageSync = defineSyncApi(API_GET_STORAGE_SYNC, (key, t) => { + const value = plus.storage.getItem(key); + const typeOrigin = plus.storage.getItem(key + STORAGE_DATA_TYPE) || ''; + const type = typeOrigin.toLowerCase(); + if (typeof value !== 'string') { + return ''; + } + return parseGetStorage(type, value); + }, GetStorageSyncProtocol); + const getStorage = defineAsyncApi(API_GET_STORAGE, ({ key }, { resolve, reject }) => { + const storage = plus.storage; + storage.getItemAsync(key, function (res) { + storage.getItemAsync(key + STORAGE_DATA_TYPE, function (typeRes) { + const typeOrigin = typeRes.data || ''; + const type = typeOrigin.toLowerCase(); resolve({ - errMsg: 'addPhoneContact:ok', + data: parseGetStorage(type, res.data), }); - }, (e) => { - reject('addPhoneContact:fail'); - }); - }, (e) => { - reject('addPhoneContact:fail'); - }); - }, MakePhoneCallProtocol); - - function requireNativePlugin(pluginName) { - /* eslint-disable no-undef */ - if (typeof weex !== 'undefined') { - return weex.requireModule(pluginName); - } - /* eslint-disable no-undef */ - return __requireNativePlugin__(pluginName); - } - - const getClipboardData = defineAsyncApi(API_GET_CLIPBOARD_DATA, (_, { resolve, reject }) => { - const clipboard = requireNativePlugin('clipboard'); - clipboard.getString((ret) => { - if (ret.result === 'success') { + }, function () { + const type = ''; resolve({ - data: ret.data, + data: parseGetStorage(type, res.data), }); + }); + }, warpPlusErrorCallback(reject)); + }, GetStorageProtocol); + const removeStorageSync = defineSyncApi(API_REMOVE_STORAGE, (key) => { + plus.storage.removeItem(key + STORAGE_DATA_TYPE); + plus.storage.removeItem(key); + }, RemoveStorageSyncProtocol); + const removeStorage = defineAsyncApi(API_REMOVE_STORAGE, ({ key }, { resolve, reject }) => { + // 兼容App端历史格式 + plus.storage.removeItemAsync(key + STORAGE_DATA_TYPE); + plus.storage.removeItemAsync(key, resolve, warpPlusErrorCallback(reject)); + }, RemoveStorageProtocol); + const clearStorageSync = (defineSyncApi('clearStorageSync', () => { + plus.storage.clear(); + })); + const clearStorage = (defineAsyncApi('clearStorage', (_, { resolve, reject }) => { + plus.storage.clearAsync(resolve, warpPlusErrorCallback(reject)); + })); + const getStorageInfoSync = (defineSyncApi('getStorageInfoSync', () => { + const length = plus.storage.getLength() || 0; + const keys = []; + let currentSize = 0; + for (let index = 0; index < length; index++) { + const key = plus.storage.key(index); + if (key !== STORAGE_KEYS && + (key.indexOf(STORAGE_DATA_TYPE) < 0 || + key.indexOf(STORAGE_DATA_TYPE) + STORAGE_DATA_TYPE.length !== + key.length)) { + const value = plus.storage.getItem(key); + currentSize += key.length + value.length; + keys.push(key); } - else { - reject('getClipboardData:fail'); - } - }); - }); - const setClipboardData = defineAsyncApi(API_SET_CLIPBOARD_DATA, (options, { resolve }) => { - const clipboard = requireNativePlugin('clipboard'); - clipboard.setString(options.data); - resolve(); - }); + } + return { + keys, + currentSize: Math.ceil((currentSize * 2) / 1024), + limitSize: Number.MAX_VALUE, + }; + })); + const getStorageInfo = (defineAsyncApi('getStorageInfo', (_, { resolve }) => { + resolve(getStorageInfoSync()); + })); - const API_ON_NETWORK_STATUS_CHANGE = 'onNetworkStatusChange'; - function networkListener() { - getNetworkType().then(({ networkType }) => { - UniServiceJSBridge.invokeOnCallback(API_ON_NETWORK_STATUS_CHANGE, { - isConnected: networkType !== 'none', - networkType, - }); - }); - } - // 注意:框架对on类的API已做了统一的前置处理(仅首次调用on方法时,会调用具体的平台on实现,后续调用,框架不会再调用,实现时,直接监听平台事件即可) - const onNetworkStatusChange = defineOnApi(API_ON_NETWORK_STATUS_CHANGE, () => { - plus.globalEvent.addEventListener('netchange', networkListener); - }); - // 注意:框架对off类的API已做了统一的前置处理(仅当框架内不存在对应的on监听时,会调用具体的平台off实现,若还存在事件,框架不会再调用,具体实现时,直接移除平台事件即可) - const offNetworkStatusChange = defineOffApi('offNetworkStatusChange', () => { - plus.globalEvent.removeEventListener('netchange', networkListener); - }); - const getNetworkType = defineAsyncApi('getNetworkType', (_args, { resolve }) => { - let networkType = NETWORK_TYPES[plus.networkinfo.getCurrentType()] || 'unknown'; - return resolve({ networkType }); - }); + const getFileInfo$1 = defineAsyncApi(API_GET_FILE_INFO, (options, { resolve, reject }) => { + plus.io.getFileInfo(extend(options, { + success: warpPlusSuccessCallback(resolve), + fail: warpPlusErrorCallback(reject), + })); + }, GetFileInfoProtocol, GetFileInfoOptions); - function checkIsSupportFaceID() { - const platform = plus.os.name.toLowerCase(); - if (platform !== 'ios') { - return false; - } - const faceID = requireNativePlugin('faceID'); - return !!(faceID && faceID.isSupport()); - } - function checkIsSupportFingerPrint() { - return !!(plus.fingerprint && plus.fingerprint.isSupport()); - } - const baseCheckIsSupportSoterAuthentication = (resolve) => { - const supportMode = []; - if (checkIsSupportFingerPrint()) { - supportMode.push('fingerPrint'); - } - if (checkIsSupportFaceID()) { - supportMode.push('facial'); - } - resolve && - resolve({ - supportMode, - }); - return { - supportMode, - errMsg: 'checkIsSupportSoterAuthentication:ok', - }; - }; - const checkIsSupportSoterAuthentication = defineAsyncApi(API_CHECK_IS_SUPPORT_SOTER_AUTHENTICATION, (_, { resolve, reject }) => { - baseCheckIsSupportSoterAuthentication(resolve); - }); - const basecheckIsSoterEnrolledInDevice = ({ checkAuthMode, resolve, reject, }) => { - const wrapReject = (errMsg, errRes) => reject && reject(errMsg, ...errRes); - const wrapResolve = (res) => resolve && resolve(res); - if (checkAuthMode === 'fingerPrint') { - if (checkIsSupportFingerPrint()) { - const isEnrolled = plus.fingerprint.isKeyguardSecure() && - plus.fingerprint.isEnrolledFingerprints(); - wrapResolve({ isEnrolled }); - return { - isEnrolled, - errMsg: 'checkIsSoterEnrolledInDevice:ok', - }; - } - wrapReject('not support', { isEnrolled: false }); - return { - isEnrolled: false, - errMsg: 'checkIsSoterEnrolledInDevice:fail not support', - }; - } - else if (checkAuthMode === 'facial') { - if (checkIsSupportFaceID()) { - const faceID = requireNativePlugin('faceID'); - const isEnrolled = faceID && faceID.isKeyguardSecure() && faceID.isEnrolledFaceID(); - wrapResolve({ isEnrolled }); - return { - isEnrolled, - errMsg: 'checkIsSoterEnrolledInDevice:ok', - }; - } - wrapReject('not support', { isEnrolled: false }); - return { - isEnrolled: false, - errMsg: 'checkIsSoterEnrolledInDevice:fail not support', - }; - } - wrapReject('not support', { isEnrolled: false }); - return { - isEnrolled: false, - errMsg: 'checkIsSoterEnrolledInDevice:fail not support', - }; - }; - const checkIsSoterEnrolledInDevice = defineAsyncApi(API_CHECK_IS_SOTER_ENROLLED_IN_DEVICE, ({ checkAuthMode }, { resolve, reject }) => { - basecheckIsSoterEnrolledInDevice({ checkAuthMode, resolve, reject }); - }, CheckIsSoterEnrolledInDeviceProtocols, CheckIsSoterEnrolledInDeviceOptions); - const startSoterAuthentication = defineAsyncApi(API_START_SOTER_AUTHENTICATION, ({ requestAuthModes, challenge = false, authContent }, { resolve, reject }) => { - /* - 以手机不支持facial未录入fingerPrint为例 - requestAuthModes:['facial','fingerPrint']时,微信小程序返回值里的authMode为"fingerPrint" - requestAuthModes:['fingerPrint','facial']时,微信小程序返回值里的authMode为"fingerPrint" - 即先过滤不支持的方式之后再判断是否录入 - 微信小程序errCode(从企业号开发者中心查到如下文档): - 0:识别成功 'startSoterAuthentication:ok' - 90001:本设备不支持SOTER 'startSoterAuthentication:fail not support soter' - 90002:用户未授权微信使用该生物认证接口 注:APP端暂不支持 - 90003:请求使用的生物认证方式不支持 'startSoterAuthentication:fail no corresponding mode' - 90004:未传入challenge或challenge长度过长(最长512字符)注:APP端暂不支持 - 90005:auth_content长度超过限制(最长42个字符)注:微信小程序auth_content指纹识别时无效果,faceID暂未测试 - 90007:内部错误 'startSoterAuthentication:fail auth key update error' - 90008:用户取消授权 'startSoterAuthentication:fail cancel' - 90009:识别失败 'startSoterAuthentication:fail' - 90010:重试次数过多被冻结 'startSoterAuthentication:fail authenticate freeze. please try again later' - 90011:用户未录入所选识别方式 'startSoterAuthentication:fail no fingerprint enrolled' - */ - initI18nStartSoterAuthenticationMsgsOnce(); - const { t } = useI18n(); - const supportMode = baseCheckIsSupportSoterAuthentication().supportMode; - if (supportMode.length === 0) { - return { - authMode: 'fingerPrint', - errCode: 90001, - errMsg: 'startSoterAuthentication:fail', - }; - } - const supportRequestAuthMode = []; - requestAuthModes.map((item, index) => { - if (supportMode.indexOf(item) > -1) { - supportRequestAuthMode.push(item); - } + const openDocument = defineAsyncApi(API_OPEN_DOCUMENT, ({ filePath, fileType }, { resolve, reject }) => { + plus.io.resolveLocalFileSystemURL(getRealPath(filePath), (entry) => { + plus.runtime.openFile(getRealPath(filePath)); + resolve(); + }, (err) => { + reject('openDocument:fail ' + err.message); }); - if (supportRequestAuthMode.length === 0) { - return { - authMode: 'fingerPrint', - errCode: 90003, - errMsg: 'startSoterAuthentication:fail no corresponding mode', - }; + }, OpenDocumentProtocol, OpenDocumentOptions); + + const DEVICE_FREQUENCY = 200; + const NETWORK_TYPES = [ + 'unknown', + 'none', + 'ethernet', + 'wifi', + '2g', + '3g', + '4g', + '5g', + ]; + const TEMP_PATH_BASE = '_doc/uniapp_temp'; + const TEMP_PATH = `${TEMP_PATH_BASE}_${Date.now()}`; + + let listener$1 = null; + const onCompassChange = (defineOnApi(API_ON_COMPASS, () => { + startCompass(); + })); + const offCompassChange = (defineOnApi(API_OFF_COMPASS, () => { + stopCompass(); + })); + const startCompass = (defineAsyncApi(API_START_COMPASS, (_, { resolve, reject }) => { + if (!listener$1) { + plus.orientation.watchOrientation((res) => { + UniServiceJSBridge.invokeOnCallback(API_ON_COMPASS, { + direction: res.magneticHeading, + }); + }, (err) => { + reject(err.message); + listener$1 = null; + }, { + frequency: DEVICE_FREQUENCY, + }); } - const enrolledRequestAuthMode = []; - supportRequestAuthMode.map((item, index) => { - const checked = basecheckIsSoterEnrolledInDevice({ - checkAuthMode: item, - }).isEnrolled; - if (checked) { - enrolledRequestAuthMode.push(item); - } - }); - if (enrolledRequestAuthMode.length === 0) { - return { - authMode: supportRequestAuthMode[0], - errCode: 90011, - errMsg: `startSoterAuthentication:fail no ${supportRequestAuthMode[0]} enrolled`, - }; + setTimeout(resolve, DEVICE_FREQUENCY); + })); + const stopCompass = (defineAsyncApi(API_STOP_COMPASS, (_, { resolve }) => { + if (listener$1) { + plus.orientation.clearWatch(listener$1); + listener$1 = null; } - const realAuthMode = enrolledRequestAuthMode[0]; - if (realAuthMode === 'fingerPrint') { - if (plus.os.name.toLowerCase() === 'android') { - plus.nativeUI.showWaiting(authContent || t('uni.startSoterAuthentication.authContent')).onclose = function () { - plus.fingerprint.cancel(); - }; - } - plus.fingerprint.authenticate(() => { - plus.nativeUI.closeWaiting(); - resolve({ - authMode: realAuthMode, - errCode: 0, + resolve(); + })); + + const vibrateShort = defineAsyncApi(API_VIBRATE_SHORT, (_, { resolve }) => { + plus.device.vibrate(15); + resolve(); + }); + const vibrateLong = defineAsyncApi(API_VIBRATE_LONG, (_, { resolve }) => { + plus.device.vibrate(400); + resolve(); + }); + + let listener = null; + const onAccelerometerChange = (defineOnApi(API_ON_ACCELEROMETER, () => { + startAccelerometer(); + })); + const offAccelerometerChange = (defineOnApi(API_OFF_ACCELEROMETER, () => { + stopAccelerometer(); + })); + const startAccelerometer = (defineAsyncApi(API_START_ACCELEROMETER, (_, { resolve, reject }) => { + if (!listener) { + listener = plus.accelerometer.watchAcceleration((res) => { + UniServiceJSBridge.invokeOnCallback(API_ON_ACCELEROMETER, { + x: (res && res.xAxis) || 0, + y: (res && res.yAxis) || 0, + z: (res && res.zAxis) || 0, }); - }, (e) => { - const res = { - authMode: realAuthMode, - }; - switch (e.code) { - case e.AUTHENTICATE_MISMATCH: - // 微信小程序没有这个回调,如果要实现此处回调需要多次触发需要用事件publish实现 - // invoke(callbackId, { - // authMode: realAuthMode, - // errCode: 90009, - // errMsg: 'startSoterAuthentication:fail' - // }) - break; - case e.AUTHENTICATE_OVERLIMIT: - // 微信小程序在第一次重试次数超限时安卓IOS返回不一致,安卓端会返回次数超过限制(errCode: 90010),IOS端会返回认证失败(errCode: 90009)。APP-IOS实际运行时不会次数超限,超过指定次数之后会弹出输入密码的界面 - plus.nativeUI.closeWaiting(); - reject('authenticate freeze. please try again later', extend(res, { - errCode: 90010, - })); - break; - case e.CANCEL: - plus.nativeUI.closeWaiting(); - reject('cancel', extend(res, { - errCode: 90008, - })); - break; - default: - plus.nativeUI.closeWaiting(); - reject('', extend(res, { - errCode: 90007, - })); - break; - } + }, (err) => { + listener = null; + reject(`startAccelerometer:fail ${err.message}`); }, { - message: authContent, + frequency: DEVICE_FREQUENCY, }); } - else if (realAuthMode === 'facial') { - const faceID = requireNativePlugin('faceID'); - faceID.authenticate({ - message: authContent, - }, (e) => { - const res = { - authMode: realAuthMode, - }; - if (e.type === 'success' && e.code === 0) { - resolve({ - authMode: realAuthMode, - errCode: 0, - }); - } - else { - switch (e.code) { - case 4: - reject('', extend(res, { - errCode: 90009, - })); - break; - case 5: - reject('authenticate freeze. please try again later', extend(res, { - errCode: 90010, - })); - break; - case 6: - reject('', extend(res, { - errCode: 90008, - })); - break; - default: - reject('', extend(res, { - errCode: 90007, - })); - break; - } - } - }); + setTimeout(resolve, DEVICE_FREQUENCY); + })); + const stopAccelerometer = (defineAsyncApi(API_STOP_ACCELEROMETER, (_, { resolve }) => { + if (listener) { + plus.accelerometer.clearWatch(listener); + listener = null; } - }, StartSoterAuthenticationProtocols, StartSoterAuthenticationOptions); + resolve(); + })); - const getImageInfo = defineAsyncApi(API_GET_IMAGE_INFO, (options, { resolve, reject }) => { - const path = TEMP_PATH + '/download/'; - plus.io.getImageInfo(extend(options, { - savePath: path, - filename: path, - success: warpPlusSuccessCallback(resolve), - fail: warpPlusErrorCallback(reject), - })); - }, GetImageInfoProtocol, GetImageInfoOptions); + const onBluetoothDeviceFound = defineOnApi(API_ON_BLUETOOTH_DEVICE_FOUND, warpPlusEvent(() => plus.bluetooth.onBluetoothDeviceFound, API_ON_BLUETOOTH_DEVICE_FOUND)); + const onBluetoothAdapterStateChange = defineOnApi(API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBluetoothAdapterStateChange, API_ON_BLUETOOTH_ADAPTER_STATE_CHANGE)); + const onBLEConnectionStateChange = defineOnApi(API_ON_BLE_CONNECTION_STATE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBLEConnectionStateChange, API_ON_BLE_CONNECTION_STATE_CHANGE)); + const onBLECharacteristicValueChange = defineOnApi(API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE, warpPlusEvent(() => plus.bluetooth.onBLECharacteristicValueChange, API_ON_BLE_CHARACTERISTIC_VALUE_CHANGE)); + const openBluetoothAdapter = defineAsyncApi('openBluetoothAdapter', warpPlusMethod(() => plus.bluetooth.openBluetoothAdapter)); + const closeBluetoothAdapter = defineAsyncApi('closeBluetoothAdapter', warpPlusMethod(() => plus.bluetooth.closeBluetoothAdapter)); + const getBluetoothAdapterState = defineAsyncApi('getBluetoothAdapterState', warpPlusMethod(() => plus.bluetooth.getBluetoothAdapterState)); + const startBluetoothDevicesDiscovery = defineAsyncApi(API_START_BLUETOOTH_DEVICES_DISCOVERY, warpPlusMethod(() => plus.bluetooth.startBluetoothDevicesDiscovery), StartBluetoothDevicesDiscoveryProtocol); + const stopBluetoothDevicesDiscovery = defineAsyncApi('stopBluetoothDevicesDiscovery', warpPlusMethod(() => plus.bluetooth.stopBluetoothDevicesDiscovery)); + const getBluetoothDevices = defineAsyncApi('getBluetoothDevices', warpPlusMethod(() => plus.bluetooth.getBluetoothDevices)); + const getConnectedBluetoothDevices = defineAsyncApi(API_GET_CONNECTED_BLUETOOTH_DEVICES, warpPlusMethod(() => plus.bluetooth.getConnectedBluetoothDevices), GetConnectedBluetoothDevicesProtocol); + const createBLEConnection = defineAsyncApi(API_CREATE_BLE_CONNECTION, warpPlusMethod(() => plus.bluetooth.createBLEConnection), CreateBLEConnectionProtocol); + const closeBLEConnection = defineAsyncApi(API_CLOSE_BLE_CONNECTION, warpPlusMethod(() => plus.bluetooth.closeBLEConnection), CloseBLEConnectionProtocol); + const getBLEDeviceServices = defineAsyncApi(API_GET_BLE_DEVICE_SERVICES, warpPlusMethod(() => plus.bluetooth.getBLEDeviceServices), GetBLEDeviceServicesProtocol); + const getBLEDeviceCharacteristics = defineAsyncApi(API_GET_BLE_DEVICE_CHARACTERISTICS, warpPlusMethod(() => plus.bluetooth.getBLEDeviceCharacteristics), GetBLEDeviceCharacteristicsProtocol); + const notifyBLECharacteristicValueChange = defineAsyncApi(API_NOTIFY_BLE_CHARACTERISTIC_VALUE_CHANGE, warpPlusMethod(() => plus.bluetooth.notifyBLECharacteristicValueChange), NotifyBLECharacteristicValueChangeProtocol); + const readBLECharacteristicValue = defineAsyncApi(API_READ_BLE_CHARACTERISTIC_VALUE, warpPlusMethod(() => plus.bluetooth.readBLECharacteristicValue), ReadBLECharacteristicValueProtocol); + const writeBLECharacteristicValue = defineAsyncApi(API_WRITE_BLE_CHARACTERISTIC_VALUE, warpPlusMethod(() => plus.bluetooth.writeBLECharacteristicValue), WriteBLECharacteristicValueProtocol); + const setBLEMTU = defineAsyncApi(API_SET_BLE_MTU, warpPlusMethod(() => plus.bluetooth.setBLEMTU), SetBLEMTUProtocol); + const getBLEDeviceRSSI = defineAsyncApi(API_GET_BLE_DEVICE_RSSI, warpPlusMethod(() => plus.bluetooth.getBLEDeviceRSSI), GetBLEDeviceRSSIProtocol); - const getVideoInfo = defineAsyncApi(API_GET_VIDEO_INFO, (options, { resolve, reject }) => { - plus.io.getVideoInfo({ - filePath: options.src, - success: (data) => { - return { - orientation: data.orientation, - type: data.type, - duration: data.duration, - size: data.size, - height: data.height, - width: data.width, - fps: data.fps || 30, - bitrate: data.bitrate, - }; - }, - fail: warpPlusErrorCallback(reject), - }); - }, GetVideoInfoProtocol, GetVideoInfoOptions); + const onBeaconUpdate = defineOnApi(API_ON_BEACON_UPDATE, warpPlusEvent(() => plus.ibeacon.onBeaconUpdate, API_ON_BEACON_UPDATE)); + const onBeaconServiceChange = defineOnApi(API_ON_BEACON_SERVICE_CHANGE, warpPlusEvent(() => plus.ibeacon.onBeaconServiceChange, API_ON_BEACON_SERVICE_CHANGE)); + const getBeacons = defineAsyncApi(API_GET_BEACONS, warpPlusMethod(() => plus.ibeacon.getBeacons)); + const startBeaconDiscovery = defineAsyncApi(API_START_BEACON_DISCOVERY, warpPlusMethod(() => plus.ibeacon.startBeaconDiscovery), StartBeaconDiscoveryProtocol); + const stopBeaconDiscovery = defineAsyncApi(API_STOP_BEACON_DISCOVERY, warpPlusMethod(() => plus.ibeacon.stopBeaconDiscovery)); - const previewImage = defineAsyncApi(API_PREVIEW_IMAGE, ({ current = 0, indicator = 'number', loop = false, urls, longPressActions }, { resolve, reject }) => { - initI18nChooseImageMsgsOnce(); - const { t } = useI18n(); - urls = urls.map((url) => getRealPath(url)); - const index = Number(current); - if (isNaN(index)) { - current = urls.indexOf(getRealPath(current)); - current = current < 0 ? 0 : current; - } - else { - current = index; - } - plus.nativeUI.previewImage(urls, { - current, - indicator, - loop, - onLongPress: function (res) { - let itemList = []; - let itemColor = ''; - const hasLongPressActions = longPressActions && isPlainObject(longPressActions); - if (!hasLongPressActions) { - itemList = [t('uni.previewImage.button.save')]; - itemColor = '#000000'; - } - else { - itemList = longPressActions.itemList - ? longPressActions.itemList - : []; - itemColor = longPressActions.itemColor - ? longPressActions.itemColor - : '#000000'; - } - const options = { - buttons: itemList.map((item) => ({ - title: item, - color: itemColor, - })), - cancel: t('uni.previewImage.cancel'), - }; - plus.nativeUI.actionSheet(options, (e) => { - if (e.index > 0) { - if (hasLongPressActions) { - typeof longPressActions.success === 'function' && - longPressActions.success({ - tapIndex: e.index - 1, - index: res.index, - }); - return; - } - plus.gallery.save(res.url, () => { - plus.nativeUI.toast(t('uni.previewImage.save.success')); - }, function () { - plus.nativeUI.toast(t('uni.previewImage.save.fail')); - }); - } - else if (hasLongPressActions) { - typeof longPressActions.fail === 'function' && - longPressActions.fail({ - errMsg: 'showActionSheet:fail cancel', - }); - } - }); - }, - }); - resolve(); - }, PreviewImageProtocol, PreviewImageOptions); + const makePhoneCall = defineAsyncApi(API_MAKE_PHONE_CALL, ({ phoneNumber }, { resolve }) => { + plus.device.dial(phoneNumber); + return resolve(); + }, MakePhoneCallProtocol); - let recorder; - let recording = false; - let recordTimeout; - const publishRecorderStateChange = (state, res = {}) => { - onRecorderStateChange(extend({ - state, - }, res)); - }; - const Recorder = { - start({ duration = 60000, sampleRate, numberOfChannels, encodeBitRate, format = 'mp3', frameSize, }) { - if (recording) { - return publishRecorderStateChange('start'); + const addPhoneContact = defineAsyncApi(API_ADD_PHONE_CONTACT, ({ photoFilePath = '', nickName, lastName, middleName, firstName, remark, mobilePhoneNumber, weChatNumber, addressCountry, addressState, addressCity, addressStreet, addressPostalCode, organization, title, workFaxNumber, workPhoneNumber, hostNumber, email, url, workAddressCountry, workAddressState, workAddressCity, workAddressStreet, workAddressPostalCode, homeFaxNumber, homePhoneNumber, homeAddressCountry, homeAddressState, homeAddressCity, homeAddressStreet, homeAddressPostalCode, }, { resolve, reject }) => { + plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, (addressbook) => { + const contact = addressbook.create(); + const name = {}; + if (lastName) { + name.familyName = lastName; } - recorder = plus.audio.getRecorder(); - recorder.record({ - format, - samplerate: sampleRate ? String(sampleRate) : '', - filename: TEMP_PATH + '/recorder/', - }, (res) => publishRecorderStateChange('stop', { - tempFilePath: res, - }), (err) => publishRecorderStateChange('error', { - errMsg: err.message, - })); - recordTimeout = setTimeout(() => { - Recorder.stop(); - }, duration); - publishRecorderStateChange('start'); - recording = true; - }, - stop() { - if (recording) { - recorder.stop(); - recording = false; - recordTimeout && clearTimeout(recordTimeout); + if (firstName) { + name.givenName = firstName; } - }, - pause() { - if (recording) { - publishRecorderStateChange('error', { - errMsg: 'Unsupported operation: pause', - }); + if (middleName) { + name.middleName = middleName; } - }, - resume() { - if (recording) { - publishRecorderStateChange('error', { - errMsg: 'Unsupported operation: resume', - }); + contact.name = name; + if (nickName) { + contact.nickname = nickName; } - }, - }; - const callbacks$1 = { - pause: null, - resume: null, - start: null, - stop: null, - error: null, - }; - function onRecorderStateChange(res) { - const state = res.state; - delete res.state; - delete res.errMsg; - if (state && typeof callbacks$1[state] === 'function') { - callbacks$1[state](res); - } - } - class RecorderManager { - constructor() { } - onError(callback) { - callbacks$1.error = callback; - } - onFrameRecorded(callback) { } - onInterruptionBegin(callback) { } - onInterruptionEnd(callback) { } - onPause(callback) { - callbacks$1.pause = callback; - } - onResume(callback) { - callbacks$1.resume = callback; - } - onStart(callback) { - callbacks$1.start = callback; - } - onStop(callback) { - callbacks$1.stop = callback; - } - pause() { - Recorder.pause(); - } - resume() { - Recorder.resume(); - } - start(options) { - Recorder.start(options); - } - stop() { - Recorder.stop(); - } - } - let recorderManager; - const getRecorderManager = defineSyncApi(API_GET_RECORDER_MANAGER, () => recorderManager || (recorderManager = new RecorderManager())); - - const saveVideoToPhotosAlbum = defineAsyncApi(API_SAVE_VIDEO_TO_PHOTOS_ALBUM, (options, { resolve, reject }) => { - plus.gallery.save(options.filePath, warpPlusSuccessCallback(resolve), warpPlusErrorCallback(reject)); - }, SaveVideoToPhotosAlbumProtocol, SaveVideoToPhotosAlbumOptions); - - const saveImageToPhotosAlbum = defineAsyncApi(API_SAVE_IMAGE_TO_PHOTOS_ALBUM, (options, { resolve, reject }) => { - plus.gallery.save(options.filePath, warpPlusSuccessCallback(resolve), warpPlusErrorCallback(reject)); - }, SaveImageToPhotosAlbumProtocol, SaveImageToPhotosAlbumOptions); - - function getFileName(path) { - const array = path.split('/'); - return array[array.length - 1]; - } - - const compressImage$1 = defineAsyncApi(API_COMPRESS_IMAGE, (options, { resolve, reject }) => { - const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(options.src)}`; - plus.zip.compressImage(extend({}, options, { - dst, - }), () => { - resolve({ - tempFilePath: dst, + if (photoFilePath) { + contact.photos = [ + { + type: 'url', + value: photoFilePath, + }, + ]; + } + if (remark) { + contact.note = remark; + } + const mobilePhone = { + type: 'mobile', + }; + const workPhone = { + type: 'work', + }; + const companyPhone = { + type: 'company', + }; + const homeFax = { + type: 'home fax', + }; + const workFax = { + type: 'work fax', + }; + if (mobilePhoneNumber) { + mobilePhone.value = mobilePhoneNumber; + } + if (workPhoneNumber) { + workPhone.value = workPhoneNumber; + } + if (hostNumber) { + companyPhone.value = hostNumber; + } + if (homeFaxNumber) { + homeFax.value = homeFaxNumber; + } + if (workFaxNumber) { + workFax.value = workFaxNumber; + } + contact.phoneNumbers = [ + mobilePhone, + workPhone, + companyPhone, + homeFax, + workFax, + ]; + if (email) { + contact.emails = [ + { + type: 'home', + value: email, + }, + ]; + } + if (url) { + contact.urls = [ + { + type: 'other', + value: url, + }, + ]; + } + if (weChatNumber) { + contact.ims = [ + { + type: 'other', + value: weChatNumber, + }, + ]; + } + const defaultAddress = { + type: 'other', + preferred: true, + }; + const homeAddress = { + type: 'home', + }; + const companyAddress = { + type: 'company', + }; + if (addressCountry) { + defaultAddress.country = addressCountry; + } + if (addressState) { + defaultAddress.region = addressState; + } + if (addressCity) { + defaultAddress.locality = addressCity; + } + if (addressStreet) { + defaultAddress.streetAddress = addressStreet; + } + if (addressPostalCode) { + defaultAddress.postalCode = addressPostalCode; + } + if (homeAddressCountry) { + homeAddress.country = homeAddressCountry; + } + if (homeAddressState) { + homeAddress.region = homeAddressState; + } + if (homeAddressCity) { + homeAddress.locality = homeAddressCity; + } + if (homeAddressStreet) { + homeAddress.streetAddress = homeAddressStreet; + } + if (homeAddressPostalCode) { + homeAddress.postalCode = homeAddressPostalCode; + } + if (workAddressCountry) { + companyAddress.country = workAddressCountry; + } + if (workAddressState) { + companyAddress.region = workAddressState; + } + if (workAddressCity) { + companyAddress.locality = workAddressCity; + } + if (workAddressStreet) { + companyAddress.streetAddress = workAddressStreet; + } + if (workAddressPostalCode) { + companyAddress.postalCode = workAddressPostalCode; + } + contact.addresses = [ + defaultAddress, + homeAddress, + companyAddress, + ]; + contact.save(() => { + resolve({ + errMsg: 'addPhoneContact:ok', + }); + }, (e) => { + reject('addPhoneContact:fail'); }); - }, reject); - }, CompressImageProtocol, CompressImageOptions); + }, (e) => { + reject('addPhoneContact:fail'); + }); + }, MakePhoneCallProtocol); - const compressVideo = defineAsyncApi(API_COMPRESS_VIDEO, (options, { resolve, reject }) => { - const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(options.src)}`; - plus.zip.compressVideo(extend({}, options, { - dst, - }), () => { - resolve({ - tempFilePath: dst, - }); - }, reject); - }, CompressVideoProtocol, CompressVideoOptions); + function requireNativePlugin(pluginName) { + /* eslint-disable no-undef */ + if (typeof weex !== 'undefined') { + return weex.requireModule(pluginName); + } + /* eslint-disable no-undef */ + return __requireNativePlugin__(pluginName); + } - /** - * 获取文件信息 - * @param {string} filePath 文件路径 - * @returns {Promise} 文件信息Promise - */ - function getFileInfo(filePath) { - return new Promise((resolve, reject) => { - plus.io.resolveLocalFileSystemURL(filePath, function (entry) { - entry.getMetadata(resolve, reject, false); - }, reject); + const getClipboardData = defineAsyncApi(API_GET_CLIPBOARD_DATA, (_, { resolve, reject }) => { + const clipboard = requireNativePlugin('clipboard'); + clipboard.getString((ret) => { + if (ret.result === 'success') { + resolve({ + data: ret.data, + }); + } + else { + reject('getClipboardData:fail'); + } }); - } - function compressImage(tempFilePath) { - const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(tempFilePath)}`; - return new Promise((resolve) => { - plus.nativeUI.showWaiting(); - plus.zip.compressImage({ - src: tempFilePath, - dst, - overwrite: true, - }, () => { - plus.nativeUI.closeWaiting(); - resolve(dst); - }, () => { - plus.nativeUI.closeWaiting(); - resolve(tempFilePath); + }); + const setClipboardData = defineAsyncApi(API_SET_CLIPBOARD_DATA, (options, { resolve }) => { + const clipboard = requireNativePlugin('clipboard'); + clipboard.setString(options.data); + resolve(); + }); + + const API_ON_NETWORK_STATUS_CHANGE = 'onNetworkStatusChange'; + function networkListener() { + getNetworkType().then(({ networkType }) => { + UniServiceJSBridge.invokeOnCallback(API_ON_NETWORK_STATUS_CHANGE, { + isConnected: networkType !== 'none', + networkType, }); }); } - const chooseImage = defineAsyncApi(API_CHOOSE_IMAGE, - // @ts-ignore crop 属性App特有 - ({ count, sizeType, sourceType, crop } = {}, { resolve, reject }) => { - initI18nChooseImageMsgsOnce(); - const { t } = useI18n(); - const errorCallback = warpPlusErrorCallback(reject); - function successCallback(paths) { - const tempFiles = []; - const tempFilePaths = []; - // plus.zip.compressImage 压缩文件并发调用在iOS端容易出现问题(图像错误、闪退),改为队列执行 - paths - .reduce((promise, path) => { - return promise - .then(() => { - return getFileInfo(path); - }) - .then((fileInfo) => { - const size = fileInfo.size; - // 压缩阈值 0.5 兆 - const THRESHOLD = 1024 * 1024 * 0.5; - // 判断是否需要压缩 - if (!crop && - sizeType.includes('compressed') && - size > THRESHOLD) { - return compressImage(path).then((dstPath) => { - path = dstPath; - return getFileInfo(path); - }); - } - return fileInfo; - }) - .then(({ size }) => { - tempFilePaths.push(path); - tempFiles.push({ - path, - size: size, - }); - }); - }, Promise.resolve()) - .then(() => { - resolve({ - tempFilePaths, - tempFiles, - }); - }) - .catch(errorCallback); + // 注意:框架对on类的API已做了统一的前置处理(仅首次调用on方法时,会调用具体的平台on实现,后续调用,框架不会再调用,实现时,直接监听平台事件即可) + const onNetworkStatusChange = defineOnApi(API_ON_NETWORK_STATUS_CHANGE, () => { + plus.globalEvent.addEventListener('netchange', networkListener); + }); + // 注意:框架对off类的API已做了统一的前置处理(仅当框架内不存在对应的on监听时,会调用具体的平台off实现,若还存在事件,框架不会再调用,具体实现时,直接移除平台事件即可) + const offNetworkStatusChange = defineOffApi('offNetworkStatusChange', () => { + plus.globalEvent.removeEventListener('netchange', networkListener); + }); + const getNetworkType = defineAsyncApi('getNetworkType', (_args, { resolve }) => { + let networkType = NETWORK_TYPES[plus.networkinfo.getCurrentType()] || 'unknown'; + return resolve({ networkType }); + }); + + function checkIsSupportFaceID() { + const platform = plus.os.name.toLowerCase(); + if (platform !== 'ios') { + return false; } - function openCamera() { - const camera = plus.camera.getCamera(); - camera.captureImage((path) => successCallback([path]), errorCallback, { - filename: TEMP_PATH + '/camera/', - resolution: 'high', - crop, - }); + const faceID = requireNativePlugin('faceID'); + return !!(faceID && faceID.isSupport()); + } + function checkIsSupportFingerPrint() { + return !!(plus.fingerprint && plus.fingerprint.isSupport()); + } + const baseCheckIsSupportSoterAuthentication = (resolve) => { + const supportMode = []; + if (checkIsSupportFingerPrint()) { + supportMode.push('fingerPrint'); } - function openAlbum() { - // NOTE 5+此API分单选和多选,多选返回files:string[] - // @ts-ignore - plus.gallery.pick(({ files }) => successCallback(files), errorCallback, { - maximum: count, - multiple: true, - system: false, - filename: TEMP_PATH + '/gallery/', - permissionAlert: true, - crop, + if (checkIsSupportFaceID()) { + supportMode.push('facial'); + } + resolve && + resolve({ + supportMode, }); + return { + supportMode, + errMsg: 'checkIsSupportSoterAuthentication:ok', + }; + }; + const checkIsSupportSoterAuthentication = defineAsyncApi(API_CHECK_IS_SUPPORT_SOTER_AUTHENTICATION, (_, { resolve, reject }) => { + baseCheckIsSupportSoterAuthentication(resolve); + }); + const basecheckIsSoterEnrolledInDevice = ({ checkAuthMode, resolve, reject, }) => { + const wrapReject = (errMsg, errRes) => reject && reject(errMsg, ...errRes); + const wrapResolve = (res) => resolve && resolve(res); + if (checkAuthMode === 'fingerPrint') { + if (checkIsSupportFingerPrint()) { + const isEnrolled = plus.fingerprint.isKeyguardSecure() && + plus.fingerprint.isEnrolledFingerprints(); + wrapResolve({ isEnrolled }); + return { + isEnrolled, + errMsg: 'checkIsSoterEnrolledInDevice:ok', + }; + } + wrapReject('not support', { isEnrolled: false }); + return { + isEnrolled: false, + errMsg: 'checkIsSoterEnrolledInDevice:fail not support', + }; } - if (sourceType.length === 1) { - if (sourceType.includes('album')) { - openAlbum(); - return; + else if (checkAuthMode === 'facial') { + if (checkIsSupportFaceID()) { + const faceID = requireNativePlugin('faceID'); + const isEnrolled = faceID && faceID.isKeyguardSecure() && faceID.isEnrolledFaceID(); + wrapResolve({ isEnrolled }); + return { + isEnrolled, + errMsg: 'checkIsSoterEnrolledInDevice:ok', + }; } - else if (sourceType.includes('camera')) { - openCamera(); - return; + wrapReject('not support', { isEnrolled: false }); + return { + isEnrolled: false, + errMsg: 'checkIsSoterEnrolledInDevice:fail not support', + }; + } + wrapReject('not support', { isEnrolled: false }); + return { + isEnrolled: false, + errMsg: 'checkIsSoterEnrolledInDevice:fail not support', + }; + }; + const checkIsSoterEnrolledInDevice = defineAsyncApi(API_CHECK_IS_SOTER_ENROLLED_IN_DEVICE, ({ checkAuthMode }, { resolve, reject }) => { + basecheckIsSoterEnrolledInDevice({ checkAuthMode, resolve, reject }); + }, CheckIsSoterEnrolledInDeviceProtocols, CheckIsSoterEnrolledInDeviceOptions); + const startSoterAuthentication = defineAsyncApi(API_START_SOTER_AUTHENTICATION, ({ requestAuthModes, challenge = false, authContent }, { resolve, reject }) => { + /* + 以手机不支持facial未录入fingerPrint为例 + requestAuthModes:['facial','fingerPrint']时,微信小程序返回值里的authMode为"fingerPrint" + requestAuthModes:['fingerPrint','facial']时,微信小程序返回值里的authMode为"fingerPrint" + 即先过滤不支持的方式之后再判断是否录入 + 微信小程序errCode(从企业号开发者中心查到如下文档): + 0:识别成功 'startSoterAuthentication:ok' + 90001:本设备不支持SOTER 'startSoterAuthentication:fail not support soter' + 90002:用户未授权微信使用该生物认证接口 注:APP端暂不支持 + 90003:请求使用的生物认证方式不支持 'startSoterAuthentication:fail no corresponding mode' + 90004:未传入challenge或challenge长度过长(最长512字符)注:APP端暂不支持 + 90005:auth_content长度超过限制(最长42个字符)注:微信小程序auth_content指纹识别时无效果,faceID暂未测试 + 90007:内部错误 'startSoterAuthentication:fail auth key update error' + 90008:用户取消授权 'startSoterAuthentication:fail cancel' + 90009:识别失败 'startSoterAuthentication:fail' + 90010:重试次数过多被冻结 'startSoterAuthentication:fail authenticate freeze. please try again later' + 90011:用户未录入所选识别方式 'startSoterAuthentication:fail no fingerprint enrolled' + */ + initI18nStartSoterAuthenticationMsgsOnce(); + const { t } = useI18n(); + const supportMode = baseCheckIsSupportSoterAuthentication().supportMode; + if (supportMode.length === 0) { + return { + authMode: 'fingerPrint', + errCode: 90001, + errMsg: 'startSoterAuthentication:fail', + }; + } + const supportRequestAuthMode = []; + requestAuthModes.map((item, index) => { + if (supportMode.indexOf(item) > -1) { + supportRequestAuthMode.push(item); } + }); + if (supportRequestAuthMode.length === 0) { + return { + authMode: 'fingerPrint', + errCode: 90003, + errMsg: 'startSoterAuthentication:fail no corresponding mode', + }; } - plus.nativeUI.actionSheet({ - cancel: t('uni.chooseImage.cancel'), - buttons: [ - { - title: t('uni.chooseImage.sourceType.camera'), - }, - { - title: t('uni.chooseImage.sourceType.album'), - }, - ], - }, (e) => { - switch (e.index) { - case 1: - openCamera(); - break; - case 2: - openAlbum(); - break; - default: - errorCallback(); - break; + const enrolledRequestAuthMode = []; + supportRequestAuthMode.map((item, index) => { + const checked = basecheckIsSoterEnrolledInDevice({ + checkAuthMode: item, + }).isEnrolled; + if (checked) { + enrolledRequestAuthMode.push(item); } }); - }, ChooseImageProtocol, ChooseImageOptions); - - const chooseVideo = defineAsyncApi(API_CHOOSE_VIDEO, ({ sourceType, compressed, maxDuration, camera }, { resolve, reject }) => { - initI18nChooseVideoMsgsOnce(); - const { t } = useI18n(); - const errorCallback = warpPlusErrorCallback(reject); - function successCallback(tempFilePath = '') { - const filename = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(tempFilePath)}`; - const compressVideo = compressed - ? new Promise((resolve) => { - plus.zip.compressVideo({ - src: tempFilePath, - filename, - }, ({ tempFilePath }) => { - resolve(tempFilePath); - }, () => { - resolve(tempFilePath); - }); - }) - : Promise.resolve(tempFilePath); - if (compressed) { - plus.nativeUI.showWaiting(); + if (enrolledRequestAuthMode.length === 0) { + return { + authMode: supportRequestAuthMode[0], + errCode: 90011, + errMsg: `startSoterAuthentication:fail no ${supportRequestAuthMode[0]} enrolled`, + }; + } + const realAuthMode = enrolledRequestAuthMode[0]; + if (realAuthMode === 'fingerPrint') { + if (plus.os.name.toLowerCase() === 'android') { + plus.nativeUI.showWaiting(authContent || t('uni.startSoterAuthentication.authContent')).onclose = function () { + plus.fingerprint.cancel(); + }; } - compressVideo.then((tempFilePath) => { - if (compressed) { - plus.nativeUI.closeWaiting(); - } - plus.io.getVideoInfo({ - filePath: tempFilePath, - success(videoInfo) { - const result = { - errMsg: 'chooseVideo:ok', - tempFilePath: tempFilePath, - size: videoInfo.size, - duration: videoInfo.duration, - width: videoInfo.width, - height: videoInfo.height, - }; - resolve(result); - }, - fail: errorCallback, + plus.fingerprint.authenticate(() => { + plus.nativeUI.closeWaiting(); + resolve({ + authMode: realAuthMode, + errCode: 0, }); + }, (e) => { + const res = { + authMode: realAuthMode, + }; + switch (e.code) { + case e.AUTHENTICATE_MISMATCH: + // 微信小程序没有这个回调,如果要实现此处回调需要多次触发需要用事件publish实现 + // invoke(callbackId, { + // authMode: realAuthMode, + // errCode: 90009, + // errMsg: 'startSoterAuthentication:fail' + // }) + break; + case e.AUTHENTICATE_OVERLIMIT: + // 微信小程序在第一次重试次数超限时安卓IOS返回不一致,安卓端会返回次数超过限制(errCode: 90010),IOS端会返回认证失败(errCode: 90009)。APP-IOS实际运行时不会次数超限,超过指定次数之后会弹出输入密码的界面 + plus.nativeUI.closeWaiting(); + reject('authenticate freeze. please try again later', extend(res, { + errCode: 90010, + })); + break; + case e.CANCEL: + plus.nativeUI.closeWaiting(); + reject('cancel', extend(res, { + errCode: 90008, + })); + break; + default: + plus.nativeUI.closeWaiting(); + reject('', extend(res, { + errCode: 90007, + })); + break; + } + }, { + message: authContent, }); } - function openAlbum() { - plus.gallery.pick( - // NOTE 5+此API分单选和多选,多选返回files:string[] - // @ts-ignore - ({ files }) => successCallback(files[0]), errorCallback, { - filter: 'video', - system: false, - // 不启用 multiple 时 system 无效 - multiple: true, - maximum: 1, - filename: TEMP_PATH + '/gallery/', - permissionAlert: true, + else if (realAuthMode === 'facial') { + const faceID = requireNativePlugin('faceID'); + faceID.authenticate({ + message: authContent, + }, (e) => { + const res = { + authMode: realAuthMode, + }; + if (e.type === 'success' && e.code === 0) { + resolve({ + authMode: realAuthMode, + errCode: 0, + }); + } + else { + switch (e.code) { + case 4: + reject('', extend(res, { + errCode: 90009, + })); + break; + case 5: + reject('authenticate freeze. please try again later', extend(res, { + errCode: 90010, + })); + break; + case 6: + reject('', extend(res, { + errCode: 90008, + })); + break; + default: + reject('', extend(res, { + errCode: 90007, + })); + break; + } + } }); } - function openCamera() { - const plusCamera = plus.camera.getCamera(); - plusCamera.startVideoCapture(successCallback, errorCallback, { - index: camera === 'front' ? '2' : '1', - videoMaximumDuration: maxDuration, - filename: TEMP_PATH + '/camera/', - }); + }, StartSoterAuthenticationProtocols, StartSoterAuthenticationOptions); + + const getImageInfo = defineAsyncApi(API_GET_IMAGE_INFO, (options, { resolve, reject }) => { + const path = TEMP_PATH + '/download/'; + plus.io.getImageInfo(extend(options, { + savePath: path, + filename: path, + success: warpPlusSuccessCallback(resolve), + fail: warpPlusErrorCallback(reject), + })); + }, GetImageInfoProtocol, GetImageInfoOptions); + + const getVideoInfo = defineAsyncApi(API_GET_VIDEO_INFO, (options, { resolve, reject }) => { + plus.io.getVideoInfo({ + filePath: options.src, + success: (data) => { + return { + orientation: data.orientation, + type: data.type, + duration: data.duration, + size: data.size, + height: data.height, + width: data.width, + fps: data.fps || 30, + bitrate: data.bitrate, + }; + }, + fail: warpPlusErrorCallback(reject), + }); + }, GetVideoInfoProtocol, GetVideoInfoOptions); + + const previewImage = defineAsyncApi(API_PREVIEW_IMAGE, ({ current = 0, indicator = 'number', loop = false, urls, longPressActions }, { resolve, reject }) => { + initI18nChooseImageMsgsOnce(); + const { t } = useI18n(); + urls = urls.map((url) => getRealPath(url)); + const index = Number(current); + if (isNaN(index)) { + current = urls.indexOf(getRealPath(current)); + current = current < 0 ? 0 : current; } - if (sourceType.length === 1) { - if (sourceType.includes('album')) { - openAlbum(); - return; - } - else if (sourceType.includes('camera')) { - openCamera(); - return; - } + else { + current = index; } - plus.nativeUI.actionSheet({ - cancel: t('uni.chooseVideo.cancel'), - buttons: [ - { - title: t('uni.chooseVideo.sourceType.camera'), - }, - { - title: t('uni.chooseVideo.sourceType.album'), - }, - ], - }, (e) => { - switch (e.index) { - case 1: - openCamera(); - break; - case 2: - openAlbum(); - break; - default: - errorCallback(); - break; - } + plus.nativeUI.previewImage(urls, { + current, + indicator, + loop, + onLongPress: function (res) { + let itemList = []; + let itemColor = ''; + const hasLongPressActions = longPressActions && isPlainObject(longPressActions); + if (!hasLongPressActions) { + itemList = [t('uni.previewImage.button.save')]; + itemColor = '#000000'; + } + else { + itemList = longPressActions.itemList + ? longPressActions.itemList + : []; + itemColor = longPressActions.itemColor + ? longPressActions.itemColor + : '#000000'; + } + const options = { + buttons: itemList.map((item) => ({ + title: item, + color: itemColor, + })), + cancel: t('uni.previewImage.cancel'), + }; + plus.nativeUI.actionSheet(options, (e) => { + if (e.index > 0) { + if (hasLongPressActions) { + typeof longPressActions.success === 'function' && + longPressActions.success({ + tapIndex: e.index - 1, + index: res.index, + }); + return; + } + plus.gallery.save(res.url, () => { + plus.nativeUI.toast(t('uni.previewImage.save.success')); + }, function () { + plus.nativeUI.toast(t('uni.previewImage.save.fail')); + }); + } + else if (hasLongPressActions) { + typeof longPressActions.fail === 'function' && + longPressActions.fail({ + errMsg: 'showActionSheet:fail cancel', + }); + } + }); + }, }); - }, ChooseVideoProtocol, ChooseVideoOptions); - - const showKeyboard = defineAsyncApi(API_SHOW_KEYBOARD, (_, { resolve }) => { - plus.key.showSoftKeybord(); - resolve(); - }); - const hideKeyboard = defineAsyncApi(API_HIDE_KEYBOARD, (_, { resolve }) => { - plus.key.hideSoftKeybord(); resolve(); - }); + }, PreviewImageProtocol, PreviewImageOptions); - class DownloadTask { - constructor(downloader) { - this._callbacks = []; - this._downloader = downloader; - downloader.addEventListener('statechanged', (download, status) => { - if (download.downloadedSize && download.totalSize) { - this._callbacks.forEach((callback) => { - callback({ - progress: Math.round((download.downloadedSize / download.totalSize) * 100), - totalBytesWritten: download.downloadedSize, - totalBytesExpectedToWrite: download.totalSize, - }); - }); - } - }); - } - abort() { - this._downloader.abort(); - } - onProgressUpdate(callback) { - if (typeof callback !== 'function') { - return; + let recorder; + let recording = false; + let recordTimeout; + const publishRecorderStateChange = (state, res = {}) => { + onRecorderStateChange(extend({ + state, + }, res)); + }; + const Recorder = { + start({ duration = 60000, sampleRate, numberOfChannels, encodeBitRate, format = 'mp3', frameSize, }) { + if (recording) { + return publishRecorderStateChange('start'); } - this._callbacks.push(callback); - } - offProgressUpdate(callback) { - const index = this._callbacks.indexOf(callback); - if (index >= 0) { - this._callbacks.splice(index, 1); + recorder = plus.audio.getRecorder(); + recorder.record({ + format, + samplerate: sampleRate ? String(sampleRate) : '', + filename: TEMP_PATH + '/recorder/', + }, (res) => publishRecorderStateChange('stop', { + tempFilePath: res, + }), (err) => publishRecorderStateChange('error', { + errMsg: err.message, + })); + recordTimeout = setTimeout(() => { + Recorder.stop(); + }, duration); + publishRecorderStateChange('start'); + recording = true; + }, + stop() { + if (recording) { + recorder.stop(); + recording = false; + recordTimeout && clearTimeout(recordTimeout); } - } - onHeadersReceived(callback) { - throw new Error('Method not implemented.'); - } - offHeadersReceived(callback) { - throw new Error('Method not implemented.'); - } - } - const downloadFile = defineTaskApi(API_DOWNLOAD_FILE, ({ url, header, timeout }, { resolve, reject }) => { - timeout = - (timeout || - (__uniConfig.networkTimeout && __uniConfig.networkTimeout.request) || - 60 * 1000) / 1000; - const downloader = plus.downloader.createDownload(url, { - timeout, - filename: TEMP_PATH + '/download/', - // 需要与其它平台上的表现保持一致,不走重试的逻辑。 - retry: 0, - retryInterval: 0, - }, (download, statusCode) => { - if (statusCode) { - resolve({ - tempFilePath: download.filename, - statusCode, + }, + pause() { + if (recording) { + publishRecorderStateChange('error', { + errMsg: 'Unsupported operation: pause', }); } - else { - reject(`statusCode: ${statusCode}`); - } - }); - const downloadTask = new DownloadTask(downloader); - for (const name in header) { - if (hasOwn$1(header, name)) { - downloader.setRequestHeader(name, header[name]); - } - } - downloader.start(); - return downloadTask; - }, DownloadFileProtocol, DownloadFileOptions); - - const cookiesParse = (header) => { - let cookiesStr = header['Set-Cookie'] || header['set-cookie']; - let cookiesArr = []; - if (!cookiesStr) { - return []; - } - if (cookiesStr[0] === '[' && cookiesStr[cookiesStr.length - 1] === ']') { - cookiesStr = cookiesStr.slice(1, -1); - } - const handleCookiesArr = cookiesStr.split(';'); - for (let i = 0; i < handleCookiesArr.length; i++) { - if (handleCookiesArr[i].indexOf('Expires=') !== -1 || - handleCookiesArr[i].indexOf('expires=') !== -1) { - cookiesArr.push(handleCookiesArr[i].replace(',', '')); - } - else { - cookiesArr.push(handleCookiesArr[i]); + }, + resume() { + if (recording) { + publishRecorderStateChange('error', { + errMsg: 'Unsupported operation: resume', + }); } - } - cookiesArr = cookiesArr.join(';').split(','); - return cookiesArr; + }, }; - function formatResponse(res, args) { - if (typeof res.data === 'string' && res.data.charCodeAt(0) === 65279) { - res.data = res.data.substr(1); - } - res.statusCode = parseInt(String(res.statusCode), 10); - if (isPlainObject(res.header)) { - res.header = Object.keys(res.header).reduce(function (ret, key) { - const value = res.header[key]; - if (Array.isArray(value)) { - ret[key] = value.join(','); - } - else if (typeof value === 'string') { - ret[key] = value; - } - return ret; - }, {}); - } - if (args.dataType && args.dataType.toLowerCase() === 'json') { - try { - res.data = JSON.parse(res.data); - } - catch (e) { } + const callbacks$1 = { + pause: null, + resume: null, + start: null, + stop: null, + error: null, + }; + function onRecorderStateChange(res) { + const state = res.state; + delete res.state; + delete res.errMsg; + if (state && typeof callbacks$1[state] === 'function') { + callbacks$1[state](res); } - return res; } - /** - * 请求任务类 - */ - class RequestTask { - constructor(requestTask) { - this._requestTask = requestTask; + class RecorderManager { + constructor() { } + onError(callback) { + callbacks$1.error = callback; } - abort() { - this._requestTask.abort(); + onFrameRecorded(callback) { } + onInterruptionBegin(callback) { } + onInterruptionEnd(callback) { } + onPause(callback) { + callbacks$1.pause = callback; } - offHeadersReceived() { } - onHeadersReceived() { } - } - const request = defineTaskApi(API_REQUEST, (args, { resolve, reject }) => { - let { header, method, data, timeout, url, responseType, sslVerify, firstIpv4, - // NOTE 属性有但是types没有 - // @ts-ignore - tls, } = args; - let contentType; - for (const name in header) { - if (name.toLowerCase() === 'content-type') { - contentType = header[name]; - break; - } + onResume(callback) { + callbacks$1.resume = callback; } - if (method !== 'GET' && - contentType.indexOf('application/json') === 0 && - isPlainObject(data)) { - data = JSON.stringify(data); + onStart(callback) { + callbacks$1.start = callback; } - const stream = requireNativePlugin('stream'); - const headers = {}; - let abortTimeout; - let aborted; - let hasContentType = false; - for (const name in header) { - if (!hasContentType && name.toLowerCase() === 'content-type') { - hasContentType = true; - headers['Content-Type'] = header[name]; - // TODO 需要重构 - if (method !== 'GET' && - header[name].indexOf('application/x-www-form-urlencoded') === 0 && - typeof data !== 'string' && - !(data instanceof ArrayBuffer)) { - const bodyArray = []; - for (const key in data) { - if (hasOwn$1(data, key)) { - bodyArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); - } - } - data = bodyArray.join('&'); - } - } - else { - headers[name] = header[name]; - } + onStop(callback) { + callbacks$1.stop = callback; } - if (!hasContentType && method === 'POST') { - headers['Content-Type'] = - 'application/x-www-form-urlencoded; charset=UTF-8'; + pause() { + Recorder.pause(); } - if (timeout) { - abortTimeout = setTimeout(() => { - aborted = true; - reject('timeout'); - }, timeout + 200); // TODO +200 发消息到原生层有时间开销,以后考虑由原生层回调超时 + resume() { + Recorder.resume(); } - const options = { - method, - url: url.trim(), - // weex 官方文档有误,headers 类型实际 object,用 string 类型会无响应 - headers, - type: responseType === 'arraybuffer' ? 'base64' : 'text', - // weex 官方文档未说明实际支持 timeout,单位:ms - timeout: timeout || 6e5, - // 配置和weex模块内相反 - sslVerify: !sslVerify, - firstIpv4: firstIpv4, - tls, - }; - if (method !== 'GET') { - options.body = typeof data === 'string' ? data : JSON.stringify(data); + start(options) { + Recorder.start(options); } - stream.fetch(options, ({ ok, status, data, headers, errorMsg, }) => { - if (aborted) { - return; - } - if (abortTimeout) { - clearTimeout(abortTimeout); - } - const statusCode = status; - if (statusCode > 0) { - resolve(formatResponse({ - data: ok && responseType === 'arraybuffer' - ? base64ToArrayBuffer(data) - : data, - statusCode, - header: headers, - cookies: cookiesParse(headers), - }, args)); - } - else { - let errMsg = 'abort statusCode:' + statusCode; - if (errorMsg) { - errMsg = errMsg + ' ' + errorMsg; - } - reject(errMsg); - } - }); - return new RequestTask({ - abort() { - aborted = true; - if (abortTimeout) { - clearTimeout(abortTimeout); - } - reject('abort'); - }, - }); - }, RequestProtocol, RequestOptions); - - const socketTasks = []; - const socketsMap = {}; - const globalEvent = { - open: '', - close: '', - error: '', - message: '', - }; - let socket; - function createSocketTask(args) { - const socketId = String(Date.now()); - let errMsg; - try { - if (!socket) { - socket = requireNativePlugin('uni-webSocket'); - bindSocketCallBack(socket); - } - socket.WebSocket({ - id: socketId, - url: args.url, - protocol: Array.isArray(args.protocols) - ? args.protocols.join(',') - : args.protocols, - header: args.header, - }); - } - catch (error) { - errMsg = error; + stop() { + Recorder.stop(); } - return { socket, socketId, errMsg }; } - function bindSocketCallBack(socket) { - socket.onopen((e) => { - const curSocket = socketsMap[e.id]; - if (!curSocket) - return; - curSocket._socketOnOpen(); - }); - socket.onmessage((e) => { - const curSocket = socketsMap[e.id]; - if (!curSocket) - return; - curSocket._socketOnMessage(e); - }); - socket.onerror((e) => { - const curSocket = socketsMap[e.id]; - if (!curSocket) - return; - curSocket._socketOnError(); + let recorderManager; + const getRecorderManager = defineSyncApi(API_GET_RECORDER_MANAGER, () => recorderManager || (recorderManager = new RecorderManager())); + + const saveVideoToPhotosAlbum = defineAsyncApi(API_SAVE_VIDEO_TO_PHOTOS_ALBUM, (options, { resolve, reject }) => { + plus.gallery.save(options.filePath, warpPlusSuccessCallback(resolve), warpPlusErrorCallback(reject)); + }, SaveVideoToPhotosAlbumProtocol, SaveVideoToPhotosAlbumOptions); + + const saveImageToPhotosAlbum = defineAsyncApi(API_SAVE_IMAGE_TO_PHOTOS_ALBUM, (options, { resolve, reject }) => { + plus.gallery.save(options.filePath, warpPlusSuccessCallback(resolve), warpPlusErrorCallback(reject)); + }, SaveImageToPhotosAlbumProtocol, SaveImageToPhotosAlbumOptions); + + function getFileName(path) { + const array = path.split('/'); + return array[array.length - 1]; + } + + const compressImage$1 = defineAsyncApi(API_COMPRESS_IMAGE, (options, { resolve, reject }) => { + const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(options.src)}`; + plus.zip.compressImage(extend({}, options, { + dst, + }), () => { + resolve({ + tempFilePath: dst, + }); + }, reject); + }, CompressImageProtocol, CompressImageOptions); + + const compressVideo = defineAsyncApi(API_COMPRESS_VIDEO, (options, { resolve, reject }) => { + const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(options.src)}`; + plus.zip.compressVideo(extend({}, options, { + dst, + }), () => { + resolve({ + tempFilePath: dst, + }); + }, reject); + }, CompressVideoProtocol, CompressVideoOptions); + + /** + * 获取文件信息 + * @param {string} filePath 文件路径 + * @returns {Promise} 文件信息Promise + */ + function getFileInfo(filePath) { + return new Promise((resolve, reject) => { + plus.io.resolveLocalFileSystemURL(filePath, function (entry) { + entry.getMetadata(resolve, reject, false); + }, reject); }); - socket.onclose((e) => { - const curSocket = socketsMap[e.id]; - if (!curSocket) - return; - curSocket._socketOnClose(); + } + function compressImage(tempFilePath) { + const dst = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(tempFilePath)}`; + return new Promise((resolve) => { + plus.nativeUI.showWaiting(); + plus.zip.compressImage({ + src: tempFilePath, + dst, + overwrite: true, + }, () => { + plus.nativeUI.closeWaiting(); + resolve(dst); + }, () => { + plus.nativeUI.closeWaiting(); + resolve(tempFilePath); + }); }); } - class SocketTask { - constructor(socket, socketId) { - this.id = socketId; - this._socket = socket; - this._callbacks = { - open: [], - close: [], - error: [], - message: [], - }; - this.CLOSED = 3; - this.CLOSING = 2; - this.CONNECTING = 0; - this.OPEN = 1; - this.readyState = this.CLOSED; - if (!this._socket) - return; - } - _socketOnOpen() { - this.readyState = this.OPEN; - this.socketStateChange('open'); + const chooseImage = defineAsyncApi(API_CHOOSE_IMAGE, + // @ts-ignore crop 属性App特有 + ({ count, sizeType, sourceType, crop } = {}, { resolve, reject }) => { + initI18nChooseImageMsgsOnce(); + const { t } = useI18n(); + const errorCallback = warpPlusErrorCallback(reject); + function successCallback(paths) { + const tempFiles = []; + const tempFilePaths = []; + // plus.zip.compressImage 压缩文件并发调用在iOS端容易出现问题(图像错误、闪退),改为队列执行 + paths + .reduce((promise, path) => { + return promise + .then(() => { + return getFileInfo(path); + }) + .then((fileInfo) => { + const size = fileInfo.size; + // 压缩阈值 0.5 兆 + const THRESHOLD = 1024 * 1024 * 0.5; + // 判断是否需要压缩 + if (!crop && + sizeType.includes('compressed') && + size > THRESHOLD) { + return compressImage(path).then((dstPath) => { + path = dstPath; + return getFileInfo(path); + }); + } + return fileInfo; + }) + .then(({ size }) => { + tempFilePaths.push(path); + tempFiles.push({ + path, + size: size, + }); + }); + }, Promise.resolve()) + .then(() => { + resolve({ + tempFilePaths, + tempFiles, + }); + }) + .catch(errorCallback); } - _socketOnMessage(e) { - this.socketStateChange('message', { - data: typeof e.data === 'object' - ? base64ToArrayBuffer(e.data.base64) - : e.data, + function openCamera() { + const camera = plus.camera.getCamera(); + camera.captureImage((path) => successCallback([path]), errorCallback, { + filename: TEMP_PATH + '/camera/', + resolution: 'high', + crop, }); } - _socketOnError() { - this.socketStateChange('error'); - this.onErrorOrClose(); + function openAlbum() { + // NOTE 5+此API分单选和多选,多选返回files:string[] + // @ts-ignore + plus.gallery.pick(({ files }) => successCallback(files), errorCallback, { + maximum: count, + multiple: true, + system: false, + filename: TEMP_PATH + '/gallery/', + permissionAlert: true, + crop, + }); } - _socketOnClose() { - this.socketStateChange('close'); - this.onErrorOrClose(); + if (sourceType.length === 1) { + if (sourceType.includes('album')) { + openAlbum(); + return; + } + else if (sourceType.includes('camera')) { + openCamera(); + return; + } } - onErrorOrClose() { - this.readyState = this.CLOSED; - delete socketsMap[this.id]; - const index = socketTasks.indexOf(this); - if (index >= 0) { - socketTasks.splice(index, 1); + plus.nativeUI.actionSheet({ + cancel: t('uni.chooseImage.cancel'), + buttons: [ + { + title: t('uni.chooseImage.sourceType.camera'), + }, + { + title: t('uni.chooseImage.sourceType.album'), + }, + ], + }, (e) => { + switch (e.index) { + case 1: + openCamera(); + break; + case 2: + openAlbum(); + break; + default: + errorCallback(); + break; } - } - socketStateChange(name, res = {}) { - const data = name === 'message' ? res : {}; - if (this === socketTasks[0] && globalEvent[name]) { - UniServiceJSBridge.invokeOnCallback(globalEvent[name], data); + }); + }, ChooseImageProtocol, ChooseImageOptions); + + const chooseVideo = defineAsyncApi(API_CHOOSE_VIDEO, ({ sourceType, compressed, maxDuration, camera }, { resolve, reject }) => { + initI18nChooseVideoMsgsOnce(); + const { t } = useI18n(); + const errorCallback = warpPlusErrorCallback(reject); + function successCallback(tempFilePath = '') { + const filename = `${TEMP_PATH}/compressed/${Date.now()}_${getFileName(tempFilePath)}`; + const compressVideo = compressed + ? new Promise((resolve) => { + plus.zip.compressVideo({ + src: tempFilePath, + filename, + }, ({ tempFilePath }) => { + resolve(tempFilePath); + }, () => { + resolve(tempFilePath); + }); + }) + : Promise.resolve(tempFilePath); + if (compressed) { + plus.nativeUI.showWaiting(); } - // WYQ fix: App平台修复websocket onOpen时发送数据报错的Bug - this._callbacks[name].forEach((callback) => { - if (typeof callback === 'function') { - callback(data); + compressVideo.then((tempFilePath) => { + if (compressed) { + plus.nativeUI.closeWaiting(); } + plus.io.getVideoInfo({ + filePath: tempFilePath, + success(videoInfo) { + const result = { + errMsg: 'chooseVideo:ok', + tempFilePath: tempFilePath, + size: videoInfo.size, + duration: videoInfo.duration, + width: videoInfo.width, + height: videoInfo.height, + }; + resolve(result); + }, + fail: errorCallback, + }); }); } - send(args, callopt = true) { - if (this.readyState !== this.OPEN) { - callOptions(args, 'sendSocketMessage:fail WebSocket is not connected'); - } - try { - this._socket.send({ - id: this.id, - data: typeof args.data === 'object' - ? { - '@type': 'binary', - base64: arrayBufferToBase64(args.data), - } - : args.data, - }); - callopt && callOptions(args, 'sendSocketMessage:ok'); - } - catch (error) { - callopt && callOptions(args, `sendSocketMessage:fail ${error}`); - } + function openAlbum() { + plus.gallery.pick( + // NOTE 5+此API分单选和多选,多选返回files:string[] + // @ts-ignore + ({ files }) => successCallback(files[0]), errorCallback, { + filter: 'video', + system: false, + // 不启用 multiple 时 system 无效 + multiple: true, + maximum: 1, + filename: TEMP_PATH + '/gallery/', + permissionAlert: true, + }); } - close(args, callopt = true) { - this.readyState = this.CLOSING; - try { - this._socket.close(extend({ - id: this.id, - args, - })); - callopt && callOptions(args, 'closeSocket:ok'); + function openCamera() { + const plusCamera = plus.camera.getCamera(); + plusCamera.startVideoCapture(successCallback, errorCallback, { + index: camera === 'front' ? '2' : '1', + videoMaximumDuration: maxDuration, + filename: TEMP_PATH + '/camera/', + }); + } + if (sourceType.length === 1) { + if (sourceType.includes('album')) { + openAlbum(); + return; } - catch (error) { - callopt && callOptions(args, `closeSocket:fail ${error}`); + else if (sourceType.includes('camera')) { + openCamera(); + return; } } - onOpen(callback) { - this._callbacks.open.push(callback); - } - onClose(callback) { - this._callbacks.close.push(callback); - } - onError(callback) { - this._callbacks.error.push(callback); - } - onMessage(callback) { - this._callbacks.message.push(callback); - } - } - const connectSocket = defineTaskApi(API_CONNECT_SOCKET, ({ url, protocols, header, method }, { resolve, reject }) => { - const { socket, socketId, errMsg } = createSocketTask({ - url, - protocols, - header, - method, + plus.nativeUI.actionSheet({ + cancel: t('uni.chooseVideo.cancel'), + buttons: [ + { + title: t('uni.chooseVideo.sourceType.camera'), + }, + { + title: t('uni.chooseVideo.sourceType.album'), + }, + ], + }, (e) => { + switch (e.index) { + case 1: + openCamera(); + break; + case 2: + openAlbum(); + break; + default: + errorCallback(); + break; + } }); - const socketTask = new SocketTask(socket, socketId); - if (errMsg) { - setTimeout(() => { - reject(errMsg); - }, 0); - } - else { - socketTasks.push(socketTask); - socketsMap[socketId] = socketTask; - } - setTimeout(() => { - resolve(); - }, 0); - return socketTask; - }, ConnectSocketProtocol, ConnectSocketOptions); - const sendSocketMessage = defineAsyncApi(API_SEND_SOCKET_MESSAGE, (args, { resolve, reject }) => { - const socketTask = socketTasks[0]; - if (!socketTask || socketTask.readyState !== socketTask.OPEN) { - reject('WebSocket is not connected'); - return; - } - socketTask.send({ data: args.data }, false); + }, ChooseVideoProtocol, ChooseVideoOptions); + + const showKeyboard = defineAsyncApi(API_SHOW_KEYBOARD, (_, { resolve }) => { + plus.key.showSoftKeybord(); resolve(); - }, SendSocketMessageProtocol); - const closeSocket = defineAsyncApi(API_CLOSE_SOCKET, (args, { resolve, reject }) => { - const socketTask = socketTasks[0]; - if (!socketTask) { - reject('WebSocket is not connected'); - return; - } - socketTask.readyState = socketTask.CLOSING; - socketTask.close(args, false); + }); + const hideKeyboard = defineAsyncApi(API_HIDE_KEYBOARD, (_, { resolve }) => { + plus.key.hideSoftKeybord(); resolve(); - }, CloseSocketProtocol); - function on(event) { - const api = `onSocket${capitalize(event)}`; - return defineOnApi(api, () => { - globalEvent[event] = api; - }); - } - const onSocketOpen = /*#__PURE__*/ on('open'); - const onSocketError = /*#__PURE__*/ on('error'); - const onSocketMessage = /*#__PURE__*/ on('message'); - const onSocketClose = /*#__PURE__*/ on('close'); + }); - class UploadTask { - constructor(uploader) { + class DownloadTask { + constructor(downloader) { this._callbacks = []; - this._uploader = uploader; - uploader.addEventListener('statechanged', (upload, status) => { - if (upload.uploadedSize && upload.totalSize) { + this._downloader = downloader; + downloader.addEventListener('statechanged', (download, status) => { + if (download.downloadedSize && download.totalSize) { this._callbacks.forEach((callback) => { callback({ - progress: parseInt(String((upload.uploadedSize / upload.totalSize) * 100)), - totalBytesSent: upload.uploadedSize, - totalBytesExpectedToSend: upload.totalSize, + progress: Math.round((download.downloadedSize / download.totalSize) * 100), + totalBytesWritten: download.downloadedSize, + totalBytesExpectedToWrite: download.totalSize, }); }); } }); } abort() { - this._uploader.abort(); + this._downloader.abort(); } onProgressUpdate(callback) { if (typeof callback !== 'function') { @@ -4743,25 +6131,34 @@ var serviceContext = (function (vue) { } this._callbacks.push(callback); } - onHeadersReceived() { } offProgressUpdate(callback) { const index = this._callbacks.indexOf(callback); if (index >= 0) { this._callbacks.splice(index, 1); } } - offHeadersReceived() { } + onHeadersReceived(callback) { + throw new Error('Method not implemented.'); + } + offHeadersReceived(callback) { + throw new Error('Method not implemented.'); + } } - const uploadFile = defineTaskApi(API_UPLOAD_FILE, ({ url, timeout, header, formData, files, filePath, name }, { resolve, reject }) => { - const uploader = plus.uploader.createUpload(url, { - timeout, - // 需要与其它平台上的表现保持一致,不走重试的逻辑。 - retry: 0, - retryInterval: 0, - }, (upload, statusCode) => { + const downloadFile = defineTaskApi(API_DOWNLOAD_FILE, ({ url, header, timeout }, { resolve, reject }) => { + timeout = + (timeout || + (__uniConfig.networkTimeout && __uniConfig.networkTimeout.request) || + 60 * 1000) / 1000; + const downloader = plus.downloader.createDownload(url, { + timeout, + filename: TEMP_PATH + '/download/', + // 需要与其它平台上的表现保持一致,不走重试的逻辑。 + retry: 0, + retryInterval: 0, + }, (download, statusCode) => { if (statusCode) { resolve({ - data: upload.responseText, + tempFilePath: download.filename, statusCode, }); } @@ -4769,3213 +6166,10361 @@ var serviceContext = (function (vue) { reject(`statusCode: ${statusCode}`); } }); + const downloadTask = new DownloadTask(downloader); for (const name in header) { if (hasOwn$1(header, name)) { - uploader.setRequestHeader(name, String(header[name])); - } - } - for (const name in formData) { - if (hasOwn$1(formData, name)) { - uploader.addData(name, String(formData[name])); + downloader.setRequestHeader(name, header[name]); } } - if (files && files.length) { - files.forEach((file) => { - uploader.addFile(getRealPath(file.uri), { - key: file.name || 'file', - }); - }); - } - else { - uploader.addFile(getRealPath(filePath), { - key: name, - }); - } - const uploadFileTask = new UploadTask(uploader); - uploader.start(); - return uploadFileTask; - }, UploadFileProtocol, UploadFileOptions); + downloader.start(); + return downloadTask; + }, DownloadFileProtocol, DownloadFileOptions); - const audios = {}; - const evts = [ - 'play', - 'canplay', - 'ended', - 'stop', - 'waiting', - 'seeking', - 'seeked', - 'pause', - ]; - const initStateChage = (audioId) => { - const audio = audios[audioId]; - if (!audio) { - return; + const cookiesParse = (header) => { + let cookiesStr = header['Set-Cookie'] || header['set-cookie']; + let cookiesArr = []; + if (!cookiesStr) { + return []; } - if (!audio.initStateChage) { - audio.initStateChage = true; - audio.addEventListener('error', (error) => { - onAudioStateChange({ - state: 'error', - audioId, - errMsg: 'MediaError', - errCode: error.code, - }); - }); - evts.forEach((event) => { - audio.addEventListener(event, () => { - // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 - if (event === 'play') { - audio.isStopped = false; - } - else if (event === 'stop') { - audio.isStopped = true; - } - onAudioStateChange({ - state: event, - audioId, - }); - }); - }); + if (cookiesStr[0] === '[' && cookiesStr[cookiesStr.length - 1] === ']') { + cookiesStr = cookiesStr.slice(1, -1); } - }; - function createAudioInstance() { - const audioId = `${Date.now()}${Math.random()}`; - const audio = (audios[audioId] = plus.audio.createPlayer('')); // 此处空字符串必填 - audio.src = ''; - audio.volume = 1; - audio.startTime = 0; - return { - errMsg: 'createAudioInstance:ok', - audioId, - }; - } - function setAudioState({ audioId, src, startTime, autoplay = false, loop = false, obeyMuteSwitch, volume, }) { - const audio = audios[audioId]; - if (audio) { - const style = { - loop, - autoplay, - }; - if (src) { - audio.src = style.src = getRealPath(src); + const handleCookiesArr = cookiesStr.split(';'); + for (let i = 0; i < handleCookiesArr.length; i++) { + if (handleCookiesArr[i].indexOf('Expires=') !== -1 || + handleCookiesArr[i].indexOf('expires=') !== -1) { + cookiesArr.push(handleCookiesArr[i].replace(',', '')); } - if (startTime) { - audio.startTime = style.startTime = startTime; + else { + cookiesArr.push(handleCookiesArr[i]); } - if (typeof volume === 'number') { - audio.volume = style.volume = volume; + } + cookiesArr = cookiesArr.join(';').split(','); + return cookiesArr; + }; + function formatResponse(res, args) { + if (typeof res.data === 'string' && res.data.charCodeAt(0) === 65279) { + res.data = res.data.substr(1); + } + res.statusCode = parseInt(String(res.statusCode), 10); + if (isPlainObject(res.header)) { + res.header = Object.keys(res.header).reduce(function (ret, key) { + const value = res.header[key]; + if (Array.isArray(value)) { + ret[key] = value.join(','); + } + else if (typeof value === 'string') { + ret[key] = value; + } + return ret; + }, {}); + } + if (args.dataType && args.dataType.toLowerCase() === 'json') { + try { + res.data = JSON.parse(res.data); } - audio.setStyles(style); - initStateChage(audioId); + catch (e) { } } - return { - errMsg: 'setAudioState:ok', - }; + return res; } - function getAudioState({ audioId }) { - const audio = audios[audioId]; - if (!audio) { - return { - errMsg: 'getAudioState:fail', - }; + /** + * 请求任务类 + */ + class RequestTask { + constructor(requestTask) { + this._requestTask = requestTask; } - const { src, startTime, volume } = audio; - return { - errMsg: 'getAudioState:ok', - duration: 1e3 * (audio.getDuration() || 0), - currentTime: audio.isStopped ? 0 : 1e3 * audio.getPosition(), - paused: audio.isPaused(), - src, - volume, - startTime: 1e3 * startTime, - buffered: 1e3 * audio.getBuffered(), - }; + abort() { + this._requestTask.abort(); + } + offHeadersReceived() { } + onHeadersReceived() { } } - function operateAudio({ operationType, audioId, currentTime, }) { - const audio = audios[audioId]; - switch (operationType) { - case 'play': - case 'pause': - case 'stop': - audio[operationType === 'play' && audio.isPaused() ? 'resume' : operationType](); - break; - case 'seek': - typeof currentTime != 'undefined' ? audio.seekTo(currentTime / 1e3) : ''; + const request = defineTaskApi(API_REQUEST, (args, { resolve, reject }) => { + let { header, method, data, timeout, url, responseType, sslVerify, firstIpv4, + // NOTE 属性有但是types没有 + // @ts-ignore + tls, } = args; + let contentType; + for (const name in header) { + if (name.toLowerCase() === 'content-type') { + contentType = header[name]; break; + } } - return { - errMsg: 'operateAudio:ok', - }; - } - const innerAudioContexts = Object.create(null); - const onAudioStateChange = ({ state, audioId, errMsg, errCode, }) => { - const audio = innerAudioContexts[audioId]; - if (audio) { - emit(audio, state, errMsg, errCode); - if (state === 'play') { - const oldCurrentTime = audio.currentTime; - audio.__timing = setInterval(() => { - const currentTime = audio.currentTime; - if (currentTime !== oldCurrentTime) { - emit(audio, 'timeUpdate'); + if (method !== 'GET' && + contentType.indexOf('application/json') === 0 && + isPlainObject(data)) { + data = JSON.stringify(data); + } + const stream = requireNativePlugin('stream'); + const headers = {}; + let abortTimeout; + let aborted; + let hasContentType = false; + for (const name in header) { + if (!hasContentType && name.toLowerCase() === 'content-type') { + hasContentType = true; + headers['Content-Type'] = header[name]; + // TODO 需要重构 + if (method !== 'GET' && + header[name].indexOf('application/x-www-form-urlencoded') === 0 && + typeof data !== 'string' && + !(data instanceof ArrayBuffer)) { + const bodyArray = []; + for (const key in data) { + if (hasOwn$1(data, key)) { + bodyArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); + } } - }, 200); + data = bodyArray.join('&'); + } } - else if (state === 'pause' || state === 'stop' || state === 'error') { - clearInterval(audio.__timing); + else { + headers[name] = header[name]; } } - }; - const props$1 = [ - { - name: 'src', - cache: true, - }, - { - name: 'startTime', - default: 0, - cache: true, - }, - { - name: 'autoplay', - default: false, - cache: true, - }, - { - name: 'loop', - default: false, - cache: true, - }, - { - name: 'obeyMuteSwitch', - default: true, - readonly: true, - cache: true, - }, - { - name: 'duration', - readonly: true, - }, - { - name: 'currentTime', - readonly: true, - }, - { - name: 'paused', - readonly: true, - }, - { - name: 'buffered', - readonly: true, - }, - { - name: 'volume', - }, - ]; - class InnerAudioContext { - constructor(id) { - this.id = id; - this._callbacks = {}; - this._options = {}; - // 初始化事件监听列表 - innerAudioContextEventNames.forEach((eventName) => { - this._callbacks[eventName] = []; - }); - props$1.forEach((item) => { - const name = item.name; - Object.defineProperty(this, name, { - get: () => { - const result = item.cache - ? this._options - : getAudioState({ - audioId: this.id, - }); - const value = name in result ? result[name] : item.default; - return typeof value === 'number' && name !== 'volume' - ? value / 1e3 - : value; - }, - set: item.readonly - ? undefined - : (value) => { - this._options[name] = value; - setAudioState(extend({}, this._options, { - audioId: this.id, - })); - }, - }); - }); - initInnerAudioContextEventOnce(); - } - play() { - this._operate('play'); - } - pause() { - this._operate('pause'); + if (!hasContentType && method === 'POST') { + headers['Content-Type'] = + 'application/x-www-form-urlencoded; charset=UTF-8'; } - stop() { - this._operate('stop'); + if (timeout) { + abortTimeout = setTimeout(() => { + aborted = true; + reject('timeout'); + }, timeout + 200); // TODO +200 发消息到原生层有时间开销,以后考虑由原生层回调超时 } - seek(position) { - this._operate('seek', { - currentTime: position * 1e3, - }); + const options = { + method, + url: url.trim(), + // weex 官方文档有误,headers 类型实际 object,用 string 类型会无响应 + headers, + type: responseType === 'arraybuffer' ? 'base64' : 'text', + // weex 官方文档未说明实际支持 timeout,单位:ms + timeout: timeout || 6e5, + // 配置和weex模块内相反 + sslVerify: !sslVerify, + firstIpv4: firstIpv4, + tls, + }; + if (method !== 'GET') { + options.body = typeof data === 'string' ? data : JSON.stringify(data); } - destroy() { - clearInterval(this.__timing); - if (audios[this.id]) { - audios[this.id].close(); - delete audios[this.id]; + stream.fetch(options, ({ ok, status, data, headers, errorMsg, }) => { + if (aborted) { + return; } - delete innerAudioContexts[this.id]; - } - _operate(type, options) { - operateAudio(extend({}, options, { - audioId: this.id, - operationType: type, - })); - } - } - const initInnerAudioContextEventOnce = /*#__PURE__*/ once(() => { - // 批量设置音频上下文事件监听方法 - innerAudioContextEventNames.forEach((eventName) => { - InnerAudioContext.prototype[eventName] = function (callback) { - if (typeof callback === 'function') { - this._callbacks[eventName].push(callback); + if (abortTimeout) { + clearTimeout(abortTimeout); + } + const statusCode = status; + if (statusCode > 0) { + resolve(formatResponse({ + data: ok && responseType === 'arraybuffer' + ? base64ToArrayBuffer(data) + : data, + statusCode, + header: headers, + cookies: cookiesParse(headers), + }, args)); + } + else { + let errMsg = 'abort statusCode:' + statusCode; + if (errorMsg) { + errMsg = errMsg + ' ' + errorMsg; } - }; + reject(errMsg); + } }); - // 批量设置音频上下文事件取消监听方法 - innerAudioContextOffEventNames.forEach((eventName) => { - InnerAudioContext.prototype[eventName] = function (callback) { - const callbacks = this._callbacks[eventName]; - const index = callbacks.indexOf(callback); - if (index >= 0) { - callbacks.splice(index, 1); + return new RequestTask({ + abort() { + aborted = true; + if (abortTimeout) { + clearTimeout(abortTimeout); } - }; - }); - }); - function emit(audio, state, errMsg, errCode) { - const name = `on${capitalize(state)}`; - audio._callbacks[name].forEach((callback) => { - if (typeof callback === 'function') { - callback(state === 'error' - ? { - errMsg, - errCode, - } - : {}); - } + reject('abort'); + }, }); - } - /** - * 创建音频上下文 - */ - const createInnerAudioContext = defineSyncApi(API_CREATE_INNER_AUDIO_CONTEXT, () => { - const { audioId } = createAudioInstance(); - const innerAudioContext = new InnerAudioContext(audioId); - innerAudioContexts[audioId] = innerAudioContext; - return innerAudioContext; - }); + }, RequestProtocol, RequestOptions); - const eventNames = [ - 'canplay', - 'play', - 'pause', - 'stop', - 'ended', - 'timeUpdate', - 'prev', - 'next', - 'error', - 'waiting', - ]; - const callbacks = { - canplay: [], - play: [], - pause: [], - stop: [], - ended: [], - timeUpdate: [], - prev: [], - next: [], - error: [], - waiting: [], + const socketTasks = []; + const socketsMap = {}; + const globalEvent = { + open: '', + close: '', + error: '', + message: '', }; - let audio; - let timeUpdateTimer = null; - const TIME_UPDATE = 250; - const events = ['play', 'pause', 'ended', 'stop', 'canplay']; - function startTimeUpdateTimer() { - stopTimeUpdateTimer(); - timeUpdateTimer = setInterval(() => { - onBackgroundAudioStateChange({ state: 'timeUpdate' }); - }, TIME_UPDATE); - } - function stopTimeUpdateTimer() { - if (timeUpdateTimer !== null) { - clearInterval(timeUpdateTimer); + let socket; + function createSocketTask(args) { + const socketId = String(Date.now()); + let errMsg; + try { + if (!socket) { + socket = requireNativePlugin('uni-webSocket'); + bindSocketCallBack(socket); + } + socket.WebSocket({ + id: socketId, + url: args.url, + protocol: Array.isArray(args.protocols) + ? args.protocols.join(',') + : args.protocols, + header: args.header, + }); } - } - function initMusic() { - if (audio) { - return; + catch (error) { + errMsg = error; } - const publish = UniServiceJSBridge.invokeOnCallback; - audio = plus.audio.createPlayer({ - autoplay: true, - backgroundControl: true, + return { socket, socketId, errMsg }; + } + function bindSocketCallBack(socket) { + socket.onopen((e) => { + const curSocket = socketsMap[e.id]; + if (!curSocket) + return; + curSocket._socketOnOpen(); }); - audio.src = - audio.title = - audio.epname = - audio.singer = - audio.coverImgUrl = - audio.webUrl = - ''; - audio.startTime = 0; - events.forEach((event) => { - audio.addEventListener(event, () => { - // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 - if (event === 'play') { - audio.isStopped = false; - startTimeUpdateTimer(); - } - else if (event === 'stop') { - audio.isStopped = true; - } - if (event === 'pause' || event === 'ended' || event === 'stop') { - stopTimeUpdateTimer(); - } - const eventName = `onMusic${event[0].toUpperCase() + event.substr(1)}`; - publish(eventName, { - dataUrl: audio.src, - errMsg: `${eventName}:ok`, - }); - onBackgroundAudioStateChange({ - state: event, - dataUrl: audio.src, - }); - }); + socket.onmessage((e) => { + const curSocket = socketsMap[e.id]; + if (!curSocket) + return; + curSocket._socketOnMessage(e); }); - audio.addEventListener('waiting', () => { - stopTimeUpdateTimer(); - onBackgroundAudioStateChange({ - state: 'waiting', - dataUrl: audio.src, - }); + socket.onerror((e) => { + const curSocket = socketsMap[e.id]; + if (!curSocket) + return; + curSocket._socketOnError(); }); - audio.addEventListener('error', (err) => { - stopTimeUpdateTimer(); - publish('onMusicError', { - dataUrl: audio.src, - errMsg: 'Error:' + err.message, - }); - onBackgroundAudioStateChange({ - state: 'error', - dataUrl: audio.src, - errMsg: err.message, - errCode: err.code, - }); + socket.onclose((e) => { + const curSocket = socketsMap[e.id]; + if (!curSocket) + return; + curSocket._socketOnClose(); }); - // @ts-ignore - audio.addEventListener('prev', () => publish('onBackgroundAudioPrev')); - // @ts-ignore - audio.addEventListener('next', () => publish('onBackgroundAudioNext')); } - function getBackgroundAudioState() { - let data = { - duration: 0, - currentTime: 0, - paused: false, - src: '', - buffered: 0, - title: '', - epname: '', - singer: '', - coverImgUrl: '', - webUrl: '', - startTime: 0, - errMsg: 'getBackgroundAudioState:ok', - }; - if (audio) { - const newData = { - duration: audio.getDuration() || 0, - currentTime: audio.isStopped ? 0 : audio.getPosition(), - paused: audio.isPaused(), - src: audio.src, - buffered: audio.getBuffered(), - title: audio.title, - epname: audio.epname, - singer: audio.singer, - coverImgUrl: audio.coverImgUrl, - webUrl: audio.webUrl, - startTime: audio.startTime, + class SocketTask { + constructor(socket, socketId) { + this.id = socketId; + this._socket = socket; + this._callbacks = { + open: [], + close: [], + error: [], + message: [], }; - data = extend(data, newData); + this.CLOSED = 3; + this.CLOSING = 2; + this.CONNECTING = 0; + this.OPEN = 1; + this.readyState = this.CLOSED; + if (!this._socket) + return; } - return data; - } - function setMusicState(args) { - initMusic(); - const props = [ - 'src', - 'startTime', - 'coverImgUrl', - 'webUrl', - 'singer', - 'epname', - 'title', - ]; - const style = {}; - Object.keys(args).forEach((key) => { - if (props.indexOf(key) >= 0) { - let val = args[key]; - if (key === props[0] && val) { - val = getRealPath(val); - } - audio[key] = style[key] = val; + _socketOnOpen() { + this.readyState = this.OPEN; + this.socketStateChange('open'); + } + _socketOnMessage(e) { + this.socketStateChange('message', { + data: typeof e.data === 'object' + ? base64ToArrayBuffer(e.data.base64) + : e.data, + }); + } + _socketOnError() { + this.socketStateChange('error'); + this.onErrorOrClose(); + } + _socketOnClose() { + this.socketStateChange('close'); + this.onErrorOrClose(); + } + onErrorOrClose() { + this.readyState = this.CLOSED; + delete socketsMap[this.id]; + const index = socketTasks.indexOf(this); + if (index >= 0) { + socketTasks.splice(index, 1); } - }); - audio.setStyles(style); - } - function operateMusicPlayer({ operationType, src, position, api = 'operateMusicPlayer', title, coverImgUrl, }) { - var operationTypes = ['resume', 'pause', 'stop']; - if (operationTypes.indexOf(operationType) > 0) { - audio && audio[operationType](); } - else if (operationType === 'play') { - setMusicState({ - src, - startTime: position, - title, - coverImgUrl, + socketStateChange(name, res = {}) { + const data = name === 'message' ? res : {}; + if (this === socketTasks[0] && globalEvent[name]) { + UniServiceJSBridge.invokeOnCallback(globalEvent[name], data); + } + // WYQ fix: App平台修复websocket onOpen时发送数据报错的Bug + this._callbacks[name].forEach((callback) => { + if (typeof callback === 'function') { + callback(data); + } }); - audio.play(); } - else if (operationType === 'seek') { - audio && audio.seekTo(position); + send(args, callopt = true) { + if (this.readyState !== this.OPEN) { + callOptions(args, 'sendSocketMessage:fail WebSocket is not connected'); + } + try { + this._socket.send({ + id: this.id, + data: typeof args.data === 'object' + ? { + '@type': 'binary', + base64: arrayBufferToBase64(args.data), + } + : args.data, + }); + callopt && callOptions(args, 'sendSocketMessage:ok'); + } + catch (error) { + callopt && callOptions(args, `sendSocketMessage:fail ${error}`); + } + } + close(args, callopt = true) { + this.readyState = this.CLOSING; + try { + this._socket.close(extend({ + id: this.id, + args, + })); + callopt && callOptions(args, 'closeSocket:ok'); + } + catch (error) { + callopt && callOptions(args, `closeSocket:fail ${error}`); + } + } + onOpen(callback) { + this._callbacks.open.push(callback); + } + onClose(callback) { + this._callbacks.close.push(callback); + } + onError(callback) { + this._callbacks.error.push(callback); + } + onMessage(callback) { + this._callbacks.message.push(callback); } - return { - errMsg: `${api}:ok`, - }; } - function operateBackgroundAudio({ operationType, src, startTime, currentTime, }) { - return operateMusicPlayer({ - operationType, - src, - position: startTime || currentTime || 0, - api: 'operateBackgroundAudio', + const connectSocket = defineTaskApi(API_CONNECT_SOCKET, ({ url, protocols, header, method }, { resolve, reject }) => { + const { socket, socketId, errMsg } = createSocketTask({ + url, + protocols, + header, + method, }); - } - function onBackgroundAudioStateChange({ state, errMsg, errCode, dataUrl, }) { - callbacks[state].forEach((callback) => { - if (typeof callback === 'function') { - callback(state === 'error' - ? { - errMsg, - errCode, - } - : {}); - } + const socketTask = new SocketTask(socket, socketId); + if (errMsg) { + setTimeout(() => { + reject(errMsg); + }, 0); + } + else { + socketTasks.push(socketTask); + socketsMap[socketId] = socketTask; + } + setTimeout(() => { + resolve(); + }, 0); + return socketTask; + }, ConnectSocketProtocol, ConnectSocketOptions); + const sendSocketMessage = defineAsyncApi(API_SEND_SOCKET_MESSAGE, (args, { resolve, reject }) => { + const socketTask = socketTasks[0]; + if (!socketTask || socketTask.readyState !== socketTask.OPEN) { + reject('WebSocket is not connected'); + return; + } + socketTask.send({ data: args.data }, false); + resolve(); + }, SendSocketMessageProtocol); + const closeSocket = defineAsyncApi(API_CLOSE_SOCKET, (args, { resolve, reject }) => { + const socketTask = socketTasks[0]; + if (!socketTask) { + reject('WebSocket is not connected'); + return; + } + socketTask.readyState = socketTask.CLOSING; + socketTask.close(args, false); + resolve(); + }, CloseSocketProtocol); + function on(event) { + const api = `onSocket${capitalize(event)}`; + return defineOnApi(api, () => { + globalEvent[event] = api; }); } - const onInitBackgroundAudioManager = /*#__PURE__*/ once(() => { - eventNames.forEach((item) => { - BackgroundAudioManager.prototype[`on${capitalize(item)}`] = - function (callback) { - callbacks[item].push(callback); - }; - }); - }); - const props = [ - { - name: 'duration', - readonly: true, - }, - { - name: 'currentTime', - readonly: true, - }, - { - name: 'paused', - readonly: true, - }, - { - name: 'src', - cache: true, - }, - { - name: 'startTime', - default: 0, - cache: true, - }, - { - name: 'buffered', - readonly: true, - }, - { - name: 'title', - cache: true, - }, - { - name: 'epname', - cache: true, - }, - { - name: 'singer', - cache: true, - }, - { - name: 'coverImgUrl', - cache: true, - }, - { - name: 'webUrl', - cache: true, - }, - { - name: 'protocol', - readonly: true, - default: 'http', - }, - ]; - class BackgroundAudioManager { - constructor() { - this._options = {}; - props.forEach((item) => { - const name = item.name; - Object.defineProperty(this, name, { - get: () => { - const result = item.cache ? this._options : getBackgroundAudioState(); - return name in result ? result[name] : item.default; - }, - set: item.readonly - ? undefined - : (value) => { - this._options[name] = value; - setMusicState(this._options); - }, - }); + const onSocketOpen = /*#__PURE__*/ on('open'); + const onSocketError = /*#__PURE__*/ on('error'); + const onSocketMessage = /*#__PURE__*/ on('message'); + const onSocketClose = /*#__PURE__*/ on('close'); + + class UploadTask { + constructor(uploader) { + this._callbacks = []; + this._uploader = uploader; + uploader.addEventListener('statechanged', (upload, status) => { + if (upload.uploadedSize && upload.totalSize) { + this._callbacks.forEach((callback) => { + callback({ + progress: parseInt(String((upload.uploadedSize / upload.totalSize) * 100)), + totalBytesSent: upload.uploadedSize, + totalBytesExpectedToSend: upload.totalSize, + }); + }); + } }); - onInitBackgroundAudioManager(); } - play() { - this._operate('play'); + abort() { + this._uploader.abort(); } - pause() { - this._operate('pause'); + onProgressUpdate(callback) { + if (typeof callback !== 'function') { + return; + } + this._callbacks.push(callback); } - stop() { - this._operate('stop'); + onHeadersReceived() { } + offProgressUpdate(callback) { + const index = this._callbacks.indexOf(callback); + if (index >= 0) { + this._callbacks.splice(index, 1); + } } - seek(position) { - this._operate('seek', { - currentTime: position, + offHeadersReceived() { } + } + const uploadFile = defineTaskApi(API_UPLOAD_FILE, ({ url, timeout, header, formData, files, filePath, name }, { resolve, reject }) => { + const uploader = plus.uploader.createUpload(url, { + timeout, + // 需要与其它平台上的表现保持一致,不走重试的逻辑。 + retry: 0, + retryInterval: 0, + }, (upload, statusCode) => { + if (statusCode) { + resolve({ + data: upload.responseText, + statusCode, + }); + } + else { + reject(`statusCode: ${statusCode}`); + } + }); + for (const name in header) { + if (hasOwn$1(header, name)) { + uploader.setRequestHeader(name, String(header[name])); + } + } + for (const name in formData) { + if (hasOwn$1(formData, name)) { + uploader.addData(name, String(formData[name])); + } + } + if (files && files.length) { + files.forEach((file) => { + uploader.addFile(getRealPath(file.uri), { + key: file.name || 'file', + }); }); } - _operate(type, options) { - operateBackgroundAudio(extend({}, options, { - operationType: type, - })); + else { + uploader.addFile(getRealPath(filePath), { + key: name, + }); } - } - let backgroundAudioManager; - const getBackgroundAudioManager = defineSyncApi(API_GET_BACKGROUND_AUDIO_MANAGER, () => backgroundAudioManager || - (backgroundAudioManager = new BackgroundAudioManager())); + const uploadFileTask = new UploadTask(uploader); + uploader.start(); + return uploadFileTask; + }, UploadFileProtocol, UploadFileOptions); - const PI = 3.1415926535897932384626; - const a = 6378245.0; - const ee = 0.00669342162296594323; - function gcj02towgs84(lng, lat) { - lat = +lat; - lng = +lng; - if (outOfChina(lng, lat)) { - return [lng, lat]; + const audios = {}; + const evts = [ + 'play', + 'canplay', + 'ended', + 'stop', + 'waiting', + 'seeking', + 'seeked', + 'pause', + ]; + const initStateChage = (audioId) => { + const audio = audios[audioId]; + if (!audio) { + return; } - let dlat = _transformlat(lng - 105.0, lat - 35.0); - let dlng = _transformlng(lng - 105.0, lat - 35.0); - const radlat = (lat / 180.0) * PI; - let magic = Math.sin(radlat); - magic = 1 - ee * magic * magic; - const sqrtmagic = Math.sqrt(magic); - dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); - dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); - const mglat = lat + dlat; - const mglng = lng + dlng; - return [lng * 2 - mglng, lat * 2 - mglat]; - } - function wgs84togcj02(lng, lat) { - lat = +lat; - lng = +lng; - if (outOfChina(lng, lat)) { - return [lng, lat]; + if (!audio.initStateChage) { + audio.initStateChage = true; + audio.addEventListener('error', (error) => { + onAudioStateChange({ + state: 'error', + audioId, + errMsg: 'MediaError', + errCode: error.code, + }); + }); + evts.forEach((event) => { + audio.addEventListener(event, () => { + // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 + if (event === 'play') { + audio.isStopped = false; + } + else if (event === 'stop') { + audio.isStopped = true; + } + onAudioStateChange({ + state: event, + audioId, + }); + }); + }); } - let dlat = _transformlat(lng - 105.0, lat - 35.0); - let dlng = _transformlng(lng - 105.0, lat - 35.0); - const radlat = (lat / 180.0) * PI; - let magic = Math.sin(radlat); - magic = 1 - ee * magic * magic; - const sqrtmagic = Math.sqrt(magic); - dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); - dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); - const mglat = lat + dlat; - const mglng = lng + dlng; - return [mglng, mglat]; - } - const outOfChina = function (lng, lat) { - return (lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271 || false); - }; - const _transformlat = function (lng, lat) { - let ret = -100.0 + - 2.0 * lng + - 3.0 * lat + - 0.2 * lat * lat + - 0.1 * lng * lat + - 0.2 * Math.sqrt(Math.abs(lng)); - ret += - ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * - 2.0) / - 3.0; - ret += - ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / - 3.0; - ret += - ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * - 2.0) / - 3.0; - return ret; }; - const _transformlng = function (lng, lat) { - let ret = 300.0 + - lng + - 2.0 * lat + - 0.1 * lng * lng + - 0.1 * lng * lat + - 0.1 * Math.sqrt(Math.abs(lng)); - ret += - ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * - 2.0) / - 3.0; - ret += - ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / - 3.0; - ret += - ((150.0 * Math.sin((lng / 12.0) * PI) + - 300.0 * Math.sin((lng / 30.0) * PI)) * - 2.0) / - 3.0; - return ret; - }; - - function getLocationSuccess(type, position, resolve) { - const coords = position.coords; - if (type !== position.coordsType) { - let coordArray; - if (type === 'wgs84') { - coordArray = gcj02towgs84(coords.longitude, coords.latitude); + function createAudioInstance() { + const audioId = `${Date.now()}${Math.random()}`; + const audio = (audios[audioId] = plus.audio.createPlayer('')); // 此处空字符串必填 + audio.src = ''; + audio.volume = 1; + audio.startTime = 0; + return { + errMsg: 'createAudioInstance:ok', + audioId, + }; + } + function setAudioState({ audioId, src, startTime, autoplay = false, loop = false, obeyMuteSwitch, volume, }) { + const audio = audios[audioId]; + if (audio) { + const style = { + loop, + autoplay, + }; + if (src) { + audio.src = style.src = getRealPath(src); } - else if (type === 'gcj02') { - coordArray = wgs84togcj02(coords.longitude, coords.latitude); + if (startTime) { + audio.startTime = style.startTime = startTime; } - if (coordArray) { - coords.longitude = coordArray[0]; - coords.latitude = coordArray[1]; + if (typeof volume === 'number') { + audio.volume = style.volume = volume; } + audio.setStyles(style); + initStateChage(audioId); } - resolve({ - type, - altitude: coords.altitude || 0, - latitude: coords.latitude, - longitude: coords.longitude, - speed: coords.speed, - accuracy: coords.accuracy, - address: position.address, - errMsg: 'getLocation:ok', - }); + return { + errMsg: 'setAudioState:ok', + }; } - const getLocation = defineAsyncApi(API_GET_LOCATION, ({ type = 'wgs84', geocode = false, altitude = false }, { resolve, reject }) => { - plus.geolocation.getCurrentPosition((position) => { - getLocationSuccess(type, position, resolve); - }, (e) => { - // 坐标地址解析失败 - if (e.code === 1501) { - getLocationSuccess(type, e, resolve); - return; - } - reject('getLocation:fail ' + e.message); - }, { - geocode: geocode, - enableHighAccuracy: altitude, - }); - }, GetLocationProtocol, GetLocationOptions); - - const showModal = defineAsyncApi(API_SHOW_MODAL, ({ title = '', content = '', showCancel = true, cancelText, cancelColor, confirmText, confirmColor, } = {}, { resolve }) => { - content = content || ' '; - plus.nativeUI.confirm(content, (e) => { - if (showCancel) { - resolve({ - confirm: e.index === 1, - cancel: e.index === 0 || e.index === -1, - }); - } - else { - resolve({ - confirm: e.index === 0, - cancel: false, - }); - } - }, title, showCancel ? [cancelText, confirmText] : [confirmText]); - }, ShowModalProtocol, ShowModalOptions); - - const showActionSheet = defineAsyncApi(API_SHOW_ACTION_SHEET, ({ itemList = [], itemColor = '#000000', title = '', alertText = '', popover, }, { resolve, reject }) => { - initI18nShowActionSheetMsgsOnce(); - const { t } = useI18n(); - const options = { - title, - cancel: t('uni.showActionSheet.cancel'), - buttons: itemList.map((item) => ({ - title: item, - color: itemColor, - })), + function getAudioState({ audioId }) { + const audio = audios[audioId]; + if (!audio) { + return { + errMsg: 'getAudioState:fail', + }; + } + const { src, startTime, volume } = audio; + return { + errMsg: 'getAudioState:ok', + duration: 1e3 * (audio.getDuration() || 0), + currentTime: audio.isStopped ? 0 : 1e3 * audio.getPosition(), + paused: audio.isPaused(), + src, + volume, + startTime: 1e3 * startTime, + buffered: 1e3 * audio.getBuffered(), }; - if (title || alertText) { - options.title = alertText || title; + } + function operateAudio({ operationType, audioId, currentTime, }) { + const audio = audios[audioId]; + switch (operationType) { + case 'play': + case 'pause': + case 'stop': + audio[operationType === 'play' && audio.isPaused() ? 'resume' : operationType](); + break; + case 'seek': + typeof currentTime != 'undefined' ? audio.seekTo(currentTime / 1e3) : ''; + break; } - plus.nativeUI.actionSheet(extend(options, { - popover, - }), (e) => { - if (e.index > 0) { - resolve({ - tapIndex: e.index - 1, - }); + return { + errMsg: 'operateAudio:ok', + }; + } + const innerAudioContexts = Object.create(null); + const onAudioStateChange = ({ state, audioId, errMsg, errCode, }) => { + const audio = innerAudioContexts[audioId]; + if (audio) { + emit(audio, state, errMsg, errCode); + if (state === 'play') { + const oldCurrentTime = audio.currentTime; + audio.__timing = setInterval(() => { + const currentTime = audio.currentTime; + if (currentTime !== oldCurrentTime) { + emit(audio, 'timeUpdate'); + } + }, 200); } - else { - reject('showActionSheet:fail cancel'); - } - }); - }, ShowActionSheetProtocol, ShowActionSheetOptions); - - let toast; - let isShowToast = false; - let toastType = ''; - let timeout; - const showLoading = defineAsyncApi(API_SHOW_LOADING, (args, callbacks) => _showToast(extend({}, args, { - type: 'loading', - }), callbacks), ShowLoadingProtocol, ShowLoadingOptions); - const _showToast = ({ title = '', icon = 'success', image = '', duration = 1500, mask = false, position, - // @ts-ignore ToastType - type = 'toast', - // @ts-ignore PlusNativeUIWaitingStyles - style, }, { resolve, reject }) => { - hide(''); - toastType = type; - if (['top', 'center', 'bottom'].includes(String(position))) { - // 仅可以关闭 richtext 类型,但 iOS 部分情况换行显示有问题 - plus.nativeUI.toast(title, { - verticalAlign: position, - }); - isShowToast = true; - } - else { - if (icon && !~['success', 'loading', 'error', 'none'].indexOf(icon)) { - icon = 'success'; - } - const waitingOptions = { - modal: mask, - back: 'transmit', - padding: '10px', - size: '16px', - }; - if (!image && (!icon || icon === 'none')) { - // 无图 - // waitingOptions.width = '120px' - // waitingOptions.height = '40px' - waitingOptions.loading = { - display: 'none', - }; - } - else { - waitingOptions.width = '140px'; - waitingOptions.height = '112px'; - } - if (image) { - waitingOptions.loading = { - display: 'block', - height: '55px', - icon: image, - interval: duration, - }; - } - else { - if (['success', 'error'].indexOf(icon) !== -1) { - waitingOptions.loading = { - display: 'block', - height: '55px', - icon: icon === 'success' ? '__uniappsuccess.png' : '__uniapperror.png', - interval: duration, - }; - } - } - try { - toast = plus.nativeUI.showWaiting(title, extend(waitingOptions, style)); - } - catch (error) { - reject(`${error}`); + else if (state === 'pause' || state === 'stop' || state === 'error') { + clearInterval(audio.__timing); } } - timeout = setTimeout(() => { - hide(''); - }, duration); - return resolve(); }; - const showToast = defineAsyncApi(API_SHOW_TOAST, _showToast, ShowToastProtocol, ShowToastOptions); - const hideToast = defineAsyncApi(API_HIDE_TOAST, (_, callbacks) => hide('toast', callbacks)); - const hideLoading = defineAsyncApi(API_HIDE_LOADING, (_, callbacks) => hide('loading', callbacks)); - function hide(type = 'toast', callbacks) { - if (type && type !== toastType) { - // 应该不需要失败回调,在页面后退时,会主动 hideToast 和 hideLoading,如果 reject 会出异常。 - return callbacks && callbacks.resolve(); + const props$1 = [ + { + name: 'src', + cache: true, + }, + { + name: 'startTime', + default: 0, + cache: true, + }, + { + name: 'autoplay', + default: false, + cache: true, + }, + { + name: 'loop', + default: false, + cache: true, + }, + { + name: 'obeyMuteSwitch', + default: true, + readonly: true, + cache: true, + }, + { + name: 'duration', + readonly: true, + }, + { + name: 'currentTime', + readonly: true, + }, + { + name: 'paused', + readonly: true, + }, + { + name: 'buffered', + readonly: true, + }, + { + name: 'volume', + }, + ]; + class InnerAudioContext { + constructor(id) { + this.id = id; + this._callbacks = {}; + this._options = {}; + // 初始化事件监听列表 + innerAudioContextEventNames.forEach((eventName) => { + this._callbacks[eventName] = []; + }); + props$1.forEach((item) => { + const name = item.name; + Object.defineProperty(this, name, { + get: () => { + const result = item.cache + ? this._options + : getAudioState({ + audioId: this.id, + }); + const value = name in result ? result[name] : item.default; + return typeof value === 'number' && name !== 'volume' + ? value / 1e3 + : value; + }, + set: item.readonly + ? undefined + : (value) => { + this._options[name] = value; + setAudioState(extend({}, this._options, { + audioId: this.id, + })); + }, + }); + }); + initInnerAudioContextEventOnce(); } - if (timeout) { - clearTimeout(timeout); - timeout = null; + play() { + this._operate('play'); } - if (isShowToast) { - plus.nativeUI.closeToast(); + pause() { + this._operate('pause'); } - else if (toast && toast.close) { - toast.close(); + stop() { + this._operate('stop'); } - toast = null; - isShowToast = false; - toastType = ''; - return callbacks && callbacks.resolve(); - } - - const providers = { - oauth(callback) { - plus.oauth.getServices((services) => { - services = services; - const provider = []; - services.forEach(({ id }) => { - provider.push(id); - }); - callback(null, provider); - }, (err) => { - err = err; - callback(err); - }); - }, - share(callback) { - plus.share.getServices((services) => { - services = services; - const provider = []; - services.forEach(({ id }) => { - provider.push(id); - }); - callback(null, provider); - }, (err) => { - callback(err); - }); - }, - payment(callback) { - plus.payment.getChannels((services) => { - const provider = []; - services.forEach(({ id }) => { - provider.push(id); - }); - callback(null, provider); - }, (err) => { - callback(err); + seek(position) { + this._operate('seek', { + currentTime: position * 1e3, }); - }, - push(callback) { - if (typeof weex !== 'undefined' || typeof plus !== 'undefined') { - callback(null, [plus.push.getClientInfo().id]); - } - else { - callback(null, []); + } + destroy() { + clearInterval(this.__timing); + if (audios[this.id]) { + audios[this.id].close(); + delete audios[this.id]; } - }, - }; - const getProvider = defineAsyncApi(API_GET_PROVIDER, ({ service }, { resolve, reject }) => { - if (providers[service]) { - providers[service]((err, provider) => { - if (err) { - reject(err.message); - } - else { - resolve({ - service, - provider: provider, - }); - } - }); + delete innerAudioContexts[this.id]; } - else { - reject('service not found'); + _operate(type, options) { + operateAudio(extend({}, options, { + audioId: this.id, + operationType: type, + })); } - }, GetProviderProtocol); - - function getService(provider) { - return new Promise((resolve, reject) => { - plus.oauth.getServices((services) => { - const service = services.find(({ id }) => id === provider); - service ? resolve(service) : reject(new Error('provider not find')); - }, reject); - }); } - /** - * 微信登录 - */ - const baseLogin = (params, { resolve, reject, }) => { - const provider = params.provider || 'weixin'; - const errorCallback = warpPlusErrorCallback(reject); - getService(provider) - .then((service) => { - function login() { - service.login((res) => { - const authResult = res.target.authResult; - resolve({ - code: authResult.code, - authResult: authResult, - }); - }, errorCallback, provider === 'apple' - ? { scope: 'email' } - : { - univerifyStyle: univerifyButtonsClickHandling(params.univerifyStyle, errorCallback), - } || {}); - } - // 先注销再登录 - // apple登录logout之后无法重新触发获取email,fullname;一键登录无logout - if (provider === 'apple' || provider === 'univerify') { - login(); - } - else { - service.logout(login, login); - } - }) - .catch(errorCallback); - }; - const login = defineAsyncApi(API_LOGIN, baseLogin, LoginProtocol); - const getUserInfo = defineAsyncApi(API_GET_USER_INFO, (params, { resolve, reject }) => { - const provider = params.provider || 'weixin'; - const errorCallback = warpPlusErrorCallback(reject); - getService(provider) - .then((loginService) => { - loginService.getUserInfo((res) => { - let userInfo = { nickName: '' }; - if (provider === 'weixin') { - const wechatUserInfo = loginService.userInfo; - if (wechatUserInfo) - userInfo = { - openId: wechatUserInfo.openid, - nickName: wechatUserInfo.nickname, - gender: wechatUserInfo.sex, - city: wechatUserInfo.city, - province: wechatUserInfo.province, - country: wechatUserInfo.country, - avatarUrl: wechatUserInfo.headimgurl, - // @ts-ignore - unionId: wechatUserInfo.unionid, - }; + const initInnerAudioContextEventOnce = /*#__PURE__*/ once(() => { + // 批量设置音频上下文事件监听方法 + innerAudioContextEventNames.forEach((eventName) => { + InnerAudioContext.prototype[eventName] = function (callback) { + if (typeof callback === 'function') { + this._callbacks[eventName].push(callback); } - else if (provider === 'apple') { - const appleInfo = loginService.appleInfo; - if (appleInfo) - userInfo = { - openId: appleInfo.user, - fullName: appleInfo.fullName, - email: appleInfo.email, - authorizationCode: appleInfo.authorizationCode, - identityToken: appleInfo.identityToken, - realUserStatus: appleInfo.realUserStatus, - }; + }; + }); + // 批量设置音频上下文事件取消监听方法 + innerAudioContextOffEventNames.forEach((eventName) => { + InnerAudioContext.prototype[eventName] = function (callback) { + const callbacks = this._callbacks[eventName]; + const index = callbacks.indexOf(callback); + if (index >= 0) { + callbacks.splice(index, 1); } - else { - userInfo = loginService.userInfo; - if (userInfo) { - userInfo.openId = - userInfo.openId || - userInfo.openid || - loginService.authResult.openid; - userInfo.nickName = userInfo.nickName || userInfo.nickname; - userInfo.avatarUrl = userInfo.avatarUrl || userInfo.headimgurl; + }; + }); + }); + function emit(audio, state, errMsg, errCode) { + const name = `on${capitalize(state)}`; + audio._callbacks[name].forEach((callback) => { + if (typeof callback === 'function') { + callback(state === 'error' + ? { + errMsg, + errCode, } - } - let result = {}; - // @ts-ignore - if (params.data && params.data.api_name === 'webapi_getuserinfo') { - result.data = { - data: JSON.stringify(userInfo), - rawData: '', - signature: '', - encryptedData: '', - iv: '', - }; - } - else { - result.userInfo = userInfo; - } - resolve(result); - }, errorCallback); - }) - .catch(() => { - reject('请先调用 uni.login'); + : {}); + } }); - }, GetUserInfoProtocol); - /** - * 获取用户信息-兼容 - */ - const getUserProfile = defineAsyncApi(API_GET_USER_PROFILE, (params, { resolve, reject }) => { - return baseLogin(params, { resolve, reject }); - }, GgetUserProfileProtocol); - const preLogin = defineAsyncApi(API_PRE_LOGIN, (params, { resolve, reject }) => { - const successCallback = warpPlusSuccessCallback(resolve); - const errorCallback = warpPlusErrorCallback(reject); - getService(params.provider) - .then((service) => service.preLogin(successCallback, errorCallback)) - .catch(errorCallback); - }, PreLoginProtocol, PreLoginOptions); - const _closeAuthView = () => getService('univerify').then((service) => service.closeAuthView()); - const closeAuthView = defineAsyncApi(API_CLOSE_AUTH_VIEW, _closeAuthView); + } /** - * 一键登录自定义登陆按钮点击处理 + * 创建音频上下文 */ - function univerifyButtonsClickHandling(univerifyStyle, errorCallback) { - if (isPlainObject(univerifyStyle) && - univerifyStyle.buttons && - toTypeString(univerifyStyle.buttons.list) === '[object Array]' && - univerifyStyle.buttons.list.length > 0) { - univerifyStyle.buttons.list.forEach((button, index) => { - univerifyStyle.buttons.list[index].onclick = function () { - _closeAuthView().then(() => { - errorCallback({ - code: '30008', - message: '用户点击了自定义按钮', - index, - provider: button.provider, - }); - }); - }; - }); - } - return univerifyStyle; - } - - const registerRuntime = defineSyncApi('registerRuntime', (runtime) => { - // @ts-expect-error - extend(jsRuntime, runtime); + const createInnerAudioContext = defineSyncApi(API_CREATE_INNER_AUDIO_CONTEXT, () => { + const { audioId } = createAudioInstance(); + const innerAudioContext = new InnerAudioContext(audioId); + innerAudioContexts[audioId] = innerAudioContext; + return innerAudioContext; }); - // 0:图文,1:纯文字,2:纯图片,3:音乐,4:视频,5:小程序 - const TYPES = { - 0: { - name: 'web', - title: '图文', - }, - 1: { - name: 'text', - title: '纯文字', - }, - 2: { - name: 'image', - title: '纯图片', - }, - 3: { - name: 'music', - title: '音乐', - }, - 4: { - name: 'video', - title: '视频', - }, - 5: { - name: 'miniProgram', - title: '小程序', - }, + const eventNames = [ + 'canplay', + 'play', + 'pause', + 'stop', + 'ended', + 'timeUpdate', + 'prev', + 'next', + 'error', + 'waiting', + ]; + const callbacks = { + canplay: [], + play: [], + pause: [], + stop: [], + ended: [], + timeUpdate: [], + prev: [], + next: [], + error: [], + waiting: [], }; - const parseParams = (args) => { - args.type = args.type || 0; - let { provider, type, title, summary: content, href, imageUrl, mediaUrl: media, scene, miniProgram, } = args; - if (typeof imageUrl === 'string' && imageUrl) { - imageUrl = getRealPath(imageUrl); + let audio; + let timeUpdateTimer = null; + const TIME_UPDATE = 250; + const events = ['play', 'pause', 'ended', 'stop', 'canplay']; + function startTimeUpdateTimer() { + stopTimeUpdateTimer(); + timeUpdateTimer = setInterval(() => { + onBackgroundAudioStateChange({ state: 'timeUpdate' }); + }, TIME_UPDATE); + } + function stopTimeUpdateTimer() { + if (timeUpdateTimer !== null) { + clearInterval(timeUpdateTimer); } - const shareType = TYPES[type]; - if (shareType) { - const sendMsg = { - provider, - type: shareType.name, - title, - content, - href, - pictures: [imageUrl], - thumbs: imageUrl ? [imageUrl] : undefined, - media, - miniProgram, - extra: { - scene, - }, - }; - if (provider === 'weixin' && (type === 1 || type === 2)) { - delete sendMsg.thumbs; - } - return sendMsg; + } + function initMusic() { + if (audio) { + return; } - return '分享参数 type 不正确'; - }; - const sendShareMsg = function (service, params, resolve, reject, method = 'share') { - const errorCallback = warpPlusErrorCallback(reject); - service.send(params, () => { - resolve(); - }, errorCallback); - }; - const share = defineAsyncApi(API_SHREA, (params, { resolve, reject }) => { - const res = parseParams(params); - const errorCallback = warpPlusErrorCallback(reject); - if (typeof res === 'string') { - return reject(res); - } - else { - params = res; - } - plus.share.getServices((services) => { - const service = services.find(({ id }) => id === params.provider); - if (!service) { - reject('service not found'); - } - else { - if (service.authenticated) { - sendShareMsg(service, params, resolve, reject); + const publish = UniServiceJSBridge.invokeOnCallback; + audio = plus.audio.createPlayer({ + autoplay: true, + backgroundControl: true, + }); + audio.src = + audio.title = + audio.epname = + audio.singer = + audio.coverImgUrl = + audio.webUrl = + ''; + audio.startTime = 0; + events.forEach((event) => { + audio.addEventListener(event, () => { + // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 + if (event === 'play') { + audio.isStopped = false; + startTimeUpdateTimer(); } - else { - service.authorize(() => sendShareMsg(service, params, resolve, reject), errorCallback); + else if (event === 'stop') { + audio.isStopped = true; } - } - }, errorCallback); - }, ShareProtocols, SahreOptions); - const shareWithSystem = defineAsyncApi(API_SHARE_WITH_SYSTEM, ({ type, imageUrl, summary, href }, { resolve, reject }) => { - const errorCallback = warpPlusErrorCallback(reject); - if (typeof imageUrl === 'string' && imageUrl) { - imageUrl = getRealPath(imageUrl); + if (event === 'pause' || event === 'ended' || event === 'stop') { + stopTimeUpdateTimer(); + } + const eventName = `onMusic${event[0].toUpperCase() + event.substr(1)}`; + publish(eventName, { + dataUrl: audio.src, + errMsg: `${eventName}:ok`, + }); + onBackgroundAudioStateChange({ + state: event, + dataUrl: audio.src, + }); + }); + }); + audio.addEventListener('waiting', () => { + stopTimeUpdateTimer(); + onBackgroundAudioStateChange({ + state: 'waiting', + dataUrl: audio.src, + }); + }); + audio.addEventListener('error', (err) => { + stopTimeUpdateTimer(); + publish('onMusicError', { + dataUrl: audio.src, + errMsg: 'Error:' + err.message, + }); + onBackgroundAudioStateChange({ + state: 'error', + dataUrl: audio.src, + errMsg: err.message, + errCode: err.code, + }); + }); + // @ts-ignore + audio.addEventListener('prev', () => publish('onBackgroundAudioPrev')); + // @ts-ignore + audio.addEventListener('next', () => publish('onBackgroundAudioNext')); + } + function getBackgroundAudioState() { + let data = { + duration: 0, + currentTime: 0, + paused: false, + src: '', + buffered: 0, + title: '', + epname: '', + singer: '', + coverImgUrl: '', + webUrl: '', + startTime: 0, + errMsg: 'getBackgroundAudioState:ok', + }; + if (audio) { + const newData = { + duration: audio.getDuration() || 0, + currentTime: audio.isStopped ? 0 : audio.getPosition(), + paused: audio.isPaused(), + src: audio.src, + buffered: audio.getBuffered(), + title: audio.title, + epname: audio.epname, + singer: audio.singer, + coverImgUrl: audio.coverImgUrl, + webUrl: audio.webUrl, + startTime: audio.startTime, + }; + data = extend(data, newData); } - plus.share.sendWithSystem({ - type, - pictures: imageUrl ? [imageUrl] : undefined, - content: summary, - href, - }, () => resolve(), errorCallback); - }, ShareWithSystemProtocols, ShareWithSystemOptions); - - const requestPayment = defineAsyncApi(API_REQUEST_PAYMENT, (params, { resolve, reject }) => { - const provider = params.provider; - const errorCallback = warpPlusErrorCallback(reject); - plus.payment.getChannels((services) => { - const service = services.find(({ id }) => id === provider); - if (!service) { - reject('service not found'); - } - else { - plus.payment.request(service, params.orderInfo, (res) => { - resolve(res); - }, errorCallback); - } - }, errorCallback); - }, RequestPaymentProtocol); - - function applyOptions(options, instance, publicThis) { - Object.keys(options).forEach((name) => { - if (name.indexOf('on') === 0) { - const hook = options[name]; - if (isFunction(hook)) { - vue.injectHook(name, hook.bind(publicThis), instance); + return data; + } + function setMusicState(args) { + initMusic(); + const props = [ + 'src', + 'startTime', + 'coverImgUrl', + 'webUrl', + 'singer', + 'epname', + 'title', + ]; + const style = {}; + Object.keys(args).forEach((key) => { + if (props.indexOf(key) >= 0) { + let val = args[key]; + if (key === props[0] && val) { + val = getRealPath(val); } + audio[key] = style[key] = val; } }); - } - - function set(target, key, val) { - return (target[key] = val); - } - - function errorHandler(err, instance, info) { - if (!instance) { - throw err; - } - const app = getApp(); - if (!app || !app.$vm) { - throw err; - } - { - invokeHook(app.$vm, 'onError', err); - } - } - - function initApp(app) { - const appConfig = app._context.config; - if (isFunction(app._component.onError)) { - appConfig.errorHandler = errorHandler; - } - const globalProperties = appConfig.globalProperties; - { - globalProperties.$set = set; - globalProperties.$applyOptions = applyOptions; + audio.setStyles(style); + } + function operateMusicPlayer({ operationType, src, position, api = 'operateMusicPlayer', title, coverImgUrl, }) { + var operationTypes = ['resume', 'pause', 'stop']; + if (operationTypes.indexOf(operationType) > 0) { + audio && audio[operationType](); } - } - - let isInitEntryPage = false; - function initEntry() { - if (isInitEntryPage) { - return; + else if (operationType === 'play') { + setMusicState({ + src, + startTime: position, + title, + coverImgUrl, + }); + audio.play(); } - isInitEntryPage = true; - let entryPagePath; - let entryPageQuery; - const weexPlus = weex.requireModule('plus'); - if (weexPlus.getRedirectInfo) { - const info = weexPlus.getRedirectInfo() || {}; - entryPagePath = info.path; - entryPageQuery = info.query ? '?' + info.query : ''; + else if (operationType === 'seek') { + audio && audio.seekTo(position); } - else { - const argsJsonStr = plus.runtime.arguments; - if (!argsJsonStr) { - return; - } - try { - const args = JSON.parse(argsJsonStr); - entryPagePath = args.path || args.pathName; - entryPageQuery = args.query ? '?' + args.query : ''; + return { + errMsg: `${api}:ok`, + }; + } + function operateBackgroundAudio({ operationType, src, startTime, currentTime, }) { + return operateMusicPlayer({ + operationType, + src, + position: startTime || currentTime || 0, + api: 'operateBackgroundAudio', + }); + } + function onBackgroundAudioStateChange({ state, errMsg, errCode, dataUrl, }) { + callbacks[state].forEach((callback) => { + if (typeof callback === 'function') { + callback(state === 'error' + ? { + errMsg, + errCode, + } + : {}); } - catch (e) { } - } - if (!entryPagePath || entryPagePath === __uniConfig.entryPagePath) { - if (entryPageQuery) { - __uniConfig.entryPageQuery = entryPageQuery; - } - return; - } - const entryRoute = '/' + entryPagePath; - const routeOptions = getRouteOptions(entryRoute); - if (!routeOptions) { - return; - } - if (!routeOptions.meta.isTabBar) { - __uniConfig.realEntryPagePath = - __uniConfig.realEntryPagePath || __uniConfig.entryPagePath; - } - __uniConfig.entryPagePath = entryPagePath; - __uniConfig.entryPageQuery = entryPageQuery; - } - - const isIOS = plus.os.name === 'iOS'; - let config; - /** - * tabbar显示状态 - */ - let visible = true; - let tabBar; - /** - * 设置角标 - * @param {string} type - * @param {number} index - * @param {string} text - */ - function setTabBarBadge(type, index, text) { - if (!tabBar) { - return; - } - if (type === 'none') { - tabBar.hideTabBarRedDot({ - index, - }); - tabBar.removeTabBarBadge({ - index, - }); - } - else if (type === 'text') { - tabBar.setTabBarBadge({ - index, - text, - }); - } - else if (type === 'redDot') { - tabBar.showTabBarRedDot({ - index, - }); - } - } - /** - * 动态设置 tabBar 某一项的内容 - */ - function setTabBarItem(index, text, iconPath, selectedIconPath) { - const item = { - index, - }; - if (text !== undefined) { - item.text = text; - } - if (iconPath) { - item.iconPath = getRealPath(iconPath); - } - if (selectedIconPath) { - item.selectedIconPath = getRealPath(selectedIconPath); - } - tabBar && tabBar.setTabBarItem(item); - } - /** - * 动态设置 tabBar 的整体样式 - * @param {Object} style 样式 - */ - function setTabBarStyle(style) { - tabBar && tabBar.setTabBarStyle(style); - } - /** - * 隐藏 tabBar - * @param {boolean} animation 是否需要动画效果 - */ - function hideTabBar(animation) { - visible = false; - tabBar && - tabBar.hideTabBar({ - animation, - }); - } - /** - * 显示 tabBar - * @param {boolean} animation 是否需要动画效果 - */ - function showTabBar(animation) { - visible = true; - tabBar && - tabBar.showTabBar({ - animation, - }); + }); } - const maskClickCallback = []; - var tabBar$1 = { - id: '0', - init(options, clickCallback) { - if (options && options.list.length) { - config = options; - } - try { - tabBar = weex.requireModule('uni-tabview'); - } - catch (error) { - console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`); - } - tabBar.onMaskClick(() => { - maskClickCallback.forEach((callback) => { - callback(); - }); - }); - tabBar && - tabBar.onClick(({ index }) => { - clickCallback(config.list[index], index); - }); - tabBar && - tabBar.onMidButtonClick(() => { - // publish('onTabBarMidButtonTap', {}) - }); + const onInitBackgroundAudioManager = /*#__PURE__*/ once(() => { + eventNames.forEach((item) => { + BackgroundAudioManager.prototype[`on${capitalize(item)}`] = + function (callback) { + callbacks[item].push(callback); + }; + }); + }); + const props = [ + { + name: 'duration', + readonly: true, }, - indexOf(page) { - const itemLength = config && config.list && config.list.length; - if (itemLength) { - for (let i = 0; i < itemLength; i++) { - if (config.list[i].pagePath === page || - config.list[i].pagePath === `${page}.html`) { - return i; - } - } - } - return -1; + { + name: 'currentTime', + readonly: true, }, - switchTab(page) { - const index = this.indexOf(page); - if (index >= 0) { - tabBar && - tabBar.switchSelect({ - index, - }); - return true; - } - return false; + { + name: 'paused', + readonly: true, }, - setTabBarBadge, - setTabBarItem, - setTabBarStyle, - hideTabBar, - showTabBar, - append(webview) { - tabBar && - tabBar.append({ - id: webview.id, - }, ({ code }) => { - if (code !== 0) { - setTimeout(() => { - this.append(webview); - }, 20); - } - }); + { + name: 'src', + cache: true, }, - get visible() { - return visible; + { + name: 'startTime', + default: 0, + cache: true, }, - get height() { - return ((config && config.height ? parseFloat(config.height) : TABBAR_HEIGHT) + - plus.navigator.getSafeAreaInsets().deviceBottom); + { + name: 'buffered', + readonly: true, }, - // tabBar是否遮挡内容区域 - get cover() { - const array = ['extralight', 'light', 'dark']; - return isIOS && array.indexOf(config.blurEffect) >= 0; + { + name: 'title', + cache: true, }, - setStyle({ mask }) { - tabBar.setMask({ - color: mask, - }); + { + name: 'epname', + cache: true, }, - addEventListener(_name, callback) { - maskClickCallback.push(callback); + { + name: 'singer', + cache: true, }, - removeEventListener(_name, callback) { - const callbackIndex = maskClickCallback.indexOf(callback); - maskClickCallback.splice(callbackIndex, 1); + { + name: 'coverImgUrl', + cache: true, }, - }; - - function initTabBar() { - const { tabBar } = __uniConfig; - const len = tabBar && tabBar.list && tabBar.list.length; - if (!len) { - return; - } - const { entryPagePath } = __uniConfig; - tabBar.selectedIndex = 0; - const selected = tabBar.list.findIndex((page) => page.pagePath === entryPagePath); - tabBar$1.init(tabBar, (item, index) => { - uni.switchTab({ - url: '/' + item.pagePath, - openType: 'switchTab', - from: 'tabBar', - success() { - invokeHook('onTabItemTap', { - index, - text: item.text, - pagePath: item.pagePath, - }); - }, - }); - }); - if (selected !== -1) { - // 取当前 tab 索引值 - tabBar.selectedIndex = selected; - selected !== 0 && tabBar$1.switchTab(entryPagePath); - } - } - - function backbuttonListener() { - uni.navigateBack({ - from: 'backbutton', - }); - } - - function initGlobalEvent() { - const plusGlobalEvent = plus.globalEvent; - const weexGlobalEvent = weex.requireModule('globalEvent'); - const emit = UniServiceJSBridge.emit; - if (weex.config.preload) { - plus.key.addEventListener('backbutton', backbuttonListener); - } - else { - plusGlobalEvent.addEventListener('splashclosed', () => { - plus.key.addEventListener('backbutton', backbuttonListener); - }); - } - plusGlobalEvent.addEventListener('pause', () => { - emit('onAppEnterBackground'); - }); - plusGlobalEvent.addEventListener('resume', () => { - emit('onAppEnterForeground'); - }); - weexGlobalEvent.addEventListener('uistylechange', function (event) { - const args = { - theme: event.uistyle, - }; - emit('onThemeChange', args); - }); - plusGlobalEvent.addEventListener('plusMessage', subscribePlusMessage); - // nvue webview post message - plusGlobalEvent.addEventListener('WebviewPostMessage', subscribePlusMessage); - } - function subscribePlusMessage({ data, }) { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('plusMessage', data)); - } - if (data && data.type) { - UniServiceJSBridge.subscribeHandler('plusMessage.' + data.type, data.args); - } - } - function onPlusMessage(type, callback, once = false) { - UniServiceJSBridge.subscribe('plusMessage.' + type, callback, once); - } - - function initAppLaunch(appVm) { - const args = { - path: __uniConfig.entryPagePath, - query: {}, - scene: 1001, - }; - invokeHook(appVm, 'onLaunch', args); - invokeHook(appVm, 'onShow', args); - } - - // 统一处理路径 - function getPath(path) { - path = path.replace(/\/$/, ''); - return path.indexOf('_') === 0 - ? plus.io.convertLocalFileSystemURL(path) - : path; - } - function clearTempFile() { - const basePath = getPath(TEMP_PATH_BASE); - const tempPath = getPath(TEMP_PATH); - // 获取父目录 - const dirParts = tempPath.split('/'); - dirParts.pop(); - const dirPath = dirParts.join('/'); - plus.io.resolveLocalFileSystemURL(plus.io.convertAbsoluteFileSystem(dirPath), (entry) => { - const reader = entry.createReader(); - reader.readEntries(function (entry) { - // plus.d.ts 类型不对 - const entries = entry; - if (entries && entries.length) { - entries.forEach(function (entry) { - if (entry.isDirectory && - entry.fullPath.indexOf(basePath) === 0 && - entry.fullPath.indexOf(tempPath) !== 0) { - entry.removeRecursively(); - } - }); - } + { + name: 'webUrl', + cache: true, + }, + { + name: 'protocol', + readonly: true, + default: 'http', + }, + ]; + class BackgroundAudioManager { + constructor() { + this._options = {}; + props.forEach((item) => { + const name = item.name; + Object.defineProperty(this, name, { + get: () => { + const result = item.cache ? this._options : getBackgroundAudioState(); + return name in result ? result[name] : item.default; + }, + set: item.readonly + ? undefined + : (value) => { + this._options[name] = value; + setMusicState(this._options); + }, + }); }); - }); - } - - const VD_SYNC = 'vdSync'; - const ON_WEBVIEW_READY = 'onWebviewReady'; - const INVOKE_VIEW_API = 'invokeViewApi'; - const INVOKE_SERVICE_API = 'invokeServiceApi'; - - const ACTION_TYPE_PAGE_CREATE = 1; - const ACTION_TYPE_PAGE_CREATED = 2; - const ACTION_TYPE_CREATE = 3; - const ACTION_TYPE_INSERT = 4; - const ACTION_TYPE_REMOVE = 5; - const ACTION_TYPE_SET_ATTRIBUTE = 6; - const ACTION_TYPE_REMOVE_ATTRIBUTE = 7; - const ACTION_TYPE_ADD_EVENT = 8; - const ACTION_TYPE_REMOVE_EVENT = 9; - const ACTION_TYPE_SET_TEXT = 10; - const ACTION_TYPE_EVENT = 20; - - function onNodeEvent(nodeId, evt, pageNode) { - pageNode.fireEvent(nodeId, evt); - } - - function onVdSync(actions, pageId) { - const page = getPageById(parseInt(pageId)); - if (!page) { - if ((process.env.NODE_ENV !== 'production')) { - console.error(formatLog('onVdSync', 'page', pageId, 'not found')); - } - return; + onInitBackgroundAudioManager(); } - const pageNode = page.$.appContext.app - ._container; - actions.forEach((action) => { - switch (action[0]) { - case ACTION_TYPE_EVENT: - onNodeEvent(action[1], action[2], pageNode); - break; - } - }); - } - - function initNVue(webviewStyle, routeMeta, path) { - if (path && routeMeta.isNVue) { - webviewStyle.uniNView = { - path, - defaultFontSize: __uniConfig.defaultFontSize, - viewport: __uniConfig.viewport, - }; + play() { + this._operate('play'); } - } - - const colorRE = /^#[a-z0-9]{6}$/i; - function isColor(color) { - return color && (colorRE.test(color) || color === 'transparent'); - } - - function initBackgroundColor(webviewStyle, routeMeta) { - const { backgroundColor } = routeMeta; - if (!backgroundColor) { - return; + pause() { + this._operate('pause'); } - if (!isColor(backgroundColor)) { - return; + stop() { + this._operate('stop'); } - if (!webviewStyle.background) { - webviewStyle.background = backgroundColor; + seek(position) { + this._operate('seek', { + currentTime: position, + }); } - if (!webviewStyle.backgroundColorTop) { - webviewStyle.backgroundColorTop = backgroundColor; + _operate(type, options) { + operateBackgroundAudio(extend({}, options, { + operationType: type, + })); } - } + } + let backgroundAudioManager; + const getBackgroundAudioManager = defineSyncApi(API_GET_BACKGROUND_AUDIO_MANAGER, () => backgroundAudioManager || + (backgroundAudioManager = new BackgroundAudioManager())); - function initPopGesture(webviewStyle, routeMeta) { - // 不支持 hide - if (webviewStyle.popGesture === 'hide') { - delete webviewStyle.popGesture; - } - // 似乎没用了吧?记得是之前流应用时,需要 appback 的逻辑 - if (routeMeta.isQuit) { - webviewStyle.popGesture = (plus.os.name === 'iOS' ? 'appback' : 'none'); + const PI = 3.1415926535897932384626; + const a = 6378245.0; + const ee = 0.00669342162296594323; + function gcj02towgs84(lng, lat) { + lat = +lat; + lng = +lng; + if (outOfChina(lng, lat)) { + return [lng, lat]; } - } - - function initPullToRefresh(webviewStyle, routeMeta) { - if (!routeMeta.enablePullDownRefresh) { - return; + let dlat = _transformlat(lng - 105.0, lat - 35.0); + let dlng = _transformlng(lng - 105.0, lat - 35.0); + const radlat = (lat / 180.0) * PI; + let magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + const sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); + dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); + const mglat = lat + dlat; + const mglng = lng + dlng; + return [lng * 2 - mglng, lat * 2 - mglat]; + } + function wgs84togcj02(lng, lat) { + lat = +lat; + lng = +lng; + if (outOfChina(lng, lat)) { + return [lng, lat]; } - webviewStyle.pullToRefresh = normalizePullToRefreshRpx(extend({}, plus.os.name === 'Android' - ? defaultAndroidPullToRefresh - : defaultPullToRefresh, routeMeta.pullToRefresh)); + let dlat = _transformlat(lng - 105.0, lat - 35.0); + let dlng = _transformlng(lng - 105.0, lat - 35.0); + const radlat = (lat / 180.0) * PI; + let magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + const sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI); + dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI); + const mglat = lat + dlat; + const mglng = lng + dlng; + return [mglng, mglat]; } - const defaultAndroidPullToRefresh = { support: true, style: 'circle' }; - const defaultPullToRefresh = { - support: true, - style: 'default', - height: '50px', - range: '200px', - contentdown: { - caption: '', - }, - contentover: { - caption: '', - }, - contentrefresh: { - caption: '', - }, + const outOfChina = function (lng, lat) { + return (lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271 || false); + }; + const _transformlat = function (lng, lat) { + let ret = -100.0 + + 2.0 * lng + + 3.0 * lat + + 0.2 * lat * lat + + 0.1 * lng * lat + + 0.2 * Math.sqrt(Math.abs(lng)); + ret += + ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * + 2.0) / + 3.0; + ret += + ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / + 3.0; + ret += + ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * + 2.0) / + 3.0; + return ret; + }; + const _transformlng = function (lng, lat) { + let ret = 300.0 + + lng + + 2.0 * lat + + 0.1 * lng * lng + + 0.1 * lng * lat + + 0.1 * Math.sqrt(Math.abs(lng)); + ret += + ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * + 2.0) / + 3.0; + ret += + ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / + 3.0; + ret += + ((150.0 * Math.sin((lng / 12.0) * PI) + + 300.0 * Math.sin((lng / 30.0) * PI)) * + 2.0) / + 3.0; + return ret; }; - function initTitleNView(webviewStyle, routeMeta) { - const { navigationBar } = routeMeta; - if (navigationBar.style === 'custom') { - return false; - } - let autoBackButton = true; - if (routeMeta.isQuit) { - autoBackButton = false; - } - const titleNView = { - autoBackButton, - }; - Object.keys(navigationBar).forEach((name) => { - const value = navigationBar[name]; - if (name === 'backgroundColor') { - titleNView.backgroundColor = isColor(value) - ? value - : BACKGROUND_COLOR; - } - else if (name === 'titleImage' && value) { - titleNView.tags = createTitleImageTags(value); + function getLocationSuccess(type, position, resolve) { + const coords = position.coords; + if (type !== position.coordsType) { + let coordArray; + if (type === 'wgs84') { + coordArray = gcj02towgs84(coords.longitude, coords.latitude); } - else if (name === 'buttons' && isArray(value)) { - titleNView.buttons = value.map((button, index) => { - button.onclick = createTitleNViewBtnClick(index); - return button; - }); + else if (type === 'gcj02') { + coordArray = wgs84togcj02(coords.longitude, coords.latitude); } - else { - titleNView[name] = - value; + if (coordArray) { + coords.longitude = coordArray[0]; + coords.latitude = coordArray[1]; } + } + resolve({ + type, + altitude: coords.altitude || 0, + latitude: coords.latitude, + longitude: coords.longitude, + speed: coords.speed, + accuracy: coords.accuracy, + address: position.address, + errMsg: 'getLocation:ok', }); - webviewStyle.titleNView = titleNView; - } - function createTitleImageTags(titleImage) { - return [ - { - tag: 'img', - src: titleImage, - position: { - left: 'auto', - top: 'auto', - width: 'auto', - height: '26px', - }, - }, - ]; } - function createTitleNViewBtnClick(index) { - return function onClick(btn) { - btn.index = index; - invokeHook('onNavigationBarButtonTap', btn); - }; - } - - function parseWebviewStyle(path, routeMeta) { - const webviewStyle = { - bounce: 'vertical', - }; - Object.keys(routeMeta).forEach((name) => { - if (WEBVIEW_STYLE_BLACKLIST.indexOf(name) === -1) { - webviewStyle[name] = - routeMeta[name]; + const getLocation = defineAsyncApi(API_GET_LOCATION, ({ type = 'wgs84', geocode = false, altitude = false }, { resolve, reject }) => { + plus.geolocation.getCurrentPosition((position) => { + getLocationSuccess(type, position, resolve); + }, (e) => { + // 坐标地址解析失败 + if (e.code === 1501) { + getLocationSuccess(type, e, resolve); + return; } + reject('getLocation:fail ' + e.message); + }, { + geocode: geocode, + enableHighAccuracy: altitude, }); - initNVue(webviewStyle, routeMeta, path); - initPopGesture(webviewStyle, routeMeta); - initBackgroundColor(webviewStyle, routeMeta); - initTitleNView(webviewStyle, routeMeta); - initPullToRefresh(webviewStyle, routeMeta); - return webviewStyle; - } - const WEBVIEW_STYLE_BLACKLIST = [ - 'id', - 'route', - 'isNVue', - 'isQuit', - 'isEntry', - 'isTabBar', - 'tabBarIndex', - 'windowTop', - 'topWindow', - 'leftWindow', - 'rightWindow', - 'maxWidth', - 'usingComponents', - 'disableScroll', - 'enablePullDownRefresh', - 'navigationBar', - 'pullToRefresh', - 'onReachBottomDistance', - 'pageOrientation', - 'backgroundColor', - ]; + }, GetLocationProtocol, GetLocationOptions); - let id = 2; - function getWebviewId() { - return id; - } - function genWebviewId() { - return id++; - } - function encode(val) { - return val; - } - function initUniPageUrl(path, query) { - const queryString = query ? stringifyQuery$1(query, encode) : ''; - return { - path: path.substr(1), - query: queryString ? queryString.substr(1) : queryString, - }; - } - function initDebugRefresh(isTab, path, query) { - const queryString = query ? stringifyQuery$1(query, encode) : ''; - return { - isTab, - arguments: JSON.stringify({ - path: path.substr(1), - query: queryString ? queryString.substr(1) : queryString, - }), - }; - } + const showModal = defineAsyncApi(API_SHOW_MODAL, ({ title = '', content = '', showCancel = true, cancelText, cancelColor, confirmText, confirmColor, } = {}, { resolve }) => { + content = content || ' '; + plus.nativeUI.confirm(content, (e) => { + if (showCancel) { + resolve({ + confirm: e.index === 1, + cancel: e.index === 0 || e.index === -1, + }); + } + else { + resolve({ + confirm: e.index === 0, + cancel: false, + }); + } + }, title, showCancel ? [cancelText, confirmText] : [confirmText]); + }, ShowModalProtocol, ShowModalOptions); - function createNVueWebview({ path, query, routeOptions, webviewStyle, }) { - const curWebviewId = genWebviewId(); - const curWebviewStyle = parseWebviewStyle(path, routeOptions.meta); - curWebviewStyle.uniPageUrl = initUniPageUrl(path, query); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('createNVueWebview', curWebviewId, path, curWebviewStyle)); + const showActionSheet = defineAsyncApi(API_SHOW_ACTION_SHEET, ({ itemList = [], itemColor = '#000000', title = '', alertText = '', popover, }, { resolve, reject }) => { + initI18nShowActionSheetMsgsOnce(); + const { t } = useI18n(); + const options = { + title, + cancel: t('uni.showActionSheet.cancel'), + buttons: itemList.map((item) => ({ + title: item, + color: itemColor, + })), + }; + if (title || alertText) { + options.title = alertText || title; } - curWebviewStyle.isTab = !!routeOptions.meta.isTabBar; - return plus.webview.create('', String(curWebviewId), curWebviewStyle, extend({ - nvue: true, - }, webviewStyle)); - } + plus.nativeUI.actionSheet(extend(options, { + popover, + }), (e) => { + if (e.index > 0) { + resolve({ + tapIndex: e.index - 1, + }); + } + else { + reject('showActionSheet:fail cancel'); + } + }); + }, ShowActionSheetProtocol, ShowActionSheetOptions); - const downgrade = plus.os.name === 'Android' && parseInt(plus.os.version) < 6; - const ANI_SHOW = downgrade ? 'slide-in-right' : 'pop-in'; - const ANI_DURATION = 300; - const ANI_CLOSE = downgrade ? 'slide-out-right' : 'pop-out'; - const VIEW_WEBVIEW_PATH = '_www/__uniappview.html'; - const WEBVIEW_ID_PREFIX = 'webviewId'; - - let preloadWebview; - function setPreloadWebview(webview) { - preloadWebview = webview; - } - function getPreloadWebview() { - return preloadWebview; - } - function createPreloadWebview() { - if (!preloadWebview || preloadWebview.__uniapp_route) { - // 不存在,或已被使用 - preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(genWebviewId())); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('createPreloadWebview', preloadWebview.id)); - } - } - return preloadWebview; - } - - function initWebviewStyle(webview, path, query, routeMeta) { - const webviewStyle = parseWebviewStyle(path, routeMeta); - webviewStyle.uniPageUrl = initUniPageUrl(path, query); - const isTabBar = !!routeMeta.isTabBar; - if (!routeMeta.isNVue) { - webviewStyle.debugRefresh = initDebugRefresh(isTabBar, path, query); + let toast; + let isShowToast = false; + let toastType = ''; + let timeout; + const showLoading = defineAsyncApi(API_SHOW_LOADING, (args, callbacks) => _showToast(extend({}, args, { + type: 'loading', + }), callbacks), ShowLoadingProtocol, ShowLoadingOptions); + const _showToast = ({ title = '', icon = 'success', image = '', duration = 1500, mask = false, position, + // @ts-ignore ToastType + type = 'toast', + // @ts-ignore PlusNativeUIWaitingStyles + style, }, { resolve, reject }) => { + hide(''); + toastType = type; + if (['top', 'center', 'bottom'].includes(String(position))) { + // 仅可以关闭 richtext 类型,但 iOS 部分情况换行显示有问题 + plus.nativeUI.toast(title, { + verticalAlign: position, + }); + isShowToast = true; } else { - // android 需要使用 - webviewStyle.isTab = isTabBar; - } - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('updateWebview', webviewStyle)); - } - webview.setStyle(webviewStyle); - } - - function initWebview(webview, path, query, routeMeta) { - // 首页或非 nvue 页面 - if (webview.id === '1' || !routeMeta.isNVue) { - initWebviewStyle(webview, path, query, routeMeta); - } - } - - function createWebview(options) { - if (options.routeOptions.meta.isNVue) { - return createNVueWebview(options); - } - if (getWebviewId() === 2) { - // 如果首页非 nvue,则直接返回 Launch Webview - return plus.webview.getLaunchWebview(); - } - return getPreloadWebview(); - } - function onWebviewReady(pageId, callback) { - UniServiceJSBridge.once(ON_WEBVIEW_READY + '.' + pageId, callback); - } - - let isLaunchWebviewReady = false; // 目前首页双向确定 ready,可能会导致触发两次 onWebviewReady(主要是 Android) - function subscribeWebviewReady(_data, pageId) { - const isLaunchWebview = pageId === '1'; - if (isLaunchWebview && isLaunchWebviewReady) { - if ((process.env.NODE_ENV !== 'production')) { - console.log('[uni-app] onLaunchWebviewReady.prevent'); + if (icon && !~['success', 'loading', 'error', 'none'].indexOf(icon)) { + icon = 'success'; } - return; - } - if (isLaunchWebview) { - // 首页 - isLaunchWebviewReady = true; - setPreloadWebview(plus.webview.getLaunchWebview()); - } - else if (!preloadWebview) { - // preloadWebview 不存在,重新加载一下 - setPreloadWebview(plus.webview.getWebviewById(pageId)); - } - if (preloadWebview.id !== pageId) { - return console.error(`webviewReady[${preloadWebview.id}][${pageId}] not match`); - } - preloadWebview.loaded = true; // 标记已 ready - UniServiceJSBridge.emit(ON_WEBVIEW_READY + '.' + pageId); - isLaunchWebview && onLaunchWebviewReady(); - } - function onLaunchWebviewReady() { - const { autoclose, alwaysShowBeforeRender } = __uniConfig.splashscreen; - if (autoclose && !alwaysShowBeforeRender) { - plus.navigator.closeSplashscreen(); - } - const entryPagePath = '/' + __uniConfig.entryPagePath; - const routeOptions = getRouteOptions(entryPagePath); - if (!routeOptions.meta.isNVue) { - // 非 nvue 首页,需要主动跳转 - const args = { - url: entryPagePath + (__uniConfig.entryPageQuery || ''), - openType: 'appLaunch', + const waitingOptions = { + modal: mask, + back: 'transmit', + padding: '10px', + size: '16px', }; - if (routeOptions.meta.isTabBar) { - return uni.switchTab(args); + if (!image && (!icon || icon === 'none')) { + // 无图 + // waitingOptions.width = '120px' + // waitingOptions.height = '40px' + waitingOptions.loading = { + display: 'none', + }; + } + else { + waitingOptions.width = '140px'; + waitingOptions.height = '112px'; + } + if (image) { + waitingOptions.loading = { + display: 'block', + height: '55px', + icon: image, + interval: duration, + }; + } + else { + if (['success', 'error'].indexOf(icon) !== -1) { + waitingOptions.loading = { + display: 'block', + height: '55px', + icon: icon === 'success' ? '__uniappsuccess.png' : '__uniapperror.png', + interval: duration, + }; + } + } + try { + toast = plus.nativeUI.showWaiting(title, extend(waitingOptions, style)); + } + catch (error) { + reject(`${error}`); } - return uni.navigateTo(args); - } - } - - function initSubscribeHandlers() { - const { subscribe, subscribeHandler } = UniServiceJSBridge; - onPlusMessage('subscribeHandler', ({ type, data, pageId }) => { - subscribeHandler(type, data, pageId); - }); - if (__uniConfig.renderer !== 'native') { - // 非纯原生 - subscribe(ON_WEBVIEW_READY, subscribeWebviewReady); - subscribe(VD_SYNC, onVdSync); - subscribe(INVOKE_SERVICE_API, onInvokeServiceApi); } - } - function onInvokeServiceApi({ data: { method, args }, }) { - uni[method] && uni[method](args); - } - - let appCtx; - const defaultApp = { - globalData: {}, + timeout = setTimeout(() => { + hide(''); + }, duration); + return resolve(); }; - function getApp$1({ allowDefault = false } = {}) { - if (appCtx) { - // 真实的 App 已初始化 - return appCtx; + const showToast = defineAsyncApi(API_SHOW_TOAST, _showToast, ShowToastProtocol, ShowToastOptions); + const hideToast = defineAsyncApi(API_HIDE_TOAST, (_, callbacks) => hide('toast', callbacks)); + const hideLoading = defineAsyncApi(API_HIDE_LOADING, (_, callbacks) => hide('loading', callbacks)); + function hide(type = 'toast', callbacks) { + if (type && type !== toastType) { + // 应该不需要失败回调,在页面后退时,会主动 hideToast 和 hideLoading,如果 reject 会出异常。 + return callbacks && callbacks.resolve(); } - if (allowDefault) { - // 返回默认实现 - return defaultApp; + if (timeout) { + clearTimeout(timeout); + timeout = null; } - console.error('[warn]: getApp() failed. Learn more: https://uniapp.dcloud.io/collocation/frame/window?id=getapp.'); - } - function registerApp(appVm) { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('registerApp')); + if (isShowToast) { + plus.nativeUI.closeToast(); } - appCtx = appVm; - appCtx.$vm = appVm; - extend(appCtx, defaultApp); // 拷贝默认实现 - const { $options } = appVm; - if ($options) { - appCtx.globalData = extend($options.globalData || {}, appCtx.globalData); + else if (toast && toast.close) { + toast.close(); } - initService(); - initEntry(); - initTabBar(); - initGlobalEvent(); - initSubscribeHandlers(); - initAppLaunch(appVm); - // 10s后清理临时文件 - setTimeout(clearTempFile, 10000); - __uniConfig.ready = true; + toast = null; + isShowToast = false; + toastType = ''; + return callbacks && callbacks.resolve(); } - var __vuePlugin = { - install(app) { - initMount(app); - initApp(app); - initServicePlugin(app); + const providers = { + oauth(callback) { + plus.oauth.getServices((services) => { + services = services; + const provider = []; + services.forEach(({ id }) => { + provider.push(id); + }); + callback(null, provider); + }, (err) => { + err = err; + callback(err); + }); }, - }; - function initMount(app) { - const oldMount = app.mount; - app.mount = (rootContainer) => { - const instance = oldMount.call(app, rootContainer); - if (rootContainer === '#app') { - registerApp(instance); - } - return instance; - }; - } - - const EventType = { - load: 'load', - close: 'close', - error: 'error', - adClicked: 'adClicked', - }; - class AdEventHandler { - constructor() { - this._callbacks = {}; - } - onLoad(callback) { - this._addEventListener(EventType.load, callback); - } - onClose(callback) { - this._addEventListener(EventType.close, callback); - } - onError(callback) { - this._addEventListener(EventType.error, callback); - } - offLoad(callback) { - this._removeEventListener(EventType.load, callback); - } - offClose(callback) { - this._removeEventListener(EventType.close, callback); - } - offError(callback) { - this._removeEventListener(EventType.error, callback); - } - _addEventListener(type, callback) { - if (typeof callback !== 'function') { - return; - } - this._callbacks[type].push(callback); - } - _removeEventListener(type, callback) { - const arrayFunction = this._callbacks[type]; - const index = arrayFunction.indexOf(callback); - if (index > -1) { - arrayFunction.splice(index, 1); - } - } - _dispatchEvent(name, data) { - this._callbacks[name].forEach((callback) => { - callback(data || {}); - }); - } - } - class AdBase extends AdEventHandler { - constructor(adInstance, options) { - super(); - this._isLoaded = false; - this._isLoading = false; - this._preload = true; - this._loadPromiseResolve = null; - this._loadPromiseReject = null; - this._showPromiseResolve = null; - this._showPromiseReject = null; - this._preload = options.preload !== undefined ? options.preload : false; - const ad = (this._adInstance = adInstance); - ad.onLoad(() => { - this._isLoaded = true; - this._isLoading = false; - if (this._loadPromiseResolve != null) { - this._loadPromiseResolve(); - this._loadPromiseResolve = null; - } - if (this._showPromiseResolve != null) { - this._showPromiseResolve(); - this._showPromiseResolve = null; - this._showAd(); - } - this._dispatchEvent(EventType.load, {}); + share(callback) { + plus.share.getServices((services) => { + services = services; + const provider = []; + services.forEach(({ id }) => { + provider.push(id); + }); + callback(null, provider); + }, (err) => { + callback(err); }); - ad.onClose((e) => { - this._isLoaded = false; - this._isLoading = false; - this._dispatchEvent(EventType.close, e); - if (this._preload === true) { - this._loadAd(); - } + }, + payment(callback) { + plus.payment.getChannels((services) => { + const provider = []; + services.forEach(({ id }) => { + provider.push(id); + }); + callback(null, provider); + }, (err) => { + callback(err); }); - ad.onError((e) => { - this._isLoading = false; - const data = { - code: e.code, - errMsg: e.message, - }; - this._dispatchEvent(EventType.error, data); - const error = new Error(JSON.stringify(data)); - if (this._loadPromiseReject != null) { - this._loadPromiseReject(error); - this._loadPromiseReject = null; + }, + push(callback) { + if (typeof weex !== 'undefined' || typeof plus !== 'undefined') { + callback(null, [plus.push.getClientInfo().id]); + } + else { + callback(null, []); + } + }, + }; + const getProvider = defineAsyncApi(API_GET_PROVIDER, ({ service }, { resolve, reject }) => { + if (providers[service]) { + providers[service]((err, provider) => { + if (err) { + reject(err.message); } - if (this._showPromiseReject != null) { - this._showPromiseReject(error); - this._showPromiseReject = null; + else { + resolve({ + service, + provider: provider, + }); } }); - ad.onAdClicked && - ad.onAdClicked(() => { - this._dispatchEvent(EventType.adClicked, {}); - }); } - getProvider() { - return this._adInstance.getProvider(); + else { + reject('service not found'); } - load() { - return new Promise((resolve, reject) => { - this._loadPromiseResolve = resolve; - this._loadPromiseReject = reject; - if (this._isLoading) { - return; + }, GetProviderProtocol); + + function getService(provider) { + return new Promise((resolve, reject) => { + plus.oauth.getServices((services) => { + const service = services.find(({ id }) => id === provider); + service ? resolve(service) : reject(new Error('provider not find')); + }, reject); + }); + } + /** + * 微信登录 + */ + const baseLogin = (params, { resolve, reject, }) => { + const provider = params.provider || 'weixin'; + const errorCallback = warpPlusErrorCallback(reject); + getService(provider) + .then((service) => { + function login() { + service.login((res) => { + const authResult = res.target.authResult; + resolve({ + code: authResult.code, + authResult: authResult, + }); + }, errorCallback, provider === 'apple' + ? { scope: 'email' } + : { + univerifyStyle: univerifyButtonsClickHandling(params.univerifyStyle, errorCallback), + } || {}); + } + // 先注销再登录 + // apple登录logout之后无法重新触发获取email,fullname;一键登录无logout + if (provider === 'apple' || provider === 'univerify') { + login(); + } + else { + service.logout(login, login); + } + }) + .catch(errorCallback); + }; + const login = defineAsyncApi(API_LOGIN, baseLogin, LoginProtocol); + const getUserInfo = defineAsyncApi(API_GET_USER_INFO, (params, { resolve, reject }) => { + const provider = params.provider || 'weixin'; + const errorCallback = warpPlusErrorCallback(reject); + getService(provider) + .then((loginService) => { + loginService.getUserInfo((res) => { + let userInfo = { nickName: '' }; + if (provider === 'weixin') { + const wechatUserInfo = loginService.userInfo; + if (wechatUserInfo) + userInfo = { + openId: wechatUserInfo.openid, + nickName: wechatUserInfo.nickname, + gender: wechatUserInfo.sex, + city: wechatUserInfo.city, + province: wechatUserInfo.province, + country: wechatUserInfo.country, + avatarUrl: wechatUserInfo.headimgurl, + // @ts-ignore + unionId: wechatUserInfo.unionid, + }; } - if (this._isLoaded) { - resolve(''); + else if (provider === 'apple') { + const appleInfo = loginService.appleInfo; + if (appleInfo) + userInfo = { + openId: appleInfo.user, + fullName: appleInfo.fullName, + email: appleInfo.email, + authorizationCode: appleInfo.authorizationCode, + identityToken: appleInfo.identityToken, + realUserStatus: appleInfo.realUserStatus, + }; } else { - this._loadAd(); + userInfo = loginService.userInfo; + if (userInfo) { + userInfo.openId = + userInfo.openId || + userInfo.openid || + loginService.authResult.openid; + userInfo.nickName = userInfo.nickName || userInfo.nickname; + userInfo.avatarUrl = userInfo.avatarUrl || userInfo.headimgurl; + } } - }); - } - show() { - return new Promise((resolve, reject) => { - this._showPromiseResolve = resolve; - this._showPromiseReject = reject; - if (this._isLoading) { - return; - } - if (this._isLoaded) { - this._showAd(); - resolve(''); + let result = {}; + // @ts-ignore + if (params.data && params.data.api_name === 'webapi_getuserinfo') { + result.data = { + data: JSON.stringify(userInfo), + rawData: '', + signature: '', + encryptedData: '', + iv: '', + }; } else { - this._loadAd(); + result.userInfo = userInfo; } + resolve(result); + }, errorCallback); + }) + .catch(() => { + reject('请先调用 uni.login'); + }); + }, GetUserInfoProtocol); + /** + * 获取用户信息-兼容 + */ + const getUserProfile = defineAsyncApi(API_GET_USER_PROFILE, (params, { resolve, reject }) => { + return baseLogin(params, { resolve, reject }); + }, GgetUserProfileProtocol); + const preLogin = defineAsyncApi(API_PRE_LOGIN, (params, { resolve, reject }) => { + const successCallback = warpPlusSuccessCallback(resolve); + const errorCallback = warpPlusErrorCallback(reject); + getService(params.provider) + .then((service) => service.preLogin(successCallback, errorCallback)) + .catch(errorCallback); + }, PreLoginProtocol, PreLoginOptions); + const _closeAuthView = () => getService('univerify').then((service) => service.closeAuthView()); + const closeAuthView = defineAsyncApi(API_CLOSE_AUTH_VIEW, _closeAuthView); + /** + * 一键登录自定义登陆按钮点击处理 + */ + function univerifyButtonsClickHandling(univerifyStyle, errorCallback) { + if (isPlainObject(univerifyStyle) && + univerifyStyle.buttons && + toTypeString(univerifyStyle.buttons.list) === '[object Array]' && + univerifyStyle.buttons.list.length > 0) { + univerifyStyle.buttons.list.forEach((button, index) => { + univerifyStyle.buttons.list[index].onclick = function () { + _closeAuthView().then(() => { + errorCallback({ + code: '30008', + message: '用户点击了自定义按钮', + index, + provider: button.provider, + }); + }); + }; }); } - destroy() { - this._adInstance.destroy(); - } - _loadAd() { - this._isLoaded = false; - this._isLoading = true; - this._adInstance.load(); - } - _showAd() { - this._adInstance.show(); - } + return univerifyStyle; } - class RewardedVideoAd extends AdBase { - constructor(options) { - super(plus.ad.createRewardedVideoAd(options), options); - this._loadAd(); - } - } - const createRewardedVideoAd = (defineSyncApi(API_CREATE_REWARDED_VIDEO_AD, (options) => { - return new RewardedVideoAd(options); - }, CreateRewardedVideoAdProtocol, CreateRewardedVideoAdOptions)); - - class FullScreenVideoAd extends AdBase { - constructor(options) { - super(plus.ad.createFullScreenVideoAd(options), options); - } - } - const createFullScreenVideoAd = (defineSyncApi(API_CREATE_FULL_SCREEN_VIDEO_AD, (options) => { - return new FullScreenVideoAd(options); - }, CreateFullScreenVideoAdProtocol, CreateFullScreenVideoAdOptions)); - - class InterstitialAd extends AdBase { - constructor(options) { - super(plus.ad.createInterstitialAd(options), options); - this._loadAd(); - } - } - const createInterstitialAd = (defineSyncApi(API_CREATE_INTERSTITIAL_AD, (options) => { - return new InterstitialAd(options); - }, CreateInterstitialAdProtocol, CreateInterstitialAdOptions)); + const registerRuntime = defineSyncApi('registerRuntime', (runtime) => { + // @ts-expect-error + extend(jsRuntime, runtime); + }); - const sdkCache = {}; - const sdkQueue = {}; - function initSDK(options) { - const provider = options.provider; - if (!sdkCache[provider]) { - sdkCache[provider] = {}; - } - if (typeof sdkCache[provider].plugin === 'object') { - options.success(sdkCache[provider].plugin); - return; + // 0:图文,1:纯文字,2:纯图片,3:音乐,4:视频,5:小程序 + const TYPES = { + 0: { + name: 'web', + title: '图文', + }, + 1: { + name: 'text', + title: '纯文字', + }, + 2: { + name: 'image', + title: '纯图片', + }, + 3: { + name: 'music', + title: '音乐', + }, + 4: { + name: 'video', + title: '视频', + }, + 5: { + name: 'miniProgram', + title: '小程序', + }, + }; + const parseParams = (args) => { + args.type = args.type || 0; + let { provider, type, title, summary: content, href, imageUrl, mediaUrl: media, scene, miniProgram, } = args; + if (typeof imageUrl === 'string' && imageUrl) { + imageUrl = getRealPath(imageUrl); } - if (!sdkQueue[provider]) { - sdkQueue[provider] = []; + const shareType = TYPES[type]; + if (shareType) { + const sendMsg = { + provider, + type: shareType.name, + title, + content, + href, + pictures: [imageUrl], + thumbs: imageUrl ? [imageUrl] : undefined, + media, + miniProgram, + extra: { + scene, + }, + }; + if (provider === 'weixin' && (type === 1 || type === 2)) { + delete sendMsg.thumbs; + } + return sendMsg; } - sdkQueue[provider].push(options); - if (sdkCache[provider].status === true) { - options.__plugin = sdkCache[provider].plugin; - return; + return '分享参数 type 不正确'; + }; + const sendShareMsg = function (service, params, resolve, reject, method = 'share') { + const errorCallback = warpPlusErrorCallback(reject); + service.send(params, () => { + resolve(); + }, errorCallback); + }; + const share = defineAsyncApi(API_SHREA, (params, { resolve, reject }) => { + const res = parseParams(params); + const errorCallback = warpPlusErrorCallback(reject); + if (typeof res === 'string') { + return reject(res); } - sdkCache[provider].status = true; - const plugin = requireNativePlugin(provider); - if (!plugin || !plugin.initSDK) { - sdkQueue[provider].forEach((item) => { - item.fail({ - code: -1, - message: 'provider [' + provider + '] invalid', - }); - }); - sdkQueue[provider].length = 0; - sdkCache[provider].status = false; - return; + else { + params = res; } - // TODO - sdkCache[provider].plugin = plugin; - options.__plugin = plugin; - plugin.initSDK((res) => { - const isSuccess = res.code === 1 || res.code === '1'; - if (isSuccess) { - sdkCache[provider].plugin = plugin; + plus.share.getServices((services) => { + const service = services.find(({ id }) => id === params.provider); + if (!service) { + reject('service not found'); } else { - sdkCache[provider].status = false; - } - sdkQueue[provider].forEach((item) => { - if (isSuccess) { - item.success(item.__plugin); + if (service.authenticated) { + sendShareMsg(service, params, resolve, reject); } else { - item.fail(res); + service.authorize(() => sendShareMsg(service, params, resolve, reject), errorCallback); } - }); - sdkQueue[provider].length = 0; - }); - } - class InteractiveAd extends AdEventHandler { - constructor(options) { - super(); - this._adpid = ''; - this._provider = ''; - this._userData = null; - this._isLoaded = false; - this._isLoading = false; - this._loadPromiseResolve = null; - this._loadPromiseReject = null; - this._showPromiseResolve = null; - this._showPromiseReject = null; - this._adInstance = null; - this._adError = ''; - this._adpid = options.adpid; - this._provider = options.provider; - this._userData = options.userData; - setTimeout(() => { - this._init(); - }); + } + }, errorCallback); + }, ShareProtocols, SahreOptions); + const shareWithSystem = defineAsyncApi(API_SHARE_WITH_SYSTEM, ({ type, imageUrl, summary, href }, { resolve, reject }) => { + const errorCallback = warpPlusErrorCallback(reject); + if (typeof imageUrl === 'string' && imageUrl) { + imageUrl = getRealPath(imageUrl); } - _init() { - this._adError = ''; - initSDK({ - provider: this._provider, - success: (res) => { - this._adInstance = res; - if (this._userData) { - this.bindUserData(this._userData); - } - this._loadAd(); - }, - fail: (err) => { - this._adError = err; - if (this._loadPromiseReject != null) { - this._loadPromiseReject(this._createError(err)); - this._loadPromiseReject = null; - } - this._dispatchEvent(EventType.error, err); - }, - }); - } - getProvider() { - return this._provider; - } - load() { - return new Promise((resolve, reject) => { - this._loadPromiseResolve = resolve; - this._loadPromiseReject = reject; - if (this._isLoading) { - return; - } - if (this._adError) { - this._init(); - return; - } - if (this._isLoaded) { - resolve(''); - } - else { - this._loadAd(); - } - }); - } - show() { - return new Promise((resolve, reject) => { - this._showPromiseResolve = resolve; - this._showPromiseReject = reject; - if (this._isLoading) { - return; - } - if (this._adError) { - this._init(); - return; - } - if (this._isLoaded) { - this._showAd(); - resolve(''); - } - else { - this._loadAd(); - } - }); - } - reportExposure() { - if (this._adInstance !== null) { - this._adInstance.reportExposure(); - } - } - bindUserData(data) { - if (this._adInstance !== null) { - this._adInstance.bindUserData(data); + plus.share.sendWithSystem({ + type, + pictures: imageUrl ? [imageUrl] : undefined, + content: summary, + href, + }, () => resolve(), errorCallback); + }, ShareWithSystemProtocols, ShareWithSystemOptions); + + const requestPayment = defineAsyncApi(API_REQUEST_PAYMENT, (params, { resolve, reject }) => { + const provider = params.provider; + const errorCallback = warpPlusErrorCallback(reject); + plus.payment.getChannels((services) => { + const service = services.find(({ id }) => id === provider); + if (!service) { + reject('service not found'); } - } - destroy() { - if (this._adInstance !== null && this._adInstance.destroy) { - this._adInstance.destroy({ - adpid: this._adpid, - }); + else { + plus.payment.request(service, params.orderInfo, (res) => { + resolve(res); + }, errorCallback); } - } - _loadAd() { - if (this._adInstance !== null) { - if (this._isLoading === true) { - return; + }, errorCallback); + }, RequestPaymentProtocol); + + function applyOptions(options, instance, publicThis) { + Object.keys(options).forEach((name) => { + if (name.indexOf('on') === 0) { + const hook = options[name]; + if (isFunction(hook)) { + vue.injectHook(name, hook.bind(publicThis), instance); } - this._isLoading = true; - this._adInstance.loadData({ - adpid: this._adpid, - }, (res) => { - this._isLoaded = true; - this._isLoading = false; - if (this._loadPromiseResolve != null) { - this._loadPromiseResolve(); - this._loadPromiseResolve = null; - } - if (this._showPromiseResolve != null) { - this._showPromiseResolve(); - this._showPromiseResolve = null; - this._showAd(); - } - this._dispatchEvent(EventType.load, res); - }, (err) => { - this._isLoading = false; - if (this._showPromiseReject != null) { - this._showPromiseReject(this._createError(err)); - this._showPromiseReject = null; - } - this._dispatchEvent(EventType.error, err); - }); - } - } - _showAd() { - if (this._adInstance !== null && this._isLoaded === true) { - this._adInstance.show({ - adpid: this._adpid, - }, () => { - this._isLoaded = false; - }, (err) => { - this._isLoaded = false; - if (this._showPromiseReject != null) { - this._showPromiseReject(this._createError(err)); - this._showPromiseReject = null; - } - this._dispatchEvent(EventType.error, err); - }); } - } - _createError(err) { - return new Error(JSON.stringify(err)); - } - } - const createInteractiveAd = (defineSyncApi(API_CREATE_INTERACTIVE_AD, (options) => { - return new InteractiveAd(options); - }, CreateInteractiveAdProtocol, CreateInteractiveAdOptions)); + }); + } - let pendingNavigator = false; - function setPendingNavigator(path, callback, msg) { - pendingNavigator = { - path, - nvue: getRouteMeta(path).isNVue, - callback, - }; - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('setPendingNavigator', path, msg)); - } - } - function navigate(path, callback, isAppLaunch) { - if (!isAppLaunch && pendingNavigator) { - return console.error(`Waiting to navigate to: ${pendingNavigator.path}, do not operate continuously: ${path}.`); + function set(target, key, val) { + return (target[key] = val); + } + + function errorHandler(err, instance, info) { + if (!instance) { + throw err; } - if (__uniConfig.renderer === 'native') { - // 纯原生无需wait逻辑 - // 如果是首页还未初始化,需要等一等,其他无需等待 - if (getCurrentPages().length === 0) { - return setPendingNavigator(path, callback, 'waitForReady'); - } - return callback(); + const app = getApp(); + if (!app || !app.$vm) { + throw err; } - // 未创建 preloadWebview 或 preloadWebview 已被使用 - const waitPreloadWebview = !preloadWebview || (preloadWebview && preloadWebview.__uniapp_route); - // 已创建未 loaded - const waitPreloadWebviewReady = preloadWebview && !preloadWebview.loaded; - if (waitPreloadWebview || waitPreloadWebviewReady) { - setPendingNavigator(path, callback, waitPreloadWebview ? 'waitForCreate' : 'waitForReady'); + { + invokeHook(app.$vm, 'onError', err); } - else { - callback(); + } + + function initApp(app) { + const appConfig = app._context.config; + if (isFunction(app._component.onError)) { + appConfig.errorHandler = errorHandler; } - if (waitPreloadWebviewReady) { - onWebviewReady(preloadWebview.id, pendingNavigate); + const globalProperties = appConfig.globalProperties; + { + globalProperties.$set = set; + globalProperties.$applyOptions = applyOptions; } - } - function pendingNavigate() { - if (!pendingNavigator) { + } + + let isInitEntryPage = false; + function initEntry() { + if (isInitEntryPage) { return; } - const { callback } = pendingNavigator; - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('pendingNavigate', pendingNavigator.path)); + isInitEntryPage = true; + let entryPagePath; + let entryPageQuery; + const weexPlus = weex.requireModule('plus'); + if (weexPlus.getRedirectInfo) { + const info = weexPlus.getRedirectInfo() || {}; + entryPagePath = info.path; + entryPageQuery = info.query ? '?' + info.query : ''; } - pendingNavigator = false; - return callback(); - } - function navigateFinish() { - if (__uniConfig.renderer === 'native') { - if (!pendingNavigator) { + else { + const argsJsonStr = plus.runtime.arguments; + if (!argsJsonStr) { return; } - if (pendingNavigator.nvue) { - return pendingNavigate(); + try { + const args = JSON.parse(argsJsonStr); + entryPagePath = args.path || args.pathName; + entryPageQuery = args.query ? '?' + args.query : ''; } - return; + catch (e) { } } - // 创建预加载 - const preloadWebview = createPreloadWebview(); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('navigateFinish', 'preloadWebview', preloadWebview.id)); + if (!entryPagePath || entryPagePath === __uniConfig.entryPagePath) { + if (entryPageQuery) { + __uniConfig.entryPageQuery = entryPageQuery; + } + return; } - if (!pendingNavigator) { + const entryRoute = '/' + entryPagePath; + const routeOptions = getRouteOptions(entryRoute); + if (!routeOptions) { return; } - if (pendingNavigator.nvue) { - return pendingNavigate(); + if (!routeOptions.meta.isTabBar) { + __uniConfig.realEntryPagePath = + __uniConfig.realEntryPagePath || __uniConfig.entryPagePath; } - preloadWebview.loaded - ? pendingNavigator.callback() - : onWebviewReady(preloadWebview.id, pendingNavigate); + __uniConfig.entryPagePath = entryPagePath; + __uniConfig.entryPageQuery = entryPageQuery; } - function closeWebview(webview, animationType, animationDuration) { - webview[webview.__preload__ ? 'hide' : 'close'](animationType, animationDuration); - } - function showWebview(webview, animationType, animationDuration, showCallback, delay) { - if (typeof delay === 'undefined') { - delay = webview.nvue ? 0 : 100; - } - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('showWebview', 'delay', delay)); + const isIOS = plus.os.name === 'iOS'; + let config; + /** + * tabbar显示状态 + */ + let visible = true; + let tabBar; + /** + * 设置角标 + * @param {string} type + * @param {number} index + * @param {string} text + */ + function setTabBarBadge(type, index, text) { + if (!tabBar) { + return; } - const execShowCallback = function () { - if (execShowCallback._called) { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('execShowCallback', 'prevent')); - } - return; - } - execShowCallback._called = true; - showCallback && showCallback(); - navigateFinish(); - }; - execShowCallback._called = false; - setTimeout(() => { - const timer = setTimeout(() => { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('showWebview', 'callback', 'timer')); - } - execShowCallback(); - }, animationDuration + 150); - webview.show(animationType, animationDuration, () => { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('showWebview', 'callback')); - } - if (!execShowCallback._called) { - clearTimeout(timer); - } - execShowCallback(); + if (type === 'none') { + tabBar.hideTabBarRedDot({ + index, + }); + tabBar.removeTabBarBadge({ + index, }); - }, delay); - } - function backWebview(webview, callback) { - const children = webview.children(); - if (!children || !children.length) { - // 有子 webview - return callback(); - } - // 如果页面有subNvues,切使用了webview组件,则返回时子webview会取错,因此需要做id匹配 - const childWebview = children.find((webview) => webview.id.indexOf(WEBVIEW_ID_PREFIX) === 0) || - children[0]; - childWebview.canBack(({ canBack }) => { - if (canBack) { - childWebview.back(); // webview 返回 - } - else { - callback(); - } - }); - } - - class UniPageNode extends UniNode { - constructor(pageId, options, setup = false) { - super(NODE_TYPE_PAGE, '#page', null); - this._id = 1; - this._created = false; - this._createActionMap = new Map(); - this.updateActions = []; - this.nodeId = 0; - this.pageId = pageId; - this.pageNode = this; - this.isUnmounted = false; - this.createAction = [ACTION_TYPE_PAGE_CREATE, options]; - this.createdAction = [ACTION_TYPE_PAGE_CREATED]; - this._update = this.update.bind(this); - setup && this.setup(); - } - onCreate(thisNode, nodeName) { - pushCreateAction(this, thisNode.nodeId, nodeName); - return thisNode; } - onInsertBefore(thisNode, newChild, refChild) { - pushInsertAction(this, newChild, thisNode.nodeId, (refChild && refChild.nodeId) || -1); - return newChild; + else if (type === 'text') { + tabBar.setTabBarBadge({ + index, + text, + }); } - onRemoveChild(oldChild) { - pushRemoveAction(this, oldChild.nodeId); - return oldChild; + else if (type === 'redDot') { + tabBar.showTabBarRedDot({ + index, + }); } - onAddEvent(thisNode, name, flag) { - if (thisNode.parentNode) { - pushAddEventAction(this, thisNode.nodeId, name, flag); - } + } + /** + * 动态设置 tabBar 某一项的内容 + */ + function setTabBarItem(index, text, iconPath, selectedIconPath) { + const item = { + index, + }; + if (text !== undefined) { + item.text = text; } - onRemoveEvent(thisNode, name) { - if (thisNode.parentNode) { - pushRemoveEventAction(this, thisNode.nodeId, name); - } + if (iconPath) { + item.iconPath = getRealPath(iconPath); } - onSetAttribute(thisNode, qualifiedName, value) { - if (thisNode.parentNode) { - pushSetAttributeAction(this, thisNode.nodeId, qualifiedName, value); - } + if (selectedIconPath) { + item.selectedIconPath = getRealPath(selectedIconPath); } - onRemoveAttribute(thisNode, qualifiedName) { - if (thisNode.parentNode) { - pushRemoveAttributeAction(this, thisNode.nodeId, qualifiedName); + tabBar && tabBar.setTabBarItem(item); + } + /** + * 动态设置 tabBar 的整体样式 + * @param {Object} style 样式 + */ + function setTabBarStyle(style) { + tabBar && tabBar.setTabBarStyle(style); + } + /** + * 隐藏 tabBar + * @param {boolean} animation 是否需要动画效果 + */ + function hideTabBar(animation) { + visible = false; + tabBar && + tabBar.hideTabBar({ + animation, + }); + } + /** + * 显示 tabBar + * @param {boolean} animation 是否需要动画效果 + */ + function showTabBar(animation) { + visible = true; + tabBar && + tabBar.showTabBar({ + animation, + }); + } + const maskClickCallback = []; + var tabBar$1 = { + id: '0', + init(options, clickCallback) { + if (options && options.list.length) { + config = options; } - } - onTextContent(thisNode, text) { - if (thisNode.parentNode) { - pushSetTextAction(this, thisNode.nodeId, text); + try { + tabBar = weex.requireModule('uni-tabview'); } - } - onNodeValue(thisNode, val) { - if (thisNode.parentNode) { - pushSetTextAction(this, thisNode.nodeId, val); + catch (error) { + console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`); } - } - genId() { - return this._id++; - } - push(action, extras) { - if (this.isUnmounted) { - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('PageNode', 'push.prevent', action)); + tabBar.onMaskClick(() => { + maskClickCallback.forEach((callback) => { + callback(); + }); + }); + tabBar && + tabBar.onClick(({ index }) => { + clickCallback(config.list[index], index); + }); + tabBar && + tabBar.onMidButtonClick(() => { + // publish('onTabBarMidButtonTap', {}) + }); + }, + indexOf(page) { + const itemLength = config && config.list && config.list.length; + if (itemLength) { + for (let i = 0; i < itemLength; i++) { + if (config.list[i].pagePath === page || + config.list[i].pagePath === `${page}.html`) { + return i; + } } - return; } - switch (action[0]) { - case ACTION_TYPE_CREATE: - this._createActionMap.set(action[1], action); - break; - case ACTION_TYPE_INSERT: - const createAction = this._createActionMap.get(action[1]); - if (createAction) { - createAction[3] = action[2]; // parentNodeId - if (extras) { - createAction[4] = extras; - } - } - else { - if ((process.env.NODE_ENV !== 'production')) { - console.error(formatLog(`Insert`, action, 'not found createAction')); - } - } - break; + return -1; + }, + switchTab(page) { + const index = this.indexOf(page); + if (index >= 0) { + tabBar && + tabBar.switchSelect({ + index, + }); + return true; } - this.updateActions.push(action); - vue.queuePostFlushCb(this._update); - } - restore() { - this.push(this.createAction); - // TODO restore children - this.push(this.createdAction); - } - setup() { - this.send([this.createAction]); + return false; + }, + setTabBarBadge, + setTabBarItem, + setTabBarStyle, + hideTabBar, + showTabBar, + append(webview) { + tabBar && + tabBar.append({ + id: webview.id, + }, ({ code }) => { + if (code !== 0) { + setTimeout(() => { + this.append(webview); + }, 20); + } + }); + }, + get visible() { + return visible; + }, + get height() { + return ((config && config.height ? parseFloat(config.height) : TABBAR_HEIGHT) + + plus.navigator.getSafeAreaInsets().deviceBottom); + }, + // tabBar是否遮挡内容区域 + get cover() { + const array = ['extralight', 'light', 'dark']; + return isIOS && array.indexOf(config.blurEffect) >= 0; + }, + setStyle({ mask }) { + tabBar.setMask({ + color: mask, + }); + }, + addEventListener(_name, callback) { + maskClickCallback.push(callback); + }, + removeEventListener(_name, callback) { + const callbackIndex = maskClickCallback.indexOf(callback); + maskClickCallback.splice(callbackIndex, 1); + }, + }; + + function initTabBar() { + const { tabBar } = __uniConfig; + const len = tabBar && tabBar.list && tabBar.list.length; + if (!len) { + return; } - update() { - const { updateActions, _createActionMap } = this; - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('PageNode', 'update', updateActions.length, _createActionMap.size)); - } - _createActionMap.clear(); - // 首次 - if (!this._created) { - this._created = true; - updateActions.push(this.createdAction); - } - if (updateActions.length) { - this.send(updateActions); - updateActions.length = 0; - } + const { entryPagePath } = __uniConfig; + tabBar.selectedIndex = 0; + const selected = tabBar.list.findIndex((page) => page.pagePath === entryPagePath); + tabBar$1.init(tabBar, (item, index) => { + uni.switchTab({ + url: '/' + item.pagePath, + openType: 'switchTab', + from: 'tabBar', + success() { + invokeHook('onTabItemTap', { + index, + text: item.text, + pagePath: item.pagePath, + }); + }, + }); + }); + if (selected !== -1) { + // 取当前 tab 索引值 + tabBar.selectedIndex = selected; + selected !== 0 && tabBar$1.switchTab(entryPagePath); } - send(action) { - UniServiceJSBridge.publishHandler(VD_SYNC, action, this.pageId); + } + + function backbuttonListener() { + uni.navigateBack({ + from: 'backbutton', + }); + } + + function initGlobalEvent() { + const plusGlobalEvent = plus.globalEvent; + const weexGlobalEvent = weex.requireModule('globalEvent'); + const emit = UniServiceJSBridge.emit; + if (weex.config.preload) { + plus.key.addEventListener('backbutton', backbuttonListener); } - fireEvent(id, evt) { - const node = findNodeById(id, this); - if (node) { - node.dispatchEvent(evt); - } - else if ((process.env.NODE_ENV !== 'production')) { - console.error(formatLog('PageNode', 'fireEvent', id, 'not found', evt)); - } + else { + plusGlobalEvent.addEventListener('splashclosed', () => { + plus.key.addEventListener('backbutton', backbuttonListener); + }); } + plusGlobalEvent.addEventListener('pause', () => { + emit('onAppEnterBackground'); + }); + plusGlobalEvent.addEventListener('resume', () => { + emit('onAppEnterForeground'); + }); + weexGlobalEvent.addEventListener('uistylechange', function (event) { + const args = { + theme: event.uistyle, + }; + emit('onThemeChange', args); + }); + plusGlobalEvent.addEventListener('plusMessage', subscribePlusMessage); + // nvue webview post message + plusGlobalEvent.addEventListener('WebviewPostMessage', subscribePlusMessage); } - function findNodeById(id, uniNode) { - if (uniNode.nodeId === id) { - return uniNode; + function subscribePlusMessage({ data, }) { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('plusMessage', data)); } - const { childNodes } = uniNode; - for (let i = 0; i < childNodes.length; i++) { - const uniNode = findNodeById(id, childNodes[i]); - if (uniNode) { - return uniNode; - } + if (data && data.type) { + UniServiceJSBridge.subscribeHandler('plusMessage.' + data.type, data.args); } - return null; - } - function pushCreateAction(pageNode, nodeId, nodeName) { - pageNode.push([ACTION_TYPE_CREATE, nodeId, nodeName, -1]); - } - function pushInsertAction(pageNode, newChild, parentNodeId, refChildId) { - const nodeJson = newChild.toJSON({ attr: true }); - pageNode.push([ACTION_TYPE_INSERT, newChild.nodeId, parentNodeId, refChildId], Object.keys(nodeJson).length ? nodeJson : undefined); - } - function pushRemoveAction(pageNode, nodeId) { - pageNode.push([ACTION_TYPE_REMOVE, nodeId]); - } - function pushAddEventAction(pageNode, nodeId, name, value) { - pageNode.push([ACTION_TYPE_ADD_EVENT, nodeId, name, value]); - } - function pushRemoveEventAction(pageNode, nodeId, name) { - pageNode.push([ACTION_TYPE_REMOVE_EVENT, nodeId, name]); } - function pushSetAttributeAction(pageNode, nodeId, name, value) { - pageNode.push([ACTION_TYPE_SET_ATTRIBUTE, nodeId, name, value]); - } - function pushRemoveAttributeAction(pageNode, nodeId, name) { - pageNode.push([ACTION_TYPE_REMOVE_ATTRIBUTE, nodeId, name]); - } - function pushSetTextAction(pageNode, nodeId, text) { - pageNode.push([ACTION_TYPE_SET_TEXT, nodeId, text]); - } - function createPageNode(pageId, pageOptions, setup) { - return new UniPageNode(pageId, pageOptions, setup); + function onPlusMessage(type, callback, once = false) { + UniServiceJSBridge.subscribe('plusMessage.' + type, callback, once); } - const pages = []; - function addCurrentPage(page) { - pages.push(page); + function initAppLaunch(appVm) { + const args = { + path: __uniConfig.entryPagePath, + query: {}, + scene: 1001, + }; + invokeHook(appVm, 'onLaunch', args); + invokeHook(appVm, 'onShow', args); + } + + // 统一处理路径 + function getPath(path) { + path = path.replace(/\/$/, ''); + return path.indexOf('_') === 0 + ? plus.io.convertLocalFileSystemURL(path) + : path; } - function getCurrentPages$1() { - const curPages = []; - pages.forEach((page) => { - if (page.__isTabBar) { - if (page.$.__isActive) { - curPages.push(page); + function clearTempFile() { + const basePath = getPath(TEMP_PATH_BASE); + const tempPath = getPath(TEMP_PATH); + // 获取父目录 + const dirParts = tempPath.split('/'); + dirParts.pop(); + const dirPath = dirParts.join('/'); + plus.io.resolveLocalFileSystemURL(plus.io.convertAbsoluteFileSystem(dirPath), (entry) => { + const reader = entry.createReader(); + reader.readEntries(function (entry) { + // plus.d.ts 类型不对 + const entries = entry; + if (entries && entries.length) { + entries.forEach(function (entry) { + if (entry.isDirectory && + entry.fullPath.indexOf(basePath) === 0 && + entry.fullPath.indexOf(tempPath) !== 0) { + entry.removeRecursively(); + } + }); } - } - else { - curPages.push(page); - } + }); }); - return curPages; - } - function removePage(curPage) { - const index = pages.findIndex((page) => page === curPage); - if (index === -1) { - return; - } - if (!curPage.$page.meta.isNVue) { - curPage.$.appContext.app.unmount(); - } - pages.splice(index, 1); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('removePage', curPage.$page)); - } } - function setupPage(component) { - const oldSetup = component.setup; - component.inheritAttrs = false; // 禁止继承 __pageId 等属性,避免告警 - component.setup = (_, ctx) => { - const { attrs: { __pageId, __pagePath, __pageQuery, __pageInstance }, } = ctx; - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog(__pagePath, 'setup')); - } - const instance = vue.getCurrentInstance(); - const pageVm = instance.proxy; - pageVm.$page = __pageInstance; - addCurrentPage(initScope(__pageId, pageVm)); - if (oldSetup) { - return oldSetup(__pageQuery, ctx); - } - }; - return component; - } - function initScope(pageId, vm) { - const $getAppWebview = () => { - return plus.webview.getWebviewById(pageId + ''); - }; - vm.$getAppWebview = $getAppWebview; - vm.$scope = { - $getAppWebview, - }; - return vm; - } + const VD_SYNC = 'vdSync'; + const ON_WEBVIEW_READY = 'onWebviewReady'; + const INVOKE_VIEW_API = 'invokeViewApi'; + const INVOKE_SERVICE_API = 'invokeServiceApi'; + + const ACTION_TYPE_PAGE_CREATE = 1; + const ACTION_TYPE_PAGE_CREATED = 2; + const ACTION_TYPE_CREATE = 3; + const ACTION_TYPE_INSERT = 4; + const ACTION_TYPE_REMOVE = 5; + const ACTION_TYPE_SET_ATTRIBUTE = 6; + const ACTION_TYPE_REMOVE_ATTRIBUTE = 7; + const ACTION_TYPE_ADD_EVENT = 8; + const ACTION_TYPE_REMOVE_EVENT = 9; + const ACTION_TYPE_SET_TEXT = 10; + const ACTION_TYPE_EVENT = 20; + + function onNodeEvent(nodeId, evt, pageNode) { + pageNode.fireEvent(nodeId, evt); + } + + function onVdSync(actions, pageId) { + const page = getPageById(parseInt(pageId)); + if (!page) { + if ((process.env.NODE_ENV !== 'production')) { + console.error(formatLog('onVdSync', 'page', pageId, 'not found')); + } + return; + } + const pageNode = page.$.appContext.app + ._container; + actions.forEach((action) => { + switch (action[0]) { + case ACTION_TYPE_EVENT: + onNodeEvent(action[1], action[2], pageNode); + break; + } + }); + } + + function initNVue(webviewStyle, routeMeta, path) { + if (path && routeMeta.isNVue) { + webviewStyle.uniNView = { + path, + defaultFontSize: __uniConfig.defaultFontSize, + viewport: __uniConfig.viewport, + }; + } + } + + const colorRE = /^#[a-z0-9]{6}$/i; + function isColor(color) { + return color && (colorRE.test(color) || color === 'transparent'); + } + + function initBackgroundColor(webviewStyle, routeMeta) { + const { backgroundColor } = routeMeta; + if (!backgroundColor) { + return; + } + if (!isColor(backgroundColor)) { + return; + } + if (!webviewStyle.background) { + webviewStyle.background = backgroundColor; + } + if (!webviewStyle.backgroundColorTop) { + webviewStyle.backgroundColorTop = backgroundColor; + } + } + + function initPopGesture(webviewStyle, routeMeta) { + // 不支持 hide + if (webviewStyle.popGesture === 'hide') { + delete webviewStyle.popGesture; + } + // 似乎没用了吧?记得是之前流应用时,需要 appback 的逻辑 + if (routeMeta.isQuit) { + webviewStyle.popGesture = (plus.os.name === 'iOS' ? 'appback' : 'none'); + } + } + + function initPullToRefresh(webviewStyle, routeMeta) { + if (!routeMeta.enablePullDownRefresh) { + return; + } + webviewStyle.pullToRefresh = normalizePullToRefreshRpx(extend({}, plus.os.name === 'Android' + ? defaultAndroidPullToRefresh + : defaultPullToRefresh, routeMeta.pullToRefresh)); + } + const defaultAndroidPullToRefresh = { support: true, style: 'circle' }; + const defaultPullToRefresh = { + support: true, + style: 'default', + height: '50px', + range: '200px', + contentdown: { + caption: '', + }, + contentover: { + caption: '', + }, + contentrefresh: { + caption: '', + }, + }; + + function initTitleNView(webviewStyle, routeMeta) { + const { navigationBar } = routeMeta; + if (navigationBar.style === 'custom') { + return false; + } + let autoBackButton = true; + if (routeMeta.isQuit) { + autoBackButton = false; + } + const titleNView = { + autoBackButton, + }; + Object.keys(navigationBar).forEach((name) => { + const value = navigationBar[name]; + if (name === 'backgroundColor') { + titleNView.backgroundColor = isColor(value) + ? value + : BACKGROUND_COLOR; + } + else if (name === 'titleImage' && value) { + titleNView.tags = createTitleImageTags(value); + } + else if (name === 'buttons' && isArray(value)) { + titleNView.buttons = value.map((button, index) => { + button.onclick = createTitleNViewBtnClick(index); + return button; + }); + } + else { + titleNView[name] = + value; + } + }); + webviewStyle.titleNView = titleNView; + } + function createTitleImageTags(titleImage) { + return [ + { + tag: 'img', + src: titleImage, + position: { + left: 'auto', + top: 'auto', + width: 'auto', + height: '26px', + }, + }, + ]; + } + function createTitleNViewBtnClick(index) { + return function onClick(btn) { + btn.index = index; + invokeHook('onNavigationBarButtonTap', btn); + }; + } + + function parseWebviewStyle(path, routeMeta) { + const webviewStyle = { + bounce: 'vertical', + }; + Object.keys(routeMeta).forEach((name) => { + if (WEBVIEW_STYLE_BLACKLIST.indexOf(name) === -1) { + webviewStyle[name] = + routeMeta[name]; + } + }); + initNVue(webviewStyle, routeMeta, path); + initPopGesture(webviewStyle, routeMeta); + initBackgroundColor(webviewStyle, routeMeta); + initTitleNView(webviewStyle, routeMeta); + initPullToRefresh(webviewStyle, routeMeta); + return webviewStyle; + } + const WEBVIEW_STYLE_BLACKLIST = [ + 'id', + 'route', + 'isNVue', + 'isQuit', + 'isEntry', + 'isTabBar', + 'tabBarIndex', + 'windowTop', + 'topWindow', + 'leftWindow', + 'rightWindow', + 'maxWidth', + 'usingComponents', + 'disableScroll', + 'enablePullDownRefresh', + 'navigationBar', + 'pullToRefresh', + 'onReachBottomDistance', + 'pageOrientation', + 'backgroundColor', + ]; + + let id = 2; + function getWebviewId() { + return id; + } + function genWebviewId() { + return id++; + } + function encode(val) { + return val; + } + function initUniPageUrl(path, query) { + const queryString = query ? stringifyQuery$1(query, encode) : ''; + return { + path: path.substr(1), + query: queryString ? queryString.substr(1) : queryString, + }; + } + function initDebugRefresh(isTab, path, query) { + const queryString = query ? stringifyQuery$1(query, encode) : ''; + return { + isTab, + arguments: JSON.stringify({ + path: path.substr(1), + query: queryString ? queryString.substr(1) : queryString, + }), + }; + } + + function createNVueWebview({ path, query, routeOptions, webviewStyle, }) { + const curWebviewId = genWebviewId(); + const curWebviewStyle = parseWebviewStyle(path, routeOptions.meta); + curWebviewStyle.uniPageUrl = initUniPageUrl(path, query); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('createNVueWebview', curWebviewId, path, curWebviewStyle)); + } + curWebviewStyle.isTab = !!routeOptions.meta.isTabBar; + return plus.webview.create('', String(curWebviewId), curWebviewStyle, extend({ + nvue: true, + }, webviewStyle)); + } + + const downgrade = plus.os.name === 'Android' && parseInt(plus.os.version) < 6; + const ANI_SHOW = downgrade ? 'slide-in-right' : 'pop-in'; + const ANI_DURATION = 300; + const ANI_CLOSE = downgrade ? 'slide-out-right' : 'pop-out'; + const VIEW_WEBVIEW_PATH = '_www/__uniappview.html'; + const WEBVIEW_ID_PREFIX = 'webviewId'; + + let preloadWebview; + function setPreloadWebview(webview) { + preloadWebview = webview; + } + function getPreloadWebview() { + return preloadWebview; + } + function createPreloadWebview() { + if (!preloadWebview || preloadWebview.__uniapp_route) { + // 不存在,或已被使用 + preloadWebview = plus.webview.create(VIEW_WEBVIEW_PATH, String(genWebviewId())); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('createPreloadWebview', preloadWebview.id)); + } + } + return preloadWebview; + } + + function initWebviewStyle(webview, path, query, routeMeta) { + const webviewStyle = parseWebviewStyle(path, routeMeta); + webviewStyle.uniPageUrl = initUniPageUrl(path, query); + const isTabBar = !!routeMeta.isTabBar; + if (!routeMeta.isNVue) { + webviewStyle.debugRefresh = initDebugRefresh(isTabBar, path, query); + } + else { + // android 需要使用 + webviewStyle.isTab = isTabBar; + } + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('updateWebview', webviewStyle)); + } + webview.setStyle(webviewStyle); + } + + function initWebview(webview, path, query, routeMeta) { + // 首页或非 nvue 页面 + if (webview.id === '1' || !routeMeta.isNVue) { + initWebviewStyle(webview, path, query, routeMeta); + } + } + + function createWebview(options) { + if (options.routeOptions.meta.isNVue) { + return createNVueWebview(options); + } + if (getWebviewId() === 2) { + // 如果首页非 nvue,则直接返回 Launch Webview + return plus.webview.getLaunchWebview(); + } + return getPreloadWebview(); + } + function onWebviewReady(pageId, callback) { + UniServiceJSBridge.once(ON_WEBVIEW_READY + '.' + pageId, callback); + } + + let isLaunchWebviewReady = false; // 目前首页双向确定 ready,可能会导致触发两次 onWebviewReady(主要是 Android) + function subscribeWebviewReady(_data, pageId) { + const isLaunchWebview = pageId === '1'; + if (isLaunchWebview && isLaunchWebviewReady) { + if ((process.env.NODE_ENV !== 'production')) { + console.log('[uni-app] onLaunchWebviewReady.prevent'); + } + return; + } + if (isLaunchWebview) { + // 首页 + isLaunchWebviewReady = true; + setPreloadWebview(plus.webview.getLaunchWebview()); + } + else if (!preloadWebview) { + // preloadWebview 不存在,重新加载一下 + setPreloadWebview(plus.webview.getWebviewById(pageId)); + } + if (preloadWebview.id !== pageId) { + return console.error(`webviewReady[${preloadWebview.id}][${pageId}] not match`); + } + preloadWebview.loaded = true; // 标记已 ready + UniServiceJSBridge.emit(ON_WEBVIEW_READY + '.' + pageId); + isLaunchWebview && onLaunchWebviewReady(); + } + function onLaunchWebviewReady() { + const { autoclose, alwaysShowBeforeRender } = __uniConfig.splashscreen; + if (autoclose && !alwaysShowBeforeRender) { + plus.navigator.closeSplashscreen(); + } + const entryPagePath = '/' + __uniConfig.entryPagePath; + const routeOptions = getRouteOptions(entryPagePath); + if (!routeOptions.meta.isNVue) { + // 非 nvue 首页,需要主动跳转 + const args = { + url: entryPagePath + (__uniConfig.entryPageQuery || ''), + openType: 'appLaunch', + }; + if (routeOptions.meta.isTabBar) { + return uni.switchTab(args); + } + return uni.navigateTo(args); + } + } + + function initSubscribeHandlers() { + const { subscribe, subscribeHandler } = UniServiceJSBridge; + onPlusMessage('subscribeHandler', ({ type, data, pageId }) => { + subscribeHandler(type, data, pageId); + }); + if (__uniConfig.renderer !== 'native') { + // 非纯原生 + subscribe(ON_WEBVIEW_READY, subscribeWebviewReady); + subscribe(VD_SYNC, onVdSync); + subscribe(INVOKE_SERVICE_API, onInvokeServiceApi); + } + } + function onInvokeServiceApi({ data: { method, args }, }) { + uni[method] && uni[method](args); + } + + let appCtx; + const defaultApp = { + globalData: {}, + }; + function getApp$1({ allowDefault = false } = {}) { + if (appCtx) { + // 真实的 App 已初始化 + return appCtx; + } + if (allowDefault) { + // 返回默认实现 + return defaultApp; + } + console.error('[warn]: getApp() failed. Learn more: https://uniapp.dcloud.io/collocation/frame/window?id=getapp.'); + } + function registerApp(appVm) { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('registerApp')); + } + appCtx = appVm; + appCtx.$vm = appVm; + extend(appCtx, defaultApp); // 拷贝默认实现 + const { $options } = appVm; + if ($options) { + appCtx.globalData = extend($options.globalData || {}, appCtx.globalData); + } + initService(); + initEntry(); + initTabBar(); + initGlobalEvent(); + initSubscribeHandlers(); + initAppLaunch(appVm); + // 10s后清理临时文件 + setTimeout(clearTempFile, 10000); + __uniConfig.ready = true; + } + + var __vuePlugin = { + install(app) { + initMount(app); + initApp(app); + initServicePlugin(app); + }, + }; + function initMount(app) { + const oldMount = app.mount; + app.mount = (rootContainer) => { + const instance = oldMount.call(app, rootContainer); + if (rootContainer === '#app') { + registerApp(instance); + } + return instance; + }; + } + + const EventType = { + load: 'load', + close: 'close', + error: 'error', + adClicked: 'adClicked', + }; + class AdEventHandler { + constructor() { + this._callbacks = {}; + } + onLoad(callback) { + this._addEventListener(EventType.load, callback); + } + onClose(callback) { + this._addEventListener(EventType.close, callback); + } + onError(callback) { + this._addEventListener(EventType.error, callback); + } + offLoad(callback) { + this._removeEventListener(EventType.load, callback); + } + offClose(callback) { + this._removeEventListener(EventType.close, callback); + } + offError(callback) { + this._removeEventListener(EventType.error, callback); + } + _addEventListener(type, callback) { + if (typeof callback !== 'function') { + return; + } + this._callbacks[type].push(callback); + } + _removeEventListener(type, callback) { + const arrayFunction = this._callbacks[type]; + const index = arrayFunction.indexOf(callback); + if (index > -1) { + arrayFunction.splice(index, 1); + } + } + _dispatchEvent(name, data) { + this._callbacks[name].forEach((callback) => { + callback(data || {}); + }); + } + } + class AdBase extends AdEventHandler { + constructor(adInstance, options) { + super(); + this._isLoaded = false; + this._isLoading = false; + this._preload = true; + this._loadPromiseResolve = null; + this._loadPromiseReject = null; + this._showPromiseResolve = null; + this._showPromiseReject = null; + this._preload = options.preload !== undefined ? options.preload : false; + const ad = (this._adInstance = adInstance); + ad.onLoad(() => { + this._isLoaded = true; + this._isLoading = false; + if (this._loadPromiseResolve != null) { + this._loadPromiseResolve(); + this._loadPromiseResolve = null; + } + if (this._showPromiseResolve != null) { + this._showPromiseResolve(); + this._showPromiseResolve = null; + this._showAd(); + } + this._dispatchEvent(EventType.load, {}); + }); + ad.onClose((e) => { + this._isLoaded = false; + this._isLoading = false; + this._dispatchEvent(EventType.close, e); + if (this._preload === true) { + this._loadAd(); + } + }); + ad.onError((e) => { + this._isLoading = false; + const data = { + code: e.code, + errMsg: e.message, + }; + this._dispatchEvent(EventType.error, data); + const error = new Error(JSON.stringify(data)); + if (this._loadPromiseReject != null) { + this._loadPromiseReject(error); + this._loadPromiseReject = null; + } + if (this._showPromiseReject != null) { + this._showPromiseReject(error); + this._showPromiseReject = null; + } + }); + ad.onAdClicked && + ad.onAdClicked(() => { + this._dispatchEvent(EventType.adClicked, {}); + }); + } + getProvider() { + return this._adInstance.getProvider(); + } + load() { + return new Promise((resolve, reject) => { + this._loadPromiseResolve = resolve; + this._loadPromiseReject = reject; + if (this._isLoading) { + return; + } + if (this._isLoaded) { + resolve(''); + } + else { + this._loadAd(); + } + }); + } + show() { + return new Promise((resolve, reject) => { + this._showPromiseResolve = resolve; + this._showPromiseReject = reject; + if (this._isLoading) { + return; + } + if (this._isLoaded) { + this._showAd(); + resolve(''); + } + else { + this._loadAd(); + } + }); + } + destroy() { + this._adInstance.destroy(); + } + _loadAd() { + this._isLoaded = false; + this._isLoading = true; + this._adInstance.load(); + } + _showAd() { + this._adInstance.show(); + } + } + + class RewardedVideoAd extends AdBase { + constructor(options) { + super(plus.ad.createRewardedVideoAd(options), options); + this._loadAd(); + } + } + const createRewardedVideoAd = (defineSyncApi(API_CREATE_REWARDED_VIDEO_AD, (options) => { + return new RewardedVideoAd(options); + }, CreateRewardedVideoAdProtocol, CreateRewardedVideoAdOptions)); + + class FullScreenVideoAd extends AdBase { + constructor(options) { + super(plus.ad.createFullScreenVideoAd(options), options); + } + } + const createFullScreenVideoAd = (defineSyncApi(API_CREATE_FULL_SCREEN_VIDEO_AD, (options) => { + return new FullScreenVideoAd(options); + }, CreateFullScreenVideoAdProtocol, CreateFullScreenVideoAdOptions)); + + class InterstitialAd extends AdBase { + constructor(options) { + super(plus.ad.createInterstitialAd(options), options); + this._loadAd(); + } + } + const createInterstitialAd = (defineSyncApi(API_CREATE_INTERSTITIAL_AD, (options) => { + return new InterstitialAd(options); + }, CreateInterstitialAdProtocol, CreateInterstitialAdOptions)); + + const sdkCache = {}; + const sdkQueue = {}; + function initSDK(options) { + const provider = options.provider; + if (!sdkCache[provider]) { + sdkCache[provider] = {}; + } + if (typeof sdkCache[provider].plugin === 'object') { + options.success(sdkCache[provider].plugin); + return; + } + if (!sdkQueue[provider]) { + sdkQueue[provider] = []; + } + sdkQueue[provider].push(options); + if (sdkCache[provider].status === true) { + options.__plugin = sdkCache[provider].plugin; + return; + } + sdkCache[provider].status = true; + const plugin = requireNativePlugin(provider); + if (!plugin || !plugin.initSDK) { + sdkQueue[provider].forEach((item) => { + item.fail({ + code: -1, + message: 'provider [' + provider + '] invalid', + }); + }); + sdkQueue[provider].length = 0; + sdkCache[provider].status = false; + return; + } + // TODO + sdkCache[provider].plugin = plugin; + options.__plugin = plugin; + plugin.initSDK((res) => { + const isSuccess = res.code === 1 || res.code === '1'; + if (isSuccess) { + sdkCache[provider].plugin = plugin; + } + else { + sdkCache[provider].status = false; + } + sdkQueue[provider].forEach((item) => { + if (isSuccess) { + item.success(item.__plugin); + } + else { + item.fail(res); + } + }); + sdkQueue[provider].length = 0; + }); + } + class InteractiveAd extends AdEventHandler { + constructor(options) { + super(); + this._adpid = ''; + this._provider = ''; + this._userData = null; + this._isLoaded = false; + this._isLoading = false; + this._loadPromiseResolve = null; + this._loadPromiseReject = null; + this._showPromiseResolve = null; + this._showPromiseReject = null; + this._adInstance = null; + this._adError = ''; + this._adpid = options.adpid; + this._provider = options.provider; + this._userData = options.userData; + setTimeout(() => { + this._init(); + }); + } + _init() { + this._adError = ''; + initSDK({ + provider: this._provider, + success: (res) => { + this._adInstance = res; + if (this._userData) { + this.bindUserData(this._userData); + } + this._loadAd(); + }, + fail: (err) => { + this._adError = err; + if (this._loadPromiseReject != null) { + this._loadPromiseReject(this._createError(err)); + this._loadPromiseReject = null; + } + this._dispatchEvent(EventType.error, err); + }, + }); + } + getProvider() { + return this._provider; + } + load() { + return new Promise((resolve, reject) => { + this._loadPromiseResolve = resolve; + this._loadPromiseReject = reject; + if (this._isLoading) { + return; + } + if (this._adError) { + this._init(); + return; + } + if (this._isLoaded) { + resolve(''); + } + else { + this._loadAd(); + } + }); + } + show() { + return new Promise((resolve, reject) => { + this._showPromiseResolve = resolve; + this._showPromiseReject = reject; + if (this._isLoading) { + return; + } + if (this._adError) { + this._init(); + return; + } + if (this._isLoaded) { + this._showAd(); + resolve(''); + } + else { + this._loadAd(); + } + }); + } + reportExposure() { + if (this._adInstance !== null) { + this._adInstance.reportExposure(); + } + } + bindUserData(data) { + if (this._adInstance !== null) { + this._adInstance.bindUserData(data); + } + } + destroy() { + if (this._adInstance !== null && this._adInstance.destroy) { + this._adInstance.destroy({ + adpid: this._adpid, + }); + } + } + _loadAd() { + if (this._adInstance !== null) { + if (this._isLoading === true) { + return; + } + this._isLoading = true; + this._adInstance.loadData({ + adpid: this._adpid, + }, (res) => { + this._isLoaded = true; + this._isLoading = false; + if (this._loadPromiseResolve != null) { + this._loadPromiseResolve(); + this._loadPromiseResolve = null; + } + if (this._showPromiseResolve != null) { + this._showPromiseResolve(); + this._showPromiseResolve = null; + this._showAd(); + } + this._dispatchEvent(EventType.load, res); + }, (err) => { + this._isLoading = false; + if (this._showPromiseReject != null) { + this._showPromiseReject(this._createError(err)); + this._showPromiseReject = null; + } + this._dispatchEvent(EventType.error, err); + }); + } + } + _showAd() { + if (this._adInstance !== null && this._isLoaded === true) { + this._adInstance.show({ + adpid: this._adpid, + }, () => { + this._isLoaded = false; + }, (err) => { + this._isLoaded = false; + if (this._showPromiseReject != null) { + this._showPromiseReject(this._createError(err)); + this._showPromiseReject = null; + } + this._dispatchEvent(EventType.error, err); + }); + } + } + _createError(err) { + return new Error(JSON.stringify(err)); + } + } + const createInteractiveAd = (defineSyncApi(API_CREATE_INTERACTIVE_AD, (options) => { + return new InteractiveAd(options); + }, CreateInteractiveAdProtocol, CreateInteractiveAdOptions)); + + let pendingNavigator = false; + function setPendingNavigator(path, callback, msg) { + pendingNavigator = { + path, + nvue: getRouteMeta(path).isNVue, + callback, + }; + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('setPendingNavigator', path, msg)); + } + } + function navigate(path, callback, isAppLaunch) { + if (!isAppLaunch && pendingNavigator) { + return console.error(`Waiting to navigate to: ${pendingNavigator.path}, do not operate continuously: ${path}.`); + } + if (__uniConfig.renderer === 'native') { + // 纯原生无需wait逻辑 + // 如果是首页还未初始化,需要等一等,其他无需等待 + if (getCurrentPages().length === 0) { + return setPendingNavigator(path, callback, 'waitForReady'); + } + return callback(); + } + // 未创建 preloadWebview 或 preloadWebview 已被使用 + const waitPreloadWebview = !preloadWebview || (preloadWebview && preloadWebview.__uniapp_route); + // 已创建未 loaded + const waitPreloadWebviewReady = preloadWebview && !preloadWebview.loaded; + if (waitPreloadWebview || waitPreloadWebviewReady) { + setPendingNavigator(path, callback, waitPreloadWebview ? 'waitForCreate' : 'waitForReady'); + } + else { + callback(); + } + if (waitPreloadWebviewReady) { + onWebviewReady(preloadWebview.id, pendingNavigate); + } + } + function pendingNavigate() { + if (!pendingNavigator) { + return; + } + const { callback } = pendingNavigator; + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('pendingNavigate', pendingNavigator.path)); + } + pendingNavigator = false; + return callback(); + } + function navigateFinish() { + if (__uniConfig.renderer === 'native') { + if (!pendingNavigator) { + return; + } + if (pendingNavigator.nvue) { + return pendingNavigate(); + } + return; + } + // 创建预加载 + const preloadWebview = createPreloadWebview(); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('navigateFinish', 'preloadWebview', preloadWebview.id)); + } + if (!pendingNavigator) { + return; + } + if (pendingNavigator.nvue) { + return pendingNavigate(); + } + preloadWebview.loaded + ? pendingNavigator.callback() + : onWebviewReady(preloadWebview.id, pendingNavigate); + } + + function closeWebview(webview, animationType, animationDuration) { + webview[webview.__preload__ ? 'hide' : 'close'](animationType, animationDuration); + } + function showWebview(webview, animationType, animationDuration, showCallback, delay) { + if (typeof delay === 'undefined') { + delay = webview.nvue ? 0 : 100; + } + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('showWebview', 'delay', delay)); + } + const execShowCallback = function () { + if (execShowCallback._called) { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('execShowCallback', 'prevent')); + } + return; + } + execShowCallback._called = true; + showCallback && showCallback(); + navigateFinish(); + }; + execShowCallback._called = false; + setTimeout(() => { + const timer = setTimeout(() => { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('showWebview', 'callback', 'timer')); + } + execShowCallback(); + }, animationDuration + 150); + webview.show(animationType, animationDuration, () => { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('showWebview', 'callback')); + } + if (!execShowCallback._called) { + clearTimeout(timer); + } + execShowCallback(); + }); + }, delay); + } + function backWebview(webview, callback) { + const children = webview.children(); + if (!children || !children.length) { + // 有子 webview + return callback(); + } + // 如果页面有subNvues,切使用了webview组件,则返回时子webview会取错,因此需要做id匹配 + const childWebview = children.find((webview) => webview.id.indexOf(WEBVIEW_ID_PREFIX) === 0) || + children[0]; + childWebview.canBack(({ canBack }) => { + if (canBack) { + childWebview.back(); // webview 返回 + } + else { + callback(); + } + }); + } + + class UniPageNode extends UniNode { + constructor(pageId, options, setup = false) { + super(NODE_TYPE_PAGE, '#page', null); + this._id = 1; + this._created = false; + this._createActionMap = new Map(); + this.updateActions = []; + this.nodeId = 0; + this.pageId = pageId; + this.pageNode = this; + this.isUnmounted = false; + this.createAction = [ACTION_TYPE_PAGE_CREATE, options]; + this.createdAction = [ACTION_TYPE_PAGE_CREATED]; + this._update = this.update.bind(this); + setup && this.setup(); + } + onCreate(thisNode, nodeName) { + pushCreateAction(this, thisNode.nodeId, nodeName); + return thisNode; + } + onInsertBefore(thisNode, newChild, refChild) { + pushInsertAction(this, newChild, thisNode.nodeId, (refChild && refChild.nodeId) || -1); + return newChild; + } + onRemoveChild(oldChild) { + pushRemoveAction(this, oldChild.nodeId); + return oldChild; + } + onAddEvent(thisNode, name, flag) { + if (thisNode.parentNode) { + pushAddEventAction(this, thisNode.nodeId, name, flag); + } + } + onRemoveEvent(thisNode, name) { + if (thisNode.parentNode) { + pushRemoveEventAction(this, thisNode.nodeId, name); + } + } + onSetAttribute(thisNode, qualifiedName, value) { + if (thisNode.parentNode) { + pushSetAttributeAction(this, thisNode.nodeId, qualifiedName, value); + } + } + onRemoveAttribute(thisNode, qualifiedName) { + if (thisNode.parentNode) { + pushRemoveAttributeAction(this, thisNode.nodeId, qualifiedName); + } + } + onTextContent(thisNode, text) { + if (thisNode.parentNode) { + pushSetTextAction(this, thisNode.nodeId, text); + } + } + onNodeValue(thisNode, val) { + if (thisNode.parentNode) { + pushSetTextAction(this, thisNode.nodeId, val); + } + } + genId() { + return this._id++; + } + push(action, extras) { + if (this.isUnmounted) { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('PageNode', 'push.prevent', action)); + } + return; + } + switch (action[0]) { + case ACTION_TYPE_CREATE: + this._createActionMap.set(action[1], action); + break; + case ACTION_TYPE_INSERT: + const createAction = this._createActionMap.get(action[1]); + if (createAction) { + createAction[3] = action[2]; // parentNodeId + if (extras) { + createAction[4] = extras; + } + } + else { + if ((process.env.NODE_ENV !== 'production')) { + console.error(formatLog(`Insert`, action, 'not found createAction')); + } + } + break; + } + this.updateActions.push(action); + vue.queuePostFlushCb(this._update); + } + restore() { + this.push(this.createAction); + // TODO restore children + this.push(this.createdAction); + } + setup() { + this.send([this.createAction]); + } + update() { + const { updateActions, _createActionMap } = this; + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('PageNode', 'update', updateActions.length, _createActionMap.size)); + } + _createActionMap.clear(); + // 首次 + if (!this._created) { + this._created = true; + updateActions.push(this.createdAction); + } + if (updateActions.length) { + this.send(updateActions); + updateActions.length = 0; + } + } + send(action) { + UniServiceJSBridge.publishHandler(VD_SYNC, action, this.pageId); + } + fireEvent(id, evt) { + const node = findNodeById(id, this); + if (node) { + node.dispatchEvent(evt); + } + else if ((process.env.NODE_ENV !== 'production')) { + console.error(formatLog('PageNode', 'fireEvent', id, 'not found', evt)); + } + } + } + function findNodeById(id, uniNode) { + if (uniNode.nodeId === id) { + return uniNode; + } + const { childNodes } = uniNode; + for (let i = 0; i < childNodes.length; i++) { + const uniNode = findNodeById(id, childNodes[i]); + if (uniNode) { + return uniNode; + } + } + return null; + } + function pushCreateAction(pageNode, nodeId, nodeName) { + pageNode.push([ACTION_TYPE_CREATE, nodeId, nodeName, -1]); + } + function pushInsertAction(pageNode, newChild, parentNodeId, refChildId) { + const nodeJson = newChild.toJSON({ attr: true }); + pageNode.push([ACTION_TYPE_INSERT, newChild.nodeId, parentNodeId, refChildId], Object.keys(nodeJson).length ? nodeJson : undefined); + } + function pushRemoveAction(pageNode, nodeId) { + pageNode.push([ACTION_TYPE_REMOVE, nodeId]); + } + function pushAddEventAction(pageNode, nodeId, name, value) { + pageNode.push([ACTION_TYPE_ADD_EVENT, nodeId, name, value]); + } + function pushRemoveEventAction(pageNode, nodeId, name) { + pageNode.push([ACTION_TYPE_REMOVE_EVENT, nodeId, name]); + } + function pushSetAttributeAction(pageNode, nodeId, name, value) { + pageNode.push([ACTION_TYPE_SET_ATTRIBUTE, nodeId, name, value]); + } + function pushRemoveAttributeAction(pageNode, nodeId, name) { + pageNode.push([ACTION_TYPE_REMOVE_ATTRIBUTE, nodeId, name]); + } + function pushSetTextAction(pageNode, nodeId, text) { + pageNode.push([ACTION_TYPE_SET_TEXT, nodeId, text]); + } + function createPageNode(pageId, pageOptions, setup) { + return new UniPageNode(pageId, pageOptions, setup); + } + + const pages = []; + function addCurrentPage(page) { + pages.push(page); + } + function getCurrentPages$1() { + const curPages = []; + pages.forEach((page) => { + if (page.__isTabBar) { + if (page.$.__isActive) { + curPages.push(page); + } + } + else { + curPages.push(page); + } + }); + return curPages; + } + function removePage(curPage) { + const index = pages.findIndex((page) => page === curPage); + if (index === -1) { + return; + } + if (!curPage.$page.meta.isNVue) { + curPage.$.appContext.app.unmount(); + } + pages.splice(index, 1); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('removePage', curPage.$page)); + } + } + + function setupPage(component) { + const oldSetup = component.setup; + component.inheritAttrs = false; // 禁止继承 __pageId 等属性,避免告警 + component.setup = (_, ctx) => { + const { attrs: { __pageId, __pagePath, __pageQuery, __pageInstance }, } = ctx; + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog(__pagePath, 'setup')); + } + const instance = vue.getCurrentInstance(); + const pageVm = instance.proxy; + pageVm.$page = __pageInstance; + pageVm.$vm = pageVm; + addCurrentPage(initScope(__pageId, pageVm)); + if (oldSetup) { + return oldSetup(__pageQuery, ctx); + } + }; + return component; + } + function initScope(pageId, vm) { + const $getAppWebview = () => { + return plus.webview.getWebviewById(pageId + ''); + }; + vm.$getAppWebview = $getAppWebview; + vm.$scope = { + $getAppWebview, + }; + return vm; + } + + const pagesMap = new Map(); + function definePage(pagePath, component) { + pagesMap.set(pagePath, once(createFactory(component))); + } + function createPage(__pageId, __pagePath, __pageQuery, __pageInstance, pageOptions) { + const pageNode = createPageNode(__pageId, pageOptions, true); + const app = vue.createApp(pagesMap.get(__pagePath)(), { + __pageId, + __pagePath, + __pageQuery, + __pageInstance, + }).use(__vuePlugin); + const oldUnmount = app.unmount; + app.unmount = () => { + pageNode.isUnmounted = true; + return oldUnmount.call(app); + }; + return app.mount(pageNode); + } + function createFactory(component) { + return () => { + return setupPage(component); + }; + } + + function initRouteOptions(path, openType) { + // 需要序列化一遍 + const routeOptions = JSON.parse(JSON.stringify(getRouteOptions(path))); + routeOptions.meta = initRouteMeta(routeOptions.meta); + if (openType === 'reLaunch' || + (!__uniConfig.realEntryPagePath && getCurrentPages().length === 0) // redirectTo + ) { + routeOptions.meta.isQuit = true; + } + else if (!routeOptions.meta.isTabBar) { + routeOptions.meta.isQuit = false; + } + // TODO + // if (routeOptions.meta.isTabBar) { + // routeOptions.meta.visible = true + // } + return routeOptions; + } + + function getStatusbarHeight() { + // 横屏时 iOS 获取的状态栏高度错误,进行纠正 + return plus.navigator.isImmersedStatusbar() + ? Math.round(plus.os.name === 'iOS' + ? plus.navigator.getSafeAreaInsets().top + : plus.navigator.getStatusbarHeight()) + : 0; + } + + function registerPage({ url, path, query, openType, webview, vm, }) { + // fast 模式,nvue 首页时,会在nvue中主动调用registerPage并传入首页webview,此时初始化一下首页(因为此时可能还未调用registerApp) + if (webview) { + initEntry(); + } + // TODO preloadWebview + const routeOptions = initRouteOptions(path, openType); + if (!webview) { + webview = createWebview({ path, routeOptions, query }); + } + else { + webview = plus.webview.getWebviewById(webview.id); + webview.nvue = routeOptions.meta.isNVue; + } + routeOptions.meta.id = parseInt(webview.id); + const isTabBar = !!routeOptions.meta.isTabBar; + if (isTabBar) { + tabBar$1.append(webview); + } + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('registerPage', path, webview.id)); + } + initWebview(webview, path, query, routeOptions.meta); + const route = path.substr(1); + webview.__uniapp_route = route; + const pageInstance = initPageInternalInstance(openType, url, query, routeOptions.meta); + if (!webview.nvue) { + createPage(parseInt(webview.id), route, query, pageInstance, initPageOptions(routeOptions)); + } + else { + vm && addCurrentPage(vm); + } + return webview; + } + function initPageOptions({ meta }) { + const statusbarHeight = getStatusbarHeight(); + const { platform, pixelRatio, windowWidth } = getBaseSystemInfo(); + return { + css: true, + route: meta.route, + version: 1, + locale: '', + platform, + pixelRatio, + windowWidth, + disableScroll: meta.disableScroll === true, + onPageScroll: false, + onPageReachBottom: false, + onReachBottomDistance: hasOwn$1(meta, 'onReachBottomDistance') + ? meta.onReachBottomDistance + : ON_REACH_BOTTOM_DISTANCE, + statusbarHeight, + windowTop: meta.navigationBar.type === 'float' ? statusbarHeight + NAVBAR_HEIGHT : 0, + windowBottom: tabBar$1.indexOf(meta.route) >= 0 && tabBar$1.cover ? tabBar$1.height : 0, + }; + } + + const navigateTo = defineAsyncApi(API_NAVIGATE_TO, (args, { resolve, reject }) => { + const { url, animationType, animationDuration } = args; + const { path, query } = parseUrl(url); + const [aniType, aniDuration] = initAnimation(path, animationType, animationDuration); + navigate(path, () => { + _navigateTo({ + url, + path, + query, + aniType, + aniDuration, + }) + .then(resolve) + .catch(reject); + }, args.openType === 'appLaunch'); + }, NavigateToProtocol, NavigateToOptions); + function _navigateTo({ url, path, query, aniType, aniDuration, }) { + // TODO eventChannel + return new Promise((resolve) => { + showWebview(registerPage({ url, path, query, openType: 'navigateTo' }), aniType, aniDuration, () => { + resolve(undefined); + }); + }); + } + function initAnimation(path, animationType, animationDuration) { + const { globalStyle } = __uniConfig; + const meta = getRouteMeta(path); + return [ + animationType || + meta.animationType || + globalStyle.animationType || + ANI_SHOW, + animationDuration || + meta.animationDuration || + globalStyle.animationDuration || + ANI_DURATION, + ]; + } + + let lastStatusBarStyle; + let oldSetStatusBarStyle = plus.navigator.setStatusBarStyle; + function newSetStatusBarStyle(style) { + lastStatusBarStyle = style; + oldSetStatusBarStyle(style); + } + plus.navigator.setStatusBarStyle = newSetStatusBarStyle; + function setStatusBarStyle(statusBarStyle) { + if (!statusBarStyle) { + const pages = getCurrentPages(); + if (!pages.length) { + return; + } + statusBarStyle = pages[pages.length - 1].$page + .statusBarStyle; + if (!statusBarStyle || statusBarStyle === lastStatusBarStyle) { + return; + } + } + if (statusBarStyle === lastStatusBarStyle) { + return; + } + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('setStatusBarStyle', statusBarStyle)); + } + lastStatusBarStyle = statusBarStyle; + plus.navigator.setStatusBarStyle(statusBarStyle); + } + + const navigateBack = defineAsyncApi(API_NAVIGATE_BACK, (args, { resolve, reject }) => { + const page = getCurrentPage(); + if (!page) { + return reject(`getCurrentPages is empty`); + } + if (invokeHook(page, 'onBackPress', { + from: args.from, + })) { + return resolve(); + } + uni.hideToast(); + uni.hideLoading(); + if (page.$page.meta.isQuit) { + quit(); + } + else if (page.$page.id === 1 && __uniConfig.realEntryPagePath) { + // condition + __uniConfig.entryPagePath = __uniConfig.realEntryPagePath; + delete __uniConfig.realEntryPagePath; + uni.reLaunch({ + url: '/' + __uniConfig.entryPagePath, + }); + } + else { + const { delta, animationType, animationDuration } = args; + back(delta, animationType, animationDuration); + } + return resolve(); + }, NavigateBackProtocol, NavigateBackOptions); + let firstBackTime = 0; + function quit() { + initI18nAppMsgsOnce(); + if (!firstBackTime) { + firstBackTime = Date.now(); + plus.nativeUI.toast(useI18n().t('uni.app.quit')); + setTimeout(() => { + firstBackTime = 0; + }, 2000); + } + else if (Date.now() - firstBackTime < 2000) { + plus.runtime.quit(); + } + } + function back(delta, animationType, animationDuration) { + const pages = getCurrentPages(); + const len = pages.length; + const currentPage = pages[len - 1]; + if (delta > 1) { + // 中间页隐藏 + pages + .slice(len - delta, len - 1) + .reverse() + .forEach((deltaPage) => { + closeWebview(plus.webview.getWebviewById(deltaPage.$page.id + ''), 'none', 0); + }); + } + const backPage = function (webview) { + if (animationType) { + closeWebview(webview, animationType, animationDuration || ANI_DURATION); + } + else { + if (currentPage.$page.openType === 'redirectTo') { + // 如果是 redirectTo 跳转的,需要制定 back 动画 + closeWebview(webview, ANI_CLOSE, ANI_DURATION); + } + else { + closeWebview(webview, 'auto'); + } + } + pages + .slice(len - delta, len) + .forEach((page) => removePage(page)); + setStatusBarStyle(); + }; + const webview = plus.webview.getWebviewById(currentPage.$page.id + ''); + if (!currentPage.__uniapp_webview) { + return backPage(webview); + } + backWebview(webview, () => { + backPage(webview); + }); + } + + var uni$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + upx2px: upx2px, + addInterceptor: addInterceptor, + removeInterceptor: removeInterceptor, + promiseInterceptor: promiseInterceptor, + arrayBufferToBase64: arrayBufferToBase64, + base64ToArrayBuffer: base64ToArrayBuffer, + createIntersectionObserver: createIntersectionObserver, + createMediaQueryObserver: createMediaQueryObserver, + createSelectorQuery: createSelectorQuery, + createVideoContext: createVideoContext, + createMapContext: createMapContext, + createAnimation: createAnimation, + onTabBarMidButtonTap: onTabBarMidButtonTap, + createCanvasContext: createCanvasContext, + canvasGetImageData: canvasGetImageData, + canvasPutImageData: canvasPutImageData, + canvasToTempFilePath: canvasToTempFilePath, + getSelectedTextRange: getSelectedTextRange, + $on: $on, + $off: $off, + $once: $once, + $emit: $emit, + setStorageSync: setStorageSync, + setStorage: setStorage, + getStorageSync: getStorageSync, + getStorage: getStorage, + removeStorageSync: removeStorageSync, + removeStorage: removeStorage, + clearStorageSync: clearStorageSync, + clearStorage: clearStorage, + getStorageInfoSync: getStorageInfoSync, + getStorageInfo: getStorageInfo, + getFileInfo: getFileInfo$1, + openDocument: openDocument, + onCompassChange: onCompassChange, + offCompassChange: offCompassChange, + startCompass: startCompass, + stopCompass: stopCompass, + vibrateShort: vibrateShort, + vibrateLong: vibrateLong, + onAccelerometerChange: onAccelerometerChange, + offAccelerometerChange: offAccelerometerChange, + startAccelerometer: startAccelerometer, + stopAccelerometer: stopAccelerometer, + onBluetoothDeviceFound: onBluetoothDeviceFound, + onBluetoothAdapterStateChange: onBluetoothAdapterStateChange, + onBLEConnectionStateChange: onBLEConnectionStateChange, + onBLECharacteristicValueChange: onBLECharacteristicValueChange, + openBluetoothAdapter: openBluetoothAdapter, + closeBluetoothAdapter: closeBluetoothAdapter, + getBluetoothAdapterState: getBluetoothAdapterState, + startBluetoothDevicesDiscovery: startBluetoothDevicesDiscovery, + stopBluetoothDevicesDiscovery: stopBluetoothDevicesDiscovery, + getBluetoothDevices: getBluetoothDevices, + getConnectedBluetoothDevices: getConnectedBluetoothDevices, + createBLEConnection: createBLEConnection, + closeBLEConnection: closeBLEConnection, + getBLEDeviceServices: getBLEDeviceServices, + getBLEDeviceCharacteristics: getBLEDeviceCharacteristics, + notifyBLECharacteristicValueChange: notifyBLECharacteristicValueChange, + readBLECharacteristicValue: readBLECharacteristicValue, + writeBLECharacteristicValue: writeBLECharacteristicValue, + setBLEMTU: setBLEMTU, + getBLEDeviceRSSI: getBLEDeviceRSSI, + onBeaconUpdate: onBeaconUpdate, + onBeaconServiceChange: onBeaconServiceChange, + getBeacons: getBeacons, + startBeaconDiscovery: startBeaconDiscovery, + stopBeaconDiscovery: stopBeaconDiscovery, + makePhoneCall: makePhoneCall, + addPhoneContact: addPhoneContact, + getClipboardData: getClipboardData, + setClipboardData: setClipboardData, + onNetworkStatusChange: onNetworkStatusChange, + offNetworkStatusChange: offNetworkStatusChange, + getNetworkType: getNetworkType, + checkIsSupportSoterAuthentication: checkIsSupportSoterAuthentication, + checkIsSoterEnrolledInDevice: checkIsSoterEnrolledInDevice, + startSoterAuthentication: startSoterAuthentication, + getImageInfo: getImageInfo, + getVideoInfo: getVideoInfo, + previewImage: previewImage, + getRecorderManager: getRecorderManager, + saveVideoToPhotosAlbum: saveVideoToPhotosAlbum, + saveImageToPhotosAlbum: saveImageToPhotosAlbum, + compressImage: compressImage$1, + compressVideo: compressVideo, + chooseImage: chooseImage, + chooseVideo: chooseVideo, + showKeyboard: showKeyboard, + hideKeyboard: hideKeyboard, + downloadFile: downloadFile, + request: request, + connectSocket: connectSocket, + sendSocketMessage: sendSocketMessage, + closeSocket: closeSocket, + onSocketOpen: onSocketOpen, + onSocketError: onSocketError, + onSocketMessage: onSocketMessage, + onSocketClose: onSocketClose, + uploadFile: uploadFile, + createInnerAudioContext: createInnerAudioContext, + getBackgroundAudioManager: getBackgroundAudioManager, + getLocation: getLocation, + showModal: showModal, + showActionSheet: showActionSheet, + showLoading: showLoading, + showToast: showToast, + hideToast: hideToast, + hideLoading: hideLoading, + getProvider: getProvider, + login: login, + getUserInfo: getUserInfo, + getUserProfile: getUserProfile, + preLogin: preLogin, + closeAuthView: closeAuthView, + registerRuntime: registerRuntime, + share: share, + shareWithSystem: shareWithSystem, + requestPayment: requestPayment, + __vuePlugin: __vuePlugin, + createRewardedVideoAd: createRewardedVideoAd, + createFullScreenVideoAd: createFullScreenVideoAd, + createInterstitialAd: createInterstitialAd, + createInteractiveAd: createInteractiveAd, + navigateTo: navigateTo, + navigateBack: navigateBack + }); + + let invokeViewMethodId = 0; + const invokeViewMethod = (name, args, callback, pageId) => { + const id = invokeViewMethodId++; + UniServiceJSBridge$1.subscribe(INVOKE_VIEW_API + '.' + id, callback, true); + publishHandler(INVOKE_VIEW_API, { id, name, args }, pageId); + }; + const UniServiceJSBridge$1 = /*#__PURE__*/ extend(ServiceJSBridge, { + publishHandler, + invokeViewMethod, + }); + function publishHandler(event, args, pageIds) { + args = JSON.stringify(args); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('publishHandler', event, args, pageIds)); + } + if (!isArray(pageIds)) { + pageIds = [pageIds]; + } + const evalJSCode = `typeof UniViewJSBridge !== 'undefined' && UniViewJSBridge.subscribeHandler("${event}",${args},__PAGE_ID__)`; + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('publishHandler', 'size', evalJSCode.length)); + } + pageIds.forEach((id) => { + const idStr = String(id); + const webview = plus.webview.getWebviewById(idStr); + webview && webview.evalJS(evalJSCode.replace('__PAGE_ID__', idStr)); + }); + } + + var index = { + uni: uni$1, + getApp: getApp$1, + getCurrentPages: getCurrentPages$1, + __definePage: definePage, + __registerApp: registerApp, + __registerPage: registerPage, + UniServiceJSBridge: UniServiceJSBridge$1, + }; + + /*! pako 2.0.3 https://github.com/nodeca/pako @license (MIT AND Zlib) */ + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + /* eslint-disable space-unary-ops */ + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + + //const Z_FILTERED = 1; + //const Z_HUFFMAN_ONLY = 2; + //const Z_RLE = 3; + const Z_FIXED = 4; + //const Z_DEFAULT_STRATEGY = 0; + + /* Possible values of the data_type field (though see inflate()) */ + const Z_BINARY = 0; + const Z_TEXT = 1; + //const Z_ASCII = 1; // = Z_TEXT + const Z_UNKNOWN = 2; + + /*============================================================================*/ + + + function zero(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } + + // From zutil.h + + const STORED_BLOCK = 0; + const STATIC_TREES = 1; + const DYN_TREES = 2; + /* The three kinds of block type */ + + const MIN_MATCH = 3; + const MAX_MATCH = 258; + /* The minimum and maximum match lengths */ + + // From deflate.h + /* =========================================================================== + * Internal compression state. + */ + + const LENGTH_CODES = 29; + /* number of length codes, not counting the special END_BLOCK code */ + + const LITERALS = 256; + /* number of literal bytes 0..255 */ + + const L_CODES = LITERALS + 1 + LENGTH_CODES; + /* number of Literal or Length codes, including the END_BLOCK code */ + + const D_CODES = 30; + /* number of distance codes */ + + const BL_CODES = 19; + /* number of codes used to transfer the bit lengths */ + + const HEAP_SIZE = 2 * L_CODES + 1; + /* maximum heap size */ + + const MAX_BITS = 15; + /* All codes must not exceed MAX_BITS bits */ + + const Buf_size = 16; + /* size of bit buffer in bi_buf */ + + + /* =========================================================================== + * Constants + */ + + const MAX_BL_BITS = 7; + /* Bit length codes must not exceed MAX_BL_BITS bits */ + + const END_BLOCK = 256; + /* end of block literal code */ + + const REP_3_6 = 16; + /* repeat previous bit length 3-6 times (2 bits of repeat count) */ + + const REPZ_3_10 = 17; + /* repeat a zero length 3-10 times (3 bits of repeat count) */ + + const REPZ_11_138 = 18; + /* repeat a zero length 11-138 times (7 bits of repeat count) */ + + /* eslint-disable comma-spacing,array-bracket-spacing */ + const extra_lbits = /* extra bits for each length code */ + new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]); + + const extra_dbits = /* extra bits for each distance code */ + new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]); + + const extra_blbits = /* extra bits for each bit length code */ + new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]); + + const bl_order = + new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]); + /* eslint-enable comma-spacing,array-bracket-spacing */ + + /* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + + /* =========================================================================== + * Local data. These are initialized only once. + */ + + // We pre-fill arrays with 0 to avoid uninitialized gaps + + const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ + + // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 + const static_ltree = new Array((L_CODES + 2) * 2); + zero(static_ltree); + /* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + + const static_dtree = new Array(D_CODES * 2); + zero(static_dtree); + /* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + + const _dist_code = new Array(DIST_CODE_LEN); + zero(_dist_code); + /* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + + const _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); + zero(_length_code); + /* length code for each normalized match length (0 == MIN_MATCH) */ + + const base_length = new Array(LENGTH_CODES); + zero(base_length); + /* First normalized length for each code (0 = MIN_MATCH) */ + + const base_dist = new Array(D_CODES); + zero(base_dist); + /* First normalized distance for each code (0 = distance of 1) */ + + + function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) { + + this.static_tree = static_tree; /* static tree or NULL */ + this.extra_bits = extra_bits; /* extra bits for each code or NULL */ + this.extra_base = extra_base; /* base index for extra_bits */ + this.elems = elems; /* max number of elements in the tree */ + this.max_length = max_length; /* max bit length for the codes */ + + // show if `static_tree` has data or dummy - needed for monomorphic objects + this.has_stree = static_tree && static_tree.length; + } + + + let static_l_desc; + let static_d_desc; + let static_bl_desc; + + + function TreeDesc(dyn_tree, stat_desc) { + this.dyn_tree = dyn_tree; /* the dynamic tree */ + this.max_code = 0; /* largest code with non zero frequency */ + this.stat_desc = stat_desc; /* the corresponding static tree */ + } + + + + const d_code = (dist) => { + + return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; + }; + + + /* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ + const put_short = (s, w) => { + // put_byte(s, (uch)((w) & 0xff)); + // put_byte(s, (uch)((ush)(w) >> 8)); + s.pending_buf[s.pending++] = (w) & 0xff; + s.pending_buf[s.pending++] = (w >>> 8) & 0xff; + }; + + + /* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ + const send_bits = (s, value, length) => { + + if (s.bi_valid > (Buf_size - length)) { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + put_short(s, s.bi_buf); + s.bi_buf = value >> (Buf_size - s.bi_valid); + s.bi_valid += length - Buf_size; + } else { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + s.bi_valid += length; + } + }; + + + const send_code = (s, c, tree) => { + + send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/); + }; + + + /* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ + const bi_reverse = (code, len) => { + + let res = 0; + do { + res |= code & 1; + code >>>= 1; + res <<= 1; + } while (--len > 0); + return res >>> 1; + }; + + + /* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ + const bi_flush = (s) => { + + if (s.bi_valid === 16) { + put_short(s, s.bi_buf); + s.bi_buf = 0; + s.bi_valid = 0; + + } else if (s.bi_valid >= 8) { + s.pending_buf[s.pending++] = s.bi_buf & 0xff; + s.bi_buf >>= 8; + s.bi_valid -= 8; + } + }; + + + /* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ + const gen_bitlen = (s, desc) => + // deflate_state *s; + // tree_desc *desc; /* the tree descriptor */ + { + const tree = desc.dyn_tree; + const max_code = desc.max_code; + const stree = desc.stat_desc.static_tree; + const has_stree = desc.stat_desc.has_stree; + const extra = desc.stat_desc.extra_bits; + const base = desc.stat_desc.extra_base; + const max_length = desc.stat_desc.max_length; + let h; /* heap index */ + let n, m; /* iterate over the tree elements */ + let bits; /* bit length */ + let xbits; /* extra bits */ + let f; /* frequency */ + let overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) { + s.bl_count[bits] = 0; + } + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n * 2 + 1]/*.Len*/ = bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) { continue; } /* not a leaf node */ + + s.bl_count[bits]++; + xbits = 0; + if (n >= base) { + xbits = extra[n - base]; + } + f = tree[n * 2]/*.Freq*/; + s.opt_len += f * (bits + xbits); + if (has_stree) { + s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits); + } + } + if (overflow === 0) { return; } + + // Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (s.bl_count[bits] === 0) { bits--; } + s.bl_count[bits]--; /* move one leaf down the tree */ + s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + s.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits !== 0; bits--) { + n = s.bl_count[bits]; + while (n !== 0) { + m = s.heap[--h]; + if (m > max_code) { continue; } + if (tree[m * 2 + 1]/*.Len*/ !== bits) { + // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/; + tree[m * 2 + 1]/*.Len*/ = bits; + } + n--; + } + } + }; + + + /* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ + const gen_codes = (tree, max_code, bl_count) => + // ct_data *tree; /* the tree to decorate */ + // int max_code; /* largest code with non zero frequency */ + // ushf *bl_count; /* number of codes at each bit length */ + { + const next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ + let code = 0; /* running code value */ + let bits; /* bit index */ + let n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + //Assert (code + bl_count[MAX_BITS]-1 == (1< { + + let n; /* iterates over tree elements */ + let bits; /* bit counter */ + let length; /* length value */ + let code; /* code value */ + let dist; /* distance index */ + const bl_count = new Array(MAX_BITS + 1); + /* number of codes at each bit length for an optimal tree */ + + // do check in _tr_init() + //if (static_init_done) return; + + /* For some embedded targets, window variables are not initialized: */ + /*#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + #endif*/ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES - 1; code++) { + base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + _length_code[length++] = code; + } + } + //Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + _dist_code[dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + _dist_code[256 + dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) { + bl_count[bits] = 0; + } + + n = 0; + while (n <= 143) { + static_ltree[n * 2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]++; + } + while (n <= 255) { + static_ltree[n * 2 + 1]/*.Len*/ = 9; + n++; + bl_count[9]++; + } + while (n <= 279) { + static_ltree[n * 2 + 1]/*.Len*/ = 7; + n++; + bl_count[7]++; + } + while (n <= 287) { + static_ltree[n * 2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(static_ltree, L_CODES + 1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n * 2 + 1]/*.Len*/ = 5; + static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); + } + + // Now data ready and we can init static trees + static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); + static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + //static_init_done = true; + }; + + + /* =========================================================================== + * Initialize a new block. + */ + const init_block = (s) => { + + let n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } + + s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; + s.opt_len = s.static_len = 0; + s.last_lit = s.matches = 0; + }; + + + /* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ + const bi_windup = (s) => + { + if (s.bi_valid > 8) { + put_short(s, s.bi_buf); + } else if (s.bi_valid > 0) { + //put_byte(s, (Byte)s->bi_buf); + s.pending_buf[s.pending++] = s.bi_buf; + } + s.bi_buf = 0; + s.bi_valid = 0; + }; + + /* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ + const copy_block = (s, buf, len, header) => + //DeflateState *s; + //charf *buf; /* the input data */ + //unsigned len; /* its length */ + //int header; /* true if block header must be written */ + { + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, len); + put_short(s, ~len); + } + // while (len--) { + // put_byte(s, *buf++); + // } + s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending); + s.pending += len; + }; + + /* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ + const smaller = (tree, n, m, depth) => { + + const _n2 = n * 2; + const _m2 = m * 2; + return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || + (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); + }; + + /* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + const pqdownheap = (s, tree, k) => + // deflate_state *s; + // ct_data *tree; /* the tree to restore */ + // int k; /* node to move down */ + { + const v = s.heap[k]; + let j = k << 1; /* left son of k */ + while (j <= s.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s.heap_len && + smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s.heap[j], s.depth)) { break; } + + /* Exchange v with the smallest son */ + s.heap[k] = s.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s.heap[k] = v; + }; + + + // inlined manually + // const SMALLEST = 1; + + /* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ + const compress_block = (s, ltree, dtree) => + // deflate_state *s; + // const ct_data *ltree; /* literal tree */ + // const ct_data *dtree; /* distance tree */ + { + let dist; /* distance of matched string */ + let lc; /* match length or unmatched char (if dist == 0) */ + let lx = 0; /* running index in l_buf */ + let code; /* the code to send */ + let extra; /* number of extra bits to send */ + + if (s.last_lit !== 0) { + do { + dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]); + lc = s.pending_buf[s.l_buf + lx]; + lx++; + + if (dist === 0) { + send_code(s, lc, ltree); /* send a literal byte */ + //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra !== 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + //Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra !== 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + // "pendingBuf overflow"); + + } while (lx < s.last_lit); + } + + send_code(s, END_BLOCK, ltree); + }; + + + /* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + const build_tree = (s, desc) => + // deflate_state *s; + // tree_desc *desc; /* the tree descriptor */ + { + const tree = desc.dyn_tree; + const stree = desc.stat_desc.static_tree; + const has_stree = desc.stat_desc.has_stree; + const elems = desc.stat_desc.elems; + let n, m; /* iterate over heap elements */ + let max_code = -1; /* largest code with non zero frequency */ + let node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n * 2]/*.Freq*/ !== 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + + } else { + tree[n * 2 + 1]/*.Len*/ = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2]/*.Freq*/ = 1; + s.depth[node] = 0; + s.opt_len--; + + if (has_stree) { + s.static_len -= stree[node * 2 + 1]/*.Len*/; + } + /* node is 0 or 1 so it does not have extra bits */ + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + //pqremove(s, tree, n); /* n = node of least frequency */ + /*** pqremove ***/ + n = s.heap[1/*SMALLEST*/]; + s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; + pqdownheap(s, tree, 1/*SMALLEST*/); + /***/ + + m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ + + s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ + s.heap[--s.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; + s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; + tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node; + + /* and insert the new node in the heap */ + s.heap[1/*SMALLEST*/] = node++; + pqdownheap(s, tree, 1/*SMALLEST*/); + + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(tree, max_code, s.bl_count); + }; + + + /* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ + const scan_tree = (s, tree, max_code) => + // deflate_state *s; + // ct_data *tree; /* the tree to be scanned */ + // int max_code; /* and its largest code of non zero frequency */ + { + let n; /* iterates over all tree elements */ + let prevlen = -1; /* last emitted length */ + let curlen; /* length of current code */ + + let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ + + let count = 0; /* repeat count of the current code */ + let max_count = 7; /* max repeat count */ + let min_count = 4; /* min repeat count */ + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + + } else if (count < min_count) { + s.bl_tree[curlen * 2]/*.Freq*/ += count; + + } else if (curlen !== 0) { + + if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; } + s.bl_tree[REP_3_6 * 2]/*.Freq*/++; + + } else if (count <= 10) { + s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++; + + } else { + s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++; + } + + count = 0; + prevlen = curlen; + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } + }; + + + /* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ + const send_tree = (s, tree, max_code) => + // deflate_state *s; + // ct_data *tree; /* the tree to be scanned */ + // int max_code; /* and its largest code of non zero frequency */ + { + let n; /* iterates over all tree elements */ + let prevlen = -1; /* last emitted length */ + let curlen; /* length of current code */ + + let nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */ + + let count = 0; /* repeat count of the current code */ + let max_count = 7; /* max repeat count */ + let min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]/*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + + } else if (count < min_count) { + do { send_code(s, curlen, s.bl_tree); } while (--count !== 0); + + } else if (curlen !== 0) { + if (curlen !== prevlen) { + send_code(s, curlen, s.bl_tree); + count--; + } + //Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s.bl_tree); + send_bits(s, count - 3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s.bl_tree); + send_bits(s, count - 3, 3); + + } else { + send_code(s, REPZ_11_138, s.bl_tree); + send_bits(s, count - 11, 7); + } + + count = 0; + prevlen = curlen; + if (nextlen === 0) { + max_count = 138; + min_count = 3; + + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } + }; + + + /* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ + const build_bl_tree = (s) => { + + let max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, s.dyn_ltree, s.l_desc.max_code); + scan_tree(s, s.dyn_dtree, s.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, s.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { + break; + } + } + /* Update opt_len to include the bit length tree and counts */ + s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + // s->opt_len, s->static_len)); + + return max_blindex; + }; + + + /* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ + const send_all_trees = (s, lcodes, dcodes, blcodes) => + // deflate_state *s; + // int lcodes, dcodes, blcodes; /* number of codes for each tree */ + { + let rank; /* index in bl_order */ + + //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + // "too many codes"); + //Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes - 1, 5); + send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3); + } + //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ + //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ + //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); + }; + + + /* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ + const detect_data_type = (s) => { + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + let black_mask = 0xf3ffc07f; + let n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>>= 1) { + if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) { + return Z_BINARY; + } + } + + /* Check for textual ("white-listed") bytes. */ + if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 || + s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { + return Z_TEXT; + } + for (n = 32; n < LITERALS; n++) { + if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { + return Z_TEXT; + } + } + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; + }; + + + let static_init_done = false; + + /* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ + const _tr_init = (s) => + { + + if (!static_init_done) { + tr_static_init(); + static_init_done = true; + } + + s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); + s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); + s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); + + s.bi_buf = 0; + s.bi_valid = 0; + + /* Initialize the first block of the first file: */ + init_block(s); + }; + + + /* =========================================================================== + * Send a stored block + */ + const _tr_stored_block = (s, buf, stored_len, last) => + //DeflateState *s; + //charf *buf; /* input block */ + //ulg stored_len; /* length of input block */ + //int last; /* one if this is the last block for a file */ + { + send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ + copy_block(s, buf, stored_len, true); /* with header */ + }; + + + /* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ + const _tr_align = (s) => { + send_bits(s, STATIC_TREES << 1, 3); + send_code(s, END_BLOCK, static_ltree); + bi_flush(s); + }; + + + /* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ + const _tr_flush_block = (s, buf, stored_len, last) => + //DeflateState *s; + //charf *buf; /* input block, or NULL if too old */ + //ulg stored_len; /* length of input block */ + //int last; /* one if this is the last block for a file */ + { + let opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + let max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s.level > 0) { + + /* Check if the file is binary or text */ + if (s.strm.data_type === Z_UNKNOWN) { + s.strm.data_type = detect_data_type(s); + } + + /* Construct the literal and distance trees */ + build_tree(s, s.l_desc); + // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + + build_tree(s, s.d_desc); + // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s.opt_len + 3 + 7) >>> 3; + static_lenb = (s.static_len + 3 + 7) >>> 3; + + // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + // s->last_lit)); + + if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } + + } else { + // Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { + /* 4: two words for the lengths */ + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + + } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { + + send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); + compress_block(s, static_ltree, static_dtree); + + } else { + send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); + send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1); + compress_block(s, s.dyn_ltree, s.dyn_dtree); + } + // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); + } + // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + // s->compressed_len-7*last)); + }; + + /* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ + const _tr_tally = (s, dist, lc) => + // deflate_state *s; + // unsigned dist; /* distance of matched string */ + // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ + { + //let out_length, in_length, dcode; + + s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; + s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; + + s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; + s.last_lit++; + + if (dist === 0) { + /* lc is the unmatched char */ + s.dyn_ltree[lc * 2]/*.Freq*/++; + } else { + s.matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + //Assert((ush)dist < (ush)MAX_DIST(s) && + // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; + s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; + } + + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + + //#ifdef TRUNCATE_BLOCK + // /* Try to guess if it is profitable to stop the current block here */ + // if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { + // /* Compute an upper bound for the compressed length */ + // out_length = s.last_lit*8; + // in_length = s.strstart - s.block_start; + // + // for (dcode = 0; dcode < D_CODES; dcode++) { + // out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); + // } + // out_length >>>= 3; + // //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + // // s->last_lit, in_length, out_length, + // // 100L - out_length*100L/in_length)); + // if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { + // return true; + // } + // } + //#endif + + return (s.last_lit === s.lit_bufsize - 1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ + }; + + var _tr_init_1 = _tr_init; + var _tr_stored_block_1 = _tr_stored_block; + var _tr_flush_block_1 = _tr_flush_block; + var _tr_tally_1 = _tr_tally; + var _tr_align_1 = _tr_align; + + var trees = { + _tr_init: _tr_init_1, + _tr_stored_block: _tr_stored_block_1, + _tr_flush_block: _tr_flush_block_1, + _tr_tally: _tr_tally_1, + _tr_align: _tr_align_1 + }; + + // Note: adler32 takes 12% for level 0 and 2% for level 6. + // It isn't worth it to make additional optimizations as in original. + // Small size is preferable. + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + const adler32 = (adler, buf, len, pos) => { + let s1 = (adler & 0xffff) |0, + s2 = ((adler >>> 16) & 0xffff) |0, + n = 0; + + while (len !== 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) |0; + s2 = (s2 + s1) |0; + } while (--n); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) |0; + }; + + + var adler32_1 = adler32; + + // Note: we can't get significant speed boost here. + // So write code to minimize size - no pregenerated tables + // and array tools dependencies. + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + // Use ordinary array, since untyped makes no boost here + const makeTable = () => { + let c, table = []; + + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; + }; + + // Create table on load. Just 255 signed longs. Not a problem. + const crcTable = new Uint32Array(makeTable()); + + + const crc32 = (crc, buf, len, pos) => { + const t = crcTable; + const end = pos + len; + + crc ^= -1; + + for (let i = pos; i < end; i++) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; + }; + + + var crc32_1 = crc32; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + var messages = { + 2: 'need dictionary', /* Z_NEED_DICT 2 */ + 1: 'stream end', /* Z_STREAM_END 1 */ + 0: '', /* Z_OK 0 */ + '-1': 'file error', /* Z_ERRNO (-1) */ + '-2': 'stream error', /* Z_STREAM_ERROR (-2) */ + '-3': 'data error', /* Z_DATA_ERROR (-3) */ + '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */ + '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ + '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + var constants = { + + /* Allowed flush values; see deflate() and inflate() below for details */ + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_TREES: 6, + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + //Z_VERSION_ERROR: -6, + + /* compression levels */ + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + + + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + + /* Possible values of the data_type field (though see inflate()) */ + Z_BINARY: 0, + Z_TEXT: 1, + //Z_ASCII: 1, // = Z_TEXT (deprecated) + Z_UNKNOWN: 2, + + /* The deflate compression method */ + Z_DEFLATED: 8 + //Z_NULL: null // Use -1 or null inline, depending on var type + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + const { _tr_init: _tr_init$1, _tr_stored_block: _tr_stored_block$1, _tr_flush_block: _tr_flush_block$1, _tr_tally: _tr_tally$1, _tr_align: _tr_align$1 } = trees; + + + + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_NO_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_BLOCK, + Z_OK, Z_STREAM_END, Z_STREAM_ERROR, Z_DATA_ERROR, Z_BUF_ERROR, + Z_DEFAULT_COMPRESSION, + Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED: Z_FIXED$1, Z_DEFAULT_STRATEGY, + Z_UNKNOWN: Z_UNKNOWN$1, + Z_DEFLATED + } = constants; + + /*============================================================================*/ + + + const MAX_MEM_LEVEL = 9; + /* Maximum value for memLevel in deflateInit2 */ + const MAX_WBITS = 15; + /* 32K LZ77 window */ + const DEF_MEM_LEVEL = 8; + + + const LENGTH_CODES$1 = 29; + /* number of length codes, not counting the special END_BLOCK code */ + const LITERALS$1 = 256; + /* number of literal bytes 0..255 */ + const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; + /* number of Literal or Length codes, including the END_BLOCK code */ + const D_CODES$1 = 30; + /* number of distance codes */ + const BL_CODES$1 = 19; + /* number of codes used to transfer the bit lengths */ + const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; + /* maximum heap size */ + const MAX_BITS$1 = 15; + /* All codes must not exceed MAX_BITS bits */ + + const MIN_MATCH$1 = 3; + const MAX_MATCH$1 = 258; + const MIN_LOOKAHEAD = (MAX_MATCH$1 + MIN_MATCH$1 + 1); + + const PRESET_DICT = 0x20; + + const INIT_STATE = 42; + const EXTRA_STATE = 69; + const NAME_STATE = 73; + const COMMENT_STATE = 91; + const HCRC_STATE = 103; + const BUSY_STATE = 113; + const FINISH_STATE = 666; + + const BS_NEED_MORE = 1; /* block not completed, need more input or more output */ + const BS_BLOCK_DONE = 2; /* block flush performed */ + const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ + const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ + + const OS_CODE = 0x03; // Unix :) . Don't detect, use this default. + + const err = (strm, errorCode) => { + strm.msg = messages[errorCode]; + return errorCode; + }; + + const rank = (f) => { + return ((f) << 1) - ((f) > 4 ? 9 : 0); + }; + + const zero$1 = (buf) => { + let len = buf.length; while (--len >= 0) { buf[len] = 0; } + }; + + + /* eslint-disable new-cap */ + let HASH_ZLIB = (s, prev, data) => ((prev << s.hash_shift) ^ data) & s.hash_mask; + // This hash causes less collisions, https://github.com/nodeca/pako/issues/135 + // But breaks binary compatibility + //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask; + let HASH = HASH_ZLIB; + + /* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->output buffer and copying into it. + * (See also read_buf()). + */ + const flush_pending = (strm) => { + const s = strm.state; + + //_tr_flush_bits(s); + let len = s.pending; + if (len > strm.avail_out) { + len = strm.avail_out; + } + if (len === 0) { return; } + + strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out); + strm.next_out += len; + s.pending_out += len; + strm.total_out += len; + strm.avail_out -= len; + s.pending -= len; + if (s.pending === 0) { + s.pending_out = 0; + } + }; + + + const flush_block_only = (s, last) => { + _tr_flush_block$1(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); + s.block_start = s.strstart; + flush_pending(s.strm); + }; + + + const put_byte = (s, b) => { + s.pending_buf[s.pending++] = b; + }; + + + /* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ + const putShortMSB = (s, b) => { + + // put_byte(s, (Byte)(b >> 8)); + // put_byte(s, (Byte)(b & 0xff)); + s.pending_buf[s.pending++] = (b >>> 8) & 0xff; + s.pending_buf[s.pending++] = b & 0xff; + }; + + + /* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->input buffer and copying from it. + * (See also flush_pending()). + */ + const read_buf = (strm, buf, start, size) => { + + let len = strm.avail_in; + + if (len > size) { len = size; } + if (len === 0) { return 0; } + + strm.avail_in -= len; + + // zmemcpy(buf, strm->next_in, len); + buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start); + if (strm.state.wrap === 1) { + strm.adler = adler32_1(strm.adler, buf, len, start); + } + + else if (strm.state.wrap === 2) { + strm.adler = crc32_1(strm.adler, buf, len, start); + } + + strm.next_in += len; + strm.total_in += len; + + return len; + }; + + + /* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ + const longest_match = (s, cur_match) => { + + let chain_length = s.max_chain_length; /* max hash chain length */ + let scan = s.strstart; /* current string */ + let match; /* matched string */ + let len; /* length of current match */ + let best_len = s.prev_length; /* best match length so far */ + let nice_match = s.nice_match; /* stop if match long enough */ + const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? + s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; + + const _win = s.window; // shortcut + + const wmask = s.w_mask; + const prev = s.prev; + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + const strend = s.strstart + MAX_MATCH$1; + let scan_end1 = _win[scan + best_len - 1]; + let scan_end = _win[scan + best_len]; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s.prev_length >= s.good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (nice_match > s.lookahead) { nice_match = s.lookahead; } + + // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + // Assert(cur_match < s->strstart, "no future"); + match = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ + + if (_win[match + best_len] !== scan_end || + _win[match + best_len - 1] !== scan_end1 || + _win[match] !== _win[scan] || + _win[++match] !== _win[scan + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2; + match++; + // Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + /*jshint noempty:false*/ + } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + scan < strend); + + // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH$1 - (strend - scan); + scan = strend - MAX_MATCH$1; + + if (len > best_len) { + s.match_start = cur_match; + best_len = len; + if (len >= nice_match) { + break; + } + scan_end1 = _win[scan + best_len - 1]; + scan_end = _win[scan + best_len]; + } + } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0); + + if (best_len <= s.lookahead) { + return best_len; + } + return s.lookahead; + }; + + + /* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ + const fill_window = (s) => { + + const _w_size = s.w_size; + let p, n, m, more, str; + + //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = s.window_size - s.lookahead - s.strstart; + + // JS ints have 32 bit, block below not needed + /* Deal with !@#$% 64K limit: */ + //if (sizeof(int) <= 2) { + // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + // more = wsize; + // + // } else if (more == (unsigned)(-1)) { + // /* Very unlikely, but possible on 16 bit machine if + // * strstart == 0 && lookahead == 1 (input done a byte at time) + // */ + // more--; + // } + //} + + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { + + s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0); + s.match_start -= _w_size; + s.strstart -= _w_size; + /* we now have strstart >= MAX_DIST */ + s.block_start -= _w_size; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + + n = s.hash_size; + p = n; + + do { + m = s.head[--p]; + s.head[p] = (m >= _w_size ? m - _w_size : 0); + } while (--n); + + n = _w_size; + p = n; + + do { + m = s.prev[--p]; + s.prev[p] = (m >= _w_size ? m - _w_size : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + + more += _w_size; + } + if (s.strm.avail_in === 0) { + break; + } + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + //Assert(more >= 2, "more < 2"); + n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); + s.lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s.lookahead + s.insert >= MIN_MATCH$1) { + str = s.strstart - s.insert; + s.ins_h = s.window[str]; + + /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); + //#if MIN_MATCH != 3 + // Call update_hash() MIN_MATCH-3 more times + //#endif + while (s.insert) { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH$1 - 1]); + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = str; + str++; + s.insert--; + if (s.lookahead + s.insert < MIN_MATCH$1) { + break; + } + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + // if (s.high_water < s.window_size) { + // const curr = s.strstart + s.lookahead; + // let init = 0; + // + // if (s.high_water < curr) { + // /* Previous high water mark below current data -- zero WIN_INIT + // * bytes or up to end of window, whichever is less. + // */ + // init = s.window_size - curr; + // if (init > WIN_INIT) + // init = WIN_INIT; + // zmemzero(s->window + curr, (unsigned)init); + // s->high_water = curr + init; + // } + // else if (s->high_water < (ulg)curr + WIN_INIT) { + // /* High water mark at or above current data, but below current data + // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + // * to end of window, whichever is less. + // */ + // init = (ulg)curr + WIN_INIT - s->high_water; + // if (init > s->window_size - s->high_water) + // init = s->window_size - s->high_water; + // zmemzero(s->window + s->high_water, (unsigned)init); + // s->high_water += init; + // } + // } + // + // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + // "not enough room for search"); + }; + + /* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ + const deflate_stored = (s, flush) => { + + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + let max_block_size = 0xffff; + + if (max_block_size > s.pending_buf_size - 5) { + max_block_size = s.pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s.lookahead <= 1) { + + //Assert(s->strstart < s->w_size+MAX_DIST(s) || + // s->block_start >= (long)s->w_size, "slide too late"); + // if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || + // s.block_start >= s.w_size)) { + // throw new Error("slide too late"); + // } + + fill_window(s); + if (s.lookahead === 0 && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + + if (s.lookahead === 0) { + break; + } + /* flush the current block */ + } + //Assert(s->block_start >= 0L, "block gone"); + // if (s.block_start < 0) throw new Error("block gone"); + + s.strstart += s.lookahead; + s.lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + const max_start = s.block_start + max_block_size; + + if (s.strstart === 0 || s.strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s.lookahead = s.strstart - max_start; + s.strstart = max_start; + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + + + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + + s.insert = 0; + + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + + if (s.strstart > s.block_start) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_NEED_MORE; + }; + + /* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ + const deflate_fast = (s, flush) => { + + let hash_head; /* head of the hash chain */ + let bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { + break; /* flush the current block */ + } + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH$1) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + } + if (s.match_length >= MIN_MATCH$1) { + // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only + + /*** _tr_tally_dist(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH, bflush); ***/ + bflush = _tr_tally$1(s, s.strstart - s.match_start, s.match_length - MIN_MATCH$1); + + s.lookahead -= s.match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH$1) { + s.match_length--; /* string at strstart already in table */ + do { + s.strstart++; + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s.match_length !== 0); + s.strstart++; + } else + { + s.strstart += s.match_length; + s.match_length = 0; + s.ins_h = s.window[s.strstart]; + /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); + + //#if MIN_MATCH != 3 + // Call UPDATE_HASH() MIN_MATCH-3 more times + //#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s.window[s.strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = ((s.strstart < (MIN_MATCH$1 - 1)) ? s.strstart : MIN_MATCH$1 - 1); + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + }; + + /* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ + const deflate_slow = (s, flush) => { + + let hash_head; /* head of hash chain */ + let bflush; /* set if current block must be flushed */ + + let max_insert; + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { break; } /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH$1) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + */ + s.prev_length = s.match_length; + s.prev_match = s.match_start; + s.match_length = MIN_MATCH$1 - 1; + + if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && + s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + + if (s.match_length <= 5 && + (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH$1 && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s.match_length = MIN_MATCH$1 - 1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s.prev_length >= MIN_MATCH$1 && s.match_length <= s.prev_length) { + max_insert = s.strstart + s.lookahead - MIN_MATCH$1; + /* Do not insert strings in hash table beyond this. */ + + //check_match(s, s.strstart-1, s.prev_match, s.prev_length); + + /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, + s.prev_length - MIN_MATCH, bflush);***/ + bflush = _tr_tally$1(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH$1); + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s.lookahead -= s.prev_length - 1; + s.prev_length -= 2; + do { + if (++s.strstart <= max_insert) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + } while (--s.prev_length !== 0); + s.match_available = 0; + s.match_length = MIN_MATCH$1 - 1; + s.strstart++; + + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + } else if (s.match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = _tr_tally$1(s, 0, s.window[s.strstart - 1]); + + if (bflush) { + /*** FLUSH_BLOCK_ONLY(s, 0) ***/ + flush_block_only(s, false); + /***/ + } + s.strstart++; + s.lookahead--; + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s.match_available = 1; + s.strstart++; + s.lookahead--; + } + } + //Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s.match_available) { + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = _tr_tally$1(s, 0, s.window[s.strstart - 1]); + + s.match_available = 0; + } + s.insert = s.strstart < MIN_MATCH$1 - 1 ? s.strstart : MIN_MATCH$1 - 1; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_BLOCK_DONE; + }; + + + /* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ + const deflate_rle = (s, flush) => { + + let bflush; /* set if current block must be flushed */ + let prev; /* byte at distance one to match */ + let scan, strend; /* scan goes up to strend for length of run */ + + const _win = s.window; + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s.lookahead <= MAX_MATCH$1) { + fill_window(s); + if (s.lookahead <= MAX_MATCH$1 && flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { break; } /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s.match_length = 0; + if (s.lookahead >= MIN_MATCH$1 && s.strstart > 0) { + scan = s.strstart - 1; + prev = _win[scan]; + if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { + strend = s.strstart + MAX_MATCH$1; + do { + /*jshint noempty:false*/ + } while (prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + scan < strend); + s.match_length = MAX_MATCH$1 - (strend - scan); + if (s.match_length > s.lookahead) { + s.match_length = s.lookahead; + } + } + //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s.match_length >= MIN_MATCH$1) { + //check_match(s, s.strstart, s.strstart - 1, s.match_length); + + /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ + bflush = _tr_tally$1(s, 1, s.match_length - MIN_MATCH$1); + + s.lookahead -= s.match_length; + s.strstart += s.match_length; + s.match_length = 0; + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + }; + + /* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ + const deflate_huff = (s, flush) => { + + let bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s.lookahead === 0) { + fill_window(s); + if (s.lookahead === 0) { + if (flush === Z_NO_FLUSH) { + return BS_NEED_MORE; + } + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s.match_length = 0; + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + s.lookahead--; + s.strstart++; + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + }; + + /* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ + function Config(good_length, max_lazy, nice_length, max_chain, func) { + + this.good_length = good_length; + this.max_lazy = max_lazy; + this.nice_length = nice_length; + this.max_chain = max_chain; + this.func = func; + } + + const configuration_table = [ + /* good lazy nice chain */ + new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ + new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ + new Config(4, 5, 16, 8, deflate_fast), /* 2 */ + new Config(4, 6, 32, 32, deflate_fast), /* 3 */ + + new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ + new Config(8, 16, 32, 32, deflate_slow), /* 5 */ + new Config(8, 16, 128, 128, deflate_slow), /* 6 */ + new Config(8, 32, 128, 256, deflate_slow), /* 7 */ + new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ + new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ + ]; + + + /* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ + const lm_init = (s) => { + + s.window_size = 2 * s.w_size; + + /*** CLEAR_HASH(s); ***/ + zero$1(s.head); // Fill with NIL (= 0); + + /* Set the default configuration parameters: + */ + s.max_lazy_match = configuration_table[s.level].max_lazy; + s.good_match = configuration_table[s.level].good_length; + s.nice_match = configuration_table[s.level].nice_length; + s.max_chain_length = configuration_table[s.level].max_chain; + + s.strstart = 0; + s.block_start = 0; + s.lookahead = 0; + s.insert = 0; + s.match_length = s.prev_length = MIN_MATCH$1 - 1; + s.match_available = 0; + s.ins_h = 0; + }; + + + function DeflateState() { + this.strm = null; /* pointer back to this zlib stream */ + this.status = 0; /* as the name implies */ + this.pending_buf = null; /* output still pending */ + this.pending_buf_size = 0; /* size of pending_buf */ + this.pending_out = 0; /* next pending byte to output to the stream */ + this.pending = 0; /* nb of bytes in the pending buffer */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.gzhead = null; /* gzip header information to write */ + this.gzindex = 0; /* where in extra, name, or comment */ + this.method = Z_DEFLATED; /* can only be DEFLATED */ + this.last_flush = -1; /* value of flush param for previous deflate call */ + + this.w_size = 0; /* LZ77 window size (32K by default) */ + this.w_bits = 0; /* log2(w_size) (8..16) */ + this.w_mask = 0; /* w_size - 1 */ + + this.window = null; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. + */ + + this.window_size = 0; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + this.prev = null; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + this.head = null; /* Heads of the hash chains or NIL. */ + + this.ins_h = 0; /* hash index of string to be inserted */ + this.hash_size = 0; /* number of elements in hash table */ + this.hash_bits = 0; /* log2(hash_size) */ + this.hash_mask = 0; /* hash_size-1 */ + + this.hash_shift = 0; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + this.block_start = 0; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + this.match_length = 0; /* length of best match */ + this.prev_match = 0; /* previous match */ + this.match_available = 0; /* set if previous match exists */ + this.strstart = 0; /* start of string to insert */ + this.match_start = 0; /* start of matching string */ + this.lookahead = 0; /* number of valid bytes ahead in window */ + + this.prev_length = 0; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + this.max_chain_length = 0; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + this.max_lazy_match = 0; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ + // That's alias to max_lazy_match, don't use directly + //this.max_insert_length = 0; + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + this.level = 0; /* compression level (1..9) */ + this.strategy = 0; /* favor or force Huffman coding*/ + + this.good_match = 0; + /* Use a faster search when the previous match is longer than this */ + + this.nice_match = 0; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + + /* Didn't use ct_data typedef below to suppress compiler warning */ + + // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + // Use flat array of DOUBLE size, with interleaved fata, + // because JS does not support effective + this.dyn_ltree = new Uint16Array(HEAP_SIZE$1 * 2); + this.dyn_dtree = new Uint16Array((2 * D_CODES$1 + 1) * 2); + this.bl_tree = new Uint16Array((2 * BL_CODES$1 + 1) * 2); + zero$1(this.dyn_ltree); + zero$1(this.dyn_dtree); + zero$1(this.bl_tree); + + this.l_desc = null; /* desc. for literal tree */ + this.d_desc = null; /* desc. for distance tree */ + this.bl_desc = null; /* desc. for bit length tree */ + + //ush bl_count[MAX_BITS+1]; + this.bl_count = new Uint16Array(MAX_BITS$1 + 1); + /* number of codes at each bit length for an optimal tree */ + + //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + this.heap = new Uint16Array(2 * L_CODES$1 + 1); /* heap used to build the Huffman trees */ + zero$1(this.heap); + + this.heap_len = 0; /* number of elements in the heap */ + this.heap_max = 0; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + this.depth = new Uint16Array(2 * L_CODES$1 + 1); //uch depth[2*L_CODES+1]; + zero$1(this.depth); + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + this.l_buf = 0; /* buffer index for literals or lengths */ + + this.lit_bufsize = 0; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + this.last_lit = 0; /* running index in l_buf */ + + this.d_buf = 0; + /* Buffer index for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + this.opt_len = 0; /* bit length of current block with optimal trees */ + this.static_len = 0; /* bit length of current block with static trees */ + this.matches = 0; /* number of string matches in current block */ + this.insert = 0; /* bytes at end of window left to insert */ + + + this.bi_buf = 0; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + this.bi_valid = 0; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + // Used for window memory init. We safely ignore it for JS. That makes + // sense only for pointers and memory check tools. + //this.high_water = 0; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + } + + + const deflateResetKeep = (strm) => { + + if (!strm || !strm.state) { + return err(strm, Z_STREAM_ERROR); + } + + strm.total_in = strm.total_out = 0; + strm.data_type = Z_UNKNOWN$1; + + const s = strm.state; + s.pending = 0; + s.pending_out = 0; + + if (s.wrap < 0) { + s.wrap = -s.wrap; + /* was made negative by deflate(..., Z_FINISH); */ + } + s.status = (s.wrap ? INIT_STATE : BUSY_STATE); + strm.adler = (s.wrap === 2) ? + 0 // crc32(0, Z_NULL, 0) + : + 1; // adler32(0, Z_NULL, 0) + s.last_flush = Z_NO_FLUSH; + _tr_init$1(s); + return Z_OK; + }; + + + const deflateReset = (strm) => { + + const ret = deflateResetKeep(strm); + if (ret === Z_OK) { + lm_init(strm.state); + } + return ret; + }; + + + const deflateSetHeader = (strm, head) => { + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } + strm.state.gzhead = head; + return Z_OK; + }; + + + const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { + + if (!strm) { // === Z_NULL + return Z_STREAM_ERROR; + } + let wrap = 1; + + if (level === Z_DEFAULT_COMPRESSION) { + level = 6; + } + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } + + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } + + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED$1) { + return err(strm, Z_STREAM_ERROR); + } + + + if (windowBits === 8) { + windowBits = 9; + } + /* until 256-byte window bug fixed */ + + const s = new DeflateState(); + + strm.state = s; + s.strm = strm; + + s.wrap = wrap; + s.gzhead = null; + s.w_bits = windowBits; + s.w_size = 1 << s.w_bits; + s.w_mask = s.w_size - 1; + + s.hash_bits = memLevel + 7; + s.hash_size = 1 << s.hash_bits; + s.hash_mask = s.hash_size - 1; + s.hash_shift = ~~((s.hash_bits + MIN_MATCH$1 - 1) / MIN_MATCH$1); + + s.window = new Uint8Array(s.w_size * 2); + s.head = new Uint16Array(s.hash_size); + s.prev = new Uint16Array(s.w_size); + + // Don't need mem init magic for JS. + //s.high_water = 0; /* nothing written to s->window yet */ + + s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + s.pending_buf_size = s.lit_bufsize * 4; + + //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + //s->pending_buf = (uchf *) overlay; + s.pending_buf = new Uint8Array(s.pending_buf_size); + + // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) + //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s.d_buf = 1 * s.lit_bufsize; + + //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + s.l_buf = (1 + 2) * s.lit_bufsize; + + s.level = level; + s.strategy = strategy; + s.method = method; + + return deflateReset(strm); + }; + + const deflateInit = (strm, level) => { + + return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + }; + + + const deflate = (strm, flush) => { + + let beg, val; // for gzip header write only + + if (!strm || !strm.state || + flush > Z_BLOCK || flush < 0) { + return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; + } + + const s = strm.state; + + if (!strm.output || + (!strm.input && strm.avail_in !== 0) || + (s.status === FINISH_STATE && flush !== Z_FINISH)) { + return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); + } + + s.strm = strm; /* just in case */ + const old_flush = s.last_flush; + s.last_flush = flush; + + /* Write the header */ + if (s.status === INIT_STATE) { + + if (s.wrap === 2) { // GZIP header + strm.adler = 0; //crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (!s.gzhead) { // s->gzhead == Z_NULL + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s.level === 9 ? 2 : + (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s.status = BUSY_STATE; + } + else { + put_byte(s, (s.gzhead.text ? 1 : 0) + + (s.gzhead.hcrc ? 2 : 0) + + (!s.gzhead.extra ? 0 : 4) + + (!s.gzhead.name ? 0 : 8) + + (!s.gzhead.comment ? 0 : 16) + ); + put_byte(s, s.gzhead.time & 0xff); + put_byte(s, (s.gzhead.time >> 8) & 0xff); + put_byte(s, (s.gzhead.time >> 16) & 0xff); + put_byte(s, (s.gzhead.time >> 24) & 0xff); + put_byte(s, s.level === 9 ? 2 : + (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, s.gzhead.os & 0xff); + if (s.gzhead.extra && s.gzhead.extra.length) { + put_byte(s, s.gzhead.extra.length & 0xff); + put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); + } + if (s.gzhead.hcrc) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0); + } + s.gzindex = 0; + s.status = EXTRA_STATE; + } + } + else // DEFLATE header + { + let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; + let level_flags = -1; + + if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { + level_flags = 0; + } else if (s.level < 6) { + level_flags = 1; + } else if (s.level === 6) { + level_flags = 2; + } else { + level_flags = 3; + } + header |= (level_flags << 6); + if (s.strstart !== 0) { header |= PRESET_DICT; } + header += 31 - (header % 31); + + s.status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s.strstart !== 0) { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + strm.adler = 1; // adler32(0L, Z_NULL, 0); + } + } + + //#ifdef GZIP + if (s.status === EXTRA_STATE) { + if (s.gzhead.extra/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + + while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + break; + } + } + put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); + s.gzindex++; + } + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (s.gzindex === s.gzhead.extra.length) { + s.gzindex = 0; + s.status = NAME_STATE; + } + } + else { + s.status = NAME_STATE; + } + } + if (s.status === NAME_STATE) { + if (s.gzhead.name/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.name.length) { + val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.gzindex = 0; + s.status = COMMENT_STATE; + } + } + else { + s.status = COMMENT_STATE; + } + } + if (s.status === COMMENT_STATE) { + if (s.gzhead.comment/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.comment.length) { + val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.status = HCRC_STATE; + } + } + else { + s.status = HCRC_STATE; + } + } + if (s.status === HCRC_STATE) { + if (s.gzhead.hcrc) { + if (s.pending + 2 > s.pending_buf_size) { + flush_pending(strm); + } + if (s.pending + 2 <= s.pending_buf_size) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + strm.adler = 0; //crc32(0L, Z_NULL, 0); + s.status = BUSY_STATE; + } + } + else { + s.status = BUSY_STATE; + } + } + //#endif + + /* Flush as much pending output as possible */ + if (s.pending !== 0) { + flush_pending(strm); + if (strm.avail_out === 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s.last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && + flush !== Z_FINISH) { + return err(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s.status === FINISH_STATE && strm.avail_in !== 0) { + return err(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm.avail_in !== 0 || s.lookahead !== 0 || + (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { + let bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : + (s.strategy === Z_RLE ? deflate_rle(s, flush) : + configuration_table[s.level].func(s, flush)); + + if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { + s.status = FINISH_STATE; + } + if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { + if (strm.avail_out === 0) { + s.last_flush = -1; + /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate === BS_BLOCK_DONE) { + if (flush === Z_PARTIAL_FLUSH) { + _tr_align$1(s); + } + else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + + _tr_stored_block$1(s, 0, 0, false); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush === Z_FULL_FLUSH) { + /*** CLEAR_HASH(s); ***/ /* forget history */ + zero$1(s.head); // Fill with NIL (= 0); + + if (s.lookahead === 0) { + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + } + } + flush_pending(strm); + if (strm.avail_out === 0) { + s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + //Assert(strm->avail_out > 0, "bug2"); + //if (strm.avail_out <= 0) { throw new Error("bug2");} + + if (flush !== Z_FINISH) { return Z_OK; } + if (s.wrap <= 0) { return Z_STREAM_END; } + + /* Write the trailer */ + if (s.wrap === 2) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + put_byte(s, (strm.adler >> 16) & 0xff); + put_byte(s, (strm.adler >> 24) & 0xff); + put_byte(s, strm.total_in & 0xff); + put_byte(s, (strm.total_in >> 8) & 0xff); + put_byte(s, (strm.total_in >> 16) & 0xff); + put_byte(s, (strm.total_in >> 24) & 0xff); + } + else + { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s.wrap > 0) { s.wrap = -s.wrap; } + /* write the trailer only once! */ + return s.pending !== 0 ? Z_OK : Z_STREAM_END; + }; + + + const deflateEnd = (strm) => { + + if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { + return Z_STREAM_ERROR; + } + + const status = strm.state.status; + if (status !== INIT_STATE && + status !== EXTRA_STATE && + status !== NAME_STATE && + status !== COMMENT_STATE && + status !== HCRC_STATE && + status !== BUSY_STATE && + status !== FINISH_STATE + ) { + return err(strm, Z_STREAM_ERROR); + } + + strm.state = null; + + return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; + }; + + + /* ========================================================================= + * Initializes the compression dictionary from the given byte + * sequence without producing any compressed output. + */ + const deflateSetDictionary = (strm, dictionary) => { + + let dictLength = dictionary.length; + + if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { + return Z_STREAM_ERROR; + } + + const s = strm.state; + const wrap = s.wrap; + + if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { + return Z_STREAM_ERROR; + } + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap === 1) { + /* adler32(strm->adler, dictionary, dictLength); */ + strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0); + } + + s.wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s.w_size) { + if (wrap === 0) { /* already empty otherwise */ + /*** CLEAR_HASH(s); ***/ + zero$1(s.head); // Fill with NIL (= 0); + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + /* use the tail */ + // dictionary = dictionary.slice(dictLength - s.w_size); + let tmpDict = new Uint8Array(s.w_size); + tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0); + dictionary = tmpDict; + dictLength = s.w_size; + } + /* insert dictionary into window and hash */ + const avail = strm.avail_in; + const next = strm.next_in; + const input = strm.input; + strm.avail_in = dictLength; + strm.next_in = 0; + strm.input = dictionary; + fill_window(s); + while (s.lookahead >= MIN_MATCH$1) { + let str = s.strstart; + let n = s.lookahead - (MIN_MATCH$1 - 1); + do { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH$1 - 1]); + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + + s.head[s.ins_h] = str; + str++; + } while (--n); + s.strstart = str; + s.lookahead = MIN_MATCH$1 - 1; + fill_window(s); + } + s.strstart += s.lookahead; + s.block_start = s.strstart; + s.insert = s.lookahead; + s.lookahead = 0; + s.match_length = s.prev_length = MIN_MATCH$1 - 1; + s.match_available = 0; + strm.next_in = next; + strm.input = input; + strm.avail_in = avail; + s.wrap = wrap; + return Z_OK; + }; + + + var deflateInit_1 = deflateInit; + var deflateInit2_1 = deflateInit2; + var deflateReset_1 = deflateReset; + var deflateResetKeep_1 = deflateResetKeep; + var deflateSetHeader_1 = deflateSetHeader; + var deflate_2 = deflate; + var deflateEnd_1 = deflateEnd; + var deflateSetDictionary_1 = deflateSetDictionary; + var deflateInfo = 'pako deflate (from Nodeca project)'; + + /* Not implemented + module.exports.deflateBound = deflateBound; + module.exports.deflateCopy = deflateCopy; + module.exports.deflateParams = deflateParams; + module.exports.deflatePending = deflatePending; + module.exports.deflatePrime = deflatePrime; + module.exports.deflateTune = deflateTune; + */ + + var deflate_1 = { + deflateInit: deflateInit_1, + deflateInit2: deflateInit2_1, + deflateReset: deflateReset_1, + deflateResetKeep: deflateResetKeep_1, + deflateSetHeader: deflateSetHeader_1, + deflate: deflate_2, + deflateEnd: deflateEnd_1, + deflateSetDictionary: deflateSetDictionary_1, + deflateInfo: deflateInfo + }; + + const _has = (obj, key) => { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + + var assign = function (obj /*from1, from2, from3, ...*/) { + const sources = Array.prototype.slice.call(arguments, 1); + while (sources.length) { + const source = sources.shift(); + if (!source) { continue; } + + if (typeof source !== 'object') { + throw new TypeError(source + 'must be non-object'); + } + + for (const p in source) { + if (_has(source, p)) { + obj[p] = source[p]; + } + } + } + + return obj; + }; + + + // Join array of chunks to single array. + var flattenChunks = (chunks) => { + // calculate data length + let len = 0; + + for (let i = 0, l = chunks.length; i < l; i++) { + len += chunks[i].length; + } + + // join chunks + const result = new Uint8Array(len); + + for (let i = 0, pos = 0, l = chunks.length; i < l; i++) { + let chunk = chunks[i]; + result.set(chunk, pos); + pos += chunk.length; + } + + return result; + }; + + var common = { + assign: assign, + flattenChunks: flattenChunks + }; + + // String encode/decode helpers + + + // Quick check if we can use fast array to bin string conversion + // + // - apply(Array) can fail on Android 2.2 + // - apply(Uint8Array) can fail on iOS 5.1 Safari + // + let STR_APPLY_UIA_OK = true; + + try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; } + + + // Table with utf8 lengths (calculated by first byte of sequence) + // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, + // because max possible codepoint is 0x10ffff + const _utf8len = new Uint8Array(256); + for (let q = 0; q < 256; q++) { + _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1); + } + _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start + + + // convert string to array (typed, when possible) + var string2buf = (str) => { + let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; + + // count binary size + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; + } + + // allocate buffer + buf = new Uint8Array(buf_len); + + // convert + for (i = 0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + if (c < 0x80) { + /* one byte */ + buf[i++] = c; + } else if (c < 0x800) { + /* two bytes */ + buf[i++] = 0xC0 | (c >>> 6); + buf[i++] = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + /* three bytes */ + buf[i++] = 0xE0 | (c >>> 12); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } else { + /* four bytes */ + buf[i++] = 0xf0 | (c >>> 18); + buf[i++] = 0x80 | (c >>> 12 & 0x3f); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } + } + + return buf; + }; + + // Helper + const buf2binstring = (buf, len) => { + // On Chrome, the arguments in a function call that are allowed is `65534`. + // If the length of the buffer is smaller than that, we can use this optimization, + // otherwise we will take a slower path. + if (len < 65534) { + if (buf.subarray && STR_APPLY_UIA_OK) { + return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len)); + } + } + + let result = ''; + for (let i = 0; i < len; i++) { + result += String.fromCharCode(buf[i]); + } + return result; + }; + + + // convert array to string + var buf2string = (buf, max) => { + let i, out; + const len = max || buf.length; + + // Reserve max possible length (2 words per char) + // NB: by unknown reasons, Array is significantly faster for + // String.fromCharCode.apply than Uint16Array. + const utf16buf = new Array(len * 2); + + for (out = 0, i = 0; i < len;) { + let c = buf[i++]; + // quick process ascii + if (c < 0x80) { utf16buf[out++] = c; continue; } + + let c_len = _utf8len[c]; + // skip 5 & 6 byte codes + if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; } + + // apply mask on first byte + c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; + // join the rest + while (c_len > 1 && i < len) { + c = (c << 6) | (buf[i++] & 0x3f); + c_len--; + } + + // terminated by end of string? + if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; } + + if (c < 0x10000) { + utf16buf[out++] = c; + } else { + c -= 0x10000; + utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); + utf16buf[out++] = 0xdc00 | (c & 0x3ff); + } + } + + return buf2binstring(utf16buf, out); + }; + + + // Calculate max possible position in utf8 buffer, + // that will not break sequence. If that's not possible + // - (very small limits) return max size as is. + // + // buf[] - utf8 bytes array + // max - length limit (mandatory); + var utf8border = (buf, max) => { + + max = max || buf.length; + if (max > buf.length) { max = buf.length; } + + // go back from last position, until start of sequence found + let pos = max - 1; + while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } + + // Very small and broken sequence, + // return max, because we should return something anyway. + if (pos < 0) { return max; } + + // If we came to start of buffer - that means buffer is too small, + // return max too. + if (pos === 0) { return max; } + + return (pos + _utf8len[buf[pos]] > max) ? pos : max; + }; + + var strings = { + string2buf: string2buf, + buf2string: buf2string, + utf8border: utf8border + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + function ZStream() { + /* next input byte */ + this.input = null; // JS specific, because we have no pointers + this.next_in = 0; + /* number of bytes available at input */ + this.avail_in = 0; + /* total number of input bytes read so far */ + this.total_in = 0; + /* next output byte should be put there */ + this.output = null; // JS specific, because we have no pointers + this.next_out = 0; + /* remaining free space at output */ + this.avail_out = 0; + /* total number of bytes output so far */ + this.total_out = 0; + /* last error message, NULL if no error */ + this.msg = ''/*Z_NULL*/; + /* not visible by applications */ + this.state = null; + /* best guess about the data type: binary or text */ + this.data_type = 2/*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + this.adler = 0; + } + + var zstream = ZStream; + + const toString = Object.prototype.toString; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_NO_FLUSH: Z_NO_FLUSH$1, Z_SYNC_FLUSH, Z_FULL_FLUSH: Z_FULL_FLUSH$1, Z_FINISH: Z_FINISH$1, + Z_OK: Z_OK$1, Z_STREAM_END: Z_STREAM_END$1, + Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, + Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, + Z_DEFLATED: Z_DEFLATED$1 + } = constants; + + /* ===========================================================================*/ + + + /** + * class Deflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[deflate]], + * [[deflateRaw]] and [[gzip]]. + **/ + + /* internal + * Deflate.chunks -> Array + * + * Chunks of output data, if [[Deflate#onData]] not overridden. + **/ + + /** + * Deflate.result -> Uint8Array + * + * Compressed result, generated by default [[Deflate#onData]] + * and [[Deflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Deflate#push]] with `Z_FINISH` / `true` param). + **/ + + /** + * Deflate.err -> Number + * + * Error code after deflate finished. 0 (Z_OK) on success. + * You will not need it in real life, because deflate errors + * are possible only on wrong options or bad `onData` / `onEnd` + * custom handlers. + **/ + + /** + * Deflate.msg -> String + * + * Error message, if [[Deflate.err]] != 0 + **/ + + + /** + * new Deflate(options) + * - options (Object): zlib deflate options. + * + * Creates new deflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `level` + * - `windowBits` + * - `memLevel` + * - `strategy` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw deflate + * - `gzip` (Boolean) - create gzip wrapper + * - `header` (Object) - custom header for gzip + * - `text` (Boolean) - true if compressed data believed to be text + * - `time` (Number) - modification time, unix timestamp + * - `os` (Number) - operation system code + * - `extra` (Array) - array of bytes with extra data (max 65536) + * - `name` (String) - file name (binary string) + * - `comment` (String) - comment (binary string) + * - `hcrc` (Boolean) - true if header crc should be added + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) + * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * const deflate = new pako.Deflate({ level: 3}); + * + * deflate.push(chunk1, false); + * deflate.push(chunk2, true); // true -> last chunk + * + * if (deflate.err) { throw new Error(deflate.err); } + * + * console.log(deflate.result); + * ``` + **/ + function Deflate(options) { + this.options = common.assign({ + level: Z_DEFAULT_COMPRESSION$1, + method: Z_DEFLATED$1, + chunkSize: 16384, + windowBits: 15, + memLevel: 8, + strategy: Z_DEFAULT_STRATEGY$1 + }, options || {}); + + let opt = this.options; + + if (opt.raw && (opt.windowBits > 0)) { + opt.windowBits = -opt.windowBits; + } + + else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { + opt.windowBits += 16; + } + + this.err = 0; // error code, if happens (0 = Z_OK) + this.msg = ''; // error message + this.ended = false; // used to avoid multiple onEnd() calls + this.chunks = []; // chunks of compressed data + + this.strm = new zstream(); + this.strm.avail_out = 0; + + let status = deflate_1.deflateInit2( + this.strm, + opt.level, + opt.method, + opt.windowBits, + opt.memLevel, + opt.strategy + ); + + if (status !== Z_OK$1) { + throw new Error(messages[status]); + } + + if (opt.header) { + deflate_1.deflateSetHeader(this.strm, opt.header); + } + + if (opt.dictionary) { + let dict; + // Convert data if needed + if (typeof opt.dictionary === 'string') { + // If we need to compress text, change encoding to utf8. + dict = strings.string2buf(opt.dictionary); + } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { + dict = new Uint8Array(opt.dictionary); + } else { + dict = opt.dictionary; + } + + status = deflate_1.deflateSetDictionary(this.strm, dict); + + if (status !== Z_OK$1) { + throw new Error(messages[status]); + } + + this._dict_set = true; + } + } + + /** + * Deflate#push(data[, flush_mode]) -> Boolean + * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be + * converted to utf8 byte sequence. + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. + * + * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with + * new compressed chunks. Returns `true` on success. The last data block must + * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending + * buffers and call [[Deflate#onEnd]]. + * + * On fail call [[Deflate#onEnd]] with error code and return false. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + **/ + Deflate.prototype.push = function (data, flush_mode) { + const strm = this.strm; + const chunkSize = this.options.chunkSize; + let status, _flush_mode; + + if (this.ended) { return false; } + + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH$1 : Z_NO_FLUSH$1; + + // Convert data if needed + if (typeof data === 'string') { + // If we need to compress text, change encoding to utf8. + strm.input = strings.string2buf(data); + } else if (toString.call(data) === '[object ArrayBuffer]') { + strm.input = new Uint8Array(data); + } else { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + for (;;) { + if (strm.avail_out === 0) { + strm.output = new Uint8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + // Make sure avail_out > 6 to avoid repeating markers + if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH$1) && strm.avail_out <= 6) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + status = deflate_1.deflate(strm, _flush_mode); + + // Ended => flush and finish + if (status === Z_STREAM_END$1) { + if (strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + } + status = deflate_1.deflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return status === Z_OK$1; + } + + // Flush if out buffer full + if (strm.avail_out === 0) { + this.onData(strm.output); + continue; + } + + // Flush if requested and has data + if (_flush_mode > 0 && strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + if (strm.avail_in === 0) break; + } + + return true; + }; + + + /** + * Deflate#onData(chunk) -> Void + * - chunk (Uint8Array): output data. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + **/ + Deflate.prototype.onData = function (chunk) { + this.chunks.push(chunk); + }; + + + /** + * Deflate#onEnd(status) -> Void + * - status (Number): deflate status. 0 (Z_OK) on success, + * other if not. + * + * Called once after you tell deflate that the input stream is + * complete (Z_FINISH). By default - join collected chunks, + * free memory and fill `results` / `err` properties. + **/ + Deflate.prototype.onEnd = function (status) { + // On success - join + if (status === Z_OK$1) { + this.result = common.flattenChunks(this.chunks); + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + }; + + + /** + * deflate(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * Compress `data` with deflate algorithm and `options`. + * + * Supported options are: + * + * - level + * - windowBits + * - memLevel + * - strategy + * - dictionary + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]); + * + * console.log(pako.deflate(data)); + * ``` + **/ + function deflate$1(input, options) { + const deflator = new Deflate(options); + + deflator.push(input, true); + + // That will never happens, if you don't cheat with options :) + if (deflator.err) { throw deflator.msg || messages[deflator.err]; } + + return deflator.result; + } + + + /** + * deflateRaw(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but creates raw data, without wrapper + * (header and adler32 crc). + **/ + function deflateRaw(input, options) { + options = options || {}; + options.raw = true; + return deflate$1(input, options); + } + + + /** + * gzip(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but create gzip wrapper instead of + * deflate one. + **/ + function gzip(input, options) { + options = options || {}; + options.gzip = true; + return deflate$1(input, options); + } + + + var Deflate_1 = Deflate; + var deflate_2$1 = deflate$1; + var deflateRaw_1 = deflateRaw; + var gzip_1 = gzip; + var constants$1 = constants; + + var deflate_1$1 = { + Deflate: Deflate_1, + deflate: deflate_2$1, + deflateRaw: deflateRaw_1, + gzip: gzip_1, + constants: constants$1 + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + // See state defs from inflate.js + const BAD = 30; /* got a data error -- remain here until reset */ + const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + + /* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ + var inffast = function inflate_fast(strm, start) { + let _in; /* local strm.input */ + let last; /* have enough input while in < last */ + let _out; /* local strm.output */ + let beg; /* inflate()'s initial strm.output */ + let end; /* while out < end, enough space available */ + //#ifdef INFLATE_STRICT + let dmax; /* maximum distance from zlib header */ + //#endif + let wsize; /* window size or zero if not using window */ + let whave; /* valid bytes in the window */ + let wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + let s_window; /* allocated sliding window, if wsize != 0 */ + let hold; /* local strm.hold */ + let bits; /* local strm.bits */ + let lcode; /* local strm.lencode */ + let dcode; /* local strm.distcode */ + let lmask; /* mask for first level of length codes */ + let dmask; /* mask for first level of distance codes */ + let here; /* retrieved table entry */ + let op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + let len; /* match length, unused bytes */ + let dist; /* match distance */ + let from; /* where to copy match from */ + let from_source; + + + let input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + const state = strm.state; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); + //#ifdef INFLATE_STRICT + dmax = state.dmax; + //#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + dolen: + for (;;) { // Goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + if (op === 0) { /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff/*here.val*/; + } + else if (op & 16) { /* length base */ + len = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + dodist: + for (;;) { // goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + + if (op & 16) { /* distance base */ + dist = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); + //#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } + //#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } + + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // if (len <= op - whave) { + // do { + // output[_out++] = 0; + // } while (--len); + // continue top; + // } + // len -= op - whave; + // do { + // output[_out++] = 0; + // } while (--op > whave); + // if (op === 0) { + // from = _out - dist; + // do { + // output[_out++] = output[from++]; + // } while (--len); + // continue top; + // } + //#endif + } + from = 0; // window index + from_source = s_window; + if (wnext === 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } + else { + from = _out - dist; /* copy direct from output */ + do { /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } + else if ((op & 64) === 0) { /* 2nd level distance code */ + here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dodist; + } + else { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } + else if ((op & 64) === 0) { /* 2nd level length code */ + here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dolen; + } + else if (op & 32) { /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = TYPE; + break top; + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } while (_in < last && _out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); + strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); + state.hold = hold; + state.bits = bits; + return; + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + const MAXBITS = 15; + const ENOUGH_LENS = 852; + const ENOUGH_DISTS = 592; + //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + const CODES = 0; + const LENS = 1; + const DISTS = 2; + + const lbase = new Uint16Array([ /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + ]); + + const lext = new Uint8Array([ /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 + ]); + + const dbase = new Uint16Array([ /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0 + ]); + + const dext = new Uint8Array([ /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64 + ]); + + const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => + { + const bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + let len = 0; /* a code's length in bits */ + let sym = 0; /* index of code symbols */ + let min = 0, max = 0; /* minimum and maximum code lengths */ + let root = 0; /* number of index bits for root table */ + let curr = 0; /* number of index bits for current table */ + let drop = 0; /* code bits to drop for sub-table */ + let left = 0; /* number of prefix codes available */ + let used = 0; /* code entries in table used */ + let huff = 0; /* Huffman code */ + let incr; /* for incrementing code, index */ + let fill; /* index for replicating entries */ + let low; /* low bits for current root entry */ + let mask; /* mask for low root bits */ + let next; /* next available space in table */ + let base = null; /* base value table to use */ + let base_index = 0; + // let shoextra; /* extra bits table to use */ + let end; /* use base and extra for symbol > end */ + const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ + const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ + let extra = null; + let extra_index = 0; + + let here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) { break; } + } + if (root > max) { + root = max; + } + if (max === 0) { /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) { break; } + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES || max !== 1)) { + return -1; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type === CODES) { + base = extra = work; /* dummy value--not used */ + end = 19; + + } else if (type === LENS) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + + } else { /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } + else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; + } while (fill !== 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + if (--count[len] === 0) { + if (len === max) { break; } + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) !== low) { + /* if first time, transition to sub-tables */ + if (drop === 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { break; } + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff !== 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return 0; + }; + + + var inftrees = inflate_table; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + + + + + + const CODES$1 = 0; + const LENS$1 = 1; + const DISTS$1 = 2; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_FINISH: Z_FINISH$2, Z_BLOCK: Z_BLOCK$1, Z_TREES, + Z_OK: Z_OK$2, Z_STREAM_END: Z_STREAM_END$2, Z_NEED_DICT, Z_STREAM_ERROR: Z_STREAM_ERROR$1, Z_DATA_ERROR: Z_DATA_ERROR$1, Z_MEM_ERROR, Z_BUF_ERROR: Z_BUF_ERROR$1, + Z_DEFLATED: Z_DEFLATED$2 + } = constants; + + + /* STATES ====================================================================*/ + /* ===========================================================================*/ + + + const HEAD = 1; /* i: waiting for magic header */ + const FLAGS = 2; /* i: waiting for method and flags (gzip) */ + const TIME = 3; /* i: waiting for modification time (gzip) */ + const OS = 4; /* i: waiting for extra flags and operating system (gzip) */ + const EXLEN = 5; /* i: waiting for extra length (gzip) */ + const EXTRA = 6; /* i: waiting for extra bytes (gzip) */ + const NAME = 7; /* i: waiting for end of file name (gzip) */ + const COMMENT = 8; /* i: waiting for end of comment (gzip) */ + const HCRC = 9; /* i: waiting for header crc (gzip) */ + const DICTID = 10; /* i: waiting for dictionary check value */ + const DICT = 11; /* waiting for inflateSetDictionary() call */ + const TYPE$1 = 12; /* i: waiting for type bits, including last-flag bit */ + const TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ + const STORED = 14; /* i: waiting for stored size (length and complement) */ + const COPY_ = 15; /* i/o: same as COPY below, but only first time in */ + const COPY = 16; /* i/o: waiting for input or output to copy stored block */ + const TABLE = 17; /* i: waiting for dynamic block table lengths */ + const LENLENS = 18; /* i: waiting for code length code lengths */ + const CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ + const LEN_ = 20; /* i: same as LEN below, but only first time in */ + const LEN = 21; /* i: waiting for length/lit/eob code */ + const LENEXT = 22; /* i: waiting for length extra bits */ + const DIST = 23; /* i: waiting for distance code */ + const DISTEXT = 24; /* i: waiting for distance extra bits */ + const MATCH = 25; /* o: waiting for output space to copy string */ + const LIT = 26; /* o: waiting for output space to write literal */ + const CHECK = 27; /* i: waiting for 32-bit check value */ + const LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ + const DONE = 29; /* finished check, done -- remain here until reset */ + const BAD$1 = 30; /* got a data error -- remain here until reset */ + const MEM = 31; /* got an inflate() memory error -- remain here until reset */ + const SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + + /* ===========================================================================*/ + + + + const ENOUGH_LENS$1 = 852; + const ENOUGH_DISTS$1 = 592; + //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + const MAX_WBITS$1 = 15; + /* 32K LZ77 window */ + const DEF_WBITS = MAX_WBITS$1; + + + const zswap32 = (q) => { + + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); + }; + + + function InflateState() { + this.mode = 0; /* current inflate mode */ + this.last = false; /* true if processing last block */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib) */ + this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ + this.check = 0; /* protected copy of check value */ + this.total = 0; /* protected copy of output count */ + // TODO: may be {} + this.head = null; /* where to save gzip header information */ + + /* sliding window */ + this.wbits = 0; /* log base 2 of requested window size */ + this.wsize = 0; /* window size or zero if not using window */ + this.whave = 0; /* valid bytes in the window */ + this.wnext = 0; /* window write index */ + this.window = null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + this.hold = 0; /* input bit accumulator */ + this.bits = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + this.length = 0; /* literal or length of data to copy */ + this.offset = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + this.extra = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + this.lencode = null; /* starting table for length/literal codes */ + this.distcode = null; /* starting table for distance codes */ + this.lenbits = 0; /* index bits for lencode */ + this.distbits = 0; /* index bits for distcode */ + + /* dynamic table building */ + this.ncode = 0; /* number of code length code lengths */ + this.nlen = 0; /* number of length code lengths */ + this.ndist = 0; /* number of distance code lengths */ + this.have = 0; /* number of code lengths in lens[] */ + this.next = null; /* next available space in codes[] */ + + this.lens = new Uint16Array(320); /* temporary storage for code lengths */ + this.work = new Uint16Array(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //this.codes = new Int32Array(ENOUGH); /* space for code tables */ + this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ + this.distdyn = null; /* dynamic table for distance codes (JS specific) */ + this.sane = 0; /* if false, allow invalid distance too far */ + this.back = 0; /* bits back of last unprocessed length/lit */ + this.was = 0; /* initial length of match */ + } + + + const inflateResetKeep = (strm) => { + + if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } + const state = strm.state; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ''; /*Z_NULL*/ + if (state.wrap) { /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null/*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS$1); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS$1); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK$2; + }; + + + const inflateReset = (strm) => { + + if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } + const state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); + + }; + + + const inflateReset2 = (strm, windowBits) => { + let wrap; + + /* get the state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } + const state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR$1; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); + }; + + + const inflateInit2 = (strm, windowBits) => { + + if (!strm) { return Z_STREAM_ERROR$1; } + //strm.msg = Z_NULL; /* in case we return an error */ + + const state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.window = null/*Z_NULL*/; + const ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK$2) { + strm.state = null/*Z_NULL*/; + } + return ret; + }; + + + const inflateInit = (strm) => { + + return inflateInit2(strm, DEF_WBITS); + }; + + + /* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ + let virgin = true; + + let lenfix, distfix; // We have no pointers in JS, so keep tables separate + + + const fixedtables = (state) => { + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + lenfix = new Int32Array(512); + distfix = new Int32Array(32); + + /* literal/length table */ + let sym = 0; + while (sym < 144) { state.lens[sym++] = 8; } + while (sym < 256) { state.lens[sym++] = 9; } + while (sym < 280) { state.lens[sym++] = 7; } + while (sym < 288) { state.lens[sym++] = 8; } + + inftrees(LENS$1, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + inftrees(DISTS$1, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; + }; + + + /* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ + const updatewindow = (strm, src, end, copy) => { + + let dist; + const state = strm.state; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new Uint8Array(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + state.window.set(src.subarray(end - state.wsize, end), 0); + state.wnext = 0; + state.whave = state.wsize; + } + else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); + copy -= dist; + if (copy) { + //zmemcpy(state->window, end - copy, copy); + state.window.set(src.subarray(end - copy, end), 0); + state.wnext = copy; + state.whave = state.wsize; + } + else { + state.wnext += dist; + if (state.wnext === state.wsize) { state.wnext = 0; } + if (state.whave < state.wsize) { state.whave += dist; } + } + } + return 0; + }; + + + const inflate = (strm, flush) => { + + let state; + let input, output; // input/output buffers + let next; /* next input INDEX */ + let put; /* next output INDEX */ + let have, left; /* available input and output */ + let hold; /* bit buffer */ + let bits; /* bits in bit buffer */ + let _in, _out; /* save starting available input and output */ + let copy; /* number of stored or match bytes to copy */ + let from; /* where to copy match bytes from */ + let from_source; + let here = 0; /* current decoding table entry */ + let here_bits, here_op, here_val; // paked "here" denormalized (JS specific) + //let last; /* parent table entry */ + let last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + let len; /* length to copy for repeats, bits to drop */ + let ret; /* return code */ + const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */ + let opts; + + let n; // temporary variable for NEED_BITS + + const order = /* permutation of code lengths */ + new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); + + + if (!strm || !strm.state || !strm.output || + (!strm.input && strm.avail_in !== 0)) { + return Z_STREAM_ERROR$1; + } + + state = strm.state; + if (state.mode === TYPE$1) { state.mode = TYPEDO; } /* skip check */ + + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = Z_OK$2; + + inf_leave: // goto emulation + for (;;) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ + state.check = 0/*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + break; + } + state.flags = 0; /* expect zlib header */ + if (state.head) { + state.head.done = false; + } + if (!(state.wrap & 1) || /* check if zlib header allowed */ + (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { + strm.msg = 'incorrect header check'; + state.mode = BAD$1; + break; + } + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED$2) { + strm.msg = 'unknown compression method'; + state.mode = BAD$1; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } + else if (len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD$1; + break; + } + + // !!! pako patch. Force use `options.windowBits` if passed. + // Required to always use max window size by default. + state.dmax = 1 << state.wbits; + //state.dmax = 1 << len; + + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID : TYPE$1; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.flags = hold; + if ((state.flags & 0xff) !== Z_DEFLATED$2) { + strm.msg = 'unknown compression method'; + state.mode = BAD$1; + break; + } + if (state.flags & 0xe000) { + strm.msg = 'unknown header flags set'; + state.mode = BAD$1; + break; + } + if (state.head) { + state.head.text = ((hold >> 8) & 1); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.time = hold; + } + if (state.flags & 0x0200) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = crc32_1(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.xflags = (hold & 0xff); + state.head.os = (hold >> 8); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length = hold; + if (state.head) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400) { + copy = state.length; + if (copy > have) { copy = have; } + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + // Use untyped array for more convenient processing later + state.head.extra = new Uint8Array(state.head.extra_len); + } + state.head.extra.set( + input.subarray( + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + next + copy + ), + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length) { break inf_leave; } + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.name_max*/)) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.comm_max*/)) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD$1; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head) { + state.head.hcrc = ((state.flags >> 9) & 1); + state.head.done = true; + } + strm.adler = state.check = 0; + state.mode = TYPE$1; + break; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (state.havedict === 0) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return Z_NEED_DICT; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE$1; + /* falls through */ + case TYPE$1: + if (flush === Z_BLOCK$1 || flush === Z_TREES) { break inf_leave; } + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + break; + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.last = (hold & 0x01)/*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03)/*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush === Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break inf_leave; + } + break; + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + break; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD$1; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { + strm.msg = 'invalid stored block lengths'; + state.mode = BAD$1; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy) { + if (copy > have) { copy = have; } + if (copy > left) { copy = left; } + if (copy === 0) { break inf_leave; } + //--- zmemcpy(put, next, copy); --- + output.set(input.subarray(next, next + copy), put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE$1; + break; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + //#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = 'too many length or distance symbols'; + state.mode = BAD$1; + break; + } + //#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = { bits: state.lenbits }; + ret = inftrees(CODES$1, state.lens, 0, 19, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + + if (ret) { + strm.msg = 'invalid code lengths set'; + state.mode = BAD$1; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } + else { + if (here_val === 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have === 0) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD$1; + break; + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03);//BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } + else if (here_val === 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f);//BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD$1; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD$1) { break; } + + /* check for end-of-block code (better have one) */ + if (state.lens[256] === 0) { + strm.msg = 'invalid code -- missing end-of-block'; + state.mode = BAD$1; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = { bits: state.lenbits }; + ret = inftrees(LENS$1, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret) { + strm.msg = 'invalid literal/lengths set'; + state.mode = BAD$1; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inftrees(DISTS$1, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret) { + strm.msg = 'invalid distances set'; + state.mode = BAD$1; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + inffast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode === TYPE$1) { + state.back = -1; + } + break; + } + state.back = 0; + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_op && (here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.lencode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE$1; + break; + } + if (here_op & 64) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD$1; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + for (;;) { + here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if ((here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.distcode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64) { + strm.msg = 'invalid distance code'; + state.mode = BAD$1; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD$1; + break; + } + //#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left === 0) { break inf_leave; } + copy = _out - left; + if (state.offset > copy) { /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD$1; + break; + } + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // Trace((stderr, "inflate.c too far\n")); + // copy -= state.whave; + // if (copy > state.length) { copy = state.length; } + // if (copy > left) { copy = left; } + // left -= copy; + // state.length -= copy; + // do { + // output[put++] = 0; + // } while (--copy); + // if (state.length === 0) { state.mode = LEN; } + // break; + //#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } + else { + from = state.wnext - copy; + } + if (copy > state.length) { copy = state.length; } + from_source = state.window; + } + else { /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) { copy = left; } + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy); + if (state.length === 0) { state.mode = LEN; } + break; + case LIT: + if (left === 0) { break inf_leave; } + output[put++] = state.length; + left--; + state.mode = LEN; + break; + case CHECK: + if (state.wrap) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + // Use '|' instead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = state.check = + /*UPDATE(state.check, put - _out, _out);*/ + (state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out)); + + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD$1; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD$1; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = Z_STREAM_END$2; + break inf_leave; + case BAD$1: + ret = Z_DATA_ERROR$1; + break inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + /* falls through */ + default: + return Z_STREAM_ERROR$1; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if (state.wsize || (_out !== strm.avail_out && state.mode < BAD$1 && + (state.mode < CHECK || flush !== Z_FINISH$2))) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ; + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap && _out) { + strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + (state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE$1 ? 128 : 0) + + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH$2) && ret === Z_OK$2) { + ret = Z_BUF_ERROR$1; + } + return ret; + }; + + + const inflateEnd = (strm) => { + + if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { + return Z_STREAM_ERROR$1; + } + + let state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK$2; + }; + + + const inflateGetHeader = (strm, head) => { + + /* check state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR$1; } + const state = strm.state; + if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR$1; } + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK$2; + }; + + + const inflateSetDictionary = (strm, dictionary) => { + const dictLength = dictionary.length; + + let state; + let dictid; + let ret; + + /* check state */ + if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR$1; } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR$1; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32_1(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR$1; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK$2; + }; + + + var inflateReset_1 = inflateReset; + var inflateReset2_1 = inflateReset2; + var inflateResetKeep_1 = inflateResetKeep; + var inflateInit_1 = inflateInit; + var inflateInit2_1 = inflateInit2; + var inflate_2 = inflate; + var inflateEnd_1 = inflateEnd; + var inflateGetHeader_1 = inflateGetHeader; + var inflateSetDictionary_1 = inflateSetDictionary; + var inflateInfo = 'pako inflate (from Nodeca project)'; + + /* Not implemented + module.exports.inflateCopy = inflateCopy; + module.exports.inflateGetDictionary = inflateGetDictionary; + module.exports.inflateMark = inflateMark; + module.exports.inflatePrime = inflatePrime; + module.exports.inflateSync = inflateSync; + module.exports.inflateSyncPoint = inflateSyncPoint; + module.exports.inflateUndermine = inflateUndermine; + */ + + var inflate_1 = { + inflateReset: inflateReset_1, + inflateReset2: inflateReset2_1, + inflateResetKeep: inflateResetKeep_1, + inflateInit: inflateInit_1, + inflateInit2: inflateInit2_1, + inflate: inflate_2, + inflateEnd: inflateEnd_1, + inflateGetHeader: inflateGetHeader_1, + inflateSetDictionary: inflateSetDictionary_1, + inflateInfo: inflateInfo + }; + + // (C) 1995-2013 Jean-loup Gailly and Mark Adler + // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + + function GZheader() { + /* true if compressed data believed to be text */ + this.text = 0; + /* modification time */ + this.time = 0; + /* extra flags (not used when writing a gzip file) */ + this.xflags = 0; + /* operating system */ + this.os = 0; + /* pointer to extra field or Z_NULL if none */ + this.extra = null; + /* extra field length (valid if extra != Z_NULL) */ + this.extra_len = 0; // Actually, we don't need it in JS, + // but leave for few code modifications + + // + // Setup limits is not necessary because in js we should not preallocate memory + // for inflate use constant limit in 65536 bytes + // + + /* space at extra (only when reading header) */ + // this.extra_max = 0; + /* pointer to zero-terminated file name or Z_NULL */ + this.name = ''; + /* space at name (only when reading header) */ + // this.name_max = 0; + /* pointer to zero-terminated comment or Z_NULL */ + this.comment = ''; + /* space at comment (only when reading header) */ + // this.comm_max = 0; + /* true if there was or will be a header crc */ + this.hcrc = 0; + /* true when done reading gzip header (not used when writing a gzip file) */ + this.done = false; + } + + var gzheader = GZheader; + + const toString$1 = Object.prototype.toString; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + const { + Z_NO_FLUSH: Z_NO_FLUSH$2, Z_FINISH: Z_FINISH$3, + Z_OK: Z_OK$3, Z_STREAM_END: Z_STREAM_END$3, Z_NEED_DICT: Z_NEED_DICT$1, Z_STREAM_ERROR: Z_STREAM_ERROR$2, Z_DATA_ERROR: Z_DATA_ERROR$2, Z_MEM_ERROR: Z_MEM_ERROR$1 + } = constants; + + /* ===========================================================================*/ + + + /** + * class Inflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[inflate]] + * and [[inflateRaw]]. + **/ + + /* internal + * inflate.chunks -> Array + * + * Chunks of output data, if [[Inflate#onData]] not overridden. + **/ + + /** + * Inflate.result -> Uint8Array|String + * + * Uncompressed result, generated by default [[Inflate#onData]] + * and [[Inflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Inflate#push]] with `Z_FINISH` / `true` param). + **/ + + /** + * Inflate.err -> Number + * + * Error code after inflate finished. 0 (Z_OK) on success. + * Should be checked if broken data possible. + **/ + + /** + * Inflate.msg -> String + * + * Error message, if [[Inflate.err]] != 0 + **/ + + + /** + * new Inflate(options) + * - options (Object): zlib inflate options. + * + * Creates new inflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `windowBits` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw inflate + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * By default, when no options set, autodetect deflate/gzip data format via + * wrapper header. + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) + * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * const inflate = new pako.Inflate({ level: 3}); + * + * inflate.push(chunk1, false); + * inflate.push(chunk2, true); // true -> last chunk + * + * if (inflate.err) { throw new Error(inflate.err); } + * + * console.log(inflate.result); + * ``` + **/ + function Inflate(options) { + this.options = common.assign({ + chunkSize: 1024 * 64, + windowBits: 15, + to: '' + }, options || {}); + + const opt = this.options; - const pagesMap = new Map(); - function definePage(pagePath, component) { - pagesMap.set(pagePath, once(createFactory(component))); - } - function createPage(__pageId, __pagePath, __pageQuery, __pageInstance, pageOptions) { - const pageNode = createPageNode(__pageId, pageOptions, true); - const app = vue.createApp(pagesMap.get(__pagePath)(), { - __pageId, - __pagePath, - __pageQuery, - __pageInstance, - }).use(__vuePlugin); - const oldUnmount = app.unmount; - app.unmount = () => { - pageNode.isUnmounted = true; - return oldUnmount.call(app); - }; - return app.mount(pageNode); - } - function createFactory(component) { - return () => { - return setupPage(component); - }; - } + // Force window size for `raw` data, if not set directly, + // because we have no header for autodetect. + if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { + opt.windowBits = -opt.windowBits; + if (opt.windowBits === 0) { opt.windowBits = -15; } + } - function initRouteOptions(path, openType) { - // 需要序列化一遍 - const routeOptions = JSON.parse(JSON.stringify(getRouteOptions(path))); - routeOptions.meta = initRouteMeta(routeOptions.meta); - if (openType === 'reLaunch' || - (!__uniConfig.realEntryPagePath && getCurrentPages().length === 0) // redirectTo - ) { - routeOptions.meta.isQuit = true; - } - else if (!routeOptions.meta.isTabBar) { - routeOptions.meta.isQuit = false; - } - // TODO - // if (routeOptions.meta.isTabBar) { - // routeOptions.meta.visible = true - // } - return routeOptions; - } + // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate + if ((opt.windowBits >= 0) && (opt.windowBits < 16) && + !(options && options.windowBits)) { + opt.windowBits += 32; + } - function getStatusbarHeight() { - // 横屏时 iOS 获取的状态栏高度错误,进行纠正 - return plus.navigator.isImmersedStatusbar() - ? Math.round(plus.os.name === 'iOS' - ? plus.navigator.getSafeAreaInsets().top - : plus.navigator.getStatusbarHeight()) - : 0; - } + // Gzip header has no info about windows size, we can do autodetect only + // for deflate. So, if window size not set, force it to max when gzip possible + if ((opt.windowBits > 15) && (opt.windowBits < 48)) { + // bit 3 (16) -> gzipped data + // bit 4 (32) -> autodetect gzip/deflate + if ((opt.windowBits & 15) === 0) { + opt.windowBits |= 15; + } + } - function registerPage({ url, path, query, openType, webview, vm, }) { - // fast 模式,nvue 首页时,会在nvue中主动调用registerPage并传入首页webview,此时初始化一下首页(因为此时可能还未调用registerApp) - if (webview) { - initEntry(); - } - // TODO preloadWebview - const routeOptions = initRouteOptions(path, openType); - if (!webview) { - webview = createWebview({ path, routeOptions, query }); - } - else { - webview = plus.webview.getWebviewById(webview.id); - webview.nvue = routeOptions.meta.isNVue; - } - routeOptions.meta.id = parseInt(webview.id); - const isTabBar = !!routeOptions.meta.isTabBar; - if (isTabBar) { - tabBar$1.append(webview); - } - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('registerPage', path, webview.id)); - } - initWebview(webview, path, query, routeOptions.meta); - const route = path.substr(1); - webview.__uniapp_route = route; - const pageInstance = initPageInternalInstance(openType, url, query, routeOptions.meta); - if (!webview.nvue) { - createPage(parseInt(webview.id), route, query, pageInstance, initPageOptions(routeOptions)); - } - else { - vm && addCurrentPage(vm); - } - return webview; - } - function initPageOptions({ meta }) { - const statusbarHeight = getStatusbarHeight(); - const { platform, pixelRatio, windowWidth } = getBaseSystemInfo(); - return { - css: true, - route: meta.route, - version: 1, - locale: '', - platform, - pixelRatio, - windowWidth, - disableScroll: meta.disableScroll === true, - onPageScroll: false, - onPageReachBottom: false, - onReachBottomDistance: hasOwn$1(meta, 'onReachBottomDistance') - ? meta.onReachBottomDistance - : ON_REACH_BOTTOM_DISTANCE, - statusbarHeight, - windowTop: meta.navigationBar.type === 'float' ? statusbarHeight + NAVBAR_HEIGHT : 0, - windowBottom: tabBar$1.indexOf(meta.route) >= 0 && tabBar$1.cover ? tabBar$1.height : 0, - }; - } + this.err = 0; // error code, if happens (0 = Z_OK) + this.msg = ''; // error message + this.ended = false; // used to avoid multiple onEnd() calls + this.chunks = []; // chunks of compressed data - const navigateTo = defineAsyncApi(API_NAVIGATE_TO, (args, { resolve, reject }) => { - const { url, animationType, animationDuration } = args; - const { path, query } = parseUrl(url); - const [aniType, aniDuration] = initAnimation(path, animationType, animationDuration); - navigate(path, () => { - _navigateTo({ - url, - path, - query, - aniType, - aniDuration, - }) - .then(resolve) - .catch(reject); - }, args.openType === 'appLaunch'); - }, NavigateToProtocol, NavigateToOptions); - function _navigateTo({ url, path, query, aniType, aniDuration, }) { - // TODO eventChannel - return new Promise((resolve) => { - showWebview(registerPage({ url, path, query, openType: 'navigateTo' }), aniType, aniDuration, () => { - resolve(undefined); - }); - }); - } - function initAnimation(path, animationType, animationDuration) { - const { globalStyle } = __uniConfig; - const meta = getRouteMeta(path); - return [ - animationType || - meta.animationType || - globalStyle.animationType || - ANI_SHOW, - animationDuration || - meta.animationDuration || - globalStyle.animationDuration || - ANI_DURATION, - ]; + this.strm = new zstream(); + this.strm.avail_out = 0; + + let status = inflate_1.inflateInit2( + this.strm, + opt.windowBits + ); + + if (status !== Z_OK$3) { + throw new Error(messages[status]); + } + + this.header = new gzheader(); + + inflate_1.inflateGetHeader(this.strm, this.header); + + // Setup dictionary + if (opt.dictionary) { + // Convert data if needed + if (typeof opt.dictionary === 'string') { + opt.dictionary = strings.string2buf(opt.dictionary); + } else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') { + opt.dictionary = new Uint8Array(opt.dictionary); + } + if (opt.raw) { //In raw mode we need to set the dictionary early + status = inflate_1.inflateSetDictionary(this.strm, opt.dictionary); + if (status !== Z_OK$3) { + throw new Error(messages[status]); + } + } + } } - let lastStatusBarStyle; - let oldSetStatusBarStyle = plus.navigator.setStatusBarStyle; - function newSetStatusBarStyle(style) { - lastStatusBarStyle = style; - oldSetStatusBarStyle(style); - } - plus.navigator.setStatusBarStyle = newSetStatusBarStyle; - function setStatusBarStyle(statusBarStyle) { - if (!statusBarStyle) { - const pages = getCurrentPages(); - if (!pages.length) { - return; - } - statusBarStyle = pages[pages.length - 1].$page - .statusBarStyle; - if (!statusBarStyle || statusBarStyle === lastStatusBarStyle) { - return; - } - } - if (statusBarStyle === lastStatusBarStyle) { - return; - } - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('setStatusBarStyle', statusBarStyle)); - } - lastStatusBarStyle = statusBarStyle; - plus.navigator.setStatusBarStyle(statusBarStyle); + /** + * Inflate#push(data[, flush_mode]) -> Boolean + * - data (Uint8Array|ArrayBuffer): input data + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE + * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, + * `true` means Z_FINISH. + * + * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with + * new output chunks. Returns `true` on success. If end of stream detected, + * [[Inflate#onEnd]] will be called. + * + * `flush_mode` is not needed for normal operation, because end of stream + * detected automatically. You may try to use it for advanced things, but + * this functionality was not tested. + * + * On fail call [[Inflate#onEnd]] with error code and return false. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + **/ + Inflate.prototype.push = function (data, flush_mode) { + const strm = this.strm; + const chunkSize = this.options.chunkSize; + const dictionary = this.options.dictionary; + let status, _flush_mode, last_avail_out; + + if (this.ended) return false; + + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH$3 : Z_NO_FLUSH$2; + + // Convert data if needed + if (toString$1.call(data) === '[object ArrayBuffer]') { + strm.input = new Uint8Array(data); + } else { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + for (;;) { + if (strm.avail_out === 0) { + strm.output = new Uint8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + status = inflate_1.inflate(strm, _flush_mode); + + if (status === Z_NEED_DICT$1 && dictionary) { + status = inflate_1.inflateSetDictionary(strm, dictionary); + + if (status === Z_OK$3) { + status = inflate_1.inflate(strm, _flush_mode); + } else if (status === Z_DATA_ERROR$2) { + // Replace code with more verbose + status = Z_NEED_DICT$1; + } + } + + // Skip snyc markers if more data follows and not raw mode + while (strm.avail_in > 0 && + status === Z_STREAM_END$3 && + strm.state.wrap > 0 && + data[strm.next_in] !== 0) + { + inflate_1.inflateReset(strm); + status = inflate_1.inflate(strm, _flush_mode); + } + + switch (status) { + case Z_STREAM_ERROR$2: + case Z_DATA_ERROR$2: + case Z_NEED_DICT$1: + case Z_MEM_ERROR$1: + this.onEnd(status); + this.ended = true; + return false; + } + + // Remember real `avail_out` value, because we may patch out buffer content + // to align utf8 strings boundaries. + last_avail_out = strm.avail_out; + + if (strm.next_out) { + if (strm.avail_out === 0 || status === Z_STREAM_END$3) { + + if (this.options.to === 'string') { + + let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); + + let tail = strm.next_out - next_out_utf8; + let utf8str = strings.buf2string(strm.output, next_out_utf8); + + // move tail & realign counters + strm.next_out = tail; + strm.avail_out = chunkSize - tail; + if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); + + this.onData(utf8str); + + } else { + this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out)); + } + } + } + + // Must repeat iteration if out buffer is full + if (status === Z_OK$3 && last_avail_out === 0) continue; + + // Finalize if end of stream reached. + if (status === Z_STREAM_END$3) { + status = inflate_1.inflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return true; + } + + if (strm.avail_in === 0) break; + } + + return true; + }; + + + /** + * Inflate#onData(chunk) -> Void + * - chunk (Uint8Array|String): output data. When string output requested, + * each chunk will be string. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + **/ + Inflate.prototype.onData = function (chunk) { + this.chunks.push(chunk); + }; + + + /** + * Inflate#onEnd(status) -> Void + * - status (Number): inflate status. 0 (Z_OK) on success, + * other if not. + * + * Called either after you tell inflate that the input stream is + * complete (Z_FINISH). By default - join collected chunks, + * free memory and fill `results` / `err` properties. + **/ + Inflate.prototype.onEnd = function (status) { + // On success - join + if (status === Z_OK$3) { + if (this.options.to === 'string') { + this.result = this.chunks.join(''); + } else { + this.result = common.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + }; + + + /** + * inflate(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * Decompress `data` with inflate/ungzip and `options`. Autodetect + * format via wrapper header by default. That's why we don't provide + * separate `ungzip` method. + * + * Supported options are: + * + * - windowBits + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * + * ##### Example: + * + * ```javascript + * const pako = require('pako'); + * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9])); + * let output; + * + * try { + * output = pako.inflate(input); + * } catch (err) + * console.log(err); + * } + * ``` + **/ + function inflate$1(input, options) { + const inflator = new Inflate(options); + + inflator.push(input); + + // That will never happens, if you don't cheat with options :) + if (inflator.err) throw inflator.msg || messages[inflator.err]; + + return inflator.result; } - const navigateBack = defineAsyncApi(API_NAVIGATE_BACK, (args, { resolve, reject }) => { - const page = getCurrentPage(); - if (!page) { - return reject(`getCurrentPages is empty`); - } - if (invokeHook(page, 'onBackPress', { - from: args.from, - })) { - return resolve(); - } - uni.hideToast(); - uni.hideLoading(); - if (page.$page.meta.isQuit) { - quit(); - } - else if (page.$page.id === 1 && __uniConfig.realEntryPagePath) { - // condition - __uniConfig.entryPagePath = __uniConfig.realEntryPagePath; - delete __uniConfig.realEntryPagePath; - uni.reLaunch({ - url: '/' + __uniConfig.entryPagePath, - }); - } - else { - const { delta, animationType, animationDuration } = args; - back(delta, animationType, animationDuration); - } - return resolve(); - }, NavigateBackProtocol, NavigateBackOptions); - let firstBackTime = 0; - function quit() { - initI18nAppMsgsOnce(); - if (!firstBackTime) { - firstBackTime = Date.now(); - plus.nativeUI.toast(useI18n().t('uni.app.quit')); - setTimeout(() => { - firstBackTime = 0; - }, 2000); - } - else if (Date.now() - firstBackTime < 2000) { - plus.runtime.quit(); - } - } - function back(delta, animationType, animationDuration) { - const pages = getCurrentPages(); - const len = pages.length; - const currentPage = pages[len - 1]; - if (delta > 1) { - // 中间页隐藏 - pages - .slice(len - delta, len - 1) - .reverse() - .forEach((deltaPage) => { - closeWebview(plus.webview.getWebviewById(deltaPage.$page.id + ''), 'none', 0); - }); - } - const backPage = function (webview) { - if (animationType) { - closeWebview(webview, animationType, animationDuration || ANI_DURATION); - } - else { - if (currentPage.$page.openType === 'redirectTo') { - // 如果是 redirectTo 跳转的,需要制定 back 动画 - closeWebview(webview, ANI_CLOSE, ANI_DURATION); - } - else { - closeWebview(webview, 'auto'); - } - } - pages - .slice(len - delta, len) - .forEach((page) => removePage(page)); - setStatusBarStyle(); - }; - const webview = plus.webview.getWebviewById(currentPage.$page.id + ''); - if (!currentPage.__uniapp_webview) { - return backPage(webview); - } - backWebview(webview, () => { - backPage(webview); - }); + + /** + * inflateRaw(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * The same as [[inflate]], but creates raw data, without wrapper + * (header and adler32 crc). + **/ + function inflateRaw(input, options) { + options = options || {}; + options.raw = true; + return inflate$1(input, options); } - // TODO - // export { - // upx2px, - // addInterceptor, - // removeInterceptor, - // promiseInterceptor, - // arrayBufferToBase64, - // base64ToArrayBuffer, - // createIntersectionObserver, - // createMediaQueryObserver, - // createSelectorQuery, - // createVideoContext, - // createMapContext, - // createAnimation, - // onTabBarMidButtonTap, - // createCanvasContext, - // canvasGetImageData, - // canvasPutImageData, - // canvasToTempFilePath, - // getSelectedTextRange, - // $on, - // $off, - // $once, - // $emit, - // } from '@dcloudio/uni-api' - var uni$1 = /*#__PURE__*/Object.freeze({ - __proto__: null, - setStorageSync: setStorageSync, - setStorage: setStorage, - getStorageSync: getStorageSync, - getStorage: getStorage, - removeStorageSync: removeStorageSync, - removeStorage: removeStorage, - clearStorageSync: clearStorageSync, - clearStorage: clearStorage, - getStorageInfoSync: getStorageInfoSync, - getStorageInfo: getStorageInfo, - getFileInfo: getFileInfo$1, - openDocument: openDocument, - onCompassChange: onCompassChange, - offCompassChange: offCompassChange, - startCompass: startCompass, - stopCompass: stopCompass, - vibrateShort: vibrateShort, - vibrateLong: vibrateLong, - onAccelerometerChange: onAccelerometerChange, - offAccelerometerChange: offAccelerometerChange, - startAccelerometer: startAccelerometer, - stopAccelerometer: stopAccelerometer, - onBluetoothDeviceFound: onBluetoothDeviceFound, - onBluetoothAdapterStateChange: onBluetoothAdapterStateChange, - onBLEConnectionStateChange: onBLEConnectionStateChange, - onBLECharacteristicValueChange: onBLECharacteristicValueChange, - openBluetoothAdapter: openBluetoothAdapter, - closeBluetoothAdapter: closeBluetoothAdapter, - getBluetoothAdapterState: getBluetoothAdapterState, - startBluetoothDevicesDiscovery: startBluetoothDevicesDiscovery, - stopBluetoothDevicesDiscovery: stopBluetoothDevicesDiscovery, - getBluetoothDevices: getBluetoothDevices, - getConnectedBluetoothDevices: getConnectedBluetoothDevices, - createBLEConnection: createBLEConnection, - closeBLEConnection: closeBLEConnection, - getBLEDeviceServices: getBLEDeviceServices, - getBLEDeviceCharacteristics: getBLEDeviceCharacteristics, - notifyBLECharacteristicValueChange: notifyBLECharacteristicValueChange, - readBLECharacteristicValue: readBLECharacteristicValue, - writeBLECharacteristicValue: writeBLECharacteristicValue, - setBLEMTU: setBLEMTU, - getBLEDeviceRSSI: getBLEDeviceRSSI, - onBeaconUpdate: onBeaconUpdate, - onBeaconServiceChange: onBeaconServiceChange, - getBeacons: getBeacons, - startBeaconDiscovery: startBeaconDiscovery, - stopBeaconDiscovery: stopBeaconDiscovery, - makePhoneCall: makePhoneCall, - addPhoneContact: addPhoneContact, - getClipboardData: getClipboardData, - setClipboardData: setClipboardData, - onNetworkStatusChange: onNetworkStatusChange, - offNetworkStatusChange: offNetworkStatusChange, - getNetworkType: getNetworkType, - checkIsSupportSoterAuthentication: checkIsSupportSoterAuthentication, - checkIsSoterEnrolledInDevice: checkIsSoterEnrolledInDevice, - startSoterAuthentication: startSoterAuthentication, - getImageInfo: getImageInfo, - getVideoInfo: getVideoInfo, - previewImage: previewImage, - getRecorderManager: getRecorderManager, - saveVideoToPhotosAlbum: saveVideoToPhotosAlbum, - saveImageToPhotosAlbum: saveImageToPhotosAlbum, - compressImage: compressImage$1, - compressVideo: compressVideo, - chooseImage: chooseImage, - chooseVideo: chooseVideo, - showKeyboard: showKeyboard, - hideKeyboard: hideKeyboard, - downloadFile: downloadFile, - request: request, - connectSocket: connectSocket, - sendSocketMessage: sendSocketMessage, - closeSocket: closeSocket, - onSocketOpen: onSocketOpen, - onSocketError: onSocketError, - onSocketMessage: onSocketMessage, - onSocketClose: onSocketClose, - uploadFile: uploadFile, - createInnerAudioContext: createInnerAudioContext, - getBackgroundAudioManager: getBackgroundAudioManager, - getLocation: getLocation, - showModal: showModal, - showActionSheet: showActionSheet, - showLoading: showLoading, - showToast: showToast, - hideToast: hideToast, - hideLoading: hideLoading, - getProvider: getProvider, - login: login, - getUserInfo: getUserInfo, - getUserProfile: getUserProfile, - preLogin: preLogin, - closeAuthView: closeAuthView, - registerRuntime: registerRuntime, - share: share, - shareWithSystem: shareWithSystem, - requestPayment: requestPayment, - __vuePlugin: __vuePlugin, - createRewardedVideoAd: createRewardedVideoAd, - createFullScreenVideoAd: createFullScreenVideoAd, - createInterstitialAd: createInterstitialAd, - createInteractiveAd: createInteractiveAd, - navigateTo: navigateTo, - navigateBack: navigateBack - }); + /** + * ungzip(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * Just shortcut to [[inflate]], because it autodetects format + * by header.content. Done for convenience. + **/ - let invokeViewMethodId = 0; - const invokeViewMethod = (name, args, callback, pageId) => { - const id = invokeViewMethodId++; - UniServiceJSBridge$1.subscribe(INVOKE_VIEW_API + '.' + id, callback, true); - publishHandler(INVOKE_VIEW_API, { id, name, args }, pageId); - }; - const UniServiceJSBridge$1 = /*#__PURE__*/ extend(ServiceJSBridge, { - publishHandler, - invokeViewMethod, - }); - function publishHandler(event, args, pageIds) { - args = JSON.stringify(args); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('publishHandler', event, args, pageIds)); - } - if (!isArray(pageIds)) { - pageIds = [pageIds]; - } - const evalJSCode = `typeof UniViewJSBridge !== 'undefined' && UniViewJSBridge.subscribeHandler("${event}",${args},__PAGE_ID__)`; - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('publishHandler', 'size', evalJSCode.length)); - } - pageIds.forEach((id) => { - const idStr = String(id); - const webview = plus.webview.getWebviewById(idStr); - webview && webview.evalJS(evalJSCode.replace('__PAGE_ID__', idStr)); - }); - } - var index = { - uni: uni$1, - getApp: getApp$1, - getCurrentPages: getCurrentPages$1, - __definePage: definePage, - __registerApp: registerApp, - __registerPage: registerPage, - UniServiceJSBridge: UniServiceJSBridge$1, + var Inflate_1 = Inflate; + var inflate_2$1 = inflate$1; + var inflateRaw_1 = inflateRaw; + var ungzip = inflate$1; + var constants$2 = constants; + + var inflate_1$1 = { + Inflate: Inflate_1, + inflate: inflate_2$1, + inflateRaw: inflateRaw_1, + ungzip: ungzip, + constants: constants$2 + }; + + const { Deflate: Deflate$1, deflate: deflate$2, deflateRaw: deflateRaw$1, gzip: gzip$1 } = deflate_1$1; + + const { Inflate: Inflate$1, inflate: inflate$2, inflateRaw: inflateRaw$1, ungzip: ungzip$1 } = inflate_1$1; + + + + var Deflate_1$1 = Deflate$1; + var deflate_1$2 = deflate$2; + var deflateRaw_1$1 = deflateRaw$1; + var gzip_1$1 = gzip$1; + var Inflate_1$1 = Inflate$1; + var inflate_1$2 = inflate$2; + var inflateRaw_1$1 = inflateRaw$1; + var ungzip_1 = ungzip$1; + var constants_1 = constants; + + var pako = { + Deflate: Deflate_1$1, + deflate: deflate_1$2, + deflateRaw: deflateRaw_1$1, + gzip: gzip_1$1, + Inflate: Inflate_1$1, + inflate: inflate_1$2, + inflateRaw: inflateRaw_1$1, + ungzip: ungzip_1, + constants: constants_1 }; + var pako_esm = /*#__PURE__*/Object.freeze({ + __proto__: null, + 'default': pako, + Deflate: Deflate_1$1, + Inflate: Inflate_1$1, + constants: constants_1, + deflate: deflate_1$2, + deflateRaw: deflateRaw_1$1, + gzip: gzip_1$1, + inflate: inflate_1$2, + inflateRaw: inflateRaw_1$1, + ungzip: ungzip_1 + }); + return index; }(Vue)); diff --git a/packages/uni-app-plus/dist/uni-app-view.umd.js b/packages/uni-app-plus/dist/uni-app-view.umd.js index a1c96a0a5..dd30fde5e 100644 --- a/packages/uni-app-plus/dist/uni-app-view.umd.js +++ b/packages/uni-app-plus/dist/uni-app-view.umd.js @@ -15494,7 +15494,12 @@ return info; } function findElm(component, pageVm2) { - return component ? component.$el : pageVm2.$el; + if (!component) { + return pageVm2.$el; + } + { + return window.__$__(component).$; + } } function getNodesInfo(pageVm2, component, selector, single, fields) { const parentElement = findElm(component, pageVm2).parentElement; @@ -15551,6 +15556,7 @@ window.uni = uni$1; window.UniViewJSBridge = UniViewJSBridge$1; window.rpx2px = upx2px; + window.__$__ = $; function onWebviewReady() { initView(); initSubscribeHandlers(); diff --git a/packages/uni-app-plus/src/service/api/index.ts b/packages/uni-app-plus/src/service/api/index.ts index 4ba5abcee..c1dcd2e4c 100644 --- a/packages/uni-app-plus/src/service/api/index.ts +++ b/packages/uni-app-plus/src/service/api/index.ts @@ -55,28 +55,27 @@ export * from './ad/interactiveAd' export * from './route/navigateTo' export * from './route/navigateBack' -// TODO -// export { -// upx2px, -// addInterceptor, -// removeInterceptor, -// promiseInterceptor, -// arrayBufferToBase64, -// base64ToArrayBuffer, -// createIntersectionObserver, -// createMediaQueryObserver, -// createSelectorQuery, -// createVideoContext, -// createMapContext, -// createAnimation, -// onTabBarMidButtonTap, -// createCanvasContext, -// canvasGetImageData, -// canvasPutImageData, -// canvasToTempFilePath, -// getSelectedTextRange, -// $on, -// $off, -// $once, -// $emit, -// } from '@dcloudio/uni-api' +export { + upx2px, + addInterceptor, + removeInterceptor, + promiseInterceptor, + arrayBufferToBase64, + base64ToArrayBuffer, + createIntersectionObserver, + createMediaQueryObserver, + createSelectorQuery, + createVideoContext, + createMapContext, + createAnimation, + onTabBarMidButtonTap, + createCanvasContext, + canvasGetImageData, + canvasPutImageData, + canvasToTempFilePath, + getSelectedTextRange, + $on, + $off, + $once, + $emit, +} from '@dcloudio/uni-api' diff --git a/packages/uni-app-plus/src/service/api/ui/requestComponentInfo.ts b/packages/uni-app-plus/src/service/api/ui/requestComponentInfo.ts index aa03a841a..abcb37270 100644 --- a/packages/uni-app-plus/src/service/api/ui/requestComponentInfo.ts +++ b/packages/uni-app-plus/src/service/api/ui/requestComponentInfo.ts @@ -9,7 +9,12 @@ export function requestComponentInfo( UniServiceJSBridge.invokeViewMethod( 'requestComponentInfo', { - reqs, + reqs: reqs.map((req) => { + if (req.component) { + req.component = req.component.$el.nodeId + } + return req + }), }, callback, page.$page.id diff --git a/packages/uni-app-plus/src/service/framework/page/setup.ts b/packages/uni-app-plus/src/service/framework/page/setup.ts index 5ec71af64..a6290266c 100644 --- a/packages/uni-app-plus/src/service/framework/page/setup.ts +++ b/packages/uni-app-plus/src/service/framework/page/setup.ts @@ -16,6 +16,7 @@ export function setupPage(component: VuePageComponent) { const instance = getCurrentInstance()! const pageVm = instance.proxy! pageVm.$page = __pageInstance as Page.PageInstance['$page'] + pageVm.$vm = pageVm addCurrentPage(initScope(__pageId as number, pageVm)) if (oldSetup) { return oldSetup(__pageQuery as any, ctx) diff --git a/packages/uni-app-plus/src/view/index.ts b/packages/uni-app-plus/src/view/index.ts index a28b82a3a..f0a1fd0bd 100644 --- a/packages/uni-app-plus/src/view/index.ts +++ b/packages/uni-app-plus/src/view/index.ts @@ -6,9 +6,11 @@ import { UniViewJSBridge } from './bridge' import * as uni from './api' import { preventDoubleTap } from './framework/gesture' import { initSubscribeHandlers } from './framework/subscriber' +import { $ } from './framework/dom/page' ;(window as any).uni = uni ;(window as any).UniViewJSBridge = UniViewJSBridge ;(window as any).rpx2px = uni.upx2px +;(window as any).__$__ = $ function onWebviewReady() { initView() diff --git a/packages/uni-h5/dist/uni-h5.es.js b/packages/uni-h5/dist/uni-h5.es.js index 756d7e2d4..e19918b0e 100644 --- a/packages/uni-h5/dist/uni-h5.es.js +++ b/packages/uni-h5/dist/uni-h5.es.js @@ -2047,7 +2047,10 @@ function getNodeInfo(el, fields2) { return info; } function findElm(component, pageVm) { - return component ? component.$el : pageVm.$el; + if (!component) { + return pageVm.$el; + } + return component.$el; } function getNodesInfo(pageVm, component, selector, single, fields2) { const parentElement = findElm(component, pageVm).parentElement; diff --git a/packages/uni-h5/src/service/api/ui/requestComponentInfo.ts b/packages/uni-h5/src/service/api/ui/requestComponentInfo.ts index e38391ebf..3d5f0efbf 100644 --- a/packages/uni-h5/src/service/api/ui/requestComponentInfo.ts +++ b/packages/uni-h5/src/service/api/ui/requestComponentInfo.ts @@ -100,7 +100,14 @@ export function findElm( component: ComponentPublicInstance | undefined | null, pageVm: ComponentPublicInstance ): HTMLElement { - return component ? component.$el : pageVm.$el + if (!component) { + return pageVm.$el + } + if (__APP_VIEW__) { + // App 端,传入的是 nodeId + return (window as any).__$__(component).$ + } + return component.$el } function getNodesInfo( -- GitLab