jsir 1.1.1 → 1.1.6
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/cmd/ooa.js +62 -31
- package/ethWeb.js +37 -12
- package/index.js +7 -3
- package/package.json +1 -2
- package/util.js +16 -1
- package/sql.js +0 -11
package/cmd/ooa.js
CHANGED
|
@@ -21,7 +21,7 @@ const _setting = require('../setting')
|
|
|
21
21
|
let _cmdMap = {}
|
|
22
22
|
let _rl
|
|
23
23
|
let _haveWrapperInput = false
|
|
24
|
-
let _haveStartRead = false
|
|
24
|
+
let _haveStartRead = false
|
|
25
25
|
let _repos = arrayDataFile('repos.json')
|
|
26
26
|
|
|
27
27
|
const lib = require('../index')
|
|
@@ -133,7 +133,7 @@ run(async () => {
|
|
|
133
133
|
|
|
134
134
|
if (['run', 'edit', 'rm', 'diff', 'push', 'pull'].indexOf(_args[0]) !== -1
|
|
135
135
|
&& _args[1] && !/^\d+$/.test(_args[1])) {
|
|
136
|
-
let name = `e ${_args[1]}
|
|
136
|
+
let name = toJsirFileName(`e ${_args[1]}`)
|
|
137
137
|
let path = _home + '/' + name
|
|
138
138
|
if (_fs.existsSync(path)) {
|
|
139
139
|
_cmdMap = {
|
|
@@ -204,7 +204,7 @@ async function fileLine(name) {
|
|
|
204
204
|
}
|
|
205
205
|
let tempDir = getLibDataDir() + "/ooa"
|
|
206
206
|
mkdir(tempDir)
|
|
207
|
-
let workFile = `${tempDir}/f ${name}
|
|
207
|
+
let workFile = `${tempDir}/f ${toJsirFileName(name)}`;
|
|
208
208
|
if (!_fs.existsSync(workFile)) {
|
|
209
209
|
_fs.writeFileSync(workFile, '');
|
|
210
210
|
}
|
|
@@ -228,9 +228,7 @@ async function fileLine(name) {
|
|
|
228
228
|
&& newLines.length >= lines.length
|
|
229
229
|
&& newLine !== line) {
|
|
230
230
|
let exeStrs
|
|
231
|
-
if (newLine.startsWith(
|
|
232
|
-
exeStrs = []
|
|
233
|
-
} else if (newLine.startsWith('/*')) {
|
|
231
|
+
if (newLine.startsWith('/*')) {
|
|
234
232
|
exeStrs = newLine.replace(/^\/\*|\*\/$/g, '').split('\n')
|
|
235
233
|
.map(i => trim(trim(i).replace(/^\*/, '')))
|
|
236
234
|
.filter(i => i)
|
|
@@ -271,21 +269,15 @@ function getLines(text) {
|
|
|
271
269
|
continue
|
|
272
270
|
}
|
|
273
271
|
|
|
274
|
-
if (line.startsWith('
|
|
272
|
+
if (line.startsWith('/*')) {
|
|
275
273
|
if (cLine.length > 0) {
|
|
276
274
|
lines.push(cLine.join('\n'))
|
|
277
275
|
cLine = []
|
|
278
276
|
}
|
|
279
|
-
lines.push(line)
|
|
280
|
-
continue
|
|
281
|
-
}
|
|
282
277
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
lines.push(cLine.join('\n'))
|
|
286
|
-
cLine = []
|
|
278
|
+
if (!line.endsWith('*/')) {
|
|
279
|
+
nLine.push(line)
|
|
287
280
|
}
|
|
288
|
-
nLine.push(line)
|
|
289
281
|
continue
|
|
290
282
|
}
|
|
291
283
|
|
|
@@ -307,8 +299,7 @@ async function dealInitData() {
|
|
|
307
299
|
continue;
|
|
308
300
|
}
|
|
309
301
|
let filePath = _home + "/" + file;
|
|
310
|
-
let initDataName = file.replace(/^i\s+/, '')
|
|
311
|
-
.replace(/\.js$/, '')
|
|
302
|
+
let initDataName = trimJsirFileName(file.replace(/^i\s+/, ''))
|
|
312
303
|
.split(/\s+/).filter(item => item).join("_");
|
|
313
304
|
let text = String(_fs.readFileSync(filePath))
|
|
314
305
|
$i[initDataName] = null
|
|
@@ -354,7 +345,7 @@ function _nextLine(callback, preStr, hidden, resolve) {
|
|
|
354
345
|
_rl.on('line', async line => {
|
|
355
346
|
line = trim(line)
|
|
356
347
|
_haveWrapperInput = true;
|
|
357
|
-
if (hidden || (callback && callback !== wrapperInput)) {
|
|
348
|
+
if (line && (hidden || (callback && callback !== wrapperInput))) {
|
|
358
349
|
_rl.history = _rl.history.slice(1)
|
|
359
350
|
}
|
|
360
351
|
if (_noAppendNextLine && _rl) {
|
|
@@ -408,7 +399,7 @@ function dealSourceCmds() {
|
|
|
408
399
|
mkdir(source)
|
|
409
400
|
}
|
|
410
401
|
if (source && _fs.existsSync(source)) {
|
|
411
|
-
_fs.readdirSync(source).filter(
|
|
402
|
+
_fs.readdirSync(source).filter(isJsirFileName).forEach(item => {
|
|
412
403
|
if (!_fs.existsSync(_home + "/" + item)) {
|
|
413
404
|
_fs.writeFileSync(_home + "/" + item, String(_fs.readFileSync(source + "/" + item)))
|
|
414
405
|
}
|
|
@@ -419,7 +410,7 @@ function dealSourceCmds() {
|
|
|
419
410
|
}
|
|
420
411
|
|
|
421
412
|
async function save(args) {
|
|
422
|
-
let path = `${_home}/${args.join(' ')}
|
|
413
|
+
let path = `${_home}/${toJsirFileName(args.join(' '))}`
|
|
423
414
|
if (_fs.existsSync(path)) {
|
|
424
415
|
warn('already exist')
|
|
425
416
|
return
|
|
@@ -508,11 +499,12 @@ function listCmd() {
|
|
|
508
499
|
|
|
509
500
|
text = text1
|
|
510
501
|
}
|
|
511
|
-
let name = _cmdMap[i]
|
|
502
|
+
let name = trimJsirFileName(_cmdMap[i])
|
|
503
|
+
let suffix = getJsirFileSuffix(_cmdMap[i])
|
|
512
504
|
let item = {
|
|
513
505
|
key: prefix.join('') + i,
|
|
514
|
-
|
|
515
|
-
|
|
506
|
+
name: name.replace(/^[eif]\s+/, ''),
|
|
507
|
+
type: [_types[name.split(/\s+/)[0]] || 'note', suffix].map(trim).join(".")
|
|
516
508
|
}
|
|
517
509
|
items.push(item)
|
|
518
510
|
|
|
@@ -558,6 +550,7 @@ function printObjProfile(result) {
|
|
|
558
550
|
}
|
|
559
551
|
|
|
560
552
|
async function wrapperInput(str) {
|
|
553
|
+
_haveWrapperInput = true;
|
|
561
554
|
global.$newInput = true;
|
|
562
555
|
|
|
563
556
|
str = trim(str)
|
|
@@ -657,7 +650,7 @@ async function wrapperInput(str) {
|
|
|
657
650
|
if (_cmdMap[strs[0]]) {
|
|
658
651
|
putHis(_cmdMap[strs[0]])
|
|
659
652
|
let path = _home + '/' + _cmdMap[strs[0]]
|
|
660
|
-
let fileName = _cmdMap[strs[0]]
|
|
653
|
+
let fileName = trimJsirFileName(_cmdMap[strs[0]]);
|
|
661
654
|
let firstName = trim(fileName).split(/\s+/)[0]
|
|
662
655
|
if (firstName === 'f') {
|
|
663
656
|
await fileLine(fileName.replace(/^\s*f\s*/, ''))
|
|
@@ -735,7 +728,7 @@ async function dealKeyword(str, strs, fstr, ostr) {
|
|
|
735
728
|
} else {
|
|
736
729
|
let newName =trim(ostr.join(" "))
|
|
737
730
|
if (newName) {
|
|
738
|
-
newName = newName
|
|
731
|
+
newName = toJsirFileName(newName)
|
|
739
732
|
if (rename(_home + '/' + name, _home + '/' + newName)) {
|
|
740
733
|
info(`${_home + '/' + newName} renamed`)
|
|
741
734
|
}
|
|
@@ -838,7 +831,6 @@ async function dealKeyword(str, strs, fstr, ostr) {
|
|
|
838
831
|
}
|
|
839
832
|
}
|
|
840
833
|
|
|
841
|
-
|
|
842
834
|
function getComments(text, cols = [], col) {
|
|
843
835
|
let docLines = []
|
|
844
836
|
text = trim(text)
|
|
@@ -873,16 +865,49 @@ function getComments(text, cols = [], col) {
|
|
|
873
865
|
}
|
|
874
866
|
}
|
|
875
867
|
|
|
868
|
+
function toJsirFileName(name) {
|
|
869
|
+
name = trim(name)
|
|
870
|
+
if (isJsirFileName(name)) {
|
|
871
|
+
return name;
|
|
872
|
+
}
|
|
873
|
+
name = name + '.js'
|
|
874
|
+
if (!isJsirFileName(name)) {
|
|
875
|
+
throw `invalid name`
|
|
876
|
+
}
|
|
877
|
+
return name
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function isJsirFileName(name) {
|
|
881
|
+
name = trim(name)
|
|
882
|
+
return /^[^.]*[^.\s]\.[^.\s]+$/.test(name)
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function trimJsirFileName(name) {
|
|
886
|
+
name = trim(name)
|
|
887
|
+
if (isJsirFileName(name)) {
|
|
888
|
+
return trim(name.replace(/\.[^.\s]+$/, ''))
|
|
889
|
+
}
|
|
890
|
+
return name
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function getJsirFileSuffix(name) {
|
|
894
|
+
name = trim(name)
|
|
895
|
+
if (isJsirFileName(name)) {
|
|
896
|
+
return trim(name.split('.').slice(-1))
|
|
897
|
+
}
|
|
898
|
+
return ''
|
|
899
|
+
}
|
|
900
|
+
|
|
876
901
|
function filterCmd(args){
|
|
877
902
|
let cmdMap = {}
|
|
878
903
|
let files = _fs.readdirSync(_home)
|
|
879
904
|
let i = 1
|
|
880
905
|
for (let file of files) {
|
|
881
906
|
file = trim(file)
|
|
882
|
-
|
|
883
|
-
if (!file.endsWith('.js') || !fileName) {
|
|
907
|
+
if (!isJsirFileName(file)) {
|
|
884
908
|
continue
|
|
885
909
|
}
|
|
910
|
+
let fileName = trimJsirFileName(file)
|
|
886
911
|
isArgsMatch(fileName, args, () => {
|
|
887
912
|
if (Object.values(cmdMap).indexOf(file) === -1) {
|
|
888
913
|
cmdMap[i] = file
|
|
@@ -1020,8 +1045,9 @@ function enrichArgs(str) {
|
|
|
1020
1045
|
}
|
|
1021
1046
|
i ++
|
|
1022
1047
|
})
|
|
1023
|
-
return args
|
|
1048
|
+
return args.map(trim)
|
|
1024
1049
|
}
|
|
1050
|
+
|
|
1025
1051
|
function translateArgs(arg) {
|
|
1026
1052
|
let rpStr = s => {
|
|
1027
1053
|
let ori = s;
|
|
@@ -1051,13 +1077,18 @@ function getArgDef(text) {
|
|
|
1051
1077
|
argDef = {}
|
|
1052
1078
|
}
|
|
1053
1079
|
let temp = {}
|
|
1080
|
+
let tempOpt = {}
|
|
1054
1081
|
for (let key of Object.keys(argDef)) {
|
|
1055
1082
|
let vlKey = trim(key)
|
|
1056
1083
|
if (vlKey) {
|
|
1057
|
-
|
|
1084
|
+
if (vlKey.startsWith('_')) {
|
|
1085
|
+
tempOpt[vlKey] = argDef[key]
|
|
1086
|
+
} else {
|
|
1087
|
+
temp[vlKey] = argDef[key]
|
|
1088
|
+
}
|
|
1058
1089
|
}
|
|
1059
1090
|
}
|
|
1060
|
-
return temp
|
|
1091
|
+
return Object.assign(temp, tempOpt)
|
|
1061
1092
|
}
|
|
1062
1093
|
|
|
1063
1094
|
function setTips(key, value, onRm) {
|
package/ethWeb.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
2
|
const BigNumber = require('bignumber.js');
|
|
3
3
|
let {mkdir, getLibDataDir, trim, got, toBigNum, getConfig, objDataFile,
|
|
4
|
-
eFn, removeFirst, randomInt, splitArray, sleep, vl, cacheFn, isError} = require('./util')
|
|
4
|
+
eFn, removeFirst, randomInt, splitArray, sleep, vl, cacheFn, isError, bMin} = require('./util')
|
|
5
5
|
const abiDecoder = require('abi-decoder');
|
|
6
6
|
let fs = require('fs')
|
|
7
7
|
let contractMapPath = getLibDataDir() + "/contractMap.json"
|
|
@@ -500,7 +500,9 @@ async function getTokenMap(tokens, web3) {
|
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
tokens = tokens || []
|
|
503
|
-
tokens = tokens.
|
|
503
|
+
tokens = tokens.map(token => '0x' + token.toLowerCase().replace(/^0x/, ''))
|
|
504
|
+
tokens = [...new Set(tokens)]
|
|
505
|
+
tokens = tokens.filter(token => !chainMap[token])
|
|
504
506
|
if (tokens.length <= 0) {
|
|
505
507
|
return chainMap
|
|
506
508
|
}
|
|
@@ -770,7 +772,7 @@ async function ethRead(address, abi, method, args, web3) {
|
|
|
770
772
|
|
|
771
773
|
function createEthWrite(config = {}) {
|
|
772
774
|
return async (sender, address, opt) => {
|
|
773
|
-
await ethWrite(sender, address, Object.assign(
|
|
775
|
+
return await ethWrite(sender, address, Object.assign({}, config, opt))
|
|
774
776
|
}
|
|
775
777
|
}
|
|
776
778
|
|
|
@@ -804,13 +806,15 @@ async function ethWrite(sender, address, {data, abi, method, args, gasPrice, gas
|
|
|
804
806
|
}
|
|
805
807
|
console.log(`${msg} send ${signedTx.hash} ${txObject.gasPrice/1000000000}g`)
|
|
806
808
|
result = result.then(resp => {
|
|
807
|
-
console.log(`${msg} ${signedTx.hash} 交易成功`)
|
|
809
|
+
console.log('\x1B[32m%s\x1B[39m', `${msg} ${signedTx.hash} 交易成功`)
|
|
810
|
+
return true
|
|
808
811
|
}).catch(async e => {
|
|
809
|
-
console.log(`${msg} ${signedTx.hash} 交易失败`)
|
|
812
|
+
console.log('\x1B[35m%s\x1B[39m', `${msg} ${signedTx.hash} 交易失败`)
|
|
810
813
|
if (onError) {
|
|
811
|
-
await onError(e)
|
|
814
|
+
return await onError(e)
|
|
812
815
|
} else {
|
|
813
816
|
console.error(e.toString().split("\n")[0])
|
|
817
|
+
return false
|
|
814
818
|
}
|
|
815
819
|
})
|
|
816
820
|
if (waitConfirm) {
|
|
@@ -1000,7 +1004,7 @@ async function batchTranfer(ethTranserWrite, token, froms, tos, min, max) {
|
|
|
1000
1004
|
if (Number(fb) < max) {
|
|
1001
1005
|
continue
|
|
1002
1006
|
}
|
|
1003
|
-
let transferAmt =
|
|
1007
|
+
let transferAmt = bMin(max, tb)
|
|
1004
1008
|
let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
|
|
1005
1009
|
pros.push(ethTranserWrite(sender, token, {
|
|
1006
1010
|
abi: erc20Abi,
|
|
@@ -1018,7 +1022,7 @@ async function batchTranfer(ethTranserWrite, token, froms, tos, min, max) {
|
|
|
1018
1022
|
if (Number(fb) < max) {
|
|
1019
1023
|
continue
|
|
1020
1024
|
}
|
|
1021
|
-
let transferAmt =
|
|
1025
|
+
let transferAmt = bMin(max, tb)
|
|
1022
1026
|
let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
|
|
1023
1027
|
pros.push(ethTranserWrite(sender, toAddress, {
|
|
1024
1028
|
value: transferAmt,
|
|
@@ -1053,7 +1057,7 @@ async function batchCollect(ethTranserWrite, token, froms, to, remain) {
|
|
|
1053
1057
|
if (Number(fb) <= remain) {
|
|
1054
1058
|
continue
|
|
1055
1059
|
}
|
|
1056
|
-
let transferAmt =
|
|
1060
|
+
let transferAmt = bMin(fb, remain)
|
|
1057
1061
|
let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
|
|
1058
1062
|
pros.push(ethTranserWrite(sender, token, {
|
|
1059
1063
|
abi: erc20Abi,
|
|
@@ -1067,7 +1071,7 @@ async function batchCollect(ethTranserWrite, token, froms, to, remain) {
|
|
|
1067
1071
|
if (Number(fb) <= remain) {
|
|
1068
1072
|
continue
|
|
1069
1073
|
}
|
|
1070
|
-
let transferAmt =
|
|
1074
|
+
let transferAmt = bMin(fb, remain)
|
|
1071
1075
|
let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
|
|
1072
1076
|
pros.push(ethTranserWrite(sender, toAddress, {
|
|
1073
1077
|
value: transferAmt,
|
|
@@ -1145,13 +1149,33 @@ async function tokenApprove(ethWrite, sender, senderAddress, erc20, spender, opt
|
|
|
1145
1149
|
let {amt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', msg} = opt
|
|
1146
1150
|
let bal = await ethRead(erc20, erc20Abi, 'allowance', [senderAddress, spender])
|
|
1147
1151
|
if (bal <Number(amt) ) {
|
|
1148
|
-
await ethWrite(sender, erc20, {
|
|
1152
|
+
return await ethWrite(sender, erc20, {
|
|
1149
1153
|
abi: erc20Abi,
|
|
1150
1154
|
method: 'approve',
|
|
1151
1155
|
args: [spender, amt],
|
|
1152
1156
|
msg
|
|
1153
1157
|
})
|
|
1154
1158
|
}
|
|
1159
|
+
|
|
1160
|
+
return {
|
|
1161
|
+
promise: true
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function txnInputReplacer(input, callback) {
|
|
1166
|
+
let item = [input.substring(0, 10)]
|
|
1167
|
+
input = input.substring(10, input.length)
|
|
1168
|
+
|
|
1169
|
+
let str = ''
|
|
1170
|
+
while (input.length > 0) {
|
|
1171
|
+
str = input.substring(0, 64);
|
|
1172
|
+
input = input.substring(64, input.length);
|
|
1173
|
+
if (callback) {
|
|
1174
|
+
str = callback(str)
|
|
1175
|
+
}
|
|
1176
|
+
item.push(str)
|
|
1177
|
+
}
|
|
1178
|
+
return item.join('')
|
|
1155
1179
|
}
|
|
1156
1180
|
|
|
1157
1181
|
module.exports = {
|
|
@@ -1204,5 +1228,6 @@ module.exports = {
|
|
|
1204
1228
|
fastSwapForExact,
|
|
1205
1229
|
getTokenBal,
|
|
1206
1230
|
transferToken,
|
|
1207
|
-
tokenApprove
|
|
1231
|
+
tokenApprove,
|
|
1232
|
+
txnInputReplacer
|
|
1208
1233
|
}
|
package/index.js
CHANGED
|
@@ -68,7 +68,8 @@ const {
|
|
|
68
68
|
wrapRows,
|
|
69
69
|
info,
|
|
70
70
|
warn,
|
|
71
|
-
error
|
|
71
|
+
error,
|
|
72
|
+
parseSteps
|
|
72
73
|
} = require('./util')
|
|
73
74
|
const {
|
|
74
75
|
sc,
|
|
@@ -127,7 +128,8 @@ const {
|
|
|
127
128
|
fastSwapForExact,
|
|
128
129
|
getTokenBal,
|
|
129
130
|
transferToken,
|
|
130
|
-
tokenApprove
|
|
131
|
+
tokenApprove,
|
|
132
|
+
txnInputReplacer
|
|
131
133
|
} = require('./ethWeb')
|
|
132
134
|
|
|
133
135
|
module.exports = {
|
|
@@ -255,5 +257,7 @@ module.exports = {
|
|
|
255
257
|
wrapRows,
|
|
256
258
|
info,
|
|
257
259
|
warn,
|
|
258
|
-
error
|
|
260
|
+
error,
|
|
261
|
+
txnInputReplacer,
|
|
262
|
+
parseSteps
|
|
259
263
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsir",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"abi-decoder": "^2.3.0",
|
|
32
32
|
"address": "^1.1.2",
|
|
33
33
|
"axios": "^0.20.0",
|
|
34
|
-
"better-sqlite3": "^7.4.3",
|
|
35
34
|
"bignumber.js": "^9.0.0",
|
|
36
35
|
"chokidar": "^3.5.2",
|
|
37
36
|
"console.table": "^0.10.0",
|
package/util.js
CHANGED
|
@@ -806,6 +806,20 @@ function error(e, msg) {
|
|
|
806
806
|
console.log(e)
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
+
function parseSteps(str) {
|
|
810
|
+
let items = str.split(',').map(trim).filter(i => i)
|
|
811
|
+
let result = []
|
|
812
|
+
for (let item of items) {
|
|
813
|
+
let ss = item.split('-').map(trim).filter(i => i)
|
|
814
|
+
if (ss.length > 1) {
|
|
815
|
+
result.push(...range(Number(ss[0]), Number(ss[1])))
|
|
816
|
+
} else {
|
|
817
|
+
result.push(Number(ss[0]))
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return result
|
|
821
|
+
}
|
|
822
|
+
|
|
809
823
|
module.exports = {
|
|
810
824
|
run,
|
|
811
825
|
reget,
|
|
@@ -876,5 +890,6 @@ module.exports = {
|
|
|
876
890
|
wrapRows,
|
|
877
891
|
info,
|
|
878
892
|
warn,
|
|
879
|
-
error
|
|
893
|
+
error,
|
|
894
|
+
parseSteps
|
|
880
895
|
}
|
package/sql.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const {getLibDataDir, mkdir, createLimitLogger} = require('./util');
|
|
2
|
-
const Database = require('better-sqlite3');
|
|
3
|
-
let dataDir = getLibDataDir()
|
|
4
|
-
let dbDir = dataDir + '/db'
|
|
5
|
-
mkdir(dbDir)
|
|
6
|
-
|
|
7
|
-
function getDb(name) {
|
|
8
|
-
let dbFile = dbDir + '/' + name
|
|
9
|
-
return new Database(dbFile, { verbose: createLimitLogger(name + '.dblog') });
|
|
10
|
-
}
|
|
11
|
-
|