efront 3.32.0 → 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/removeFromList.js +7 -5
- package/coms/basic/removeFromList_test.js +3 -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 +41 -15
- package/coms/reptile/colored_console_test.js +3 -1
- 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,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
|
}
|
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
|
}
|