@descope/web-components-ui 1.0.249 → 1.0.251
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/dist/cjs/index.cjs.js +75 -11
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/index.esm.js +75 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/4567.js +1 -1
- package/dist/umd/descope-multi-select-combo-box-index-js.js +1 -1
- package/package.json +2 -2
- package/src/components/descope-multi-select-combo-box/MultiSelectComboBoxClass.js +50 -12
- package/src/helpers/index.js +27 -0
package/dist/umd/4567.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[4567],{5279:(e,t,r)=>{r.d(t,{gh:()=>s,k4:()=>i,qM:()=>
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[4567],{5279:(e,t,r)=>{r.d(t,{gh:()=>s,k4:()=>i,qM:()=>o,qg:()=>n});const s="descope",i=3,n="host",o="@"},4567:(e,t,r)=>{r.d(t,{Db:()=>b,FX:()=>n,P$:()=>o,Tk:()=>l,iY:()=>u,oP:()=>d,tg:()=>c});var s=r(2061),i=r(5279);const n=(e,t,{excludeAttrs:r=[],includeAttrs:s=[]})=>{const i=Array.from(e.attributes).filter((e=>!r.includes(e.name)&&(!s.length||s.includes(e.name)))).map((e=>e.name));t(i),new MutationObserver((e=>{e.forEach((e=>{"attributes"!==e.type||r.includes(e.attributeName)||s.length&&!s.includes(e.attributeName)||t([e.attributeName])}))})).observe(e,{attributes:!0})},o=(e,t)=>{t({addedNodes:Array.from(e.children),removedNodes:[]}),new MutationObserver((e=>{e.forEach((e=>{"childList"===e.type&&t(e)}))})).observe(e,{childList:!0})},a=(e,t,r={})=>s=>{s.forEach((s=>{const i=r[s]||s,n=e.getAttribute(s);null!==n?t.getAttribute(i)!==n&&t.setAttribute(i,n):t.removeAttribute(i)}))},c=(e,t,r)=>{n(e,a(e,t),r),n(t,a(t,e),r)},u=e=>(0,s.E3)(i.gh,e),l=(...e)=>`--${(0,s.E3)(...e)}`,d=(e,t,r={})=>{n(e,a(e,t,r.mapAttrs),r)},b=(e,t,r=[])=>{if(!r.length)return;const s=r.reduce(((t,r)=>Object.assign(t,{[r]:{get:()=>e[r],set(t){e[r]=t}}})),{});Object.defineProperties(t,s)}},2061:(e,t,r)=>{r.d(t,{E3:()=>i,GL:()=>s,lo:()=>a,mf:()=>o,qC:()=>n});const s=e=>e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[\s_.]+/g,"-").toLowerCase(),i=(...e)=>s(e.filter((e=>!!e)).join("-")),n=(...e)=>t=>e.reduceRight(((e,t)=>t(e)),t),o=e=>"function"==typeof e,a=(e,t)=>{if(!Array.isArray(e)||!Array.isArray(t))return!1;if(e.length!==t.length)return!1;const r=e.slice().sort(),s=t.slice().sort();for(let e=0;e<r.length;e++)if(r[e]!==s[e])return!1;return!0}}}]);
|
@@ -1 +1 @@
|
|
1
|
-
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[3840],{9393:(e,t,o)=>{o.r(t),o.d(t,{MultiSelectComboBoxClass:()=>I}),o(1438);var r=o(2061),i=o(4567),a=o(4201),l=o(1e3);const
|
1
|
+
"use strict";(self.webpackChunkDescopeUI=self.webpackChunkDescopeUI||[]).push([[3840],{9393:(e,t,o)=>{o.r(t),o.d(t,{MultiSelectComboBoxClass:()=>I}),o(1438);var r=o(2061),i=o(4567),a=o(4201),l=o(1e3);const s=(0,i.iY)("multi-select-combo-box"),{host:n,inputField:d,inputElement:c,placeholder:p,toggle:u,label:m,requiredIndicator:h,helperText:b,errorMessage:y,chip:v,chipLabel:g,overflowChipFirstBorder:f,overflowChipSecondBorder:x}={host:{selector:()=>":host"},inputField:{selector:"::part(input-field)"},inputElement:{selector:"input"},placeholder:{selector:"> input:placeholder-shown"},toggle:{selector:"::part(toggle-button)"},label:{selector:"::part(label)"},requiredIndicator:{selector:"[required]::part(required-indicator)::after"},helperText:{selector:"::part(helper-text)"},errorMessage:{selector:"::part(error-message)"},chip:{selector:"vaadin-multi-select-combo-box-chip"},chipLabel:{selector:"vaadin-multi-select-combo-box-chip::part(label)"},overflowChipFirstBorder:{selector:"vaadin-multi-select-combo-box-chip[slot='overflow']::before"},overflowChipSecondBorder:{selector:"vaadin-multi-select-combo-box-chip[slot='overflow']::after"}},I=(0,r.qC)((0,l.yk)({mappings:{hostWidth:{...n,property:"width"},hostDirection:{...n,property:"direction"},fontSize:[{},n],chipFontSize:{...g,property:"font-size"},fontFamily:[m,p,d,b,y,g],labelTextColor:[{...m,property:"color"},{...h,property:"color"}],errorMessageTextColor:{...y,property:"color"},inputHeight:{...d,property:"min-height"},inputBackgroundColor:{...d,property:"background-color"},inputBorderColor:{...d,property:"border-color"},inputBorderWidth:{...d,property:"border-width"},inputBorderStyle:{...d,property:"border-style"},inputBorderRadius:{...d,property:"border-radius"},labelRequiredIndicator:{...h,property:"content"},inputValueTextColor:{...d,property:"color"},inputPlaceholderTextColor:{...p,property:"color"},inputDropdownButtonCursor:{...u,property:"cursor"},inputDropdownButtonColor:{...u,property:"color"},inputDropdownButtonSize:{...u,property:"font-size"},inputDropdownButtonOffset:[{...u,property:"margin-right"},{...u,property:"margin-left"}],inputOutlineColor:{...d,property:"outline-color"},inputOutlineWidth:{...d,property:"outline-width"},inputOutlineStyle:{...d,property:"outline-style"},inputOutlineOffset:{...d,property:"outline-offset"},inputHorizontalPadding:[{...c,property:"padding-left"},{...c,property:"padding-right"},{...d,property:"padding-inline-start"}],inputVerticalPadding:[{...d,property:"padding-top"},{...d,property:"padding-bottom"}],chipTextColor:{...g,property:"color"},chipBackgroundColor:[{...v,property:"background-color"},{...f,property:"border-color"},{...x,property:"border-color"}],overlayBackground:{property:()=>I.cssVarList.overlay.backgroundColor},overlayBorder:{property:()=>I.cssVarList.overlay.border},overlayFontSize:{property:()=>I.cssVarList.overlay.fontSize},overlayFontFamily:{property:()=>I.cssVarList.overlay.fontFamily},overlayCursor:{property:()=>I.cssVarList.overlay.cursor},overlayItemBoxShadow:{property:()=>I.cssVarList.overlay.itemBoxShadow},overlayItemPaddingInlineStart:{property:()=>I.cssVarList.overlay.itemPaddingInlineStart},overlayItemPaddingInlineEnd:{property:()=>I.cssVarList.overlay.itemPaddingInlineEnd}}}),l.e4,(0,l.Iw)({name:"overlay",selector:"vaadin-multi-select-combo-box-internal",mappings:{backgroundColor:{selector:"vaadin-multi-select-combo-box-scroller"},minHeight:{selector:"vaadin-multi-select-combo-box-overlay"},margin:{selector:"vaadin-multi-select-combo-box-overlay"},cursor:{selector:"vaadin-multi-select-combo-box-item"},fontFamily:{selector:"vaadin-multi-select-combo-box-item"},fontSize:{selector:"vaadin-multi-select-combo-box-item"},itemBoxShadow:{selector:"vaadin-multi-select-combo-box-item",property:"box-shadow"},itemPaddingInlineStart:{selector:"vaadin-multi-select-combo-box-item",property:"padding-inline-start"},itemPaddingInlineEnd:{selector:"vaadin-multi-select-combo-box-item",property:"padding-inline-end"}},forward:{include:!1,attributes:["size"]}}),(0,l.dj)({proxyProps:["selectionStart"],inputEvent:"selected-items-changed"}),l.li,l.Ae,(e=>class extends e{#e=({displayName:e,value:t,label:o})=>`<span data-name="${o}" data-id="${t}">${e||o}</span>`;#t;#o=[];get defaultValues(){const e=this.getAttribute("default-values");if(e)try{const t=JSON.parse(e);if(this.isValidDataType(t))return t}catch(e){console.error('could not parse data string from attribute "default-values" -',e.message)}return[]}get renderItem(){return this.#e}set renderItem(e){this.#e=e,this.renderItems()}get data(){if(this.#t)return this.#t;const e=this.getAttribute("data");if(e)try{const t=JSON.parse(e);if(this.isValidDataType(t))return t}catch(e){console.error('could not parse data string from attribute "data" -',e.message)}return[]}set data(e){this.isValidDataType(e)&&(this.#t=e,this.renderItems())}get allowCustomValue(){return"true"===this.getAttribute("allow-custom-value")}get minItemsSelection(){return parseInt(this.getAttribute("min-items-selection"),10)||0}get maxItemsSelection(){return parseInt(this.getAttribute("max-items-selection"),10)||0}isValidDataType(e){const t=Array.isArray(e);return t||console.error("data and default-values must be an array, received:",e),t}getItemsTemplate(){return this.data?.reduce?.(((e,t)=>e+(this.renderItem?.(t||{})||"")),"")}renderItems(){const e=this.getItemsTemplate();e&&(this.innerHTML=e)}updateSelectedItems(){const e=this.baseElement.selectedItems?.map((e=>e.getAttribute("data-id")))||[];e.length>0&&(this.value=e),0===this.value.length&&this.setDefaultValues()}customValueTransformFn(e){return e}setComboBoxDescriptor(){const e=Object.getOwnPropertyDescriptor(this.inputElement.constructor.prototype,"value"),t=this;Object.defineProperties(this.inputElement,{value:{...e,set(o){const r=t.customValueTransformFn(o)||"";r!==this.value&&e.set.call(this,r)}}})}#r(){const e=Array.from(this.children);e.length?(this.removeAttribute("has-no-options"),e.forEach((e=>{Object.defineProperty(e,"data-name",{value:e.getAttribute("data-name"),configurable:!0,writable:!0}),Object.defineProperty(e,"data-id",{value:e.getAttribute("data-id"),configurable:!0,writable:!0})})),this.baseElement.items=e,setTimeout((()=>{this.updateSelectedItems()}),0)):(this.baseElement.items=[],this.setAttribute("has-no-options","")),this.baseElement.renderer=(e,t,o)=>{e.innerHTML=o.item.outerHTML}}#i(){const e=this.baseElement.shadowRoot.querySelector("vaadin-multi-select-combo-box-internal").shadowRoot.querySelector("vaadin-multi-select-combo-box-overlay");e._attachOverlay=()=>{e.bringToFront()},e._detachOverlay=()=>{},e._enterModalState=()=>{}}#a(){this.allowCustomValue&&this.baseElement.addEventListener("custom-value-set",(e=>{const t=this.#e({label:e.detail,displayName:e.detail,value:e.detail});this.innerHTML+=t,this.baseElement._lastFilter="",setTimeout((()=>{this.value=[...this.value,e.detail]}),0)}))}setGetValidity(){this.getValidity=function(){return this.isRequired&&!this.value.length?{valueMissing:!0}:this.isRequired&&this.minItemsSelection&&this.value.length<this.minItemsSelection?{rangeUnderflow:!0}:this.maxItemsSelection&&this.value.length>this.maxItemsSelection?{rangeOverflow:!0}:{}};const e=this;this.baseElement.checkValidity=()=>e.validity.valid}init(){super.init?.(),this.setGetValidity(),this.setComboBoxDescriptor(),this.#i(),this.#a(),this.renderItems(),(0,i.FX)(this,this.renderItems.bind(this),{includeAttrs:["data"]}),(0,i.P$)(this,this.#r.bind(this)),(0,i.oP)(this,this.baseElement,{includeAttrs:["placeholder"]}),this.setDefaultValues(),this.baseElement.addEventListener("selected-items-changed",(()=>{this.#l(),this.dispatchEvent(new CustomEvent("input",{bubbles:!0}))}))}setDefaultValues(){this.defaultValues.length>0&&(this.value=this.defaultValues)}#l(){this.#o=this.baseElement.selectedItems?.map((e=>e.getAttribute("data-id")))||[]}set value(e){if(e&&e.length>0){const t=this.baseElement.items?.filter((t=>e.includes(t["data-id"]))),o=t?.map((e=>e.getAttribute("data-id")))||[];(0,r.lo)(this.#o,o)||(this.baseElement.selectedItems=t)}else this.baseElement.selectedItems=[]}get value(){return this.#o}}))((0,l.DM)({slots:["","prefix"],wrappedEleName:"vaadin-multi-select-combo-box",style:()=>`\n\t\t:host {\n\t\t\tdisplay: inline-flex;\n\t\t\tbox-sizing: border-box;\n\t\t\t-webkit-mask-image: none;\n\t\t}\n\t\t${(0,a.bi)(I.cssVarList)}\n\t\t${(0,a.PH)("vaadin-multi-select-combo-box")}\n\t\t${(0,a.jI)("vaadin-multi-select-combo-box")}\n\t\t${(0,a.Pd)("vaadin-multi-select-combo-box")}\n\n\t\tvaadin-multi-select-combo-box {\n\t\t\tpadding: 0;\n\t\t\twidth: 100%;\n\t\t}\n\t\tvaadin-multi-select-combo-box::before {\n\t\t\theight: initial;\n\t\t}\n\t\tvaadin-multi-select-combo-box [slot="input"] {\n\t\t\t-webkit-mask-image: none;\n\t\t\tmin-height: 0;\n align-self: center;\n\t\t\tbox-sizing: border-box;\n\t\t}\n\n\t\t::part(input-field) {\n padding: 0;\n\t\t\tbox-shadow: none;\n\t\t}\n ${(0,a.Wf)("vaadin-multi-select-combo-box")}\n :host([has-label]) vaadin-multi-select-combo-box-chip::part(label) {\n display: block;\n }\n\n vaadin-multi-select-combo-box vaadin-multi-select-combo-box-chip[slot='overflow']::before,\n vaadin-multi-select-combo-box vaadin-multi-select-combo-box-chip[slot='overflow']::after {\n left: -4px;\n right: -4px;\n border-left-width: 0;\n border-inline-start-style: solid;\n border-inline-start-width: 2px;\n }\n vaadin-multi-select-combo-box vaadin-multi-select-combo-box-chip[slot='overflow']::after {\n left: -8px;\n right: -8px;\n }\n\n :host([has-no-options][allow-custom-value='true']) ::part(toggle-button) {\n display: none;\n }\n\t\t`,excludeAttrsSync:["tabindex","size","data","placeholder"],componentName:s,includeForwardProps:["items","renderer","selectedItems"]}));customElements.define(s,I)}}]);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@descope/web-components-ui",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.251",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/cjs/index.cjs.js",
|
6
6
|
"module": "dist/index.esm.js",
|
@@ -67,7 +67,7 @@
|
|
67
67
|
"eslint-plugin-import": "^2.28.1",
|
68
68
|
"eslint-plugin-prettier": "^5.0.0",
|
69
69
|
"html-webpack-plugin": "^5.5.1",
|
70
|
-
"husky": "^
|
70
|
+
"husky": "^9.0.0",
|
71
71
|
"jest": "^29.5.0",
|
72
72
|
"jest-environment-jsdom": "^29.5.0",
|
73
73
|
"prettier": "^3.0.2",
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { compose } from '../../helpers';
|
1
|
+
import { compose, compareArraysUnordered } from '../../helpers';
|
2
2
|
import {
|
3
3
|
forwardAttrs,
|
4
4
|
getComponentName,
|
@@ -19,6 +19,7 @@ import {
|
|
19
19
|
componentNameValidationMixin,
|
20
20
|
portalMixin,
|
21
21
|
proxyInputMixin,
|
22
|
+
changeMixin,
|
22
23
|
} from '../../mixins';
|
23
24
|
|
24
25
|
export const componentName = getComponentName('multi-select-combo-box');
|
@@ -32,6 +33,8 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
32
33
|
|
33
34
|
#data;
|
34
35
|
|
36
|
+
#value = [];
|
37
|
+
|
35
38
|
get defaultValues() {
|
36
39
|
const defaultValuesAttr = this.getAttribute('default-values');
|
37
40
|
if (defaultValuesAttr) {
|
@@ -116,14 +119,14 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
116
119
|
if (template) this.innerHTML = template;
|
117
120
|
}
|
118
121
|
|
119
|
-
|
122
|
+
updateSelectedItems() {
|
123
|
+
// This is a list of all the selected items, including ones that may have been removed from the DOM
|
120
124
|
const currentSelected =
|
121
125
|
this.baseElement.selectedItems?.map((item) => item.getAttribute('data-id')) || [];
|
122
126
|
|
123
|
-
|
124
|
-
|
125
|
-
// if previously selected item ID exists in current children, set it as selected
|
127
|
+
// if there are selected items, we want to trigger a potential update to the value if some child elements were removed
|
126
128
|
if (currentSelected.length > 0) {
|
129
|
+
// setting the value checks that the selected items are still in the DOM and will not set the value if they are not
|
127
130
|
this.value = currentSelected;
|
128
131
|
}
|
129
132
|
|
@@ -192,7 +195,7 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
192
195
|
|
193
196
|
setTimeout(() => {
|
194
197
|
// set timeout to ensure this runs after customValueTransformFn had the chance to be overriden
|
195
|
-
this.
|
198
|
+
this.updateSelectedItems();
|
196
199
|
}, 0);
|
197
200
|
} else {
|
198
201
|
this.baseElement.items = [];
|
@@ -231,6 +234,9 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
231
234
|
value: e.detail,
|
232
235
|
});
|
233
236
|
this.innerHTML += newItemHtml;
|
237
|
+
// The internal filter needs to be removed, otherwise there's a bug where a new custom item
|
238
|
+
// added can't be removed from the dropdown because of how the vaadin component is implemented
|
239
|
+
this.baseElement._lastFilter = '';
|
234
240
|
// The value needs to be set with a timeout because it needs to execute after
|
235
241
|
// the custom value is added to items by the children change observer
|
236
242
|
setTimeout(() => {
|
@@ -265,6 +271,15 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
265
271
|
}
|
266
272
|
return {};
|
267
273
|
};
|
274
|
+
|
275
|
+
// This is required to override the default validity check of the vaadin component
|
276
|
+
// which is triggered when the component is checked for validity after blur
|
277
|
+
// Without this, our minItemsSelection and maxItemsSelection constraints will not be checked
|
278
|
+
const that = this;
|
279
|
+
// eslint-disable-next-line func-names
|
280
|
+
this.baseElement.checkValidity = () => {
|
281
|
+
return that.validity.valid;
|
282
|
+
};
|
268
283
|
}
|
269
284
|
|
270
285
|
init() {
|
@@ -289,18 +304,40 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
289
304
|
forwardAttrs(this, this.baseElement, { includeAttrs: ['placeholder'] });
|
290
305
|
|
291
306
|
this.setDefaultValues();
|
307
|
+
|
308
|
+
this.baseElement.addEventListener('selected-items-changed', () => {
|
309
|
+
this.#updateInternalValue();
|
310
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true }));
|
311
|
+
});
|
292
312
|
}
|
293
313
|
|
294
314
|
setDefaultValues() {
|
295
|
-
|
315
|
+
const initialDefaultValues = this.defaultValues;
|
316
|
+
if (initialDefaultValues.length > 0) {
|
317
|
+
this.value = this.defaultValues;
|
318
|
+
}
|
296
319
|
}
|
297
320
|
|
321
|
+
#updateInternalValue() {
|
322
|
+
// This is done here because we don't want to return a different copy of the same array
|
323
|
+
// every time get value is called if a new value wasn't set
|
324
|
+
this.#value =
|
325
|
+
this.baseElement.selectedItems?.map((elem) => elem.getAttribute('data-id')) || [];
|
326
|
+
}
|
327
|
+
|
328
|
+
// Updating the value will update the selectedItems, which will trigger an event 'selected-items-changed'
|
329
|
+
// which we listen to in the init function to update the internal value
|
298
330
|
set value(vals) {
|
299
331
|
if (vals && vals.length > 0) {
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
332
|
+
// Filters the children of the component to find the ones that match the values,
|
333
|
+
// since it's possible that some values that are trying to set are not in the children
|
334
|
+
const selectedChildren = this.baseElement.items?.filter((item) =>
|
335
|
+
vals.includes(item['data-id'])
|
336
|
+
);
|
337
|
+
const newSelectedValues =
|
338
|
+
selectedChildren?.map((child) => child.getAttribute('data-id')) || [];
|
339
|
+
if (!compareArraysUnordered(this.#value, newSelectedValues)) {
|
340
|
+
this.baseElement.selectedItems = selectedChildren;
|
304
341
|
}
|
305
342
|
} else {
|
306
343
|
this.baseElement.selectedItems = [];
|
@@ -308,7 +345,7 @@ const multiSelectComboBoxMixin = (superclass) =>
|
|
308
345
|
}
|
309
346
|
|
310
347
|
get value() {
|
311
|
-
return this
|
348
|
+
return this.#value;
|
312
349
|
}
|
313
350
|
};
|
314
351
|
|
@@ -445,6 +482,7 @@ export const MultiSelectComboBoxClass = compose(
|
|
445
482
|
},
|
446
483
|
}),
|
447
484
|
proxyInputMixin({ proxyProps: ['selectionStart'], inputEvent: 'selected-items-changed' }),
|
485
|
+
changeMixin,
|
448
486
|
componentNameValidationMixin,
|
449
487
|
multiSelectComboBoxMixin
|
450
488
|
)(
|
package/src/helpers/index.js
CHANGED
@@ -25,3 +25,30 @@ export const isUrl = (maybeUrl) => {
|
|
25
25
|
return false;
|
26
26
|
}
|
27
27
|
};
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Compares two arrays unorderedly.
|
31
|
+
* @param {Array} arr1 - The first array to compare.
|
32
|
+
* @param {Array} arr2 - The second array to compare.
|
33
|
+
* @returns {boolean} - Returns true if the arrays are equal unorderedly, false otherwise.
|
34
|
+
*/
|
35
|
+
export const compareArraysUnordered = (arr1, arr2) => {
|
36
|
+
if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
|
40
|
+
if (arr1.length !== arr2.length) {
|
41
|
+
return false;
|
42
|
+
}
|
43
|
+
|
44
|
+
const sortedArr1 = arr1.slice().sort();
|
45
|
+
const sortedArr2 = arr2.slice().sort();
|
46
|
+
|
47
|
+
for (let i = 0; i < sortedArr1.length; i++) {
|
48
|
+
if (sortedArr1[i] !== sortedArr2[i]) {
|
49
|
+
return false;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
return true;
|
54
|
+
};
|