first commit

This commit is contained in:
2023-09-07 00:56:03 +08:00
commit c0ca154d31
718 changed files with 56107 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import { imClient } from "./lim/core/ImClient";
// if(typeof window !== 'undefined'){
// console.log("window");
// }
// if(typeof global !== 'undefined'){
// console.log("global");
// }
// @ts-ignore
if (typeof uni !== 'undefined') {
// console.log("uni");
// @ts-ignore
uni['im'] = imClient;
// @ts-ignore
// uni['im_webtoolkit'] = WebToolkit;
}
export {
imClient as im
// Logger as log,
// WebToolkit as webtoolkit,
};

View File

@@ -0,0 +1,399 @@
export default class ByteBuffer {
Type_Byte:number = 1;
Type_Short:number = 2;
Type_UShort:number = 3;
Type_Int32:number = 4;
Type_UInt32:number = 5;
Type_String:number = 6;//变长字符串,前两个字节表示长度
Type_VString:number = 7;//定长字符串
Type_Int64:number = 8;
Type_Float:number = 9;
Type_Double:number = 10;
Type_ByteArray:number = 11;
_org_buf!:any;
_offset:number = 0
_list:any[] = [];
_littleEndian:boolean = false;
arrayBuf:any;
offset?:number;
constructor(arrayBuf:any,offset?:number){
this._org_buf = arrayBuf ? (arrayBuf.constructor == DataView ? arrayBuf
: (arrayBuf.constructor == Uint8Array ? new DataView(arrayBuf.buffer, offset) : new DataView(arrayBuf, offset))) : new DataView(new Uint8Array([]).buffer);
this.arrayBuf = arrayBuf;
this.offset = offset;
// if (!ArrayBuffer.prototype.slice) {
// ArrayBuffer.prototype.slice = function (start, end) {
// var that = new Uint8Array(this);
// if (end == undefined) end = that.length;
// var result = new ArrayBuffer(end - start);
// var resultArray = new Uint8Array(result);
// for (var i = 0; i < resultArray.length; i++)
// resultArray[i] = that[i + start];
// return result;
// }
// }
}
//指定字节序 为BigEndian
bigEndian() {
this._littleEndian = false;
return this;
};
//指定字节序 为LittleEndian
littleEndianfunction () {
this._littleEndian = true;
return this;
};
utf8Write(view, offset, str) {
var c = 0;
for (var i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i);
if (c < 0x80) {
view.setUint8(offset++, c);
} else if (c < 0x800) {
view.setUint8(offset++, 0xc0 | (c >> 6));
view.setUint8(offset++, 0x80 | (c & 0x3f));
} else if (c < 0xd800 || c >= 0xe000) {
view.setUint8(offset++, 0xe0 | (c >> 12));
view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
view.setUint8(offset++, 0x80 | (c & 0x3f));
} else {
i++;
c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
view.setUint8(offset++, 0xf0 | (c >> 18));
view.setUint8(offset++, 0x80 | (c >> 12) & 0x3f);
view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
view.setUint8(offset++, 0x80 | (c & 0x3f));
}
}
}
// byte (val, index) {
// if (arguments.length == 0) {
// this._list.push(this._org_buf.getUint8(this._offset, this._littleEndian));
// this._offset += 1;
// } else {
// this._list.splice(index != undefined ? index : this._list.length, 0);
// this._offset += 1;
// }
// return this;
// };
// short(val:number, index:number) {
// if (arguments.length == 0) {
// this._list.push(this._org_buf.getInt16(this._offset, this._littleEndian));
// this._offset += 2;
// } else {
// this._list.splice(index != undefined ? index : this._list.length, 0, this.Type_Short);
// this._offset += 2;
// }
// return this;
// };
// ushort(val, index) {
// if (arguments.length == 0) {
// this._list.push(this._org_buf.getUint16(this._offset, this._littleEndian));
// this._offset += 2;
// } else {
// // , {t: this.Type_UShort, d: val, l: 2}
// this._list.splice(index != undefined ? index : this._list.length, 0);
// this._offset += 2;
// }
// return this;
// };
int32(val?, index?) {
if (arguments.length == 0) {
this._list.push(this._org_buf.getInt32(this._offset, this._littleEndian));
this._offset += 4;
}
else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_Int32, d: val, l: 4});
this._offset += 4;
}
return this;
};
uint32(val, index) {
if (arguments.length == 0) {
this._list.push(this._org_buf.getUint32(this._offset, this._littleEndian));
this._offset += 4;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_UInt32, d: val, l: 4});
this._offset += 4;
}
return this;
};
/**
* 新加的方法获取bytebuffer的长度
*/
blength () {
return this._offset;
};
/**
* 变长字符串 前4个字节表示字符串长度
**/
string(val, index) {
if (arguments.length == 0) {
var len:any = this._org_buf.getInt32(this._offset, this._littleEndian);
this._offset += 4;
this._list.push(this.utf8Read(this._org_buf, this._offset, len));
this._offset += len;
} else {
var len:any = 0;
if (val) {
len = this.utf8Length(val);
}
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_String, d: val, l: len});
this._offset += len + 4;
}
return this;
};
utf8Length(str) {
var c = 0, length = 0;
for (var i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i);
if (c < 0x80) {
length += 1;
} else if (c < 0x800) {
length += 2;
} else if (c < 0xd800 || c >= 0xe000) {
length += 3;
} else {
i++;
length += 4;
}
}
return length;
}
utf8Read(view, offset, length) {
var string = '', chr = 0;
for (var i = offset, end = offset + length; i < end; i++) {
var byte = view.getUint8(i);
if ((byte & 0x80) === 0x00) {
string += String.fromCharCode(byte);
continue;
}
if ((byte & 0xe0) === 0xc0) {
string += String.fromCharCode(
((byte & 0x0f) << 6) |
(view.getUint8(++i) & 0x3f)
);
continue;
}
if ((byte & 0xf0) === 0xe0) {
string += String.fromCharCode(
((byte & 0x0f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0)
);
continue;
}
if ((byte & 0xf8) === 0xf0) {
chr = ((byte & 0x07) << 18) |
((view.getUint8(++i) & 0x3f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0);
if (chr >= 0x010000) { // surrogate pair
chr -= 0x010000;
string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
} else {
string += String.fromCharCode(chr);
}
continue;
}
throw new Error('Invalid byte ' + byte.toString(16));
}
return string;
}
/**
* 定长字符串 val为null时读取定长字符串需指定长度len
**/
vstring(val?, len?, index?) {
if (!len) {
throw new Error('vstring must got len argument');
return this;
}
if (val == undefined || val == null) {
var vlen = 0;//实际长度
for (var i = this._offset; i < this._offset + len; i++) {
if (this._org_buf.getUint8(i) > 0) vlen++;
}
this._list.push(this.utf8Read(this._org_buf, this._offset, vlen));
this._offset += len;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_VString, d: val, l: len});
this._offset += len;
}
return this;
};
int64 (val, index) {
if (arguments.length == 0) {
this._list.push(this._org_buf.getFloat64(this._offset, this._littleEndian));
this._offset += 8;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_Int64, d: val, l: 8});
this._offset += 8;
}
return this;
};
float(val, index) {
if (arguments.length == 0) {
this._list.push(this._org_buf.getFloat32(this._offset, this._littleEndian));
this._offset += 4;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_Float, d: val, l: 4});
this._offset += 4;
}
return this;
};
double(val, index) {
if (arguments.length == 0) {
this._list.push(this._org_buf.getFloat64(this._offset, this._littleEndian));
this._offset += 8;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_Double, d: val, l: 8});
this._offset += 8;
}
return this;
};
/**
* 写入或读取一段字节数组
**/
byteArray(val, len, index) {
if (!len) {
throw new Error('byteArray must got len argument');
return this;
}
if (val == undefined || val == null) {
var arr = new Uint8Array(this._org_buf.buffer.slice(this._offset, this._offset + len));
this._list.push(arr);
this._offset += len;
} else {
this._list.splice(index != undefined ? index : this._list.length, 0, {t: this.Type_ByteArray, d: val, l: len});
this._offset += len;
}
return this;
};
/**
* 解包成数据数组
**/
unpack () {
return this._list;
};
/**
* 打包成二进制,在前面加上4个字节表示包长
**/
packWithHead() {
return this.pack(true);
};
/**
* 打包成二进制
* @param ifHead 是否在前面加上4个字节表示包长
**/
pack(ifHead) {
this._org_buf = new DataView(new ArrayBuffer((ifHead) ? this._offset + 4 : this._offset));
var offset = 0;
if (ifHead) {
this._org_buf.setUint32(offset, this._offset, this._littleEndian);
offset += 4;
}
for (var i = 0; i < this._list.length; i++) {
switch (this._list[i].t) {
case this.Type_Byte:
this._org_buf.setInt8(offset, this._list[i].d);
offset += this._list[i].l;
break;
case this.Type_Short:
this._org_buf.setInt16(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_UShort:
this._org_buf.setUint16(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_Int32:
this._org_buf.setInt32(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_UInt32:
this._org_buf.setUint32(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_String:
//前4个字节表示字符串长度
this._org_buf.setUint32(offset, this._list[i].l, this._littleEndian);
offset += 4;
this.utf8Write(this._org_buf, offset, this._list[i].d);
offset += this._list[i].l;
break;
case this.Type_VString:
this.utf8Write(this._org_buf, offset, this._list[i].d);
var vlen = this.utf8Length(this._list[i].d);//字符串实际长度
//补齐\0
for (var j = offset + vlen; j < offset + this._list[i].l; j++) {
this._org_buf.setUint8(j, 0);
}
offset += this._list[i].l;
break;
case this.Type_Int64:
this._org_buf.setFloat64(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_Float:
this._org_buf.setFloat32(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_Double:
this._org_buf.setFloat64(offset, this._list[i].d, this._littleEndian);
offset += this._list[i].l;
break;
case this.Type_ByteArray:
var indx = 0;
for (var j = offset; j < offset + this._list[i].l; j++) {
if (indx < this._list[i].d.length) {
this._org_buf.setUint8(j, this._list[i].d[indx]);
} else {//不够的话后面补齐0x00
this._org_buf.setUint8(j, 0);
}
indx++
}
offset += this._list[i].l;
break;
}
}
return this._org_buf.buffer;
};
/**
* 未读数据长度
**/
getAvailable() {
if (!this._org_buf) return this._offset;
return this._org_buf.buffer.byteLength - this._offset;
};
}

View File

@@ -0,0 +1,184 @@
enum MessageCommand {
MSG_P2P = 0x44F,
MSG_P2P_SYNC = 0x454,
//发送消息已读 1106
MSG_READED = 0x452,
//消息接收ack
MSG_RECIVE_ACK = 1107,
//单聊消息ACK 1046
MSG_ACK = 0x416,
// 消息撤回 1050
MSG_RECALL = 0x41A,
// //消息撤回通知 1052
MSG_RECALL_NOTIFY = 0x41C,
// 消息撤回回包 1051
MSG_RECALL_ACK = 0x41B,
// //消息已读通知 1053
MSG_READED_NOTIFY = 0x41D,
}
enum FriendShipCommand{
//添加好友 3000
FRIEND_ADD = 0xbb8,
//更新好友 3001
FRIEND_UPDATE = 0xbb9,
//删除好友 3002
FRIEND_DELETE = 0xbba,
//好友申请 3003
FRIEND_REQUEST = 0xbbb,
//好友申请已读 3004
FRIEND_REQUEST_READ = 0xbbc,
//好友申请审批 3005
FRIEND_REQUEST_APPROVER = 0xbbd,
//添加黑名单 3010
FRIEND_BLACK_ADD = 0xbc2,
//移除黑名单 3011
FRIEND_BLACK_DELETE = 0xbc3,
//新建好友分组 3012
FRIEND_GROUP_ADD = 0xbc4,
//删除好友分组 3013
FRIEND_GROUP_DELETE = 0xbc5,
//好友分组添加成员 3014
FRIEND_GROUP_MEMBER_ADD = 0xbc6,
//好友分组移除成员 3015
FRIEND_GROUP_MEMBER_DELETE = 0xbc7,
}
enum GroupCommand{
/**
* 推送申请入群通知 2000
*/
JOIN_GROUP = 0x7d0,
/**
* 推送添加群成员 2001通知给所有管理员和本人
*/
ADDED_MEMBER = 0x7d1,
/**
* 推送创建群组通知 2002通知给所有人
*/
CREATED_GROUP = 0x7d2,
/**
* 推送更新群组通知 2003通知给所有人
*/
UPDATED_GROUP = 0x7d3,
/**
* 推送退出群组通知 2004通知给管理员和操作人
*/
EXIT_GROUP = 0x7d4,
/**
* 推送修改群成员通知 2005通知给管理员和被操作人
*/
UPDATED_MEMBER = 0x7d5,
/**
* 推送删除群成员通知 2006通知给所有群成员和被踢人
*/
DELETED_MEMBER = 0x7d6,
/**
* 推送解散群通知 2007通知所有人
*/
DESTROY_GROUP = 0x7d7,
/**
* 推送转让群主 2008通知所有人
*/
TRANSFER_GROUP = 0x7d8,
/**
* 禁言群 2009通知所有人
*/
MUTE_GROUP = 0x7d9,
/**
* 禁言/解禁 群成员 2010通知管理员和被操作人
*/
SPEAK_GOUP_MEMBER = 0x7da,
//群聊消息收发 2104
MSG_GROUP = 0x838,
//群聊消息收发同步消息 2105
MSG_GROUP_SYNC = 0x839,
//群聊消息ACK 2047
GROUP_MSG_ACK = 0x7ff,
}
enum SystemCommand{
//心跳 9999
PING = 0x270f,
//登陸 9000
LOGIN = 0x2328,
//登录ack 9001
LOGINACK = 0x2329,
//下线通知 用于多端互斥 9002
MUTUALLOGIN = 0x232a,
//登出 9003
LOGOUT = 0x232b,
}
enum UserEventCommand{
//4000
USER_MODIFY = 0xfa0,
//4001
USER_ONLINE_STATUS_CHANGE = 0xfa1,
//4002 在线状态订阅
USER_ONLINE_STATUS_SUBSCRIBE = 0xfa2,
//4003 拉取订阅的在线状态好友,只发送给请求端
PULL_USER_ONLINE_STATUS = 0xfa3,
//4004 用户在线状态通知报文
USER_ONLINE_STATUS_CHANGE_NOTIFY = 0xfa4,
}
enum ConversationEventCommand{
//5000 会话删除
CONVERSATION_DELETE = 0x1388,
//5001 会话修改
CONVERSATION_UPDATE = 0x1389,
}
export {
MessageCommand,
FriendShipCommand,
GroupCommand,
SystemCommand,
UserEventCommand,
ConversationEventCommand
};

View File

@@ -0,0 +1,33 @@
/**
* 返回平台所用的 fetch 工具
* */
export default class Fetch {
public static getFetchToolkit():any{
let httpFetch:any;
// @ts-ignore
if(typeof global.fetch !== 'undefined' && typeof global.fetch === 'function'){
// @ts-ignore
httpFetch = global.fetch;
}
else if(typeof fetch === 'function'){
httpFetch = fetch; // RN FETCH
}
else {
/*IFTRUE_WXAPP*/
httpFetch = require("wxapp-fetch");
/*FITRUE_WXAPP*/
/*IFTRUE_WEBAPP*/
httpFetch = require("isomorphic-fetch");
/*FITRUE_WEBAPP*/
/*IFTRUE_UNIAPP*/
let uniFetch = require("../uniapp/http/uni-fetch");
httpFetch = uniFetch.fetch;
/*FITRUE_UNIAPP*/
}
return httpFetch
}
}

View File

@@ -0,0 +1,308 @@
import Fetch from "../common/Fetch";
import Logger from "../log/Logger";
const Base64 = {
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function(e) {
var t = "";
var n, r, i, s, o, u, a;
var f = 0;
e = Base64._utf8_encode(e);
while (f < e.length) {
n = e.charCodeAt(f++);
r = e.charCodeAt(f++);
i = e.charCodeAt(f++);
s = n >> 2;
o = (n & 3) << 4 | r >> 4;
u = (r & 15) << 2 | i >> 6;
a = i & 63;
if (isNaN(r)) {
u = a = 64
} else if (isNaN(i)) {
a = 64
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
}
return t
},
decode: function(e) {
var t = "";
var n, r, i;
var s, o, u, a;
var f = 0;
e = e.replace(/[^A-Za-z0-9+/=]/g, "");
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++));
o = this._keyStr.indexOf(e.charAt(f++));
u = this._keyStr.indexOf(e.charAt(f++));
a = this._keyStr.indexOf(e.charAt(f++));
n = s << 2 | o >> 4;
r = (o & 15) << 4 | u >> 2;
i = (u & 3) << 6 | a;
t = t + String.fromCharCode(n);
if (u != 64) {
t = t + String.fromCharCode(r)
}
if (a != 64) {
t = t + String.fromCharCode(i)
}
}
t = Base64._utf8_decode(t);
return t
},
_utf8_encode: function(e) {
e = e.replace(/rn/g, "n");
var t = "";
for (var n = 0; n < e.length; n++) {
var r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r)
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192);
t += String.fromCharCode(r & 63 | 128)
} else {
t += String.fromCharCode(r >> 12 | 224);
t += String.fromCharCode(r >> 6 & 63 | 128);
t += String.fromCharCode(r & 63 | 128)
}
}
return t
},
_utf8_decode: function(e) {
var t = "";
var n = 0;
var r = 0;
var c1 = 0;
var c2 = 0;
while (n < e.length) {
r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
n++
} else if (r > 191 && r < 224) {
c2 = e.charCodeAt(n + 1);
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
n += 2
} else {
c2 = e.charCodeAt(n + 1);
let c3 = e.charCodeAt(n + 2);
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
n += 3
}
}
return t
}
};
export default class WebToolkit {
// 获取浏览器信息
private static getBrowserInfo(): any {
let agent: any = navigator.userAgent.toLowerCase();
let system = agent.split(" ")[1].split(" ")[0].split("(")[1];
let REGSTR_EDGE = /edge\/[\d.]+/gi;
let REGSTR_IE = /trident\/[\d.]+/gi;
let OLD_IE = /msie\s[\d.]+/gi;
let REGSTR_FF = /firefox\/[\d.]+/gi;
let REGSTR_CHROME = /chrome\/[\d.]+/gi;
let REGSTR_SAF = /safari\/[\d.]+/gi;
let REGSTR_OPERA = /opr\/[\d.]+/gi;
let info = {
code: 0,
system: system,
browser: "",
browserVersion: ""
};
// IE
if (agent.indexOf("trident") > 0) {
info.browser = agent.match(REGSTR_IE)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_IE)[0].split("/")[1];
return info;
}
// OLD_IE
if (agent.indexOf("msie") > 0) {
info.browser = agent.match(OLD_IE)[0].split(" ")[0];
info.browserVersion = agent.match(OLD_IE)[0].split(" ")[1];
return info;
}
// Edge
if (agent.indexOf("edge") > 0) {
info.browser = agent.match(REGSTR_EDGE)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_EDGE)[0].split("/")[1];
return info;
}
// firefox
if (agent.indexOf("firefox") > 0) {
info.browser = agent.match(REGSTR_FF)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_FF)[0].split("/")[1];
return info;
}
// Opera
if (agent.indexOf("opr") > 0) {
info.browser = agent.match(REGSTR_OPERA)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_OPERA)[0].split("/")[1];
return info;
}
// Safari
if (agent.indexOf("safari") > 0 && agent.indexOf("chrome") < 0) {
info.browser = agent.match(REGSTR_SAF)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_SAF)[0].split("/")[1];
return info;
}
// Chrome
if (agent.indexOf("chrome") > 0) {
info.browser = agent.match(REGSTR_CHROME)[0].split("/")[0];
info.browserVersion = agent.match(REGSTR_CHROME)[0].split("/")[1];
return info;
} else {
info.code = -1;
return info;
}
}
// TODO: 获取小程序设备信息
private static getWxappInfo(): any {
return {
system: 'WXAPP',
browser: 'WXAPP',
browserVersion: '1.0'
}
}
// TODO: 获取ReactNative设备信息
private static getReactNativeInfo(): any {
return {
system: 'RNNative',
browser: 'RNNative',
browserVersion: '1.0'
}
}
// TODO: 获取UniApp设备信息
private static getUniAppInfo(): any {
return {
system: 'UNIAPP',
browser: 'UNIAPP',
browserVersion: '1.0'
}
}
// 动态加入script 到head 标签处
private static loadJS(url, callback ){
var script:any = document.createElement('script'), fn = callback || function(){};
script.type = 'text/javascript';
// document.getElementsByTagName('head')[0].children[16].outerHTML.indexOf('http://pv.sohu.com/cityjson?ie=utf-8')
let exist = false;
for(const v in document.getElementsByTagName('head')[0].children){
const dom = document.getElementsByTagName('head')[0].children[v];
if(dom.outerHTML !== undefined && dom.outerHTML.indexOf(url) >= 0){
exist = true;
}
}
if(exist){
fn();
return;
}
//IE
if(script.readyState){
script.onreadystatechange = function(){
if( script.readyState == 'loaded' || script.readyState == 'complete' ){
script.onreadystatechange = null;
fn();
}
};
}else{
//其他浏览器
script.onload = function(){
fn();
};
}
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
// 获取当前ip信息(fetch方式)
private static getIpInfoByFetch(callback) {
const url = 'http://pv.sohu.com/cityjson?ie=utf-8';
let fetch:any = Fetch.getFetchToolkit();
const request: any = {method: "GET", mode: "cors", headers:{"Content-Type":"application/json"}};
return fetch(url as string, request).then(response =>{
return response.text();
}).then(res =>{
if(typeof callback === 'function'){
let currentCity = eval('_current_city=' + res.replace('var returnCitySN = ', ''));
callback(currentCity);
}
}).catch((e) =>{
Logger.trace(e);
});
}
// 获取当前ip信息(动态插入script脚本方式)
private static getIpInfoByInsertScript(callback) {
const url = 'http://pv.sohu.com/cityjson?ie=utf-8';
WebToolkit.loadJS(url, function() {
callback(window['returnCitySN']);
})
}
public static getIpInfo(callback) {
/*IFTRUE_WXAPP*/
// 小程序的情况需要把pv.sohu.com域名加入白名单中
WebToolkit.getIpInfoByFetch(callback);
/*FITRUE_WXAPP*/
/*IFTRUE_WEBAPP*/
WebToolkit.getIpInfoByInsertScript(callback);
/*FITRUE_WEBAPP*/
/*IFTRUE_RNAPP*/
//
WebToolkit.getIpInfoByInsertScript(callback);
/*FITRUE_RNAPP*/
/*IFTRUE_UNIAPP*/
// 小程序的情况需要把pv.sohu.com域名加入白名单中
WebToolkit.getIpInfoByFetch(callback);
/*FITRUE_UNIAPP*/
}
// 获取客户端设备信息
public static getDeviceInfo():any {
/*IFTRUE_WXAPP*/
// 小程序的情况需要把pv.sohu.com域名加入白名单中
const deviceInfo = WebToolkit.getWxappInfo();
/*FITRUE_WXAPP*/
/*IFTRUE_WEBAPP*/
const deviceInfo = WebToolkit.getBrowserInfo();
/*FITRUE_WEBAPP*/
/*IFTRUE_RNAPP*/
const deviceInfo = WebToolkit.getReactNativeInfo();
/*FITRUE_RNAPP*/
/*IFTRUE_UNIAPP*/
const deviceInfo = WebToolkit.getUniAppInfo();
/*FITRUE_UNIAPP*/
return deviceInfo;
}
public static base64Encode(str){
return Base64.encode(str);
}
public static base64Decode(str){
return Base64.decode(str);
}
}

View File

@@ -0,0 +1,61 @@
export default class Beans {
public static to(target:any):any{
return JSON.parse(JSON.stringify(target));
}
public static bean(json:string):any{
return JSON.parse(json);
}
public static copy(target:any):any{
return Beans.to(target);
}
public static replace(target, replaceJSON){
const r = Beans.to(target);
for(const v in replaceJSON){
r[v] = replaceJSON[v];
}
return r;
}
public static toMapByKey(arrs:Array<any>, key:string){
const result = {};
arrs.forEach((v)=>{
if(v.hasOwnProperty(key)){
result[key] = v;
}
});
return result;
}
public static json(target:any):any{
return JSON.stringify(target)
}
public static strEmpty(s:string):boolean{
return !!s;
}
public static strNotEmpty(s:string):boolean{
return !s;
}
public static isEmpty = (str: any): boolean => {
if (
str === null ||
str === '' ||
str === undefined ||
str.length === 0
) {
return true
} else {
return false
}
};
public static uuid = ():string => {
return (Math.random()*36).toString(36).slice(2)+new Date().getTime().toString();
}
}

View File

@@ -0,0 +1,131 @@
import log from "../log/Logger";
import { imClient } from './ImClient';
import ApiResponse from "../model/ApiResponse";
import Beans from "../common/utils";
import Fetch from "../common/Fetch";
export default class HttpApi {
url:string = "";//http://127.0.0.1:8000/v1
constructor(url :string){
this.url = url;
}
call(url:string,params?:any,body?: any): Promise<ApiResponse> {
let userId = imClient.userId;
log.info(userId);
let reqUrl = this.url + url;
if(params != null){
let paramStr = this.setQueryConfig(params)
console.log(paramStr);
reqUrl += paramStr;
}
let requestMethod = "POST";
const headers = {
'Content-Type':'application/json',
};
const request: any = {method: requestMethod,headers:headers, mode: 'cors', body: Beans.json(body)};
return this.httpFetch(reqUrl, request);
}
setQueryConfig(params:any){
  var _str = "?";
  for(const o in params){
  _str += o + "=" + params[o] + "&";
  }
  var _str = _str.substring(0, _str.length-1); //末尾是&
  return _str;
}
private httpFetch(url:string, request:any):Promise<ApiResponse>{
console.log("httpFetch")
/*IFTRUE_WXAPP*/
// @ts-ignore
if(wx === undefined){
throw new Error('wx handle not exist');
}
return new Promise<ApiResponse>(function (resolve, reject) {
// @ts-ignore
wx.request({
method: request.method,
url: url,
data: Beans.bean(request),
header: request.headers,
success (res) {
console.log(res)
resolve(res.data);
},
fail(res){
console.log(res)
reject(res.data);
}
});
});
/*FITRUE_WXAPP*/
/*IFTRUE_WEBAPP*/
let webfetch = Fetch.getFetchToolkit();
console.log("webfetch")
return webfetch(url as string, request).then(response =>{
return response.json();
}).then(res =>{
log.info(`==> [${request.method}] ${url} back:` + Beans.json(res));
const resp = this.response2ApiResponse(res);
console.log(res)
if(resp.isFailed()){
return Promise.reject(resp);
}
return Promise.resolve(this.response2ApiResponse(res));
});
/*FITRUE_WEBAPP*/
/*IFTRUE_RNAPP*/
let rnfetch = Fetch.getFetchToolkit();
console.log("rnfetch")
return rnfetch(url as string, request).then(response =>{
return response.json();
}).then(res =>{
log.info(`==> [${request.method}] ${url} back:` + Beans.json(res));
const resp = this.response2ApiResponse(res);
if(resp.isFailed()){
return Promise.reject(resp);
}
return Promise.resolve(this.response2ApiResponse(res));
});
/*FITRUE_RNAPP*/
/*IFTRUE_UNIAPP*/
let rnfetch1 = Fetch.getFetchToolkit();
return rnfetch1(url as string, request).then(response =>{
console.log(response)
return response.json();
}).then(res =>{
log.info(`==> [${request.method}] ${url} back:` + Beans.json(res));
const resp = this.response2ApiResponse(res);
if(resp.isFailed()){
return Promise.reject(resp);
}
// return Promise.resolve(this.response2ApiResponse(res));
return Promise.resolve(resp);
});
/*FITRUE_UNIAPP*/
}
public response2ApiResponse( response: any): ApiResponse {
const apiResponse: ApiResponse = new ApiResponse(true);
apiResponse.data = response.data;
apiResponse.msg = response.msg;
apiResponse.code = response.code;
return apiResponse;
}
}
// export const httpApi = new HttpApi();

View File

@@ -0,0 +1,422 @@
import log from "../log/Logger";
import ByteBuffer from '../codec/ByteBuffer';
import { LoginPack } from '../pack/LoginPack';
import WebToolkit from '../common/WebToolkit';
import { w3cwebsocket, IMessageEvent, ICloseEvent } from 'websocket';
import { RequestBase } from '../model/RequestBase';
import { RequestParams } from '../model/RequestParams';
import HttpApi from './HttpApi';
import Beans from '../common/utils';
import { reverse } from 'dns';
import {
MessageCommand,
FriendShipCommand,
GroupCommand,
SystemCommand,
UserEventCommand,
ConversationEventCommand
} from '../common/Command';
import { MessagePack } from '../pack/MessagePack';
import { MessageContent } from '../model/dto/MessageContent';
const loginTimeout = 10 * 1000 // 10 seconds
const heartbeatInterval = 10 * 1000 // seconds
let firstMonitorSocket: boolean = false;// 第一次监听socket
export enum State {
INIT,
CONNECTING,
CONNECTED,
RECONNECTING,
CLOSEING,
CLOSED,
}
enum TimeUnit {
Second = 1000,
Millisecond = 1,
}
export let sleep = async (second: number, Unit: TimeUnit = TimeUnit.Second): Promise<void> => {
return new Promise((resolve, _) => {
setTimeout(() => {
resolve()
}, second * Unit)
})
}
export interface IListener {
onLogin(userId: string): void; // 登录成功事件
onSocketConnectEvent(url: string, data: any): void; // socket 连接事件
onSocketErrorEvent(e: any): void;// 异常回调
onSocketReConnectEvent(): void;// 重连事件
onSocketReConnectSuccessEvent(): void;// 重连事件
onSocketCloseEvent(): void;//连接关闭事件
onP2PMessage(e: any): void;//收到单聊消息事件
onTestMessage(e: any): void;//收到消息事件 测试用
// onOfflineMessage(data):void; // 拉取到离线消息事件
}
export class ImClient {
url: string = ""
userId!: string
version: number = 1
clientType: number = 1
imei!: string;
listeners: IListener | any = null;
appId!: number
userSign!: string;
imeiLength?: number
state = State.INIT
// lastOfflineMessageSequence: number = 0;
// offlineMessageList: Array<any> = new Array<any>()
httpUrl: string = ""//http://127.0.0.1:8000/v1
private conn?: w3cwebsocket
constructor() {
}
public getRequestBase(): RequestBase {
return new RequestBase(this.appId, this.clientType, this.imei);
}
public isInit(): boolean {
return this.state == State.CONNECTED;
}
public getRequestParams(): RequestParams {
return new RequestParams(this.appId, this.userId, this.userSign);
}
public async init(httpUrl: string, appId: number, userId: string, userSign: string, listeners: any, callback: (sdk: ImClient) => void) {
var self = this;
self.httpUrl = httpUrl
self.appId = appId;
self.listeners = listeners
self.imei = WebToolkit.getDeviceInfo().system;
self.imeiLength = getLen(self.imei);
self.userId = userId;
this.userSign = userSign
this.imeiLength = self.imeiLength;
if (Beans.isEmpty(this.url)) {
log.info("获取im地址")
let api = new HttpApi(this.httpUrl);
let resp = await api.call("/user/login", {}, { clientType: this.clientType, appId: this.appId, userId: this.userId })
// console.log(resp)
if (resp.isFailed()) {
log.info("登录失败获取im地址失败")
return;
}
let ip = resp.data.ip;
let port = resp.data.port;
this.url = "ws://" + ip + ":" + port + "/ws";
}
var req = new LoginPack(self.appId, self.userId, 1);
let { success, err, conn } = await limLogin(self.url, req, self);
if (success) {
if (!firstMonitorSocket) {
firstMonitorSocket = true;
}
//登录成功以后操作
conn.onerror = (error) => {
log.info("websocket error: ", error)
// 加入socket 连接事件
if (typeof imClient.listeners.onSocketErrorEvent === 'function') {
imClient.listeners.onSocketErrorEvent(error);
}
//异步方法,自动重连
this.errorHandler(error, req)
}
conn.onclose = (e: ICloseEvent) => {
log.info("event[onclose] fired")
if (self.state == State.CLOSEING) {
this.onclose("logout")
return
}
// socket断开事件 连接事件
if (typeof imClient.listeners.onSocketCloseEvent === 'function') {
imClient.listeners.onSocketCloseEvent();
}
// 异步方法 自动重连
this.errorHandler(new Error(e.reason), req)
}
conn.onmessage = (evt) => {
var bytebuf = new ByteBuffer(evt.data);
let byteBuffer = bytebuf.int32().int32().unpack();
let command = byteBuffer[0];//解析command
let bodyLen = byteBuffer[1];//解析bodylen
let unpack = bytebuf.vstring(null, bodyLen).unpack();//解析出字符串
let msgBody = unpack[2];
console.log("sdk收到服务端数据" + msgBody)
if (command === MessageCommand.MSG_P2P) {
//单聊消息收发
if (typeof imClient.listeners.onP2PMessage === 'function') {
imClient.listeners.onP2PMessage(msgBody);
}
} else {
if (typeof imClient.listeners.onTestMessage === 'function') {
imClient.listeners.onTestMessage(msgBody);
}
}
}
this.conn = conn;
this.state = State.CONNECTED
//拉取离线消息
// this.loadOfflineMessage();
//心跳包
this.heartbeatLoop(this.conn);
if (typeof imClient.listeners.onLogin === 'function') {
imClient.listeners.onLogin(this.userId);
}
callback(self);
} else {
log.error(err?.message)
}
}
public buildMessagePack(command: number, messagePack: any) {
var jsonData = JSON.stringify(messagePack);
let bodyLen = getLen(jsonData);
let pack = new ByteBuffer(null, 0);
pack.int32(command).int32(this.version).int32(this.clientType)
.int32(0x0)
.int32(this.appId)
.int32(this.imeiLength)
.int32(bodyLen)
.vstring(this.imei, this.imeiLength)
.vstring(jsonData, bodyLen);
return pack;
}
// 4. 自动重连
private async errorHandler(error: Error, req: LoginPack) {
// 如果是主动断开连接,就没有必要自动重连
// 比如收到被踢或者主动调用logout()方法
if (this.state == State.CLOSED || this.state == State.CLOSEING) {
return
}
this.state = State.RECONNECTING
if (typeof imClient.listeners.onSocketReConnectEvent === 'function') {
imClient.listeners.onSocketReConnectEvent();
}
// 重连10次
for (let index = 0; index < 10; index++) {
await sleep(3)
try {
log.info("try to relogin")
// let { success, err } = await this.login()
let { success, err, conn } = await limLogin(this.url, req, this);
if (success) {
if (typeof imClient.listeners.onSocketReConnectSuccessEvent === 'function') {
imClient.listeners.onSocketReConnectSuccessEvent();
}
return
}
log.info(err)
} catch (error) {
log.info(error)
}
}
this.onclose("reconnect timeout")
}
// 表示连接中止
private onclose(reason: string) {
if (this.state == State.CLOSED) {
return
}
this.state = State.CLOSED
log.info("connection closed due to " + reason)
this.conn = undefined
this.userId = ""
// 加入socket 关闭事件
if (typeof imClient.listeners.onSocketErrorEvent === 'function') {
imClient.listeners.onSocketCloseEvent();
}
}
public getSingleUserInfo(uid: string): Promise<any> {
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/user/data/getSingleUserInfo", this.getRequestParams(), { userId: uid })
resolve(resp);
})
}
public async syncGetUserInfo(userId: string[]) {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/user/data/getUserInfo", this.getRequestParams(), { userIds: userId })
return resp;
}
public getUserInfo(userId: string[]): Promise<any> {
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/user/data/getUserInfo", this.getRequestParams(), { userIds: userId })
resolve(resp);
})
}
public getAllFriend(): Promise<any> {
return new Promise((resolve, _) => {
let api = new HttpApi(this.httpUrl);
let resp = api.call("/friendship/getAllFriendShip", this.getRequestParams(), { fromId: this.userId })
resolve(resp);
})
}
// 2、心跳
private heartbeatLoop(conn) {
let start = Date.now()
let loop = () => {
if (this.state != State.CONNECTED) {
log.error("heartbeatLoop exited")
return
}
if (Date.now() - start >= heartbeatInterval) {
log.info(`>>> send ping ;`)
start = Date.now()
let pingPack = imClient.buildMessagePack(SystemCommand.PING, {});
conn.send(pingPack.pack(false));
}
setTimeout(loop, 500)
}
setTimeout(loop, 500)
}
//构建单聊消息对象
public createP2PTextMessage(to: string, text: string) {
let messagePack = new MessagePack(this.appId);
messagePack.buildTextMessagePack(this.userId, to, text);
return messagePack;
}
public sendP2PMessage(pack: MessagePack) {
let p2pPack = imClient.buildMessagePack(MessageCommand.MSG_P2P, pack);
if (this.conn) {
this.conn.send(p2pPack.pack(false));
}
}
public getUserId() {
return this.userId;
}
// private async loadOfflineMessage() {
// log.info("loadOfflineMessage start")
// let api = new HttpApi(this.httpUrl);
// let resp = await api.call("/message/syncOfflineMessage",this.getRequestParams(),{clientType : this.clientType,appId : this.appId,lastSequence:this.lastOfflineMessageSequence,maxLimit:100})
// if(resp.isSucceed()){
// this.lastOfflineMessageSequence = resp.data.maxSequence;
// let offmessages = resp.data.dataList;
// this.offlineMessageList.push(offmessages)
// if(offmessages.length > 0 && typeof imClient.listeners.onOfflineMessage === 'function'){
// imClient.listeners.onOfflineMessage(offmessages);
// }
// console.log(resp.data.completed)
// if(!resp.data.completed){
// this.loadOfflineMessage();
// }
// }else{
// log.error("loadOfflineMessage - error")
// }
// }
}
export let limLogin = async (url: string, req: LoginPack, imClient: ImClient): Promise<{ success: boolean, err?: Error, conn: w3cwebsocket }> => {
return new Promise((resolve, _) => {
let conn = new w3cwebsocket(url)
conn.binaryType = "arraybuffer"
log.info("limLogin");
// 设置一个登陆超时器
let tr = setTimeout(() => {
clearTimeout(tr)
resolve({ success: false, err: new Error("timeout"), conn: conn });
}, loginTimeout);
conn.onopen = () => {
if (conn.readyState == w3cwebsocket.OPEN) {
// 加入socket 连接事件
if (typeof imClient.listeners.onSocketConnectEvent === 'function') {
imClient.listeners.onSocketConnectEvent(url, req);
}
log.info(`开启连接`);
//登录数据包
var data = {
"userId": req.userId
}
let loginPack = imClient.buildMessagePack(0x2328, data);
conn.send(loginPack.pack(false));
}
}
conn.onerror = (error: Error) => {
clearTimeout(tr)
log.error(error)
resolve({ success: false, err: error, conn: conn });
}
conn.onmessage = (evt) => {
if (typeof evt.data === 'string') {
log.info("Received: '" + evt.data + "'");
return
}
clearTimeout(tr)
var bytebuf = new ByteBuffer(evt.data);
let byteBuffer = bytebuf.int32().int32().unpack();
let command = byteBuffer[0];
let bodyLen = byteBuffer[1];
if (command == 0x2329) {
resolve({ success: true, conn: conn });
}
}
})
}
export let getLen = (str) => {
var len = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
//单字节加1
if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {
len++;
} else {
len += 3;
}
}
return len;
}
export const imClient = new ImClient();

View File

@@ -0,0 +1,38 @@
export default class Logger {
static debug = true;
static info(message?: any, ...optionalParams: any[]): void {
if(Logger.debug){
console.info(`${new Date().toISOString()} : ${message}`, ...optionalParams);
}
}
static infoTag(tag : String, message?: any, ...optionalParams: any[]): void {
if(Logger.debug){
console.info(`${new Date().toISOString()} ${tag} : ${message}`, ...optionalParams);
}
}
static error(message?: any, ...optionalParams: any[]): void {
if(Logger.debug){
console.error(`${new Date().toISOString()} : ${message}`, ...optionalParams);
}
}
static errorTag(tag : String, message?: any, ...optionalParams: any[]): void {
if(Logger.debug){
console.error(`${new Date().toISOString()} ${tag} : ${message}`, ...optionalParams);
}
}
static trace(e: any): void {
if(Logger.debug){
if(e instanceof Error){
console.error(`${e.message} \n ${e.stack !== undefined ? e.stack : ''}`);
}
else {
console.error(e);
}
}
}
}

View File

@@ -0,0 +1,20 @@
export default class ApiResponse {
public code;
public msg = '';
public data: any = null;
constructor(succeed = false) {
if(succeed){
this.code = 200;
}
}
public isSucceed(): boolean {
return this.code === 200;
}
public isFailed(): boolean{
return !this.isSucceed();
}
}

View File

@@ -0,0 +1,10 @@
export class RequestBase {
appId:number;
clientType: number;
imei:string
constructor(appId:number,clientType: number, imei: string) {
this.appId = appId;
this.clientType = clientType;
this.imei = imei;
}
}

View File

@@ -0,0 +1,12 @@
export class RequestParams {
appId:number;
identifier?: string;
userSign:string
constructor(appId:number,identifier:string,userSign:string){
this.appId = appId
this.identifier = identifier
this.userSign = userSign
}
}

View File

@@ -0,0 +1,17 @@
import {Long} from 'long';
import Beans from '../../common/utils';
export class MessageContent {
messageKey:Long;
appId?:number;
messageId?: string;
fromId?: string;
toId?: string;
messageRandom?: number;
messageTime?: number;
messageBody?: string;
constructor(messageKey:Long) {
this.messageKey = messageKey;
}
}

View File

@@ -0,0 +1,10 @@
export class LoginPack {
appId:number;
userId?: string;
clientType?: number;
constructor(appId:number,userId: string, clientType?: number) {
this.userId = userId;
this.clientType = clientType;
this.appId = appId;
}
}

View File

@@ -0,0 +1,39 @@
import Beans from '../common/utils';
export class MessagePack {
appId: number;
messageId: string;
fromId?: string;
toId?: string;
messageRandom?: number;
messageTime?: number;
messageBody?: string;
constructor(appId: number) {
this.messageId = Beans.uuid();
this.appId = appId;
this.messageRandom = this.RangeInteger(0, 10000);
this.messageTime = Date.parse(new Date().toString());
}
RangeInteger(min: number, max: number) {
const range = max - min
const value = Math.floor(Math.random() * range) + min
return value
}
buildTextMessagePack(fromId: string, toId: string, text: string) {
this.fromId = fromId;
this.toId = toId;
let body = { type: 1, content: text }
this.messageBody = Beans.json(body);
}
buildCustomerMessagePack(fromId: string, toId: string, type: number, obj: any) {
this.fromId = fromId;
this.toId = toId;
let body = { type: type, content: obj }
this.messageBody = Beans.json(body);
}
}

View File

@@ -0,0 +1,612 @@
var global =
(typeof globalThis !== 'undefined' && globalThis) ||
(typeof self !== 'undefined' && self) ||
(typeof global !== 'undefined' && global)
var support = {
searchParams: 'URLSearchParams' in global,
iterable: 'Symbol' in global && 'iterator' in Symbol,
blob:
'FileReader' in global &&
'Blob' in global &&
(function() {
try {
new Blob()
return true
} catch (e) {
return false
}
})(),
formData: 'FormData' in global,
arrayBuffer: 'ArrayBuffer' in global
}
function isDataView(obj) {
return obj && DataView.prototype.isPrototypeOf(obj)
}
if (support.arrayBuffer) {
var viewClasses = [
'[object Int8Array]',
'[object Uint8Array]',
'[object Uint8ClampedArray]',
'[object Int16Array]',
'[object Uint16Array]',
'[object Int32Array]',
'[object Uint32Array]',
'[object Float32Array]',
'[object Float64Array]'
]
var isArrayBufferView =
ArrayBuffer.isView ||
function(obj) {
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
}
}
function normalizeName(name) {
if (typeof name !== 'string') {
name = String(name)
}
if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
throw new TypeError('Invalid character in header field name: "' + name + '"')
}
// return name.toLowerCase()
return name;
}
function normalizeValue(value) {
if (typeof value !== 'string') {
value = String(value)
}
return value
}
// Build a destructive iterator for the value list
function iteratorFor(items) {
var iterator = {
next: function() {
var value = items.shift()
return {done: value === undefined, value: value}
}
}
if (support.iterable) {
iterator[Symbol.iterator] = function() {
return iterator
}
}
return iterator
}
export function Headers(headers) {
this.map = {}
if (headers instanceof Headers) {
headers.forEach(function(value, name) {
this.append(name, value)
}, this)
} else if (Array.isArray(headers)) {
headers.forEach(function(header) {
this.append(header[0], header[1])
}, this)
} else if (headers) {
Object.getOwnPropertyNames(headers).forEach(function(name) {
this.append(name, headers[name])
}, this)
}
}
Headers.prototype.append = function(name, value) {
name = normalizeName(name)
value = normalizeValue(value)
var oldValue = this.map[name]
this.map[name] = oldValue ? oldValue + ', ' + value : value
}
Headers.prototype['delete'] = function(name) {
delete this.map[normalizeName(name)]
}
Headers.prototype.get = function(name) {
name = normalizeName(name)
return this.has(name) ? this.map[name] : null
}
Headers.prototype.has = function(name) {
return this.map.hasOwnProperty(normalizeName(name))
}
Headers.prototype.set = function(name, value) {
this.map[normalizeName(name)] = normalizeValue(value)
}
Headers.prototype.forEach = function(callback, thisArg) {
for (var name in this.map) {
if (this.map.hasOwnProperty(name)) {
callback.call(thisArg, this.map[name], name, this)
}
}
}
Headers.prototype.keys = function() {
var items = []
this.forEach(function(value, name) {
items.push(name)
})
return iteratorFor(items)
}
Headers.prototype.values = function() {
var items = []
this.forEach(function(value) {
items.push(value)
})
return iteratorFor(items)
}
Headers.prototype.entries = function() {
var items = []
this.forEach(function(value, name) {
items.push([name, value])
})
return iteratorFor(items)
}
if (support.iterable) {
Headers.prototype[Symbol.iterator] = Headers.prototype.entries
}
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
})
}
function readBlobAsArrayBuffer(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsArrayBuffer(blob)
return promise
}
function readBlobAsText(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsText(blob)
return promise
}
function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf)
var chars = new Array(view.length)
for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i])
}
return chars.join('')
}
function bufferClone(buf) {
if (buf.slice) {
return buf.slice(0)
} else {
var view = new Uint8Array(buf.byteLength)
view.set(new Uint8Array(buf))
return view.buffer
}
}
function Body() {
this.bodyUsed = false
this._initBody = function(body) {
/*
fetch-mock wraps the Response object in an ES6 Proxy to
provide useful test harness features such as flush. However, on
ES5 browsers without fetch or Proxy support pollyfills must be used;
the proxy-pollyfill is unable to proxy an attribute unless it exists
on the object before the Proxy is created. This change ensures
Response.bodyUsed exists on the instance, while maintaining the
semantic of setting Request.bodyUsed in the constructor before
_initBody is called.
*/
this.bodyUsed = this.bodyUsed
this._bodyInit = body
if (!body) {
this._bodyText = ''
} else if (typeof body === 'string') {
this._bodyText = body
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
this._bodyBlob = body
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
this._bodyFormData = body
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this._bodyText = body.toString()
} else if (support.arrayBuffer && support.blob && isDataView(body)) {
this._bodyArrayBuffer = bufferClone(body.buffer)
// IE 10-11 can't handle a DataView body.
this._bodyInit = new Blob([this._bodyArrayBuffer])
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
this._bodyArrayBuffer = bufferClone(body)
} else {
this._bodyText = body = Object.prototype.toString.call(body)
}
if (!this.headers.get('Content-Type')) {
if (typeof body === 'string') {
this.headers.set('Content-Type', 'text/plain;charset=UTF-8')
} else if (this._bodyBlob && this._bodyBlob.type) {
this.headers.set('Content-Type', this._bodyBlob.type)
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this.headers.set('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8')
}
}
}
if (support.blob) {
this.blob = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return Promise.resolve(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(new Blob([this._bodyArrayBuffer]))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as blob')
} else {
return Promise.resolve(new Blob([this._bodyText]))
}
}
this.arrayBuffer = function() {
if (this._bodyArrayBuffer) {
var isConsumed = consumed(this)
if (isConsumed) {
return isConsumed
}
if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
return Promise.resolve(
this._bodyArrayBuffer.buffer.slice(
this._bodyArrayBuffer.byteOffset,
this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength
)
)
} else {
return Promise.resolve(this._bodyArrayBuffer)
}
} else {
return this.blob().then(readBlobAsArrayBuffer)
}
}
}
this.text = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return readBlobAsText(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as text')
} else {
return Promise.resolve(this._bodyText)
}
}
if (support.formData) {
this.formData = function() {
return this.text().then(decode)
}
}
this.json = function() {
return this.text().then(JSON.parse)
}
return this
}
// HTTP methods whose capitalization should be normalized
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
function normalizeMethod(method) {
var upcased = method.toUpperCase()
return methods.indexOf(upcased) > -1 ? upcased : method
}
export function Request(input, options) {
if (!(this instanceof Request)) {
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
}
options = options || {}
var body = options.body
if (input instanceof Request) {
if (input.bodyUsed) {
throw new TypeError('Already read')
}
this.url = input.url
this.credentials = input.credentials
if (!options.headers) {
this.headers = new Headers(input.headers)
}
this.method = input.method
this.mode = input.mode
this.signal = input.signal
if (!body && input._bodyInit != null) {
body = input._bodyInit
input.bodyUsed = true
}
} else {
this.url = String(input)
}
this.credentials = options.credentials || this.credentials || 'same-origin'
if (options.headers || !this.headers) {
this.headers = new Headers(options.headers)
}
this.method = normalizeMethod(options.method || this.method || 'GET')
this.mode = options.mode || this.mode || null
this.signal = options.signal || this.signal
this.referrer = null
if ((this.method === 'GET' || this.method === 'HEAD') && body) {
throw new TypeError('Body not allowed for GET or HEAD requests')
}
this._initBody(body)
if (this.method === 'GET' || this.method === 'HEAD') {
if (options.cache === 'no-store' || options.cache === 'no-cache') {
// Search for a '_' parameter in the query string
var reParamSearch = /([?&])_=[^&]*/
if (reParamSearch.test(this.url)) {
// If it already exists then set the value with the current time
this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime())
} else {
// Otherwise add a new '_' parameter to the end with the current time
var reQueryString = /\?/
this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime()
}
}
}
}
Request.prototype.clone = function() {
return new Request(this, {body: this._bodyInit})
}
function decode(body) {
var form = new FormData()
body
.trim()
.split('&')
.forEach(function(bytes) {
if (bytes) {
var split = bytes.split('=')
var name = split.shift().replace(/\+/g, ' ')
var value = split.join('=').replace(/\+/g, ' ')
form.append(decodeURIComponent(name), decodeURIComponent(value))
}
})
return form
}
function parseHeaders(rawHeaders) {
var headers = new Headers()
// Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
// https://tools.ietf.org/html/rfc7230#section-3.2
var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
// Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill
// https://github.com/github/fetch/issues/748
// https://github.com/zloirock/core-js/issues/751
preProcessedHeaders
.split('\r')
.map(function(header) {
return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header
})
.forEach(function(line) {
var parts = line.split(':')
var key = parts.shift().trim()
if (key) {
var value = parts.join(':').trim()
headers.append(key, value)
}
})
return headers
}
function parseUniappHeaders(rawHeaders) {
var headers = new Headers()
for(var key in rawHeaders){
headers.append(key, rawHeaders[key])
}
return headers
}
Body.call(Request.prototype)
export function Response(bodyInit, options) {
if (!(this instanceof Response)) {
throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.')
}
if (!options) {
options = {}
}
this.type = 'default'
this.status = options.status === undefined ? 200 : options.status
this.ok = this.status >= 200 && this.status < 300
this.statusText = options.statusText === undefined ? '' : '' + options.statusText
this.headers = new Headers(options.headers)
this.url = options.url || ''
this._initBody(bodyInit)
}
Body.call(Response.prototype)
Response.prototype.clone = function() {
return new Response(this._bodyInit, {
status: this.status,
statusText: this.statusText,
headers: new Headers(this.headers),
url: this.url
})
}
Response.error = function() {
var response = new Response(null, {status: 0, statusText: ''})
response.type = 'error'
return response
}
var redirectStatuses = [301, 302, 303, 307, 308]
Response.redirect = function(url, status) {
if (redirectStatuses.indexOf(status) === -1) {
throw new RangeError('Invalid status code')
}
return new Response(null, {status: status, headers: {location: url}})
}
export var DOMException = global.DOMException
try {
new DOMException()
} catch (err) {
DOMException = function(message, name) {
this.message = message
this.name = name
var error = Error(message)
this.stack = error.stack
}
DOMException.prototype = Object.create(Error.prototype)
DOMException.prototype.constructor = DOMException
}
// 从 whatwg-fetch 项目改写
export function fetch(input, init) {
return new Promise(function(resolve, reject) {
var request = new Request(input, init)
if (request.signal && request.signal.aborted) {
return reject(new DOMException('Aborted', 'AbortError'))
}
function fixUrl(url) {
try {
return url === '' && global.location.href ? global.location.href : url
} catch (e) {
return url
}
}
function isJson(json) {
try {
if (typeof json == "object") {
return true;
}
if (typeof json == "string") {
if (typeof JSON.parse(str) == "object") {
return true;
}
}
} catch(e) {
}
return false;
}
function toStr(json) {
try {
if (typeof json == "object") {
return JSON.stringify(json);
}
if (typeof json == "string") {
return json;
}
} catch(e) {
}
return json.toString();
}
let rawBody = typeof request._bodyInit === 'undefined' ? null : request._bodyInit;
if(request.headers.hasOwnProperty('Content-Type') && request.headers['Content-Type'].indexOf('application/json') >= 0){
if(request.method.toUpperCase() === 'POST'){
rawBody = JSON.parse(rawBody); // uniapp框架遇到 method = 'POST' 且 Content-Type = 'application/json' 会自动序列化
}
}
const requestTask = uni.request({
url: fixUrl(request.url),
method: request.method,
data: rawBody,
header: request.headers.map,
complete: (resp)=> {
// Logger.info(resp);
if (request.signal) {
request.signal.removeEventListener('abort', abortXhr);
}
if(resp.errMsg.indexOf('fail') >= 0){
setTimeout(function() {
reject(new TypeError('Network request failed'))
}, 0);
return;
}
// if(resp.statusCode !== 200){
// setTimeout(function() {
// reject(new TypeError('Network request failed, statusCode:' + resp.statusCode + " full text:" + JSON.stringify(resp.data)))
// }, 0)
// return;
// }
var options = {
status: resp.statusCode,
statusText: resp.data ? toStr(resp.data) : '',
headers: parseUniappHeaders(resp.header || {})
};
options.url = options.headers.get('X-Request-URL')
var body = options.statusText;
setTimeout(function() {
resolve(new Response(body, options))
}, 0)
}
});
function abortXhr() {
requestTask.abort()
}
if (request.signal) {
request.signal.addEventListener('abort', abortXhr)
}
})
}
fetch.polyfill = true
if (!global.fetch) {
global.fetch = fetch
global.Headers = Headers
global.Request = Request
global.Response = Response
}