efront 3.31.2 → 3.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/coms/basic/getIndexFromOrderedArray_test.js +11 -13
- package/coms/{zimoli → basic}/isNode.js +0 -0
- package/coms/basic/removeFromList.js +7 -5
- package/coms/basic/removeFromList_test.js +3 -0
- package/coms/basic/saveToOrderedArray_test.js +25 -0
- package/coms/{zimoli → basic}/search.js +0 -0
- package/coms/basic/serialize.js +4 -4
- package/coms/frame/chat.html +33 -4
- package/coms/frame/chat.js +202 -24
- package/coms/frame/chat.less +255 -13
- package/coms/kugou/loading.less +1 -1
- package/coms/reptile/colored_console.js +205 -29
- package/coms/reptile/colored_console_test.js +57 -0
- package/coms/shapes/file.html +1 -2
- package/coms/zimoli/autofocus.js +2 -2
- package/coms/zimoli/chooseFile.js +1 -1
- package/coms/zimoli/drag.js +2 -1
- package/coms/zimoli/loading.less +1 -1
- package/coms/zimoli/maps.js +0 -1
- package/coms/zimoli/render.js +3 -1
- package/coms/zimoli/resize.js +7 -0
- package/package.json +1 -1
- package/public/efront.js +1 -1
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
throw new Error("expect getIndexFromOrderedArray([1,2,3],2) to be equal 1");
|
|
13
|
-
}
|
|
1
|
+
if (getIndexFromOrderedArray([0, 0, 0], 0) !== 2) {
|
|
2
|
+
throw new Error("expect getIndexFromOrderedArray([0,0,0],0) to be equal 2");
|
|
3
|
+
}
|
|
4
|
+
if (getIndexFromOrderedArray([0, 1, 2], 0) !== 0) {
|
|
5
|
+
throw new Error("expect getIndexFromOrderedArray([0,1,2],0) to be equal 0");
|
|
6
|
+
}
|
|
7
|
+
if (getIndexFromOrderedArray([1, 2, 3], 0) !== -1) {
|
|
8
|
+
throw new Error("expect getIndexFromOrderedArray([1,2,3],0) to be equal -1");
|
|
9
|
+
}
|
|
10
|
+
if (getIndexFromOrderedArray([1, 2, 3], 2) !== 1) {
|
|
11
|
+
throw new Error("expect getIndexFromOrderedArray([1,2,3],2) to be equal 1");
|
|
14
12
|
}
|
|
File without changes
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
function removeFromList(list, item) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
var count = 0;
|
|
3
|
+
var cx = list.indexOf(item);
|
|
4
|
+
while (cx >= 0) {
|
|
5
|
+
list.splice(cx, 1);
|
|
6
|
+
count++;
|
|
7
|
+
cx = list.indexOf(item, cx);
|
|
6
8
|
}
|
|
7
|
-
return
|
|
9
|
+
return count;
|
|
8
10
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
var float = function () {
|
|
2
|
+
return Math.random();
|
|
3
|
+
};
|
|
4
|
+
var int = function () {
|
|
5
|
+
return Math.random() * 10 | 0;
|
|
6
|
+
};
|
|
7
|
+
var str = function () {
|
|
8
|
+
return Math.random().toString(36).slice(2, 3);
|
|
9
|
+
}
|
|
10
|
+
var test = function (gen, isle, sort = isle) {
|
|
11
|
+
var array = [];
|
|
12
|
+
for (var cx = 0, dx = 10; cx < dx; cx++)saveToOrderedArray(array, gen(), isle);
|
|
13
|
+
var res = array.slice();
|
|
14
|
+
assert(res.join(','), array.sort(sort).join(','));
|
|
15
|
+
console.log(res)
|
|
16
|
+
}
|
|
17
|
+
test(float, undefined, (a, b) => a - b);
|
|
18
|
+
test(float, (a, b) => a >= b, (a, b) => b - a);
|
|
19
|
+
test(float, (a, b) => a > b, (a, b) => b - a);
|
|
20
|
+
test(int, (a, b) => a <= b, (a, b) => a - b);
|
|
21
|
+
test(int, (a, b) => a > b, (a, b) => b - a);
|
|
22
|
+
test(int, (a, b) => a >= b, (a, b) => b - a);
|
|
23
|
+
test(str, (a, b) => a > b);
|
|
24
|
+
test(str, (a, b) => a <= b);
|
|
25
|
+
|
|
File without changes
|
package/coms/basic/serialize.js
CHANGED
|
@@ -4,15 +4,15 @@ function serialize(map) {
|
|
|
4
4
|
for (var cx = 1, dx = arguments.length; cx < dx; cx++) {
|
|
5
5
|
var a = arguments[cx];
|
|
6
6
|
if (typeof a === 'string') {
|
|
7
|
-
if (
|
|
8
|
-
else if (
|
|
7
|
+
if (spliter === undefined) spliter = a;
|
|
8
|
+
else if (equals === undefined) equals = a;
|
|
9
9
|
}
|
|
10
10
|
else {
|
|
11
11
|
encode = a;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
14
|
+
if (spliter === undefined) spliter = '&';
|
|
15
|
+
if (equals === undefined) equals = '=';
|
|
16
16
|
if (encode === void 0) encode = spliter === "&" && equals === "=" ? encodeURIComponent : trim;
|
|
17
17
|
var result = [];
|
|
18
18
|
for (var k in map) {
|
package/coms/frame/chat.html
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
-
<div head>
|
|
2
|
-
<
|
|
1
|
+
<div head mount>
|
|
2
|
+
<btn -if="users.length>0" class="menubtn" @click="showList=!showList" type_="showList?'default':'white'">
|
|
3
|
+
<i></i>
|
|
4
|
+
<i></i>
|
|
5
|
+
<i></i>
|
|
6
|
+
</btn>
|
|
7
|
+
<template -if="user">
|
|
8
|
+
<span -bind="user.name"></span>
|
|
9
|
+
<span class="id">(<span -bind="user.id"></span>)</span>
|
|
3
10
|
</template>
|
|
11
|
+
<template -elseif="title" -src="title"> </template>
|
|
4
12
|
<close @click="remove()"></close>
|
|
5
13
|
</div>
|
|
14
|
+
<list -if="users.length>0" -src="u in users">
|
|
15
|
+
<btn class="user" @click="user=u" type_="u===user?'default':'white'">
|
|
16
|
+
<avatar -if="user.icon" -style="{'background-image':'url(\'icons/'+u.icon+'.ico\')'}"></avatar>
|
|
17
|
+
<avatar -else>
|
|
18
|
+
</avatar>
|
|
19
|
+
<span -text="u.name"></span>
|
|
20
|
+
<div class="unread" -if="u.msgread<u.msglist?.length">
|
|
21
|
+
<span -bind="u.msglist.length-u.msgread"></span>
|
|
22
|
+
</div>
|
|
23
|
+
<div>
|
|
24
|
+
<span class="id" -text="u.id"></span>
|
|
25
|
+
</div>
|
|
26
|
+
<span class="local" -if="u.id===localid">本机</span>
|
|
27
|
+
</btn>
|
|
28
|
+
</list>
|
|
6
29
|
<grid #body disabled>
|
|
7
30
|
<chat -src="m in msglist">
|
|
8
31
|
<padding>
|
|
@@ -11,9 +34,15 @@
|
|
|
11
34
|
</padding>
|
|
12
35
|
</chat>
|
|
13
36
|
<div textarea @mousedown="this.firstChild.focus()">
|
|
14
|
-
<div @keydown.alt.enter="send()" contenteditable="true" -model="text"></div>
|
|
37
|
+
<div @keydown.alt.enter="send('html',text)" contenteditable="true" -model="text"></div>
|
|
15
38
|
</div>
|
|
16
39
|
</grid>
|
|
17
40
|
<div foot>
|
|
18
|
-
<
|
|
41
|
+
<a class="file" @click="chooseFile()" -if="user">
|
|
42
|
+
<template -src="fileIcon"></template>
|
|
43
|
+
<span>
|
|
44
|
+
文件
|
|
45
|
+
</span>
|
|
46
|
+
</a>
|
|
47
|
+
<a @click="send('html',text);">发送</a>
|
|
19
48
|
</div>
|
package/coms/frame/chat.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
function clickfile(event) {
|
|
2
|
+
var target = getTargetIn(this, event.target, false);
|
|
3
|
+
var children = this.children;
|
|
4
|
+
for (var cx = 0, dx = children.length; cx < dx; cx++) {
|
|
5
|
+
var c = children[cx];
|
|
6
|
+
if (c === target) {
|
|
7
|
+
break;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
this.$eval(`pullFile(m.content[${cx}])`);
|
|
11
|
+
}
|
|
1
12
|
|
|
2
|
-
function msg(elem, { m
|
|
3
|
-
if (!data) return;
|
|
4
|
-
data = encode62.timedecode(data);
|
|
5
|
-
var m = JSAM.parse(data);
|
|
13
|
+
function msg(elem, { m }, parentScopes) {
|
|
6
14
|
if (m.sender === parentScopes[parentScopes.length - 1].localid) {
|
|
7
15
|
elem.setAttribute("self", "");
|
|
8
16
|
}
|
|
@@ -10,61 +18,217 @@ function msg(elem, { m: data }, parentScopes) {
|
|
|
10
18
|
case "html":
|
|
11
19
|
elem.innerHTML = m.content;
|
|
12
20
|
break;
|
|
21
|
+
case "file":
|
|
22
|
+
var files = m.content;
|
|
23
|
+
elem.setAttribute("files", '');
|
|
24
|
+
elem.innerHTML = files.map(f => `<a class=file>${shapes$file}${f.name}</a>`).join("");
|
|
25
|
+
elem.files = files;
|
|
26
|
+
onclick(elem, clickfile);
|
|
27
|
+
break;
|
|
13
28
|
default:
|
|
14
|
-
elem.innerText =
|
|
29
|
+
elem.innerText = m.content;
|
|
15
30
|
}
|
|
16
31
|
}
|
|
17
|
-
var userManager = function (users,
|
|
32
|
+
var userManager = function (users, map) {
|
|
18
33
|
for (var cx = 0, dx = users.length; cx < dx; cx++) {
|
|
19
34
|
var u = users[cx];
|
|
20
|
-
if (u.id
|
|
35
|
+
if (u.id in map) {
|
|
36
|
+
var m = map[u.id];
|
|
21
37
|
if (m.deleted) users.splice(cx, 1);
|
|
22
38
|
else Object.assign(u, m);
|
|
39
|
+
delete map[u.id];
|
|
23
40
|
return;
|
|
24
41
|
}
|
|
25
42
|
}
|
|
26
|
-
|
|
43
|
+
var ms = Object.keys(map).map(k => map[k]);
|
|
44
|
+
for (var u of ms) u.msgread = 0;
|
|
45
|
+
users.push.apply(users, ms);
|
|
27
46
|
};
|
|
47
|
+
var saved_event, moving = null;
|
|
48
|
+
var dragpage = {
|
|
49
|
+
start(event) {
|
|
50
|
+
moving = null;
|
|
51
|
+
saved_event = null;
|
|
52
|
+
if (getTargetIn(a => /^(msg)$/i.test(a.tagName), event.target)) return;
|
|
53
|
+
if (!this.$scope.users.length) return;
|
|
54
|
+
saved_event = event;
|
|
55
|
+
},
|
|
56
|
+
move(event) {
|
|
57
|
+
if (!saved_event) return;
|
|
58
|
+
if (event.moveLocked) return;
|
|
59
|
+
var target = this;
|
|
60
|
+
if (target.hasAttribute('resizing') || target.hasAttribute("dragging")) {
|
|
61
|
+
saved_event = null;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
var deltaX = saved_event.clientX - event.clientX;
|
|
65
|
+
var deltaY = saved_event.clientY - event.clientY;
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
if (!moving) {
|
|
68
|
+
if (!onclick.preventClick) return;
|
|
69
|
+
if (Math.abs(deltaY) * 1.5 >= Math.abs(deltaX)) {
|
|
70
|
+
saved_event = null;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
moving = {
|
|
74
|
+
restX: parseFloat(getComputedStyle(target).paddingLeft) - saved_event.clientX,
|
|
75
|
+
}
|
|
76
|
+
target.style.transition = 'none';
|
|
77
|
+
}
|
|
78
|
+
event.moveLocked = true;
|
|
79
|
+
moving.deltaX = deltaX;
|
|
80
|
+
var left = event.clientX + moving.restX;
|
|
81
|
+
if (left < 0) left = 0;
|
|
82
|
+
var menuWidth = target.children[1].offsetWidth;
|
|
83
|
+
if (left > menuWidth) left = menuWidth;
|
|
84
|
+
target.style.paddingLeft = fromOffset(left);
|
|
85
|
+
},
|
|
86
|
+
end() {
|
|
87
|
+
if (!moving) return;
|
|
88
|
+
var target = this;
|
|
89
|
+
target.style.transition = "";
|
|
90
|
+
var left = freeOffset(target.style.paddingLeft);
|
|
91
|
+
var menuWidth = target.children[1].offsetWidth;
|
|
92
|
+
target.style.paddingLeft = '';
|
|
93
|
+
if (moving.deltaX < 0 && left > menuWidth * .1 || moving.deltaX > 0 && left > menuWidth * .9 || !moving.deltaX && left > menuWidth >> 1) {
|
|
94
|
+
target.$scope.showList = true;
|
|
95
|
+
addClass(target, "showList")
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
target.$scope.showList = false;
|
|
99
|
+
removeClass(target, "showList")
|
|
100
|
+
}
|
|
101
|
+
render.refresh();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
28
104
|
function chat(title = '会话窗口') {
|
|
29
105
|
var page = view();
|
|
30
106
|
page.innerHTML = template;
|
|
31
107
|
drag.on(page.firstElementChild, page);
|
|
32
108
|
resize.on(page);
|
|
33
|
-
var localid = Date.now() + Math.sin(Math.random());
|
|
109
|
+
var localid = title.id || (Date.now() / 1000 | 0) + Math.sin(Math.random());
|
|
34
110
|
var users = [];
|
|
111
|
+
var addToMsgList = function (list, msgs) {
|
|
112
|
+
list.push.apply(list, msgs);
|
|
113
|
+
if (list === page.$scope.msglist) {
|
|
114
|
+
var chat = page.querySelector("chat");
|
|
115
|
+
var lastmsg = chat.getLastVisibleElement();
|
|
116
|
+
if (msgs.length && (!lastmsg || lastmsg.offsetTop + lastmsg.offsetHeight === chat.scrollHeight)) {
|
|
117
|
+
chat.go(list.length ? list.length - 1 : 0);
|
|
118
|
+
}
|
|
119
|
+
if (page.$scope.user) page.$scope.user.msgread = list.length;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
page.$scope.totalunread += msgs.length;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
35
125
|
page.push = function (msgs) {
|
|
36
126
|
var { msglist } = this.$scope;
|
|
127
|
+
var userMap = null;
|
|
37
128
|
msgs = msgs.filter(m => {
|
|
38
129
|
if (!m) return false;
|
|
39
130
|
if (isString(m)) return true;
|
|
40
131
|
switch (m.type) {
|
|
41
132
|
case 'user':
|
|
42
|
-
|
|
133
|
+
if (!userMap) userMap = Object.create(null);
|
|
134
|
+
if (!m.icon) m.icon = m.id.replace(/[\.\d]+$/, '');
|
|
135
|
+
userMap[m.id] = m;
|
|
43
136
|
break;
|
|
44
137
|
}
|
|
45
138
|
return false;
|
|
139
|
+
}).map(m => JSAM.parse(encode62.timedecode(m))).filter(m => {
|
|
140
|
+
if (m.type === 'accept') {
|
|
141
|
+
page.$scope.pushFile(m.content);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
46
145
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
146
|
+
if (userMap) {
|
|
147
|
+
userManager(users, userMap);
|
|
148
|
+
if (users.indexOf(page.$scope.user) < 0) page.$scope.user = users[0];
|
|
149
|
+
if (users.length > 0 && page.$scope.showList === 0) page.$scope.showList = true;
|
|
150
|
+
}
|
|
151
|
+
if (msgs.length) {
|
|
152
|
+
var msgMap = Object.create(null);
|
|
153
|
+
for (var m of msgs) {
|
|
154
|
+
var { sender } = m;
|
|
155
|
+
if (sender) {
|
|
156
|
+
if (!msgMap[sender]) msgMap[sender] = [];
|
|
157
|
+
msgMap[sender].push(m);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (users.length) for (var u of users) {
|
|
161
|
+
if (u.id in msgMap) {
|
|
162
|
+
if (!u.msglist) u.msglist = [];
|
|
163
|
+
addToMsgList(u.msglist, msgMap[u.id]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
addToMsgList(msglist, msgs);
|
|
168
|
+
}
|
|
52
169
|
}
|
|
53
170
|
};
|
|
54
171
|
page.renders = [function () {
|
|
55
172
|
this.$scope.resize(this.$scope.body);
|
|
56
173
|
}];
|
|
174
|
+
page.localid = localid;
|
|
175
|
+
Object.defineProperty(page, 'userid', {
|
|
176
|
+
get() {
|
|
177
|
+
var user = this.$scope.user;
|
|
178
|
+
if (user) return user.id;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
page.setAttribute('ng-class', "{showList:showList}");
|
|
182
|
+
var fid = 0;
|
|
183
|
+
var filesMap = Object.create(null);
|
|
57
184
|
renderWithDefaults(page, {
|
|
58
185
|
chat: list,
|
|
59
186
|
title,
|
|
60
187
|
msglist: [],
|
|
188
|
+
showList: 0,
|
|
61
189
|
users,
|
|
62
190
|
text: '',
|
|
63
191
|
localid,
|
|
192
|
+
totalunread: 0,
|
|
193
|
+
_user: null,
|
|
194
|
+
fileIcon: shapes$file,
|
|
195
|
+
set user(v) {
|
|
196
|
+
if (!v.msglist) v.msglist = []
|
|
197
|
+
if (v.msgread !== v.msglist.length) {
|
|
198
|
+
v.msgread = v.msglist.length;
|
|
199
|
+
this.totalunread -= v.msglist.length - v.msgread;
|
|
200
|
+
}
|
|
201
|
+
this.msglist = v.msglist;
|
|
202
|
+
this._user = v;
|
|
203
|
+
},
|
|
204
|
+
get user() {
|
|
205
|
+
return this._user;
|
|
206
|
+
},
|
|
64
207
|
msg,
|
|
65
208
|
remove() {
|
|
66
209
|
remove(page);
|
|
67
210
|
},
|
|
211
|
+
async pullFile(f) {
|
|
212
|
+
cast(page, 'pullfile', f);
|
|
213
|
+
},
|
|
214
|
+
async pushFile(msg) {
|
|
215
|
+
cast(page, 'pushfile', [msg.channel, filesMap[msg.file]]);
|
|
216
|
+
},
|
|
217
|
+
async chooseFile() {
|
|
218
|
+
/**
|
|
219
|
+
* @type {[:File]}
|
|
220
|
+
*/
|
|
221
|
+
var files = await chooseFile('*.*', true);
|
|
222
|
+
this.sendFiles(files);
|
|
223
|
+
},
|
|
224
|
+
sendFiles(files) {
|
|
225
|
+
var flist = [];
|
|
226
|
+
for (var f of files) {
|
|
227
|
+
flist.push({ name: f.name, size: f.size, id: ++fid, mtime: +f.lastModified });
|
|
228
|
+
filesMap[fid] = f;
|
|
229
|
+
}
|
|
230
|
+
return this.send('file', flist);
|
|
231
|
+
},
|
|
68
232
|
resize(body) {
|
|
69
233
|
var textarea = body.querySelector("[textarea]");
|
|
70
234
|
var lastElementChild = textarea.lastElementChild;
|
|
@@ -72,22 +236,36 @@ function chat(title = '会话窗口') {
|
|
|
72
236
|
if (Math.abs(targetHeight - textarea.clientHeight - textarea.clientTop) < 2) return;
|
|
73
237
|
body.resizeCell(textarea, 'top', textarea.clientHeight - targetHeight - 2);
|
|
74
238
|
},
|
|
75
|
-
send() {
|
|
76
|
-
if (!this.text) return;
|
|
77
|
-
var data = JSAM.stringify({
|
|
78
|
-
type: 'html',
|
|
79
|
-
sender: this.localid,
|
|
80
|
-
content: this.text,
|
|
81
|
-
});
|
|
82
239
|
|
|
83
|
-
|
|
84
|
-
if (
|
|
240
|
+
send(type, content) {
|
|
241
|
+
if (!content) return;
|
|
242
|
+
var msg = {
|
|
243
|
+
type,
|
|
244
|
+
sender: this.localid,
|
|
245
|
+
content,
|
|
246
|
+
};
|
|
247
|
+
var data = JSAM.stringify(msg);
|
|
248
|
+
if (data.length > 2000) {
|
|
85
249
|
return alert("信息太长,无法发送!");
|
|
86
250
|
}
|
|
251
|
+
if (this.user && this.user.id !== this.localid) {
|
|
252
|
+
addToMsgList(this.msglist, [msg]);
|
|
253
|
+
}
|
|
254
|
+
data = encode62.timeencode(data);
|
|
87
255
|
cast(page, "send", data);
|
|
88
256
|
this.body.lastElementChild.focus();
|
|
89
257
|
this.text = '';
|
|
90
258
|
}
|
|
91
259
|
});
|
|
260
|
+
var headHeight = 0;
|
|
261
|
+
resizingList.set(page, function () {
|
|
262
|
+
var height = page.firstElementChild.offsetHeight;
|
|
263
|
+
if (height !== headHeight) {
|
|
264
|
+
headHeight = height;
|
|
265
|
+
css(page.firstElementChild, { marginBottom: fromOffset(-headHeight) });
|
|
266
|
+
css(page.$scope.body.firstElementChild, { paddingTop: fromOffset(headHeight) });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
moveupon(page, dragpage);
|
|
92
270
|
return page;
|
|
93
271
|
}
|