node-red-contrib-mobius-flow-base 3.0.1 → 3.1.1
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.
|
@@ -42,19 +42,23 @@
|
|
|
42
42
|
initMobsearch = {
|
|
43
43
|
// Standard mobsearch to rid level
|
|
44
44
|
toRid: (mobiusSrvc, opts) => {
|
|
45
|
-
getMobsearch(mobiusSrvc,
|
|
45
|
+
getMobsearch(mobiusSrvc, 'rid', false, opts);
|
|
46
46
|
},
|
|
47
47
|
// Standard mobsearch to ins level
|
|
48
48
|
toIns: (mobiusSrvc, opts) => {
|
|
49
|
-
getMobsearch(mobiusSrvc,
|
|
49
|
+
getMobsearch(mobiusSrvc, 'ins', false, opts);
|
|
50
|
+
},
|
|
51
|
+
// Standard mobsearch to sid level
|
|
52
|
+
toSid: (mobiusSrvc, opts) => {
|
|
53
|
+
getMobsearch(mobiusSrvc, 'sid', false, opts);
|
|
50
54
|
},
|
|
51
55
|
// Mobsearch to rid level, for nodes using editable list
|
|
52
56
|
toRid_listed: (mobiusSrvc, opts) => {
|
|
53
|
-
getMobsearch(mobiusSrvc,
|
|
57
|
+
getMobsearch(mobiusSrvc, 'rid', true, opts);
|
|
54
58
|
},
|
|
55
59
|
// Mobsearch to ins level, for nodes using editable list
|
|
56
60
|
toIns_listed: (mobiusSrvc, opts) => {
|
|
57
|
-
getMobsearch(mobiusSrvc,
|
|
61
|
+
getMobsearch(mobiusSrvc, 'ins', true, opts);
|
|
58
62
|
},
|
|
59
63
|
};
|
|
60
64
|
|
|
@@ -96,11 +100,11 @@ function addMobsearchListButton(mobiusSrvc, field, rowIndex, opts) {
|
|
|
96
100
|
/**
|
|
97
101
|
*
|
|
98
102
|
* @param mobiusSrvc - Connection to Mobius
|
|
99
|
-
* @param {boolean}
|
|
103
|
+
* @param {boolean} level - Flag for Mobsearch to include rid field
|
|
100
104
|
* @param {boolean} listed - Flag for Mobsearch work with nodes using editableLists
|
|
101
105
|
* @param {object} opts - Mobsearhc options object
|
|
102
106
|
*/
|
|
103
|
-
function getMobsearch(mobiusSrvc,
|
|
107
|
+
function getMobsearch(mobiusSrvc, level, listed, opts) {
|
|
104
108
|
selectionIndex = 0;
|
|
105
109
|
// Get current state of input field
|
|
106
110
|
let uriFieldValue = '';
|
|
@@ -125,9 +129,24 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
125
129
|
<input type="text" id="instance-search-ins" style="width:10%;" placeholder="INS">
|
|
126
130
|
`;
|
|
127
131
|
|
|
132
|
+
const serviceHtml = `
|
|
133
|
+
<input type="text" id="instance-search-hid" style="width:15%;" placeholder="HID">
|
|
134
|
+
<input type="text" id="instance-search-sid" style="width:10%;" placeholder="SID">
|
|
135
|
+
`;
|
|
136
|
+
|
|
128
137
|
//set level dependent html
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
switch (level) {
|
|
139
|
+
case 'sid':
|
|
140
|
+
rows = serviceHtml;
|
|
141
|
+
break;
|
|
142
|
+
case 'ins':
|
|
143
|
+
rows = instanceHtml;
|
|
144
|
+
break;
|
|
145
|
+
case 'rid':
|
|
146
|
+
rows = resourceHtml;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
let alertMsg = level === 'rid' ? `and/or RID ` : ``;
|
|
131
150
|
|
|
132
151
|
//Inject new html
|
|
133
152
|
document.getElementById(opts.htmlElementId).innerHTML += `
|
|
@@ -316,11 +335,15 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
316
335
|
$('#instance-search-pid').removeAttr('disabled');
|
|
317
336
|
$('#instance-search-ins').removeAttr('disabled');
|
|
318
337
|
$('#instance-search-rid').removeAttr('disabled');
|
|
319
|
-
let services = opts.removeWildcards
|
|
338
|
+
let services = opts.removeWildcards
|
|
339
|
+
? []
|
|
340
|
+
: level === 'sid'
|
|
341
|
+
? ['#']
|
|
342
|
+
: ['#', '+'];
|
|
320
343
|
for (let i = 0; i < response.length; i++) {
|
|
321
344
|
if (response[i].sid !== '009') {
|
|
322
345
|
if (opts.filterSpids) {
|
|
323
|
-
if (opts.filterSpids.includes(response[i].
|
|
346
|
+
if (opts.filterSpids.includes(response[i].spid)) {
|
|
324
347
|
services.push(`${response[i].sid} - ${response[i].name}`);
|
|
325
348
|
}
|
|
326
349
|
} else {
|
|
@@ -407,9 +430,15 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
407
430
|
*/
|
|
408
431
|
function finishSearch() {
|
|
409
432
|
$('#wildcard-alert-row').hide();
|
|
410
|
-
let uri = `${searchObject.hid}/${searchObject.sid}
|
|
411
|
-
|
|
412
|
-
|
|
433
|
+
let uri = `${searchObject.hid}/${searchObject.sid}`;
|
|
434
|
+
switch (level) {
|
|
435
|
+
case 'ins':
|
|
436
|
+
uri = uri + `/${searchObject.pid}/${searchObject.ins}`;
|
|
437
|
+
break;
|
|
438
|
+
case 'rid':
|
|
439
|
+
uri =
|
|
440
|
+
uri + `/${searchObject.pid}/${searchObject.ins}/${searchObject.rid}`;
|
|
441
|
+
break;
|
|
413
442
|
}
|
|
414
443
|
if (uri.includes('#')) {
|
|
415
444
|
uri = uri.substring(0, uri.indexOf('#') + 1);
|
|
@@ -442,14 +471,35 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
442
471
|
* Update if accept button is grade out and functional, based on conditions of fields
|
|
443
472
|
*/
|
|
444
473
|
function acceptCheck() {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
474
|
+
switch (level) {
|
|
475
|
+
case 'sid':
|
|
476
|
+
checkFieldsToSid();
|
|
477
|
+
break;
|
|
478
|
+
case 'ins':
|
|
479
|
+
checkFieldsToIns();
|
|
480
|
+
break;
|
|
481
|
+
case 'rid':
|
|
482
|
+
checkFieldsToRid();
|
|
483
|
+
break;
|
|
449
484
|
}
|
|
450
485
|
checkWildcards();
|
|
451
486
|
}
|
|
452
487
|
|
|
488
|
+
function checkFieldsToSid() {
|
|
489
|
+
const wildcardPass =
|
|
490
|
+
!opts.removeWildcards && $('#instance-search-sid').val() === '#';
|
|
491
|
+
if (
|
|
492
|
+
$('#instance-search-hid').val() !== '' &&
|
|
493
|
+
$('#instance-search-sid').val() !== ''
|
|
494
|
+
) {
|
|
495
|
+
$('#node-search-accept').css('color', '#555555');
|
|
496
|
+
readyToAccept = true;
|
|
497
|
+
} else {
|
|
498
|
+
$('#node-search-accept').css('color', '#CCCCCC');
|
|
499
|
+
readyToAccept = false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
453
503
|
/**
|
|
454
504
|
* Check state of all field to rid level, change state of accept button accordingly
|
|
455
505
|
*/
|
|
@@ -541,11 +591,15 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
541
591
|
$(`#${opts.searchButtonId}`).click(function () {
|
|
542
592
|
if (mobConnected) {
|
|
543
593
|
//When search button is clicked
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
594
|
+
if (level === 'sid' || level === 'ins' || level === 'rid') {
|
|
595
|
+
$('#instance-search-sid').val('');
|
|
596
|
+
if (level === 'ins' || level === 'rid') {
|
|
597
|
+
$('#instance-search-pid').val('');
|
|
598
|
+
$('#instance-search-ins').val('');
|
|
599
|
+
if (level === 'rid') {
|
|
600
|
+
$('#instance-search-rid').val('');
|
|
601
|
+
}
|
|
602
|
+
}
|
|
549
603
|
}
|
|
550
604
|
$(`#${opts.searchButtonId}`).addClass('disabled');
|
|
551
605
|
$('#node-search-return').removeClass('disabled');
|
|
@@ -561,7 +615,7 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
561
615
|
$('#instance-search-ins').val('');
|
|
562
616
|
searchObject.pid = '';
|
|
563
617
|
searchObject.ins = '';
|
|
564
|
-
if (
|
|
618
|
+
if (level === 'rid') {
|
|
565
619
|
searchObject.rid = '';
|
|
566
620
|
}
|
|
567
621
|
searchObject.objects = [];
|
|
@@ -573,7 +627,7 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
573
627
|
|
|
574
628
|
searchObject.ins = '';
|
|
575
629
|
|
|
576
|
-
if (
|
|
630
|
+
if (level === 'rid') {
|
|
577
631
|
$('#instance-search-rid').val('');
|
|
578
632
|
searchObject.rid = '';
|
|
579
633
|
}
|
|
@@ -583,7 +637,7 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
583
637
|
});
|
|
584
638
|
$('#instance-search-ins').click(function () {
|
|
585
639
|
//INS field clicked
|
|
586
|
-
if (
|
|
640
|
+
if (level === 'rid') {
|
|
587
641
|
$('#instance-search-rid').val('');
|
|
588
642
|
searchObject.rid = '';
|
|
589
643
|
}
|
|
@@ -596,7 +650,7 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
596
650
|
}
|
|
597
651
|
});
|
|
598
652
|
|
|
599
|
-
if (
|
|
653
|
+
if (level === 'rid') {
|
|
600
654
|
$('#instance-search-rid').click(function () {
|
|
601
655
|
//RID clicked
|
|
602
656
|
if (
|
|
@@ -632,34 +686,40 @@ function getMobsearch(mobiusSrvc, useRid, listed, opts) {
|
|
|
632
686
|
|
|
633
687
|
$('#instance-search-hid').change(function () {
|
|
634
688
|
acceptCheck();
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (
|
|
639
|
-
$('#instance-search-
|
|
689
|
+
if (level === 'sid' || level === 'ins' || level === 'rid') {
|
|
690
|
+
$('#instance-search-sid').val('');
|
|
691
|
+
}
|
|
692
|
+
if (level === 'ins' || level === 'rid') {
|
|
693
|
+
$('#instance-search-pid').val('');
|
|
694
|
+
$('#instance-search-ins').val('');
|
|
695
|
+
if (level === 'rid') {
|
|
696
|
+
$('#instance-search-rid').val('');
|
|
697
|
+
}
|
|
640
698
|
}
|
|
641
699
|
});
|
|
642
700
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
searchObject.ins = $('#instance-search-ins').val();
|
|
655
|
-
acceptCheck();
|
|
656
|
-
});
|
|
701
|
+
if (level === 'sid' || level === 'ins' || level === 'rid') {
|
|
702
|
+
$('#instance-search-sid').change(function () {
|
|
703
|
+
searchObject.sid = $('#instance-search-sid').val();
|
|
704
|
+
acceptCheck();
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
if (level === 'ins' || level === 'rid') {
|
|
708
|
+
$('#instance-search-pid').change(function () {
|
|
709
|
+
searchObject.pid = $('#instance-search-pid').val();
|
|
710
|
+
acceptCheck();
|
|
711
|
+
});
|
|
657
712
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
searchObject.rid = $('#instance-search-rid').val();
|
|
713
|
+
$('#instance-search-ins').change(function () {
|
|
714
|
+
searchObject.ins = $('#instance-search-ins').val();
|
|
661
715
|
acceptCheck();
|
|
662
716
|
});
|
|
717
|
+
if (level === 'rid') {
|
|
718
|
+
$('#instance-search-rid').change(function () {
|
|
719
|
+
searchObject.rid = $('#instance-search-rid').val();
|
|
720
|
+
acceptCheck();
|
|
721
|
+
});
|
|
722
|
+
}
|
|
663
723
|
}
|
|
664
724
|
|
|
665
725
|
// * END CHANGE RESPONSE * //
|
|
@@ -24,7 +24,10 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
24
24
|
POSSIBILITY OF SUCH DAMAGE.
|
|
25
25
|
-->
|
|
26
26
|
|
|
27
|
-
<script
|
|
27
|
+
<script
|
|
28
|
+
type="text/x-red"
|
|
29
|
+
data-template-name="mobius discover object list"
|
|
30
|
+
>
|
|
28
31
|
<div class="form-row">
|
|
29
32
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
30
33
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
@@ -138,7 +141,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
138
141
|
},
|
|
139
142
|
oneditprepare: function () {
|
|
140
143
|
var node = this;
|
|
141
|
-
let mobiusSrvc = $(
|
|
144
|
+
let mobiusSrvc = $('#node-input-connection').val();
|
|
142
145
|
|
|
143
146
|
/** Create the subs container and populate it */
|
|
144
147
|
$('#node-input-subs-container')
|
|
@@ -167,15 +170,21 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
167
170
|
}).appendTo(row);
|
|
168
171
|
|
|
169
172
|
// *** MOBSEARCH *** //
|
|
170
|
-
var searchField = $(
|
|
173
|
+
var searchField = $(
|
|
174
|
+
'<a class="searchbutton" style="margin-left: 5px; vertical-align: middle"><i id="node-lookup-instance-icon" class="fa fa-search"></i></a>'
|
|
175
|
+
).appendTo(row);
|
|
171
176
|
// *** END MOBSEARCH *** //
|
|
172
177
|
|
|
173
178
|
var typeField = $('<select/>', {
|
|
174
179
|
class: 'node-input-sub-type',
|
|
175
180
|
style: 'width:auto; margin-left: 5px; text-align: center;',
|
|
176
181
|
}).appendTo(row);
|
|
177
|
-
typeField.append(
|
|
178
|
-
|
|
182
|
+
typeField.append(
|
|
183
|
+
$('<option></option>').val('include').text('Include')
|
|
184
|
+
);
|
|
185
|
+
typeField.append(
|
|
186
|
+
$('<option></option>').val('exclude').text('Exclude')
|
|
187
|
+
);
|
|
179
188
|
|
|
180
189
|
// Populate the new stat
|
|
181
190
|
uriField.val(subscription.uri);
|
|
@@ -184,10 +193,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
184
193
|
// *** MOBSEARCH *** //
|
|
185
194
|
|
|
186
195
|
if (!node.subscriptions[i]) {
|
|
187
|
-
node.subscriptions[i] = {uri: '', type: 'include'}
|
|
196
|
+
node.subscriptions[i] = { uri: '', type: 'include' };
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
addMobsearchListButton(mobiusSrvc, searchField, i, mobsearchOpts)
|
|
199
|
+
addMobsearchListButton(mobiusSrvc, searchField, i, mobsearchOpts);
|
|
191
200
|
// *** END MOBSEARCH *** //
|
|
192
201
|
},
|
|
193
202
|
});
|
|
@@ -198,7 +207,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
198
207
|
if (!node.subscriptions) {
|
|
199
208
|
node.subscriptions = [(subscription = { uri: '', type: 'include' })];
|
|
200
209
|
}
|
|
201
|
-
|
|
210
|
+
|
|
202
211
|
// *** MOBSEARCH *** //
|
|
203
212
|
|
|
204
213
|
mobsearchOpts = {
|
|
@@ -244,7 +253,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
244
253
|
}
|
|
245
254
|
var editorRow = $('#dialog-form>div.node-input-subs-container-row');
|
|
246
255
|
height -=
|
|
247
|
-
4 *
|
|
256
|
+
4 *
|
|
257
|
+
(parseInt(editorRow.css('marginTop')) +
|
|
258
|
+
parseInt(editorRow.css('marginBottom')));
|
|
248
259
|
|
|
249
260
|
$('#node-input-subs-container').editableList('height', height);
|
|
250
261
|
},
|
|
@@ -270,7 +281,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
270
281
|
});
|
|
271
282
|
</script>
|
|
272
283
|
|
|
273
|
-
<script
|
|
284
|
+
<script
|
|
285
|
+
type="text/x-red"
|
|
286
|
+
data-help-name="mobius discover object list"
|
|
287
|
+
>
|
|
274
288
|
<p>Discover MOBiUS objects and return a filtered list of objects or URIs.</p>
|
|
275
289
|
|
|
276
290
|
<h3>Configuration Page</h3>
|
|
@@ -126,7 +126,11 @@ module.exports = function (RED) {
|
|
|
126
126
|
let objectList = [];
|
|
127
127
|
try {
|
|
128
128
|
const hid = node.hid === '' ? node.serviceConn.hubId : node.hid;
|
|
129
|
-
const profiles = await discoverProfiles(
|
|
129
|
+
const profiles = await discoverProfiles(
|
|
130
|
+
node.serviceConn,
|
|
131
|
+
hid,
|
|
132
|
+
node.subscriptions
|
|
133
|
+
);
|
|
130
134
|
|
|
131
135
|
for await (let entry of profiles.entries()) {
|
|
132
136
|
let discoverParams = {};
|
|
@@ -150,15 +154,28 @@ module.exports = function (RED) {
|
|
|
150
154
|
};
|
|
151
155
|
}
|
|
152
156
|
try {
|
|
153
|
-
let objects = await filterObjectsByRIDValue(
|
|
157
|
+
let objects = await filterObjectsByRIDValue(
|
|
158
|
+
node.serviceConn,
|
|
159
|
+
discoverParams
|
|
160
|
+
);
|
|
154
161
|
for (let lp = 0; lp < objects.value.length; lp++) {
|
|
155
162
|
objects.value[lp][
|
|
156
163
|
'uri'
|
|
157
164
|
] = `${serviceURI}/${objects.value[lp].pid}/${objects.value[lp].ins}`;
|
|
158
|
-
|
|
165
|
+
if (
|
|
166
|
+
isIncluded(
|
|
167
|
+
`${objects.value[lp]['uri']}`,
|
|
168
|
+
node.subscriptions
|
|
169
|
+
) &&
|
|
170
|
+
!isExcluded(`${objects.value[lp]['uri']}`, node.subscriptions)
|
|
171
|
+
) {
|
|
172
|
+
objectList.push(objects.value[lp]);
|
|
173
|
+
}
|
|
159
174
|
}
|
|
160
175
|
} catch (err) {
|
|
161
|
-
node.warn(
|
|
176
|
+
node.warn(
|
|
177
|
+
`Discover object failed for service (${serviceURI}) - ${err.message}`
|
|
178
|
+
);
|
|
162
179
|
}
|
|
163
180
|
}
|
|
164
181
|
|
|
@@ -185,10 +202,7 @@ module.exports = function (RED) {
|
|
|
185
202
|
let profiles = await discover(serviceConn, `${hid}/${sid}`);
|
|
186
203
|
let pids = [];
|
|
187
204
|
for (const pid of profiles.data) {
|
|
188
|
-
if (
|
|
189
|
-
isIncluded(`${hid}/${sid}/${pid}/#`, subscriptions) &&
|
|
190
|
-
!isExcluded(`${hid}/${sid}/${pid}/#`, subscriptions)
|
|
191
|
-
) {
|
|
205
|
+
if (isIncluded(`${hid}/${sid}/${pid}/#`, subscriptions)) {
|
|
192
206
|
pids.push(pid);
|
|
193
207
|
}
|
|
194
208
|
}
|
|
@@ -198,7 +212,9 @@ module.exports = function (RED) {
|
|
|
198
212
|
}
|
|
199
213
|
} else {
|
|
200
214
|
RED.log.warn(
|
|
201
|
-
`Failed to discover objects for service ${
|
|
215
|
+
`Failed to discover objects for service ${
|
|
216
|
+
service.id.split('/')[1]
|
|
217
|
+
} as it is offline.`
|
|
202
218
|
);
|
|
203
219
|
}
|
|
204
220
|
}
|
|
@@ -240,7 +256,10 @@ module.exports = function (RED) {
|
|
|
240
256
|
function isIncluded(uri, subscriptions) {
|
|
241
257
|
let include = false;
|
|
242
258
|
for (const subscription of subscriptions) {
|
|
243
|
-
if (
|
|
259
|
+
if (
|
|
260
|
+
subscription.type === 'include' &&
|
|
261
|
+
validateSubscription(subscription.uri)
|
|
262
|
+
) {
|
|
244
263
|
if (matchTopic(subscription.uri, uri)) {
|
|
245
264
|
include = true;
|
|
246
265
|
}
|
|
@@ -252,7 +271,10 @@ module.exports = function (RED) {
|
|
|
252
271
|
function isExcluded(uri, subscriptions) {
|
|
253
272
|
let exclude = false;
|
|
254
273
|
for (const subscription of subscriptions) {
|
|
255
|
-
if (
|
|
274
|
+
if (
|
|
275
|
+
subscription.type === 'exclude' &&
|
|
276
|
+
validateSubscription(subscription.uri)
|
|
277
|
+
) {
|
|
256
278
|
if (matchTopic(subscription.uri, uri)) {
|
|
257
279
|
exclude = true;
|
|
258
280
|
}
|
|
@@ -280,12 +302,23 @@ module.exports = function (RED) {
|
|
|
280
302
|
function matchTopic(filter, topic) {
|
|
281
303
|
const filterArray = filter.split('/');
|
|
282
304
|
const topicArray = topic.split('/');
|
|
283
|
-
const length =
|
|
305
|
+
const length =
|
|
306
|
+
filterArray.length < topicArray.length
|
|
307
|
+
? filterArray.length
|
|
308
|
+
: topicArray.length;
|
|
284
309
|
|
|
285
310
|
for (let i = 0; i < length; ++i) {
|
|
286
311
|
const left = filterArray[i];
|
|
287
312
|
const right = topicArray[i];
|
|
288
|
-
if (
|
|
313
|
+
if (
|
|
314
|
+
!(
|
|
315
|
+
left === right ||
|
|
316
|
+
left === '+' ||
|
|
317
|
+
left === '#' ||
|
|
318
|
+
right === '+' ||
|
|
319
|
+
right === '#'
|
|
320
|
+
)
|
|
321
|
+
) {
|
|
289
322
|
return false;
|
|
290
323
|
}
|
|
291
324
|
}
|