{"version":3,"sources":["src/assets/i18n/ua.json","src/assets/i18n/en.json","node_modules/moment/moment.js","node_modules/crypto-js/core.js","node_modules/crypto-js/x64-core.js","node_modules/crypto-js/lib-typedarrays.js","node_modules/crypto-js/enc-utf16.js","node_modules/crypto-js/enc-base64.js","node_modules/crypto-js/enc-base64url.js","node_modules/crypto-js/md5.js","node_modules/crypto-js/sha1.js","node_modules/crypto-js/sha256.js","node_modules/crypto-js/sha224.js","node_modules/crypto-js/sha512.js","node_modules/crypto-js/sha384.js","node_modules/crypto-js/sha3.js","node_modules/crypto-js/ripemd160.js","node_modules/crypto-js/hmac.js","node_modules/crypto-js/pbkdf2.js","node_modules/crypto-js/evpkdf.js","node_modules/crypto-js/cipher-core.js","node_modules/crypto-js/mode-cfb.js","node_modules/crypto-js/mode-ctr.js","node_modules/crypto-js/mode-ctr-gladman.js","node_modules/crypto-js/mode-ofb.js","node_modules/crypto-js/mode-ecb.js","node_modules/crypto-js/pad-ansix923.js","node_modules/crypto-js/pad-iso10126.js","node_modules/crypto-js/pad-iso97971.js","node_modules/crypto-js/pad-zeropadding.js","node_modules/crypto-js/pad-nopadding.js","node_modules/crypto-js/format-hex.js","node_modules/crypto-js/aes.js","node_modules/crypto-js/tripledes.js","node_modules/crypto-js/rc4.js","node_modules/crypto-js/rabbit.js","node_modules/crypto-js/rabbit-legacy.js","node_modules/crypto-js/blowfish.js","node_modules/crypto-js/index.js","node_modules/base64-js/index.js","node_modules/ieee754/index.js","node_modules/buffer/index.js","node_modules/js-sha256/src/sha256.js","node_modules/lodash/lodash.js","node_modules/photoswipe/dist/photoswipe.js","node_modules/photoswipe/dist/photoswipe-ui-default.js","node_modules/qr-code-styling/lib/qr-code-styling.js","node_modules/uuid/lib/rng-browser.js","node_modules/uuid/lib/bytesToUuid.js","node_modules/uuid/v1.js","node_modules/uuid/v4.js","node_modules/uuid/index.js","node_modules/@angular/core/fesm2022/primitives/signals.mjs","node_modules/rxjs/dist/esm/internal/util/isFunction.js","node_modules/rxjs/dist/esm/internal/util/createErrorClass.js","node_modules/rxjs/dist/esm/internal/util/UnsubscriptionError.js","node_modules/rxjs/dist/esm/internal/util/arrRemove.js","node_modules/rxjs/dist/esm/internal/Subscription.js","node_modules/rxjs/dist/esm/internal/config.js","node_modules/rxjs/dist/esm/internal/scheduler/timeoutProvider.js","node_modules/rxjs/dist/esm/internal/util/reportUnhandledError.js","node_modules/rxjs/dist/esm/internal/util/noop.js","node_modules/rxjs/dist/esm/internal/NotificationFactories.js","node_modules/rxjs/dist/esm/internal/util/errorContext.js","node_modules/rxjs/dist/esm/internal/Subscriber.js","node_modules/rxjs/dist/esm/internal/symbol/observable.js","node_modules/rxjs/dist/esm/internal/util/identity.js","node_modules/rxjs/dist/esm/internal/util/pipe.js","node_modules/rxjs/dist/esm/internal/Observable.js","node_modules/rxjs/dist/esm/internal/util/lift.js","node_modules/rxjs/dist/esm/internal/operators/OperatorSubscriber.js","node_modules/rxjs/dist/esm/internal/operators/refCount.js","node_modules/rxjs/dist/esm/internal/observable/ConnectableObservable.js","node_modules/rxjs/dist/esm/internal/scheduler/animationFrameProvider.js","node_modules/rxjs/dist/esm/internal/util/ObjectUnsubscribedError.js","node_modules/rxjs/dist/esm/internal/Subject.js","node_modules/rxjs/dist/esm/internal/BehaviorSubject.js","node_modules/rxjs/dist/esm/internal/scheduler/dateTimestampProvider.js","node_modules/rxjs/dist/esm/internal/ReplaySubject.js","node_modules/rxjs/dist/esm/internal/scheduler/Action.js","node_modules/rxjs/dist/esm/internal/scheduler/intervalProvider.js","node_modules/rxjs/dist/esm/internal/scheduler/AsyncAction.js","node_modules/rxjs/dist/esm/internal/util/Immediate.js","node_modules/rxjs/dist/esm/internal/scheduler/immediateProvider.js","node_modules/rxjs/dist/esm/internal/scheduler/AsapAction.js","node_modules/rxjs/dist/esm/internal/Scheduler.js","node_modules/rxjs/dist/esm/internal/scheduler/AsyncScheduler.js","node_modules/rxjs/dist/esm/internal/scheduler/AsapScheduler.js","node_modules/rxjs/dist/esm/internal/scheduler/asap.js","node_modules/rxjs/dist/esm/internal/scheduler/async.js","node_modules/rxjs/dist/esm/internal/scheduler/AnimationFrameAction.js","node_modules/rxjs/dist/esm/internal/scheduler/AnimationFrameScheduler.js","node_modules/rxjs/dist/esm/internal/scheduler/animationFrame.js","node_modules/rxjs/dist/esm/internal/observable/empty.js","node_modules/rxjs/dist/esm/internal/util/isScheduler.js","node_modules/rxjs/dist/esm/internal/util/args.js","node_modules/tslib/tslib.es6.mjs","node_modules/rxjs/dist/esm/internal/util/isArrayLike.js","node_modules/rxjs/dist/esm/internal/util/isPromise.js","node_modules/rxjs/dist/esm/internal/util/isInteropObservable.js","node_modules/rxjs/dist/esm/internal/util/isAsyncIterable.js","node_modules/rxjs/dist/esm/internal/util/throwUnobservableError.js","node_modules/rxjs/dist/esm/internal/symbol/iterator.js","node_modules/rxjs/dist/esm/internal/util/isIterable.js","node_modules/rxjs/dist/esm/internal/util/isReadableStreamLike.js","node_modules/rxjs/dist/esm/internal/observable/innerFrom.js","node_modules/rxjs/dist/esm/internal/util/executeSchedule.js","node_modules/rxjs/dist/esm/internal/operators/observeOn.js","node_modules/rxjs/dist/esm/internal/operators/subscribeOn.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduleObservable.js","node_modules/rxjs/dist/esm/internal/scheduled/schedulePromise.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduleArray.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduleIterable.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduleAsyncIterable.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduleReadableStreamLike.js","node_modules/rxjs/dist/esm/internal/scheduled/scheduled.js","node_modules/rxjs/dist/esm/internal/observable/from.js","node_modules/rxjs/dist/esm/internal/observable/of.js","node_modules/rxjs/dist/esm/internal/observable/throwError.js","node_modules/rxjs/dist/esm/internal/util/isObservable.js","node_modules/rxjs/dist/esm/internal/util/EmptyError.js","node_modules/rxjs/dist/esm/internal/util/isDate.js","node_modules/rxjs/dist/esm/internal/operators/map.js","node_modules/rxjs/dist/esm/internal/util/mapOneOrManyArgs.js","node_modules/rxjs/dist/esm/internal/util/argsArgArrayOrObject.js","node_modules/rxjs/dist/esm/internal/util/createObject.js","node_modules/rxjs/dist/esm/internal/observable/combineLatest.js","node_modules/rxjs/dist/esm/internal/operators/mergeInternals.js","node_modules/rxjs/dist/esm/internal/operators/mergeMap.js","node_modules/rxjs/dist/esm/internal/operators/mergeAll.js","node_modules/rxjs/dist/esm/internal/operators/concatAll.js","node_modules/rxjs/dist/esm/internal/observable/concat.js","node_modules/rxjs/dist/esm/internal/observable/defer.js","node_modules/rxjs/dist/esm/internal/observable/forkJoin.js","node_modules/rxjs/dist/esm/internal/observable/fromEvent.js","node_modules/rxjs/dist/esm/internal/observable/timer.js","node_modules/rxjs/dist/esm/internal/observable/interval.js","node_modules/rxjs/dist/esm/internal/observable/merge.js","node_modules/rxjs/dist/esm/internal/util/not.js","node_modules/rxjs/dist/esm/internal/operators/filter.js","node_modules/rxjs/dist/esm/internal/observable/partition.js","node_modules/rxjs/dist/esm/internal/operators/audit.js","node_modules/rxjs/dist/esm/internal/operators/auditTime.js","node_modules/rxjs/dist/esm/internal/operators/catchError.js","node_modules/rxjs/dist/esm/internal/operators/scanInternals.js","node_modules/rxjs/dist/esm/internal/operators/concatMap.js","node_modules/rxjs/dist/esm/internal/operators/debounceTime.js","node_modules/rxjs/dist/esm/internal/operators/defaultIfEmpty.js","node_modules/rxjs/dist/esm/internal/operators/take.js","node_modules/rxjs/dist/esm/internal/operators/ignoreElements.js","node_modules/rxjs/dist/esm/internal/operators/mapTo.js","node_modules/rxjs/dist/esm/internal/operators/delayWhen.js","node_modules/rxjs/dist/esm/internal/operators/delay.js","node_modules/rxjs/dist/esm/internal/operators/distinctUntilChanged.js","node_modules/rxjs/dist/esm/internal/operators/throwIfEmpty.js","node_modules/rxjs/dist/esm/internal/operators/finalize.js","node_modules/rxjs/dist/esm/internal/operators/first.js","node_modules/rxjs/dist/esm/internal/operators/takeLast.js","node_modules/rxjs/dist/esm/internal/operators/last.js","node_modules/rxjs/dist/esm/internal/operators/scan.js","node_modules/rxjs/dist/esm/internal/operators/share.js","node_modules/rxjs/dist/esm/internal/operators/shareReplay.js","node_modules/rxjs/dist/esm/internal/operators/skip.js","node_modules/rxjs/dist/esm/internal/operators/skipWhile.js","node_modules/rxjs/dist/esm/internal/operators/startWith.js","node_modules/rxjs/dist/esm/internal/operators/switchMap.js","node_modules/rxjs/dist/esm/internal/operators/takeUntil.js","node_modules/rxjs/dist/esm/internal/operators/takeWhile.js","node_modules/rxjs/dist/esm/internal/operators/tap.js","node_modules/@angular/core/fesm2022/core.mjs","node_modules/@angular/common/fesm2022/common.mjs","node_modules/@angular/common/fesm2022/http.mjs","node_modules/@angular/platform-browser/fesm2022/platform-browser.mjs","node_modules/@angular/forms/fesm2022/forms.mjs","node_modules/@angular/cdk/fesm2022/coercion.mjs","node_modules/@angular/cdk/fesm2022/platform.mjs","node_modules/@angular/cdk/fesm2022/text-field.mjs","node_modules/@angular/cdk/fesm2022/keycodes.mjs","node_modules/@angular/cdk/fesm2022/observers.mjs","node_modules/@angular/cdk/fesm2022/layout.mjs","node_modules/@angular/cdk/fesm2022/a11y.mjs","node_modules/@angular/cdk/fesm2022/bidi.mjs","node_modules/@angular/material/fesm2022/core.mjs","node_modules/@angular/cdk/fesm2022/observers/private.mjs","node_modules/@angular/animations/fesm2022/animations.mjs","node_modules/@angular/material/fesm2022/form-field.mjs","node_modules/@angular/material/fesm2022/input.mjs","node_modules/ngx-mask/fesm2020/ngx-mask.mjs","node_modules/@angular/cdk/fesm2022/collections.mjs","node_modules/@angular/cdk/fesm2022/scrolling.mjs","node_modules/@angular/cdk/fesm2022/portal.mjs","node_modules/@angular/cdk/fesm2022/overlay.mjs","node_modules/@angular/cdk/fesm2022/menu.mjs","node_modules/@angular/material/fesm2022/divider.mjs","node_modules/@angular/material/fesm2022/autocomplete.mjs","node_modules/@angular/material/fesm2022/button.mjs","node_modules/@angular/material/fesm2022/button-toggle.mjs","node_modules/@angular/material/fesm2022/card.mjs","node_modules/@angular/material/fesm2022/chips.mjs","node_modules/@angular/material/fesm2022/checkbox.mjs","node_modules/@angular/material/fesm2022/datepicker.mjs","node_modules/@angular/cdk/fesm2022/dialog.mjs","node_modules/@angular/material/fesm2022/dialog.mjs","node_modules/@angular/material/fesm2022/radio.mjs","node_modules/@angular/material/fesm2022/grid-list.mjs","node_modules/@angular/material/fesm2022/icon.mjs","node_modules/@angular/material/fesm2022/list.mjs","node_modules/@angular/material/fesm2022/menu.mjs","node_modules/@angular/material/fesm2022/progress-bar.mjs","node_modules/@angular/material/fesm2022/progress-spinner.mjs","node_modules/@angular/material/fesm2022/select.mjs","node_modules/@angular/material/fesm2022/sidenav.mjs","node_modules/@angular/material/fesm2022/slider.mjs","node_modules/@angular/material/fesm2022/slide-toggle.mjs","node_modules/@angular/material/fesm2022/snack-bar.mjs","node_modules/@angular/cdk/fesm2022/stepper.mjs","node_modules/@angular/material/fesm2022/stepper.mjs","node_modules/@angular/material/fesm2022/tabs.mjs","node_modules/@angular/material/fesm2022/toolbar.mjs","node_modules/@angular/material/fesm2022/tooltip.mjs","node_modules/@angular/cdk/fesm2022/table.mjs","node_modules/@angular/material/fesm2022/table.mjs","node_modules/@angular/material/fesm2022/paginator.mjs","node_modules/@angular/material/fesm2022/sort.mjs","node_modules/@angular/material/fesm2022/badge.mjs","node_modules/@angular/cdk/fesm2022/accordion.mjs","node_modules/@angular/material/fesm2022/expansion.mjs","node_modules/@angular/cdk/fesm2022/drag-drop.mjs","node_modules/@angular/material/fesm2022/bottom-sheet.mjs","src/app/modules/shared/material/material.module.ts","node_modules/@angular/router/fesm2022/router.mjs","src/app/enum/constants.ts","src/environments/environment.prod.ts","node_modules/ngx-toastr/fesm2022/ngx-toastr.mjs","src/app/services/notifications.service.ts","src/app/components/layout/chat-sidebar/components/chat-list/chat-list.interface.ts","src/app/services/chat.service.ts","src/app/components/layout/chat-sidebar/components/chat/chat.interface.ts","src/app/helpers/index.ts","node_modules/ngx-online-status/fesm2020/ngx-online-status.mjs","src/app/services/base.service.ts","node_modules/engine.io-parser/build/esm/commons.js","node_modules/engine.io-parser/build/esm/encodePacket.browser.js","node_modules/engine.io-parser/build/esm/contrib/base64-arraybuffer.js","node_modules/engine.io-parser/build/esm/decodePacket.browser.js","node_modules/engine.io-parser/build/esm/index.js","node_modules/@socket.io/component-emitter/lib/esm/index.js","node_modules/engine.io-client/build/esm/globals.js","node_modules/engine.io-client/build/esm/util.js","node_modules/engine.io-client/build/esm/contrib/parseqs.js","node_modules/engine.io-client/build/esm/transport.js","node_modules/engine.io-client/build/esm/transports/polling.js","node_modules/engine.io-client/build/esm/contrib/has-cors.js","node_modules/engine.io-client/build/esm/transports/polling-xhr.js","node_modules/engine.io-client/build/esm/transports/websocket.js","node_modules/engine.io-client/build/esm/transports/webtransport.js","node_modules/engine.io-client/build/esm/transports/index.js","node_modules/engine.io-client/build/esm/contrib/parseuri.js","node_modules/engine.io-client/build/esm/socket.js","node_modules/engine.io-client/build/esm/index.js","node_modules/socket.io-client/build/esm/url.js","node_modules/socket.io-parser/build/esm/index.js","node_modules/socket.io-parser/build/esm/is-binary.js","node_modules/socket.io-parser/build/esm/binary.js","node_modules/socket.io-client/build/esm/on.js","node_modules/socket.io-client/build/esm/socket.js","node_modules/socket.io-client/build/esm/contrib/backo2.js","node_modules/socket.io-client/build/esm/manager.js","node_modules/socket.io-client/build/esm/index.js","src/app/components/icon/icon.component.ts","src/app/components/icon/icon.component.html","node_modules/@ngx-translate/core/dist/fesm2022/ngx-translate-core.mjs","src/app/components/modals/verifying-email/verifying-email.component.ts","src/app/components/modals/verifying-email/verifying-email.component.html","node_modules/keycloak-js/dist/keycloak.mjs","node_modules/jwt-decode/build/esm/index.js","node_modules/keycloak-angular/fesm2022/keycloak-angular.mjs","src/app/components/modal/modal.component.ts","src/app/components/modal/modal.component.html","node_modules/@angular/cdk/fesm2022/clipboard.mjs","src/app/services/binotel.service.ts","src/app/services/right-sidebar.service.ts","src/app/components/button/button.component.ts","src/app/components/button/button.component.html","src/app/modals/tariff-update/tariff-update.component.ts","src/app/modals/tariff-update/tariff-update.component.html","src/app/services/theme.service.ts","src/app/components/loader/loader.component.ts","src/app/components/loader/loader.component.html","src/app/modals/themes/themes.component.ts","src/app/modals/themes/themes.component.html","src/app/services/file-system.service.ts","src/app/components/snack-bar/snack-bar.component.ts","src/app/components/snack-bar/snack-bar.component.html","src/app/services/snack-bar.service.ts","src/app/directives/dnd.directive.ts","src/app/modals/file-upload/file-upload.component.ts","src/app/modals/file-upload/file-upload.component.html","src/app/services/audio.service.ts","node_modules/docviewhelper/index.js","node_modules/ngx-doc-viewer/fesm2020/ngx-doc-viewer.mjs","src/app/components/audio-player-v2/audio-player-v2.component.ts","src/app/components/audio-player-v2/audio-player-v2.component.html","src/app/components/file-viewer/file-viewer.component.ts","src/app/components/file-viewer/file-viewer.component.html","src/app/services/modal.service.ts","src/app/services/auth.service.ts","src/app/services/gateway.service.ts","src/app/services/widgets.service.ts","src/app/components/activity/activity.service.ts","src/app/services/crm.service.ts","src/app/services/channel.service.ts","src/app/components/textarea/textarea.component.ts","src/app/components/textarea/textarea.component.html","node_modules/@ng-select/ng-select/fesm2022/ng-select-ng-select.mjs","src/app/components/chip/chip.interface.ts","src/app/components/chip/chip.component.ts","src/app/components/chip/chip.component.html","src/app/components/dropdown/dropdown.component.ts","src/app/components/dropdown/dropdown.component.html","src/app/components/text-field/text-field.component.ts","src/app/components/text-field/text-field.component.html","src/app/components/checkbox/checkbox.component.ts","src/app/components/checkbox/checkbox.component.html","src/app/components/layout/right-sidebar/components/create-widget/create-widget.component.ts","src/app/components/layout/right-sidebar/components/create-widget/create-widget.component.html","src/app/components/layout/right-sidebar/components/deal/deal.interface.ts","src/app/components/activity/activity.interface.ts","src/app/components/layout/right-sidebar/components/deal/deal.component.ts","src/app/components/layout/right-sidebar/components/deal/deal.component.html","src/app/services/contact.service.ts","src/app/dialogs/confirm/confirm.component.ts","src/app/dialogs/confirm/confirm.component.html","src/app/services/dialog.service.ts","src/app/services/custom-attributes.service.ts","src/app/services/google-address.service.ts","src/app/components/google-address-textfield/google-address-textfield.component.ts","src/app/components/google-address-textfield/google-address-textfield.component.html","src/app/components/activity/activity.component.ts","src/app/components/activity/activity.component.html","src/app/components/color/color.component.ts","src/app/components/color/color.component.html","src/app/components/layout/right-sidebar/components/header/header.component.ts","src/app/components/layout/right-sidebar/components/header/header.component.html","src/app/components/phone-field/phone-field.component.ts","src/app/components/phone-field/phone-field.component.html","src/app/components/tabs/tabs.component.ts","src/app/components/tabs/tabs.component.html","src/app/components/input/input.component.ts","src/app/components/input/input.component.html","src/app/services/ticket.service.ts","node_modules/@twogate/ngx-photo-gallery/fesm2022/twogate-ngx-photo-gallery.mjs","src/app/components/layout/right-sidebar/components/ticket/ticket.component.ts","src/app/components/layout/right-sidebar/components/ticket/ticket.component.html","src/app/components/layout/right-sidebar/components/invite-manager/invite-manager.component.ts","src/app/components/layout/right-sidebar/components/invite-manager/invite-manager.component.html","src/app/components/layout/right-sidebar/components/create-group/create-group.interface.ts","src/app/components/layout/right-sidebar/components/create-group/create-group.component.ts","src/app/components/layout/right-sidebar/components/create-group/create-group.component.html","src/app/components/layout/right-sidebar/components/update-manager/update-manager.component.ts","src/app/components/layout/right-sidebar/components/update-manager/update-manager.component.html","src/app/components/layout/right-sidebar/components/create-channel/create-channel.component.ts","src/app/components/layout/right-sidebar/components/create-channel/create-channel.component.html","src/app/services/integration.service.ts","src/app/services/ringostat.service.ts","src/app/components/layout/right-sidebar/components/integration-ringostat/integration-ringostat.component.ts","src/app/components/layout/right-sidebar/components/integration-ringostat/integration-ringostat.component.html","src/app/services/integration-telegram-bot.service.ts","src/app/components/layout/right-sidebar/components/integration-telegram-bot/integration-telegram-bot.component.ts","src/app/components/layout/right-sidebar/components/integration-telegram-bot/integration-telegram-bot.component.html","src/app/components/layout/right-sidebar/components/integration-telegram-user/integration-telegram-user.component.ts","src/app/components/layout/right-sidebar/components/integration-telegram-user/integration-telegram-user.component.html","src/app/services/integration-telegram.service.ts","src/app/services/ai.service.ts","src/app/services/payment.service.ts","src/app/model/table.model.ts","src/app/components/table/table.component.ts","src/app/components/table/table.component.html","src/app/components/table/pagination/pagination.component.ts","src/app/components/table/pagination/pagination.component.html","src/app/components/toggle/toggle.component.ts","src/app/components/toggle/toggle.component.html","src/app/components/layout/right-sidebar/components/integration-ai/integration-ai.component.ts","src/app/components/layout/right-sidebar/components/integration-ai/integration-ai.component.html","src/app/dialogs/channel/channel.component.ts","src/app/dialogs/channel/channel.component.html","src/app/components/layout/right-sidebar/components/integration-olx/integration-olx.component.ts","src/app/components/layout/right-sidebar/components/integration-olx/integration-olx.component.html","src/app/components/layout/right-sidebar/components/contacts/contacts.component.ts","src/app/components/layout/right-sidebar/components/contacts/contacts.component.html","src/app/components/layout/right-sidebar/components/reminders/reminders.component.ts","src/app/components/layout/right-sidebar/components/reminders/reminders.component.html","src/app/components/layout/right-sidebar/components/handbook/handbook.component.ts","src/app/components/layout/right-sidebar/components/handbook/handbook.component.html","src/app/services/warehouse.service.ts","src/app/components/layout/right-sidebar/components/create-category/create-category.component.ts","src/app/components/layout/right-sidebar/components/create-category/create-category.component.html","src/app/components/layout/right-sidebar/components/create-product/create-product.component.ts","src/app/components/layout/right-sidebar/components/create-product/create-product.component.html","src/app/modals/planner/planner.component.ts","src/app/modals/planner/planner.component.html","src/app/services/olx-advert.service.ts","src/app/components/layout/right-sidebar/components/create-ads/create-ads.component.ts","src/app/components/layout/right-sidebar/components/create-ads/create-ads.component.html","src/app/services/site.service.ts","src/app/components/layout/right-sidebar/components/create-site/create-site.component.ts","src/app/components/layout/right-sidebar/components/create-site/create-site.component.html","src/app/services/props.service.ts","src/app/components/layout/right-sidebar/components/create-order-site/create-order-site.component.ts","src/app/components/layout/right-sidebar/components/create-order-site/create-order-site.component.html","src/app/services/telegram.service.ts","src/app/components/layout/right-sidebar/components/integration-telegram/integration-telegram.component.ts","src/app/components/layout/right-sidebar/components/integration-telegram/integration-telegram.component.html","src/app/services/nova-poshta.service.ts","src/app/components/layout/right-sidebar/components/integration-novaposhta/integration-novaposhta.component.ts","src/app/components/layout/right-sidebar/components/integration-novaposhta/integration-novaposhta.component.html","src/app/components/layout/right-sidebar/components/integration-checkbox/integration-checkbox.component.ts","src/app/components/layout/right-sidebar/components/integration-checkbox/integration-checkbox.component.html","src/app/components/layout/right-sidebar/components/integration-ria/integration-ria.component.ts","src/app/components/layout/right-sidebar/components/integration-ria/integration-ria.component.html","src/app/modals/file-upload-new/file-upload-new.component.ts","src/app/modals/file-upload-new/file-upload-new.component.html","src/app/components/layout/right-sidebar/components/create-update-olx-advert/create-update-olx-advert.component.ts","src/app/components/layout/right-sidebar/components/create-update-olx-advert/create-update-olx-advert.component.html","src/app/components/price-field/price-field.component.ts","src/app/components/price-field/price-field.component.html","src/app/components/layout/right-sidebar/components/buy-olx-package/buy-olx-package.component.ts","src/app/components/layout/right-sidebar/components/buy-olx-package/buy-olx-package.component.html","src/app/components/layout/right-sidebar/right-sidebar.component.ts","src/app/components/layout/right-sidebar/right-sidebar.component.html","src/app/services/sidebar.service.ts","src/app/components/layout/left-menu/left-menu.component.ts","src/app/components/layout/left-menu/left-menu.component.html","src/app/dialogs/notification-popup/notification-popup.component.ts","src/app/dialogs/notification-popup/notification-popup.component.html","src/app/services/company.service.ts","src/app/services/right-menu.service.ts","src/app/services/tariff.service.ts","src/app/services/settings.service.ts","src/app/components/audio-player/audio-player.component.ts","src/app/components/audio-player/audio-player.component.html","src/app/pipes/date.pipe.ts","src/app/components/layout/header/header.component.ts","src/app/components/layout/header/header.component.html","src/app/components/layout/footer/footer.component.ts","src/app/components/layout/footer/footer.component.html","src/app/components/layout/chat-sidebar/chat-sidebar.interface.ts","src/app/services/cache.service.ts","src/app/components/search/search.component.ts","src/app/components/search/search.component.html","src/app/components/layout/chat-sidebar/components/chat-list-item/chat-list-item.component.ts","src/app/components/layout/chat-sidebar/components/chat-list-item/chat-list-item.component.html","src/app/components/layout/chat-sidebar/components/chat-list/chat-list.component.ts","src/app/components/layout/chat-sidebar/components/chat-list/chat-list.component.html","src/app/components/layout/chat-sidebar/components/chat/chat.component.ts","src/app/components/layout/chat-sidebar/components/chat/chat.component.html","src/app/services/messages-templates.service.ts","src/app/bottom-sheet/message-templates/message-templates.component.ts","src/app/bottom-sheet/message-templates/message-templates.component.html","node_modules/@ctrl/ngx-emoji-mart/fesm2020/ctrl-ngx-emoji-mart-ngx-emoji.mjs","node_modules/@ctrl/ngx-emoji-mart/fesm2020/ctrl-ngx-emoji-mart.mjs","src/app/dialogs/change-price/change-price.component.ts","src/app/dialogs/change-price/change-price.component.html","src/app/components/layout/chat-sidebar/components/chat-advert-info/chat-advert-info.interface.ts","src/app/components/layout/chat-sidebar/components/chat-advert-info/chat-advert-info.component.ts","src/app/components/layout/chat-sidebar/components/chat-advert-info/chat-advert-info.component.html","src/app/components/layout/chat-sidebar/components/chat-message/chat-message.interface.ts","src/app/components/layout/chat-sidebar/components/chat-message/chat-message.component.ts","src/app/components/layout/chat-sidebar/components/chat-message/chat-message.component.html","node_modules/pako/dist/pako.esm.mjs","src/app/services/file-save.service.ts","node_modules/marked/lib/marked.esm.js","node_modules/ngx-markdown/fesm2022/ngx-markdown.mjs","node_modules/@angular/core/fesm2022/rxjs-interop.mjs","node_modules/ngx-lottie/fesm2022/ngx-lottie.mjs","src/app/pipes/media-seconds.pipe.ts","src/app/components/round/round.component.ts","src/app/components/round/round.component.html","src/app/pipes/linkify.pipe.ts","src/app/pipes/file-size.pipe.ts","src/app/pipes/highlight.pipe.ts","src/app/pipes/safe-html.pipe.ts","src/app/pipes/truncate.pipe.ts","src/app/components/no-results/no-results.component.ts","src/app/components/no-results/no-results.component.html","src/app/directives/textarea-autoresize.directive.ts","src/app/components/layout/chat-sidebar/components/chat-settings/chat-settings.interface.ts","src/app/components/radio/radio.component.ts","src/app/components/radio/radio.component.html","src/app/components/layout/chat-sidebar/components/chat-settings/chat-settings.component.ts","src/app/components/layout/chat-sidebar/components/chat-settings/chat-settings.component.html","src/app/components/layout/chat-sidebar/components/chat-about/chat-about.interface.ts","src/app/dialogs/one-field/one-field.component.ts","src/app/dialogs/one-field/one-field.component.html","src/app/components/layout/chat-sidebar/components/chat-about/chat-about.component.ts","src/app/components/layout/chat-sidebar/components/chat-about/chat-about.component.html","src/app/components/layout/chat-sidebar/chat-sidebar.component.ts","src/app/components/layout/chat-sidebar/chat-sidebar.component.html","src/app/components/layout/layout.component.ts","src/app/components/layout/layout.component.html","src/app/components/integration-block/integration-block.component.ts","src/app/components/integration-block/integration-block.component.html","src/app/components/board/board.component.ts","src/app/components/board/board.component.html","src/app/services/profile.service.ts","src/app/pages/additional-info/additional-info.component.ts","src/app/pages/additional-info/additional-info.component.html","src/app/components/layout/right-sidebar/components/chat-list-old/chat-old/chat-old.component.ts","src/app/components/layout/right-sidebar/components/chat-list-old/chat-old/chat-old.component.html","src/app/services/logistic.service.ts","src/app/tabs/tab-invoices/tab-invoices.interface.ts","src/app/tabs/tab-invoices/tab-invoices.component.ts","src/app/tabs/tab-invoices/tab-invoices.component.html","src/app/tabs/tab-templates/tab-templates.interface.ts","src/app/components/modals/create-template/create-template.component.ts","src/app/components/modals/create-template/create-template.component.html","src/app/tabs/tab-templates/tab-templates.component.ts","src/app/tabs/tab-templates/tab-templates.component.html","src/app/tabs/tab-support-video/tab-support-video.component.ts","src/app/tabs/tab-support-video/tab-support-video.component.html","src/app/tabs/tab-support-faq/tab-support-faq.component.ts","src/app/tabs/tab-support-faq/tab-support-faq.component.html","src/app/tabs/tab-support-ticket/tab-support-ticket.component.ts","src/app/tabs/tab-support-ticket/tab-support-ticket.component.html","src/app/tabs/tab-deals-settings-common/tab-deals-settings-common.interface.ts","src/app/tabs/tab-deals-settings-common/tab-deals-settings-common.component.ts","src/app/tabs/tab-deals-settings-common/tab-deals-settings-common.component.html","src/app/components/modals/create-deals-fields/create-deals-fields.component.ts","src/app/components/modals/create-deals-fields/create-deals-fields.component.html","src/app/tabs/tab-deals-settings-fields/tab-deals-settings-fields.component.ts","src/app/tabs/tab-deals-settings-fields/tab-deals-settings-fields.component.html","src/app/tabs/tab-support-contact/tab-support-contact.component.ts","src/app/tabs/tab-support-contact/tab-support-contact.component.html","src/app/tabs/tab-logistic-telegram/tab-logistic-telegram.interface.ts","src/app/tabs/tab-logistic-telegram/tab-logistic-telegram.component.ts","src/app/tabs/tab-logistic-telegram/tab-logistic-telegram.component.html","src/app/tabs/np-tab-tracker-invoice/np-tab-tracker-invoice.interface.ts","src/app/services/ukr-poshta.service.ts","src/app/dialogs/create-track-invoice/create-track-invoice.component.ts","src/app/dialogs/create-track-invoice/create-track-invoice.component.html","src/app/tabs/np-tab-tracker-invoice/np-tab-tracker-invoice.component.ts","src/app/tabs/np-tab-tracker-invoice/np-tab-tracker-invoice.component.html","src/app/tabs/ukr-tab-tracker-invoice/ukr-tab-tracker-invoice.interface.ts","src/app/tabs/ukr-tab-tracker-invoice/ukr-tab-tracker-invoice.component.ts","src/app/tabs/ukr-tab-tracker-invoice/ukr-tab-tracker-invoice.component.html","src/app/dialogs/create-funnel/create-funnel.component.ts","src/app/dialogs/create-funnel/create-funnel.component.html","src/app/modals/create-column/create-column.component.ts","src/app/modals/create-column/create-column.component.html","src/app/tabs/tab-funnel-columns/tab-funnel-columns.component.ts","src/app/tabs/tab-funnel-columns/tab-funnel-columns.component.html","src/app/pipes/days-left.pipe.ts","src/app/modules/power-olx/components/my-packets/my-packets.component.ts","src/app/modules/power-olx/components/my-packets/my-packets.component.html","src/app/modules/power-olx/components/my-adverts/my-adverts.interface.ts","src/app/modules/power-olx/components/my-adverts/my-adverts.component.ts","src/app/modules/power-olx/components/my-adverts/my-adverts.component.html","src/app/modules/power-olx/components/my-templates/my-templates.interface.ts","src/app/modules/power-olx/components/my-templates/my-templates.component.ts","src/app/modules/power-olx/components/my-templates/my-templates.component.html","src/app/modules/crm/team/components/team/team.interface.ts","src/app/tabs/tab-managers/tab-managers.component.ts","src/app/tabs/tab-managers/tab-managers.component.html","src/app/tabs/tab-groups/tab-groups.component.ts","src/app/tabs/tab-groups/tab-groups.component.html","src/app/tabs/tab-inviters/tab-inviters.component.ts","src/app/tabs/tab-inviters/tab-inviters.component.html","src/app/modules/crm/team/components/team/team.component.ts","src/app/modules/crm/team/components/team/team.component.html","src/app/dialogs/archived-channel/archived-channel.component.ts","src/app/dialogs/archived-channel/archived-channel.component.html","src/app/modules/power-olx/components/my-planners/my-planners.interface.ts","src/app/modules/power-olx/components/my-planners/my-planners.component.ts","src/app/modules/power-olx/components/my-planners/my-planners.component.html","src/app/services/market.service.ts","src/app/modals/chanel-right-bar/chanel-right-bar.component.ts","src/app/modals/chanel-right-bar/chanel-right-bar.component.html","src/app/modules/power-olx/components/my-backups/my-backups.interface.ts","src/app/modules/power-olx/components/my-backups/my-backups.component.ts","src/app/modules/power-olx/components/my-backups/my-backups.component.html","src/app/modules/crm/settings/components/profile/profile.interface.ts","src/app/modules/crm/settings/components/profile/profile.component.ts","src/app/modules/crm/settings/components/profile/profile.component.html","src/app/modules/crm/settings/components/settings/settings.component.ts","src/app/modules/crm/settings/components/settings/settings.component.html","src/app/pages/home/home.component.interface.ts","src/app/pages/home/home.component.ts","src/app/pages/home/home.component.html","src/app/components/modals/reset-password/reset-password.component.ts","src/app/components/modals/reset-password/reset-password.component.html","src/app/pages/login/login.component.ts","src/app/pages/login/login.component.html","node_modules/ngx-slick-carousel-ver2/fesm2022/ngx-slick-carousel-ver2.mjs","src/app/pages/companies/companies.component.ts","src/app/pages/companies/companies.component.html","src/app/pages/single-auth/single-auth.component.ts","src/app/pages/policy/policy.component.interface.ts","src/app/pages/policy/policy.component.ts","src/app/pages/policy/policy.component.html","src/app/modules/market/components/market/market.interface.ts","src/app/modules/market/components/market/market.component.ts","src/app/modules/market/components/market/market.component.html","node_modules/@ngx-translate/http-loader/dist/fesm2022/ngx-translate-http-loader.mjs","node_modules/@amcharts/amcharts5/.internal/core/util/Percent.js","node_modules/@amcharts/amcharts5/.internal/core/util/Type.js","node_modules/@amcharts/amcharts5/.internal/core/util/Array.js","node_modules/@amcharts/amcharts5/.internal/core/util/Object.js","node_modules/@amcharts/amcharts5/.internal/core/util/Disposer.js","node_modules/@amcharts/amcharts5/.internal/core/util/Utils.js","node_modules/@amcharts/amcharts5/.internal/core/util/Color.js","node_modules/@amcharts/amcharts5/.internal/core/util/EventDispatcher.js","node_modules/@amcharts/amcharts5/.internal/core/util/Animation.js","node_modules/@amcharts/amcharts5/.internal/core/util/List.js","node_modules/@amcharts/amcharts5/.internal/core/util/Children.js","node_modules/@amcharts/amcharts5/.internal/core/util/Math.js","node_modules/@amcharts/amcharts5/.internal/core/util/Ease.js","node_modules/@amcharts/amcharts5/.internal/core/util/States.js","node_modules/@amcharts/amcharts5/.internal/core/Registry.js","node_modules/@amcharts/amcharts5/.internal/core/util/Order.js","node_modules/@amcharts/amcharts5/.internal/core/util/Entity.js","node_modules/@amcharts/amcharts5/.internal/core/util/Template.js","node_modules/@amcharts/amcharts5/.internal/core/render/Sprite.js","node_modules/@amcharts/amcharts5/.internal/core/render/patterns/Pattern.js","node_modules/@amcharts/amcharts5/.internal/core/render/patterns/PicturePattern.js","node_modules/@amcharts/amcharts5/.internal/core/render/backend/Renderer.js","node_modules/@amcharts/amcharts5/.internal/core/render/Graphics.js","node_modules/@amcharts/amcharts5/.internal/core/render/Rectangle.js","node_modules/@amcharts/amcharts5/.internal/core/render/Layout.js","node_modules/@amcharts/amcharts5/.internal/core/render/HorizontalLayout.js","node_modules/@amcharts/amcharts5/.internal/core/render/VerticalLayout.js","node_modules/@amcharts/amcharts5/.internal/core/render/GridLayout.js","node_modules/@amcharts/amcharts5/.internal/core/util/TextFormatter.js","node_modules/@amcharts/amcharts5/.internal/core/util/PopulateString.js","node_modules/@amcharts/amcharts5/.internal/core/render/Container.js","node_modules/@amcharts/amcharts5/.internal/core/render/Text.js","node_modules/@amcharts/amcharts5/.internal/core/util/ResizeSensor.js","node_modules/@amcharts/amcharts5/.internal/core/util/InterfaceColors.js","node_modules/@amcharts/amcharts5/.internal/core/render/Label.js","node_modules/@amcharts/amcharts5/.internal/core/render/PointedRectangle.js","node_modules/@amcharts/amcharts5/.internal/core/render/Tooltip.js","node_modules/@amcharts/amcharts5/.internal/core/util/NumberFormatter.js","node_modules/@amcharts/amcharts5/.internal/core/util/Timezone.js","node_modules/@amcharts/amcharts5/.internal/core/util/DateFormatter.js","node_modules/@amcharts/amcharts5/.internal/core/util/DurationFormatter.js","node_modules/@amcharts/amcharts5/locales/en.js","node_modules/@amcharts/amcharts5/.internal/core/util/Language.js","node_modules/@amcharts/amcharts5/.internal/core/Theme.js","node_modules/@amcharts/amcharts5/.internal/themes/DefaultTheme.js","node_modules/@amcharts/amcharts5/.internal/core/util/Matrix.js","node_modules/svg-arc-to-cubic-bezier/modules/index.js","node_modules/@amcharts/amcharts5/.internal/core/render/backend/CanvasRenderer.js","node_modules/@amcharts/amcharts5/.internal/core/Root.js","node_modules/@amcharts/amcharts5/.internal/core/render/RoundedRectangle.js","node_modules/@amcharts/amcharts5/.internal/core/render/Button.js","node_modules/@amcharts/amcharts5/.internal/core/util/Data.js","node_modules/@amcharts/amcharts5/.internal/core/render/Component.js","node_modules/@amcharts/amcharts5/.internal/core/util/Time.js","node_modules/@amcharts/amcharts5/.internal/core/render/Series.js","node_modules/@amcharts/amcharts5/.internal/core/render/Legend.js","node_modules/@amcharts/amcharts5/.internal/core/util/Draw.js","node_modules/@amcharts/amcharts5/.internal/core/render/Line.js","node_modules/@amcharts/amcharts5/.internal/core/render/Scrollbar.js","node_modules/d3-shape/src/constant.js","node_modules/d3-path/src/path.js","node_modules/d3-shape/src/path.js","node_modules/d3-shape/src/array.js","node_modules/d3-shape/src/curve/linear.js","node_modules/d3-shape/src/point.js","node_modules/d3-shape/src/line.js","node_modules/d3-shape/src/area.js","node_modules/@amcharts/amcharts5/.internal/core/render/Chart.js","node_modules/@amcharts/amcharts5/.internal/core/render/SerialChart.js","node_modules/@amcharts/amcharts5/.internal/core/render/Tick.js","node_modules/@amcharts/amcharts5/.internal/core/util/ColorSet.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/XYChartDefaultTheme.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/XYChart.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/Grid.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/XYCursor.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/series/XYSeries.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/Axis.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/ValueAxis.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/CategoryAxis.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/AxisLabel.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/AxisTick.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/AxisRenderer.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/AxisRendererX.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/axes/AxisRendererY.js","node_modules/@amcharts/amcharts5/.internal/charts/xy/series/LineSeries.js","node_modules/@amcharts/amcharts5/.internal/themes/AnimatedTheme.js","node_modules/@amcharts/amcharts5/themes/Animated.js","src/app/components/chart/chart.component.ts","src/app/components/chart/chart.component.html","src/app/tabs/tab-categories/tab-categories.interface.ts","src/app/tabs/tab-categories/tab-categories.component.ts","src/app/tabs/tab-categories/tab-categories.component.html","src/app/modules/crm/settings/components/company-settings/company-settings.component.ts","src/app/modules/crm/settings/components/company-settings/company-settings.component.html","src/app/tabs/tab-products/tab-products.interface.ts","src/app/tabs/tab-products/tab-products.component.ts","src/app/tabs/tab-products/tab-products.component.html","src/app/modules/power-olx/components/my-deliveries/my-deliveries.component.ts","src/app/modules/power-olx/components/my-deliveries/my-deliveries.component.html","src/app/components/funnel-filter/funnel-filter.component.ts","src/app/components/funnel-filter/funnel-filter.component.html","src/app/components/modals/add-field-filter/add-field-filter.component.ts","src/app/components/modals/add-field-filter/add-field-filter.component.html","src/app/modals/planner-automation/planner-automation.component.ts","src/app/modals/planner-automation/planner-automation.component.html","src/app/pages/register/register.component.ts","src/app/pages/register/register.component.html","src/app/modules/shared/shared.module.ts"],"sourcesContent":["{\n \"choose_best_plan\": \"Не втрачайте час, виберіть план, який найкраще відповідає вашим потребам, і почніть насолоджуватися всіма перевагами прямо зараз!\",\n \"update_ad_price\": \"Потрібно змінити ціну на вашому оголошенні? Встановіть PowerOLX та змініть ціну на вашому оголошенні прямо в чаті!\",\n \"internal_number_of_the_shipment\": \"Внутрішній номер відправлення\",\n \"accompanying_documents\": \"Супровідні документи\",\n \"activate_promo_code\": \"Активувати промокод\",\n \"add\": \"Додати\",\n \"change\": \"Змінити\",\n \"reset\": \"Скинути\",\n \"add_API_Key\": \"Додати ключ API\",\n \"add_a_column\": \"Додати колонку\",\n \"add_a_phone_number\": \"Додати номер телефону\",\n \"add_phone_number\": \"Додати номер телефону\",\n \"go_back\": \"Повернутися назад\",\n \"add_place\": \"Додати місце\",\n \"add_quick_answers\": \"Додати швидкі відповіді\",\n \"add_ttn\": \"Додати ТТН\",\n \"add_users_who_will_have_access_to_Telegram\": \"Додайте користувачів, які матимуть доступ до Telegram\",\n \"additional_information\": \"Додаткова інформація\",\n \"private_or_business\": \"Приватний чи бізнес\",\n \"private_person\": \"Приватна особа\",\n \"business\": \"Бізнес\",\n \"additional_information_about_the_shipment\": \"Додаткова інформація про відправлення\",\n \"additional_services\": \"Додаткові послуги\",\n \"address\": \"Адреса\",\n \"after_payment,_please_click_the_button_to_notify_the_manager_about_the_payment\": \"\\nпісля оплати, будь ласка, натисніть кнопку, щоб повідомити менеджера про оплату\",\n \"and_must_be_an_email_address\": \"і повинен бути адресою електронної пошти\",\n \"answers_to_questions_in_our_group\": \"Відповіді на питання в нашій групі\",\n \"assign_and_save\": \"призначити та зберегти\",\n \"buy\": \"Купити\",\n \"buy_tariff\": \"Купити тариф\",\n \"buy_package\": \"Купити пакет\",\n \"cancel\": \"Скасувати\",\n \"cart\": \"Кошик\",\n \"cash\": \"Готівка\",\n \"cashless\": \"Безготівковий\",\n \"category\": \"Категорія\",\n \"channel\": \"Канал\",\n \"city\": \"Місто\",\n \"click_the_button\": \"натисніть кнопку\",\n \"climbing_to_the_floor\": \"Підйом на поверх\",\n \"close\": \"Закрити\",\n \"comment\": \"Коментар\",\n \"connect\": \"Підключити\",\n \"contact\": \"Контакт\",\n \"contacts\": \"Контакти\",\n \"control_of_piecewise_transmission\": \"Контроль часткової передачі\",\n \"copy_place\": \"Копіювати місце\",\n \"costs\": \"Витрати\",\n \"create\": \"Створити\",\n \"create_channel\": \"Створити канал\",\n \"create_company\": \"Створити компанію\",\n \"create_contact\": \"Створити контакт\",\n \"create_folder\": \"Створити папку\",\n \"create_key\": \"Створити ключ\",\n \"create_role\": \"Створити роль\",\n \"create_template\": \"Створити шаблон\",\n \"current_account\": \"Поточний акаунт\",\n \"cut\": \"Вирізати\",\n \"created_at\": \"Дата створення\",\n \"updated_at\": \"Дата оновлення\",\n \"delete\": \"Видалити\",\n \"delete_a_place\": \"Видалити місце\",\n \"delete_funnel\": \"Видалити воронку\",\n \"delivery_time\": \"Час доставки\",\n \"description\": \"Опис\",\n \"disable\": \"Відключити\",\n \"documents\": \"Документи\",\n \"enter_first_name\": \"Введіть ім'я\",\n \"enter_last_name\": \"Введіть прізвище\",\n \"enter_TTN_or_search_by_description\": \"Введіть ТТН або шукайте за описом\",\n \"enter_it_in_the_field_above_and_click_add\": \"Введіть його в поле вище і натисніть Додати\",\n \"enter_phone_number\": \"Введіть номер телефону\",\n \"enter_the_address\": \"Введіть адресу\",\n \"enter_your_e-mail\": \"Введіть свою електронну пошту\",\n \"enter_your_phone_number\": \"Введіть свій номер телефону\",\n \"enter_password\": \"Введіть пароль\",\n \"enter_password_confirmation\": \"Введіть підтвердження паролю\",\n \"email_format_is_incorrect\": \"Неправильний формат електронної пошти\",\n \"enter_promo_code\": \"Вкажіть промокод\",\n \"exit\": \"Вийти\",\n \"form_of_payment_for_delivery\": \"Форма оплати за доставку\",\n \"from_the_transaction\": \"Від операції\",\n \"general\": \"Загальні\",\n \"go_to_the_marketplace\": \"Перейти на маркетплейс\",\n \"go_to_the_settings_and_select_the_section\": \"Перейдіть до налаштувань і виберіть розділ\",\n \"here_you_can_enter/change_your_company_name\": \"Тут ви можете ввести/змінити назву вашої компанії:\",\n \"in_detail\": \"Детально\",\n \"in_the_comments_to_the_payment,_enter_the_code\": \"в коментарях до оплати введіть код\",\n \"connection_instructions_a_api_key\": \"Інструкція по підключенню нового ключа API\",\n \"connection_instructions\": \"Інструкція по підключенню\",\n \"buy_ai\": \"У вас не встановлено штучний інтелект, натисніть «Так», щоб встановити\",\n \"integration_of_Nova_Poshta\": \"Інтеграція Нової Пошти\",\n \"language\": \"Мова\",\n \"lazy_loading\": \"Підгрузка внизу сторінки (lazy loading)\",\n \"length\": \"Довжина\",\n \"log_in_with_your_OLX_account\": \"Увійдіть за допомогою свого акаунту OLX\",\n \"log_in_with_your_RIA_account\": \"Увійдіть за допомогою свого акаунту RIA\",\n \"log_in_with_your_telegram_account\": \"Увійдіть за допомогою свого акаунту Telegram\",\n \"connect_olx\": \"Підключення OLX каналів\",\n \"choose_channel\": \"Виберіть канал:\",\n \"mail\": \"Пошта\",\n \"main_currency\": \"Основна валюта\",\n \"manual_processing\": \"Ручна обробка\",\n \"mark_as_read\": \"Відмічати повідомлення як прочитані при відкритті\",\n \"max_characters\": \"максимальна кількість символів\",\n \"min_characters\": \"мінімальна кількість символів\",\n \"max_value\": \"Максимальне значення\",\n \"min_value\": \"Мінімальне значення\",\n \"min_length\": \"Введіть щонайменше 40 символів\",\n \"planner_min_min_length\": \"Введіть щонайменше 4 символа\",\n \"minimum\": \"Мінімум\",\n \"money_transfer\": \"Грошовий переказ\",\n \"name\": \"Ім'я\",\n \"apply_to_all\": \"Застосувати для всіх\",\n \"moving_up_the_list\": \"Підняти в гору\",\n \"easy_start\": \"Легкий старт\",\n \"quick_sale\": \"Швидкий продаж\",\n \"turbo_sale\": \"Турбо продаж\",\n \"name_card_cannot_be_empty\": \"Назва картки не може бути порожньою\",\n \"name_channel\": \"Назва каналу\",\n \"type_channel\": \"Тип каналу\",\n \"name_column\": \"Назва колонки\",\n \"is_read_messages\": \"Читати повідомлення\",\n \"is_create_deal\": \"Створити угоду\",\n \"name_company\": \"Назва компанії\",\n \"name_field\": \"Назва поля\",\n \"name_funnel\": \"Назва воронки\",\n \"name_office\": \"Назва офісу\",\n \"name_role\": \"Назва ролі\",\n \"name_template\": \"Назва шаблону\",\n \"notification_sound\": \"Звук сповіщень\",\n \"notifications\": \"Сповіщення\",\n \"notify\": \"Повідомити\",\n \"nova_poshta\": \"Нова Пошта\",\n \"upgrade_plan\": \"Зараз найкращий час оновити свій план і отримати доступ до повного спектру функцій та можливостей, які пропонує {{NAME_PROJECT}}\",\n \"number_of_places\": \"Кількість місць\",\n \"open_a_chat\": \"Відкрити чат\",\n \"optional\": \"Необов'язково\",\n \"package_number\": \"Номер пакету\",\n \"packaging\": \"Пакування\",\n \"pallets\": \"Палети\",\n \"parcels_and_cargoes\": \"Посилки і вантажі\",\n \"pay\": \"Оплатити\",\n \"payer_for_delivery\": \"Платник за доставку\",\n \"payment_control\": \"Контроль оплати\",\n \"payment_method\": \"Спосіб оплати\",\n \"phone\": \"Телефон\",\n \"phone_number_is_missing\": \"Відсутній номер телефону\",\n \"pin\": \"Закріпити\",\n \"please_click_the_manager_notification_button\": \"будь ласка, натисніть кнопку сповіщення менеджера\",\n \"policy_file_cookie\": \"Політика файлів cookie\",\n \"price\": \"Ціна\",\n \"private_policy\": \"Приватна політика\",\n \"product_description\": \"Опис товару\",\n \"promo_code\": \"Промокод\",\n \"recipient\": \"Одержувач\",\n \"reconnect\": \"Перепідключити\",\n \"return_delivery_of_document_subtypes\": \"Зворотння доставки підтипів документів\",\n \"return_delivery_of_documents\": \"Зворотння доставки документів\",\n \"return_delivery_of_signed_documents\": \"Зворотння доставки підписаних документів\",\n \"save\": \"Зберегти\",\n \"order\": \"Замовити\",\n \"security\": \"Безпека\",\n \"sender\": \"Відправник\",\n \"shipping_parameters\": \"Параметри відправлення\",\n \"status\": \"Статус\",\n \"telephone\": \"Телефон\",\n \"account_connected_messenger\": \"Акаунт підключено, тепер ви можете використовувати\",\n \"on_any_device\": \"на будь-якому пристрої та отримувати всі сповіщення, які надходять на цей акаунт OLX, безпосередньо в месенджер\",\n \"the_bot_is_available_at_this_link\": \"Бот доступний за цим посиланням\",\n \"the_declared_cost\": \"Заявлена вартість\",\n \"the_dollar_rate_is_calculated_by\": \"Курс долара розраховується за:\",\n \"new_plan_benefits\": \"Новий план відкриє для вас безмежні можливості для ефективного управління вашим бізнесом.\",\n \"there_are_no_columns\": \"Немає колонок\",\n \"field_cannot_be_empty\": \"Це поле не може бути порожнім.\",\n \"email_field_cannot_be_empty\": \"Це поле не може бути порожнім і має бути у форматі електронної пошти.\",\n \"tires_and_wheels\": \"Шини і диски\",\n \"total_volume\": \"Загальний об'єм\",\n \"total_weight\": \"Загальна вага\",\n \"type_of_logistics\": \"Тип логістики\",\n \"unpin\": \"Відкріпити\",\n \"update\": \"Оновити\",\n \"updated\": \"Оновлено\",\n \"updating\": \"Оновлення\",\n \"upload_file\": \"Завантажити файл\",\n \"uploaded_files\": \"Завантажні файли\",\n \"or_paste_a_link\": \"Або вставте посилання з Інтернету\",\n \"upload\": \"Завантажити\",\n \"use_promo_code\": \"Використати промокод\",\n \"volumetric_weight\": \"Об'ємна вага\",\n \"width\": \"Ширина\",\n \"weight\": \"Вага\",\n \"you_can_delete_at_any_time\": \"Ви можете видалити в будь-який момент\",\n \"you_will_be_provided_with_a_new_API_key\": \"Вам буде надано новий ключ API\",\n \"your_phone_number\": \"Ваш номер телефону\",\n \"аdd_field\": \"Додати поле\",\n \"channels\": \"Канали\",\n \"telegram_bot\": \"ТЕЛЕГРАМ БОТ\",\n \"close_chat\": \"Закрити чат\",\n \"go_to_the_Telegram_bot_and_register\": \"Перейдіть до бота Telegram і зареєструйтеся\",\n \"the_site_is_available_at_this_link\": \"Сайт доступний за цим посиланням\",\n \"account_settings\": \"Налаштування акаунту\",\n \"please_list_the_following\": \"Будь ласка, оплатіть\",\n \"this_account_is_paused:_please_contact_the_administrator\": \"Цей акаунт призупинено: будь ласка, зв'яжіться з адміністратором\",\n \"error_receiving_message:_please_contact_the_administrator\": \"Помилка отримання повідомлення: будь ласка, зв'яжіться з адміністратором\",\n \"more_about_the_functionality\": \"Більше про функціонал\",\n \"tariff_plan\": \"Тарифні плани\",\n \"your_tariff\": \"Ваш тариф\",\n \"learn_more_about_our_products_and_how_they_work_in_our_short_video\": \"Дізнайтеся більше про наші продукти та як вони працюють у нашому короткому відео\",\n \"see_how_our_solutions_can_make_your_life_and_business_easier\": \"Переконайтеся, як наші рішення можуть полегшити ваше життя та бізнес\",\n \"we_offer_you_the_following_pricing_plans\": \"Ми пропонуємо вам наступні тарифні плани\",\n \"our_primary_goal_is_your_complete_satisfaction._We_dedicate_many_hours_and_efforts_to_provide_you_with_the_best_service_and_provide_you_with_solutions_that_truly_meet_your_needs\": \"нашою основною метою є ваша повна задоволеність. Ми віддаємо багато годин і зусиль, щоб надати вам найкращий сервіс та надати вам рішення, які дійсно відповідають вашим потребам\",\n \"user\": \"Користувач\",\n \"allow\": \"Дозволити\",\n \"we_care_about_our_customers\": \"Ми турбуємося про наших клієнтів\",\n \"amount\": \"Сума\",\n \"tariff_limitations\": \"Обмеження тарифу\",\n \"month\": \"Місяць\",\n \"months\": \"Місяці\",\n \"enjoy_the_video\": \"Насолоджуйтесь відео\",\n \"application\": \"Застосунок\",\n \"a_new_window_will_appear_in_which\": \"З'явиться нове вікно, в якому\",\n \"you_need_to_grant_access_to_your_OLX_account.\": \"Потрібно надати доступ до акаунту OLX\",\n \"in_the_account_connection_tab,_click_the_button\": \"У вкладці підключення акаунту натисніть кнопку\",\n \"using_your_credentials\": \"використовуючи ваші облікові дані\",\n \"by_choosing_us_you_get\": \"Вибираючи нас, ви отримуєте\",\n \"in_a_new_tab,_go_to\": \"У новій вкладці перейдіть на\",\n \"UAH_to_the_account\": \"грн на рахунок\",\n \"for_7_days\": \"на 7 днів\",\n \"you_are_our_main_goal_and_we_are_proud_of_our_ability_to_make_your_life_better\": \"Ви - наша головна мета, і ми пишаємося можливістю поліпшити ваше життя\",\n \"channel_settings\": \"Налаштування каналу\",\n \"copy_the_message\": \"Скопіюйте повідомлення\",\n \"save_as_a_quick_reply\": \"Зберегти як швидку відповідь\",\n \"check_if_read\": \"Перевірити, чи прочитано\",\n \"send_again\": \"Відправити ще раз\",\n \"customize\": \"Налаштувати\",\n \"the_selected_subdomain_may_already_be_taken._Please_select_another_subdomain_up_to_10_characters_using_only_lowercase-Latin_letters_and_a_hyphen._The_hyphen_cannot_be_the_last_character_of_the_subdomain\": \"Вибраний піддомен може бути вже зайнятий. Будь ласка, виберіть інший піддомен до 10 символів, використовуючи лише строчні латинські літери та дефіс. Дефіс не може бути останнім символом піддомену\",\n \"you_can_change_the_company's_subdomain_only_for_the_first_3_days\": \"Ви можете змінити піддомен компанії лише протягом перших 3 днів\",\n \"save_an_already_sent_message_as_a_quick_reply_by_right-clicking_on_the_message\": \"Збережіть вже відправлене повідомлення як швидку відповідь, клацнувши правою кнопкою миші на повідомленні\",\n \"use_quick,_ready_to_use_answers_to_the_most_frequently_asked_questions_from_customers\": \"Використовуйте швидкі, готові до використання відповіді на найчастіші питання від клієнтів\",\n \"is_a_modern_and_innovative_CRM_system_that_transforms_your_business_into_a_more_efficient_and_productive_one._Our_platform_specializes_in_optimizing_the_work_with_popular_online_resources_such_as_OLX,_providing_many_opportunities_to_automate_and_simplify_your_daily_tasks\": \"це сучасна та інноваційна CRM-система, яка перетворює ваш бізнес на більш ефективний та продуктивний. Наша платформа спеціалізується на оптимізації роботи з популярними онлайн-ресурсами, такими як OLX, надаючи багато можливостей для автоматизації та спрощення вашої щоденної роботи\",\n \"home\": \"Головна\",\n \"home_page\": \"Головна сторінка\",\n \"name_color\": \"Колір назви\",\n \"profile\": \"Профіль\",\n \"no_dialog_found\": \"Діалог не знайдено\",\n \"change_password\": \"Змінити пароль\",\n \"pinned_messages\": \"Закріплені повідомлення\",\n \"change_the_name\": \"Змінити назву\",\n \"announcements\": \"Оголошення\",\n \"auto_reply_to_the_first_message\": \"Автовідповідь на перше повідомлення\",\n \"UAH\": \"грн\",\n \"end_the_conversation_t\": \"Завершити розмову\",\n \"details_of_the_ad\": \"Деталі оголошення\",\n \"block\": \"Заблокувати\",\n \"update_your_channel\": \"Оновіть свій канал\",\n \"create_a_new_channel\": \"Створити новий канал\",\n \"responsible_for_the_channel\": \"Відповідальний за канал\",\n \"select_a_client\": \"Виберіть клієнта\",\n \"choose_role\": \"Виберіть роль\",\n \"select\": \"Вибрати\",\n \"your_address\": \"Ваша адреса\",\n \"upload_more\": \"Завантажити ще\",\n \"read\": \"Прочитати\",\n \"is_read\": \"Прочитаний\",\n \"read_all\": \"Прочитати все \",\n \"start_a_dialog_by_selecting_a_contact_from_the_list_on_the_left\": \"Розпочніть діалог, вибравши контакт зі списку зліва\",\n \"first_new\": \"Спочатку нові\",\n \"first_unread\": \"Спочатку непрочитані\",\n \"topics\": \"Теми\",\n \"mark_as_reads\": \"Відмітити як прочитане\",\n \"mark_as_unread\": \"Відмітити як непрочитане\",\n \"keyboard_shortcut_to_send_a_message\": \" Комбінація клавіш для відправлення повідомлення\",\n \"mark_messages_as_read_when_opened\": \"Відмічати повідомлення як прочитані при відкритті\",\n \"saved_quick_replies\": \"Збережені швидкі відповіді\",\n \"quick_replies\": \"Швидкі відповіді\",\n \"new_line\": \"Новий рядок\",\n \"unfollow\": \"Відписатися\",\n \"create_a_new_one_in_chat_settings\": \"Створити новий в налаштуваннях чату\",\n \"enable_auto_reply\": \"Увімкнути автовідповідь\",\n \"column_color\": \"Колір колонки\",\n \"unlock_your_potential\": \"Розблокуйте свій потенціал\",\n \"choose_rate\": \"Виберіть тариф\",\n \"selected\": \"Вибрано\",\n \"create_new_role\": \"Створити нову роль\",\n \"update_your_role\": \"Оновіть свою роль\",\n \"information\": \"Інформація\",\n \"information_about\": \"інформація про\",\n \"click_here\": \"Натисніть тут\",\n \"your_account_information\": \"Інформація про ваш акаунт\",\n \"available_balance\": \"Доступний баланс\",\n \"you_can_choose_from_several_options\": \"Ви можете вибрати з кількох варіантів\",\n \"bonuses\": \"Бонуси\",\n \"display_in_quick_trades\": \"Відображати в швидких угодах\",\n \"the_user_is_entitled_to\": \"Користувач має право на\",\n \"enter_the_name_of_the\": \"Введіть назву\",\n \"type_of_scheduler\": \"Тип планувальника\",\n \"status_of_scheduler\": \"Статус планувальника\",\n \"new\": \"Новинка\",\n \"or_transfer_files\": \"або передавати файли\",\n \"now_you_can\": \"Тепер ви можете керувати всіма своїми повідомленнями та запитаннями з OLX в одному місці, відповідаючи на них швидко та ефективно. та відповідайте на них швидко та ефективно. Забудьте про необхідність входу в різні акаунти - ми зробимо спілкування через OLX простим та зручним\",\n \"communicate_with_your_customers_easily_and_efficiently._Our_service_combines_all_your_OLX_accounts_in_one_convenient_interface\": \"спілкуйтеся зі своїми клієнтами легко та ефективно. Наш сервіс об'єднує всі ваші акаунти OLX в одному зручному інтерфейсі\",\n \"messages_from_all_accounts_in_a_convenient_interface\": \"повідомлення з усіх акаунтів в зручному інтерфейсі\",\n \"introducing_the_PowerOLX_app\": \"представляємо додаток PowerOLX\",\n \"powerOLX_is_a_JECRM_tool_that_allows_you_to_automate_the_work_with_ads_on_OLX,_activate_your_ads_instantly_or_on_a_schedule._This_allows_you_to_reach_a_larger_audience_and_get_more_sales\": \"PowerOLX - це інструмент JECRM, який дозволяє автоматизувати роботу з оголошеннями на OLX, активувати ваші оголошення миттєво або за розкладом. Це дозволяє вам дістатися д�� більшої аудиторії та отримати більше продажів\",\n \"we_invite_you_to_test_PowerOLX_in_the_Applications_section_JECRM_System_Store\": \"ми запрошуємо вас протестувати PowerOLX в розділі Додатки - Магазин систем JECRM\",\n \"Log_in_to_your_OLX_account_in_this_browser\": \"Увійдіть в свій акаунт OLX в цьому браузері\",\n \"instructions_for_obtaining_account_data\": \"Інструкція по отриманню даних акаунта\",\n \"pen_the_browser_in_which_you_log_in_to_the_OLX_account_you_need\": \"Відкрийте браузер, в якому ви увійшли в акаунт OLX, який вам потрібен\",\n \"domain\": \"домен\",\n \"business_account\": \"Бізнес-акаунт\",\n \"yes\": \"Так\",\n \"no\": \"Ні\",\n \"phone_number\": \"Номер телефону\",\n \"test_mode\": \"Тестовий режим\",\n \"in_development\": \"В розробці\",\n \"coming_soon\": \"Скоро\",\n \"support\": \"Підтримка\",\n \"structure\": \"Структура\",\n \"CRM\": \"CRM\",\n \"Integrations\": \"Інтеграції\",\n \"to_connect_your_cash_register\": \"Для підключення вашого касового апарату вам потрібно ввести логін та пароль касира (або пін-код) та ліцензію Checkbox у формі\",\n \"to_connect_your_integration,_you_need_to_enter_the_API_key_in_the_form\": \"Для підключення вашої інтеграції вам потрібно ввести ключ API у формі\",\n \"integration_setup_is_very_simple_and_fast\": \"Налаштування інтеграції дуже просте і швидке\",\n \"logistics\": \"Логістика\",\n \"chats\": \"Чати\",\n \"JE_Space\": \"JE Space\",\n \"automation\": \"Автоматизація\",\n \"applications\": \"Застосунки\",\n \"API_key_settings\": \"Налаштування ключа API\",\n \"funnel_settings\": \"Налаштування воронки\",\n \"settings\": \"Налаштування\",\n \"Payment\": \"Оплата\",\n \"PowerOLX\": \"PowerOLX\",\n \"store\": \"Крамниця\",\n \"warehouse\": \"Склад\",\n \"products\": \"Продукти\",\n \"categories\": \"Категорії\",\n \"people\": \"Люди\",\n \"disk\": \"Диск\",\n \"User_profile\": \"Профіль користувача\",\n \"deals\": \"Угоди\",\n \"ringostat\": \"Ringostat\",\n \"ukr_poshta\": \"Укр Пошта\",\n \"opened\": \"Відкриті\",\n \"closed\": \"Закриті\",\n \"externals\": \"Зовнішні\",\n \"search\": \"Пошук\",\n \"fast_answer\": \"Швидкі відповіді\",\n \"blocked_users\": \"Заблоковані користувачі\",\n \"hot_key\": \"Гарячі клавіші\",\n \"team\": \"Команда\",\n \"backup_is_done_automatically_every_7_days\": \"Резервне копіювання робиться автоматично раз у 7 днів\",\n \"page\": \"Сторінка\",\n \"last_name\": \"Прізвище\",\n \"warning\": \"Увага\",\n \"end\": \"Завершити\",\n \"search_message\": \"Пошук повідомлення\",\n \"login\": \"Увійти\",\n \"connected\": \"Підключено\",\n \"telegram\": \"Telegram\",\n \"olx_message\": \"OLX\",\n \"telephony\": \"Телефонія\",\n \"month_name\": {\n \"january\": \"Січень\",\n \"february\": \"Лютий\",\n \"march\": \"Березень\",\n \"april\": \"Квітень\",\n \"mays\": \"Травень\",\n \"june\": \"Червень\",\n \"july\": \"Липень\",\n \"august\": \"Серпень\",\n \"september\": \"Вересень\",\n \"october\": \"Жовтень\",\n \"november\": \"Листопад\",\n \"december\": \"Грудень\",\n \"jan\": \"Січ\",\n \"feb\": \"Лют\",\n \"mar\": \"Бер\",\n \"apr\": \"Кві\",\n \"may\": \"Тра\",\n \"jun\": \"Чер\",\n \"jul\": \"Лип\",\n \"aug\": \"Сер\",\n \"sep\": \"Вер\",\n \"oct\": \"Жов\",\n \"nov\": \"Лис\",\n \"dec\": \"Гру\"\n },\n \"opened_chats\": \"Відкриті чати\",\n \"closed_chats\": \"Закриті чати\",\n \"reopened_chats\": \"Перевідкриті чати\",\n \"invite\": \"Запросити\",\n \"invite_new_manager\": \"Запрошення нового менеджера\",\n \"companies\": \"Компанії\",\n \"invited_users\": \"Запрошені користувачі\",\n \"managers\": \"Менеджери\",\n \"role\": \"Роль\",\n \"roles\": \"Ролі\",\n \"waybill_tracker\": \"Трекер накладних\",\n \"my_adverts\": \"Мої оголошення\",\n \"planning\": \"Планування\",\n \"backup\": \"Резервне копіювання\",\n \"select_the_tariff\": \"Виберіть тариф\",\n \"pay_my_tariff\": \"Сплатити мій тариф\",\n \"pay_tariff\": \"Сплатити тариф\",\n \"choose_payment_method\": \"Виберіть спосіб оплати та заповніть платіжну форму\",\n \"date\": \"Дата\",\n \"tariff\": \"Тариф\",\n \"approved\": \"Підтверджено\",\n \"canceled\": \"Скасовано\",\n \"in_process\": \"В процесі\",\n \"rejected\": \"Відхилено\",\n \"your_current_price_will_be\": \"Ваша поточна ціна буде складати\",\n \"email\": \"Електронна пошта\",\n \"rights_for_roles\": \"Права для ролі\",\n \"rights_for_all\": \"Права для всього\",\n \"allowed\": \"Дозволено\",\n \"denied\": \"Заборонено\",\n \"resend\": \"Перевідправити\",\n \"first_name\": \"Ім'я\",\n \"avatar\": \"Аватар\",\n \"update_data\": \"Оновити дані\",\n \"quick_deal\": \"Швидка угода\",\n \"sent_at\": \"Дата відправлення\",\n \"delivered_at\": \"Дата доставки\",\n \"approximate_arrival_date\": \"Приблизна дата прибуття\",\n \"responsible\": \"Відповідальний\",\n \"email_missing\": \"E-mail відсутній\",\n \"chat_missing\": \"Спілкування в чаті не можливо\",\n \"delete_deal\": \"Видалити угоду\",\n \"create_funnel\": \"Створити воронку\",\n \"create_client\": \"Створити клієнта\",\n \"company_name\": \"Назва компанії\",\n \"create_new_contact\": \"Створення нового контакту\",\n \"edit\": \"Оновити\",\n \"planned_activations\": \"Заплановані активації\",\n \"valid_to\": \"Дійсний до\",\n \"link\": \"Посилання\",\n \"contact_phone\": \"Контакт телефон\",\n \"contact_email\": \"Контакт пошта\",\n \"reason_error\": \"Причина помилки\",\n \"run_at\": \"Дата запуску\",\n \"type\": \"Тип\",\n \"created_at_olx\": \"Дата створення на OLX\",\n \"olx\": {\n \"adverts\": {\n \"active\": \"Активні\",\n \"pending\": \"Очікуючі\",\n \"unpaid\": \"Неоплачені\",\n \"inactive\": \"Неактивні\",\n \"rejected\": \"Відхилені\",\n \"successfully\": \"Успішно\",\n \"in_progress\": \"В прогресі\",\n \"canceled\" : \"Скасовано\",\n \"fail\": \"Помилка\",\n \"waiting\": \"Очікується\",\n \"draft\": \"Чернетка\",\n \"removed_by_moderator\": \"Видалено модератором\"\n },\n \"planners\": {\n \"planner_name\": \"Назва планера\",\n \"advert_name\": \"Назва оголошення\"\n },\n \"packets\": {\n \"active\": \"Активований\",\n \"inactive\": \"Неактивний\",\n \"start\": \"Пакет СТАРТ\",\n \"premium\": \"Пакет ПРЕМІУМ\",\n \"mega\": \"Пакет МЕГА\",\n \"available\": \"Доступно\",\n \"valid_until\": \"Діє до\",\n \"placements\": \"розміщень\",\n \"active_for\": \"активний ще\",\n \"days\": \"днів\",\n \"location\": \"Локація\"\n },\n \"delivery\": {\n \"status_short\": {\n \"accepted\": \"Підтверджені\",\n \"waiting\": \"Очікують підтвердження\",\n \"rejected\": \"Відхилені\"\n },\n \"status\": {\n \"accepted\": {\n \"accepted\": \"Очікує відправки до \",\n \"money_on_seller\": \"Завершено успішно\",\n \"delivery_in_progress\": \"На шляху до одержувача\",\n \"delivery_at_recipients_office\": \"У відділені\"\n },\n \"waiting\": {\n \"money_blocked\": \"Очікує підтвердження до \"\n },\n \"rejected\": {\n \"rejected\": \"Відхилено\",\n \"delivery_rejected_by_partner\": \"Відхилено (Замовлення прострочено)\",\n \"money_on_buyer\": \"Відхилено (Відхилено продавцем)\",\n \"rejected_by_buyer\": \"Відхилено (Відхилено покупцем)\"\n },\n \"data_error\": \"Помилка в обробці даних. Напишіть адміністратору\"\n },\n \"other\": {\n \"express_waybill\": \"Експрес-накладна\",\n \"delivery_service\": \"Служба доставки\",\n \"nova_poshta\": \"Нова Пошта\",\n \"ukrposhta\": \"Укрпошта\",\n \"meest\": \"Meest\",\n \"to_department\": \"У відділення \",\n \"order_number\": \"Номер замовлення\"\n }\n }\n },\n \"filter_by_status\": \"Фільтрувати за статусом\",\n \"date_and_hours\": \"Дата та години\",\n \"call_time\": \"Час дзвінка\",\n \"type_of_call\": \"Тип дзвінка\",\n \"link_on_call\": \"Посилання на дзвінок\",\n \"subscriber\": \"Абонент\",\n \"recording\": \"Запис\",\n \"faq\": \"Запитання та відповіді\",\n \"ticket\": \"Створити тікет\",\n \"video_guide\": \"Відео-інструкція\",\n \"part\": \"Частина\",\n \"presentation\": \"Презентація\",\n \"functionality\": \"Функціонал\",\n \"support_questions\": {\n \"question_1\": {\n \"question\": \"Як перевірити чи прочитали моє повідомлення?\",\n \"answer\": \"На ПК правою клавішею мишки і перевірити чи прочитано на телефоні просто тапнути і перевірити чи прочитано\"\n },\n \"question_2\": {\n \"question\": \"Як зареєструватися в JECRM?\",\n \"answer\": \"Для реєстрації перейдіть на сайт JECRM і заповніть форму реєстрації, вказавши вашу електронну пошту та телефон. Після реєстрації ви отримаєте доступ до особистого кабінету.\"\n },\n \"question_3\": {\n \"question\": \"Чи можу я інтегрувати JECRM з OLX без спеціальних навичок?\",\n \"answer\": \"Так, JECRM зроблено дружнім до користувача, дозволяючи легко інтегрувати ваш акаунт OLX з системою через зручний інтерфейс, без необхідності спеціальних технічних навичок.\"\n },\n \"question_4\": {\n \"question\": \"Чи підтримує JECRM роботу з мобільних пристроїв?\",\n \"answer\": \"Так, система адаптована для використання з мобільних пристроїв, дозволяючи керувати вашими оголошеннями та комунікацією з клієнтами в будь-який час та з будь-якого місця.\"\n },\n \"question_5\": {\n \"question\": \"Як налаштувати сповіщення про нові замовлення в JECRM?\",\n \"answer\": \"У налаштуваннях вашого профілю JECRM ви можете вказати переважний спосіб отримання сповіщень про нові замовлення чи повідомлення від клієнтів, включно з електронною поштою та месенджерами.\"\n },\n \"question_6\": {\n \"question\": \"Як налаштувати сповіщення в Telegram про повідомлення з OLX?\",\n \"answer\": \"Ви можете налаштувати бота JECRM у Telegram, щоб отримувати повідомлення відразу, як тільки вони надходять з OLX, забезпечуючи швидке та зручне сприйняття інформації.\"\n },\n \"question_7\": {\n \"question\": \"Що таке швидкі відповіді в JECRM?\",\n \"answer\": \"Швидкі відповіді дозволяють зберігати часто використовувані тексти, щоб полегшити та прискорити комунікацію з клієнтами, мінімізуючи потребу в ручному введенні повідомлень.\"\n },\n \"question_8\": {\n \"question\": \"Як використовувати функціонал логістики у JECRM?\",\n \"answer\": \"JECRM має інструмент для відстеження посилок Нової Пошти та Укрпошти, що дозволяє сповіщати клієнтів про статус доставки та інші важливі моменти, сприяючи покращенню обслуговування клієнтів.\"\n },\n \"question_9\": {\n \"question\": \"Як реєструватися та використовувати основні функції JECRM?\",\n \"answer\": \"JECRM пропонує легку реєстрацію та створення профілю для керування оголошеннями на OLX, забезпечуючи інтеграцію акаунтів та каналів для ефективного управління продажами.\"\n },\n \"question_10\": {\n \"question\": \"Які переваги використання JECRM для бізнесу на OLX?\",\n \"answer\": \"JECRM допомагає контролювати велику кількість замовлень, оптимізувати відповіді на повідомлення, та підвищити ефективність продажів через OLX, пропонуючи інтеграцію з месенджерами та службами доставки.\"\n },\n \"question_11\": {\n \"question\": \"Що нового в JECRM у 2024 році?\",\n \"answer\": \"У 2024 році JECRM презентувала оновлення, які включають оптимізацію системи, поліпшення роботи чату, новий функціонал для логістики та доставки, а також мінімізацію повернень завдяки вдосконаленому сповіщенню клієнтів.\"\n },\n \"question_12\": {\n \"question\": \"Як активувати оголошення на OLX за допомогою JECRM?\",\n \"answer\": \"JECRM пропонує функціонал для активації оголошень на OLX згідно з розкладом, сприяючи ефективному управлінню вашими оголошеннями та збільшуючи шанси на успішні продажі.\"\n },\n \"question_13\": {\n \"question\": \"Чи можу я використовувати JECRM для трекінгу доставок?\",\n \"answer\": \"Так, JECRM дозволяє відстежувати доставки через Нову Пошту та Укрпошту, надаючи актуальну інформацію про статус посилок прямо в системі, що підвищує задоволеність клієнтів і знижує кількість повернень.\"\n }\n },\n \"created_at_crm\": \"Дата створення в CRM\",\n \"enter_first_reply\": \"Введіть текст першої відповіді\",\n \"active_status\": \"Активний\",\n \"expired_status\": \"Потрібно перепідключити\",\n \"closed_status\": \"Потрібно підключити\",\n \"paused_status\": \"На паузі\",\n \"beta_test\": \"Beta-тест\",\n \"just_now\": \"Щойно\",\n \"ttn\": \"ТТН\",\n \"test_regime\": \"Тестовий режим\",\n \"what_on_what\": \"Що на що?\",\n \"details\": \"Реквізити\",\n \"change_name\": \"Змінити назву\",\n \"select_connection\": \"Виберіть підключення\",\n \"archived_channels\": \"Заархівовані канали\",\n \"choose_tariff\": \"Оберіть тариф\",\n \"choose_topic\": \"Оберіть тему\",\n \"choose_files\": \"Оберіть файли\",\n \"create_field\": \"Створити поле\",\n \"change_field\": \"Змінити поле\",\n \"create_column\": \"Створити колонку\",\n \"change_column\": \"Змінити колонку\",\n \"to_archive\": \"Архівувати\",\n \"activate_now\": \"Активувати зараз\",\n \"deactivate_now\": \"Деактувати зараз\",\n \"plan_activation\": \"Запланувати активацію\",\n \"to_plan\": \"Запланувати\",\n \"publish_now\": \"Опублікувати зараз\",\n \"move_up_list\": \"Підняти вгору списку\",\n \"ads\": \"Реклама\",\n \"change_price\": \"Змінити ціну\",\n \"duplicate\": \"Дублювати\",\n \"change_planning\": \"Змінити планування\",\n \"cancel_planning\": \"Скасувати планування\",\n \"to_draft\": \"В чорновик\",\n \"to_active\": \"Активувати\",\n \"restore\": \"Відновити\",\n \"download\": \"Завантажити\",\n \"add_in_tracker\": \"Додати в трекер\",\n \"waiting_connect\": \"Очікує підключення\",\n \"stopped\": \"Зупинено\",\n \"columns\": \"Колонки\",\n \"fields\": \"Поля\",\n \"change_role\": \"Змінити роль\",\n \"end_the_conversation\": \"Якщо питання в діалозі вирішено, ви можете завершити його. Це допоможе тримати фокус на відкритих діалогах. Якщо клієнт напише вам повторно, ви побачите цей чат у списку відкритих.\",\n \"no_one_field\": \"Немає жодного поля.\",\n \"your_subdomain\": \"Ваш сабдомін\",\n \"integration\": \"Інтеграція\",\n \"write_a_message\": \"Напишіть повідомлення\",\n \"announcement_scheduler\": \"Планувальник оголошень\",\n \"ads_scheduler\": \"Планувальник реклами\",\n \"ads_activation_time\": \"Час активації оголошень\",\n \"change_the_ad_activation_time\": \"Змінити час активації оголошень\",\n \"re_planning\": \"Перепланувати\",\n \"change_the_activation_time_of_the_ads\": \"Змінити час активації оголошень(ння)\",\n \"ukr_poshta_status\": {\n \"registered\": \"Зареєстровано\",\n \"in_department\": \"У відділенні\",\n \"delivering\": \"У процесі доставки\",\n \"delivered\": \"Доставлено\",\n \"returning\": \"У процесі повернення\",\n \"returned\": \"Повернено\",\n \"forwarding\": \"Пересилання\",\n \"cancelled\": \"Скасовано\",\n \"storage\": \"На зберіганні\",\n \"error\": \"Помилка\"\n },\n \"errors\": {\n \"default_error\": \"Йой, щось пішло не так, спробуйте ще раз, або звʼяжіться з адміністратором.\",\n \"internal_error\": \"Йой, щось пішло не так, спробуйте ще раз, або звʼяжіться з адміністратором.\",\n \"max_file_size\": \"Розмір файлу не більше 5MB.\",\n \"max_memory_limit_reached\": \"Досягнуто максимального ліміту пам’яті. Звʼяжіться з адміністратором.\",\n \"max_integration\": \"Ви досягли ліміту кількості інтеграцій {0} у своєму обліковому записі.\",\n \"max_price\": \"Занадто висока ціна.\",\n \"user_not_found\": \"Користувач із вказаною електронною адресою не знайдений\",\n \"integration_not_found\": \"Інтеграції не знайдена.\",\n \"contact_already_exists\": \"Контакт з таким номером вже існує.\",\n \"phone_already_exists\": \"Такий номер уже є, спробуйте інший.\",\n \"domain_already_exists\": \"Такий сабдомен уже зайнятий, спробуйте інший.\",\n \"integration_already_exists\": \"Інтеграція вже існує.\",\n \"column_already_exists\": \"Така колонка поле вже існує.\",\n \"custom_field_already_exists\": \"Кастомне поле з таким ключем вже існує.\",\n \"user_already_exists\": \"Такий користувач вже існує.\",\n \"channel_user_already_exists\": \"Канал з таким користувачем вже існує.\",\n \"user_already_invited\": \"Користувача з такою поштою вже запрошено.\",\n \"current_email_exists\": \"Електронна адреса поточного користувача не може використовуватися для запрошення.\",\n \"promo_code_expired\": \"Промокод вже був використаний або час дії сплинув.\",\n \"account_olx_expired\": \"Цей OLX акаунт - потрібно перепідключити.\",\n \"account_olx_pause\": \"Цей OLX акаунт на паузі, звʼяжіться з адміністратором.\",\n \"access_rejected\": \"Недостатньо прав, звʼяжіться з адміністратором.\",\n \"channel_already_exists\": \"Така назва вже існує.\",\n \"funnel_already_exists\": \"Така назва вже існує.\",\n \"tokens_outdated\": \"Токени вичерпані, звʼяжіться з адміністратором.\",\n \"empty_field_name\": \"Поле «Назва» не може бути пустим\",\n \"empty_field_trigger\": \"Поле «Спрацьовувати при» не може бути пустим\",\n \"empty_field_action\": \"Поле «Дія» не може бути пустим\",\n \"empty_field_time\": \"Дні або час не можуть бути пустими\",\n \"empty_field_channel\": \"Поле «Виберіть канал» не може бути пустим\",\n \"not_enough_credits\": \"Не достатньо коштів на рахунку\",\n \"email_already_exist\": \"Адреса електронної пошти вже існує\",\n \"invalid_credentials\": \"Невірне ім'я користувача або пароль\",\n \"not_verified_email\": \"Ваша електронна пошта не підтверджена\",\n \"ad_cpesified_currency\": \"У цій категорії ціна оголошення не може бути вказана у валюті.\",\n \"invite_not_found\": \"Запрошення не знайдено\",\n \"phone_number_invalid\": \"Номер телефону недійсний\",\n \"phone_number_unoccupied\": \"Номер телефону ще не використовується\",\n \"phone_number_flood\": \"Ви запитали код занадто багато разів\",\n \"phone_number_banned\": \"Наданий номер телефону заблоковано в Telegram\",\n \"phone_code_invalid\": \"Наданий телефонний код недійсний\",\n \"phone_code_expired\": \"Телефонний код, який ви надали, прострочений\",\n \"phone_code_empty\": \"Відсутній телефонний код\",\n \"phone_password_flood\": \"Ви намагалися увійти занадто багато разів\",\n \"phone_password_protected\": \"Цей телефон захищений паролем\",\n \"password_hash_invalid\": \"Наданий пароль недійсний\",\n \"sms_code_create_failed\": \"Під час створення SMS-коду виникла помилка\"\n },\n \"install\": \"Встановити\",\n \"open\": \"Відкрити\",\n \"wait_for_confirmation\": \"Очікуйте підтвердження\",\n \"installation\": \"Встановлення\",\n \"companies_info\": {\n \"you_are_just_a_step_away_from\": \"Ви на крок від...\",\n \"you_are_just_a_few_minutes_from\": \"Ви на кілька хвилин від...\",\n \"you_are_just_a_moment_away_from\": \"Ви на мить від...\",\n \"slide2\": \"Оптимізованої роботи з вашими клієнтами та покупцями завдяки нашій інтегрованій системі.\",\n \"slide3\": \"Автоматизованого планування та відстеження відвідувань сайту, а також управління обслуговуванням, що відповідає вашому бізнесу.\",\n \"slide4\": \"Чіткої документації та зручної комунікації в особистій базі даних JECRM.\",\n \"slide5\": \"Забезпечення прозорості на кожному етапі роботи, щоб ви могли контролювати кожну деталь свого бізнесу.\",\n \"slide1\": \"Ефективного управління вашим е-торговельним бізнесом за допомогою JECRM.\"\n },\n \"client_read\": \"Клієнт прочитав\",\n \"unread\": \"Не прочитано\",\n \"notifications_texts\": {\n \"info\": {\n \"thanks_for_payment\": \"Дякую за оплату!\",\n \"thanks_for_order\": \"Дякуємо за замовлення!\",\n \"new_messages\": \"Нові повідомлення!\",\n \"copied\": \"Скопійовано\",\n \"no_notifications\": \"У вас немає сповіщень\",\n \"nothing_for_request\": \"За вашим запитом нічого не знайдено\",\n \"pay_ads\": \"Успішно подано заявку на оплату реклами. Через деякий час прийде сповіщення з результатом.\"\n },\n \"success\": {\n \"fast_answer_save\": \"Швидку відповідь збережено\",\n \"message_read\": \"Повідомлення прочитано\",\n \"successfully_saved\": \"Успішно збережено\",\n \"successfully_updated\": \"Успішно оновлено\",\n \"successfully_created\": \"Успішно створено\",\n \"successfully_deleted\": \"Успішно видалено\",\n \"successfully_added\": \"Успішно додано\",\n \"successfully_moved\": \"Успішно переміщено\",\n \"successfully_installed\": \"Успішно встановлено\",\n \"successfully_downloaded\": \"Успішно завантажено\",\n \"successfully_planned\": \"Успішно заплановано\",\n \"successfully_canceled\": \"Успішно скасовано\",\n \"successfully_disconnect\": \"Успішно відключено\",\n \"successfully_bought\": \"Успішно куплено\",\n \"successfully_reconnected\": \"Успішно перепідключено\",\n \"successfully_sent\": \"Успішно відправлено\",\n \"successfully_resent\": \"Успішно перевідправлено\",\n \"successfully_archived\": \"Успішно заархівовано\",\n \"successfully_reset\": \"Успішно скинуто\",\n \"successfully_cleared\": \"Успішно очищено\",\n \"user_has_been_successfully_removed_from_the_team\": \"Користувача успішно видалено з команди\",\n \"channel_restored\": \"Канал відновлено\",\n \"settings_updated\": \"Налаштування оновлено!\",\n \"user\": \"Користувача\",\n \"blocked\": \"заблоковано!\",\n \"password_changed\": \"Пароль змінено!\",\n \"success\": \"Успіх!\",\n \"push_up_ad\": \"Успішне підняття оголошення: №\",\n \"bulk_push_up_ad\": \"Успішне підняття оголошень\",\n \"sent_verify_email\": \"Лист з підтвердженням пошти надіслано\",\n \"sent_reset_password\": \"Лист щодо зміни пароля надіслано\",\n \"the_code_is_copied_to_the_clipboard\": \"Код скопійовано у буфер!\",\n \"widget_saved\": \"Віджет збережено!\"\n },\n \"warn\": {\n \"action_not_possible\": \"Дія не можлива для даного статусу!!\",\n \"application_not_installed\": \"Застосунок не встановлено.\",\n \"no_more_upload\": \"Ви можете завантажити не більше \",\n \"select_tariff\": \"Будь ласка, виберіть тариф, який вам підходить.\",\n \"not_possible_for_active_ads\": \"Не можна для активних оголошень!\",\n \"submitted_for_activation_1\": \"Заявка подана на активацію!\",\n \"submitted_for_activation_2\": \"Заявки подані на активацію!\",\n \"possible_for_active_ads\": \"Можна тільки для активних оголошень!\",\n \"submitted_for_deactivation_1\": \"Заявка подана на деактивацію!\",\n \"submitted_for_deactivation_2\": \"Заявки подані на деактивацію!\",\n \"no_valid_status\": \"Жоден з обраних елементів немає валідного статусу!\",\n \"contain_invalid_statuses\": \"Деякі з обраних елементів містять невалідні статуси, вони будуть проігноровані!\",\n \"files\": \" файлів.\",\n \"invalid_files_type\": \"Неприпустимий тип файлів\",\n \"no_cache_to_clear\": \"Немає кешу для очищення\",\n \"warn\": \"УВАГА!\"\n },\n \"error\": {\n \"there_was_an_error_please_try_again\": \"Сталась помилка, спробуйте ще раз\",\n \"account_olx_expired\": \"Цей OLX акаунт - потрібно перепідключити.\",\n \"error_getting_columns\": \"Помилка отримання колонок!\",\n \"error_update_column_position\": \"Вибачте, виникла помилка під час оновлення позиції колонки!\\nБудь ласка, спробуйте ще раз.\\nЯкщо проблема не зникає, зверніться до адміністратора для отримання допомоги!\",\n \"error_delete_column\": \"Помилка видалення колонки!\",\n \"something_went_wrong_1\": \"Йой, щось пішло не так!\",\n \"empty_channel\": \"Канал не може бути пустим!\",\n \"empty_funnel\": \"Назва воронки не може бути пустою!\",\n \"attached_waybill\": \"Дана ТТН вже привʼязана.\",\n \"error_planning\": \"Помилка при плануванні!\",\n \"error_saving\": \"Помилка при збережені!\",\n \"error_updating\": \"Помилка оновлення!\",\n \"tracked_waybill\": \"Ця ТТН вже відслідковується.\",\n \"synchronization_error\": \"Помилка синхронізації.\",\n \"since_last_sync_error\": \"Минув деякий час після останньої синхронізації, будь ласка, зачекайте кілька секунд\",\n \"enabled_channel\": \"Спочатку відключіть канал!\",\n \"archive_error\": \"Під час спроби архівувати сталась помилка!\",\n \"channel_disable_error\": \"Під час спроби відключити канали сталась помилка!\",\n \"deal_name_field_required\": \"Поле назва угоди обов'язкове.\\n\\nБудь ласка, введіть назву!\",\n \"contact_name_field_required\": \"Поле назва контакта обов'язкове.\\n\\nБудь ласка, введіть назву!\",\n \"email_not_expected\": \"Введена адреса електронної пошти не відповідає очікуваному формату!\\n\\nБудь ласка, перевірте правильність введення і спробуйте ще раз!\",\n \"number_invalid\": \"Введений номер не є дійсним.\\n\\nБудь ласка, перевірте правильність введення і спробуйте ще раз!\",\n \"error_create_card\": \"Помилка створення карточки!\",\n \"error_create_funnel\": \"Помилка створення воронки!\",\n \"error_delete\": \"Помилка видалення!\",\n \"redo_the_ad\": \"Перестворіть оголошення від нашої системи (Можна використовувати кнопку \\\"Дублювати\\\")\",\n \"mass_activation_of_ads\": \"Якщо ваші оголошення публікуються не з бізнес-акаунту (не бізнес оголошення) - не рекомендується проводити масову активацію оголошень в один і той же час.\",\n \"invalid_phone_number_t\": \"Не валідний номер телефона!\",\n \"invalid_phone_number_m_1\": \"Номер телефона має починатися з 380\",\n \"invalid_phone_number_m_2\": \"Номер телефона занадто короткий\",\n \"error\": \"Помилка!\",\n \"number_already_exists\": \"Такий номер уже є, спробуйте інший, або звʼяжіться із адміністратором.\",\n \"something_went_wrong_2\": \"Йой, щось пішло не так, спробуйте ще раз, або звʼяжіться із адміністратором.\",\n \"empty_name\": \"Назва не може бути пуста!\",\n \"error_while_delete_1\": \"Помилка при видаленні!\",\n \"error_update_field_position\": \"Вибачте, виникла помилка під час оновлення позиції кастомного поля.\\nБудь ласка, спробуйте ще раз.\\nЯкщо проблема не зникає, зверніться до адміністратора для отримання допомоги!\",\n \"error_getting_field\": \"Помилка отримання кастомних полів!\",\n \"error_update_field\": \"Помилка оновлення кастомного поля!\",\n \"error_delete_field\": \"Помилка видалення кастомного поля!\",\n \"error_while_delete_2\": \"Під час видалення сталась помилка!\",\n \"error_OLX_messages\": \"Помилка отримання OLX повідомлень:\",\n \"file_size_error\": \"Розмір файлу не більше 12MB.\",\n \"old_messages\": \"Щоб побачити старіші повідомлення прогортайте вверх\",\n \"name_field_required\": \"Поле ім'я обов'язкове.\\n\\nБудь ласка, введіть ім'я контакту.\",\n \"phone_field_required\": \"Поле телефон обов'язкове.\",\n \"contact_creation_error\": \"Помилка створення контакта!\",\n \"contact_updating_error\": \"Помилка оновлення контакта!\",\n \"contact_deleting_error\": \"Помилка видалення контакта!\",\n \"name_card_empty\": \"Назва картки не може бути пустою!\",\n \"API_key_invalid\": \"Вказаний API ключ невалідний.\",\n \"max_auto_messages\": \"Максимальна кількість автоматизованих повідомлень досягнута\",\n \"max_automation\": \"Максимальна кількість автоматизацій досягнута\",\n \"error_getting_profile\": \"Схоже це помилка отримання профіля.\",\n \"downloaded\": \"Завантажено!\",\n \"not_enough_space\": \"Недостатньо місця для файлу: \",\n \"error_while_upload\": \"Під час вивантаження сталася помилка!\",\n \"error_while_download\": \"Під час завантаження сталася помилка!\",\n \"error_while_planner\": \"Під час планування сталася помилка!\",\n \"error_while_open_link\": \"Під час відкриття посилання сталася помилка!\",\n \"error_empty_form\": \"Створіть поля вводу для форми!\",\n \"failed_to_copy_code\": \"Не вдалося скопіювати код!\",\n \"unable_to_transfer_files\": \"Передати файли в RIA неможливо\"\n }\n },\n \"you\": \"Ви\",\n \"about_deal\": \"Про угоду\",\n \"phase\": \"Стадія\",\n \"amount_and_currency\": \"Сума та валюта\",\n \"сomment\": \"Коментар\",\n \"enter_price\": \"Введіть ціну\",\n \"enter_link\": \"Введіть посилання\",\n \"enter_text\": \"Введіть текст\",\n \"pip\": \"ПІП\",\n \"enter_pip\": \"Введіть ПІП\",\n \"select_contact\": \"Виберіть контакт\",\n \"contact_not_found\": \"Контакт(и) не знайдені\",\n \"no_one_contact\": \"Немає жодного контакту.\",\n \"nothing_found\": \"Нічого не знайдено.\",\n \"clear_selected\": \"Очистити обране\",\n \"searching\": \"Пошук...\",\n \"loading\": \"Загрузка...\",\n \"client\": \"Клієнт\",\n \"key_field\": \"Ключ поля\",\n \"this_field_must_be_unique\": \"Це поле має бути унікальним\",\n \"type_field\": \"Тип поля\",\n \"type_tooltip\": \"Для інформації у вигляді тексту, символів чи чисел\",\n \"alias_events\": {\n \"created\": \"Створено\",\n \"updated\": \"Змінено\",\n \"added\": \"Додано\",\n \"removed\": \"Видалено\"\n },\n \"alias\": {\n \"column\": \"стадію\",\n \"column_id\": \"стадію\",\n \"contact\": \"контакт\",\n \"contact_id\": \"контакт\",\n \"funnel\": \"воронку\",\n \"funnel_id\": \"воронку\",\n \"lead\": \"лід\",\n \"deal\": \"угоду\",\n \"price\": \"суму\",\n \"currency\": \"валюту\",\n \"title\": \"назву\",\n \"name\": \"назву\",\n \"bg_color\": \"колір\",\n \"text_color\": \"колір\",\n \"comment\": \"коментар\"\n },\n \"no_conversation\": \"Розмова не відбулася\",\n \"outgoing\": \"Вихідний\",\n \"incoming\": \"Вхідний\",\n \"copy\": \"Скопіювати\",\n \"call_status\": {\n \"answered\": \"Відповів\",\n \"no_answer\": \"Не відповів\",\n \"proper\": \"Цільовий\",\n \"repeated\": \"Повторений\",\n \"busy\": \"Зайнятий\"\n },\n \"check_connect_correct_account\": \"Перевірте, будь ласка, чи підключаєте вірний обліковий запис. У разі виникнення помилки, чати будуть сховані...\",\n \"list_ca\": {\n \"text\": \"Текст\",\n \"link\": \"Посилання\",\n \"textarea\": \"Великий текст\",\n \"checkbox\": \"Прапорець\"\n },\n \"status_table_nova_poshta\": {\n \"registered\": \"Створено\",\n \"delivering\": \"У процесі доставки\",\n \"delivered\": \"Доставлено\",\n \"in_department\": \"У відділенні\",\n \"returned\": \"Поверненно\",\n \"returning\": \"У процесі повернення\",\n \"forwarding\": \"Пересилання\",\n \"canceled\": \"Скасовано\",\n \"storage\": \"На зберіганні\",\n \"error\": \"Помилка\",\n \"completed\": \"Успішно\",\n \"gotten\": \"Отримав\",\n \"not_found\": \"Не знайдено\",\n \"deleted\": \"Видалено\",\n \"in_draft\": \"В чернетках\",\n \"progress\": \"В прогресі\"\n },\n \"status_table_planner\": {\n \"created\": \"Створено\",\n \"waiting\": \"Очікується\",\n \"fail\": \"Помилка\",\n \"completed\": \"Успішно\",\n \"canceled\": \"Скасовано\",\n \"not_found\": \"Не знайдено\",\n \"draft\": \"Чернетка\"\n },\n \"advert_planner_type\": {\n \"activation\": \"Активація\",\n \"deactivation\": \"Деактивація\",\n \"push_up\": \"Підняття\",\n \"packet\": \"Пакет\",\n \"ads\": \"Реклама\",\n \"creation\": \"Створення\"\n },\n \"permissions\": {\n \"access_team\": \"Доступ до даних про команду\",\n \"action_team\": \"Виконувати дії із командою\",\n \"access_apps\": \"Доступ до застосунків\",\n \"action_apps\": \"Виконувати дії із застосунками (Встановлювати/Видаляти)\",\n \"access_integration\": \"Доступ до даних про інтеграції\",\n \"action_integration\": \"Виконувати дії із інтеграціями\",\n \"access_crm\": \"Доступ до даних про CRM\",\n \"action_crm\": \"Виконувати дії із CRM\",\n \"access_contacts\": \"Доступ до даних про контакти\",\n \"action_contacts\": \"Виконувати дії із контактами\",\n \"access_logistics\": \"Доступ до даних про логістику\",\n \"action_logistics\": \"Виконувати дії із логістикою\",\n \"access_company\": \"Доступ до даних про компанію\",\n \"action_company\": \"Оновлювати дані про компанію\",\n \"access_payment\": \"Доступ до даних про оплату\",\n \"action_payment\": \"Виконувати дії з оплатою\",\n \"access_channels\": \"Доступ до каналів\",\n \"action_channels\": \"Виконувати дії з каналами\",\n \"access_p_channels\": \"Доступ до своїх каналів\",\n \"action_p_channels\": \"Виконувати дії зі своїми каналами\",\n \"access_settings\": \"Доступ до налаштувань\",\n \"action_settings\": \"Виконувати дії з налаштуваннями\",\n \"access_files\": \"Доступ до файлів\",\n \"action_files\": \"Виконувати дії з файлами\",\n \"access_chats\": \"Доступ до чатів\",\n \"action_chats\": \"Виконувати дії із чатами\"\n },\n \"permissions_types\": {\n \"crm\": \"CRM\",\n \"team\": \"Команда\",\n \"channels\": \"Канали\",\n \"logistics\": \"Логістика\",\n \"integration\": \"Інтеграції\",\n \"chats\": \"Чати\",\n \"apps\": \"Застосунки\",\n \"files\": \"Файли\",\n \"contacts\": \"Контакти\",\n \"company\": \"Компанія\",\n \"payments\": \"Оплата\",\n \"settings\": \"Налаштування\"\n },\n \"ai_integration\": {\n \"number_of_tokens\": \"Кількість токенів\",\n \"text_1\": \"Інтеграція з нашим AI відбувається швидко та просто.\",\n \"text_2\": \"Просто придбайте необхідну кількість токенів і почніть використовувати всі можливості штучного інтелекту вже сьогодні. 1 токен - приблизно 2-3 символи\",\n \"h4_1\": \"Інтеграції з каналами\",\n \"placeholder_1\": \"Введіть налаштування для AI\",\n \"label_1\": \"Макс. токенів на відповідь\",\n \"label_2\": \"Канали\",\n \"placeholder_2\": \"Канал\",\n \"h4_2\": \"Історія запитів\"\n },\n \"dialogs\": {\n \"add_category\": \"Додати категорію\",\n \"update_category\": \"Оновити категорію\",\n \"add_product\": \"Додати продукт\",\n \"update_product\": \"Оновити продукт\",\n \"delivery_choice\": \"Ви впевнені, що хочете створити ще одну?\",\n \"create_group\": \"Ви впевнені, що хочете видалити роль?\",\n \"tab_groups\": \"Ви впевнені, що хочете видалити роль?\",\n \"tab_managers_1\": \"Ви впевнені, що хочете видалити користувача з команди?\",\n \"tab_managers_2\": \"Ви впевнені, що хочете видалити користувача(ів) з команди?\",\n \"deal_1\": \"Ви впевнені, що хочете змінити воронку?\",\n \"integration-olx_1\": \"Ви впевнені, що хочете відключити акаунт?\\nУ цьому випадку старі чати з клієнтом будуть недоступні.\",\n \"tab-funnel-columns\": \"Ви впевнені, що хочете видалити колонку?\",\n \"integration-ringostat\": \"Ви впевнені, що хочете видалити API ключ?\",\n \"deals_1\": \"Ви впевнені, хочете видалити карточку?\",\n \"integration-telegram-bot\": \"Ви впевнені, що хочете видалити токен?\",\n \"integration-telegram\": \"Ви впевнені, що хочете видалити акаунт?\",\n \"update-manager\": \"Ви впевнені, що хочете видалити користувача з команди?\",\n \"channel_1\": \"Ви впевнені, що хочете заархівувати?\",\n \"channel_2\": \"Ви впевнені, що хочете відключити вибрані канали?\\nУ цьому випадку старі чати з клієнтами будуть недоступні.\",\n \"contact\": \"Ви впевнені, що хочете видалити контакт?\",\n \"my-adverts\": \"Ви впевнені, що хочете видалити оголошення?\",\n \"my-planners\": \"Ви впевнені, що хочете скасувати планування?\",\n \"contacts\": \"Ви впевнені, що хочете видалити контакт?\",\n \"header\": \"Ви впевнені, що хочете активувати тріальний режим?\",\n \"chats-settings\": \"Ви впевнені, що хочете видалити швидку відповідь?\",\n \"deal_2\": \"Ви впевнені, що хочете видалити контакт для цієї угоди?\",\n \"tab-deals-settings-fields\": \"Ви впевнені, що хочете видалити кастомне поле?\",\n \"tab-deals-settings-common\": \"Ви впевнені, що хочете видалити воронку?\",\n \"integration-olx_2\": \"Ви впевнені, що хочете видалити канал?\",\n \"ttn_delete\": \"Ви впевнені, що хочете видалити TTH?\",\n \"category_delete\": \"Ви впевнені, що хочете видалити категорію?\",\n \"product_delete\": \"Ви впевнені, що хочете видалити продукт?\",\n \"to_draft\": \"Ви впевнені, що хочете змінити статус на чорновик?\",\n \"funnel_filter\": \"Ви впевнені, що хочете видалити фільтр?\",\n \"push_up_ad\": \"Ви впевнені, що хочете підняти оголошення?\",\n \"pay_ad\": \"Ви впевнені, що хочете оплатити рекламу?\",\n \"buy_package\": \"Ви впевнені, що хочете купити пакет?\",\n \"reset_changes\": \"Ви впевнені, що хочете скинути зміни?\",\n \"delete_site\": \"Ви впевнені, що хочете видалити сайт?\",\n \"add_site\": \"Додати сайт\",\n \"update_site\": \"Оновити сайт\"\n },\n \"try_demo\": \"Спробувати Demo\",\n \"dimensional_weight\": \"Об’ємна вага розраховується за формулою (Довжина × Ширина × Висота, см) / 4000 та порівнюється з фактичною вагою. Більший показник використовується в розрахунках вартості перевезення.\",\n \"shipment_parameters\": \"Параметри відправлення не відповідають умовам вибору: Вага (об’ємна та фактична) від 0.1 до 30 кг (включно); Сторони: Довжина, Ширина, Висота <= 120 см\",\n \"check_shipment\": \"Встановіть відмітку, якщо дане відправлення (місце): не запаковано в коробку або гофрокартон з пласкою основою та не є пласким пакетом з одягом; відправляється та доставляється з/до вантажних відділень, адреси або пункту приймання-видачі\",\n \"1_month\": \"1 місяць\",\n \"3_month\": \"3 місяці\",\n \"for\": \"на\",\n \"generate_invoice\": \"Згенерувати рахунок\",\n \"product\": \"Продукт\",\n \"to_be_paid\": \"До сплати\",\n \"total\": \"Всього\",\n \"discount\": \"Знижка\",\n \"field_name\": \"Назва\",\n \"last_file_update\": \"Останні зміни\",\n \"file_created_by\": \"Створив(-ла)\",\n \"file_size\": \"Розмір файлу\",\n \"upload_new\": \"Нове\",\n \"snack_bar\": {\n \"upload\": \"Вивантаження\",\n \"download\": \"Завантаження\",\n \"in_progress\": \"В процесі - {count}\",\n \"completed\": \"Завершено - {count}\",\n \"failed\": \" ({count} з помилками)\"\n },\n \"paste\": \"Вставити\",\n \"automation_action\": {\n \"trigger\": \"Спрацювувати при\",\n \"get_phone\": \"Отриманні телефону\",\n \"get_email\": \"Отриманні пошти\",\n \"get_first_message\": \"Першому повідомленні\",\n \"first_message\": \"Першому повідомленні\",\n \"move_lead\": \"Змістити лід\",\n \"move_deal\": \"Змістити угоду\",\n \"action\": \"Дія\",\n \"choose_field\": \"Виберіть воронку\",\n \"choose_column\": \"Виберіть колонку\",\n \"send_message\": \"Відправити повідомлення\",\n \"generate_ai\": \"Генерувати АІ відповідь\",\n \"enter_name_automation\": \"Введіть назву автоматизації\",\n \"enter_prompt\": \"Введіть налаштування для АІ\",\n \"get_only_first_message\": \"Тільки при першому повідомленні\",\n \"get_all_message_except_first\": \"Всіх повідомленнях окрім першого\",\n \"get_all_message\": \"Всіх повідомленнях\",\n \"get_manager_reply\": \"Відповіді менеджером\",\n \"get_ai_reply\": \"Відповіді АІ\"\n },\n \"step\": \"Крок\",\n \"you_need_to_pay_the_tariff_before\": \"Сплатити тариф потрібно до\",\n \"change_advert\": \"Змінити оголошення\",\n \"create_advert\": \"Створити оголошення\",\n \"total_size_text_files_and_documents\": \"Текстові файли та документи\",\n \"total_size_images\": \"Зображення\",\n \"total_size_audio\": \"Аудіо\",\n \"total_size_video\": \"Відео\",\n \"total_size_outherFiles\": \"Інші файли\",\n \"size_available\": \"Вільне місце\",\n \"size_used\": \"Використано\",\n \"size_of\": \"із\",\n \"time_zone\": \"Часовий пояс\",\n \"CompanySettings\": \"Компанії\",\n \"from\": \"з\",\n \"to\": \"до\",\n \"work\": \"Працювати\",\n \"chose_days\": \"Виберіть дні\",\n \"sunday\": \"неділя\",\n \"monday\": \"понеділок\",\n \"tuesday\": \"вівторок\",\n \"wednesday\": \"середа\",\n \"thursday\": \"четвер\",\n \"friday\": \"п’ятниця\",\n \"saturday\": \"субота\",\n \"automation_work\": \"Автоматизація буде працювати\",\n \"mon\": \"пн\",\n \"tue\": \"вт\",\n \"wed\": \"ср\",\n \"thu\": \"чт\",\n \"fri\": \"пт\",\n \"sat\": \"сб\",\n \"sun\": \"нд\",\n \"all_week\": \"Всі дні\",\n \"days\": {\n \"TUESDAY\": \"Вівторок\",\n \"THURSDAY\": \"Четвер\",\n \"SATURDAY\": \"Субота\",\n \"WEDNESDAY\": \"Середа\",\n \"FRIDAY\": \"П'ятниця\",\n \"MONDAY\": \"Понеділок\",\n \"SUNDAY\": \"Неділя\"\n },\n \"work_all_day\": \"цілодобово\",\n \"on\": \"Ввімкнути\",\n \"off\": \"Вимкнути\",\n \"delete_automation\": \"Ви впевнені що хочете видалити автоматизацію?\",\n \"sync_advert\": \"Синхронізувати оголошення\",\n \"olx_chats\": \"OLX чати\",\n \"autoria_chats\": \"Autoria чати\",\n \"autoria_chats_old\": \"Autoria старі чати\",\n \"telegram_bot_chats\": \"Telegram BOT чати\",\n \"history_version\": \"Історія версій\",\n \"version\": \"Версія\",\n \"release\": \"Випуск\",\n \"contact_the_administrator\": \"Зверніться до адміністратора\",\n \"error_while_download\": \"Під час завантаження сталася помилка!\",\n \"error_while_upload\": \"Під час вивантаження сталася помилка!\",\n \"not_enough_space\": \"Недостатньо місця для файлу!\",\n \"group\": \"Група\",\n \"free\": \"Безкоштовно\",\n \"trade\": \"Обмін\",\n \"negotiable\": \"Договірна\",\n \"owner\": \"Адміністратор\",\n \"delivery\": \"OLX Доставка\",\n \"packets\": \"Мої пакети\",\n \"manager\": \"Менеджер\",\n \"all\": \"Всі\",\n \"all_notification_read\": \"Усі сповіщення прочитано\",\n \"your_zone\": \"Ваше місцезнаходження\",\n \"count_adverts\": \"Кількість оголошень\",\n \"your_region\": \"Ваш регіон\",\n \"your_category\": \"Категорія\",\n \"planner_paid\": \"Планувальник пакетів\",\n \"token\": \"Токен\",\n \"filters\": \"Фільтри\",\n \"add_filter\": \"Додати фільтр\",\n \"plan_packet\": \"Запланувати\",\n \"select_category\": \"Виберіть категорію\",\n \"selected_category\": \"Обрана категорія\",\n \"select_city_and_region\": \"Виберіть місто та регіон\",\n \"selected_city\": \"Обране місто та регіон\",\n \"contact_name\": \"Контактна особа\",\n \"custom_filters\": {\n \"name\": \"Заголовок\",\n \"price\": \"Ціна\",\n \"is_unread\": \"Не Прочитано\",\n \"id\": \"ID\"\n },\n \"add_field\": \"Додати поле\",\n \"default_fields\": \"Відновити стандартні поля\",\n \"filter_fields_settings\": \"Налаштування полів фільтра\",\n \"color\": \"Колір\",\n \"delete_filter\": \"Видалити фільтр\",\n \"ticket_system\": {\n \"message\": \"Повідомлення\",\n \"ticket\": \"Тікет\",\n \"new_ticket\": \"Новий тікет\",\n \"topic\": \"Тема\",\n \"enter_a_topic\": \"Введіть тему\",\n \"files\": \"Файли\",\n \"attach_a_file\": \"Прикріпити файл\",\n \"add_ticket\": \"Додати тікет\",\n \"list_of_tickets\": \"Список тікетів\",\n \"code\": \"Код\",\n \"name\": \"Назва\",\n \"date\": \"Дата\",\n \"status\": \"Статус\",\n \"opened\": \"Відкритий\",\n \"closed\": \"Закритий\",\n \"progress\": \"В процесі\"\n },\n \"id\": \"Айді Оголошення\",\n \"planner_create\": \"Планувальник створення оголошень\",\n \"not_found_packet\": \"Не вдалося знайти пакети\",\n \"password\": \"Пароль\",\n \"confirm_password\": \"Підтвердження пароля\",\n \"sign_in_account\": \"Увійдіть у свій акаунт\",\n \"sign_up\": \"Зареєструватися\",\n \"new_user\": \"Новий користувач?\",\n \"sign_in\": \"Увійти\",\n \"forgot_password\": \"Забули пароль?\",\n \"return_to_login\": \"« Назад до входу\",\n \"sign_up_account\": \"Створіть новий акаунт\",\n \"please_enter_first_name\": \"Будь ласка, вкажіть ім'я!\",\n \"please_enter_last_name\": \"Будь ласка, вкажіть прізвище!\",\n \"please_enter_phone\": \"Будь ласка, вкажіть номер телефона!\",\n \"wrong_phone_start\": \"Номер телефона має починатися з 380\",\n \"please_enter_valid_phone\": \"Будь ласка, вкажіть дійсний номер телефона!\",\n \"please_enter_email\": \"Будь ласка, вкажіть адресу електронної пошти!\",\n \"please_enter_valid_email\": \"Будь ласка, вкажіть дійсну адресу електронної пошти!\",\n \"please_enter_password\": \"Будь ласка, вкажіть пароль!\",\n \"please_enter_password_confirmation\": \"Будь ласка, підтвердіть пароль!\",\n \"passwords_do_not_match\": \"Пароль підтвердження не збігається\",\n \"password_min_length\": \"Довжина пароля має бути не менше\",\n \"characters\": \"символів\",\n \"verifying_email\": {\n \"title\": \"Підтвердження адреси E-mail\",\n \"text_1\": \"Вам необхідно підтвердити Ваш e-mail, щоб активувати Ваш обліковий запис.\",\n \"text_2\": \"Електронний лист з інструкціями щодо підтвердження Вашої адреси електронної пошти було надіслано на Вашу адресу\",\n \"text_3\": \"Не отримали листа?\",\n \"text_4\": \"Перевірте папку СПАМ або зателефонуйте за номером +380(98)-575-98-51.\",\n \"text_5\": \"Натисніть сюди\",\n \"text_6\": \"для повторного надсилання листа.\"\n },\n \"reset_password\": {\n \"title\": \"Забули пароль?\",\n \"btn\": \"Надіслати\"\n },\n \"add_real_photos\": \"Додайте в оголошення справжні фото товару...\",\n \"add_photo\": \"Додати фото\",\n \"select_another_city\": \"Обрати інше місто\",\n \"main\": \"Головне\",\n \"select_city\": \"Виберіть місто\",\n \"select_region\": \"Виберіть регіон\",\n \"buy_tokens\": \"Купити токени\",\n \"sites\": \"Сайти\",\n \"site_statuses\": {\n \"published\": \"Опубліковано\",\n \"archived\": \"Архівовано\",\n \"not_published\": \"Не опубліковано\",\n \"not_paid\": \"Не оплачено\"\n },\n \"name_site_cannot_be_empty\": \"Назва сайту не може бути порожньою\",\n \"name_of_product\": \"Назва товару\",\n \"sell_product\": \"Продаж продукту\",\n \"subdomain_site_cannot_be_empty\": \"Піддомен сайту не може бути порожнім\",\n \"subdomain\": \"Піддомен\",\n \"subdomain_min_length\": \"Введіть щонайменше 3 символи\",\n \"autosave\": \"Автозбереження\",\n \"builder\": \"Конструктор\",\n \"order_site\": \"Замовте сайт у нас\",\n \"help_with_site\": \"Потрібна допомога з розробкою сайтів?\",\n \"offer_an_exchange\": \"Пропоную торг\",\n \"offer_a_trade\": \"Пропоную обмін на\",\n \"with_your_surcharge\": \"З вашою доплатою\",\n \"with_my_surcharge\": \"З моєю доплатою\",\n \"offer_accepted\": \"Пропозицію прийнято\",\n \"offer_rejected\": \"Пропозицію відхилено\",\n \"accept\": \"Прийняти\",\n \"reject\": \"Відхилити\",\n \"channels_type\": {\n \"olx\": \"OLX\",\n \"ria\": \"RIA\",\n \"telegram_bot\": \"Telegram BOT\",\n \"telegram\": \"Telegram Номерний\",\n \"facebook\": \"Facebook\",\n \"instagram\": \"Instagram\",\n \"viber\": \"Viber\",\n \"whatsapp\": \"WhatsApp\",\n \"widget\": \"Віджет\"\n },\n \"chats_sidebar_menu\": {\n \"external\": \"Зовнішні чати\",\n \"olx\": \"OLX чати\",\n \"autoria\": \"Autoria чати\",\n \"autoria_old\": \"Autoria старі чати\",\n \"telegram_bot\": \"Telegram BOT чати\",\n \"telegram_user\": \"Telegram чати\",\n \"settings\": \"Налаштування\"\n },\n \"about_chat\": \"Про чат\",\n \"check\": \"Перевірити\",\n \"translate\": \"Перекласти\",\n \"yesterday\": \"Вчора\",\n \"today\": \"Сьогодні\",\n \"tabs\": {\n \"chats\": \"Чати\",\n \"opened\": \"Відкриті\",\n \"closed\": \"Закриті\"\n },\n \"sort\": {\n \"sort\": \"Сортування\",\n \"new\": \"Нові\",\n \"unread\": \"Непрочитані\"\n },\n \"mute\": \"Не сповіщати\",\n \"unmute\": \"Сповіщати\",\n \"update_chats\": \"Оновити чати\",\n \"select_chat\": \"Оберіть чат, щоб почати спілкування\",\n \"no_messages\": \"Немає повідомлень\",\n \"unblock\": \"Розблокувати\",\n \"share\": \"Поділитися\",\n \"unshare\": \"Скасувати спільний доступ\",\n \"pinned_header\": \"Прикріплено: {{count}} повідомлень\",\n \"card\": \"Карточка\",\n \"chat_settings\": {\n \"general_settings\": \"Загальні налаштування\",\n \"keyboard\": \"Клавіатура\",\n \"blocked_users\": \"Заблоковані користувачі\",\n \"enter_send\": \"Enter - надіслати\",\n \"cmd_enter_send\": \"⌘ + Enter – надіслати\",\n \"ctrl_enter_send\": \"Ctrl + Enter - надіслати\",\n \"shift_enter_send\": \"Shift + Enter - надіслати\",\n \"new_line_enter\": \"Новий рядок - Enter\",\n \"new_line_shift_enter\": \"Новий рядок - Shift + Enter\",\n \"notifications\": \"Сповіщення\",\n \"mark_as_read\": \"Відзначати повідомлення прочитаними під час відкриття\",\n \"enabled\": \"Увімкнено\",\n \"disabled\": \"Вимкнено\",\n \"no_blocked_users\": \"Заблокованих користувачів немає\",\n \"no_replies\": \"Швидких відповідей немає\",\n \"fast_replies\": \"Швидкі відповіді\",\n \"add_fast_replies\": \"Додати швидку відповідь\",\n \"saved_fast_replies\": \"Збережені швидкі відповіді\",\n \"other\": \"Інше\",\n \"lazy_loading\": \"Автоматична підгрузка сторінки (lazy loading)\"\n },\n \"file\": \"Файл\",\n \"fast_reply\": \"Швидка відповідь\",\n \"auto_reply\": \"Авто-відповідь\",\n \"no_results\": \"Результатів за запитом «{{text}}» немає. Спробуйте інший.\",\n \"api_key\": \"API ключ\",\n \"user_id\": \"ID користувача\",\n \"channel_id_for_webhook_ria\": \"ID каналу для вебхука RIA\",\n \"pass_this_indicator_related\": \"Передайте цей індикатор як related_user_id для webhooks для RIA\",\n \"pass_this_indicator_webhook\": \"Передайте цей індикатор як webhook_url для webhooks для RIA\",\n \"you_have_uploaded\": \"Ви завантажили\",\n \"files\": \"файлів\",\n \"code\": \"Код\",\n \"next\": \"Далі\",\n \"login_with_phone\": \"Увійти за допомогою телефону\",\n \"login_with_qr\": \"Увійти за допомогою QR-коду\",\n \"widgets\": \"Віджети\",\n \"edit_widget\": \"Редагувати віджет\",\n \"widgets_list\": \"Список віджетів\",\n \"choose_company\": \"Оберіть компанію\",\n \"spaces\": {\n \"shared\": \"Відкриті файли\",\n \"private\": \"Приватні файли\",\n \"cart\": \"Кошик\"\n },\n \"change_display_type\": \"Змінити тип відображення\",\n \"time\": {\n \"three\": \"3 сек\",\n \"five\": \"5 сек\",\n \"ten\": \"10 сек\",\n \"fifteen\": \"15 сек\",\n \"thirty\": \"30 сек\"\n },\n \"text_input\": \"Текстове поле\",\n \"textarea\": \"Коментар\",\n \"embed_code\": \"Код для вставки\",\n \"widget_name\": \"Назва віджета\",\n \"general_appearance\": \"Загальний вигляд\",\n \"background_color\": \"Колір фону\",\n \"icon_color\": \"Колір іконки\",\n \"choose_position\": \"Обрати положення\",\n \"show_time\": \"Час показу\",\n \"immediately\": \"одразу після завантаження сторінки\",\n \"delay\": \"з затримкою\",\n \"form\": \"Форма\",\n \"placeholder\": \"Заповнювач\",\n \"required\": \"Обов'язково\",\n \"mobile_devices\": \"Мобільні пристрої\",\n \"do_not_show\": \"не показувати на мобільних пристроях\",\n \"created_in_jecrm\": \"Створено у JECRM\",\n \"show_caption\": \"показувати надпис\",\n \"copy_to_clipboard\": \"Скопіювати у буфер\",\n \"input_name\": \"Назва поля\",\n \"seconds_short\": \"сек.\",\n \"duration\": \"Тривалість\",\n \"attachments\": {\n \"image\": \"Зображення\",\n \"video\": \"Відео\",\n \"gif\": \"Гіф\",\n \"audio\": \"Аудіо\",\n \"voice\": \"Голосове повідомлення\",\n \"round\": \"Кружочок\",\n \"sticker\": \"Стікер\",\n \"file\": \"Файл\",\n \"document\": \"Файл\"\n },\n \"plan_applied_ads\": \"Ви вибрали рекламні пакети, які вже придбані для таких оголошень: {{ advertsWithExpTime }}. Ви впевнені що хочете придбати або запланувати покупку?\",\n \"download_as\": \"Завантажити як...\",\n \"download_all\": \"Завантажити все\",\n \"clear_media_cache\": \"Очистити медіа кеш\",\n \"bot\": \"Бот\"\n}\n","{\n \"choose_best_plan\": \"Don't waste time, choose the plan that best suits your needs and start enjoying all the benefits right now!\",\n \"update_ad_price\": \"Need to change the price of your ad? Install PowerOLX and change the price of your ad right in the chat.\",\n \"internal_number_of_the_shipment\": \"Internal number of the shipment\",\n \"accompanying_documents\": \"Accompanying documents\",\n \"activate_promo_code\": \"Activate promo code\",\n \"add\": \"Add\",\n \"change\": \"Change\",\n \"reset\": \"Reset\",\n \"add_API_Key\": \"Add API Key\",\n \"add_a_column\": \"Add a column\",\n \"add_a_phone_number\": \"Add a phone number\",\n \"add_phone_number\": \"Add phone number\",\n \"go_back\": \"Go back to login\",\n \"add_place\": \"Add place\",\n \"add_quick_answers\": \"Add quick answers\",\n \"add_ttn\": \"Add waybill\",\n \"add_users_who_will_have_access_to_Telegram\": \"Add users who will have access to Telegram\",\n \"additional_information\": \"Additional information\",\n \"private_or_business\": \"Private or business\",\n \"private_person\": \"Private person\",\n \"business\": \"Business\",\n \"additional_information_about_the_shipment\": \"Additional information about the shipment\",\n \"additional_services\": \"Additional services\",\n \"address\": \"Address\",\n \"after_payment,_please_click_the_button_to_notify_the_manager_about_the_payment\": \"\\nAfter payment, please click the button to notify the manager about the payment\",\n \"and_must_be_an_email_address.\": \"And must be an email address.\",\n \"answers_to_questions_in_our_group\": \"Answers to questions in our group\",\n \"assign_and_save\": \"Assign and save\",\n \"buy\": \"Buy\",\n \"cancel\": \"Cancel\",\n \"buy_tariff\": \"Buy tariff\",\n \"buy_package\": \"Buy package\",\n \"cart\": \"Cart\",\n \"cash\": \"Cash\",\n \"cashless\": \"Cashless\",\n \"category\": \"Category\",\n \"channel\": \"Channel\",\n \"city\": \"City\",\n \"connect_olx\": \"Connect OLX channels\",\n \"choose_channel\": \"Choose channel:\",\n \"click_the_button\": \"Click the button\",\n \"climbing_to_the_floor\": \"Climbing to the floor\",\n \"close\": \"Close\",\n \"comment\": \"Comment\",\n \"connect\": \"Connect\",\n \"contact\": \"Contact\",\n \"contacts\": \"Contacts\",\n \"control_of_piecewise_transmission\": \"Control_of_piecewise_transmission\",\n \"copy_place\": \"Copy place\",\n \"costs\": \"Costs\",\n \"create\": \"Create\",\n \"create_channel\": \"Create channel\",\n \"create_company\": \"Create company\",\n \"create_contact\": \"Create contact\",\n \"create_folder\": \"Create folder\",\n \"create_key\": \"Create key\",\n \"create_role\": \"Create role\",\n \"create_template\": \"Create template\",\n \"current_account\": \"Current account\",\n \"cut\": \"Cut\",\n \"created_at\": \"Date of creation\",\n \"updated_at\": \"Date of update\",\n \"delete\": \"Delete\",\n \"delete_a_place\": \"Delete a place\",\n \"delete_funnel\": \"Delete funnel\",\n \"delivery_time\": \"Delivery time\",\n \"description\": \"Description\",\n \"disable\": \"Disable\",\n \"documents\": \"Documents\",\n \"enter_first_name\": \"Enter your first name\",\n \"enter_last_name\": \"Enter your last name\",\n \"enter_TTN_or_search_by_description\": \"Enter TTN or search by description\",\n \"enter_it_in_the_field_above_and_click_add\": \"Enter it in the field above and click add\",\n \"enter_phone_number\": \"Enter phone number\",\n \"enter_the_address\": \"Enter the address\",\n \"enter_your_e-mail\": \"Enter your e-mail\",\n \"email_format_is_incorrect\": \"Email format is incorrect\",\n \"enter_your_phone_number\": \"Enter your phone number\",\n \"enter_password\": \"Enter password\",\n \"enter_password_confirmation\": \"Enter password again\",\n \"enter_promo_code\": \"Enter promo code\",\n \"exit\": \"Exit\",\n \"form_of_payment_for_delivery\": \"Form of payment for delivery\",\n \"from_the_transaction\": \"From the transaction\",\n \"general\": \"General\",\n \"go_to_the_marketplace\": \"Go to the marketplace\",\n \"go_to_the_settings_and_select_the_section\": \"Go to the settings and select the section\",\n \"here_you_can_enter/change_your_company_name\": \"Here you can enter/change your company name\",\n \"in_detail\": \"In detail\",\n \"in_the_comments_to_the_payment,_enter_the_code\": \"In the comments to the payment, enter the code\",\n \"connection_instructions_a_api_key\": \"Instructions for connecting a new API\",\n \"connection_instructions\": \"Instructions for connecting\",\n \"buy_ai\": \"You do not have artificial intelligence installed, click yes to install\",\n \"integration_of_Nova_Poshta\": \"Integration of Nova Poshta\",\n \"language\": \"Language\",\n \"lazy_loading\": \"Lazy loading\",\n \"length\": \"Length\",\n \"log_in_with_your_OLX_account\": \"Log in with your OLX account\",\n \"log_in_with_your_RIA_account\": \"Log in with your RIA account\",\n \"log_in_with_your_telegram_account\": \"Log in with your Telegram account\",\n \"mail\": \"Mail\",\n \"main_currency\": \"Main currency\",\n \"manual_processing\": \"Manual processing\",\n \"mark_as_read\": \"Mark as read on opening\",\n \"max_characters\": \"Maximum number of characters\",\n \"min_characters\": \"Minimum number of characters\",\n \"max_value\": \"Maximum value\",\n \"min_value\": \"Minimum value\",\n \"min_length\": \"Enter at least 40 characters\",\n \"planner_min_min_length\": \"Enter at least 4 characters\",\n \"minimum\": \"Minimum\",\n \"money_transfer\": \"Money transfer\",\n \"name\": \"Name\",\n \"apply_to_all\": \"apply_to_all\",\n \"moving_up_the_list\": \"Moving up the list\",\n \"easy_start\": \"Easy start\",\n \"quick_sale\": \"Quick sale\",\n \"turbo_sale\": \"Turbo sale\",\n \"name_card_cannot_be_empty\": \"Name card cannot be empty\",\n \"name_channel\": \"Name channel\",\n \"type_channel\": \"Type channel\",\n \"name_column\": \"Name column\",\n \"is_read_messages\": \"Read messages\",\n \"is_create_deal\": \"Create a deal\",\n \"name_company\": \"Name company\",\n \"name_field\": \"Name field\",\n \"name_funnel\": \"Name funnel\",\n \"name_office\": \"Name office\",\n \"name_role\": \"Name role\",\n \"name_template\": \"Name template\",\n \"notification_sound\": \"Notifications sound\",\n \"notifications\": \"Notifications\",\n \"notify\": \"Notify\",\n \"nova_poshta\": \"Nova Poshta\",\n \"upgrade_plan\": \"Now is the best time to upgrade your plan and get access to the full range of features and capabilities offered by {{NAME_PROJECT}}\",\n \"number_of_places\": \"Number of places\",\n \"open_a_chat\": \"Open_a_chat\",\n \"optional\": \"Optional\",\n \"package_number\": \"Package number\",\n \"packaging\": \"Packaging\",\n \"pallets\": \"Pallets\",\n \"parcels_and_cargoes\": \"Parcels and cargoes\",\n \"pay\": \"Pay\",\n \"payer_for_delivery\": \"Payer for delivery\",\n \"payment_control\": \"Payment control\",\n \"payment_method\": \"Payment method\",\n \"phone\": \"Phone\",\n \"phone_number_is_missing\": \"Phone number is missing\",\n \"pin\": \"Pin\",\n \"please_click_the_manager_notification_button\": \"Please click the manager notification button\",\n \"policy_file_cookie\": \"Policy file cookie\",\n \"price\": \"Price\",\n \"private_policy\": \"Private policy\",\n \"product_description\": \"Product description\",\n \"promo_code\": \"Promo code\",\n \"recipient\": \"Recipient\",\n \"reconnect\": \"Reconnect\",\n \"return_delivery_of_document_subtypes\": \"Return delivery of document subtypes\",\n \"return_delivery_of_documents\": \"Return delivery of documents\",\n \"return_delivery_of_signed_documents\": \"Return delivery of signed documents\",\n \"save\": \"Save\",\n \"order\": \"Order\",\n \"security\": \"Security\",\n \"sender\": \"Sender\",\n \"shipping_parameters\": \"Shipping parameters\",\n \"status\": \"Status\",\n \"telephone\": \"Telephone\",\n \"account_connected_messenger\": \"The account is connected, now you can use\",\n \"on_any_device\": \"on any device and receive all notifications that come to this OLX account directly in the messenger\",\n \"the_bot_is_available_at_this_link\": \"The bot is available at this link\",\n \"the_declared_cost\": \"The declared cost\",\n \"the_dollar_rate_is_calculated_by\": \"The dollar rate is calculated by:\",\n \"new_plan_benefits\": \"The new plan will open up endless possibilities for you to manage your business effectively\",\n \"there_are_no_columns\": \"There are no columns\",\n \"field_cannot_be_empty\": \"This field can't be empty.\",\n \"email_field_cannot_be_empty\": \"This field can't be empty, and must be in email format.\",\n \"tires_and_wheels\": \"Tires and wheels\",\n \"total_volume\": \"Total volume\",\n \"total_weight\": \"Total weight\",\n \"type_of_logistics\": \"Type of logistics\",\n \"unpin\": \"Unpin\",\n \"update\": \"Update\",\n \"updated\": \"Updated\",\n \"updating\": \"Updating\",\n \"upload_file\": \"Upload file\",\n \"uploaded_files\": \"Uploaded files\",\n \"or_paste_a_link\": \"Or paste a link from web\",\n \"upload\": \"Upload\",\n \"use_promo_code\": \"Use promo code\",\n \"volumetric_weight\": \"Volumetric weight\",\n \"width\": \"Width\",\n \"weight\": \"Weight\",\n \"you_can_delete_at_any_time\": \"You can delete at any time\",\n \"you_will_be_provided_with_a_new_API_key\": \"You will be provided with a new API key\",\n \"your_phone_number\": \"Your phone number\",\n \"аdd_field\": \"Add field\",\n \"channels\": \"Channels\",\n \"telegram_bot\": \"TELEGRAM BOT\",\n \"close_chat\": \"Close chat\",\n \"go_to_the_Telegram_bot_and_register\": \"Go to the Telegram bot and register\",\n \"the_site_is_available_at_this_link\": \"The site is available at this link\",\n \"account_settings\": \"Account settings\",\n \"please_list_the_following\": \"Please list the following\",\n \"this_account_is_paused:_please_contact_the_administrator\": \"This account is paused: please contact the administrator\",\n \"error_receiving_message:_please_contact_the_administrator\": \"Error receiving message: please contact the administrator\",\n \"more_about_the_functionality\": \"More about the functionality\",\n \"tariff_plan\": \"Tariff plan\",\n \"your_tariff\": \"Your tariff\",\n \"learn_more_about_our_products_and_how_they_work_in_our_short_video\": \"Learn more about our products and how they work in our short video\",\n \"see_how_our_solutions_can_make_your_life_and_business_easier\": \"See how our solutions can make your life and business easier\",\n \"we_offer_you_the_following_pricing_plans\": \"We offer you the following pricing plans\",\n \"our_primary_goal_is_your_complete_satisfaction._We_dedicate_many_hours_and_effort_to_provide_you_with_the_best_service_and_provide_you_with_solutions_that_truly_meet_your_needs\": \"Our primary goal is your complete satisfaction. We dedicate many hours and efforts to provide you with the best service and provide you with solutions that truly meet your needs\",\n \"user\": \"User\",\n \"allow\": \"Allow\",\n \"we_care_about_our_customers\": \"We care about our customers\",\n \"amount\": \"Amount\",\n \"tariff_limitations\": \"Limitations of the tariff\",\n \"month\": \"Month\",\n \"months\": \"Months\",\n \"enjoy_the_video\": \"Enjoy the video\",\n \"application\": \"Application\",\n \"a_new_window_will_appear_in_which\": \"A new window will appear in which\",\n \"you_need_to_grant_access_to_your_OLX_account.\": \"You need to provide access to the OLX account\",\n \"in_the_account_connection_tab,_click_the_button\": \"In the account connection tab, click the button\",\n \"using_your_credentials\": \"using your credentials\",\n \"by_choosing_us_you_get\": \"By choosing us you get\",\n \"in_a_new_tab,_go_to\": \"In a new tab, go to\",\n \"UAH_to_the_account\": \"UAH to the account\",\n \"for_7_days\": \"For 7 days\",\n \"you_are_our_main_goal_and_we_are_proud_of_our_ability_to_make_your_life_better\": \"You are our main goal and we are proud of our ability to make your life better\",\n \"channel_settings\": \"Channel settings\",\n \"copy_the_message\": \"Copy the message\",\n \"save_as_a_quick_reply\": \"Save as a quick reply\",\n \"check_if_read\": \"Check if read\",\n \"send_again\": \"Send again\",\n \"customize\": \"Customize\",\n \"the_selected_subdomain_may_already_be_taken._Please_select_another_subdomain_up_to_10_characters_using_only_lowercase-Latin_letters_and_a_hyphen._The_hyphen_cannot_be_the_last_character_of_the_subdomain.\": \"The selected subdomain may already be taken. Please select another subdomain up to 10 characters using only lowercase Latin letters and a hyphen. The hyphen cannot be the last character.\",\n \"you_can_change_the_company's_subdomain_only_for_the_first_3_days\": \"You can change the company's subdomain only for the first 3 days\",\n \"save_an_already_sent_message_as_a_quick_reply_by_right-clicking_on_the_message\": \"Save an already sent message as a quick reply by right-clicking on the message\",\n \"use_quick,_ready_to_use_answers_to_the_most_frequently_asked_questions_from_customers\": \"Use quick, ready-to-use answers to the most frequently asked questions from customers\",\n \"is_a_modern_and_innovative_CRM_system_that_transforms_your_business_into_a_more_efficient_and_productive_one._Our_platform_specializes_in_optimizing_the_work_with_popular_online_resources_such_as_OLX,_providing_many_opportunities_to_automate_and_simplify_your_daily_tasks\": \"is a modern and innovative CRM system that transforms your business into a more efficient and productive one. Our platform specializes in optimizing the work with popular online resources such as OLX, providing many opportunities to automate and simplify your daily tasks\",\n \"home\": \"Home\",\n \"home_page\": \"Home page\",\n \"name_color\": \"Name color\",\n \"profile\": \"Profile\",\n \"no_dialog_found\": \"No dialog found\",\n \"change_password\": \" Change password\",\n \"pinned_messages\": \"Pinned messages\",\n \"change_the_name\": \"Change the name\",\n \"announcements\": \"Announcements\",\n \"auto_reply_to_the_first_message\": \"Auto reply to the first message\",\n \"UAH\": \"UAH\",\n \"end_the_conversation_t\": \"End the conversation\",\n \"details_of_the_ad\": \"Details of the ad\",\n \"block\": \"Block\",\n \"update_your_channel\": \"Update your channel\",\n \"create_a_new_channel\": \"Create a new channel\",\n \"responsible_for_the_channel\": \"Responsible for the channel\",\n \"select_a_client\": \"Select a client\",\n \"choose_role\": \"Choose a role\",\n \"select\": \"Select\",\n \"your_address\": \"Your address\",\n \"upload_more\": \"Upload more\",\n \"read\": \"Read\",\n \"is_read\": \"Is read\",\n \"read_all\": \"Read all\",\n \"start_a_dialog_by_selecting_a_contact_from_the_list_on_the_left\": \"Start a dialog by selecting a contact from the list on the left\",\n \"first_new\": \"First new\",\n \"first_unread\": \"First unread\",\n \"topics\": \"Topics\",\n \"mark_as_reads\": \"Mark as read\",\n \"mark_as_unread\": \"Mark as unread\",\n \"keyboard_shortcut_to_send_a_message\": \"Keyboard shortcut to send a message\",\n \"mark_messages_as_read_when_opened\": \"Mark messages as read when opened\",\n \"saved_quick_replies\": \"Saved quick replies\",\n \"quick_replies\": \"Quick replies\",\n \"new_line\": \"New line\",\n \"unfollow\": \"Unfollow\",\n \"create_a_new_one_in_chat_settings\": \"Create a new one in chat settings\",\n \"enable_auto_reply\": \"Enable auto-reply\",\n \"column_color\": \"Column color\",\n \"unlock_your_potential\": \"Unlock your potential\",\n \"choose_rate\": \"Choose a rate\",\n \"selected\": \"Selected\",\n \"create_new_role\": \"Create a new role\",\n \"update_your_role\": \"Update your role\",\n \"information\": \"Information\",\n \"information_about\": \"Information about\",\n \"click_here\": \"Click here\",\n \"your_account_information\": \"Your account information\",\n \"available_balance\": \"Available balance\",\n \"you_can_choose_from_several_options\": \"You can choose from several options\",\n \"bonuses\": \"Bonuses\",\n \"display_in_quick_trades\": \"Display in quick trades\",\n \"the_user_is_entitled_to\": \"The user is entitled to\",\n \"enter_the_name_of_the\": \"Enter the name of the\",\n \"type_of_scheduler\": \"Type of scheduler\",\n \"status_of_scheduler\": \"Status of scheduler\",\n \"new\": \"New\",\n \"or_transfer_files\": \"Or transfer files\",\n \"now_you_can\": \"Now you can manage all your messages and inquiries from OLX in one place, responding to them quickly and efficiently .and respond to them quickly and efficiently .Forget about the need to log in to different accounts - we\\\\'ll make communication via OLX simple and convenient\",\n \"communicate_with_your_customers_easily_and_efficiently._Our_service_combines_all_your_OLX_accounts_in_one_convenient_interface\": \"Communicate with your customers easily and efficiently. Our service combines all your OLX accounts in one convenient interface\",\n \"messages_from_all_accounts_in_a_convenient_interface\": \"Messages from all accounts in a convenient interface\",\n \"introducing_the_PowerOLX_app\": \"Introducing the PowerOLX app\",\n \"powerOLX_is_a_JECRM_tool_that_allows_you_to_automate_the_work_with_ads_on_OLX,_activate_your_ads_instantly_or_on_a_schedule._This_allows_you_to_reach_a_larger_audience_and_get_more_sales.\": \"PowerOLX is a JECRM tool that allows you to automate the work with ads on OLX, activate your ads instantly or on a schedule. This allows you to reach a larger audience and get more sales.\",\n \"we_invite_you_to_test_PowerOLX_in_the_Applications_section_JECRM_System_Store\": \"We invite you to test PowerOLX in the Applications section - JECRM System Store\",\n \"Log_in_to_your_OLX_account_in_this_browser\": \"Log in to your OLX account in this browser\",\n \"instructions_for_obtaining_account_data\": \"Instructions for obtaining account data\",\n \"pen_the_browser_in_which_you_log_in_to_the_OLX_account_you_need\": \"Open the browser in which you log in to the OLX account you need\",\n \"domain\": \"Domain\",\n \"business_account\": \"Business account\",\n \"yes\": \"Yes\",\n \"no\": \"No\",\n \"phone_number\": \"Phone number\",\n \"test_mode\": \"Test mode\",\n \"in_development\": \"In development\",\n \"coming_soon\": \"Coming soon\",\n \"support\": \"Support\",\n \"structure\": \"Structure\",\n \"CRM\": \"CRM\",\n \"Integrations\": \"Integrations\",\n \"to_connect_your_cash_register\": \"To connect your cash register, you need to enter the cashier's login and password (or pin code) and the Checkbox license in the form\",\n \"to_connect_your_integration,_you_need_to_enter_the_API_key_in_the_form\": \"To connect your integration, you need to enter the API key in the form\",\n \"integration_setup_is_very_simple_and_fast\": \"Integration setup is very simple and fast\",\n \"logistics\": \"Logistics\",\n \"chats\": \"Chats\",\n \"JE_Space\": \"JE Space\",\n \"automation\": \"Automation\",\n \"applications\": \"Applications\",\n \"API_key_settings\": \"API key settings\",\n \"funnel_settings\": \"Funnel settings\",\n \"settings\": \"Settings\",\n \"Payment\": \"Payment\",\n \"PowerOLX\": \"PowerOLX\",\n \"store\": \"Store\",\n \"warehouse\": \"Warehouse\",\n \"products\": \"Products\",\n \"categories\": \"Categories\",\n \"people\": \"People\",\n \"disk\": \"Drive\",\n \"User_profile\": \"User profile\",\n \"deals\": \"Deals\",\n \"ringostat\": \"Ringostat\",\n \"ukr_poshta\": \"Ukr Poshta\",\n \"opened\": \"Opened\",\n \"closed\": \"Closed\",\n \"externals\": \"Externals\",\n \"search\": \"Search\",\n \"fast_answer\": \"Fast answer\",\n \"blocked_users\": \"Blocked users\",\n \"hot_key\": \"Hot Key\",\n \"team\": \"Team\",\n \"backup_is_done_automatically_every_7_days\": \"Backup is done automatically every 7 days\",\n \"page\": \"Page\",\n \"last_name\": \"Last name\",\n \"warning\": \"Warning\",\n \"end\": \"End\",\n \"search_message\": \"Search message\",\n \"login\": \"Login\",\n \"connected\": \"Connected\",\n \"telegram\": \"Telegram\",\n \"olx_message\": \"OLX\",\n \"telephony\": \"Telephony\",\n \"month_name\": {\n \"january\": \"January\",\n \"february\": \"February\",\n \"march\": \"March\",\n \"april\": \"April\",\n \"mays\": \"May\",\n \"june\": \"June\",\n \"july\": \"July\",\n \"august\": \"August\",\n \"september\": \"September\",\n \"october\": \"October\",\n \"november\": \"November\",\n \"december\": \"December\",\n \"jan\": \"Jan\",\n \"feb\": \"Feb\",\n \"mar\": \"Mar\",\n \"apr\": \"Apr\",\n \"may\": \"May\",\n \"jun\": \"Jun\",\n \"jul\": \"Jul\",\n \"aug\": \"Aug\",\n \"sep\": \"Sep\",\n \"oct\": \"Oct\",\n \"nov\": \"Nov\",\n \"dec\": \"Dec\"\n },\n \"opened_chats\": \"Opened chats\",\n \"closed_chats\": \"Closed chats\",\n \"reopened_chats\": \"Reopened chats\",\n \"invite\": \"Invite\",\n \"invite_new_manager\": \"Invite new manager\",\n \"companies\": \"Companies\",\n \"invited_users\": \"Invited users\",\n \"managers\": \"Managers\",\n \"role\": \"Role\",\n \"roles\": \"Roles\",\n \"waybill_tracker\": \"Waybill tracker\",\n \"my_adverts\": \"My adverts\",\n \"planning\": \"Planning\",\n \"backup\": \"Backup\",\n \"select_the_tariff\": \"Select the tariff\",\n \"pay_my_tariff\": \"Pay my tariff\",\n \"pay_tariff\": \"Pay tariff\",\n \"choose_payment_method\": \"Choose a payment method and fill out the payment form\",\n \"date\": \"Date\",\n \"tariff\": \"Tariff\",\n \"approved\": \"Approved\",\n \"canceled\": \"Canceled\",\n \"in_process\": \"In process\",\n \"rejected\": \"Rejected\",\n \"your_current_price_will_be\": \"Your current price will be\",\n \"email\": \"Email\",\n \"rights_for_roles\": \"Rights for roles\",\n \"rights_for_all\": \"Rights for all\",\n \"allowed\": \"Allowed\",\n \"denied\": \"Denied\",\n \"resend\": \"Resend\",\n \"first_name\": \"First name\",\n \"avatar\": \"Avatar\",\n \"update_data\": \"Update data\",\n \"quick_deal\": \"Quick deal\",\n \"sent_at\": \"Sent date\",\n \"delivered_at\": \"Delivered date\",\n \"approximate_arrival_date\": \"Approximate arrival date\",\n \"responsible\": \"Responsible\",\n \"email_missing\": \"Email is missing\",\n \"chat_missing\": \"Chat is missing\",\n \"delete_deal\": \"Delete deal\",\n \"create_funnel\": \"Create funnel\",\n \"create_client\": \"Create client\",\n \"company_name\": \"Company name\",\n \"create_new_contact\": \"Creating new contact\",\n \"edit\": \"Edit\",\n \"planned_activations\": \"Planned activations\",\n \"valid_to\": \"Valid to\",\n \"link\": \"Link\",\n \"contact_phone\": \"Contact phone\",\n \"contact_email\": \"Contact email\",\n \"reason_error\": \"Reason error\",\n \"run_at\": \"Date run\",\n \"type\": \"Type\",\n \"created_at_olx\": \"Date created in OLX\",\n \"olx\": {\n \"adverts\": {\n \"active\": \"Active\",\n \"pending\": \"Pending\",\n \"unpaid\": \"Unpaid\",\n \"inactive\": \"Inactive\",\n \"rejected\": \"Rejected\",\n \"successfully\": \"Successfully\",\n \"in_progress\": \"In progress\",\n \"canceled\": \"Canceled\",\n \"fail\": \"Fail\",\n \"waiting\": \"Waiting\",\n \"draft\": \"Draft\",\n \"removed_by_moderator\": \"Removed by moderator\"\n },\n \"planners\": {\n \"planner_name\": \"Planner name\",\n \"advert_name\": \"Advert name\"\n },\n \"packets\": {\n \"active\": \"Active\",\n \"inactive\": \"Inactive\",\n \"start\": \"START package\",\n \"premium\": \"PREMIUM package\",\n \"mega\": \"MEGA package\",\n \"available\": \"Available\",\n \"valid_until\": \"Valid until\",\n \"placements\": \"placements\",\n \"active_for\": \"active for\",\n \"days\": \"more days\",\n \"location\": \"Location\"\n },\n \"delivery\": {\n \"status_short\": {\n \"accepted\": \"Confirmed\",\n \"waiting\": \"Awaiting confirmation\",\n \"rejected\": \"Rejected\"\n },\n \"status\": {\n \"accepted\": {\n \"accepted\": \"Expected to ship by \",\n \"money_on_seller\": \"Completed successfully\",\n \"delivery_in_progress\": \"On the way to the recipient\",\n \"delivery_at_recipients_office\": \"At recipients office\"\n },\n \"waiting\": {\n \"money_blocked\": \"Awaiting confirmation by \"\n },\n \"rejected\": {\n \"rejected\": \"Rejected\",\n \"delivery_rejected_by_partner\": \"Rejected (The order has expired)\",\n \"money_on_buyer\": \"Rejected\",\n \"rejected_by_buyer\": \"Rejected\"\n },\n \"data_error\": \"Data processing error. Contact the administrator\"\n },\n \"other\": {\n \"express_waybill\": \"Express-waybill\",\n \"delivery_service\": \"Delivery service\",\n \"nova_poshta\": \"Nova Poshta\",\n \"ukrposhta\": \"Ukrposhta\",\n \"meest\": \"Meest\",\n \"to_department\": \"To the department \",\n \"order_number\": \"Order number\"\n }\n }\n },\n \"filter_by_status\": \"Filter by status\",\n \"date_and_hours\": \"Date and hours\",\n \"call_time\": \"Call time\",\n \"type_of_call\": \"Type of call\",\n \"link_on_call\": \"Link on call\",\n \"subscriber\": \"Subscriber\",\n \"recording\": \"Recording\",\n \"faq\": \"FAQ\",\n \"ticket\": \"Create ticket\",\n \"video_guide\": \"Video guide\",\n \"part\": \"Part\",\n \"presentation\": \"Presentation\",\n \"functionality\": \"Functionality\",\n \"support_questions\": {\n \"question_1\": {\n \"question\": \"How to check if my message was read?\",\n \"answer\": \"On PC, right-click and check if it was read, on phone just tap and check if it was read.\"\n },\n \"question_2\": {\n \"question\": \"How to register in JECRM?\",\n \"answer\": \"To register, go to the JECRM website and fill out the registration form, providing your email and phone number. After registration, you will gain access to your personal account.\"\n },\n \"question_3\": {\n \"question\": \"Can I integrate JECRM with OLX without special skills?\",\n \"answer\": \"Yes, JECRM is user-friendly, allowing you to easily integrate your OLX account with the system through a convenient interface without the need for special technical skills.\"\n },\n \"question_4\": {\n \"question\": \"Does JECRM support mobile devices?\",\n \"answer\": \"Yes, the system is adapted for use with mobile devices, allowing you to manage your ads and communicate with customers anytime and anywhere.\"\n },\n \"question_5\": {\n \"question\": \"How to set up notifications for new orders in JECRM?\",\n \"answer\": \"In your JECRM profile settings, you can specify your preferred method of receiving notifications about new orders or messages from customers, including email and messengers.\"\n },\n \"question_6\": {\n \"question\": \"How to set up notifications in Telegram about messages from OLX?\",\n \"answer\": \"You can set up the JECRM bot in Telegram to receive messages as soon as they arrive from OLX, ensuring quick and convenient information perception.\"\n },\n \"question_7\": {\n \"question\": \"What are quick replies in JECRM?\",\n \"answer\": \"Quick replies allow you to save frequently used texts to facilitate and speed up communication with customers, minimizing the need for manual message entry.\"\n },\n \"question_8\": {\n \"question\": \"How to use the logistics functionality in JECRM?\",\n \"answer\": \"JECRM has a tool for tracking parcels from Nova Poshta and Ukrposhta, which allows you to notify customers about the delivery status and other important moments, improving customer service.\"\n },\n \"question_9\": {\n \"question\": \"How to register and use the main functions of JECRM?\",\n \"answer\": \"JECRM offers easy registration and profile creation for managing ads on OLX, ensuring account integration and channels for efficient sales management.\"\n },\n \"question_10\": {\n \"question\": \"What are the benefits of using JECRM for business on OLX?\",\n \"answer\": \"JECRM helps to control a large number of orders, optimize message responses, and increase sales efficiency through OLX by offering integration with messengers and delivery services.\"\n },\n \"question_11\": {\n \"question\": \"What's new in JECRM in 2024?\",\n \"answer\": \"In 2024, JECRM introduced updates that include system optimization, chat improvement, new logistics and delivery functionality, and minimized returns thanks to improved customer notifications.\"\n },\n \"question_12\": {\n \"question\": \"How to activate ads on OLX using JECRM?\",\n \"answer\": \"JECRM offers functionality to activate ads on OLX according to a schedule, facilitating efficient management of your ads and increasing the chances of successful sales.\"\n },\n \"question_13\": {\n \"question\": \"Can I use JECRM for delivery tracking?\",\n \"answer\": \"Yes, JECRM allows you to track deliveries through Nova Poshta and Ukrposhta, providing up-to-date information on the status of parcels directly in the system, which increases customer satisfaction and reduces the number of returns.\"\n }\n },\n \"created_at_crm\": \"Date created in CRM\",\n \"enter_first_reply\": \"Enter the text of the first answer\",\n \"active_status\": \"Active\",\n \"expired_status\": \"Need reconnect\",\n \"closed_status\": \"Need connect\",\n \"paused_status\": \"On pause\",\n \"beta_test\": \"Beta-test\",\n \"just_now\": \"Just now\",\n \"ttn\": \"Waybill\",\n \"test_regime\": \"Test regime\",\n \"what_on_what\": \"What on what?\",\n \"details\": \"Details\",\n \"change_name\": \"Change name\",\n \"select_connection\": \"Select connection\",\n \"archived_channels\": \"Archived channels\",\n \"choose_tariff\": \"Choose tariff\",\n \"choose_topic\": \"Choose topic\",\n \"choose_files\": \"Choose files\",\n \"create_field\": \"Create field\",\n \"change_field\": \"Change field\",\n \"create_column\": \"Create column\",\n \"change_column\": \"Change column\",\n \"to_archive\": \"To archive\",\n \"activate_now\": \"Activate now\",\n \"deactivate_now\": \"Deactivate now\",\n \"plan_activation\": \"Planning activation\",\n \"to_plan\": \"To plan\",\n \"publish_now\": \"Publish now\",\n \"move_up_list\": \"Move to up in list\",\n \"ads\": \"Advertising\",\n \"change_price\": \"Change price\",\n \"duplicate\": \"Duplicate\",\n \"change_planning\": \"Change planning\",\n \"cancel_planning\": \"Cancel planning\",\n \"to_draft\": \"To draft\",\n \"to_active\": \"To active\",\n \"restore\": \"Restore\",\n \"download\": \"Download\",\n \"add_in_tracker\": \"Add in tracker\",\n \"waiting_connect\": \"Waiting connect\",\n \"stopped\": \"Stopped\",\n \"columns\": \"Columns\",\n \"fields\": \"Fields\",\n \"change_role\": \"Change role\",\n \"end_the_conversation\": \"If the question in the dialogue is resolved, you can end it. This will help keep focus on open dialogues. If the client writes to you again, you will see this chat in the open list.\",\n \"no_one_field\": \"There is no one field.\",\n \"your_subdomain\": \"Your subdomain\",\n \"integration\": \"Integration\",\n \"write_a_message\": \"Write a message\",\n \"announcement_scheduler\": \"Announcement scheduler\",\n \"ads_scheduler\": \"ADS scheduler\",\n \"ads_activation_time\": \"Ads activation time\",\n \"change_the_ad_activation_time\": \"Change the ad activation time\",\n \"re_planning\": \"Re-planning\",\n \"change_the_activation_time_of_the_ads\": \"Change the activation time of the ad(s)\",\n \"ukr_poshta_status\": {\n \"registered\": \"Registered\",\n \"in_department\": \"In Department\",\n \"delivering\": \"Delivering\",\n \"delivered\": \"Delivered\",\n \"returning\": \"Returning\",\n \"returned\": \"Returned\",\n \"forwarding\": \"Forwarding\",\n \"cancelled\": \"Cancelled\",\n \"storage\": \"Storage\",\n \"error\": \"Error\"\n },\n \"errors\": {\n \"default_error\": \"Oops, something went wrong. Please try again or contact the administrator.\",\n \"internal_error\": \"Oops, something went wrong. Please try again or contact the administrator.\",\n \"max_file_size\": \"File size should not exceed 5MB.\",\n \"max_memory_limit_reached\": \"Maximum memory limit reached. Contact the administrator.\",\n \"max_integration\": \"You have reached the integration limit of {0} for your account.\",\n \"max_price\": \"The price is too high.\",\n \"user_not_found\": \"User with the specified email address not found\",\n \"integration_not_found\": \"Integration not found.\",\n \"contact_already_exists\": \"A contact with this number already exists.\",\n \"phone_already_exists\": \"This phone number is already in use. Please try another.\",\n \"domain_already_exists\": \"This subdomain is already taken. Please try another.\",\n \"integration_already_exists\": \"Integration already exists.\",\n \"column_already_exists\": \"This column field already exists.\",\n \"custom_field_already_exists\": \"A custom field with this key already exists.\",\n \"user_already_exists\": \"This user already exists.\",\n \"channel_user_already_exists\": \"Channel with this user already exists.\",\n \"user_already_invited\": \"A user with this email has already been invited.\",\n \"current_email_exists\": \"The current user's email address cannot be used for the invitation.\",\n \"promo_code_expired\": \"The promo code has already been used or has expired.\",\n \"account_olx_expired\": \"This OLX account needs to be reconnected.\",\n \"account_olx_pause\": \"This OLX account is on pause. Please contact the administrator.\",\n \"access_rejected\": \"Insufficient permissions. Please contact the administrator.\",\n \"channel_already_exists\": \"This name already exists.\",\n \"funnel_already_exists\": \"This name already exists.\",\n \"tokens_outdated\": \"Tokens are outdated. Please contact the administrator.\",\n \"empty_field_name\": \"The «Name» field cannot be empty\",\n \"empty_field_trigger\": \"The «Trigger» field cannot be empty\",\n \"empty_field_action\": \"The «Action» field cannot be empty\",\n \"empty_field_channel\": \"The «Channel» cannot be empty\",\n \"not_enough_credits\": \"Not enough credits\",\n \"email_already_exist\": \"User exists with same email\",\n \"invalid_credentials\": \"Incorrect username or password\",\n \"not_verified_email\": \"Your email has not been verified\",\n \"ad_cpesified_currency\": \"In this category, the ad price cannot be specified in currency.\",\n \"invite_not_found\": \"Invite not found\",\n \"phone_number_invalid\": \"The phone number is invalid\",\n \"phone_number_unoccupied\": \"The phone number is not yet being used\",\n \"phone_number_flood\": \"You asked for the code too many times\",\n \"phone_number_banned\": \"The provided phone number is banned from telegram\",\n \"phone_code_invalid\": \"The provided phone code is invalid\",\n \"phone_code_expired\": \"The phone code you provided has expired\",\n \"phone_code_empty\": \"The phone code is missing\",\n \"phone_password_flood\": \"You have tried logging in too many times\",\n \"phone_password_protected\": \"This phone is password protected\",\n \"password_hash_invalid\": \"The provided password is invalid\",\n \"sms_code_create_failed\": \"An error occurred while creating the SMS code\"\n},\n \"install\": \"Install\",\n \"open\": \"Open\",\n \"wait_for_confirmation\": \"Wait for confirmation\",\n \"installation\": \"Installation\",\n \"companies_info\": {\n \"you_are_just_a_step_away_from\": \"You are just a step away from...\",\n \"you_are_just_a_few_minutes_from\": \"You are just a few minutes from...\",\n \"you_are_just_a_moment_away_from\": \"You are just a moment away from...\",\n \"slide2\": \"Optimized work with your clients and customers thanks to our integrated system.\",\n \"slide3\": \"Automated scheduling and tracking of site visits, as well as service management tailored to your business.\",\n \"slide4\": \"Clear documentation and convenient communication in your personal database JECRM.\",\n \"slide5\": \"Ensuring transparency at every stage of work, so you can control every detail of your business.\",\n \"slide1\": \"Effective management of your e-commerce business with JECRM.\"\n },\n \"client_read\": \"Client read\",\n \"unread\": \"Unread\",\n \"notifications_texts\": {\n \"info\": {\n \"thanks_for_payment\": \"Thank you for payment!\",\n \"thanks_for_order\": \"Thank you for order!\",\n \"new_messages\": \"New messages!\",\n \"copied\": \"Copied\",\n \"no_notifications\": \"You have no notifications\",\n \"nothing_for_request\": \"Nothing found for your request\",\n \"pay_ads\": \"You have successfully applied for payment for advertising. After some time, you will receive a notification with the result.\"\n },\n \"success\": {\n \"fast_answer_save\": \"Quick response saved\",\n \"message_read\": \"Message read\",\n \"successfully_saved\": \"Successfully saved\",\n \"successfully_updated\": \"Successfully updated\",\n \"successfully_created\": \"Successfully created\",\n \"successfully_deleted\": \"Successfully deleted\",\n \"successfully_added\": \"Successfully added\",\n \"successfully_moved\": \"Successfully moved\",\n \"successfully_installed\": \"Successfully installed\",\n \"successfully_downloaded\": \"Successfully downloaded\",\n \"successfully_planned\": \"Successfully planned\",\n \"successfully_bought\": \"Successfully bought\",\n \"successfully_canceled\": \"Successfully canceled\",\n \"successfully_disconnect\": \"Successfully disconnected\",\n \"successfully_reconnected\": \"Successfully reconnected\",\n \"successfully_sent\": \"Successfully sent\",\n \"successfully_resent\": \"Successfully resent\",\n \"successfully_archived\": \"Successfully archived\",\n \"successfully_reset\": \"Successfully reset\",\n \"successfully_cleared\": \"Successfully cleared\",\n \"user_has_been_successfully_removed_from_the_team\": \"User has been successfully removed from the team\",\n \"channel_restored\": \"The channel has been restored\",\n \"settings_updated\": \"Settings updated!\",\n \"user\": \"User\",\n \"blocked\": \"has been blocked!\",\n \"password_changed\": \"Password changed!\",\n \"success\": \"Success!\",\n \"push_up_ad\": \"Successfully push up the advert: №\",\n \"bulk_push_up_ad\": \"Successfully push up adverts\",\n \"sent_verify_email\": \"Email verifying sent\",\n \"sent_reset_password\": \"Email with password reset instructions sent\",\n \"the_code_is_copied_to_the_clipboard\": \"The code is copied to the clipboard!\",\n \"widget_saved\": \"Widget saved!\"\n },\n \"warn\": {\n \"action_not_possible\": \"Action is not possible for this status!!\",\n \"application_not_installed\": \"The application is not installed.\",\n \"no_more_upload\": \"You can upload no more than \",\n \"select_tariff\": \"Please choose the tariff that suits you.\",\n \"not_possible_for_active_ads\": \"Not possible for active ads!\",\n \"submitted_for_activation_1\": \"Application submitted for activation!\",\n \"submitted_for_activation_2\": \"Applications submitted for activation!\",\n \"possible_for_active_ads\": \"It is possible only for active ads!\",\n \"submitted_for_deactivation_1\": \"Application submitted for deactivation!\",\n \"submitted_for_deactivation_2\": \"Applications submitted for deactivation!\",\n \"no_valid_status\": \"None of the selected items have a valid status!\",\n \"contain_invalid_statuses\": \"Some of the selected items contain invalid statuses, they will be ignored!\",\n \"files\": \" files.\",\n \"invalid_files_type\": \"Invalid files type\",\n \"no_cache_to_clear\": \"No cache to clear\",\n \"warn\": \"WARNING!\"\n },\n \"error\": {\n \"there_was_an_error_please_try_again\": \"There was an error, please try again\",\n \"account_olx_expired\": \"This OLX account needs to be reconnected.\",\n \"error_getting_columns\": \"Error getting columns!\",\n \"error_update_column_position\": \"Sorry, an error occurred while updating the column position!\\nPlease try again.\\nIf the problem persists, contact an administrator for help!\",\n \"error_delete_column\": \"Column deletion error!\",\n \"something_went_wrong_1\": \"Oh, something went wrong!\",\n \"empty_channel\": \"The channel cannot be empty!\",\n \"empty_funnel\": \"The name of the funnel cannot be empty!\",\n \"attached_waybill\": \"This waybill is already attached.\",\n \"error_planning\": \"Planning error!\",\n \"error_saving\": \"Error while saving!\",\n \"error_updating\": \"Update failed!\",\n \"tracked_waybill\": \"This waybill is already being tracked.\",\n \"synchronization_error\": \"Synchronization error.\",\n \"since_last_sync_error\": \"It's been a while since the last sync, please wait few seconds\",\n \"enabled_channel\": \"Disable the channel first!\",\n \"archive_error\": \"An error occurred while trying to archive!\",\n \"channel_disable_error\": \"An error occurred while trying to disable channels!\\nЗберегти переклад\\n\",\n \"deal_name_field_required\": \"The deal name field is required.\\n\\nPlease enter a name!\",\n \"contact_name_field_required\": \"The contact name field is required.\\n\\nPlease enter a name!\",\n \"email_not_expected\": \"The email address entered does not match the expected format!\\n\\nPlease check your input and try again!\",\n \"number_invalid\": \"The number entered is not valid.\\n\\nPlease check your input and try again!\",\n \"error_create_card\": \"Card creation error!\",\n \"error_create_funnel\": \"Funnel creation error!\",\n \"error_delete\": \"Deletion error!\",\n \"redo_the_ad\": \"Redo the ad from our system (You can use the \\\"Duplicate\\\" button)\",\n \"mass_activation_of_ads\": \"If your ads are not published from a business account (non-business ads), it is not recommended to mass activate ads at the same time.\",\n \"invalid_phone_number_t\": \"Invalid phone number!\",\n \"invalid_phone_number_m_1\": \"The phone number must start with 380\",\n \"invalid_phone_number_m_2\": \"The phone number is too short\",\n \"error\": \"Error!\",\n \"number_already_exists\": \"This number already exists, try another one, or contact the administrator.\",\n \"something_went_wrong_2\": \"Oops, something went wrong, try again or contact an administrator.\",\n \"empty_name\": \"The name cannot be empty!\",\n \"error_while_delete_1\": \"Error deleting!\",\n \"error_update_field_position\": \"Sorry, an error occurred while updating the position of the custom field.\\nPlease try again.\\nIf the problem persists, contact an administrator for help!\",\n \"error_getting_field\": \"Error getting custom fields!\",\n \"error_update_field\": \"Error updating custom field!\",\n \"error_delete_field\": \"Error deleting custom field!\",\n \"error_while_delete_2\": \"An error occurred while deleting!\",\n \"error_OLX_messages\": \"Error receiving OLX messages:\",\n \"file_size_error\": \"The file size is no more than 12MB.\",\n \"old_messages\": \"Scroll up to see old messages\",\n \"name_field_required\": \"The name field is required.\\n\\nPlease enter a contact name.\",\n \"phone_field_required\": \"The phone field is required.\",\n \"contact_creation_error\": \"Contact creation error!\",\n \"contact_updating_error\": \"Contact updating error!\",\n \"contact_deleting_error\": \"Contact deleting error!\",\n \"name_card_empty\": \"The name of the card cannot be empty!\",\n \"API_key_invalid\": \"The specified API key is invalid.\",\n \"max_auto_messages\": \"The maximum number of automation messages has been reached\",\n \"max_automation\": \"The maximum number of automation has been reached\",\n \"error_getting_profile\": \"It seems to be a profile fetching error.\",\n \"downloaded\": \"Downloaded!\",\n \"not_enough_space\": \"Not enough space for file: \",\n \"error_while_upload\": \"An error occurred during upload!\",\n \"error_while_download\": \"An error occurred during download!\",\n \"error_while_planner\": \"An error occurred during planning!\",\n \"error_while_open_link\": \"Error opening the link!\",\n \"error_empty_form\": \"Create input fields for the form!\",\n \"failed_to_copy_code\": \"Failed to copy code!\",\n \"unable_to_transfer_files\": \"Unable to transfer files to RIA\"\n }\n },\n \"you\": \"You\",\n \"about_deal\": \"About deal\",\n \"phase\": \"Phase\",\n \"amount_and_currency\": \"Amount and currency\",\n \"сomment\": \"Comment\",\n \"enter_price\": \"Enter price\",\n \"enter_link\": \"Enter link\",\n \"enter_text\": \"Enter text\",\n \"pip\": \"LFP\",\n \"enter_pip\": \"Enter LFP\",\n \"select_contact\": \"Select contact\",\n \"contact_not_found\": \"Contact(s) not found.\",\n \"no_one_contact\": \"There is no one contact.\",\n \"nothing_found\": \"Nothing found.\",\n \"clear_selected\": \"Clear selected\",\n \"searching\": \"Searching...\",\n \"loading\": \"Loading...\",\n \"client\": \"Client\",\n \"key_field\": \"Key field\",\n \"this_field_must_be_unique\": \"This field must be unique\",\n \"type_field\": \"Type field\",\n \"type_tooltip\": \"For information in the form of text, symbols or numbers\",\n \"alias_events\": {\n \"created\": \"Created\",\n \"updated\": \"Updated\",\n \"added\": \"Added\",\n \"removed\": \"Removed\"\n },\n \"alias\": {\n \"column\": \"stage\",\n \"column_id\": \"stage\",\n \"contact\": \"contact\",\n \"contact_id\": \"contact\",\n \"funnel\": \"funnel\",\n \"funnel_id\": \"funnel\",\n \"lead\": \"lead\",\n \"deal\": \"deal\",\n \"price\": \"price\",\n \"currency\": \"currency\",\n \"title\": \"title\",\n \"name\": \"name\",\n \"bg_color\": \"color\",\n \"text_color\": \"color\",\n \"comment\": \"comment\"\n },\n \"no_conversation\": \"The conversation didn't happen\",\n \"outgoing\": \"Outgoing\",\n \"incoming\": \"Incoming\",\n \"copy\": \"Copy\",\n \"call_status\": {\n \"answered\": \"Answered\",\n \"no_answer\": \"Didn't answer\",\n \"proper\": \"Proper\",\n \"repeated\": \"Repeated\",\n \"busy\": \"Busy\"\n },\n \"check_connect_correct_account\": \"Please check that you are connecting the correct account. In case of error, chats will be hidden...\",\n \"list_ca\": {\n \"text\": \"Text\",\n \"link\": \"Link\",\n \"textarea\": \"Textarea\",\n \"checkbox\": \"Checkbox\"\n },\n \"status_table_nova_poshta\": {\n \"registered\": \"Registered\",\n \"delivering\": \"Delivering\",\n \"delivered\": \"Delivered\",\n \"in_department\": \"In Department\",\n \"returned\": \"Returned\",\n \"returning\": \"Returning\",\n \"forwarding\": \"Forwarding\",\n \"canceled\": \"Canceled\",\n \"storage\": \"In Storage\",\n \"error\": \"Error\",\n \"completed\": \"Completed\",\n \"gotten\": \"Gotten\",\n \"not_found\": \"Not Found\",\n \"deleted\": \"Deleted\",\n \"in_draft\": \"In draft\",\n \"progress\": \"In progress\"\n },\n \"status_table_planner\": {\n \"created\": \"Created\",\n \"waiting\": \"Waiting\",\n \"fail\": \"Fail\",\n \"completed\": \"Completed\",\n \"canceled\": \"Canceled\",\n \"not_found\": \"Not Found\",\n \"draft\": \"Draft\"\n },\n \"advert_planner_type\": {\n \"activation\": \"Activation\",\n \"deactivation\": \"Deactivation\",\n \"push_up\": \"Push Up\",\n \"packet\": \"Packet\",\n \"ads\": \"ADS\",\n \"creation\": \"Creation\"\n },\n \"permissions\": {\n \"access_team\": \"Access to team data\",\n \"action_team\": \"Perform actions with a team\",\n \"access_apps\": \"Access to applications\",\n \"action_apps\": \"Perform actions with applications (Install/Uninstall)\",\n \"access_integration\": \"Access to integration data\",\n \"action_integration\": \"Perform actions with integrations\",\n \"access_crm\": \"Access to CRM data\",\n \"action_crm\": \"Perform actions with CRM\",\n \"access_contacts\": \"Access to contact data\",\n \"action_contacts\": \"Perform actions with contacts\",\n \"access_logistics\": \"Access to logistics data\",\n \"action_logistics\": \"Perform actions with logistics\",\n \"access_company\": \"Access to company data\",\n \"action_company\": \"Update company data\",\n \"access_payment\": \"Access to payment data\",\n \"action_payment\": \"Perform actions with payment\",\n \"access_channels\": \"Access to channels\",\n \"action_channels\": \"Perform actions with channels\",\n \"access_p_channels\": \"Access to your channels\",\n \"action_p_channels\": \"Perform actions with your channels\",\n \"access_settings\": \"Access to settings\",\n \"action_settings\": \"Perform actions with settings\",\n \"access_files\": \"Access to files\",\n \"action_files\": \"Perform actions with files\",\n \"access_chats\": \"Access to chats\",\n \"action_chats\": \"Perform actions with chats\"\n },\n \"permissions_types\": {\n \"crm\": \"CRM\",\n \"team\": \"Team\",\n \"channels\": \"Channels\",\n \"logistics\": \"Logistics\",\n \"integration\": \"Integration\",\n \"chats\": \"Chats\",\n \"apps\": \"Apps\",\n \"files\": \"Files\",\n \"contacts\": \"Contacts\",\n \"company\": \"Company\",\n \"payments\": \"Payments\",\n \"settings\": \"Settings\"\n },\n \"ai_integration\": {\n \"number_of_tokens\": \"Number of tokens\",\n \"text_1\": \"Integration with our AI is quick and easy.\",\n \"text_2\": \"Simply purchase the required number of tokens and start using all the possibilities of artificial intelligence today.\",\n \"h4_1\": \"Integration with channels\",\n \"placeholder_1\": \"Enter settings for AI\",\n \"label_1\": \"Max. response tokens\",\n \"label_2\": \"Channels\",\n \"placeholder_2\": \"Channel\",\n \"h4_2\": \"Request history\"\n },\n \"dialogs\": {\n \"add_category\": \"Add category\",\n \"update_category\": \"Update category\",\n \"add_product\": \"Add product\",\n \"update_product\": \"Update product\",\n \"delivery_choice\": \"Are you sure you want to create another one?\",\n \"create_group\": \"Are you sure you want to delete the role?\",\n \"tab_groups\": \"Are you sure you want to delete the role?\",\n \"tab_managers_1\": \"Are you sure you want to remove the user from the team?\",\n \"tab_managers_2\": \"Are you sure you want to remove the user(s) from the team?\",\n \"deal_1\": \"Are you sure you want to change the funnel?\",\n \"integration-olx_1\": \"Are you sure you want to disable your account?\\nIn this case, old chats with the client will be unavailable.\",\n \"tab-funnel-columns\": \"Are you sure you want to delete the column?\",\n \"integration-ringostat\": \"Are you sure you want to delete the API key?\",\n \"deals_1\": \"Are you sure you want to delete the card?\",\n \"integration-telegram-bot\": \"Are you sure you want to delete the token?\",\n \"integration-telegram\": \"Are you sure you want to delete account?\",\n \"update-manager\": \"Are you sure you want to remove the user from the team?\",\n \"channel_1\": \"Are you sure you want to archive?\",\n \"channel_2\": \"Are you sure you want to mute the selected channels?\\nIn this case, old customer chats will be unavailable.\",\n \"contact\": \"Are you sure you want to delete the contact?\",\n \"my-adverts\": \"Are you sure you want to remove the ad?\",\n \"my-planners\": \"Are you sure you want to cancel planning?\",\n \"contacts\": \"Are you sure you want to delete the contact?\",\n \"header\": \"Are you sure you want to activate trial mode?\",\n \"chats-settings\": \"Are you sure you want to delete a quick reply?\",\n \"deal_2\": \"Are you sure you want to delete the contact for this deal?\",\n \"tab-deals-settings-fields\": \"Are you sure you want to delete the custom field?\",\n \"tab-deals-settings-common\": \"Are you sure you want to delete the funnel?\",\n \"integration-olx_2\": \"Are you sure you want to delete the channel?\",\n \"ttn_delete\": \"Are you sure you want to delete the waybill?\",\n \"category_delete\": \"Are you sure you want to delete the category?\",\n \"product_delete\": \"Are you sure you want to delete the product?\",\n \"to_draft\": \"Are you sure you want change status on draft?\",\n \"funnel_filter\": \"Are you sure you want to remove the filter?\",\n \"push_up_ad\": \"Are you sure you want to push up the advert?\",\n \"pay_ad\": \"Are you sure you want to pay for the advert?\",\n \"buy_package\": \"Are you sure you want to buy a package?\",\n \"reset_changes\": \"Are you sure you want to reset the changes?\",\n \"delete_site\": \"Are you sure you want to delete the site?\",\n \"add_site\": \"Add site\",\n \"update_site\": \"Update site\"\n },\n \"try_demo\": \"Try Demo\",\n \"dimensional_weight\": \"The volumetric weight is calculated by the formula (Length × Width × Height, cm) / 4000 and compared with the actual weight. The larger indicator is used in calculations of the cost of transportation.\",\n \"shipment_parameters\": \"The shipment parameters do not meet the selection conditions: Weight (volumetric and actual) from 0.1 to 30 kg (inclusive); Sides: Length, Width, Height <= 120 cm\",\n \"check_shipment\": \"Check if this shipment (location): is not packed in a box or corrugated cardboard with a flat base and is not a flat package with clothes; is sent and delivered from/to cargo offices, address or point of acceptance and delivery\",\n \"1_month\": \"1 month\",\n \"3_months\": \"3 months\",\n \"automation_action\": {\n \"trigger\": \"Trigger at\",\n \"get_phone\": \"Get phone\",\n \"get_email\": \"Get mail\",\n \"action\": \"Action\",\n \"choose_field\": \"Choose field\",\n \"choose_column\": \"Choose column\",\n \"get_first_message\": \"Get first message\",\n \"first_message\": \"To the first message\",\n \"move_lead\": \"Move lead\",\n \"move_deal\": \"Move deal\",\n \"send_message\": \"Send message\",\n \"generate_ai\": \"Generate AI reply\",\n \"enter_name_automation\": \"Enter the name automation\",\n \"enter_prompt\": \"Enter AI prompt\",\n \"get_only_first_message\": \"Get only first message\",\n \"get_all_message_except_first\": \"Get all message except first\",\n \"get_all_message\": \"Get all message\",\n \"get_manager_reply\": \"Get manager reply\",\n \"get_ai_reply\": \"Get AI reply\"\n },\n \"for\": \"for\",\n \"generate_invoice\": \"Generate invoice\",\n \"product\": \"Product\",\n \"to_be_paid\": \"To be paid\",\n \"total\": \"Total\",\n \"discount\": \"Discount\",\n \"field_name\": \"Name\",\n \"last_file_update\": \"Last update\",\n \"file_created_by\": \"Created by\",\n \"file_size\": \"File size\",\n \"upload_new\": \"New\",\n \"snack_bar\": {\n \"upload\": \"Upload\",\n \"download\": \"Download\",\n \"in_progress\": \"In Progress - {count}\",\n \"completed\": \"Completed - {count}\",\n \"failed\": \" ({count} with errors)\"\n },\n \"paste\": \"Paste\",\n \"step\": \"Step\",\n \"you_need_to_pay_the_tariff_before\": \"You need to pay the tariff before\",\n \"change_advert\": \"Change Advert\",\n \"create_advert\": \"Create Advert\",\n \"total_size_text_files_and_documents\": \"Text files and documents\",\n \"total_size_images\": \"Images\",\n \"total_size_audio\": \"Audio\",\n \"total_size_video\": \"Video\",\n \"total_size_outherFiles\": \"Outher files\",\n \"size_available\": \"Available size\",\n \"size_used\": \"Used\",\n \"size_of\": \"of\",\n \"time_zone\": \"time zone\",\n \"CompanySettings\": \"CompanySettings\",\n \"from\": \"from\",\n \"to\": \"to\",\n \"work\": \"Work\",\n \"chose_days\": \"Chose days\",\n \"sunday\": \"sunday\",\n \"monday\": \"monday\",\n \"tuesday\": \"tuesday\",\n \"wednesday\": \"wednesday\",\n \"thursday\": \"thursday\",\n \"friday\": \"friday\",\n \"saturday\": \"saturday\",\n \"automation_work\": \"Automation will work\",\n \"mon\": \"mon\",\n \"tue\": \"tue\",\n \"wed\": \"wed\",\n \"thu\": \"thu\",\n \"fri\": \"fri\",\n \"sat\": \"sat\",\n \"sun\": \"sun\",\n \"all_week\": \"All week\",\n \"days\": {\n \"TUESDAY\": \"Tuesday\",\n \"THURSDAY\": \"Thursday\",\n \"SATURDAY\": \"Saturday\",\n \"WEDNESDAY\": \"Wednesday\",\n \"FRIDAY\": \"Friday\",\n \"MONDAY\": \"Monday\",\n \"SUNDAY\": \"Sunday\"\n },\n \"work_all_day\": \"Work all day\",\n \"on\": \"On\",\n \"off\": \"Off\",\n \"delete_automation\": \"Are you sure you want to delete this automation?\",\n \"sync_advert\": \"Synchronize advert\",\n \"olx_chats\": \"Olx chats\",\n \"autoria_chats\": \"Autoria chats\",\n \"autoria_chats_old\": \"Autoria old chats\",\n \"telegram_bot_chats\": \"Telegram BOT chats\",\n \"history_version\": \"History version\",\n \"version\": \"Version\",\n \"release\": \"Release\",\n \"contact_the_administrator\": \"Contact the administrator\",\n \"error_while_download\": \"An error occurred during download!\",\n \"error_while_upload\": \"An error occurred during upload!\",\n \"not_enough_space\": \"Not enough space for file!\",\n \"group\": \"Group\",\n \"your_zone\": \"Your location\",\n \"count_adverts\": \"Number adverts\",\n \"your_region\": \"Your region\",\n \"your_category\": \"Category\",\n \"planner_paid\": \"Planner packets\",\n \"free\": \"Free\",\n \"trade\": \"Trade\",\n \"negotiable\": \"Negotiable\",\n \"delivery\": \"OLX Delivery\",\n \"packets\": \"My packets\",\n \"owner\": \"Owner\",\n \"manager\": \"Manager\",\n \"all\": \"All\",\n \"all_notification_read\": \"All notification is read\",\n \"token\": \"Token\",\n \"filters\": \"Filters\",\n \"add_filter\": \"Add filter\",\n \"select_category\": \"Select category\",\n \"selected_category\": \"Selected category\",\n \"select_city_and_region\": \"Select city and district\",\n \"selected_city\": \"Selected city and district\",\n \"contact_name\": \"Contact name\",\n \"plan_packet\": \"Plan packet\",\n \"custom_filters\": {\n \"name\": \"Title\",\n \"price\": \"Price\",\n \"is_unread\": \"Unread\",\n \"id\": \"ID\"\n },\n \"add_field\": \"Add field\",\n \"default_fields\": \"Restore default fields\",\n \"filter_fields_settings\": \"Filter fields settings\",\n \"color\": \"Color\",\n \"delete_filter\": \"Delete filter\",\n \"ticket_system\": {\n \"message\": \"Message\",\n \"ticket\": \"Ticket\",\n \"new_ticket\": \"New ticket\",\n \"topic\": \"Topic\",\n \"enter_a_topic\": \"Enter a topic\",\n \"files\": \"Files\",\n \"attach_a_file\": \"Attach a file\",\n \"add_ticket\": \"Add ticket\",\n \"list_of_tickets\": \"List of tickets\",\n \"code\": \"Code\",\n \"name\": \"Name\",\n \"date\": \"Date\",\n \"status\": \"Status\",\n \"opened\": \"Opened\",\n \"closed\": \"Closed\",\n \"progress\": \"In progress\"\n },\n \"planner_create\": \"Planner create advert\",\n \"id\": \"Id\",\n \"not_found_packet\": \"Сould not find packages\",\n \"password\": \"Password\",\n \"confirm_password\": \"Confirm Password\",\n \"sign_in_account\": \"Sign in to your account\",\n \"sign_up\": \"Sign Up\",\n \"new_user\": \"New user?\",\n \"sign_in\": \"Sign In\",\n \"forgot_password\": \"Forgot your password?\",\n \"return_to_login\": \"« Return to login\",\n \"sign_up_account\": \"Sign Up\",\n \"please_enter_first_name\": \"Please enter first name!\",\n \"please_enter_last_name\": \"Please enter last name!\",\n \"please_enter_phone\": \"Please enter phone!\",\n \"wrong_phone_start\": \"The phone number must start with 380\",\n \"please_enter_valid_phone\": \"Будь ласка, вкажіть дійсний номер телефона!\",\n \"please_enter_email\": \"Please enter email!\",\n \"please_enter_valid_email\": \"Please enter a valid email!\",\n \"please_enter_password\": \"Please enter password!\",\n \"please_enter_password_confirmation\": \"Please enter password again!\",\n \"passwords_do_not_match\": \"Passwords do not match\",\n \"password_min_length\": \"Password length must be at least\",\n \"characters\": \"characters\",\n \"verifying_email\": {\n \"title\": \"E-mail address confirmation\",\n \"text_1\": \"You need to confirm your email to activate your account.\",\n \"text_2\": \"An email with instructions to verify your email address has been sent to your address\",\n \"text_3\": \"Didn't receive an email?\",\n \"text_4\": \"Check your SPAM folder or call +380(98)-575-98-51.\",\n \"text_5\": \"Click here\",\n \"text_6\": \"to resend the email.\"\n },\n \"reset_password\": {\n \"title\": \"Forgot Your Password?\",\n \"btn\": \"Submit\"\n },\n \"add_real_photos\": \"Add real photos of the product to your advert...\",\n \"add_photo\": \"Add a photo\",\n \"select_another_city\": \"Select Another City\",\n \"main\": \"Main\",\n \"select_city\": \"Select City\",\n \"select_region\": \"Select Region\",\n \"buy_tokens\": \"Buy tokens\",\n \"sites\": \"Sites\",\n \"site_statuses\": {\n \"published\": \"Published\",\n \"archived\": \"Archived\",\n \"not_published\": \"Not published\",\n \"not_paid\": \"Not paid\"\n },\n \"name_site_cannot_be_empty\": \"Name of the site cannot be empty\",\n \"name_of_product\": \"Name of product\",\n \"sell_product\": \"Sell product\",\n \"subdomain_site_cannot_be_empty\": \"Subdomain of the site cannot be empty\",\n \"subdomain\": \"Subdomain\",\n \"subdomain_min_length\": \"Enter at least 3 characters\",\n \"autosave\": \"Autosave\",\n \"builder\": \"Builder\",\n \"order_site\": \"Order site\",\n \"help_with_site\": \"Do you need help with the site?\",\n \"offer_an_exchange\": \"I offer an exchange\",\n \"offer_a_trade\": \"I offer a trade for\",\n \"with_your_surcharge\": \"With your surcharge\",\n \"with_my_surcharge\": \"With my surcharge\",\n \"offer_accepted\": \"Offer accepted\",\n \"offer_rejected\": \"Offer rejected\",\n \"accept\": \"Accept\",\n \"reject\": \"Reject\",\n \"channels_type\": {\n \"olx\": \"OLX\",\n \"ria\": \"RIA\",\n \"telegram_bot\": \"Telegram BOT\",\n \"telegram\": \"Telegram Number\",\n \"facebook\": \"Facebook\",\n \"instagram\": \"Instagram\",\n \"viber\": \"Viber\",\n \"whatsapp\": \"WhatsApp\",\n \"widget\": \"Widget\"\n },\n \"chats_sidebar_menu\": {\n \"external\": \"External chats\",\n \"olx\": \"OLX chats\",\n \"autoria\": \"Autoria chats\",\n \"autoria_old\": \"Autoria old chats\",\n \"telegram_bot\": \"Telegram BOT chats\",\n \"telegram_user\": \"Telegram chats\",\n \"settings\": \"Settings\"\n },\n \"about_chat\": \"About this chat\",\n \"check\": \"Check\",\n \"translate\": \"Translate\",\n \"yesterday\": \"Yesterday\",\n \"today\": \"Today\",\n \"sorting\": \"Sorting\",\n \"tabs\": {\n \"chats\": \"Chats\",\n \"opened\": \"Opened\",\n \"closed\": \"Closed\"\n },\n \"sort\": {\n \"sort\": \"Sort\",\n \"new\": \"New\",\n \"unread\": \"Unread\"\n },\n \"mute\": \"Mute\",\n \"unmute\": \"Unmute\",\n \"update_chats\": \"Update chats\",\n \"select_chat\": \"Select a chat to start communicating\",\n \"no_messages\": \"No messages\",\n \"unblock\": \"Unblock\",\n \"share\": \"Share\",\n \"unshare\": \"Unshare\",\n \"no_results\": \"There were no results for «{{searchText}}». Try a new search.\",\n \"pinned_header\": \"Pinned: {{count}} messages\",\n \"card\": \"Card\",\n \"chat_settings\": {\n \"general_settings\": \"General settings\",\n \"keyboard\": \"Keyboard\",\n \"blocked_users\": \"Blocked users\",\n \"enter_send\": \"Enter - send\",\n \"cmd_enter_send\": \"⌘ + Enter – send\",\n \"ctrl_enter_send\": \"Ctrl + Enter - send\",\n \"shift_enter_send\": \"Shift + Enter - send\",\n \"new_line_enter\": \"New line - Enter\",\n \"new_line_shift_enter\": \"New line - Shift + Enter\",\n \"notifications\": \"Notifications\",\n \"mark_as_read\": \"Mark messages as read upon opening\",\n \"enabled\": \"Enabled\",\n \"disabled\": \"Disabled\",\n \"no_blocked_users\": \"No blocked users yet\",\n \"no_replies\": \"No quick answers yet\",\n \"fast_replies\": \"Fast replies\",\n \"add_fast_replies\": \"Add fast replies\",\n \"saved_fast_replies\": \"Saved fast replies\",\n \"other\": \"Other\",\n \"lazy_loading\": \"Automatic page loading (lazy loading)\"\n },\n \"file\": \"File\",\n \"fast_reply\": \"Fast reply\",\n \"auto_reply\": \"Auto reply\",\n \"api_key\": \"API key\",\n \"user_id\": \"User ID\",\n \"channel_id_for_webhook_ria\": \"Channel ID for webhook RIA\",\n \"pass_this_indicator_related\": \"Pass this indicator as related_user_id to webhooks for RIA\",\n \"pass_this_indicator_webhook\": \"Pass this indicator as webhook_url to webhooks for RIA\",\n \"you_have_uploaded\": \"You have uploaded\",\n \"files\": \"files\",\n \"code\": \"Code\",\n \"next\": \"Next\",\n \"login_with_phone\": \"Login with phone number\",\n \"login_with_qr\": \"Login with QR code\",\n \"widgets\": \"Widgets\",\n \"edit_widget\": \"Edit widget\",\n \"widgets_list\": \"Widgets list\",\n \"choose_company\": \"Choose company\",\n \"spaces\": {\n \"shared\": \"Shared files\",\n \"private\": \"Private files\",\n \"cart\": \"Cart\"\n },\n \"change_display_type\": \"Change display type\",\n \"time\": {\n \"three\": \"3s\",\n \"five\": \"5s\",\n \"ten\": \"10s\",\n \"fifteen\": \"15s\",\n \"thirty\": \"30s\"\n },\n \"text_input\": \"Text input\",\n \"textarea\": \"Comment\",\n \"embed_code\": \"Embed code\",\n \"widget_name\": \"The name of the widget\",\n \"general_appearance\": \"General appearance\",\n \"background_color\": \"Background color\",\n \"icon_color\": \"Icon color\",\n \"choose_position\": \"Choose position\",\n \"show_time\": \"Show time\",\n \"immediately\": \"immediately after loading the page\",\n \"delay\": \"with a delay\",\n \"form\": \"Form\",\n \"placeholder\": \"Placeholder\",\n \"required\": \"Required\",\n \"mobile_devices\": \"Mobile devices\",\n \"do_not_show\": \"do not show on mobile devices\",\n \"created_in_jecrm\": \"Created in JECRM\",\n \"show_caption\": \"show caption\",\n \"copy_to_clipboard\": \"Copy to clipboard\",\n \"input_name\": \"Input name\",\n \"seconds_short\": \"sec.\",\n \"duration\": \"Duration\",\n \"attachments\": {\n \"image\": \"Image\",\n \"video\": \"Video\",\n \"gif\": \"Gif\",\n \"audio\": \"Audio\",\n \"voice\": \"Voice\",\n \"round\": \"Round\",\n \"sticker\": \"Sticker\",\n \"file\": \"File\",\n \"document\": \"File\"\n },\n \"plan_applied_ads\": \"You have selected advertising packages that are also created for such messages: {{ advertsWithExpTime }}. Do you want to schedule a purchase yet?\",\n \"download_as\": \"Download as...\",\n \"download_all\": \"Download all\",\n \"clear_media_cache\": \"Clear media cache\",\n \"bot\": \"Bot\"\n}\n","//! moment.js\n//! version : 2.30.1\n//! authors : Tim Wood, Iskren Chernev, Moment.js contributors\n//! license : MIT\n//! momentjs.com\n\n;\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.moment = factory();\n})(this, function () {\n 'use strict';\n\n var hookCallback;\n function hooks() {\n return hookCallback.apply(null, arguments);\n }\n\n // This is done to register the method called with moment()\n // without creating circular dependencies.\n function setHookCallback(callback) {\n hookCallback = callback;\n }\n function isArray(input) {\n return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';\n }\n function isObject(input) {\n // IE8 will treat undefined and null as object if it wasn't for\n // input != null\n return input != null && Object.prototype.toString.call(input) === '[object Object]';\n }\n function hasOwnProp(a, b) {\n return Object.prototype.hasOwnProperty.call(a, b);\n }\n function isObjectEmpty(obj) {\n if (Object.getOwnPropertyNames) {\n return Object.getOwnPropertyNames(obj).length === 0;\n } else {\n var k;\n for (k in obj) {\n if (hasOwnProp(obj, k)) {\n return false;\n }\n }\n return true;\n }\n }\n function isUndefined(input) {\n return input === void 0;\n }\n function isNumber(input) {\n return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';\n }\n function isDate(input) {\n return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';\n }\n function map(arr, fn) {\n var res = [],\n i,\n arrLen = arr.length;\n for (i = 0; i < arrLen; ++i) {\n res.push(fn(arr[i], i));\n }\n return res;\n }\n function extend(a, b) {\n for (var i in b) {\n if (hasOwnProp(b, i)) {\n a[i] = b[i];\n }\n }\n if (hasOwnProp(b, 'toString')) {\n a.toString = b.toString;\n }\n if (hasOwnProp(b, 'valueOf')) {\n a.valueOf = b.valueOf;\n }\n return a;\n }\n function createUTC(input, format, locale, strict) {\n return createLocalOrUTC(input, format, locale, strict, true).utc();\n }\n function defaultParsingFlags() {\n // We need to deep clone this object.\n return {\n empty: false,\n unusedTokens: [],\n unusedInput: [],\n overflow: -2,\n charsLeftOver: 0,\n nullInput: false,\n invalidEra: null,\n invalidMonth: null,\n invalidFormat: false,\n userInvalidated: false,\n iso: false,\n parsedDateParts: [],\n era: null,\n meridiem: null,\n rfc2822: false,\n weekdayMismatch: false\n };\n }\n function getParsingFlags(m) {\n if (m._pf == null) {\n m._pf = defaultParsingFlags();\n }\n return m._pf;\n }\n var some;\n if (Array.prototype.some) {\n some = Array.prototype.some;\n } else {\n some = function (fun) {\n var t = Object(this),\n len = t.length >>> 0,\n i;\n for (i = 0; i < len; i++) {\n if (i in t && fun.call(this, t[i], i, t)) {\n return true;\n }\n }\n return false;\n };\n }\n function isValid(m) {\n var flags = null,\n parsedParts = false,\n isNowValid = m._d && !isNaN(m._d.getTime());\n if (isNowValid) {\n flags = getParsingFlags(m);\n parsedParts = some.call(flags.parsedDateParts, function (i) {\n return i != null;\n });\n isNowValid = flags.overflow < 0 && !flags.empty && !flags.invalidEra && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || flags.meridiem && parsedParts);\n if (m._strict) {\n isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined;\n }\n }\n if (Object.isFrozen == null || !Object.isFrozen(m)) {\n m._isValid = isNowValid;\n } else {\n return isNowValid;\n }\n return m._isValid;\n }\n function createInvalid(flags) {\n var m = createUTC(NaN);\n if (flags != null) {\n extend(getParsingFlags(m), flags);\n } else {\n getParsingFlags(m).userInvalidated = true;\n }\n return m;\n }\n\n // Plugins that add properties should also add the key here (null value),\n // so we can properly clone ourselves.\n var momentProperties = hooks.momentProperties = [],\n updateInProgress = false;\n function copyConfig(to, from) {\n var i,\n prop,\n val,\n momentPropertiesLen = momentProperties.length;\n if (!isUndefined(from._isAMomentObject)) {\n to._isAMomentObject = from._isAMomentObject;\n }\n if (!isUndefined(from._i)) {\n to._i = from._i;\n }\n if (!isUndefined(from._f)) {\n to._f = from._f;\n }\n if (!isUndefined(from._l)) {\n to._l = from._l;\n }\n if (!isUndefined(from._strict)) {\n to._strict = from._strict;\n }\n if (!isUndefined(from._tzm)) {\n to._tzm = from._tzm;\n }\n if (!isUndefined(from._isUTC)) {\n to._isUTC = from._isUTC;\n }\n if (!isUndefined(from._offset)) {\n to._offset = from._offset;\n }\n if (!isUndefined(from._pf)) {\n to._pf = getParsingFlags(from);\n }\n if (!isUndefined(from._locale)) {\n to._locale = from._locale;\n }\n if (momentPropertiesLen > 0) {\n for (i = 0; i < momentPropertiesLen; i++) {\n prop = momentProperties[i];\n val = from[prop];\n if (!isUndefined(val)) {\n to[prop] = val;\n }\n }\n }\n return to;\n }\n\n // Moment prototype object\n function Moment(config) {\n copyConfig(this, config);\n this._d = new Date(config._d != null ? config._d.getTime() : NaN);\n if (!this.isValid()) {\n this._d = new Date(NaN);\n }\n // Prevent infinite loop in case updateOffset creates new moment\n // objects.\n if (updateInProgress === false) {\n updateInProgress = true;\n hooks.updateOffset(this);\n updateInProgress = false;\n }\n }\n function isMoment(obj) {\n return obj instanceof Moment || obj != null && obj._isAMomentObject != null;\n }\n function warn(msg) {\n if (hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) {\n console.warn('Deprecation warning: ' + msg);\n }\n }\n function deprecate(msg, fn) {\n var firstTime = true;\n return extend(function () {\n if (hooks.deprecationHandler != null) {\n hooks.deprecationHandler(null, msg);\n }\n if (firstTime) {\n var args = [],\n arg,\n i,\n key,\n argLen = arguments.length;\n for (i = 0; i < argLen; i++) {\n arg = '';\n if (typeof arguments[i] === 'object') {\n arg += '\\n[' + i + '] ';\n for (key in arguments[0]) {\n if (hasOwnProp(arguments[0], key)) {\n arg += key + ': ' + arguments[0][key] + ', ';\n }\n }\n arg = arg.slice(0, -2); // Remove trailing comma and space\n } else {\n arg = arguments[i];\n }\n args.push(arg);\n }\n warn(msg + '\\nArguments: ' + Array.prototype.slice.call(args).join('') + '\\n' + new Error().stack);\n firstTime = false;\n }\n return fn.apply(this, arguments);\n }, fn);\n }\n var deprecations = {};\n function deprecateSimple(name, msg) {\n if (hooks.deprecationHandler != null) {\n hooks.deprecationHandler(name, msg);\n }\n if (!deprecations[name]) {\n warn(msg);\n deprecations[name] = true;\n }\n }\n hooks.suppressDeprecationWarnings = false;\n hooks.deprecationHandler = null;\n function isFunction(input) {\n return typeof Function !== 'undefined' && input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';\n }\n function set(config) {\n var prop, i;\n for (i in config) {\n if (hasOwnProp(config, i)) {\n prop = config[i];\n if (isFunction(prop)) {\n this[i] = prop;\n } else {\n this['_' + i] = prop;\n }\n }\n }\n this._config = config;\n // Lenient ordinal parsing accepts just a number in addition to\n // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.\n // TODO: Remove \"ordinalParse\" fallback in next major release.\n this._dayOfMonthOrdinalParseLenient = new RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + /\\d{1,2}/.source);\n }\n function mergeConfigs(parentConfig, childConfig) {\n var res = extend({}, parentConfig),\n prop;\n for (prop in childConfig) {\n if (hasOwnProp(childConfig, prop)) {\n if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {\n res[prop] = {};\n extend(res[prop], parentConfig[prop]);\n extend(res[prop], childConfig[prop]);\n } else if (childConfig[prop] != null) {\n res[prop] = childConfig[prop];\n } else {\n delete res[prop];\n }\n }\n }\n for (prop in parentConfig) {\n if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) {\n // make sure changes to properties don't modify parent config\n res[prop] = extend({}, res[prop]);\n }\n }\n return res;\n }\n function Locale(config) {\n if (config != null) {\n this.set(config);\n }\n }\n var keys;\n if (Object.keys) {\n keys = Object.keys;\n } else {\n keys = function (obj) {\n var i,\n res = [];\n for (i in obj) {\n if (hasOwnProp(obj, i)) {\n res.push(i);\n }\n }\n return res;\n };\n }\n var defaultCalendar = {\n sameDay: '[Today at] LT',\n nextDay: '[Tomorrow at] LT',\n nextWeek: 'dddd [at] LT',\n lastDay: '[Yesterday at] LT',\n lastWeek: '[Last] dddd [at] LT',\n sameElse: 'L'\n };\n function calendar(key, mom, now) {\n var output = this._calendar[key] || this._calendar['sameElse'];\n return isFunction(output) ? output.call(mom, now) : output;\n }\n function zeroFill(number, targetLength, forceSign) {\n var absNumber = '' + Math.abs(number),\n zerosToFill = targetLength - absNumber.length,\n sign = number >= 0;\n return (sign ? forceSign ? '+' : '' : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;\n }\n var formattingTokens = /(\\[[^\\[]*\\])|(\\\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,\n localFormattingTokens = /(\\[[^\\[]*\\])|(\\\\)?(LTS|LT|LL?L?L?|l{1,4})/g,\n formatFunctions = {},\n formatTokenFunctions = {};\n\n // token: 'M'\n // padded: ['MM', 2]\n // ordinal: 'Mo'\n // callback: function () { this.month() + 1 }\n function addFormatToken(token, padded, ordinal, callback) {\n var func = callback;\n if (typeof callback === 'string') {\n func = function () {\n return this[callback]();\n };\n }\n if (token) {\n formatTokenFunctions[token] = func;\n }\n if (padded) {\n formatTokenFunctions[padded[0]] = function () {\n return zeroFill(func.apply(this, arguments), padded[1], padded[2]);\n };\n }\n if (ordinal) {\n formatTokenFunctions[ordinal] = function () {\n return this.localeData().ordinal(func.apply(this, arguments), token);\n };\n }\n }\n function removeFormattingTokens(input) {\n if (input.match(/\\[[\\s\\S]/)) {\n return input.replace(/^\\[|\\]$/g, '');\n }\n return input.replace(/\\\\/g, '');\n }\n function makeFormatFunction(format) {\n var array = format.match(formattingTokens),\n i,\n length;\n for (i = 0, length = array.length; i < length; i++) {\n if (formatTokenFunctions[array[i]]) {\n array[i] = formatTokenFunctions[array[i]];\n } else {\n array[i] = removeFormattingTokens(array[i]);\n }\n }\n return function (mom) {\n var output = '',\n i;\n for (i = 0; i < length; i++) {\n output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];\n }\n return output;\n };\n }\n\n // format date using native date object\n function formatMoment(m, format) {\n if (!m.isValid()) {\n return m.localeData().invalidDate();\n }\n format = expandFormat(format, m.localeData());\n formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);\n return formatFunctions[format](m);\n }\n function expandFormat(format, locale) {\n var i = 5;\n function replaceLongDateFormatTokens(input) {\n return locale.longDateFormat(input) || input;\n }\n localFormattingTokens.lastIndex = 0;\n while (i >= 0 && localFormattingTokens.test(format)) {\n format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);\n localFormattingTokens.lastIndex = 0;\n i -= 1;\n }\n return format;\n }\n var defaultLongDateFormat = {\n LTS: 'h:mm:ss A',\n LT: 'h:mm A',\n L: 'MM/DD/YYYY',\n LL: 'MMMM D, YYYY',\n LLL: 'MMMM D, YYYY h:mm A',\n LLLL: 'dddd, MMMM D, YYYY h:mm A'\n };\n function longDateFormat(key) {\n var format = this._longDateFormat[key],\n formatUpper = this._longDateFormat[key.toUpperCase()];\n if (format || !formatUpper) {\n return format;\n }\n this._longDateFormat[key] = formatUpper.match(formattingTokens).map(function (tok) {\n if (tok === 'MMMM' || tok === 'MM' || tok === 'DD' || tok === 'dddd') {\n return tok.slice(1);\n }\n return tok;\n }).join('');\n return this._longDateFormat[key];\n }\n var defaultInvalidDate = 'Invalid date';\n function invalidDate() {\n return this._invalidDate;\n }\n var defaultOrdinal = '%d',\n defaultDayOfMonthOrdinalParse = /\\d{1,2}/;\n function ordinal(number) {\n return this._ordinal.replace('%d', number);\n }\n var defaultRelativeTime = {\n future: 'in %s',\n past: '%s ago',\n s: 'a few seconds',\n ss: '%d seconds',\n m: 'a minute',\n mm: '%d minutes',\n h: 'an hour',\n hh: '%d hours',\n d: 'a day',\n dd: '%d days',\n w: 'a week',\n ww: '%d weeks',\n M: 'a month',\n MM: '%d months',\n y: 'a year',\n yy: '%d years'\n };\n function relativeTime(number, withoutSuffix, string, isFuture) {\n var output = this._relativeTime[string];\n return isFunction(output) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number);\n }\n function pastFuture(diff, output) {\n var format = this._relativeTime[diff > 0 ? 'future' : 'past'];\n return isFunction(format) ? format(output) : format.replace(/%s/i, output);\n }\n var aliases = {\n D: 'date',\n dates: 'date',\n date: 'date',\n d: 'day',\n days: 'day',\n day: 'day',\n e: 'weekday',\n weekdays: 'weekday',\n weekday: 'weekday',\n E: 'isoWeekday',\n isoweekdays: 'isoWeekday',\n isoweekday: 'isoWeekday',\n DDD: 'dayOfYear',\n dayofyears: 'dayOfYear',\n dayofyear: 'dayOfYear',\n h: 'hour',\n hours: 'hour',\n hour: 'hour',\n ms: 'millisecond',\n milliseconds: 'millisecond',\n millisecond: 'millisecond',\n m: 'minute',\n minutes: 'minute',\n minute: 'minute',\n M: 'month',\n months: 'month',\n month: 'month',\n Q: 'quarter',\n quarters: 'quarter',\n quarter: 'quarter',\n s: 'second',\n seconds: 'second',\n second: 'second',\n gg: 'weekYear',\n weekyears: 'weekYear',\n weekyear: 'weekYear',\n GG: 'isoWeekYear',\n isoweekyears: 'isoWeekYear',\n isoweekyear: 'isoWeekYear',\n w: 'week',\n weeks: 'week',\n week: 'week',\n W: 'isoWeek',\n isoweeks: 'isoWeek',\n isoweek: 'isoWeek',\n y: 'year',\n years: 'year',\n year: 'year'\n };\n function normalizeUnits(units) {\n return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;\n }\n function normalizeObjectUnits(inputObject) {\n var normalizedInput = {},\n normalizedProp,\n prop;\n for (prop in inputObject) {\n if (hasOwnProp(inputObject, prop)) {\n normalizedProp = normalizeUnits(prop);\n if (normalizedProp) {\n normalizedInput[normalizedProp] = inputObject[prop];\n }\n }\n }\n return normalizedInput;\n }\n var priorities = {\n date: 9,\n day: 11,\n weekday: 11,\n isoWeekday: 11,\n dayOfYear: 4,\n hour: 13,\n millisecond: 16,\n minute: 14,\n month: 8,\n quarter: 7,\n second: 15,\n weekYear: 1,\n isoWeekYear: 1,\n week: 5,\n isoWeek: 5,\n year: 1\n };\n function getPrioritizedUnits(unitsObj) {\n var units = [],\n u;\n for (u in unitsObj) {\n if (hasOwnProp(unitsObj, u)) {\n units.push({\n unit: u,\n priority: priorities[u]\n });\n }\n }\n units.sort(function (a, b) {\n return a.priority - b.priority;\n });\n return units;\n }\n var match1 = /\\d/,\n // 0 - 9\n match2 = /\\d\\d/,\n // 00 - 99\n match3 = /\\d{3}/,\n // 000 - 999\n match4 = /\\d{4}/,\n // 0000 - 9999\n match6 = /[+-]?\\d{6}/,\n // -999999 - 999999\n match1to2 = /\\d\\d?/,\n // 0 - 99\n match3to4 = /\\d\\d\\d\\d?/,\n // 999 - 9999\n match5to6 = /\\d\\d\\d\\d\\d\\d?/,\n // 99999 - 999999\n match1to3 = /\\d{1,3}/,\n // 0 - 999\n match1to4 = /\\d{1,4}/,\n // 0 - 9999\n match1to6 = /[+-]?\\d{1,6}/,\n // -999999 - 999999\n matchUnsigned = /\\d+/,\n // 0 - inf\n matchSigned = /[+-]?\\d+/,\n // -inf - inf\n matchOffset = /Z|[+-]\\d\\d:?\\d\\d/gi,\n // +00:00 -00:00 +0000 -0000 or Z\n matchShortOffset = /Z|[+-]\\d\\d(?::?\\d\\d)?/gi,\n // +00 -00 +00:00 -00:00 +0000 -0000 or Z\n matchTimestamp = /[+-]?\\d+(\\.\\d{1,3})?/,\n // 123456789 123456789.123\n // any word (or two) characters or numbers including two/three word month in arabic.\n // includes scottish gaelic two word and hyphenated months\n matchWord = /[0-9]{0,256}['a-z\\u00A0-\\u05FF\\u0700-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFF07\\uFF10-\\uFFEF]{1,256}|[\\u0600-\\u06FF\\/]{1,256}(\\s*?[\\u0600-\\u06FF]{1,256}){1,2}/i,\n match1to2NoLeadingZero = /^[1-9]\\d?/,\n // 1-99\n match1to2HasZero = /^([1-9]\\d|\\d)/,\n // 0-99\n regexes;\n regexes = {};\n function addRegexToken(token, regex, strictRegex) {\n regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {\n return isStrict && strictRegex ? strictRegex : regex;\n };\n }\n function getParseRegexForToken(token, config) {\n if (!hasOwnProp(regexes, token)) {\n return new RegExp(unescapeFormat(token));\n }\n return regexes[token](config._strict, config._locale);\n }\n\n // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript\n function unescapeFormat(s) {\n return regexEscape(s.replace('\\\\', '').replace(/\\\\(\\[)|\\\\(\\])|\\[([^\\]\\[]*)\\]|\\\\(.)/g, function (matched, p1, p2, p3, p4) {\n return p1 || p2 || p3 || p4;\n }));\n }\n function regexEscape(s) {\n return s.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n }\n function absFloor(number) {\n if (number < 0) {\n // -0 -> 0\n return Math.ceil(number) || 0;\n } else {\n return Math.floor(number);\n }\n }\n function toInt(argumentForCoercion) {\n var coercedNumber = +argumentForCoercion,\n value = 0;\n if (coercedNumber !== 0 && isFinite(coercedNumber)) {\n value = absFloor(coercedNumber);\n }\n return value;\n }\n var tokens = {};\n function addParseToken(token, callback) {\n var i,\n func = callback,\n tokenLen;\n if (typeof token === 'string') {\n token = [token];\n }\n if (isNumber(callback)) {\n func = function (input, array) {\n array[callback] = toInt(input);\n };\n }\n tokenLen = token.length;\n for (i = 0; i < tokenLen; i++) {\n tokens[token[i]] = func;\n }\n }\n function addWeekParseToken(token, callback) {\n addParseToken(token, function (input, array, config, token) {\n config._w = config._w || {};\n callback(input, config._w, config, token);\n });\n }\n function addTimeToArrayFromToken(token, input, config) {\n if (input != null && hasOwnProp(tokens, token)) {\n tokens[token](input, config._a, config, token);\n }\n }\n function isLeapYear(year) {\n return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;\n }\n var YEAR = 0,\n MONTH = 1,\n DATE = 2,\n HOUR = 3,\n MINUTE = 4,\n SECOND = 5,\n MILLISECOND = 6,\n WEEK = 7,\n WEEKDAY = 8;\n\n // FORMATTING\n\n addFormatToken('Y', 0, 0, function () {\n var y = this.year();\n return y <= 9999 ? zeroFill(y, 4) : '+' + y;\n });\n addFormatToken(0, ['YY', 2], 0, function () {\n return this.year() % 100;\n });\n addFormatToken(0, ['YYYY', 4], 0, 'year');\n addFormatToken(0, ['YYYYY', 5], 0, 'year');\n addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');\n\n // PARSING\n\n addRegexToken('Y', matchSigned);\n addRegexToken('YY', match1to2, match2);\n addRegexToken('YYYY', match1to4, match4);\n addRegexToken('YYYYY', match1to6, match6);\n addRegexToken('YYYYYY', match1to6, match6);\n addParseToken(['YYYYY', 'YYYYYY'], YEAR);\n addParseToken('YYYY', function (input, array) {\n array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);\n });\n addParseToken('YY', function (input, array) {\n array[YEAR] = hooks.parseTwoDigitYear(input);\n });\n addParseToken('Y', function (input, array) {\n array[YEAR] = parseInt(input, 10);\n });\n\n // HELPERS\n\n function daysInYear(year) {\n return isLeapYear(year) ? 366 : 365;\n }\n\n // HOOKS\n\n hooks.parseTwoDigitYear = function (input) {\n return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);\n };\n\n // MOMENTS\n\n var getSetYear = makeGetSet('FullYear', true);\n function getIsLeapYear() {\n return isLeapYear(this.year());\n }\n function makeGetSet(unit, keepTime) {\n return function (value) {\n if (value != null) {\n set$1(this, unit, value);\n hooks.updateOffset(this, keepTime);\n return this;\n } else {\n return get(this, unit);\n }\n };\n }\n function get(mom, unit) {\n if (!mom.isValid()) {\n return NaN;\n }\n var d = mom._d,\n isUTC = mom._isUTC;\n switch (unit) {\n case 'Milliseconds':\n return isUTC ? d.getUTCMilliseconds() : d.getMilliseconds();\n case 'Seconds':\n return isUTC ? d.getUTCSeconds() : d.getSeconds();\n case 'Minutes':\n return isUTC ? d.getUTCMinutes() : d.getMinutes();\n case 'Hours':\n return isUTC ? d.getUTCHours() : d.getHours();\n case 'Date':\n return isUTC ? d.getUTCDate() : d.getDate();\n case 'Day':\n return isUTC ? d.getUTCDay() : d.getDay();\n case 'Month':\n return isUTC ? d.getUTCMonth() : d.getMonth();\n case 'FullYear':\n return isUTC ? d.getUTCFullYear() : d.getFullYear();\n default:\n return NaN;\n // Just in case\n }\n }\n function set$1(mom, unit, value) {\n var d, isUTC, year, month, date;\n if (!mom.isValid() || isNaN(value)) {\n return;\n }\n d = mom._d;\n isUTC = mom._isUTC;\n switch (unit) {\n case 'Milliseconds':\n return void (isUTC ? d.setUTCMilliseconds(value) : d.setMilliseconds(value));\n case 'Seconds':\n return void (isUTC ? d.setUTCSeconds(value) : d.setSeconds(value));\n case 'Minutes':\n return void (isUTC ? d.setUTCMinutes(value) : d.setMinutes(value));\n case 'Hours':\n return void (isUTC ? d.setUTCHours(value) : d.setHours(value));\n case 'Date':\n return void (isUTC ? d.setUTCDate(value) : d.setDate(value));\n // case 'Day': // Not real\n // return void (isUTC ? d.setUTCDay(value) : d.setDay(value));\n // case 'Month': // Not used because we need to pass two variables\n // return void (isUTC ? d.setUTCMonth(value) : d.setMonth(value));\n case 'FullYear':\n break;\n // See below ...\n default:\n return;\n // Just in case\n }\n year = value;\n month = mom.month();\n date = mom.date();\n date = date === 29 && month === 1 && !isLeapYear(year) ? 28 : date;\n void (isUTC ? d.setUTCFullYear(year, month, date) : d.setFullYear(year, month, date));\n }\n\n // MOMENTS\n\n function stringGet(units) {\n units = normalizeUnits(units);\n if (isFunction(this[units])) {\n return this[units]();\n }\n return this;\n }\n function stringSet(units, value) {\n if (typeof units === 'object') {\n units = normalizeObjectUnits(units);\n var prioritized = getPrioritizedUnits(units),\n i,\n prioritizedLen = prioritized.length;\n for (i = 0; i < prioritizedLen; i++) {\n this[prioritized[i].unit](units[prioritized[i].unit]);\n }\n } else {\n units = normalizeUnits(units);\n if (isFunction(this[units])) {\n return this[units](value);\n }\n }\n return this;\n }\n function mod(n, x) {\n return (n % x + x) % x;\n }\n var indexOf;\n if (Array.prototype.indexOf) {\n indexOf = Array.prototype.indexOf;\n } else {\n indexOf = function (o) {\n // I know\n var i;\n for (i = 0; i < this.length; ++i) {\n if (this[i] === o) {\n return i;\n }\n }\n return -1;\n };\n }\n function daysInMonth(year, month) {\n if (isNaN(year) || isNaN(month)) {\n return NaN;\n }\n var modMonth = mod(month, 12);\n year += (month - modMonth) / 12;\n return modMonth === 1 ? isLeapYear(year) ? 29 : 28 : 31 - modMonth % 7 % 2;\n }\n\n // FORMATTING\n\n addFormatToken('M', ['MM', 2], 'Mo', function () {\n return this.month() + 1;\n });\n addFormatToken('MMM', 0, 0, function (format) {\n return this.localeData().monthsShort(this, format);\n });\n addFormatToken('MMMM', 0, 0, function (format) {\n return this.localeData().months(this, format);\n });\n\n // PARSING\n\n addRegexToken('M', match1to2, match1to2NoLeadingZero);\n addRegexToken('MM', match1to2, match2);\n addRegexToken('MMM', function (isStrict, locale) {\n return locale.monthsShortRegex(isStrict);\n });\n addRegexToken('MMMM', function (isStrict, locale) {\n return locale.monthsRegex(isStrict);\n });\n addParseToken(['M', 'MM'], function (input, array) {\n array[MONTH] = toInt(input) - 1;\n });\n addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {\n var month = config._locale.monthsParse(input, token, config._strict);\n // if we didn't find a month name, mark the date as invalid.\n if (month != null) {\n array[MONTH] = month;\n } else {\n getParsingFlags(config).invalidMonth = input;\n }\n });\n\n // LOCALES\n\n var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),\n defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),\n MONTHS_IN_FORMAT = /D[oD]?(\\[[^\\[\\]]*\\]|\\s)+MMMM?/,\n defaultMonthsShortRegex = matchWord,\n defaultMonthsRegex = matchWord;\n function localeMonths(m, format) {\n if (!m) {\n return isArray(this._months) ? this._months : this._months['standalone'];\n }\n return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];\n }\n function localeMonthsShort(m, format) {\n if (!m) {\n return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone'];\n }\n return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];\n }\n function handleStrictParse(monthName, format, strict) {\n var i,\n ii,\n mom,\n llc = monthName.toLocaleLowerCase();\n if (!this._monthsParse) {\n // this is not used\n this._monthsParse = [];\n this._longMonthsParse = [];\n this._shortMonthsParse = [];\n for (i = 0; i < 12; ++i) {\n mom = createUTC([2000, i]);\n this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();\n this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();\n }\n }\n if (strict) {\n if (format === 'MMM') {\n ii = indexOf.call(this._shortMonthsParse, llc);\n return ii !== -1 ? ii : null;\n } else {\n ii = indexOf.call(this._longMonthsParse, llc);\n return ii !== -1 ? ii : null;\n }\n } else {\n if (format === 'MMM') {\n ii = indexOf.call(this._shortMonthsParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._longMonthsParse, llc);\n return ii !== -1 ? ii : null;\n } else {\n ii = indexOf.call(this._longMonthsParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._shortMonthsParse, llc);\n return ii !== -1 ? ii : null;\n }\n }\n }\n function localeMonthsParse(monthName, format, strict) {\n var i, mom, regex;\n if (this._monthsParseExact) {\n return handleStrictParse.call(this, monthName, format, strict);\n }\n if (!this._monthsParse) {\n this._monthsParse = [];\n this._longMonthsParse = [];\n this._shortMonthsParse = [];\n }\n\n // TODO: add sorting\n // Sorting makes sure if one month (or abbr) is a prefix of another\n // see sorting in computeMonthsParse\n for (i = 0; i < 12; i++) {\n // make the regex if we don't have it already\n mom = createUTC([2000, i]);\n if (strict && !this._longMonthsParse[i]) {\n this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');\n this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');\n }\n if (!strict && !this._monthsParse[i]) {\n regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');\n this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');\n }\n // test the regex\n if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {\n return i;\n } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {\n return i;\n } else if (!strict && this._monthsParse[i].test(monthName)) {\n return i;\n }\n }\n }\n\n // MOMENTS\n\n function setMonth(mom, value) {\n if (!mom.isValid()) {\n // No op\n return mom;\n }\n if (typeof value === 'string') {\n if (/^\\d+$/.test(value)) {\n value = toInt(value);\n } else {\n value = mom.localeData().monthsParse(value);\n // TODO: Another silent failure?\n if (!isNumber(value)) {\n return mom;\n }\n }\n }\n var month = value,\n date = mom.date();\n date = date < 29 ? date : Math.min(date, daysInMonth(mom.year(), month));\n void (mom._isUTC ? mom._d.setUTCMonth(month, date) : mom._d.setMonth(month, date));\n return mom;\n }\n function getSetMonth(value) {\n if (value != null) {\n setMonth(this, value);\n hooks.updateOffset(this, true);\n return this;\n } else {\n return get(this, 'Month');\n }\n }\n function getDaysInMonth() {\n return daysInMonth(this.year(), this.month());\n }\n function monthsShortRegex(isStrict) {\n if (this._monthsParseExact) {\n if (!hasOwnProp(this, '_monthsRegex')) {\n computeMonthsParse.call(this);\n }\n if (isStrict) {\n return this._monthsShortStrictRegex;\n } else {\n return this._monthsShortRegex;\n }\n } else {\n if (!hasOwnProp(this, '_monthsShortRegex')) {\n this._monthsShortRegex = defaultMonthsShortRegex;\n }\n return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex;\n }\n }\n function monthsRegex(isStrict) {\n if (this._monthsParseExact) {\n if (!hasOwnProp(this, '_monthsRegex')) {\n computeMonthsParse.call(this);\n }\n if (isStrict) {\n return this._monthsStrictRegex;\n } else {\n return this._monthsRegex;\n }\n } else {\n if (!hasOwnProp(this, '_monthsRegex')) {\n this._monthsRegex = defaultMonthsRegex;\n }\n return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex;\n }\n }\n function computeMonthsParse() {\n function cmpLenRev(a, b) {\n return b.length - a.length;\n }\n var shortPieces = [],\n longPieces = [],\n mixedPieces = [],\n i,\n mom,\n shortP,\n longP;\n for (i = 0; i < 12; i++) {\n // make the regex if we don't have it already\n mom = createUTC([2000, i]);\n shortP = regexEscape(this.monthsShort(mom, ''));\n longP = regexEscape(this.months(mom, ''));\n shortPieces.push(shortP);\n longPieces.push(longP);\n mixedPieces.push(longP);\n mixedPieces.push(shortP);\n }\n // Sorting makes sure if one month (or abbr) is a prefix of another it\n // will match the longer piece.\n shortPieces.sort(cmpLenRev);\n longPieces.sort(cmpLenRev);\n mixedPieces.sort(cmpLenRev);\n this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');\n this._monthsShortRegex = this._monthsRegex;\n this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');\n this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');\n }\n function createDate(y, m, d, h, M, s, ms) {\n // can't just apply() to create a date:\n // https://stackoverflow.com/q/181348\n var date;\n // the date constructor remaps years 0-99 to 1900-1999\n if (y < 100 && y >= 0) {\n // preserve leap years using a full 400 year cycle, then reset\n date = new Date(y + 400, m, d, h, M, s, ms);\n if (isFinite(date.getFullYear())) {\n date.setFullYear(y);\n }\n } else {\n date = new Date(y, m, d, h, M, s, ms);\n }\n return date;\n }\n function createUTCDate(y) {\n var date, args;\n // the Date.UTC function remaps years 0-99 to 1900-1999\n if (y < 100 && y >= 0) {\n args = Array.prototype.slice.call(arguments);\n // preserve leap years using a full 400 year cycle, then reset\n args[0] = y + 400;\n date = new Date(Date.UTC.apply(null, args));\n if (isFinite(date.getUTCFullYear())) {\n date.setUTCFullYear(y);\n }\n } else {\n date = new Date(Date.UTC.apply(null, arguments));\n }\n return date;\n }\n\n // start-of-first-week - start-of-year\n function firstWeekOffset(year, dow, doy) {\n var\n // first-week day -- which january is always in the first week (4 for iso, 1 for other)\n fwd = 7 + dow - doy,\n // first-week day local weekday -- which local weekday is fwd\n fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;\n return -fwdlw + fwd - 1;\n }\n\n // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday\n function dayOfYearFromWeeks(year, week, weekday, dow, doy) {\n var localWeekday = (7 + weekday - dow) % 7,\n weekOffset = firstWeekOffset(year, dow, doy),\n dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,\n resYear,\n resDayOfYear;\n if (dayOfYear <= 0) {\n resYear = year - 1;\n resDayOfYear = daysInYear(resYear) + dayOfYear;\n } else if (dayOfYear > daysInYear(year)) {\n resYear = year + 1;\n resDayOfYear = dayOfYear - daysInYear(year);\n } else {\n resYear = year;\n resDayOfYear = dayOfYear;\n }\n return {\n year: resYear,\n dayOfYear: resDayOfYear\n };\n }\n function weekOfYear(mom, dow, doy) {\n var weekOffset = firstWeekOffset(mom.year(), dow, doy),\n week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,\n resWeek,\n resYear;\n if (week < 1) {\n resYear = mom.year() - 1;\n resWeek = week + weeksInYear(resYear, dow, doy);\n } else if (week > weeksInYear(mom.year(), dow, doy)) {\n resWeek = week - weeksInYear(mom.year(), dow, doy);\n resYear = mom.year() + 1;\n } else {\n resYear = mom.year();\n resWeek = week;\n }\n return {\n week: resWeek,\n year: resYear\n };\n }\n function weeksInYear(year, dow, doy) {\n var weekOffset = firstWeekOffset(year, dow, doy),\n weekOffsetNext = firstWeekOffset(year + 1, dow, doy);\n return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;\n }\n\n // FORMATTING\n\n addFormatToken('w', ['ww', 2], 'wo', 'week');\n addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');\n\n // PARSING\n\n addRegexToken('w', match1to2, match1to2NoLeadingZero);\n addRegexToken('ww', match1to2, match2);\n addRegexToken('W', match1to2, match1to2NoLeadingZero);\n addRegexToken('WW', match1to2, match2);\n addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {\n week[token.substr(0, 1)] = toInt(input);\n });\n\n // HELPERS\n\n // LOCALES\n\n function localeWeek(mom) {\n return weekOfYear(mom, this._week.dow, this._week.doy).week;\n }\n var defaultLocaleWeek = {\n dow: 0,\n // Sunday is the first day of the week.\n doy: 6 // The week that contains Jan 6th is the first week of the year.\n };\n function localeFirstDayOfWeek() {\n return this._week.dow;\n }\n function localeFirstDayOfYear() {\n return this._week.doy;\n }\n\n // MOMENTS\n\n function getSetWeek(input) {\n var week = this.localeData().week(this);\n return input == null ? week : this.add((input - week) * 7, 'd');\n }\n function getSetISOWeek(input) {\n var week = weekOfYear(this, 1, 4).week;\n return input == null ? week : this.add((input - week) * 7, 'd');\n }\n\n // FORMATTING\n\n addFormatToken('d', 0, 'do', 'day');\n addFormatToken('dd', 0, 0, function (format) {\n return this.localeData().weekdaysMin(this, format);\n });\n addFormatToken('ddd', 0, 0, function (format) {\n return this.localeData().weekdaysShort(this, format);\n });\n addFormatToken('dddd', 0, 0, function (format) {\n return this.localeData().weekdays(this, format);\n });\n addFormatToken('e', 0, 0, 'weekday');\n addFormatToken('E', 0, 0, 'isoWeekday');\n\n // PARSING\n\n addRegexToken('d', match1to2);\n addRegexToken('e', match1to2);\n addRegexToken('E', match1to2);\n addRegexToken('dd', function (isStrict, locale) {\n return locale.weekdaysMinRegex(isStrict);\n });\n addRegexToken('ddd', function (isStrict, locale) {\n return locale.weekdaysShortRegex(isStrict);\n });\n addRegexToken('dddd', function (isStrict, locale) {\n return locale.weekdaysRegex(isStrict);\n });\n addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {\n var weekday = config._locale.weekdaysParse(input, token, config._strict);\n // if we didn't get a weekday name, mark the date as invalid\n if (weekday != null) {\n week.d = weekday;\n } else {\n getParsingFlags(config).invalidWeekday = input;\n }\n });\n addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {\n week[token] = toInt(input);\n });\n\n // HELPERS\n\n function parseWeekday(input, locale) {\n if (typeof input !== 'string') {\n return input;\n }\n if (!isNaN(input)) {\n return parseInt(input, 10);\n }\n input = locale.weekdaysParse(input);\n if (typeof input === 'number') {\n return input;\n }\n return null;\n }\n function parseIsoWeekday(input, locale) {\n if (typeof input === 'string') {\n return locale.weekdaysParse(input) % 7 || 7;\n }\n return isNaN(input) ? null : input;\n }\n\n // LOCALES\n function shiftWeekdays(ws, n) {\n return ws.slice(n, 7).concat(ws.slice(0, n));\n }\n var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),\n defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),\n defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),\n defaultWeekdaysRegex = matchWord,\n defaultWeekdaysShortRegex = matchWord,\n defaultWeekdaysMinRegex = matchWord;\n function localeWeekdays(m, format) {\n var weekdays = isArray(this._weekdays) ? this._weekdays : this._weekdays[m && m !== true && this._weekdays.isFormat.test(format) ? 'format' : 'standalone'];\n return m === true ? shiftWeekdays(weekdays, this._week.dow) : m ? weekdays[m.day()] : weekdays;\n }\n function localeWeekdaysShort(m) {\n return m === true ? shiftWeekdays(this._weekdaysShort, this._week.dow) : m ? this._weekdaysShort[m.day()] : this._weekdaysShort;\n }\n function localeWeekdaysMin(m) {\n return m === true ? shiftWeekdays(this._weekdaysMin, this._week.dow) : m ? this._weekdaysMin[m.day()] : this._weekdaysMin;\n }\n function handleStrictParse$1(weekdayName, format, strict) {\n var i,\n ii,\n mom,\n llc = weekdayName.toLocaleLowerCase();\n if (!this._weekdaysParse) {\n this._weekdaysParse = [];\n this._shortWeekdaysParse = [];\n this._minWeekdaysParse = [];\n for (i = 0; i < 7; ++i) {\n mom = createUTC([2000, 1]).day(i);\n this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();\n this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();\n this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();\n }\n }\n if (strict) {\n if (format === 'dddd') {\n ii = indexOf.call(this._weekdaysParse, llc);\n return ii !== -1 ? ii : null;\n } else if (format === 'ddd') {\n ii = indexOf.call(this._shortWeekdaysParse, llc);\n return ii !== -1 ? ii : null;\n } else {\n ii = indexOf.call(this._minWeekdaysParse, llc);\n return ii !== -1 ? ii : null;\n }\n } else {\n if (format === 'dddd') {\n ii = indexOf.call(this._weekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._shortWeekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._minWeekdaysParse, llc);\n return ii !== -1 ? ii : null;\n } else if (format === 'ddd') {\n ii = indexOf.call(this._shortWeekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._weekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._minWeekdaysParse, llc);\n return ii !== -1 ? ii : null;\n } else {\n ii = indexOf.call(this._minWeekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._weekdaysParse, llc);\n if (ii !== -1) {\n return ii;\n }\n ii = indexOf.call(this._shortWeekdaysParse, llc);\n return ii !== -1 ? ii : null;\n }\n }\n }\n function localeWeekdaysParse(weekdayName, format, strict) {\n var i, mom, regex;\n if (this._weekdaysParseExact) {\n return handleStrictParse$1.call(this, weekdayName, format, strict);\n }\n if (!this._weekdaysParse) {\n this._weekdaysParse = [];\n this._minWeekdaysParse = [];\n this._shortWeekdaysParse = [];\n this._fullWeekdaysParse = [];\n }\n for (i = 0; i < 7; i++) {\n // make the regex if we don't have it already\n\n mom = createUTC([2000, 1]).day(i);\n if (strict && !this._fullWeekdaysParse[i]) {\n this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\\\.?') + '$', 'i');\n this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\\\.?') + '$', 'i');\n this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\\\.?') + '$', 'i');\n }\n if (!this._weekdaysParse[i]) {\n regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');\n this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');\n }\n // test the regex\n if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {\n return i;\n } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {\n return i;\n } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {\n return i;\n } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {\n return i;\n }\n }\n }\n\n // MOMENTS\n\n function getSetDayOfWeek(input) {\n if (!this.isValid()) {\n return input != null ? this : NaN;\n }\n var day = get(this, 'Day');\n if (input != null) {\n input = parseWeekday(input, this.localeData());\n return this.add(input - day, 'd');\n } else {\n return day;\n }\n }\n function getSetLocaleDayOfWeek(input) {\n if (!this.isValid()) {\n return input != null ? this : NaN;\n }\n var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;\n return input == null ? weekday : this.add(input - weekday, 'd');\n }\n function getSetISODayOfWeek(input) {\n if (!this.isValid()) {\n return input != null ? this : NaN;\n }\n\n // behaves the same as moment#day except\n // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)\n // as a setter, sunday should belong to the previous week.\n\n if (input != null) {\n var weekday = parseIsoWeekday(input, this.localeData());\n return this.day(this.day() % 7 ? weekday : weekday - 7);\n } else {\n return this.day() || 7;\n }\n }\n function weekdaysRegex(isStrict) {\n if (this._weekdaysParseExact) {\n if (!hasOwnProp(this, '_weekdaysRegex')) {\n computeWeekdaysParse.call(this);\n }\n if (isStrict) {\n return this._weekdaysStrictRegex;\n } else {\n return this._weekdaysRegex;\n }\n } else {\n if (!hasOwnProp(this, '_weekdaysRegex')) {\n this._weekdaysRegex = defaultWeekdaysRegex;\n }\n return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex;\n }\n }\n function weekdaysShortRegex(isStrict) {\n if (this._weekdaysParseExact) {\n if (!hasOwnProp(this, '_weekdaysRegex')) {\n computeWeekdaysParse.call(this);\n }\n if (isStrict) {\n return this._weekdaysShortStrictRegex;\n } else {\n return this._weekdaysShortRegex;\n }\n } else {\n if (!hasOwnProp(this, '_weekdaysShortRegex')) {\n this._weekdaysShortRegex = defaultWeekdaysShortRegex;\n }\n return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex;\n }\n }\n function weekdaysMinRegex(isStrict) {\n if (this._weekdaysParseExact) {\n if (!hasOwnProp(this, '_weekdaysRegex')) {\n computeWeekdaysParse.call(this);\n }\n if (isStrict) {\n return this._weekdaysMinStrictRegex;\n } else {\n return this._weekdaysMinRegex;\n }\n } else {\n if (!hasOwnProp(this, '_weekdaysMinRegex')) {\n this._weekdaysMinRegex = defaultWeekdaysMinRegex;\n }\n return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex;\n }\n }\n function computeWeekdaysParse() {\n function cmpLenRev(a, b) {\n return b.length - a.length;\n }\n var minPieces = [],\n shortPieces = [],\n longPieces = [],\n mixedPieces = [],\n i,\n mom,\n minp,\n shortp,\n longp;\n for (i = 0; i < 7; i++) {\n // make the regex if we don't have it already\n mom = createUTC([2000, 1]).day(i);\n minp = regexEscape(this.weekdaysMin(mom, ''));\n shortp = regexEscape(this.weekdaysShort(mom, ''));\n longp = regexEscape(this.weekdays(mom, ''));\n minPieces.push(minp);\n shortPieces.push(shortp);\n longPieces.push(longp);\n mixedPieces.push(minp);\n mixedPieces.push(shortp);\n mixedPieces.push(longp);\n }\n // Sorting makes sure if one weekday (or abbr) is a prefix of another it\n // will match the longer piece.\n minPieces.sort(cmpLenRev);\n shortPieces.sort(cmpLenRev);\n longPieces.sort(cmpLenRev);\n mixedPieces.sort(cmpLenRev);\n this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');\n this._weekdaysShortRegex = this._weekdaysRegex;\n this._weekdaysMinRegex = this._weekdaysRegex;\n this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');\n this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');\n this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');\n }\n\n // FORMATTING\n\n function hFormat() {\n return this.hours() % 12 || 12;\n }\n function kFormat() {\n return this.hours() || 24;\n }\n addFormatToken('H', ['HH', 2], 0, 'hour');\n addFormatToken('h', ['hh', 2], 0, hFormat);\n addFormatToken('k', ['kk', 2], 0, kFormat);\n addFormatToken('hmm', 0, 0, function () {\n return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);\n });\n addFormatToken('hmmss', 0, 0, function () {\n return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);\n });\n addFormatToken('Hmm', 0, 0, function () {\n return '' + this.hours() + zeroFill(this.minutes(), 2);\n });\n addFormatToken('Hmmss', 0, 0, function () {\n return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2);\n });\n function meridiem(token, lowercase) {\n addFormatToken(token, 0, 0, function () {\n return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);\n });\n }\n meridiem('a', true);\n meridiem('A', false);\n\n // PARSING\n\n function matchMeridiem(isStrict, locale) {\n return locale._meridiemParse;\n }\n addRegexToken('a', matchMeridiem);\n addRegexToken('A', matchMeridiem);\n addRegexToken('H', match1to2, match1to2HasZero);\n addRegexToken('h', match1to2, match1to2NoLeadingZero);\n addRegexToken('k', match1to2, match1to2NoLeadingZero);\n addRegexToken('HH', match1to2, match2);\n addRegexToken('hh', match1to2, match2);\n addRegexToken('kk', match1to2, match2);\n addRegexToken('hmm', match3to4);\n addRegexToken('hmmss', match5to6);\n addRegexToken('Hmm', match3to4);\n addRegexToken('Hmmss', match5to6);\n addParseToken(['H', 'HH'], HOUR);\n addParseToken(['k', 'kk'], function (input, array, config) {\n var kInput = toInt(input);\n array[HOUR] = kInput === 24 ? 0 : kInput;\n });\n addParseToken(['a', 'A'], function (input, array, config) {\n config._isPm = config._locale.isPM(input);\n config._meridiem = input;\n });\n addParseToken(['h', 'hh'], function (input, array, config) {\n array[HOUR] = toInt(input);\n getParsingFlags(config).bigHour = true;\n });\n addParseToken('hmm', function (input, array, config) {\n var pos = input.length - 2;\n array[HOUR] = toInt(input.substr(0, pos));\n array[MINUTE] = toInt(input.substr(pos));\n getParsingFlags(config).bigHour = true;\n });\n addParseToken('hmmss', function (input, array, config) {\n var pos1 = input.length - 4,\n pos2 = input.length - 2;\n array[HOUR] = toInt(input.substr(0, pos1));\n array[MINUTE] = toInt(input.substr(pos1, 2));\n array[SECOND] = toInt(input.substr(pos2));\n getParsingFlags(config).bigHour = true;\n });\n addParseToken('Hmm', function (input, array, config) {\n var pos = input.length - 2;\n array[HOUR] = toInt(input.substr(0, pos));\n array[MINUTE] = toInt(input.substr(pos));\n });\n addParseToken('Hmmss', function (input, array, config) {\n var pos1 = input.length - 4,\n pos2 = input.length - 2;\n array[HOUR] = toInt(input.substr(0, pos1));\n array[MINUTE] = toInt(input.substr(pos1, 2));\n array[SECOND] = toInt(input.substr(pos2));\n });\n\n // LOCALES\n\n function localeIsPM(input) {\n // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays\n // Using charAt should be more compatible.\n return (input + '').toLowerCase().charAt(0) === 'p';\n }\n var defaultLocaleMeridiemParse = /[ap]\\.?m?\\.?/i,\n // Setting the hour should keep the time, because the user explicitly\n // specified which hour they want. So trying to maintain the same hour (in\n // a new timezone) makes sense. Adding/subtracting hours does not follow\n // this rule.\n getSetHour = makeGetSet('Hours', true);\n function localeMeridiem(hours, minutes, isLower) {\n if (hours > 11) {\n return isLower ? 'pm' : 'PM';\n } else {\n return isLower ? 'am' : 'AM';\n }\n }\n var baseConfig = {\n calendar: defaultCalendar,\n longDateFormat: defaultLongDateFormat,\n invalidDate: defaultInvalidDate,\n ordinal: defaultOrdinal,\n dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,\n relativeTime: defaultRelativeTime,\n months: defaultLocaleMonths,\n monthsShort: defaultLocaleMonthsShort,\n week: defaultLocaleWeek,\n weekdays: defaultLocaleWeekdays,\n weekdaysMin: defaultLocaleWeekdaysMin,\n weekdaysShort: defaultLocaleWeekdaysShort,\n meridiemParse: defaultLocaleMeridiemParse\n };\n\n // internal storage for locale config files\n var locales = {},\n localeFamilies = {},\n globalLocale;\n function commonPrefix(arr1, arr2) {\n var i,\n minl = Math.min(arr1.length, arr2.length);\n for (i = 0; i < minl; i += 1) {\n if (arr1[i] !== arr2[i]) {\n return i;\n }\n }\n return minl;\n }\n function normalizeLocale(key) {\n return key ? key.toLowerCase().replace('_', '-') : key;\n }\n\n // pick the locale from the array\n // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each\n // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root\n function chooseLocale(names) {\n var i = 0,\n j,\n next,\n locale,\n split;\n while (i < names.length) {\n split = normalizeLocale(names[i]).split('-');\n j = split.length;\n next = normalizeLocale(names[i + 1]);\n next = next ? next.split('-') : null;\n while (j > 0) {\n locale = loadLocale(split.slice(0, j).join('-'));\n if (locale) {\n return locale;\n }\n if (next && next.length >= j && commonPrefix(split, next) >= j - 1) {\n //the next array item is better than a shallower substring of this one\n break;\n }\n j--;\n }\n i++;\n }\n return globalLocale;\n }\n function isLocaleNameSane(name) {\n // Prevent names that look like filesystem paths, i.e contain '/' or '\\'\n // Ensure name is available and function returns boolean\n return !!(name && name.match('^[^/\\\\\\\\]*$'));\n }\n function loadLocale(name) {\n var oldLocale = null,\n aliasedRequire;\n // TODO: Find a better way to register and load all the locales in Node\n if (locales[name] === undefined && typeof module !== 'undefined' && module && module.exports && isLocaleNameSane(name)) {\n try {\n oldLocale = globalLocale._abbr;\n aliasedRequire = require;\n aliasedRequire('./locale/' + name);\n getSetGlobalLocale(oldLocale);\n } catch (e) {\n // mark as not found to avoid repeating expensive file require call causing high CPU\n // when trying to find en-US, en_US, en-us for every format call\n locales[name] = null; // null means not found\n }\n }\n return locales[name];\n }\n\n // This function will load locale and then set the global locale. If\n // no arguments are passed in, it will simply return the current global\n // locale key.\n function getSetGlobalLocale(key, values) {\n var data;\n if (key) {\n if (isUndefined(values)) {\n data = getLocale(key);\n } else {\n data = defineLocale(key, values);\n }\n if (data) {\n // moment.duration._locale = moment._locale = data;\n globalLocale = data;\n } else {\n if (typeof console !== 'undefined' && console.warn) {\n //warn user if arguments are passed but the locale could not be set\n console.warn('Locale ' + key + ' not found. Did you forget to load it?');\n }\n }\n }\n return globalLocale._abbr;\n }\n function defineLocale(name, config) {\n if (config !== null) {\n var locale,\n parentConfig = baseConfig;\n config.abbr = name;\n if (locales[name] != null) {\n deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');\n parentConfig = locales[name]._config;\n } else if (config.parentLocale != null) {\n if (locales[config.parentLocale] != null) {\n parentConfig = locales[config.parentLocale]._config;\n } else {\n locale = loadLocale(config.parentLocale);\n if (locale != null) {\n parentConfig = locale._config;\n } else {\n if (!localeFamilies[config.parentLocale]) {\n localeFamilies[config.parentLocale] = [];\n }\n localeFamilies[config.parentLocale].push({\n name: name,\n config: config\n });\n return null;\n }\n }\n }\n locales[name] = new Locale(mergeConfigs(parentConfig, config));\n if (localeFamilies[name]) {\n localeFamilies[name].forEach(function (x) {\n defineLocale(x.name, x.config);\n });\n }\n\n // backwards compat for now: also set the locale\n // make sure we set the locale AFTER all child locales have been\n // created, so we won't end up with the child locale set.\n getSetGlobalLocale(name);\n return locales[name];\n } else {\n // useful for testing\n delete locales[name];\n return null;\n }\n }\n function updateLocale(name, config) {\n if (config != null) {\n var locale,\n tmpLocale,\n parentConfig = baseConfig;\n if (locales[name] != null && locales[name].parentLocale != null) {\n // Update existing child locale in-place to avoid memory-leaks\n locales[name].set(mergeConfigs(locales[name]._config, config));\n } else {\n // MERGE\n tmpLocale = loadLocale(name);\n if (tmpLocale != null) {\n parentConfig = tmpLocale._config;\n }\n config = mergeConfigs(parentConfig, config);\n if (tmpLocale == null) {\n // updateLocale is called for creating a new locale\n // Set abbr so it will have a name (getters return\n // undefined otherwise).\n config.abbr = name;\n }\n locale = new Locale(config);\n locale.parentLocale = locales[name];\n locales[name] = locale;\n }\n\n // backwards compat for now: also set the locale\n getSetGlobalLocale(name);\n } else {\n // pass null for config to unupdate, useful for tests\n if (locales[name] != null) {\n if (locales[name].parentLocale != null) {\n locales[name] = locales[name].parentLocale;\n if (name === getSetGlobalLocale()) {\n getSetGlobalLocale(name);\n }\n } else if (locales[name] != null) {\n delete locales[name];\n }\n }\n }\n return locales[name];\n }\n\n // returns locale data\n function getLocale(key) {\n var locale;\n if (key && key._locale && key._locale._abbr) {\n key = key._locale._abbr;\n }\n if (!key) {\n return globalLocale;\n }\n if (!isArray(key)) {\n //short-circuit everything else\n locale = loadLocale(key);\n if (locale) {\n return locale;\n }\n key = [key];\n }\n return chooseLocale(key);\n }\n function listLocales() {\n return keys(locales);\n }\n function checkOverflow(m) {\n var overflow,\n a = m._a;\n if (a && getParsingFlags(m).overflow === -2) {\n overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1;\n if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {\n overflow = DATE;\n }\n if (getParsingFlags(m)._overflowWeeks && overflow === -1) {\n overflow = WEEK;\n }\n if (getParsingFlags(m)._overflowWeekday && overflow === -1) {\n overflow = WEEKDAY;\n }\n getParsingFlags(m).overflow = overflow;\n }\n return m;\n }\n\n // iso 8601 regex\n // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)\n var extendedIsoRegex = /^\\s*((?:[+-]\\d{6}|\\d{4})-(?:\\d\\d-\\d\\d|W\\d\\d-\\d|W\\d\\d|\\d\\d\\d|\\d\\d))(?:(T| )(\\d\\d(?::\\d\\d(?::\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,\n basicIsoRegex = /^\\s*((?:[+-]\\d{6}|\\d{4})(?:\\d\\d\\d\\d|W\\d\\d\\d|W\\d\\d|\\d\\d\\d|\\d\\d|))(?:(T| )(\\d\\d(?:\\d\\d(?:\\d\\d(?:[.,]\\d+)?)?)?)([+-]\\d\\d(?::?\\d\\d)?|\\s*Z)?)?$/,\n tzRegex = /Z|[+-]\\d\\d(?::?\\d\\d)?/,\n isoDates = [['YYYYYY-MM-DD', /[+-]\\d{6}-\\d\\d-\\d\\d/], ['YYYY-MM-DD', /\\d{4}-\\d\\d-\\d\\d/], ['GGGG-[W]WW-E', /\\d{4}-W\\d\\d-\\d/], ['GGGG-[W]WW', /\\d{4}-W\\d\\d/, false], ['YYYY-DDD', /\\d{4}-\\d{3}/], ['YYYY-MM', /\\d{4}-\\d\\d/, false], ['YYYYYYMMDD', /[+-]\\d{10}/], ['YYYYMMDD', /\\d{8}/], ['GGGG[W]WWE', /\\d{4}W\\d{3}/], ['GGGG[W]WW', /\\d{4}W\\d{2}/, false], ['YYYYDDD', /\\d{7}/], ['YYYYMM', /\\d{6}/, false], ['YYYY', /\\d{4}/, false]],\n // iso time formats and regexes\n isoTimes = [['HH:mm:ss.SSSS', /\\d\\d:\\d\\d:\\d\\d\\.\\d+/], ['HH:mm:ss,SSSS', /\\d\\d:\\d\\d:\\d\\d,\\d+/], ['HH:mm:ss', /\\d\\d:\\d\\d:\\d\\d/], ['HH:mm', /\\d\\d:\\d\\d/], ['HHmmss.SSSS', /\\d\\d\\d\\d\\d\\d\\.\\d+/], ['HHmmss,SSSS', /\\d\\d\\d\\d\\d\\d,\\d+/], ['HHmmss', /\\d\\d\\d\\d\\d\\d/], ['HHmm', /\\d\\d\\d\\d/], ['HH', /\\d\\d/]],\n aspNetJsonRegex = /^\\/?Date\\((-?\\d+)/i,\n // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3\n rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\\d{4}))$/,\n obsOffsets = {\n UT: 0,\n GMT: 0,\n EDT: -4 * 60,\n EST: -5 * 60,\n CDT: -5 * 60,\n CST: -6 * 60,\n MDT: -6 * 60,\n MST: -7 * 60,\n PDT: -7 * 60,\n PST: -8 * 60\n };\n\n // date from iso format\n function configFromISO(config) {\n var i,\n l,\n string = config._i,\n match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),\n allowTime,\n dateFormat,\n timeFormat,\n tzFormat,\n isoDatesLen = isoDates.length,\n isoTimesLen = isoTimes.length;\n if (match) {\n getParsingFlags(config).iso = true;\n for (i = 0, l = isoDatesLen; i < l; i++) {\n if (isoDates[i][1].exec(match[1])) {\n dateFormat = isoDates[i][0];\n allowTime = isoDates[i][2] !== false;\n break;\n }\n }\n if (dateFormat == null) {\n config._isValid = false;\n return;\n }\n if (match[3]) {\n for (i = 0, l = isoTimesLen; i < l; i++) {\n if (isoTimes[i][1].exec(match[3])) {\n // match[2] should be 'T' or space\n timeFormat = (match[2] || ' ') + isoTimes[i][0];\n break;\n }\n }\n if (timeFormat == null) {\n config._isValid = false;\n return;\n }\n }\n if (!allowTime && timeFormat != null) {\n config._isValid = false;\n return;\n }\n if (match[4]) {\n if (tzRegex.exec(match[4])) {\n tzFormat = 'Z';\n } else {\n config._isValid = false;\n return;\n }\n }\n config._f = dateFormat + (timeFormat || '') + (tzFormat || '');\n configFromStringAndFormat(config);\n } else {\n config._isValid = false;\n }\n }\n function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {\n var result = [untruncateYear(yearStr), defaultLocaleMonthsShort.indexOf(monthStr), parseInt(dayStr, 10), parseInt(hourStr, 10), parseInt(minuteStr, 10)];\n if (secondStr) {\n result.push(parseInt(secondStr, 10));\n }\n return result;\n }\n function untruncateYear(yearStr) {\n var year = parseInt(yearStr, 10);\n if (year <= 49) {\n return 2000 + year;\n } else if (year <= 999) {\n return 1900 + year;\n }\n return year;\n }\n function preprocessRFC2822(s) {\n // Remove comments and folding whitespace and replace multiple-spaces with a single space\n return s.replace(/\\([^()]*\\)|[\\n\\t]/g, ' ').replace(/(\\s\\s+)/g, ' ').replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n }\n function checkWeekday(weekdayStr, parsedInput, config) {\n if (weekdayStr) {\n // TODO: Replace the vanilla JS Date object with an independent day-of-week check.\n var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),\n weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();\n if (weekdayProvided !== weekdayActual) {\n getParsingFlags(config).weekdayMismatch = true;\n config._isValid = false;\n return false;\n }\n }\n return true;\n }\n function calculateOffset(obsOffset, militaryOffset, numOffset) {\n if (obsOffset) {\n return obsOffsets[obsOffset];\n } else if (militaryOffset) {\n // the only allowed military tz is Z\n return 0;\n } else {\n var hm = parseInt(numOffset, 10),\n m = hm % 100,\n h = (hm - m) / 100;\n return h * 60 + m;\n }\n }\n\n // date and time from ref 2822 format\n function configFromRFC2822(config) {\n var match = rfc2822.exec(preprocessRFC2822(config._i)),\n parsedArray;\n if (match) {\n parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);\n if (!checkWeekday(match[1], parsedArray, config)) {\n return;\n }\n config._a = parsedArray;\n config._tzm = calculateOffset(match[8], match[9], match[10]);\n config._d = createUTCDate.apply(null, config._a);\n config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);\n getParsingFlags(config).rfc2822 = true;\n } else {\n config._isValid = false;\n }\n }\n\n // date from 1) ASP.NET, 2) ISO, 3) RFC 2822 formats, or 4) optional fallback if parsing isn't strict\n function configFromString(config) {\n var matched = aspNetJsonRegex.exec(config._i);\n if (matched !== null) {\n config._d = new Date(+matched[1]);\n return;\n }\n configFromISO(config);\n if (config._isValid === false) {\n delete config._isValid;\n } else {\n return;\n }\n configFromRFC2822(config);\n if (config._isValid === false) {\n delete config._isValid;\n } else {\n return;\n }\n if (config._strict) {\n config._isValid = false;\n } else {\n // Final attempt, use Input Fallback\n hooks.createFromInputFallback(config);\n }\n }\n hooks.createFromInputFallback = deprecate('value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) {\n config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));\n });\n\n // Pick the first defined of two or three arguments.\n function defaults(a, b, c) {\n if (a != null) {\n return a;\n }\n if (b != null) {\n return b;\n }\n return c;\n }\n function currentDateArray(config) {\n // hooks is actually the exported moment object\n var nowValue = new Date(hooks.now());\n if (config._useUTC) {\n return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];\n }\n return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];\n }\n\n // convert an array to a date.\n // the array should mirror the parameters below\n // note: all values past the year are optional and will default to the lowest possible value.\n // [year, month, day , hour, minute, second, millisecond]\n function configFromArray(config) {\n var i,\n date,\n input = [],\n currentDate,\n expectedWeekday,\n yearToUse;\n if (config._d) {\n return;\n }\n currentDate = currentDateArray(config);\n\n //compute day of the year from weeks and weekdays\n if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {\n dayOfYearFromWeekInfo(config);\n }\n\n //if the day of the year is set, figure out what it is\n if (config._dayOfYear != null) {\n yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);\n if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {\n getParsingFlags(config)._overflowDayOfYear = true;\n }\n date = createUTCDate(yearToUse, 0, config._dayOfYear);\n config._a[MONTH] = date.getUTCMonth();\n config._a[DATE] = date.getUTCDate();\n }\n\n // Default to current date.\n // * if no year, month, day of month are given, default to today\n // * if day of month is given, default month and year\n // * if month is given, default only year\n // * if year is given, don't default anything\n for (i = 0; i < 3 && config._a[i] == null; ++i) {\n config._a[i] = input[i] = currentDate[i];\n }\n\n // Zero out whatever was not defaulted, including time\n for (; i < 7; i++) {\n config._a[i] = input[i] = config._a[i] == null ? i === 2 ? 1 : 0 : config._a[i];\n }\n\n // Check for 24:00:00.000\n if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) {\n config._nextDay = true;\n config._a[HOUR] = 0;\n }\n config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);\n expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay();\n\n // Apply timezone offset from input. The actual utcOffset can be changed\n // with parseZone.\n if (config._tzm != null) {\n config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);\n }\n if (config._nextDay) {\n config._a[HOUR] = 24;\n }\n\n // check for mismatching day of week\n if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) {\n getParsingFlags(config).weekdayMismatch = true;\n }\n }\n function dayOfYearFromWeekInfo(config) {\n var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow, curWeek;\n w = config._w;\n if (w.GG != null || w.W != null || w.E != null) {\n dow = 1;\n doy = 4;\n\n // TODO: We need to take the current isoWeekYear, but that depends on\n // how we interpret now (local, utc, fixed offset). So create\n // a now version of current config (take local/utc/offset flags, and\n // create now).\n weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);\n week = defaults(w.W, 1);\n weekday = defaults(w.E, 1);\n if (weekday < 1 || weekday > 7) {\n weekdayOverflow = true;\n }\n } else {\n dow = config._locale._week.dow;\n doy = config._locale._week.doy;\n curWeek = weekOfYear(createLocal(), dow, doy);\n weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);\n\n // Default to current week.\n week = defaults(w.w, curWeek.week);\n if (w.d != null) {\n // weekday -- low day numbers are considered next week\n weekday = w.d;\n if (weekday < 0 || weekday > 6) {\n weekdayOverflow = true;\n }\n } else if (w.e != null) {\n // local weekday -- counting starts from beginning of week\n weekday = w.e + dow;\n if (w.e < 0 || w.e > 6) {\n weekdayOverflow = true;\n }\n } else {\n // default to beginning of week\n weekday = dow;\n }\n }\n if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {\n getParsingFlags(config)._overflowWeeks = true;\n } else if (weekdayOverflow != null) {\n getParsingFlags(config)._overflowWeekday = true;\n } else {\n temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);\n config._a[YEAR] = temp.year;\n config._dayOfYear = temp.dayOfYear;\n }\n }\n\n // constant that refers to the ISO standard\n hooks.ISO_8601 = function () {};\n\n // constant that refers to the RFC 2822 form\n hooks.RFC_2822 = function () {};\n\n // date from string and format string\n function configFromStringAndFormat(config) {\n // TODO: Move this to another part of the creation flow to prevent circular deps\n if (config._f === hooks.ISO_8601) {\n configFromISO(config);\n return;\n }\n if (config._f === hooks.RFC_2822) {\n configFromRFC2822(config);\n return;\n }\n config._a = [];\n getParsingFlags(config).empty = true;\n\n // This array is used to make a Date, either with `new Date` or `Date.UTC`\n var string = '' + config._i,\n i,\n parsedInput,\n tokens,\n token,\n skipped,\n stringLength = string.length,\n totalParsedInputLength = 0,\n era,\n tokenLen;\n tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];\n tokenLen = tokens.length;\n for (i = 0; i < tokenLen; i++) {\n token = tokens[i];\n parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];\n if (parsedInput) {\n skipped = string.substr(0, string.indexOf(parsedInput));\n if (skipped.length > 0) {\n getParsingFlags(config).unusedInput.push(skipped);\n }\n string = string.slice(string.indexOf(parsedInput) + parsedInput.length);\n totalParsedInputLength += parsedInput.length;\n }\n // don't parse if it's not a known token\n if (formatTokenFunctions[token]) {\n if (parsedInput) {\n getParsingFlags(config).empty = false;\n } else {\n getParsingFlags(config).unusedTokens.push(token);\n }\n addTimeToArrayFromToken(token, parsedInput, config);\n } else if (config._strict && !parsedInput) {\n getParsingFlags(config).unusedTokens.push(token);\n }\n }\n\n // add remaining unparsed input length to the string\n getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;\n if (string.length > 0) {\n getParsingFlags(config).unusedInput.push(string);\n }\n\n // clear _12h flag if hour is <= 12\n if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) {\n getParsingFlags(config).bigHour = undefined;\n }\n getParsingFlags(config).parsedDateParts = config._a.slice(0);\n getParsingFlags(config).meridiem = config._meridiem;\n // handle meridiem\n config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);\n\n // handle era\n era = getParsingFlags(config).era;\n if (era !== null) {\n config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]);\n }\n configFromArray(config);\n checkOverflow(config);\n }\n function meridiemFixWrap(locale, hour, meridiem) {\n var isPm;\n if (meridiem == null) {\n // nothing to do\n return hour;\n }\n if (locale.meridiemHour != null) {\n return locale.meridiemHour(hour, meridiem);\n } else if (locale.isPM != null) {\n // Fallback\n isPm = locale.isPM(meridiem);\n if (isPm && hour < 12) {\n hour += 12;\n }\n if (!isPm && hour === 12) {\n hour = 0;\n }\n return hour;\n } else {\n // this is not supposed to happen\n return hour;\n }\n }\n\n // date from string and array of format strings\n function configFromStringAndArray(config) {\n var tempConfig,\n bestMoment,\n scoreToBeat,\n i,\n currentScore,\n validFormatFound,\n bestFormatIsValid = false,\n configfLen = config._f.length;\n if (configfLen === 0) {\n getParsingFlags(config).invalidFormat = true;\n config._d = new Date(NaN);\n return;\n }\n for (i = 0; i < configfLen; i++) {\n currentScore = 0;\n validFormatFound = false;\n tempConfig = copyConfig({}, config);\n if (config._useUTC != null) {\n tempConfig._useUTC = config._useUTC;\n }\n tempConfig._f = config._f[i];\n configFromStringAndFormat(tempConfig);\n if (isValid(tempConfig)) {\n validFormatFound = true;\n }\n\n // if there is any input that was not parsed add a penalty for that format\n currentScore += getParsingFlags(tempConfig).charsLeftOver;\n\n //or tokens\n currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;\n getParsingFlags(tempConfig).score = currentScore;\n if (!bestFormatIsValid) {\n if (scoreToBeat == null || currentScore < scoreToBeat || validFormatFound) {\n scoreToBeat = currentScore;\n bestMoment = tempConfig;\n if (validFormatFound) {\n bestFormatIsValid = true;\n }\n }\n } else {\n if (currentScore < scoreToBeat) {\n scoreToBeat = currentScore;\n bestMoment = tempConfig;\n }\n }\n }\n extend(config, bestMoment || tempConfig);\n }\n function configFromObject(config) {\n if (config._d) {\n return;\n }\n var i = normalizeObjectUnits(config._i),\n dayOrDate = i.day === undefined ? i.date : i.day;\n config._a = map([i.year, i.month, dayOrDate, i.hour, i.minute, i.second, i.millisecond], function (obj) {\n return obj && parseInt(obj, 10);\n });\n configFromArray(config);\n }\n function createFromConfig(config) {\n var res = new Moment(checkOverflow(prepareConfig(config)));\n if (res._nextDay) {\n // Adding is smart enough around DST\n res.add(1, 'd');\n res._nextDay = undefined;\n }\n return res;\n }\n function prepareConfig(config) {\n var input = config._i,\n format = config._f;\n config._locale = config._locale || getLocale(config._l);\n if (input === null || format === undefined && input === '') {\n return createInvalid({\n nullInput: true\n });\n }\n if (typeof input === 'string') {\n config._i = input = config._locale.preparse(input);\n }\n if (isMoment(input)) {\n return new Moment(checkOverflow(input));\n } else if (isDate(input)) {\n config._d = input;\n } else if (isArray(format)) {\n configFromStringAndArray(config);\n } else if (format) {\n configFromStringAndFormat(config);\n } else {\n configFromInput(config);\n }\n if (!isValid(config)) {\n config._d = null;\n }\n return config;\n }\n function configFromInput(config) {\n var input = config._i;\n if (isUndefined(input)) {\n config._d = new Date(hooks.now());\n } else if (isDate(input)) {\n config._d = new Date(input.valueOf());\n } else if (typeof input === 'string') {\n configFromString(config);\n } else if (isArray(input)) {\n config._a = map(input.slice(0), function (obj) {\n return parseInt(obj, 10);\n });\n configFromArray(config);\n } else if (isObject(input)) {\n configFromObject(config);\n } else if (isNumber(input)) {\n // from milliseconds\n config._d = new Date(input);\n } else {\n hooks.createFromInputFallback(config);\n }\n }\n function createLocalOrUTC(input, format, locale, strict, isUTC) {\n var c = {};\n if (format === true || format === false) {\n strict = format;\n format = undefined;\n }\n if (locale === true || locale === false) {\n strict = locale;\n locale = undefined;\n }\n if (isObject(input) && isObjectEmpty(input) || isArray(input) && input.length === 0) {\n input = undefined;\n }\n // object construction must be done this way.\n // https://github.com/moment/moment/issues/1423\n c._isAMomentObject = true;\n c._useUTC = c._isUTC = isUTC;\n c._l = locale;\n c._i = input;\n c._f = format;\n c._strict = strict;\n return createFromConfig(c);\n }\n function createLocal(input, format, locale, strict) {\n return createLocalOrUTC(input, format, locale, strict, false);\n }\n var prototypeMin = deprecate('moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () {\n var other = createLocal.apply(null, arguments);\n if (this.isValid() && other.isValid()) {\n return other < this ? this : other;\n } else {\n return createInvalid();\n }\n }),\n prototypeMax = deprecate('moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () {\n var other = createLocal.apply(null, arguments);\n if (this.isValid() && other.isValid()) {\n return other > this ? this : other;\n } else {\n return createInvalid();\n }\n });\n\n // Pick a moment m from moments so that m[fn](other) is true for all\n // other. This relies on the function fn to be transitive.\n //\n // moments should either be an array of moment objects or an array, whose\n // first element is an array of moment objects.\n function pickBy(fn, moments) {\n var res, i;\n if (moments.length === 1 && isArray(moments[0])) {\n moments = moments[0];\n }\n if (!moments.length) {\n return createLocal();\n }\n res = moments[0];\n for (i = 1; i < moments.length; ++i) {\n if (!moments[i].isValid() || moments[i][fn](res)) {\n res = moments[i];\n }\n }\n return res;\n }\n\n // TODO: Use [].sort instead?\n function min() {\n var args = [].slice.call(arguments, 0);\n return pickBy('isBefore', args);\n }\n function max() {\n var args = [].slice.call(arguments, 0);\n return pickBy('isAfter', args);\n }\n var now = function () {\n return Date.now ? Date.now() : +new Date();\n };\n var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];\n function isDurationValid(m) {\n var key,\n unitHasDecimal = false,\n i,\n orderLen = ordering.length;\n for (key in m) {\n if (hasOwnProp(m, key) && !(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {\n return false;\n }\n }\n for (i = 0; i < orderLen; ++i) {\n if (m[ordering[i]]) {\n if (unitHasDecimal) {\n return false; // only allow non-integers for smallest unit\n }\n if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {\n unitHasDecimal = true;\n }\n }\n }\n return true;\n }\n function isValid$1() {\n return this._isValid;\n }\n function createInvalid$1() {\n return createDuration(NaN);\n }\n function Duration(duration) {\n var normalizedInput = normalizeObjectUnits(duration),\n years = normalizedInput.year || 0,\n quarters = normalizedInput.quarter || 0,\n months = normalizedInput.month || 0,\n weeks = normalizedInput.week || normalizedInput.isoWeek || 0,\n days = normalizedInput.day || 0,\n hours = normalizedInput.hour || 0,\n minutes = normalizedInput.minute || 0,\n seconds = normalizedInput.second || 0,\n milliseconds = normalizedInput.millisecond || 0;\n this._isValid = isDurationValid(normalizedInput);\n\n // representation for dateAddRemove\n this._milliseconds = +milliseconds + seconds * 1e3 +\n // 1000\n minutes * 6e4 +\n // 1000 * 60\n hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978\n // Because of dateAddRemove treats 24 hours as different from a\n // day when working around DST, we need to store them separately\n this._days = +days + weeks * 7;\n // It is impossible to translate months into days without knowing\n // which months you are are talking about, so we have to store\n // it separately.\n this._months = +months + quarters * 3 + years * 12;\n this._data = {};\n this._locale = getLocale();\n this._bubble();\n }\n function isDuration(obj) {\n return obj instanceof Duration;\n }\n function absRound(number) {\n if (number < 0) {\n return Math.round(-1 * number) * -1;\n } else {\n return Math.round(number);\n }\n }\n\n // compare two arrays, return the number of differences\n function compareArrays(array1, array2, dontConvert) {\n var len = Math.min(array1.length, array2.length),\n lengthDiff = Math.abs(array1.length - array2.length),\n diffs = 0,\n i;\n for (i = 0; i < len; i++) {\n if (dontConvert && array1[i] !== array2[i] || !dontConvert && toInt(array1[i]) !== toInt(array2[i])) {\n diffs++;\n }\n }\n return diffs + lengthDiff;\n }\n\n // FORMATTING\n\n function offset(token, separator) {\n addFormatToken(token, 0, 0, function () {\n var offset = this.utcOffset(),\n sign = '+';\n if (offset < 0) {\n offset = -offset;\n sign = '-';\n }\n return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~offset % 60, 2);\n });\n }\n offset('Z', ':');\n offset('ZZ', '');\n\n // PARSING\n\n addRegexToken('Z', matchShortOffset);\n addRegexToken('ZZ', matchShortOffset);\n addParseToken(['Z', 'ZZ'], function (input, array, config) {\n config._useUTC = true;\n config._tzm = offsetFromString(matchShortOffset, input);\n });\n\n // HELPERS\n\n // timezone chunker\n // '+10:00' > ['10', '00']\n // '-1530' > ['-15', '30']\n var chunkOffset = /([\\+\\-]|\\d\\d)/gi;\n function offsetFromString(matcher, string) {\n var matches = (string || '').match(matcher),\n chunk,\n parts,\n minutes;\n if (matches === null) {\n return null;\n }\n chunk = matches[matches.length - 1] || [];\n parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];\n minutes = +(parts[1] * 60) + toInt(parts[2]);\n return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes;\n }\n\n // Return a moment from input, that is local/utc/zone equivalent to model.\n function cloneWithOffset(input, model) {\n var res, diff;\n if (model._isUTC) {\n res = model.clone();\n diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();\n // Use low-level api, because this fn is low-level api.\n res._d.setTime(res._d.valueOf() + diff);\n hooks.updateOffset(res, false);\n return res;\n } else {\n return createLocal(input).local();\n }\n }\n function getDateOffset(m) {\n // On Firefox.24 Date#getTimezoneOffset returns a floating point.\n // https://github.com/moment/moment/pull/1871\n return -Math.round(m._d.getTimezoneOffset());\n }\n\n // HOOKS\n\n // This function will be called whenever a moment is mutated.\n // It is intended to keep the offset in sync with the timezone.\n hooks.updateOffset = function () {};\n\n // MOMENTS\n\n // keepLocalTime = true means only change the timezone, without\n // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->\n // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset\n // +0200, so we adjust the time as needed, to be valid.\n //\n // Keeping the time actually adds/subtracts (one hour)\n // from the actual represented time. That is why we call updateOffset\n // a second time. In case it wants us to change the offset again\n // _changeInProgress == true case, then we have to adjust, because\n // there is no such time in the given timezone.\n function getSetOffset(input, keepLocalTime, keepMinutes) {\n var offset = this._offset || 0,\n localAdjust;\n if (!this.isValid()) {\n return input != null ? this : NaN;\n }\n if (input != null) {\n if (typeof input === 'string') {\n input = offsetFromString(matchShortOffset, input);\n if (input === null) {\n return this;\n }\n } else if (Math.abs(input) < 16 && !keepMinutes) {\n input = input * 60;\n }\n if (!this._isUTC && keepLocalTime) {\n localAdjust = getDateOffset(this);\n }\n this._offset = input;\n this._isUTC = true;\n if (localAdjust != null) {\n this.add(localAdjust, 'm');\n }\n if (offset !== input) {\n if (!keepLocalTime || this._changeInProgress) {\n addSubtract(this, createDuration(input - offset, 'm'), 1, false);\n } else if (!this._changeInProgress) {\n this._changeInProgress = true;\n hooks.updateOffset(this, true);\n this._changeInProgress = null;\n }\n }\n return this;\n } else {\n return this._isUTC ? offset : getDateOffset(this);\n }\n }\n function getSetZone(input, keepLocalTime) {\n if (input != null) {\n if (typeof input !== 'string') {\n input = -input;\n }\n this.utcOffset(input, keepLocalTime);\n return this;\n } else {\n return -this.utcOffset();\n }\n }\n function setOffsetToUTC(keepLocalTime) {\n return this.utcOffset(0, keepLocalTime);\n }\n function setOffsetToLocal(keepLocalTime) {\n if (this._isUTC) {\n this.utcOffset(0, keepLocalTime);\n this._isUTC = false;\n if (keepLocalTime) {\n this.subtract(getDateOffset(this), 'm');\n }\n }\n return this;\n }\n function setOffsetToParsedOffset() {\n if (this._tzm != null) {\n this.utcOffset(this._tzm, false, true);\n } else if (typeof this._i === 'string') {\n var tZone = offsetFromString(matchOffset, this._i);\n if (tZone != null) {\n this.utcOffset(tZone);\n } else {\n this.utcOffset(0, true);\n }\n }\n return this;\n }\n function hasAlignedHourOffset(input) {\n if (!this.isValid()) {\n return false;\n }\n input = input ? createLocal(input).utcOffset() : 0;\n return (this.utcOffset() - input) % 60 === 0;\n }\n function isDaylightSavingTime() {\n return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset();\n }\n function isDaylightSavingTimeShifted() {\n if (!isUndefined(this._isDSTShifted)) {\n return this._isDSTShifted;\n }\n var c = {},\n other;\n copyConfig(c, this);\n c = prepareConfig(c);\n if (c._a) {\n other = c._isUTC ? createUTC(c._a) : createLocal(c._a);\n this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0;\n } else {\n this._isDSTShifted = false;\n }\n return this._isDSTShifted;\n }\n function isLocal() {\n return this.isValid() ? !this._isUTC : false;\n }\n function isUtcOffset() {\n return this.isValid() ? this._isUTC : false;\n }\n function isUtc() {\n return this.isValid() ? this._isUTC && this._offset === 0 : false;\n }\n\n // ASP.NET json date format regex\n var aspNetRegex = /^(-|\\+)?(?:(\\d*)[. ])?(\\d+):(\\d+)(?::(\\d+)(\\.\\d*)?)?$/,\n // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html\n // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere\n // and further modified to allow for strings containing both week and day\n isoRegex = /^(-|\\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;\n function createDuration(input, key) {\n var duration = input,\n // matching against regexp is expensive, do it on demand\n match = null,\n sign,\n ret,\n diffRes;\n if (isDuration(input)) {\n duration = {\n ms: input._milliseconds,\n d: input._days,\n M: input._months\n };\n } else if (isNumber(input) || !isNaN(+input)) {\n duration = {};\n if (key) {\n duration[key] = +input;\n } else {\n duration.milliseconds = +input;\n }\n } else if (match = aspNetRegex.exec(input)) {\n sign = match[1] === '-' ? -1 : 1;\n duration = {\n y: 0,\n d: toInt(match[DATE]) * sign,\n h: toInt(match[HOUR]) * sign,\n m: toInt(match[MINUTE]) * sign,\n s: toInt(match[SECOND]) * sign,\n ms: toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match\n };\n } else if (match = isoRegex.exec(input)) {\n sign = match[1] === '-' ? -1 : 1;\n duration = {\n y: parseIso(match[2], sign),\n M: parseIso(match[3], sign),\n w: parseIso(match[4], sign),\n d: parseIso(match[5], sign),\n h: parseIso(match[6], sign),\n m: parseIso(match[7], sign),\n s: parseIso(match[8], sign)\n };\n } else if (duration == null) {\n // checks for null or undefined\n duration = {};\n } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {\n diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));\n duration = {};\n duration.ms = diffRes.milliseconds;\n duration.M = diffRes.months;\n }\n ret = new Duration(duration);\n if (isDuration(input) && hasOwnProp(input, '_locale')) {\n ret._locale = input._locale;\n }\n if (isDuration(input) && hasOwnProp(input, '_isValid')) {\n ret._isValid = input._isValid;\n }\n return ret;\n }\n createDuration.fn = Duration.prototype;\n createDuration.invalid = createInvalid$1;\n function parseIso(inp, sign) {\n // We'd normally use ~~inp for this, but unfortunately it also\n // converts floats to ints.\n // inp may be undefined, so careful calling replace on it.\n var res = inp && parseFloat(inp.replace(',', '.'));\n // apply sign while we're at it\n return (isNaN(res) ? 0 : res) * sign;\n }\n function positiveMomentsDifference(base, other) {\n var res = {};\n res.months = other.month() - base.month() + (other.year() - base.year()) * 12;\n if (base.clone().add(res.months, 'M').isAfter(other)) {\n --res.months;\n }\n res.milliseconds = +other - +base.clone().add(res.months, 'M');\n return res;\n }\n function momentsDifference(base, other) {\n var res;\n if (!(base.isValid() && other.isValid())) {\n return {\n milliseconds: 0,\n months: 0\n };\n }\n other = cloneWithOffset(other, base);\n if (base.isBefore(other)) {\n res = positiveMomentsDifference(base, other);\n } else {\n res = positiveMomentsDifference(other, base);\n res.milliseconds = -res.milliseconds;\n res.months = -res.months;\n }\n return res;\n }\n\n // TODO: remove 'name' arg after deprecation is removed\n function createAdder(direction, name) {\n return function (val, period) {\n var dur, tmp;\n //invert the arguments, but complain about it\n if (period !== null && !isNaN(+period)) {\n deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');\n tmp = val;\n val = period;\n period = tmp;\n }\n dur = createDuration(val, period);\n addSubtract(this, dur, direction);\n return this;\n };\n }\n function addSubtract(mom, duration, isAdding, updateOffset) {\n var milliseconds = duration._milliseconds,\n days = absRound(duration._days),\n months = absRound(duration._months);\n if (!mom.isValid()) {\n // No op\n return;\n }\n updateOffset = updateOffset == null ? true : updateOffset;\n if (months) {\n setMonth(mom, get(mom, 'Month') + months * isAdding);\n }\n if (days) {\n set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);\n }\n if (milliseconds) {\n mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);\n }\n if (updateOffset) {\n hooks.updateOffset(mom, days || months);\n }\n }\n var add = createAdder(1, 'add'),\n subtract = createAdder(-1, 'subtract');\n function isString(input) {\n return typeof input === 'string' || input instanceof String;\n }\n\n // type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined\n function isMomentInput(input) {\n return isMoment(input) || isDate(input) || isString(input) || isNumber(input) || isNumberOrStringArray(input) || isMomentInputObject(input) || input === null || input === undefined;\n }\n function isMomentInputObject(input) {\n var objectTest = isObject(input) && !isObjectEmpty(input),\n propertyTest = false,\n properties = ['years', 'year', 'y', 'months', 'month', 'M', 'days', 'day', 'd', 'dates', 'date', 'D', 'hours', 'hour', 'h', 'minutes', 'minute', 'm', 'seconds', 'second', 's', 'milliseconds', 'millisecond', 'ms'],\n i,\n property,\n propertyLen = properties.length;\n for (i = 0; i < propertyLen; i += 1) {\n property = properties[i];\n propertyTest = propertyTest || hasOwnProp(input, property);\n }\n return objectTest && propertyTest;\n }\n function isNumberOrStringArray(input) {\n var arrayTest = isArray(input),\n dataTypeTest = false;\n if (arrayTest) {\n dataTypeTest = input.filter(function (item) {\n return !isNumber(item) && isString(input);\n }).length === 0;\n }\n return arrayTest && dataTypeTest;\n }\n function isCalendarSpec(input) {\n var objectTest = isObject(input) && !isObjectEmpty(input),\n propertyTest = false,\n properties = ['sameDay', 'nextDay', 'lastDay', 'nextWeek', 'lastWeek', 'sameElse'],\n i,\n property;\n for (i = 0; i < properties.length; i += 1) {\n property = properties[i];\n propertyTest = propertyTest || hasOwnProp(input, property);\n }\n return objectTest && propertyTest;\n }\n function getCalendarFormat(myMoment, now) {\n var diff = myMoment.diff(now, 'days', true);\n return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse';\n }\n function calendar$1(time, formats) {\n // Support for single parameter, formats only overload to the calendar function\n if (arguments.length === 1) {\n if (!arguments[0]) {\n time = undefined;\n formats = undefined;\n } else if (isMomentInput(arguments[0])) {\n time = arguments[0];\n formats = undefined;\n } else if (isCalendarSpec(arguments[0])) {\n formats = arguments[0];\n time = undefined;\n }\n }\n // We want to compare the start of today, vs this.\n // Getting start-of-today depends on whether we're local/utc/offset or not.\n var now = time || createLocal(),\n sod = cloneWithOffset(now, this).startOf('day'),\n format = hooks.calendarFormat(this, sod) || 'sameElse',\n output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);\n return this.format(output || this.localeData().calendar(format, this, createLocal(now)));\n }\n function clone() {\n return new Moment(this);\n }\n function isAfter(input, units) {\n var localInput = isMoment(input) ? input : createLocal(input);\n if (!(this.isValid() && localInput.isValid())) {\n return false;\n }\n units = normalizeUnits(units) || 'millisecond';\n if (units === 'millisecond') {\n return this.valueOf() > localInput.valueOf();\n } else {\n return localInput.valueOf() < this.clone().startOf(units).valueOf();\n }\n }\n function isBefore(input, units) {\n var localInput = isMoment(input) ? input : createLocal(input);\n if (!(this.isValid() && localInput.isValid())) {\n return false;\n }\n units = normalizeUnits(units) || 'millisecond';\n if (units === 'millisecond') {\n return this.valueOf() < localInput.valueOf();\n } else {\n return this.clone().endOf(units).valueOf() < localInput.valueOf();\n }\n }\n function isBetween(from, to, units, inclusivity) {\n var localFrom = isMoment(from) ? from : createLocal(from),\n localTo = isMoment(to) ? to : createLocal(to);\n if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) {\n return false;\n }\n inclusivity = inclusivity || '()';\n return (inclusivity[0] === '(' ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && (inclusivity[1] === ')' ? this.isBefore(localTo, units) : !this.isAfter(localTo, units));\n }\n function isSame(input, units) {\n var localInput = isMoment(input) ? input : createLocal(input),\n inputMs;\n if (!(this.isValid() && localInput.isValid())) {\n return false;\n }\n units = normalizeUnits(units) || 'millisecond';\n if (units === 'millisecond') {\n return this.valueOf() === localInput.valueOf();\n } else {\n inputMs = localInput.valueOf();\n return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();\n }\n }\n function isSameOrAfter(input, units) {\n return this.isSame(input, units) || this.isAfter(input, units);\n }\n function isSameOrBefore(input, units) {\n return this.isSame(input, units) || this.isBefore(input, units);\n }\n function diff(input, units, asFloat) {\n var that, zoneDelta, output;\n if (!this.isValid()) {\n return NaN;\n }\n that = cloneWithOffset(input, this);\n if (!that.isValid()) {\n return NaN;\n }\n zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;\n units = normalizeUnits(units);\n switch (units) {\n case 'year':\n output = monthDiff(this, that) / 12;\n break;\n case 'month':\n output = monthDiff(this, that);\n break;\n case 'quarter':\n output = monthDiff(this, that) / 3;\n break;\n case 'second':\n output = (this - that) / 1e3;\n break;\n // 1000\n case 'minute':\n output = (this - that) / 6e4;\n break;\n // 1000 * 60\n case 'hour':\n output = (this - that) / 36e5;\n break;\n // 1000 * 60 * 60\n case 'day':\n output = (this - that - zoneDelta) / 864e5;\n break;\n // 1000 * 60 * 60 * 24, negate dst\n case 'week':\n output = (this - that - zoneDelta) / 6048e5;\n break;\n // 1000 * 60 * 60 * 24 * 7, negate dst\n default:\n output = this - that;\n }\n return asFloat ? output : absFloor(output);\n }\n function monthDiff(a, b) {\n if (a.date() < b.date()) {\n // end-of-month calculations work correct when the start month has more\n // days than the end month.\n return -monthDiff(b, a);\n }\n // difference in months\n var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()),\n // b is in (anchor - 1 month, anchor + 1 month)\n anchor = a.clone().add(wholeMonthDiff, 'months'),\n anchor2,\n adjust;\n if (b - anchor < 0) {\n anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');\n // linear across the month\n adjust = (b - anchor) / (anchor - anchor2);\n } else {\n anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');\n // linear across the month\n adjust = (b - anchor) / (anchor2 - anchor);\n }\n\n //check for negative zero, return zero if negative zero\n return -(wholeMonthDiff + adjust) || 0;\n }\n hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';\n hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';\n function toString() {\n return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');\n }\n function toISOString(keepOffset) {\n if (!this.isValid()) {\n return null;\n }\n var utc = keepOffset !== true,\n m = utc ? this.clone().utc() : this;\n if (m.year() < 0 || m.year() > 9999) {\n return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ');\n }\n if (isFunction(Date.prototype.toISOString)) {\n // native implementation is ~50x faster, use it when we can\n if (utc) {\n return this.toDate().toISOString();\n } else {\n return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z'));\n }\n }\n return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ');\n }\n\n /**\n * Return a human readable representation of a moment that can\n * also be evaluated to get a new moment which is the same\n *\n * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects\n */\n function inspect() {\n if (!this.isValid()) {\n return 'moment.invalid(/* ' + this._i + ' */)';\n }\n var func = 'moment',\n zone = '',\n prefix,\n year,\n datetime,\n suffix;\n if (!this.isLocal()) {\n func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';\n zone = 'Z';\n }\n prefix = '[' + func + '(\"]';\n year = 0 <= this.year() && this.year() <= 9999 ? 'YYYY' : 'YYYYYY';\n datetime = '-MM-DD[T]HH:mm:ss.SSS';\n suffix = zone + '[\")]';\n return this.format(prefix + year + datetime + suffix);\n }\n function format(inputString) {\n if (!inputString) {\n inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;\n }\n var output = formatMoment(this, inputString);\n return this.localeData().postformat(output);\n }\n function from(time, withoutSuffix) {\n if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {\n return createDuration({\n to: this,\n from: time\n }).locale(this.locale()).humanize(!withoutSuffix);\n } else {\n return this.localeData().invalidDate();\n }\n }\n function fromNow(withoutSuffix) {\n return this.from(createLocal(), withoutSuffix);\n }\n function to(time, withoutSuffix) {\n if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) {\n return createDuration({\n from: this,\n to: time\n }).locale(this.locale()).humanize(!withoutSuffix);\n } else {\n return this.localeData().invalidDate();\n }\n }\n function toNow(withoutSuffix) {\n return this.to(createLocal(), withoutSuffix);\n }\n\n // If passed a locale key, it will set the locale for this\n // instance. Otherwise, it will return the locale configuration\n // variables for this instance.\n function locale(key) {\n var newLocaleData;\n if (key === undefined) {\n return this._locale._abbr;\n } else {\n newLocaleData = getLocale(key);\n if (newLocaleData != null) {\n this._locale = newLocaleData;\n }\n return this;\n }\n }\n var lang = deprecate('moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) {\n if (key === undefined) {\n return this.localeData();\n } else {\n return this.locale(key);\n }\n });\n function localeData() {\n return this._locale;\n }\n var MS_PER_SECOND = 1000,\n MS_PER_MINUTE = 60 * MS_PER_SECOND,\n MS_PER_HOUR = 60 * MS_PER_MINUTE,\n MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR;\n\n // actual modulo - handles negative numbers (for dates before 1970):\n function mod$1(dividend, divisor) {\n return (dividend % divisor + divisor) % divisor;\n }\n function localStartOfDate(y, m, d) {\n // the date constructor remaps years 0-99 to 1900-1999\n if (y < 100 && y >= 0) {\n // preserve leap years using a full 400 year cycle, then reset\n return new Date(y + 400, m, d) - MS_PER_400_YEARS;\n } else {\n return new Date(y, m, d).valueOf();\n }\n }\n function utcStartOfDate(y, m, d) {\n // Date.UTC remaps years 0-99 to 1900-1999\n if (y < 100 && y >= 0) {\n // preserve leap years using a full 400 year cycle, then reset\n return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS;\n } else {\n return Date.UTC(y, m, d);\n }\n }\n function startOf(units) {\n var time, startOfDate;\n units = normalizeUnits(units);\n if (units === undefined || units === 'millisecond' || !this.isValid()) {\n return this;\n }\n startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;\n switch (units) {\n case 'year':\n time = startOfDate(this.year(), 0, 1);\n break;\n case 'quarter':\n time = startOfDate(this.year(), this.month() - this.month() % 3, 1);\n break;\n case 'month':\n time = startOfDate(this.year(), this.month(), 1);\n break;\n case 'week':\n time = startOfDate(this.year(), this.month(), this.date() - this.weekday());\n break;\n case 'isoWeek':\n time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1));\n break;\n case 'day':\n case 'date':\n time = startOfDate(this.year(), this.month(), this.date());\n break;\n case 'hour':\n time = this._d.valueOf();\n time -= mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR);\n break;\n case 'minute':\n time = this._d.valueOf();\n time -= mod$1(time, MS_PER_MINUTE);\n break;\n case 'second':\n time = this._d.valueOf();\n time -= mod$1(time, MS_PER_SECOND);\n break;\n }\n this._d.setTime(time);\n hooks.updateOffset(this, true);\n return this;\n }\n function endOf(units) {\n var time, startOfDate;\n units = normalizeUnits(units);\n if (units === undefined || units === 'millisecond' || !this.isValid()) {\n return this;\n }\n startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate;\n switch (units) {\n case 'year':\n time = startOfDate(this.year() + 1, 0, 1) - 1;\n break;\n case 'quarter':\n time = startOfDate(this.year(), this.month() - this.month() % 3 + 3, 1) - 1;\n break;\n case 'month':\n time = startOfDate(this.year(), this.month() + 1, 1) - 1;\n break;\n case 'week':\n time = startOfDate(this.year(), this.month(), this.date() - this.weekday() + 7) - 1;\n break;\n case 'isoWeek':\n time = startOfDate(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1;\n break;\n case 'day':\n case 'date':\n time = startOfDate(this.year(), this.month(), this.date() + 1) - 1;\n break;\n case 'hour':\n time = this._d.valueOf();\n time += MS_PER_HOUR - mod$1(time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), MS_PER_HOUR) - 1;\n break;\n case 'minute':\n time = this._d.valueOf();\n time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1;\n break;\n case 'second':\n time = this._d.valueOf();\n time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1;\n break;\n }\n this._d.setTime(time);\n hooks.updateOffset(this, true);\n return this;\n }\n function valueOf() {\n return this._d.valueOf() - (this._offset || 0) * 60000;\n }\n function unix() {\n return Math.floor(this.valueOf() / 1000);\n }\n function toDate() {\n return new Date(this.valueOf());\n }\n function toArray() {\n var m = this;\n return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];\n }\n function toObject() {\n var m = this;\n return {\n years: m.year(),\n months: m.month(),\n date: m.date(),\n hours: m.hours(),\n minutes: m.minutes(),\n seconds: m.seconds(),\n milliseconds: m.milliseconds()\n };\n }\n function toJSON() {\n // new Date(NaN).toJSON() === null\n return this.isValid() ? this.toISOString() : null;\n }\n function isValid$2() {\n return isValid(this);\n }\n function parsingFlags() {\n return extend({}, getParsingFlags(this));\n }\n function invalidAt() {\n return getParsingFlags(this).overflow;\n }\n function creationData() {\n return {\n input: this._i,\n format: this._f,\n locale: this._locale,\n isUTC: this._isUTC,\n strict: this._strict\n };\n }\n addFormatToken('N', 0, 0, 'eraAbbr');\n addFormatToken('NN', 0, 0, 'eraAbbr');\n addFormatToken('NNN', 0, 0, 'eraAbbr');\n addFormatToken('NNNN', 0, 0, 'eraName');\n addFormatToken('NNNNN', 0, 0, 'eraNarrow');\n addFormatToken('y', ['y', 1], 'yo', 'eraYear');\n addFormatToken('y', ['yy', 2], 0, 'eraYear');\n addFormatToken('y', ['yyy', 3], 0, 'eraYear');\n addFormatToken('y', ['yyyy', 4], 0, 'eraYear');\n addRegexToken('N', matchEraAbbr);\n addRegexToken('NN', matchEraAbbr);\n addRegexToken('NNN', matchEraAbbr);\n addRegexToken('NNNN', matchEraName);\n addRegexToken('NNNNN', matchEraNarrow);\n addParseToken(['N', 'NN', 'NNN', 'NNNN', 'NNNNN'], function (input, array, config, token) {\n var era = config._locale.erasParse(input, token, config._strict);\n if (era) {\n getParsingFlags(config).era = era;\n } else {\n getParsingFlags(config).invalidEra = input;\n }\n });\n addRegexToken('y', matchUnsigned);\n addRegexToken('yy', matchUnsigned);\n addRegexToken('yyy', matchUnsigned);\n addRegexToken('yyyy', matchUnsigned);\n addRegexToken('yo', matchEraYearOrdinal);\n addParseToken(['y', 'yy', 'yyy', 'yyyy'], YEAR);\n addParseToken(['yo'], function (input, array, config, token) {\n var match;\n if (config._locale._eraYearOrdinalRegex) {\n match = input.match(config._locale._eraYearOrdinalRegex);\n }\n if (config._locale.eraYearOrdinalParse) {\n array[YEAR] = config._locale.eraYearOrdinalParse(input, match);\n } else {\n array[YEAR] = parseInt(input, 10);\n }\n });\n function localeEras(m, format) {\n var i,\n l,\n date,\n eras = this._eras || getLocale('en')._eras;\n for (i = 0, l = eras.length; i < l; ++i) {\n switch (typeof eras[i].since) {\n case 'string':\n // truncate time\n date = hooks(eras[i].since).startOf('day');\n eras[i].since = date.valueOf();\n break;\n }\n switch (typeof eras[i].until) {\n case 'undefined':\n eras[i].until = +Infinity;\n break;\n case 'string':\n // truncate time\n date = hooks(eras[i].until).startOf('day').valueOf();\n eras[i].until = date.valueOf();\n break;\n }\n }\n return eras;\n }\n function localeErasParse(eraName, format, strict) {\n var i,\n l,\n eras = this.eras(),\n name,\n abbr,\n narrow;\n eraName = eraName.toUpperCase();\n for (i = 0, l = eras.length; i < l; ++i) {\n name = eras[i].name.toUpperCase();\n abbr = eras[i].abbr.toUpperCase();\n narrow = eras[i].narrow.toUpperCase();\n if (strict) {\n switch (format) {\n case 'N':\n case 'NN':\n case 'NNN':\n if (abbr === eraName) {\n return eras[i];\n }\n break;\n case 'NNNN':\n if (name === eraName) {\n return eras[i];\n }\n break;\n case 'NNNNN':\n if (narrow === eraName) {\n return eras[i];\n }\n break;\n }\n } else if ([name, abbr, narrow].indexOf(eraName) >= 0) {\n return eras[i];\n }\n }\n }\n function localeErasConvertYear(era, year) {\n var dir = era.since <= era.until ? +1 : -1;\n if (year === undefined) {\n return hooks(era.since).year();\n } else {\n return hooks(era.since).year() + (year - era.offset) * dir;\n }\n }\n function getEraName() {\n var i,\n l,\n val,\n eras = this.localeData().eras();\n for (i = 0, l = eras.length; i < l; ++i) {\n // truncate time\n val = this.clone().startOf('day').valueOf();\n if (eras[i].since <= val && val <= eras[i].until) {\n return eras[i].name;\n }\n if (eras[i].until <= val && val <= eras[i].since) {\n return eras[i].name;\n }\n }\n return '';\n }\n function getEraNarrow() {\n var i,\n l,\n val,\n eras = this.localeData().eras();\n for (i = 0, l = eras.length; i < l; ++i) {\n // truncate time\n val = this.clone().startOf('day').valueOf();\n if (eras[i].since <= val && val <= eras[i].until) {\n return eras[i].narrow;\n }\n if (eras[i].until <= val && val <= eras[i].since) {\n return eras[i].narrow;\n }\n }\n return '';\n }\n function getEraAbbr() {\n var i,\n l,\n val,\n eras = this.localeData().eras();\n for (i = 0, l = eras.length; i < l; ++i) {\n // truncate time\n val = this.clone().startOf('day').valueOf();\n if (eras[i].since <= val && val <= eras[i].until) {\n return eras[i].abbr;\n }\n if (eras[i].until <= val && val <= eras[i].since) {\n return eras[i].abbr;\n }\n }\n return '';\n }\n function getEraYear() {\n var i,\n l,\n dir,\n val,\n eras = this.localeData().eras();\n for (i = 0, l = eras.length; i < l; ++i) {\n dir = eras[i].since <= eras[i].until ? +1 : -1;\n\n // truncate time\n val = this.clone().startOf('day').valueOf();\n if (eras[i].since <= val && val <= eras[i].until || eras[i].until <= val && val <= eras[i].since) {\n return (this.year() - hooks(eras[i].since).year()) * dir + eras[i].offset;\n }\n }\n return this.year();\n }\n function erasNameRegex(isStrict) {\n if (!hasOwnProp(this, '_erasNameRegex')) {\n computeErasParse.call(this);\n }\n return isStrict ? this._erasNameRegex : this._erasRegex;\n }\n function erasAbbrRegex(isStrict) {\n if (!hasOwnProp(this, '_erasAbbrRegex')) {\n computeErasParse.call(this);\n }\n return isStrict ? this._erasAbbrRegex : this._erasRegex;\n }\n function erasNarrowRegex(isStrict) {\n if (!hasOwnProp(this, '_erasNarrowRegex')) {\n computeErasParse.call(this);\n }\n return isStrict ? this._erasNarrowRegex : this._erasRegex;\n }\n function matchEraAbbr(isStrict, locale) {\n return locale.erasAbbrRegex(isStrict);\n }\n function matchEraName(isStrict, locale) {\n return locale.erasNameRegex(isStrict);\n }\n function matchEraNarrow(isStrict, locale) {\n return locale.erasNarrowRegex(isStrict);\n }\n function matchEraYearOrdinal(isStrict, locale) {\n return locale._eraYearOrdinalRegex || matchUnsigned;\n }\n function computeErasParse() {\n var abbrPieces = [],\n namePieces = [],\n narrowPieces = [],\n mixedPieces = [],\n i,\n l,\n erasName,\n erasAbbr,\n erasNarrow,\n eras = this.eras();\n for (i = 0, l = eras.length; i < l; ++i) {\n erasName = regexEscape(eras[i].name);\n erasAbbr = regexEscape(eras[i].abbr);\n erasNarrow = regexEscape(eras[i].narrow);\n namePieces.push(erasName);\n abbrPieces.push(erasAbbr);\n narrowPieces.push(erasNarrow);\n mixedPieces.push(erasName);\n mixedPieces.push(erasAbbr);\n mixedPieces.push(erasNarrow);\n }\n this._erasRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');\n this._erasNameRegex = new RegExp('^(' + namePieces.join('|') + ')', 'i');\n this._erasAbbrRegex = new RegExp('^(' + abbrPieces.join('|') + ')', 'i');\n this._erasNarrowRegex = new RegExp('^(' + narrowPieces.join('|') + ')', 'i');\n }\n\n // FORMATTING\n\n addFormatToken(0, ['gg', 2], 0, function () {\n return this.weekYear() % 100;\n });\n addFormatToken(0, ['GG', 2], 0, function () {\n return this.isoWeekYear() % 100;\n });\n function addWeekYearFormatToken(token, getter) {\n addFormatToken(0, [token, token.length], 0, getter);\n }\n addWeekYearFormatToken('gggg', 'weekYear');\n addWeekYearFormatToken('ggggg', 'weekYear');\n addWeekYearFormatToken('GGGG', 'isoWeekYear');\n addWeekYearFormatToken('GGGGG', 'isoWeekYear');\n\n // ALIASES\n\n // PARSING\n\n addRegexToken('G', matchSigned);\n addRegexToken('g', matchSigned);\n addRegexToken('GG', match1to2, match2);\n addRegexToken('gg', match1to2, match2);\n addRegexToken('GGGG', match1to4, match4);\n addRegexToken('gggg', match1to4, match4);\n addRegexToken('GGGGG', match1to6, match6);\n addRegexToken('ggggg', match1to6, match6);\n addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {\n week[token.substr(0, 2)] = toInt(input);\n });\n addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {\n week[token] = hooks.parseTwoDigitYear(input);\n });\n\n // MOMENTS\n\n function getSetWeekYear(input) {\n return getSetWeekYearHelper.call(this, input, this.week(), this.weekday() + this.localeData()._week.dow, this.localeData()._week.dow, this.localeData()._week.doy);\n }\n function getSetISOWeekYear(input) {\n return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4);\n }\n function getISOWeeksInYear() {\n return weeksInYear(this.year(), 1, 4);\n }\n function getISOWeeksInISOWeekYear() {\n return weeksInYear(this.isoWeekYear(), 1, 4);\n }\n function getWeeksInYear() {\n var weekInfo = this.localeData()._week;\n return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);\n }\n function getWeeksInWeekYear() {\n var weekInfo = this.localeData()._week;\n return weeksInYear(this.weekYear(), weekInfo.dow, weekInfo.doy);\n }\n function getSetWeekYearHelper(input, week, weekday, dow, doy) {\n var weeksTarget;\n if (input == null) {\n return weekOfYear(this, dow, doy).year;\n } else {\n weeksTarget = weeksInYear(input, dow, doy);\n if (week > weeksTarget) {\n week = weeksTarget;\n }\n return setWeekAll.call(this, input, week, weekday, dow, doy);\n }\n }\n function setWeekAll(weekYear, week, weekday, dow, doy) {\n var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),\n date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);\n this.year(date.getUTCFullYear());\n this.month(date.getUTCMonth());\n this.date(date.getUTCDate());\n return this;\n }\n\n // FORMATTING\n\n addFormatToken('Q', 0, 'Qo', 'quarter');\n\n // PARSING\n\n addRegexToken('Q', match1);\n addParseToken('Q', function (input, array) {\n array[MONTH] = (toInt(input) - 1) * 3;\n });\n\n // MOMENTS\n\n function getSetQuarter(input) {\n return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);\n }\n\n // FORMATTING\n\n addFormatToken('D', ['DD', 2], 'Do', 'date');\n\n // PARSING\n\n addRegexToken('D', match1to2, match1to2NoLeadingZero);\n addRegexToken('DD', match1to2, match2);\n addRegexToken('Do', function (isStrict, locale) {\n // TODO: Remove \"ordinalParse\" fallback in next major release.\n return isStrict ? locale._dayOfMonthOrdinalParse || locale._ordinalParse : locale._dayOfMonthOrdinalParseLenient;\n });\n addParseToken(['D', 'DD'], DATE);\n addParseToken('Do', function (input, array) {\n array[DATE] = toInt(input.match(match1to2)[0]);\n });\n\n // MOMENTS\n\n var getSetDayOfMonth = makeGetSet('Date', true);\n\n // FORMATTING\n\n addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');\n\n // PARSING\n\n addRegexToken('DDD', match1to3);\n addRegexToken('DDDD', match3);\n addParseToken(['DDD', 'DDDD'], function (input, array, config) {\n config._dayOfYear = toInt(input);\n });\n\n // HELPERS\n\n // MOMENTS\n\n function getSetDayOfYear(input) {\n var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;\n return input == null ? dayOfYear : this.add(input - dayOfYear, 'd');\n }\n\n // FORMATTING\n\n addFormatToken('m', ['mm', 2], 0, 'minute');\n\n // PARSING\n\n addRegexToken('m', match1to2, match1to2HasZero);\n addRegexToken('mm', match1to2, match2);\n addParseToken(['m', 'mm'], MINUTE);\n\n // MOMENTS\n\n var getSetMinute = makeGetSet('Minutes', false);\n\n // FORMATTING\n\n addFormatToken('s', ['ss', 2], 0, 'second');\n\n // PARSING\n\n addRegexToken('s', match1to2, match1to2HasZero);\n addRegexToken('ss', match1to2, match2);\n addParseToken(['s', 'ss'], SECOND);\n\n // MOMENTS\n\n var getSetSecond = makeGetSet('Seconds', false);\n\n // FORMATTING\n\n addFormatToken('S', 0, 0, function () {\n return ~~(this.millisecond() / 100);\n });\n addFormatToken(0, ['SS', 2], 0, function () {\n return ~~(this.millisecond() / 10);\n });\n addFormatToken(0, ['SSS', 3], 0, 'millisecond');\n addFormatToken(0, ['SSSS', 4], 0, function () {\n return this.millisecond() * 10;\n });\n addFormatToken(0, ['SSSSS', 5], 0, function () {\n return this.millisecond() * 100;\n });\n addFormatToken(0, ['SSSSSS', 6], 0, function () {\n return this.millisecond() * 1000;\n });\n addFormatToken(0, ['SSSSSSS', 7], 0, function () {\n return this.millisecond() * 10000;\n });\n addFormatToken(0, ['SSSSSSSS', 8], 0, function () {\n return this.millisecond() * 100000;\n });\n addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {\n return this.millisecond() * 1000000;\n });\n\n // PARSING\n\n addRegexToken('S', match1to3, match1);\n addRegexToken('SS', match1to3, match2);\n addRegexToken('SSS', match1to3, match3);\n var token, getSetMillisecond;\n for (token = 'SSSS'; token.length <= 9; token += 'S') {\n addRegexToken(token, matchUnsigned);\n }\n function parseMs(input, array) {\n array[MILLISECOND] = toInt(('0.' + input) * 1000);\n }\n for (token = 'S'; token.length <= 9; token += 'S') {\n addParseToken(token, parseMs);\n }\n getSetMillisecond = makeGetSet('Milliseconds', false);\n\n // FORMATTING\n\n addFormatToken('z', 0, 0, 'zoneAbbr');\n addFormatToken('zz', 0, 0, 'zoneName');\n\n // MOMENTS\n\n function getZoneAbbr() {\n return this._isUTC ? 'UTC' : '';\n }\n function getZoneName() {\n return this._isUTC ? 'Coordinated Universal Time' : '';\n }\n var proto = Moment.prototype;\n proto.add = add;\n proto.calendar = calendar$1;\n proto.clone = clone;\n proto.diff = diff;\n proto.endOf = endOf;\n proto.format = format;\n proto.from = from;\n proto.fromNow = fromNow;\n proto.to = to;\n proto.toNow = toNow;\n proto.get = stringGet;\n proto.invalidAt = invalidAt;\n proto.isAfter = isAfter;\n proto.isBefore = isBefore;\n proto.isBetween = isBetween;\n proto.isSame = isSame;\n proto.isSameOrAfter = isSameOrAfter;\n proto.isSameOrBefore = isSameOrBefore;\n proto.isValid = isValid$2;\n proto.lang = lang;\n proto.locale = locale;\n proto.localeData = localeData;\n proto.max = prototypeMax;\n proto.min = prototypeMin;\n proto.parsingFlags = parsingFlags;\n proto.set = stringSet;\n proto.startOf = startOf;\n proto.subtract = subtract;\n proto.toArray = toArray;\n proto.toObject = toObject;\n proto.toDate = toDate;\n proto.toISOString = toISOString;\n proto.inspect = inspect;\n if (typeof Symbol !== 'undefined' && Symbol.for != null) {\n proto[Symbol.for('nodejs.util.inspect.custom')] = function () {\n return 'Moment<' + this.format() + '>';\n };\n }\n proto.toJSON = toJSON;\n proto.toString = toString;\n proto.unix = unix;\n proto.valueOf = valueOf;\n proto.creationData = creationData;\n proto.eraName = getEraName;\n proto.eraNarrow = getEraNarrow;\n proto.eraAbbr = getEraAbbr;\n proto.eraYear = getEraYear;\n proto.year = getSetYear;\n proto.isLeapYear = getIsLeapYear;\n proto.weekYear = getSetWeekYear;\n proto.isoWeekYear = getSetISOWeekYear;\n proto.quarter = proto.quarters = getSetQuarter;\n proto.month = getSetMonth;\n proto.daysInMonth = getDaysInMonth;\n proto.week = proto.weeks = getSetWeek;\n proto.isoWeek = proto.isoWeeks = getSetISOWeek;\n proto.weeksInYear = getWeeksInYear;\n proto.weeksInWeekYear = getWeeksInWeekYear;\n proto.isoWeeksInYear = getISOWeeksInYear;\n proto.isoWeeksInISOWeekYear = getISOWeeksInISOWeekYear;\n proto.date = getSetDayOfMonth;\n proto.day = proto.days = getSetDayOfWeek;\n proto.weekday = getSetLocaleDayOfWeek;\n proto.isoWeekday = getSetISODayOfWeek;\n proto.dayOfYear = getSetDayOfYear;\n proto.hour = proto.hours = getSetHour;\n proto.minute = proto.minutes = getSetMinute;\n proto.second = proto.seconds = getSetSecond;\n proto.millisecond = proto.milliseconds = getSetMillisecond;\n proto.utcOffset = getSetOffset;\n proto.utc = setOffsetToUTC;\n proto.local = setOffsetToLocal;\n proto.parseZone = setOffsetToParsedOffset;\n proto.hasAlignedHourOffset = hasAlignedHourOffset;\n proto.isDST = isDaylightSavingTime;\n proto.isLocal = isLocal;\n proto.isUtcOffset = isUtcOffset;\n proto.isUtc = isUtc;\n proto.isUTC = isUtc;\n proto.zoneAbbr = getZoneAbbr;\n proto.zoneName = getZoneName;\n proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);\n proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);\n proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);\n proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);\n proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);\n function createUnix(input) {\n return createLocal(input * 1000);\n }\n function createInZone() {\n return createLocal.apply(null, arguments).parseZone();\n }\n function preParsePostFormat(string) {\n return string;\n }\n var proto$1 = Locale.prototype;\n proto$1.calendar = calendar;\n proto$1.longDateFormat = longDateFormat;\n proto$1.invalidDate = invalidDate;\n proto$1.ordinal = ordinal;\n proto$1.preparse = preParsePostFormat;\n proto$1.postformat = preParsePostFormat;\n proto$1.relativeTime = relativeTime;\n proto$1.pastFuture = pastFuture;\n proto$1.set = set;\n proto$1.eras = localeEras;\n proto$1.erasParse = localeErasParse;\n proto$1.erasConvertYear = localeErasConvertYear;\n proto$1.erasAbbrRegex = erasAbbrRegex;\n proto$1.erasNameRegex = erasNameRegex;\n proto$1.erasNarrowRegex = erasNarrowRegex;\n proto$1.months = localeMonths;\n proto$1.monthsShort = localeMonthsShort;\n proto$1.monthsParse = localeMonthsParse;\n proto$1.monthsRegex = monthsRegex;\n proto$1.monthsShortRegex = monthsShortRegex;\n proto$1.week = localeWeek;\n proto$1.firstDayOfYear = localeFirstDayOfYear;\n proto$1.firstDayOfWeek = localeFirstDayOfWeek;\n proto$1.weekdays = localeWeekdays;\n proto$1.weekdaysMin = localeWeekdaysMin;\n proto$1.weekdaysShort = localeWeekdaysShort;\n proto$1.weekdaysParse = localeWeekdaysParse;\n proto$1.weekdaysRegex = weekdaysRegex;\n proto$1.weekdaysShortRegex = weekdaysShortRegex;\n proto$1.weekdaysMinRegex = weekdaysMinRegex;\n proto$1.isPM = localeIsPM;\n proto$1.meridiem = localeMeridiem;\n function get$1(format, index, field, setter) {\n var locale = getLocale(),\n utc = createUTC().set(setter, index);\n return locale[field](utc, format);\n }\n function listMonthsImpl(format, index, field) {\n if (isNumber(format)) {\n index = format;\n format = undefined;\n }\n format = format || '';\n if (index != null) {\n return get$1(format, index, field, 'month');\n }\n var i,\n out = [];\n for (i = 0; i < 12; i++) {\n out[i] = get$1(format, i, field, 'month');\n }\n return out;\n }\n\n // ()\n // (5)\n // (fmt, 5)\n // (fmt)\n // (true)\n // (true, 5)\n // (true, fmt, 5)\n // (true, fmt)\n function listWeekdaysImpl(localeSorted, format, index, field) {\n if (typeof localeSorted === 'boolean') {\n if (isNumber(format)) {\n index = format;\n format = undefined;\n }\n format = format || '';\n } else {\n format = localeSorted;\n index = format;\n localeSorted = false;\n if (isNumber(format)) {\n index = format;\n format = undefined;\n }\n format = format || '';\n }\n var locale = getLocale(),\n shift = localeSorted ? locale._week.dow : 0,\n i,\n out = [];\n if (index != null) {\n return get$1(format, (index + shift) % 7, field, 'day');\n }\n for (i = 0; i < 7; i++) {\n out[i] = get$1(format, (i + shift) % 7, field, 'day');\n }\n return out;\n }\n function listMonths(format, index) {\n return listMonthsImpl(format, index, 'months');\n }\n function listMonthsShort(format, index) {\n return listMonthsImpl(format, index, 'monthsShort');\n }\n function listWeekdays(localeSorted, format, index) {\n return listWeekdaysImpl(localeSorted, format, index, 'weekdays');\n }\n function listWeekdaysShort(localeSorted, format, index) {\n return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');\n }\n function listWeekdaysMin(localeSorted, format, index) {\n return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');\n }\n getSetGlobalLocale('en', {\n eras: [{\n since: '0001-01-01',\n until: +Infinity,\n offset: 1,\n name: 'Anno Domini',\n narrow: 'AD',\n abbr: 'AD'\n }, {\n since: '0000-12-31',\n until: -Infinity,\n offset: 1,\n name: 'Before Christ',\n narrow: 'BC',\n abbr: 'BC'\n }],\n dayOfMonthOrdinalParse: /\\d{1,2}(th|st|nd|rd)/,\n ordinal: function (number) {\n var b = number % 10,\n output = toInt(number % 100 / 10) === 1 ? 'th' : b === 1 ? 'st' : b === 2 ? 'nd' : b === 3 ? 'rd' : 'th';\n return number + output;\n }\n });\n\n // Side effect imports\n\n hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);\n hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);\n var mathAbs = Math.abs;\n function abs() {\n var data = this._data;\n this._milliseconds = mathAbs(this._milliseconds);\n this._days = mathAbs(this._days);\n this._months = mathAbs(this._months);\n data.milliseconds = mathAbs(data.milliseconds);\n data.seconds = mathAbs(data.seconds);\n data.minutes = mathAbs(data.minutes);\n data.hours = mathAbs(data.hours);\n data.months = mathAbs(data.months);\n data.years = mathAbs(data.years);\n return this;\n }\n function addSubtract$1(duration, input, value, direction) {\n var other = createDuration(input, value);\n duration._milliseconds += direction * other._milliseconds;\n duration._days += direction * other._days;\n duration._months += direction * other._months;\n return duration._bubble();\n }\n\n // supports only 2.0-style add(1, 's') or add(duration)\n function add$1(input, value) {\n return addSubtract$1(this, input, value, 1);\n }\n\n // supports only 2.0-style subtract(1, 's') or subtract(duration)\n function subtract$1(input, value) {\n return addSubtract$1(this, input, value, -1);\n }\n function absCeil(number) {\n if (number < 0) {\n return Math.floor(number);\n } else {\n return Math.ceil(number);\n }\n }\n function bubble() {\n var milliseconds = this._milliseconds,\n days = this._days,\n months = this._months,\n data = this._data,\n seconds,\n minutes,\n hours,\n years,\n monthsFromDays;\n\n // if we have a mix of positive and negative values, bubble down first\n // check: https://github.com/moment/moment/issues/2166\n if (!(milliseconds >= 0 && days >= 0 && months >= 0 || milliseconds <= 0 && days <= 0 && months <= 0)) {\n milliseconds += absCeil(monthsToDays(months) + days) * 864e5;\n days = 0;\n months = 0;\n }\n\n // The following code bubbles up values, see the tests for\n // examples of what that means.\n data.milliseconds = milliseconds % 1000;\n seconds = absFloor(milliseconds / 1000);\n data.seconds = seconds % 60;\n minutes = absFloor(seconds / 60);\n data.minutes = minutes % 60;\n hours = absFloor(minutes / 60);\n data.hours = hours % 24;\n days += absFloor(hours / 24);\n\n // convert days to months\n monthsFromDays = absFloor(daysToMonths(days));\n months += monthsFromDays;\n days -= absCeil(monthsToDays(monthsFromDays));\n\n // 12 months -> 1 year\n years = absFloor(months / 12);\n months %= 12;\n data.days = days;\n data.months = months;\n data.years = years;\n return this;\n }\n function daysToMonths(days) {\n // 400 years have 146097 days (taking into account leap year rules)\n // 400 years have 12 months === 4800\n return days * 4800 / 146097;\n }\n function monthsToDays(months) {\n // the reverse of daysToMonths\n return months * 146097 / 4800;\n }\n function as(units) {\n if (!this.isValid()) {\n return NaN;\n }\n var days,\n months,\n milliseconds = this._milliseconds;\n units = normalizeUnits(units);\n if (units === 'month' || units === 'quarter' || units === 'year') {\n days = this._days + milliseconds / 864e5;\n months = this._months + daysToMonths(days);\n switch (units) {\n case 'month':\n return months;\n case 'quarter':\n return months / 3;\n case 'year':\n return months / 12;\n }\n } else {\n // handle milliseconds separately because of floating point math errors (issue #1867)\n days = this._days + Math.round(monthsToDays(this._months));\n switch (units) {\n case 'week':\n return days / 7 + milliseconds / 6048e5;\n case 'day':\n return days + milliseconds / 864e5;\n case 'hour':\n return days * 24 + milliseconds / 36e5;\n case 'minute':\n return days * 1440 + milliseconds / 6e4;\n case 'second':\n return days * 86400 + milliseconds / 1000;\n // Math.floor prevents floating point math errors here\n case 'millisecond':\n return Math.floor(days * 864e5) + milliseconds;\n default:\n throw new Error('Unknown unit ' + units);\n }\n }\n }\n function makeAs(alias) {\n return function () {\n return this.as(alias);\n };\n }\n var asMilliseconds = makeAs('ms'),\n asSeconds = makeAs('s'),\n asMinutes = makeAs('m'),\n asHours = makeAs('h'),\n asDays = makeAs('d'),\n asWeeks = makeAs('w'),\n asMonths = makeAs('M'),\n asQuarters = makeAs('Q'),\n asYears = makeAs('y'),\n valueOf$1 = asMilliseconds;\n function clone$1() {\n return createDuration(this);\n }\n function get$2(units) {\n units = normalizeUnits(units);\n return this.isValid() ? this[units + 's']() : NaN;\n }\n function makeGetter(name) {\n return function () {\n return this.isValid() ? this._data[name] : NaN;\n };\n }\n var milliseconds = makeGetter('milliseconds'),\n seconds = makeGetter('seconds'),\n minutes = makeGetter('minutes'),\n hours = makeGetter('hours'),\n days = makeGetter('days'),\n months = makeGetter('months'),\n years = makeGetter('years');\n function weeks() {\n return absFloor(this.days() / 7);\n }\n var round = Math.round,\n thresholds = {\n ss: 44,\n // a few seconds to seconds\n s: 45,\n // seconds to minute\n m: 45,\n // minutes to hour\n h: 22,\n // hours to day\n d: 26,\n // days to month/week\n w: null,\n // weeks to month\n M: 11 // months to year\n };\n\n // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize\n function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {\n return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);\n }\n function relativeTime$1(posNegDuration, withoutSuffix, thresholds, locale) {\n var duration = createDuration(posNegDuration).abs(),\n seconds = round(duration.as('s')),\n minutes = round(duration.as('m')),\n hours = round(duration.as('h')),\n days = round(duration.as('d')),\n months = round(duration.as('M')),\n weeks = round(duration.as('w')),\n years = round(duration.as('y')),\n a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days];\n if (thresholds.w != null) {\n a = a || weeks <= 1 && ['w'] || weeks < thresholds.w && ['ww', weeks];\n }\n a = a || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years];\n a[2] = withoutSuffix;\n a[3] = +posNegDuration > 0;\n a[4] = locale;\n return substituteTimeAgo.apply(null, a);\n }\n\n // This function allows you to set the rounding function for relative time strings\n function getSetRelativeTimeRounding(roundingFunction) {\n if (roundingFunction === undefined) {\n return round;\n }\n if (typeof roundingFunction === 'function') {\n round = roundingFunction;\n return true;\n }\n return false;\n }\n\n // This function allows you to set a threshold for relative time strings\n function getSetRelativeTimeThreshold(threshold, limit) {\n if (thresholds[threshold] === undefined) {\n return false;\n }\n if (limit === undefined) {\n return thresholds[threshold];\n }\n thresholds[threshold] = limit;\n if (threshold === 's') {\n thresholds.ss = limit - 1;\n }\n return true;\n }\n function humanize(argWithSuffix, argThresholds) {\n if (!this.isValid()) {\n return this.localeData().invalidDate();\n }\n var withSuffix = false,\n th = thresholds,\n locale,\n output;\n if (typeof argWithSuffix === 'object') {\n argThresholds = argWithSuffix;\n argWithSuffix = false;\n }\n if (typeof argWithSuffix === 'boolean') {\n withSuffix = argWithSuffix;\n }\n if (typeof argThresholds === 'object') {\n th = Object.assign({}, thresholds, argThresholds);\n if (argThresholds.s != null && argThresholds.ss == null) {\n th.ss = argThresholds.s - 1;\n }\n }\n locale = this.localeData();\n output = relativeTime$1(this, !withSuffix, th, locale);\n if (withSuffix) {\n output = locale.pastFuture(+this, output);\n }\n return locale.postformat(output);\n }\n var abs$1 = Math.abs;\n function sign(x) {\n return (x > 0) - (x < 0) || +x;\n }\n function toISOString$1() {\n // for ISO strings we do not use the normal bubbling rules:\n // * milliseconds bubble up until they become hours\n // * days do not bubble at all\n // * months bubble up until they become years\n // This is because there is no context-free conversion between hours and days\n // (think of clock changes)\n // and also not between days and months (28-31 days per month)\n if (!this.isValid()) {\n return this.localeData().invalidDate();\n }\n var seconds = abs$1(this._milliseconds) / 1000,\n days = abs$1(this._days),\n months = abs$1(this._months),\n minutes,\n hours,\n years,\n s,\n total = this.asSeconds(),\n totalSign,\n ymSign,\n daysSign,\n hmsSign;\n if (!total) {\n // this is the same as C#'s (Noda) and python (isodate)...\n // but not other JS (goog.date)\n return 'P0D';\n }\n\n // 3600 seconds -> 60 minutes -> 1 hour\n minutes = absFloor(seconds / 60);\n hours = absFloor(minutes / 60);\n seconds %= 60;\n minutes %= 60;\n\n // 12 months -> 1 year\n years = absFloor(months / 12);\n months %= 12;\n\n // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js\n s = seconds ? seconds.toFixed(3).replace(/\\.?0+$/, '') : '';\n totalSign = total < 0 ? '-' : '';\n ymSign = sign(this._months) !== sign(total) ? '-' : '';\n daysSign = sign(this._days) !== sign(total) ? '-' : '';\n hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';\n return totalSign + 'P' + (years ? ymSign + years + 'Y' : '') + (months ? ymSign + months + 'M' : '') + (days ? daysSign + days + 'D' : '') + (hours || minutes || seconds ? 'T' : '') + (hours ? hmsSign + hours + 'H' : '') + (minutes ? hmsSign + minutes + 'M' : '') + (seconds ? hmsSign + s + 'S' : '');\n }\n var proto$2 = Duration.prototype;\n proto$2.isValid = isValid$1;\n proto$2.abs = abs;\n proto$2.add = add$1;\n proto$2.subtract = subtract$1;\n proto$2.as = as;\n proto$2.asMilliseconds = asMilliseconds;\n proto$2.asSeconds = asSeconds;\n proto$2.asMinutes = asMinutes;\n proto$2.asHours = asHours;\n proto$2.asDays = asDays;\n proto$2.asWeeks = asWeeks;\n proto$2.asMonths = asMonths;\n proto$2.asQuarters = asQuarters;\n proto$2.asYears = asYears;\n proto$2.valueOf = valueOf$1;\n proto$2._bubble = bubble;\n proto$2.clone = clone$1;\n proto$2.get = get$2;\n proto$2.milliseconds = milliseconds;\n proto$2.seconds = seconds;\n proto$2.minutes = minutes;\n proto$2.hours = hours;\n proto$2.days = days;\n proto$2.weeks = weeks;\n proto$2.months = months;\n proto$2.years = years;\n proto$2.humanize = humanize;\n proto$2.toISOString = toISOString$1;\n proto$2.toString = toISOString$1;\n proto$2.toJSON = toISOString$1;\n proto$2.locale = locale;\n proto$2.localeData = localeData;\n proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);\n proto$2.lang = lang;\n\n // FORMATTING\n\n addFormatToken('X', 0, 0, 'unix');\n addFormatToken('x', 0, 0, 'valueOf');\n\n // PARSING\n\n addRegexToken('x', matchSigned);\n addRegexToken('X', matchTimestamp);\n addParseToken('X', function (input, array, config) {\n config._d = new Date(parseFloat(input) * 1000);\n });\n addParseToken('x', function (input, array, config) {\n config._d = new Date(toInt(input));\n });\n\n //! moment.js\n\n hooks.version = '2.30.1';\n setHookCallback(createLocal);\n hooks.fn = proto;\n hooks.min = min;\n hooks.max = max;\n hooks.now = now;\n hooks.utc = createUTC;\n hooks.unix = createUnix;\n hooks.months = listMonths;\n hooks.isDate = isDate;\n hooks.locale = getSetGlobalLocale;\n hooks.invalid = createInvalid;\n hooks.duration = createDuration;\n hooks.isMoment = isMoment;\n hooks.weekdays = listWeekdays;\n hooks.parseZone = createInZone;\n hooks.localeData = getLocale;\n hooks.isDuration = isDuration;\n hooks.monthsShort = listMonthsShort;\n hooks.weekdaysMin = listWeekdaysMin;\n hooks.defineLocale = defineLocale;\n hooks.updateLocale = updateLocale;\n hooks.locales = listLocales;\n hooks.weekdaysShort = listWeekdaysShort;\n hooks.normalizeUnits = normalizeUnits;\n hooks.relativeTimeRounding = getSetRelativeTimeRounding;\n hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;\n hooks.calendarFormat = getCalendarFormat;\n hooks.prototype = proto;\n\n // currently HTML5 input type only supports 24-hour formats\n hooks.HTML5_FMT = {\n DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm',\n // \n DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss',\n // \n DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS',\n // \n DATE: 'YYYY-MM-DD',\n // \n TIME: 'HH:mm',\n // \n TIME_SECONDS: 'HH:mm:ss',\n // \n TIME_MS: 'HH:mm:ss.SSS',\n // \n WEEK: 'GGGG-[W]WW',\n // \n MONTH: 'YYYY-MM' // \n };\n return hooks;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory();\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([], factory);\n } else {\n // Global (browser)\n root.CryptoJS = factory();\n }\n})(this, function () {\n /*globals window, global, require*/\n\n /**\n * CryptoJS core components.\n */\n var CryptoJS = CryptoJS || function (Math, undefined) {\n var crypto;\n\n // Native crypto from window (Browser)\n if (typeof window !== 'undefined' && window.crypto) {\n crypto = window.crypto;\n }\n\n // Native crypto in web worker (Browser)\n if (typeof self !== 'undefined' && self.crypto) {\n crypto = self.crypto;\n }\n\n // Native crypto from worker\n if (typeof globalThis !== 'undefined' && globalThis.crypto) {\n crypto = globalThis.crypto;\n }\n\n // Native (experimental IE 11) crypto from window (Browser)\n if (!crypto && typeof window !== 'undefined' && window.msCrypto) {\n crypto = window.msCrypto;\n }\n\n // Native crypto from global (NodeJS)\n if (!crypto && typeof global !== 'undefined' && global.crypto) {\n crypto = global.crypto;\n }\n\n // Native crypto import via require (NodeJS)\n if (!crypto && typeof require === 'function') {\n try {\n crypto = require('crypto');\n } catch (err) {}\n }\n\n /*\n * Cryptographically secure pseudorandom number generator\n *\n * As Math.random() is cryptographically not safe to use\n */\n var cryptoSecureRandomInt = function () {\n if (crypto) {\n // Use getRandomValues method (Browser)\n if (typeof crypto.getRandomValues === 'function') {\n try {\n return crypto.getRandomValues(new Uint32Array(1))[0];\n } catch (err) {}\n }\n\n // Use randomBytes method (NodeJS)\n if (typeof crypto.randomBytes === 'function') {\n try {\n return crypto.randomBytes(4).readInt32LE();\n } catch (err) {}\n }\n }\n throw new Error('Native crypto module could not be used to get secure random number.');\n };\n\n /*\n * Local polyfill of Object.create\n */\n var create = Object.create || function () {\n function F() {}\n return function (obj) {\n var subtype;\n F.prototype = obj;\n subtype = new F();\n F.prototype = null;\n return subtype;\n };\n }();\n\n /**\n * CryptoJS namespace.\n */\n var C = {};\n\n /**\n * Library namespace.\n */\n var C_lib = C.lib = {};\n\n /**\n * Base object for prototypal inheritance.\n */\n var Base = C_lib.Base = function () {\n return {\n /**\n * Creates a new object that inherits from this object.\n *\n * @param {Object} overrides Properties to copy into the new object.\n *\n * @return {Object} The new object.\n *\n * @static\n *\n * @example\n *\n * var MyType = CryptoJS.lib.Base.extend({\n * field: 'value',\n *\n * method: function () {\n * }\n * });\n */\n extend: function (overrides) {\n // Spawn\n var subtype = create(this);\n\n // Augment\n if (overrides) {\n subtype.mixIn(overrides);\n }\n\n // Create default initializer\n if (!subtype.hasOwnProperty('init') || this.init === subtype.init) {\n subtype.init = function () {\n subtype.$super.init.apply(this, arguments);\n };\n }\n\n // Initializer's prototype is the subtype object\n subtype.init.prototype = subtype;\n\n // Reference supertype\n subtype.$super = this;\n return subtype;\n },\n /**\n * Extends this object and runs the init method.\n * Arguments to create() will be passed to init().\n *\n * @return {Object} The new object.\n *\n * @static\n *\n * @example\n *\n * var instance = MyType.create();\n */\n create: function () {\n var instance = this.extend();\n instance.init.apply(instance, arguments);\n return instance;\n },\n /**\n * Initializes a newly created object.\n * Override this method to add some logic when your objects are created.\n *\n * @example\n *\n * var MyType = CryptoJS.lib.Base.extend({\n * init: function () {\n * // ...\n * }\n * });\n */\n init: function () {},\n /**\n * Copies properties into this object.\n *\n * @param {Object} properties The properties to mix in.\n *\n * @example\n *\n * MyType.mixIn({\n * field: 'value'\n * });\n */\n mixIn: function (properties) {\n for (var propertyName in properties) {\n if (properties.hasOwnProperty(propertyName)) {\n this[propertyName] = properties[propertyName];\n }\n }\n\n // IE won't copy toString using the loop above\n if (properties.hasOwnProperty('toString')) {\n this.toString = properties.toString;\n }\n },\n /**\n * Creates a copy of this object.\n *\n * @return {Object} The clone.\n *\n * @example\n *\n * var clone = instance.clone();\n */\n clone: function () {\n return this.init.prototype.extend(this);\n }\n };\n }();\n\n /**\n * An array of 32-bit words.\n *\n * @property {Array} words The array of 32-bit words.\n * @property {number} sigBytes The number of significant bytes in this word array.\n */\n var WordArray = C_lib.WordArray = Base.extend({\n /**\n * Initializes a newly created word array.\n *\n * @param {Array} words (Optional) An array of 32-bit words.\n * @param {number} sigBytes (Optional) The number of significant bytes in the words.\n *\n * @example\n *\n * var wordArray = CryptoJS.lib.WordArray.create();\n * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);\n * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);\n */\n init: function (words, sigBytes) {\n words = this.words = words || [];\n if (sigBytes != undefined) {\n this.sigBytes = sigBytes;\n } else {\n this.sigBytes = words.length * 4;\n }\n },\n /**\n * Converts this word array to a string.\n *\n * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex\n *\n * @return {string} The stringified word array.\n *\n * @example\n *\n * var string = wordArray + '';\n * var string = wordArray.toString();\n * var string = wordArray.toString(CryptoJS.enc.Utf8);\n */\n toString: function (encoder) {\n return (encoder || Hex).stringify(this);\n },\n /**\n * Concatenates a word array to this word array.\n *\n * @param {WordArray} wordArray The word array to append.\n *\n * @return {WordArray} This word array.\n *\n * @example\n *\n * wordArray1.concat(wordArray2);\n */\n concat: function (wordArray) {\n // Shortcuts\n var thisWords = this.words;\n var thatWords = wordArray.words;\n var thisSigBytes = this.sigBytes;\n var thatSigBytes = wordArray.sigBytes;\n\n // Clamp excess bits\n this.clamp();\n\n // Concat\n if (thisSigBytes % 4) {\n // Copy one byte at a time\n for (var i = 0; i < thatSigBytes; i++) {\n var thatByte = thatWords[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;\n thisWords[thisSigBytes + i >>> 2] |= thatByte << 24 - (thisSigBytes + i) % 4 * 8;\n }\n } else {\n // Copy one word at a time\n for (var j = 0; j < thatSigBytes; j += 4) {\n thisWords[thisSigBytes + j >>> 2] = thatWords[j >>> 2];\n }\n }\n this.sigBytes += thatSigBytes;\n\n // Chainable\n return this;\n },\n /**\n * Removes insignificant bits.\n *\n * @example\n *\n * wordArray.clamp();\n */\n clamp: function () {\n // Shortcuts\n var words = this.words;\n var sigBytes = this.sigBytes;\n\n // Clamp\n words[sigBytes >>> 2] &= 0xffffffff << 32 - sigBytes % 4 * 8;\n words.length = Math.ceil(sigBytes / 4);\n },\n /**\n * Creates a copy of this word array.\n *\n * @return {WordArray} The clone.\n *\n * @example\n *\n * var clone = wordArray.clone();\n */\n clone: function () {\n var clone = Base.clone.call(this);\n clone.words = this.words.slice(0);\n return clone;\n },\n /**\n * Creates a word array filled with random bytes.\n *\n * @param {number} nBytes The number of random bytes to generate.\n *\n * @return {WordArray} The random word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.lib.WordArray.random(16);\n */\n random: function (nBytes) {\n var words = [];\n for (var i = 0; i < nBytes; i += 4) {\n words.push(cryptoSecureRandomInt());\n }\n return new WordArray.init(words, nBytes);\n }\n });\n\n /**\n * Encoder namespace.\n */\n var C_enc = C.enc = {};\n\n /**\n * Hex encoding strategy.\n */\n var Hex = C_enc.Hex = {\n /**\n * Converts a word array to a hex string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The hex string.\n *\n * @static\n *\n * @example\n *\n * var hexString = CryptoJS.enc.Hex.stringify(wordArray);\n */\n stringify: function (wordArray) {\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n\n // Convert\n var hexChars = [];\n for (var i = 0; i < sigBytes; i++) {\n var bite = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;\n hexChars.push((bite >>> 4).toString(16));\n hexChars.push((bite & 0x0f).toString(16));\n }\n return hexChars.join('');\n },\n /**\n * Converts a hex string to a word array.\n *\n * @param {string} hexStr The hex string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Hex.parse(hexString);\n */\n parse: function (hexStr) {\n // Shortcut\n var hexStrLength = hexStr.length;\n\n // Convert\n var words = [];\n for (var i = 0; i < hexStrLength; i += 2) {\n words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << 24 - i % 8 * 4;\n }\n return new WordArray.init(words, hexStrLength / 2);\n }\n };\n\n /**\n * Latin1 encoding strategy.\n */\n var Latin1 = C_enc.Latin1 = {\n /**\n * Converts a word array to a Latin1 string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The Latin1 string.\n *\n * @static\n *\n * @example\n *\n * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);\n */\n stringify: function (wordArray) {\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n\n // Convert\n var latin1Chars = [];\n for (var i = 0; i < sigBytes; i++) {\n var bite = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;\n latin1Chars.push(String.fromCharCode(bite));\n }\n return latin1Chars.join('');\n },\n /**\n * Converts a Latin1 string to a word array.\n *\n * @param {string} latin1Str The Latin1 string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Latin1.parse(latin1String);\n */\n parse: function (latin1Str) {\n // Shortcut\n var latin1StrLength = latin1Str.length;\n\n // Convert\n var words = [];\n for (var i = 0; i < latin1StrLength; i++) {\n words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << 24 - i % 4 * 8;\n }\n return new WordArray.init(words, latin1StrLength);\n }\n };\n\n /**\n * UTF-8 encoding strategy.\n */\n var Utf8 = C_enc.Utf8 = {\n /**\n * Converts a word array to a UTF-8 string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The UTF-8 string.\n *\n * @static\n *\n * @example\n *\n * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);\n */\n stringify: function (wordArray) {\n try {\n return decodeURIComponent(escape(Latin1.stringify(wordArray)));\n } catch (e) {\n throw new Error('Malformed UTF-8 data');\n }\n },\n /**\n * Converts a UTF-8 string to a word array.\n *\n * @param {string} utf8Str The UTF-8 string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Utf8.parse(utf8String);\n */\n parse: function (utf8Str) {\n return Latin1.parse(unescape(encodeURIComponent(utf8Str)));\n }\n };\n\n /**\n * Abstract buffered block algorithm template.\n *\n * The property blockSize must be implemented in a concrete subtype.\n *\n * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0\n */\n var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({\n /**\n * Resets this block algorithm's data buffer to its initial state.\n *\n * @example\n *\n * bufferedBlockAlgorithm.reset();\n */\n reset: function () {\n // Initial values\n this._data = new WordArray.init();\n this._nDataBytes = 0;\n },\n /**\n * Adds new data to this block algorithm's buffer.\n *\n * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.\n *\n * @example\n *\n * bufferedBlockAlgorithm._append('data');\n * bufferedBlockAlgorithm._append(wordArray);\n */\n _append: function (data) {\n // Convert string to WordArray, else assume WordArray already\n if (typeof data == 'string') {\n data = Utf8.parse(data);\n }\n\n // Append\n this._data.concat(data);\n this._nDataBytes += data.sigBytes;\n },\n /**\n * Processes available data blocks.\n *\n * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.\n *\n * @param {boolean} doFlush Whether all blocks and partial blocks should be processed.\n *\n * @return {WordArray} The processed data.\n *\n * @example\n *\n * var processedData = bufferedBlockAlgorithm._process();\n * var processedData = bufferedBlockAlgorithm._process(!!'flush');\n */\n _process: function (doFlush) {\n var processedWords;\n\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var dataSigBytes = data.sigBytes;\n var blockSize = this.blockSize;\n var blockSizeBytes = blockSize * 4;\n\n // Count blocks ready\n var nBlocksReady = dataSigBytes / blockSizeBytes;\n if (doFlush) {\n // Round up to include partial blocks\n nBlocksReady = Math.ceil(nBlocksReady);\n } else {\n // Round down to include only full blocks,\n // less the number of blocks that must remain in the buffer\n nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);\n }\n\n // Count words ready\n var nWordsReady = nBlocksReady * blockSize;\n\n // Count bytes ready\n var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);\n\n // Process blocks\n if (nWordsReady) {\n for (var offset = 0; offset < nWordsReady; offset += blockSize) {\n // Perform concrete-algorithm logic\n this._doProcessBlock(dataWords, offset);\n }\n\n // Remove processed words\n processedWords = dataWords.splice(0, nWordsReady);\n data.sigBytes -= nBytesReady;\n }\n\n // Return processed words\n return new WordArray.init(processedWords, nBytesReady);\n },\n /**\n * Creates a copy of this object.\n *\n * @return {Object} The clone.\n *\n * @example\n *\n * var clone = bufferedBlockAlgorithm.clone();\n */\n clone: function () {\n var clone = Base.clone.call(this);\n clone._data = this._data.clone();\n return clone;\n },\n _minBufferSize: 0\n });\n\n /**\n * Abstract hasher template.\n *\n * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)\n */\n var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({\n /**\n * Configuration options.\n */\n cfg: Base.extend(),\n /**\n * Initializes a newly created hasher.\n *\n * @param {Object} cfg (Optional) The configuration options to use for this hash computation.\n *\n * @example\n *\n * var hasher = CryptoJS.algo.SHA256.create();\n */\n init: function (cfg) {\n // Apply config defaults\n this.cfg = this.cfg.extend(cfg);\n\n // Set initial values\n this.reset();\n },\n /**\n * Resets this hasher to its initial state.\n *\n * @example\n *\n * hasher.reset();\n */\n reset: function () {\n // Reset data buffer\n BufferedBlockAlgorithm.reset.call(this);\n\n // Perform concrete-hasher logic\n this._doReset();\n },\n /**\n * Updates this hasher with a message.\n *\n * @param {WordArray|string} messageUpdate The message to append.\n *\n * @return {Hasher} This hasher.\n *\n * @example\n *\n * hasher.update('message');\n * hasher.update(wordArray);\n */\n update: function (messageUpdate) {\n // Append\n this._append(messageUpdate);\n\n // Update the hash\n this._process();\n\n // Chainable\n return this;\n },\n /**\n * Finalizes the hash computation.\n * Note that the finalize operation is effectively a destructive, read-once operation.\n *\n * @param {WordArray|string} messageUpdate (Optional) A final message update.\n *\n * @return {WordArray} The hash.\n *\n * @example\n *\n * var hash = hasher.finalize();\n * var hash = hasher.finalize('message');\n * var hash = hasher.finalize(wordArray);\n */\n finalize: function (messageUpdate) {\n // Final message update\n if (messageUpdate) {\n this._append(messageUpdate);\n }\n\n // Perform concrete-hasher logic\n var hash = this._doFinalize();\n return hash;\n },\n blockSize: 512 / 32,\n /**\n * Creates a shortcut function to a hasher's object interface.\n *\n * @param {Hasher} hasher The hasher to create a helper for.\n *\n * @return {Function} The shortcut function.\n *\n * @static\n *\n * @example\n *\n * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);\n */\n _createHelper: function (hasher) {\n return function (message, cfg) {\n return new hasher.init(cfg).finalize(message);\n };\n },\n /**\n * Creates a shortcut function to the HMAC's object interface.\n *\n * @param {Hasher} hasher The hasher to use in this HMAC helper.\n *\n * @return {Function} The shortcut function.\n *\n * @static\n *\n * @example\n *\n * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);\n */\n _createHmacHelper: function (hasher) {\n return function (message, key) {\n return new C_algo.HMAC.init(hasher, key).finalize(message);\n };\n }\n });\n\n /**\n * Algorithm namespace.\n */\n var C_algo = C.algo = {};\n return C;\n }(Math);\n return CryptoJS;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function (undefined) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Base = C_lib.Base;\n var X32WordArray = C_lib.WordArray;\n\n /**\n * x64 namespace.\n */\n var C_x64 = C.x64 = {};\n\n /**\n * A 64-bit word.\n */\n var X64Word = C_x64.Word = Base.extend({\n /**\n * Initializes a newly created 64-bit word.\n *\n * @param {number} high The high 32 bits.\n * @param {number} low The low 32 bits.\n *\n * @example\n *\n * var x64Word = CryptoJS.x64.Word.create(0x00010203, 0x04050607);\n */\n init: function (high, low) {\n this.high = high;\n this.low = low;\n }\n\n /**\n * Bitwise NOTs this word.\n *\n * @return {X64Word} A new x64-Word object after negating.\n *\n * @example\n *\n * var negated = x64Word.not();\n */\n // not: function () {\n // var high = ~this.high;\n // var low = ~this.low;\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Bitwise ANDs this word with the passed word.\n *\n * @param {X64Word} word The x64-Word to AND with this word.\n *\n * @return {X64Word} A new x64-Word object after ANDing.\n *\n * @example\n *\n * var anded = x64Word.and(anotherX64Word);\n */\n // and: function (word) {\n // var high = this.high & word.high;\n // var low = this.low & word.low;\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Bitwise ORs this word with the passed word.\n *\n * @param {X64Word} word The x64-Word to OR with this word.\n *\n * @return {X64Word} A new x64-Word object after ORing.\n *\n * @example\n *\n * var ored = x64Word.or(anotherX64Word);\n */\n // or: function (word) {\n // var high = this.high | word.high;\n // var low = this.low | word.low;\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Bitwise XORs this word with the passed word.\n *\n * @param {X64Word} word The x64-Word to XOR with this word.\n *\n * @return {X64Word} A new x64-Word object after XORing.\n *\n * @example\n *\n * var xored = x64Word.xor(anotherX64Word);\n */\n // xor: function (word) {\n // var high = this.high ^ word.high;\n // var low = this.low ^ word.low;\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Shifts this word n bits to the left.\n *\n * @param {number} n The number of bits to shift.\n *\n * @return {X64Word} A new x64-Word object after shifting.\n *\n * @example\n *\n * var shifted = x64Word.shiftL(25);\n */\n // shiftL: function (n) {\n // if (n < 32) {\n // var high = (this.high << n) | (this.low >>> (32 - n));\n // var low = this.low << n;\n // } else {\n // var high = this.low << (n - 32);\n // var low = 0;\n // }\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Shifts this word n bits to the right.\n *\n * @param {number} n The number of bits to shift.\n *\n * @return {X64Word} A new x64-Word object after shifting.\n *\n * @example\n *\n * var shifted = x64Word.shiftR(7);\n */\n // shiftR: function (n) {\n // if (n < 32) {\n // var low = (this.low >>> n) | (this.high << (32 - n));\n // var high = this.high >>> n;\n // } else {\n // var low = this.high >>> (n - 32);\n // var high = 0;\n // }\n\n // return X64Word.create(high, low);\n // },\n\n /**\n * Rotates this word n bits to the left.\n *\n * @param {number} n The number of bits to rotate.\n *\n * @return {X64Word} A new x64-Word object after rotating.\n *\n * @example\n *\n * var rotated = x64Word.rotL(25);\n */\n // rotL: function (n) {\n // return this.shiftL(n).or(this.shiftR(64 - n));\n // },\n\n /**\n * Rotates this word n bits to the right.\n *\n * @param {number} n The number of bits to rotate.\n *\n * @return {X64Word} A new x64-Word object after rotating.\n *\n * @example\n *\n * var rotated = x64Word.rotR(7);\n */\n // rotR: function (n) {\n // return this.shiftR(n).or(this.shiftL(64 - n));\n // },\n\n /**\n * Adds this word with the passed word.\n *\n * @param {X64Word} word The x64-Word to add with this word.\n *\n * @return {X64Word} A new x64-Word object after adding.\n *\n * @example\n *\n * var added = x64Word.add(anotherX64Word);\n */\n // add: function (word) {\n // var low = (this.low + word.low) | 0;\n // var carry = (low >>> 0) < (this.low >>> 0) ? 1 : 0;\n // var high = (this.high + word.high + carry) | 0;\n\n // return X64Word.create(high, low);\n // }\n });\n\n /**\n * An array of 64-bit words.\n *\n * @property {Array} words The array of CryptoJS.x64.Word objects.\n * @property {number} sigBytes The number of significant bytes in this word array.\n */\n var X64WordArray = C_x64.WordArray = Base.extend({\n /**\n * Initializes a newly created word array.\n *\n * @param {Array} words (Optional) An array of CryptoJS.x64.Word objects.\n * @param {number} sigBytes (Optional) The number of significant bytes in the words.\n *\n * @example\n *\n * var wordArray = CryptoJS.x64.WordArray.create();\n *\n * var wordArray = CryptoJS.x64.WordArray.create([\n * CryptoJS.x64.Word.create(0x00010203, 0x04050607),\n * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)\n * ]);\n *\n * var wordArray = CryptoJS.x64.WordArray.create([\n * CryptoJS.x64.Word.create(0x00010203, 0x04050607),\n * CryptoJS.x64.Word.create(0x18191a1b, 0x1c1d1e1f)\n * ], 10);\n */\n init: function (words, sigBytes) {\n words = this.words = words || [];\n if (sigBytes != undefined) {\n this.sigBytes = sigBytes;\n } else {\n this.sigBytes = words.length * 8;\n }\n },\n /**\n * Converts this 64-bit word array to a 32-bit word array.\n *\n * @return {CryptoJS.lib.WordArray} This word array's data as a 32-bit word array.\n *\n * @example\n *\n * var x32WordArray = x64WordArray.toX32();\n */\n toX32: function () {\n // Shortcuts\n var x64Words = this.words;\n var x64WordsLength = x64Words.length;\n\n // Convert\n var x32Words = [];\n for (var i = 0; i < x64WordsLength; i++) {\n var x64Word = x64Words[i];\n x32Words.push(x64Word.high);\n x32Words.push(x64Word.low);\n }\n return X32WordArray.create(x32Words, this.sigBytes);\n },\n /**\n * Creates a copy of this word array.\n *\n * @return {X64WordArray} The clone.\n *\n * @example\n *\n * var clone = x64WordArray.clone();\n */\n clone: function () {\n var clone = Base.clone.call(this);\n\n // Clone \"words\" array\n var words = clone.words = this.words.slice(0);\n\n // Clone each X64Word object\n var wordsLength = words.length;\n for (var i = 0; i < wordsLength; i++) {\n words[i] = words[i].clone();\n }\n return clone;\n }\n });\n })();\n return CryptoJS;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Check if typed arrays are supported\n if (typeof ArrayBuffer != 'function') {\n return;\n }\n\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n\n // Reference original init\n var superInit = WordArray.init;\n\n // Augment WordArray.init to handle typed arrays\n var subInit = WordArray.init = function (typedArray) {\n // Convert buffers to uint8\n if (typedArray instanceof ArrayBuffer) {\n typedArray = new Uint8Array(typedArray);\n }\n\n // Convert other array views to uint8\n if (typedArray instanceof Int8Array || typeof Uint8ClampedArray !== \"undefined\" && typedArray instanceof Uint8ClampedArray || typedArray instanceof Int16Array || typedArray instanceof Uint16Array || typedArray instanceof Int32Array || typedArray instanceof Uint32Array || typedArray instanceof Float32Array || typedArray instanceof Float64Array) {\n typedArray = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);\n }\n\n // Handle Uint8Array\n if (typedArray instanceof Uint8Array) {\n // Shortcut\n var typedArrayByteLength = typedArray.byteLength;\n\n // Extract bytes\n var words = [];\n for (var i = 0; i < typedArrayByteLength; i++) {\n words[i >>> 2] |= typedArray[i] << 24 - i % 4 * 8;\n }\n\n // Initialize this word array\n superInit.call(this, words, typedArrayByteLength);\n } else {\n // Else call normal init\n superInit.apply(this, arguments);\n }\n };\n subInit.prototype = WordArray;\n })();\n return CryptoJS.lib.WordArray;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var C_enc = C.enc;\n\n /**\n * UTF-16 BE encoding strategy.\n */\n var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = {\n /**\n * Converts a word array to a UTF-16 BE string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The UTF-16 BE string.\n *\n * @static\n *\n * @example\n *\n * var utf16String = CryptoJS.enc.Utf16.stringify(wordArray);\n */\n stringify: function (wordArray) {\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n\n // Convert\n var utf16Chars = [];\n for (var i = 0; i < sigBytes; i += 2) {\n var codePoint = words[i >>> 2] >>> 16 - i % 4 * 8 & 0xffff;\n utf16Chars.push(String.fromCharCode(codePoint));\n }\n return utf16Chars.join('');\n },\n /**\n * Converts a UTF-16 BE string to a word array.\n *\n * @param {string} utf16Str The UTF-16 BE string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Utf16.parse(utf16String);\n */\n parse: function (utf16Str) {\n // Shortcut\n var utf16StrLength = utf16Str.length;\n\n // Convert\n var words = [];\n for (var i = 0; i < utf16StrLength; i++) {\n words[i >>> 1] |= utf16Str.charCodeAt(i) << 16 - i % 2 * 16;\n }\n return WordArray.create(words, utf16StrLength * 2);\n }\n };\n\n /**\n * UTF-16 LE encoding strategy.\n */\n C_enc.Utf16LE = {\n /**\n * Converts a word array to a UTF-16 LE string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The UTF-16 LE string.\n *\n * @static\n *\n * @example\n *\n * var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray);\n */\n stringify: function (wordArray) {\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n\n // Convert\n var utf16Chars = [];\n for (var i = 0; i < sigBytes; i += 2) {\n var codePoint = swapEndian(words[i >>> 2] >>> 16 - i % 4 * 8 & 0xffff);\n utf16Chars.push(String.fromCharCode(codePoint));\n }\n return utf16Chars.join('');\n },\n /**\n * Converts a UTF-16 LE string to a word array.\n *\n * @param {string} utf16Str The UTF-16 LE string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str);\n */\n parse: function (utf16Str) {\n // Shortcut\n var utf16StrLength = utf16Str.length;\n\n // Convert\n var words = [];\n for (var i = 0; i < utf16StrLength; i++) {\n words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << 16 - i % 2 * 16);\n }\n return WordArray.create(words, utf16StrLength * 2);\n }\n };\n function swapEndian(word) {\n return word << 8 & 0xff00ff00 | word >>> 8 & 0x00ff00ff;\n }\n })();\n return CryptoJS.enc.Utf16;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var C_enc = C.enc;\n\n /**\n * Base64 encoding strategy.\n */\n var Base64 = C_enc.Base64 = {\n /**\n * Converts a word array to a Base64 string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @return {string} The Base64 string.\n *\n * @static\n *\n * @example\n *\n * var base64String = CryptoJS.enc.Base64.stringify(wordArray);\n */\n stringify: function (wordArray) {\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n var map = this._map;\n\n // Clamp excess bits\n wordArray.clamp();\n\n // Convert\n var base64Chars = [];\n for (var i = 0; i < sigBytes; i += 3) {\n var byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;\n var byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 0xff;\n var byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 0xff;\n var triplet = byte1 << 16 | byte2 << 8 | byte3;\n for (var j = 0; j < 4 && i + j * 0.75 < sigBytes; j++) {\n base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 0x3f));\n }\n }\n\n // Add padding\n var paddingChar = map.charAt(64);\n if (paddingChar) {\n while (base64Chars.length % 4) {\n base64Chars.push(paddingChar);\n }\n }\n return base64Chars.join('');\n },\n /**\n * Converts a Base64 string to a word array.\n *\n * @param {string} base64Str The Base64 string.\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Base64.parse(base64String);\n */\n parse: function (base64Str) {\n // Shortcuts\n var base64StrLength = base64Str.length;\n var map = this._map;\n var reverseMap = this._reverseMap;\n if (!reverseMap) {\n reverseMap = this._reverseMap = [];\n for (var j = 0; j < map.length; j++) {\n reverseMap[map.charCodeAt(j)] = j;\n }\n }\n\n // Ignore padding\n var paddingChar = map.charAt(64);\n if (paddingChar) {\n var paddingIndex = base64Str.indexOf(paddingChar);\n if (paddingIndex !== -1) {\n base64StrLength = paddingIndex;\n }\n }\n\n // Convert\n return parseLoop(base64Str, base64StrLength, reverseMap);\n },\n _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='\n };\n function parseLoop(base64Str, base64StrLength, reverseMap) {\n var words = [];\n var nBytes = 0;\n for (var i = 0; i < base64StrLength; i++) {\n if (i % 4) {\n var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << i % 4 * 2;\n var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> 6 - i % 4 * 2;\n var bitsCombined = bits1 | bits2;\n words[nBytes >>> 2] |= bitsCombined << 24 - nBytes % 4 * 8;\n nBytes++;\n }\n }\n return WordArray.create(words, nBytes);\n }\n })();\n return CryptoJS.enc.Base64;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var C_enc = C.enc;\n\n /**\n * Base64url encoding strategy.\n */\n var Base64url = C_enc.Base64url = {\n /**\n * Converts a word array to a Base64url string.\n *\n * @param {WordArray} wordArray The word array.\n *\n * @param {boolean} urlSafe Whether to use url safe\n *\n * @return {string} The Base64url string.\n *\n * @static\n *\n * @example\n *\n * var base64String = CryptoJS.enc.Base64url.stringify(wordArray);\n */\n stringify: function (wordArray, urlSafe) {\n if (urlSafe === undefined) {\n urlSafe = true;\n }\n // Shortcuts\n var words = wordArray.words;\n var sigBytes = wordArray.sigBytes;\n var map = urlSafe ? this._safe_map : this._map;\n\n // Clamp excess bits\n wordArray.clamp();\n\n // Convert\n var base64Chars = [];\n for (var i = 0; i < sigBytes; i += 3) {\n var byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;\n var byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 0xff;\n var byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 0xff;\n var triplet = byte1 << 16 | byte2 << 8 | byte3;\n for (var j = 0; j < 4 && i + j * 0.75 < sigBytes; j++) {\n base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 0x3f));\n }\n }\n\n // Add padding\n var paddingChar = map.charAt(64);\n if (paddingChar) {\n while (base64Chars.length % 4) {\n base64Chars.push(paddingChar);\n }\n }\n return base64Chars.join('');\n },\n /**\n * Converts a Base64url string to a word array.\n *\n * @param {string} base64Str The Base64url string.\n *\n * @param {boolean} urlSafe Whether to use url safe\n *\n * @return {WordArray} The word array.\n *\n * @static\n *\n * @example\n *\n * var wordArray = CryptoJS.enc.Base64url.parse(base64String);\n */\n parse: function (base64Str, urlSafe) {\n if (urlSafe === undefined) {\n urlSafe = true;\n }\n\n // Shortcuts\n var base64StrLength = base64Str.length;\n var map = urlSafe ? this._safe_map : this._map;\n var reverseMap = this._reverseMap;\n if (!reverseMap) {\n reverseMap = this._reverseMap = [];\n for (var j = 0; j < map.length; j++) {\n reverseMap[map.charCodeAt(j)] = j;\n }\n }\n\n // Ignore padding\n var paddingChar = map.charAt(64);\n if (paddingChar) {\n var paddingIndex = base64Str.indexOf(paddingChar);\n if (paddingIndex !== -1) {\n base64StrLength = paddingIndex;\n }\n }\n\n // Convert\n return parseLoop(base64Str, base64StrLength, reverseMap);\n },\n _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',\n _safe_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'\n };\n function parseLoop(base64Str, base64StrLength, reverseMap) {\n var words = [];\n var nBytes = 0;\n for (var i = 0; i < base64StrLength; i++) {\n if (i % 4) {\n var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << i % 4 * 2;\n var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> 6 - i % 4 * 2;\n var bitsCombined = bits1 | bits2;\n words[nBytes >>> 2] |= bitsCombined << 24 - nBytes % 4 * 8;\n nBytes++;\n }\n }\n return WordArray.create(words, nBytes);\n }\n })();\n return CryptoJS.enc.Base64url;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function (Math) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var Hasher = C_lib.Hasher;\n var C_algo = C.algo;\n\n // Constants table\n var T = [];\n\n // Compute constants\n (function () {\n for (var i = 0; i < 64; i++) {\n T[i] = Math.abs(Math.sin(i + 1)) * 0x100000000 | 0;\n }\n })();\n\n /**\n * MD5 hash algorithm.\n */\n var MD5 = C_algo.MD5 = Hasher.extend({\n _doReset: function () {\n this._hash = new WordArray.init([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]);\n },\n _doProcessBlock: function (M, offset) {\n // Swap endian\n for (var i = 0; i < 16; i++) {\n // Shortcuts\n var offset_i = offset + i;\n var M_offset_i = M[offset_i];\n M[offset_i] = (M_offset_i << 8 | M_offset_i >>> 24) & 0x00ff00ff | (M_offset_i << 24 | M_offset_i >>> 8) & 0xff00ff00;\n }\n\n // Shortcuts\n var H = this._hash.words;\n var M_offset_0 = M[offset + 0];\n var M_offset_1 = M[offset + 1];\n var M_offset_2 = M[offset + 2];\n var M_offset_3 = M[offset + 3];\n var M_offset_4 = M[offset + 4];\n var M_offset_5 = M[offset + 5];\n var M_offset_6 = M[offset + 6];\n var M_offset_7 = M[offset + 7];\n var M_offset_8 = M[offset + 8];\n var M_offset_9 = M[offset + 9];\n var M_offset_10 = M[offset + 10];\n var M_offset_11 = M[offset + 11];\n var M_offset_12 = M[offset + 12];\n var M_offset_13 = M[offset + 13];\n var M_offset_14 = M[offset + 14];\n var M_offset_15 = M[offset + 15];\n\n // Working variables\n var a = H[0];\n var b = H[1];\n var c = H[2];\n var d = H[3];\n\n // Computation\n a = FF(a, b, c, d, M_offset_0, 7, T[0]);\n d = FF(d, a, b, c, M_offset_1, 12, T[1]);\n c = FF(c, d, a, b, M_offset_2, 17, T[2]);\n b = FF(b, c, d, a, M_offset_3, 22, T[3]);\n a = FF(a, b, c, d, M_offset_4, 7, T[4]);\n d = FF(d, a, b, c, M_offset_5, 12, T[5]);\n c = FF(c, d, a, b, M_offset_6, 17, T[6]);\n b = FF(b, c, d, a, M_offset_7, 22, T[7]);\n a = FF(a, b, c, d, M_offset_8, 7, T[8]);\n d = FF(d, a, b, c, M_offset_9, 12, T[9]);\n c = FF(c, d, a, b, M_offset_10, 17, T[10]);\n b = FF(b, c, d, a, M_offset_11, 22, T[11]);\n a = FF(a, b, c, d, M_offset_12, 7, T[12]);\n d = FF(d, a, b, c, M_offset_13, 12, T[13]);\n c = FF(c, d, a, b, M_offset_14, 17, T[14]);\n b = FF(b, c, d, a, M_offset_15, 22, T[15]);\n a = GG(a, b, c, d, M_offset_1, 5, T[16]);\n d = GG(d, a, b, c, M_offset_6, 9, T[17]);\n c = GG(c, d, a, b, M_offset_11, 14, T[18]);\n b = GG(b, c, d, a, M_offset_0, 20, T[19]);\n a = GG(a, b, c, d, M_offset_5, 5, T[20]);\n d = GG(d, a, b, c, M_offset_10, 9, T[21]);\n c = GG(c, d, a, b, M_offset_15, 14, T[22]);\n b = GG(b, c, d, a, M_offset_4, 20, T[23]);\n a = GG(a, b, c, d, M_offset_9, 5, T[24]);\n d = GG(d, a, b, c, M_offset_14, 9, T[25]);\n c = GG(c, d, a, b, M_offset_3, 14, T[26]);\n b = GG(b, c, d, a, M_offset_8, 20, T[27]);\n a = GG(a, b, c, d, M_offset_13, 5, T[28]);\n d = GG(d, a, b, c, M_offset_2, 9, T[29]);\n c = GG(c, d, a, b, M_offset_7, 14, T[30]);\n b = GG(b, c, d, a, M_offset_12, 20, T[31]);\n a = HH(a, b, c, d, M_offset_5, 4, T[32]);\n d = HH(d, a, b, c, M_offset_8, 11, T[33]);\n c = HH(c, d, a, b, M_offset_11, 16, T[34]);\n b = HH(b, c, d, a, M_offset_14, 23, T[35]);\n a = HH(a, b, c, d, M_offset_1, 4, T[36]);\n d = HH(d, a, b, c, M_offset_4, 11, T[37]);\n c = HH(c, d, a, b, M_offset_7, 16, T[38]);\n b = HH(b, c, d, a, M_offset_10, 23, T[39]);\n a = HH(a, b, c, d, M_offset_13, 4, T[40]);\n d = HH(d, a, b, c, M_offset_0, 11, T[41]);\n c = HH(c, d, a, b, M_offset_3, 16, T[42]);\n b = HH(b, c, d, a, M_offset_6, 23, T[43]);\n a = HH(a, b, c, d, M_offset_9, 4, T[44]);\n d = HH(d, a, b, c, M_offset_12, 11, T[45]);\n c = HH(c, d, a, b, M_offset_15, 16, T[46]);\n b = HH(b, c, d, a, M_offset_2, 23, T[47]);\n a = II(a, b, c, d, M_offset_0, 6, T[48]);\n d = II(d, a, b, c, M_offset_7, 10, T[49]);\n c = II(c, d, a, b, M_offset_14, 15, T[50]);\n b = II(b, c, d, a, M_offset_5, 21, T[51]);\n a = II(a, b, c, d, M_offset_12, 6, T[52]);\n d = II(d, a, b, c, M_offset_3, 10, T[53]);\n c = II(c, d, a, b, M_offset_10, 15, T[54]);\n b = II(b, c, d, a, M_offset_1, 21, T[55]);\n a = II(a, b, c, d, M_offset_8, 6, T[56]);\n d = II(d, a, b, c, M_offset_15, 10, T[57]);\n c = II(c, d, a, b, M_offset_6, 15, T[58]);\n b = II(b, c, d, a, M_offset_13, 21, T[59]);\n a = II(a, b, c, d, M_offset_4, 6, T[60]);\n d = II(d, a, b, c, M_offset_11, 10, T[61]);\n c = II(c, d, a, b, M_offset_2, 15, T[62]);\n b = II(b, c, d, a, M_offset_9, 21, T[63]);\n\n // Intermediate hash value\n H[0] = H[0] + a | 0;\n H[1] = H[1] + b | 0;\n H[2] = H[2] + c | 0;\n H[3] = H[3] + d | 0;\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x80 << 24 - nBitsLeft % 32;\n var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);\n var nBitsTotalL = nBitsTotal;\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = (nBitsTotalH << 8 | nBitsTotalH >>> 24) & 0x00ff00ff | (nBitsTotalH << 24 | nBitsTotalH >>> 8) & 0xff00ff00;\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = (nBitsTotalL << 8 | nBitsTotalL >>> 24) & 0x00ff00ff | (nBitsTotalL << 24 | nBitsTotalL >>> 8) & 0xff00ff00;\n data.sigBytes = (dataWords.length + 1) * 4;\n\n // Hash final blocks\n this._process();\n\n // Shortcuts\n var hash = this._hash;\n var H = hash.words;\n\n // Swap endian\n for (var i = 0; i < 4; i++) {\n // Shortcut\n var H_i = H[i];\n H[i] = (H_i << 8 | H_i >>> 24) & 0x00ff00ff | (H_i << 24 | H_i >>> 8) & 0xff00ff00;\n }\n\n // Return final computed hash\n return hash;\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n clone._hash = this._hash.clone();\n return clone;\n }\n });\n function FF(a, b, c, d, x, s, t) {\n var n = a + (b & c | ~b & d) + x + t;\n return (n << s | n >>> 32 - s) + b;\n }\n function GG(a, b, c, d, x, s, t) {\n var n = a + (b & d | c & ~d) + x + t;\n return (n << s | n >>> 32 - s) + b;\n }\n function HH(a, b, c, d, x, s, t) {\n var n = a + (b ^ c ^ d) + x + t;\n return (n << s | n >>> 32 - s) + b;\n }\n function II(a, b, c, d, x, s, t) {\n var n = a + (c ^ (b | ~d)) + x + t;\n return (n << s | n >>> 32 - s) + b;\n }\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.MD5('message');\n * var hash = CryptoJS.MD5(wordArray);\n */\n C.MD5 = Hasher._createHelper(MD5);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacMD5(message, key);\n */\n C.HmacMD5 = Hasher._createHmacHelper(MD5);\n })(Math);\n return CryptoJS.MD5;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var Hasher = C_lib.Hasher;\n var C_algo = C.algo;\n\n // Reusable object\n var W = [];\n\n /**\n * SHA-1 hash algorithm.\n */\n var SHA1 = C_algo.SHA1 = Hasher.extend({\n _doReset: function () {\n this._hash = new WordArray.init([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]);\n },\n _doProcessBlock: function (M, offset) {\n // Shortcut\n var H = this._hash.words;\n\n // Working variables\n var a = H[0];\n var b = H[1];\n var c = H[2];\n var d = H[3];\n var e = H[4];\n\n // Computation\n for (var i = 0; i < 80; i++) {\n if (i < 16) {\n W[i] = M[offset + i] | 0;\n } else {\n var n = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];\n W[i] = n << 1 | n >>> 31;\n }\n var t = (a << 5 | a >>> 27) + e + W[i];\n if (i < 20) {\n t += (b & c | ~b & d) + 0x5a827999;\n } else if (i < 40) {\n t += (b ^ c ^ d) + 0x6ed9eba1;\n } else if (i < 60) {\n t += (b & c | b & d | c & d) - 0x70e44324;\n } else /* if (i < 80) */{\n t += (b ^ c ^ d) - 0x359d3e2a;\n }\n e = d;\n d = c;\n c = b << 30 | b >>> 2;\n b = a;\n a = t;\n }\n\n // Intermediate hash value\n H[0] = H[0] + a | 0;\n H[1] = H[1] + b | 0;\n H[2] = H[2] + c | 0;\n H[3] = H[3] + d | 0;\n H[4] = H[4] + e | 0;\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x80 << 24 - nBitsLeft % 32;\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = nBitsTotal;\n data.sigBytes = dataWords.length * 4;\n\n // Hash final blocks\n this._process();\n\n // Return final computed hash\n return this._hash;\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n clone._hash = this._hash.clone();\n return clone;\n }\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA1('message');\n * var hash = CryptoJS.SHA1(wordArray);\n */\n C.SHA1 = Hasher._createHelper(SHA1);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA1(message, key);\n */\n C.HmacSHA1 = Hasher._createHmacHelper(SHA1);\n })();\n return CryptoJS.SHA1;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function (Math) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var Hasher = C_lib.Hasher;\n var C_algo = C.algo;\n\n // Initialization and round constants tables\n var H = [];\n var K = [];\n\n // Compute constants\n (function () {\n function isPrime(n) {\n var sqrtN = Math.sqrt(n);\n for (var factor = 2; factor <= sqrtN; factor++) {\n if (!(n % factor)) {\n return false;\n }\n }\n return true;\n }\n function getFractionalBits(n) {\n return (n - (n | 0)) * 0x100000000 | 0;\n }\n var n = 2;\n var nPrime = 0;\n while (nPrime < 64) {\n if (isPrime(n)) {\n if (nPrime < 8) {\n H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));\n }\n K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));\n nPrime++;\n }\n n++;\n }\n })();\n\n // Reusable object\n var W = [];\n\n /**\n * SHA-256 hash algorithm.\n */\n var SHA256 = C_algo.SHA256 = Hasher.extend({\n _doReset: function () {\n this._hash = new WordArray.init(H.slice(0));\n },\n _doProcessBlock: function (M, offset) {\n // Shortcut\n var H = this._hash.words;\n\n // Working variables\n var a = H[0];\n var b = H[1];\n var c = H[2];\n var d = H[3];\n var e = H[4];\n var f = H[5];\n var g = H[6];\n var h = H[7];\n\n // Computation\n for (var i = 0; i < 64; i++) {\n if (i < 16) {\n W[i] = M[offset + i] | 0;\n } else {\n var gamma0x = W[i - 15];\n var gamma0 = (gamma0x << 25 | gamma0x >>> 7) ^ (gamma0x << 14 | gamma0x >>> 18) ^ gamma0x >>> 3;\n var gamma1x = W[i - 2];\n var gamma1 = (gamma1x << 15 | gamma1x >>> 17) ^ (gamma1x << 13 | gamma1x >>> 19) ^ gamma1x >>> 10;\n W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];\n }\n var ch = e & f ^ ~e & g;\n var maj = a & b ^ a & c ^ b & c;\n var sigma0 = (a << 30 | a >>> 2) ^ (a << 19 | a >>> 13) ^ (a << 10 | a >>> 22);\n var sigma1 = (e << 26 | e >>> 6) ^ (e << 21 | e >>> 11) ^ (e << 7 | e >>> 25);\n var t1 = h + sigma1 + ch + K[i] + W[i];\n var t2 = sigma0 + maj;\n h = g;\n g = f;\n f = e;\n e = d + t1 | 0;\n d = c;\n c = b;\n b = a;\n a = t1 + t2 | 0;\n }\n\n // Intermediate hash value\n H[0] = H[0] + a | 0;\n H[1] = H[1] + b | 0;\n H[2] = H[2] + c | 0;\n H[3] = H[3] + d | 0;\n H[4] = H[4] + e | 0;\n H[5] = H[5] + f | 0;\n H[6] = H[6] + g | 0;\n H[7] = H[7] + h | 0;\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x80 << 24 - nBitsLeft % 32;\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 15] = nBitsTotal;\n data.sigBytes = dataWords.length * 4;\n\n // Hash final blocks\n this._process();\n\n // Return final computed hash\n return this._hash;\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n clone._hash = this._hash.clone();\n return clone;\n }\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA256('message');\n * var hash = CryptoJS.SHA256(wordArray);\n */\n C.SHA256 = Hasher._createHelper(SHA256);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA256(message, key);\n */\n C.HmacSHA256 = Hasher._createHmacHelper(SHA256);\n })(Math);\n return CryptoJS.SHA256;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./sha256\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./sha256\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var C_algo = C.algo;\n var SHA256 = C_algo.SHA256;\n\n /**\n * SHA-224 hash algorithm.\n */\n var SHA224 = C_algo.SHA224 = SHA256.extend({\n _doReset: function () {\n this._hash = new WordArray.init([0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]);\n },\n _doFinalize: function () {\n var hash = SHA256._doFinalize.call(this);\n hash.sigBytes -= 4;\n return hash;\n }\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA224('message');\n * var hash = CryptoJS.SHA224(wordArray);\n */\n C.SHA224 = SHA256._createHelper(SHA224);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA224(message, key);\n */\n C.HmacSHA224 = SHA256._createHmacHelper(SHA224);\n })();\n return CryptoJS.SHA224;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./x64-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./x64-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Hasher = C_lib.Hasher;\n var C_x64 = C.x64;\n var X64Word = C_x64.Word;\n var X64WordArray = C_x64.WordArray;\n var C_algo = C.algo;\n function X64Word_create() {\n return X64Word.create.apply(X64Word, arguments);\n }\n\n // Constants\n var K = [X64Word_create(0x428a2f98, 0xd728ae22), X64Word_create(0x71374491, 0x23ef65cd), X64Word_create(0xb5c0fbcf, 0xec4d3b2f), X64Word_create(0xe9b5dba5, 0x8189dbbc), X64Word_create(0x3956c25b, 0xf348b538), X64Word_create(0x59f111f1, 0xb605d019), X64Word_create(0x923f82a4, 0xaf194f9b), X64Word_create(0xab1c5ed5, 0xda6d8118), X64Word_create(0xd807aa98, 0xa3030242), X64Word_create(0x12835b01, 0x45706fbe), X64Word_create(0x243185be, 0x4ee4b28c), X64Word_create(0x550c7dc3, 0xd5ffb4e2), X64Word_create(0x72be5d74, 0xf27b896f), X64Word_create(0x80deb1fe, 0x3b1696b1), X64Word_create(0x9bdc06a7, 0x25c71235), X64Word_create(0xc19bf174, 0xcf692694), X64Word_create(0xe49b69c1, 0x9ef14ad2), X64Word_create(0xefbe4786, 0x384f25e3), X64Word_create(0x0fc19dc6, 0x8b8cd5b5), X64Word_create(0x240ca1cc, 0x77ac9c65), X64Word_create(0x2de92c6f, 0x592b0275), X64Word_create(0x4a7484aa, 0x6ea6e483), X64Word_create(0x5cb0a9dc, 0xbd41fbd4), X64Word_create(0x76f988da, 0x831153b5), X64Word_create(0x983e5152, 0xee66dfab), X64Word_create(0xa831c66d, 0x2db43210), X64Word_create(0xb00327c8, 0x98fb213f), X64Word_create(0xbf597fc7, 0xbeef0ee4), X64Word_create(0xc6e00bf3, 0x3da88fc2), X64Word_create(0xd5a79147, 0x930aa725), X64Word_create(0x06ca6351, 0xe003826f), X64Word_create(0x14292967, 0x0a0e6e70), X64Word_create(0x27b70a85, 0x46d22ffc), X64Word_create(0x2e1b2138, 0x5c26c926), X64Word_create(0x4d2c6dfc, 0x5ac42aed), X64Word_create(0x53380d13, 0x9d95b3df), X64Word_create(0x650a7354, 0x8baf63de), X64Word_create(0x766a0abb, 0x3c77b2a8), X64Word_create(0x81c2c92e, 0x47edaee6), X64Word_create(0x92722c85, 0x1482353b), X64Word_create(0xa2bfe8a1, 0x4cf10364), X64Word_create(0xa81a664b, 0xbc423001), X64Word_create(0xc24b8b70, 0xd0f89791), X64Word_create(0xc76c51a3, 0x0654be30), X64Word_create(0xd192e819, 0xd6ef5218), X64Word_create(0xd6990624, 0x5565a910), X64Word_create(0xf40e3585, 0x5771202a), X64Word_create(0x106aa070, 0x32bbd1b8), X64Word_create(0x19a4c116, 0xb8d2d0c8), X64Word_create(0x1e376c08, 0x5141ab53), X64Word_create(0x2748774c, 0xdf8eeb99), X64Word_create(0x34b0bcb5, 0xe19b48a8), X64Word_create(0x391c0cb3, 0xc5c95a63), X64Word_create(0x4ed8aa4a, 0xe3418acb), X64Word_create(0x5b9cca4f, 0x7763e373), X64Word_create(0x682e6ff3, 0xd6b2b8a3), X64Word_create(0x748f82ee, 0x5defb2fc), X64Word_create(0x78a5636f, 0x43172f60), X64Word_create(0x84c87814, 0xa1f0ab72), X64Word_create(0x8cc70208, 0x1a6439ec), X64Word_create(0x90befffa, 0x23631e28), X64Word_create(0xa4506ceb, 0xde82bde9), X64Word_create(0xbef9a3f7, 0xb2c67915), X64Word_create(0xc67178f2, 0xe372532b), X64Word_create(0xca273ece, 0xea26619c), X64Word_create(0xd186b8c7, 0x21c0c207), X64Word_create(0xeada7dd6, 0xcde0eb1e), X64Word_create(0xf57d4f7f, 0xee6ed178), X64Word_create(0x06f067aa, 0x72176fba), X64Word_create(0x0a637dc5, 0xa2c898a6), X64Word_create(0x113f9804, 0xbef90dae), X64Word_create(0x1b710b35, 0x131c471b), X64Word_create(0x28db77f5, 0x23047d84), X64Word_create(0x32caab7b, 0x40c72493), X64Word_create(0x3c9ebe0a, 0x15c9bebc), X64Word_create(0x431d67c4, 0x9c100d4c), X64Word_create(0x4cc5d4be, 0xcb3e42b6), X64Word_create(0x597f299c, 0xfc657e2a), X64Word_create(0x5fcb6fab, 0x3ad6faec), X64Word_create(0x6c44198c, 0x4a475817)];\n\n // Reusable objects\n var W = [];\n (function () {\n for (var i = 0; i < 80; i++) {\n W[i] = X64Word_create();\n }\n })();\n\n /**\n * SHA-512 hash algorithm.\n */\n var SHA512 = C_algo.SHA512 = Hasher.extend({\n _doReset: function () {\n this._hash = new X64WordArray.init([new X64Word.init(0x6a09e667, 0xf3bcc908), new X64Word.init(0xbb67ae85, 0x84caa73b), new X64Word.init(0x3c6ef372, 0xfe94f82b), new X64Word.init(0xa54ff53a, 0x5f1d36f1), new X64Word.init(0x510e527f, 0xade682d1), new X64Word.init(0x9b05688c, 0x2b3e6c1f), new X64Word.init(0x1f83d9ab, 0xfb41bd6b), new X64Word.init(0x5be0cd19, 0x137e2179)]);\n },\n _doProcessBlock: function (M, offset) {\n // Shortcuts\n var H = this._hash.words;\n var H0 = H[0];\n var H1 = H[1];\n var H2 = H[2];\n var H3 = H[3];\n var H4 = H[4];\n var H5 = H[5];\n var H6 = H[6];\n var H7 = H[7];\n var H0h = H0.high;\n var H0l = H0.low;\n var H1h = H1.high;\n var H1l = H1.low;\n var H2h = H2.high;\n var H2l = H2.low;\n var H3h = H3.high;\n var H3l = H3.low;\n var H4h = H4.high;\n var H4l = H4.low;\n var H5h = H5.high;\n var H5l = H5.low;\n var H6h = H6.high;\n var H6l = H6.low;\n var H7h = H7.high;\n var H7l = H7.low;\n\n // Working variables\n var ah = H0h;\n var al = H0l;\n var bh = H1h;\n var bl = H1l;\n var ch = H2h;\n var cl = H2l;\n var dh = H3h;\n var dl = H3l;\n var eh = H4h;\n var el = H4l;\n var fh = H5h;\n var fl = H5l;\n var gh = H6h;\n var gl = H6l;\n var hh = H7h;\n var hl = H7l;\n\n // Rounds\n for (var i = 0; i < 80; i++) {\n var Wil;\n var Wih;\n\n // Shortcut\n var Wi = W[i];\n\n // Extend message\n if (i < 16) {\n Wih = Wi.high = M[offset + i * 2] | 0;\n Wil = Wi.low = M[offset + i * 2 + 1] | 0;\n } else {\n // Gamma0\n var gamma0x = W[i - 15];\n var gamma0xh = gamma0x.high;\n var gamma0xl = gamma0x.low;\n var gamma0h = (gamma0xh >>> 1 | gamma0xl << 31) ^ (gamma0xh >>> 8 | gamma0xl << 24) ^ gamma0xh >>> 7;\n var gamma0l = (gamma0xl >>> 1 | gamma0xh << 31) ^ (gamma0xl >>> 8 | gamma0xh << 24) ^ (gamma0xl >>> 7 | gamma0xh << 25);\n\n // Gamma1\n var gamma1x = W[i - 2];\n var gamma1xh = gamma1x.high;\n var gamma1xl = gamma1x.low;\n var gamma1h = (gamma1xh >>> 19 | gamma1xl << 13) ^ (gamma1xh << 3 | gamma1xl >>> 29) ^ gamma1xh >>> 6;\n var gamma1l = (gamma1xl >>> 19 | gamma1xh << 13) ^ (gamma1xl << 3 | gamma1xh >>> 29) ^ (gamma1xl >>> 6 | gamma1xh << 26);\n\n // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]\n var Wi7 = W[i - 7];\n var Wi7h = Wi7.high;\n var Wi7l = Wi7.low;\n var Wi16 = W[i - 16];\n var Wi16h = Wi16.high;\n var Wi16l = Wi16.low;\n Wil = gamma0l + Wi7l;\n Wih = gamma0h + Wi7h + (Wil >>> 0 < gamma0l >>> 0 ? 1 : 0);\n Wil = Wil + gamma1l;\n Wih = Wih + gamma1h + (Wil >>> 0 < gamma1l >>> 0 ? 1 : 0);\n Wil = Wil + Wi16l;\n Wih = Wih + Wi16h + (Wil >>> 0 < Wi16l >>> 0 ? 1 : 0);\n Wi.high = Wih;\n Wi.low = Wil;\n }\n var chh = eh & fh ^ ~eh & gh;\n var chl = el & fl ^ ~el & gl;\n var majh = ah & bh ^ ah & ch ^ bh & ch;\n var majl = al & bl ^ al & cl ^ bl & cl;\n var sigma0h = (ah >>> 28 | al << 4) ^ (ah << 30 | al >>> 2) ^ (ah << 25 | al >>> 7);\n var sigma0l = (al >>> 28 | ah << 4) ^ (al << 30 | ah >>> 2) ^ (al << 25 | ah >>> 7);\n var sigma1h = (eh >>> 14 | el << 18) ^ (eh >>> 18 | el << 14) ^ (eh << 23 | el >>> 9);\n var sigma1l = (el >>> 14 | eh << 18) ^ (el >>> 18 | eh << 14) ^ (el << 23 | eh >>> 9);\n\n // t1 = h + sigma1 + ch + K[i] + W[i]\n var Ki = K[i];\n var Kih = Ki.high;\n var Kil = Ki.low;\n var t1l = hl + sigma1l;\n var t1h = hh + sigma1h + (t1l >>> 0 < hl >>> 0 ? 1 : 0);\n var t1l = t1l + chl;\n var t1h = t1h + chh + (t1l >>> 0 < chl >>> 0 ? 1 : 0);\n var t1l = t1l + Kil;\n var t1h = t1h + Kih + (t1l >>> 0 < Kil >>> 0 ? 1 : 0);\n var t1l = t1l + Wil;\n var t1h = t1h + Wih + (t1l >>> 0 < Wil >>> 0 ? 1 : 0);\n\n // t2 = sigma0 + maj\n var t2l = sigma0l + majl;\n var t2h = sigma0h + majh + (t2l >>> 0 < sigma0l >>> 0 ? 1 : 0);\n\n // Update working variables\n hh = gh;\n hl = gl;\n gh = fh;\n gl = fl;\n fh = eh;\n fl = el;\n el = dl + t1l | 0;\n eh = dh + t1h + (el >>> 0 < dl >>> 0 ? 1 : 0) | 0;\n dh = ch;\n dl = cl;\n ch = bh;\n cl = bl;\n bh = ah;\n bl = al;\n al = t1l + t2l | 0;\n ah = t1h + t2h + (al >>> 0 < t1l >>> 0 ? 1 : 0) | 0;\n }\n\n // Intermediate hash value\n H0l = H0.low = H0l + al;\n H0.high = H0h + ah + (H0l >>> 0 < al >>> 0 ? 1 : 0);\n H1l = H1.low = H1l + bl;\n H1.high = H1h + bh + (H1l >>> 0 < bl >>> 0 ? 1 : 0);\n H2l = H2.low = H2l + cl;\n H2.high = H2h + ch + (H2l >>> 0 < cl >>> 0 ? 1 : 0);\n H3l = H3.low = H3l + dl;\n H3.high = H3h + dh + (H3l >>> 0 < dl >>> 0 ? 1 : 0);\n H4l = H4.low = H4l + el;\n H4.high = H4h + eh + (H4l >>> 0 < el >>> 0 ? 1 : 0);\n H5l = H5.low = H5l + fl;\n H5.high = H5h + fh + (H5l >>> 0 < fl >>> 0 ? 1 : 0);\n H6l = H6.low = H6l + gl;\n H6.high = H6h + gh + (H6l >>> 0 < gl >>> 0 ? 1 : 0);\n H7l = H7.low = H7l + hl;\n H7.high = H7h + hh + (H7l >>> 0 < hl >>> 0 ? 1 : 0);\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x80 << 24 - nBitsLeft % 32;\n dataWords[(nBitsLeft + 128 >>> 10 << 5) + 30] = Math.floor(nBitsTotal / 0x100000000);\n dataWords[(nBitsLeft + 128 >>> 10 << 5) + 31] = nBitsTotal;\n data.sigBytes = dataWords.length * 4;\n\n // Hash final blocks\n this._process();\n\n // Convert hash to 32-bit word array before returning\n var hash = this._hash.toX32();\n\n // Return final computed hash\n return hash;\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n clone._hash = this._hash.clone();\n return clone;\n },\n blockSize: 1024 / 32\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA512('message');\n * var hash = CryptoJS.SHA512(wordArray);\n */\n C.SHA512 = Hasher._createHelper(SHA512);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA512(message, key);\n */\n C.HmacSHA512 = Hasher._createHmacHelper(SHA512);\n })();\n return CryptoJS.SHA512;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./x64-core\"), require(\"./sha512\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./x64-core\", \"./sha512\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_x64 = C.x64;\n var X64Word = C_x64.Word;\n var X64WordArray = C_x64.WordArray;\n var C_algo = C.algo;\n var SHA512 = C_algo.SHA512;\n\n /**\n * SHA-384 hash algorithm.\n */\n var SHA384 = C_algo.SHA384 = SHA512.extend({\n _doReset: function () {\n this._hash = new X64WordArray.init([new X64Word.init(0xcbbb9d5d, 0xc1059ed8), new X64Word.init(0x629a292a, 0x367cd507), new X64Word.init(0x9159015a, 0x3070dd17), new X64Word.init(0x152fecd8, 0xf70e5939), new X64Word.init(0x67332667, 0xffc00b31), new X64Word.init(0x8eb44a87, 0x68581511), new X64Word.init(0xdb0c2e0d, 0x64f98fa7), new X64Word.init(0x47b5481d, 0xbefa4fa4)]);\n },\n _doFinalize: function () {\n var hash = SHA512._doFinalize.call(this);\n hash.sigBytes -= 16;\n return hash;\n }\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA384('message');\n * var hash = CryptoJS.SHA384(wordArray);\n */\n C.SHA384 = SHA512._createHelper(SHA384);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA384(message, key);\n */\n C.HmacSHA384 = SHA512._createHmacHelper(SHA384);\n })();\n return CryptoJS.SHA384;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./x64-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./x64-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function (Math) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var Hasher = C_lib.Hasher;\n var C_x64 = C.x64;\n var X64Word = C_x64.Word;\n var C_algo = C.algo;\n\n // Constants tables\n var RHO_OFFSETS = [];\n var PI_INDEXES = [];\n var ROUND_CONSTANTS = [];\n\n // Compute Constants\n (function () {\n // Compute rho offset constants\n var x = 1,\n y = 0;\n for (var t = 0; t < 24; t++) {\n RHO_OFFSETS[x + 5 * y] = (t + 1) * (t + 2) / 2 % 64;\n var newX = y % 5;\n var newY = (2 * x + 3 * y) % 5;\n x = newX;\n y = newY;\n }\n\n // Compute pi index constants\n for (var x = 0; x < 5; x++) {\n for (var y = 0; y < 5; y++) {\n PI_INDEXES[x + 5 * y] = y + (2 * x + 3 * y) % 5 * 5;\n }\n }\n\n // Compute round constants\n var LFSR = 0x01;\n for (var i = 0; i < 24; i++) {\n var roundConstantMsw = 0;\n var roundConstantLsw = 0;\n for (var j = 0; j < 7; j++) {\n if (LFSR & 0x01) {\n var bitPosition = (1 << j) - 1;\n if (bitPosition < 32) {\n roundConstantLsw ^= 1 << bitPosition;\n } else /* if (bitPosition >= 32) */{\n roundConstantMsw ^= 1 << bitPosition - 32;\n }\n }\n\n // Compute next LFSR\n if (LFSR & 0x80) {\n // Primitive polynomial over GF(2): x^8 + x^6 + x^5 + x^4 + 1\n LFSR = LFSR << 1 ^ 0x71;\n } else {\n LFSR <<= 1;\n }\n }\n ROUND_CONSTANTS[i] = X64Word.create(roundConstantMsw, roundConstantLsw);\n }\n })();\n\n // Reusable objects for temporary values\n var T = [];\n (function () {\n for (var i = 0; i < 25; i++) {\n T[i] = X64Word.create();\n }\n })();\n\n /**\n * SHA-3 hash algorithm.\n */\n var SHA3 = C_algo.SHA3 = Hasher.extend({\n /**\n * Configuration options.\n *\n * @property {number} outputLength\n * The desired number of bits in the output hash.\n * Only values permitted are: 224, 256, 384, 512.\n * Default: 512\n */\n cfg: Hasher.cfg.extend({\n outputLength: 512\n }),\n _doReset: function () {\n var state = this._state = [];\n for (var i = 0; i < 25; i++) {\n state[i] = new X64Word.init();\n }\n this.blockSize = (1600 - 2 * this.cfg.outputLength) / 32;\n },\n _doProcessBlock: function (M, offset) {\n // Shortcuts\n var state = this._state;\n var nBlockSizeLanes = this.blockSize / 2;\n\n // Absorb\n for (var i = 0; i < nBlockSizeLanes; i++) {\n // Shortcuts\n var M2i = M[offset + 2 * i];\n var M2i1 = M[offset + 2 * i + 1];\n\n // Swap endian\n M2i = (M2i << 8 | M2i >>> 24) & 0x00ff00ff | (M2i << 24 | M2i >>> 8) & 0xff00ff00;\n M2i1 = (M2i1 << 8 | M2i1 >>> 24) & 0x00ff00ff | (M2i1 << 24 | M2i1 >>> 8) & 0xff00ff00;\n\n // Absorb message into state\n var lane = state[i];\n lane.high ^= M2i1;\n lane.low ^= M2i;\n }\n\n // Rounds\n for (var round = 0; round < 24; round++) {\n // Theta\n for (var x = 0; x < 5; x++) {\n // Mix column lanes\n var tMsw = 0,\n tLsw = 0;\n for (var y = 0; y < 5; y++) {\n var lane = state[x + 5 * y];\n tMsw ^= lane.high;\n tLsw ^= lane.low;\n }\n\n // Temporary values\n var Tx = T[x];\n Tx.high = tMsw;\n Tx.low = tLsw;\n }\n for (var x = 0; x < 5; x++) {\n // Shortcuts\n var Tx4 = T[(x + 4) % 5];\n var Tx1 = T[(x + 1) % 5];\n var Tx1Msw = Tx1.high;\n var Tx1Lsw = Tx1.low;\n\n // Mix surrounding columns\n var tMsw = Tx4.high ^ (Tx1Msw << 1 | Tx1Lsw >>> 31);\n var tLsw = Tx4.low ^ (Tx1Lsw << 1 | Tx1Msw >>> 31);\n for (var y = 0; y < 5; y++) {\n var lane = state[x + 5 * y];\n lane.high ^= tMsw;\n lane.low ^= tLsw;\n }\n }\n\n // Rho Pi\n for (var laneIndex = 1; laneIndex < 25; laneIndex++) {\n var tMsw;\n var tLsw;\n\n // Shortcuts\n var lane = state[laneIndex];\n var laneMsw = lane.high;\n var laneLsw = lane.low;\n var rhoOffset = RHO_OFFSETS[laneIndex];\n\n // Rotate lanes\n if (rhoOffset < 32) {\n tMsw = laneMsw << rhoOffset | laneLsw >>> 32 - rhoOffset;\n tLsw = laneLsw << rhoOffset | laneMsw >>> 32 - rhoOffset;\n } else /* if (rhoOffset >= 32) */{\n tMsw = laneLsw << rhoOffset - 32 | laneMsw >>> 64 - rhoOffset;\n tLsw = laneMsw << rhoOffset - 32 | laneLsw >>> 64 - rhoOffset;\n }\n\n // Transpose lanes\n var TPiLane = T[PI_INDEXES[laneIndex]];\n TPiLane.high = tMsw;\n TPiLane.low = tLsw;\n }\n\n // Rho pi at x = y = 0\n var T0 = T[0];\n var state0 = state[0];\n T0.high = state0.high;\n T0.low = state0.low;\n\n // Chi\n for (var x = 0; x < 5; x++) {\n for (var y = 0; y < 5; y++) {\n // Shortcuts\n var laneIndex = x + 5 * y;\n var lane = state[laneIndex];\n var TLane = T[laneIndex];\n var Tx1Lane = T[(x + 1) % 5 + 5 * y];\n var Tx2Lane = T[(x + 2) % 5 + 5 * y];\n\n // Mix rows\n lane.high = TLane.high ^ ~Tx1Lane.high & Tx2Lane.high;\n lane.low = TLane.low ^ ~Tx1Lane.low & Tx2Lane.low;\n }\n }\n\n // Iota\n var lane = state[0];\n var roundConstant = ROUND_CONSTANTS[round];\n lane.high ^= roundConstant.high;\n lane.low ^= roundConstant.low;\n }\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n var blockSizeBits = this.blockSize * 32;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x1 << 24 - nBitsLeft % 32;\n dataWords[(Math.ceil((nBitsLeft + 1) / blockSizeBits) * blockSizeBits >>> 5) - 1] |= 0x80;\n data.sigBytes = dataWords.length * 4;\n\n // Hash final blocks\n this._process();\n\n // Shortcuts\n var state = this._state;\n var outputLengthBytes = this.cfg.outputLength / 8;\n var outputLengthLanes = outputLengthBytes / 8;\n\n // Squeeze\n var hashWords = [];\n for (var i = 0; i < outputLengthLanes; i++) {\n // Shortcuts\n var lane = state[i];\n var laneMsw = lane.high;\n var laneLsw = lane.low;\n\n // Swap endian\n laneMsw = (laneMsw << 8 | laneMsw >>> 24) & 0x00ff00ff | (laneMsw << 24 | laneMsw >>> 8) & 0xff00ff00;\n laneLsw = (laneLsw << 8 | laneLsw >>> 24) & 0x00ff00ff | (laneLsw << 24 | laneLsw >>> 8) & 0xff00ff00;\n\n // Squeeze state to retrieve hash\n hashWords.push(laneLsw);\n hashWords.push(laneMsw);\n }\n\n // Return final computed hash\n return new WordArray.init(hashWords, outputLengthBytes);\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n var state = clone._state = this._state.slice(0);\n for (var i = 0; i < 25; i++) {\n state[i] = state[i].clone();\n }\n return clone;\n }\n });\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.SHA3('message');\n * var hash = CryptoJS.SHA3(wordArray);\n */\n C.SHA3 = Hasher._createHelper(SHA3);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacSHA3(message, key);\n */\n C.HmacSHA3 = Hasher._createHmacHelper(SHA3);\n })(Math);\n return CryptoJS.SHA3;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /** @preserve\n (c) 2012 by Cédric Mesnil. All rights reserved.\n \tRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n \t - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n \tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n (function (Math) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var Hasher = C_lib.Hasher;\n var C_algo = C.algo;\n\n // Constants table\n var _zl = WordArray.create([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13]);\n var _zr = WordArray.create([5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]);\n var _sl = WordArray.create([11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6]);\n var _sr = WordArray.create([8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]);\n var _hl = WordArray.create([0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E]);\n var _hr = WordArray.create([0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]);\n\n /**\n * RIPEMD160 hash algorithm.\n */\n var RIPEMD160 = C_algo.RIPEMD160 = Hasher.extend({\n _doReset: function () {\n this._hash = WordArray.create([0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]);\n },\n _doProcessBlock: function (M, offset) {\n // Swap endian\n for (var i = 0; i < 16; i++) {\n // Shortcuts\n var offset_i = offset + i;\n var M_offset_i = M[offset_i];\n\n // Swap\n M[offset_i] = (M_offset_i << 8 | M_offset_i >>> 24) & 0x00ff00ff | (M_offset_i << 24 | M_offset_i >>> 8) & 0xff00ff00;\n }\n // Shortcut\n var H = this._hash.words;\n var hl = _hl.words;\n var hr = _hr.words;\n var zl = _zl.words;\n var zr = _zr.words;\n var sl = _sl.words;\n var sr = _sr.words;\n\n // Working variables\n var al, bl, cl, dl, el;\n var ar, br, cr, dr, er;\n ar = al = H[0];\n br = bl = H[1];\n cr = cl = H[2];\n dr = dl = H[3];\n er = el = H[4];\n // Computation\n var t;\n for (var i = 0; i < 80; i += 1) {\n t = al + M[offset + zl[i]] | 0;\n if (i < 16) {\n t += f1(bl, cl, dl) + hl[0];\n } else if (i < 32) {\n t += f2(bl, cl, dl) + hl[1];\n } else if (i < 48) {\n t += f3(bl, cl, dl) + hl[2];\n } else if (i < 64) {\n t += f4(bl, cl, dl) + hl[3];\n } else {\n // if (i<80) {\n t += f5(bl, cl, dl) + hl[4];\n }\n t = t | 0;\n t = rotl(t, sl[i]);\n t = t + el | 0;\n al = el;\n el = dl;\n dl = rotl(cl, 10);\n cl = bl;\n bl = t;\n t = ar + M[offset + zr[i]] | 0;\n if (i < 16) {\n t += f5(br, cr, dr) + hr[0];\n } else if (i < 32) {\n t += f4(br, cr, dr) + hr[1];\n } else if (i < 48) {\n t += f3(br, cr, dr) + hr[2];\n } else if (i < 64) {\n t += f2(br, cr, dr) + hr[3];\n } else {\n // if (i<80) {\n t += f1(br, cr, dr) + hr[4];\n }\n t = t | 0;\n t = rotl(t, sr[i]);\n t = t + er | 0;\n ar = er;\n er = dr;\n dr = rotl(cr, 10);\n cr = br;\n br = t;\n }\n // Intermediate hash value\n t = H[1] + cl + dr | 0;\n H[1] = H[2] + dl + er | 0;\n H[2] = H[3] + el + ar | 0;\n H[3] = H[4] + al + br | 0;\n H[4] = H[0] + bl + cr | 0;\n H[0] = t;\n },\n _doFinalize: function () {\n // Shortcuts\n var data = this._data;\n var dataWords = data.words;\n var nBitsTotal = this._nDataBytes * 8;\n var nBitsLeft = data.sigBytes * 8;\n\n // Add padding\n dataWords[nBitsLeft >>> 5] |= 0x80 << 24 - nBitsLeft % 32;\n dataWords[(nBitsLeft + 64 >>> 9 << 4) + 14] = (nBitsTotal << 8 | nBitsTotal >>> 24) & 0x00ff00ff | (nBitsTotal << 24 | nBitsTotal >>> 8) & 0xff00ff00;\n data.sigBytes = (dataWords.length + 1) * 4;\n\n // Hash final blocks\n this._process();\n\n // Shortcuts\n var hash = this._hash;\n var H = hash.words;\n\n // Swap endian\n for (var i = 0; i < 5; i++) {\n // Shortcut\n var H_i = H[i];\n\n // Swap\n H[i] = (H_i << 8 | H_i >>> 24) & 0x00ff00ff | (H_i << 24 | H_i >>> 8) & 0xff00ff00;\n }\n\n // Return final computed hash\n return hash;\n },\n clone: function () {\n var clone = Hasher.clone.call(this);\n clone._hash = this._hash.clone();\n return clone;\n }\n });\n function f1(x, y, z) {\n return x ^ y ^ z;\n }\n function f2(x, y, z) {\n return x & y | ~x & z;\n }\n function f3(x, y, z) {\n return (x | ~y) ^ z;\n }\n function f4(x, y, z) {\n return x & z | y & ~z;\n }\n function f5(x, y, z) {\n return x ^ (y | ~z);\n }\n function rotl(x, n) {\n return x << n | x >>> 32 - n;\n }\n\n /**\n * Shortcut function to the hasher's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n *\n * @return {WordArray} The hash.\n *\n * @static\n *\n * @example\n *\n * var hash = CryptoJS.RIPEMD160('message');\n * var hash = CryptoJS.RIPEMD160(wordArray);\n */\n C.RIPEMD160 = Hasher._createHelper(RIPEMD160);\n\n /**\n * Shortcut function to the HMAC's object interface.\n *\n * @param {WordArray|string} message The message to hash.\n * @param {WordArray|string} key The secret key.\n *\n * @return {WordArray} The HMAC.\n *\n * @static\n *\n * @example\n *\n * var hmac = CryptoJS.HmacRIPEMD160(message, key);\n */\n C.HmacRIPEMD160 = Hasher._createHmacHelper(RIPEMD160);\n })(Math);\n return CryptoJS.RIPEMD160;\n});",";\n(function (root, factory) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Base = C_lib.Base;\n var C_enc = C.enc;\n var Utf8 = C_enc.Utf8;\n var C_algo = C.algo;\n\n /**\n * HMAC algorithm.\n */\n var HMAC = C_algo.HMAC = Base.extend({\n /**\n * Initializes a newly created HMAC.\n *\n * @param {Hasher} hasher The hash algorithm to use.\n * @param {WordArray|string} key The secret key.\n *\n * @example\n *\n * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);\n */\n init: function (hasher, key) {\n // Init hasher\n hasher = this._hasher = new hasher.init();\n\n // Convert string to WordArray, else assume WordArray already\n if (typeof key == 'string') {\n key = Utf8.parse(key);\n }\n\n // Shortcuts\n var hasherBlockSize = hasher.blockSize;\n var hasherBlockSizeBytes = hasherBlockSize * 4;\n\n // Allow arbitrary length keys\n if (key.sigBytes > hasherBlockSizeBytes) {\n key = hasher.finalize(key);\n }\n\n // Clamp excess bits\n key.clamp();\n\n // Clone key for inner and outer pads\n var oKey = this._oKey = key.clone();\n var iKey = this._iKey = key.clone();\n\n // Shortcuts\n var oKeyWords = oKey.words;\n var iKeyWords = iKey.words;\n\n // XOR keys with pad constants\n for (var i = 0; i < hasherBlockSize; i++) {\n oKeyWords[i] ^= 0x5c5c5c5c;\n iKeyWords[i] ^= 0x36363636;\n }\n oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;\n\n // Set initial values\n this.reset();\n },\n /**\n * Resets this HMAC to its initial state.\n *\n * @example\n *\n * hmacHasher.reset();\n */\n reset: function () {\n // Shortcut\n var hasher = this._hasher;\n\n // Reset\n hasher.reset();\n hasher.update(this._iKey);\n },\n /**\n * Updates this HMAC with a message.\n *\n * @param {WordArray|string} messageUpdate The message to append.\n *\n * @return {HMAC} This HMAC instance.\n *\n * @example\n *\n * hmacHasher.update('message');\n * hmacHasher.update(wordArray);\n */\n update: function (messageUpdate) {\n this._hasher.update(messageUpdate);\n\n // Chainable\n return this;\n },\n /**\n * Finalizes the HMAC computation.\n * Note that the finalize operation is effectively a destructive, read-once operation.\n *\n * @param {WordArray|string} messageUpdate (Optional) A final message update.\n *\n * @return {WordArray} The HMAC.\n *\n * @example\n *\n * var hmac = hmacHasher.finalize();\n * var hmac = hmacHasher.finalize('message');\n * var hmac = hmacHasher.finalize(wordArray);\n */\n finalize: function (messageUpdate) {\n // Shortcut\n var hasher = this._hasher;\n\n // Compute HMAC\n var innerHash = hasher.finalize(messageUpdate);\n hasher.reset();\n var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));\n return hmac;\n }\n });\n })();\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./sha256\"), require(\"./hmac\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./sha256\", \"./hmac\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Base = C_lib.Base;\n var WordArray = C_lib.WordArray;\n var C_algo = C.algo;\n var SHA256 = C_algo.SHA256;\n var HMAC = C_algo.HMAC;\n\n /**\n * Password-Based Key Derivation Function 2 algorithm.\n */\n var PBKDF2 = C_algo.PBKDF2 = Base.extend({\n /**\n * Configuration options.\n *\n * @property {number} keySize The key size in words to generate. Default: 4 (128 bits)\n * @property {Hasher} hasher The hasher to use. Default: SHA256\n * @property {number} iterations The number of iterations to perform. Default: 250000\n */\n cfg: Base.extend({\n keySize: 128 / 32,\n hasher: SHA256,\n iterations: 250000\n }),\n /**\n * Initializes a newly created key derivation function.\n *\n * @param {Object} cfg (Optional) The configuration options to use for the derivation.\n *\n * @example\n *\n * var kdf = CryptoJS.algo.PBKDF2.create();\n * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 });\n * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 });\n */\n init: function (cfg) {\n this.cfg = this.cfg.extend(cfg);\n },\n /**\n * Computes the Password-Based Key Derivation Function 2.\n *\n * @param {WordArray|string} password The password.\n * @param {WordArray|string} salt A salt.\n *\n * @return {WordArray} The derived key.\n *\n * @example\n *\n * var key = kdf.compute(password, salt);\n */\n compute: function (password, salt) {\n // Shortcut\n var cfg = this.cfg;\n\n // Init HMAC\n var hmac = HMAC.create(cfg.hasher, password);\n\n // Initial values\n var derivedKey = WordArray.create();\n var blockIndex = WordArray.create([0x00000001]);\n\n // Shortcuts\n var derivedKeyWords = derivedKey.words;\n var blockIndexWords = blockIndex.words;\n var keySize = cfg.keySize;\n var iterations = cfg.iterations;\n\n // Generate key\n while (derivedKeyWords.length < keySize) {\n var block = hmac.update(salt).finalize(blockIndex);\n hmac.reset();\n\n // Shortcuts\n var blockWords = block.words;\n var blockWordsLength = blockWords.length;\n\n // Iterations\n var intermediate = block;\n for (var i = 1; i < iterations; i++) {\n intermediate = hmac.finalize(intermediate);\n hmac.reset();\n\n // Shortcut\n var intermediateWords = intermediate.words;\n\n // XOR intermediate with block\n for (var j = 0; j < blockWordsLength; j++) {\n blockWords[j] ^= intermediateWords[j];\n }\n }\n derivedKey.concat(block);\n blockIndexWords[0]++;\n }\n derivedKey.sigBytes = keySize * 4;\n return derivedKey;\n }\n });\n\n /**\n * Computes the Password-Based Key Derivation Function 2.\n *\n * @param {WordArray|string} password The password.\n * @param {WordArray|string} salt A salt.\n * @param {Object} cfg (Optional) The configuration options to use for this computation.\n *\n * @return {WordArray} The derived key.\n *\n * @static\n *\n * @example\n *\n * var key = CryptoJS.PBKDF2(password, salt);\n * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 });\n * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 });\n */\n C.PBKDF2 = function (password, salt, cfg) {\n return PBKDF2.create(cfg).compute(password, salt);\n };\n })();\n return CryptoJS.PBKDF2;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./sha1\"), require(\"./hmac\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./sha1\", \"./hmac\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Base = C_lib.Base;\n var WordArray = C_lib.WordArray;\n var C_algo = C.algo;\n var MD5 = C_algo.MD5;\n\n /**\n * This key derivation function is meant to conform with EVP_BytesToKey.\n * www.openssl.org/docs/crypto/EVP_BytesToKey.html\n */\n var EvpKDF = C_algo.EvpKDF = Base.extend({\n /**\n * Configuration options.\n *\n * @property {number} keySize The key size in words to generate. Default: 4 (128 bits)\n * @property {Hasher} hasher The hash algorithm to use. Default: MD5\n * @property {number} iterations The number of iterations to perform. Default: 1\n */\n cfg: Base.extend({\n keySize: 128 / 32,\n hasher: MD5,\n iterations: 1\n }),\n /**\n * Initializes a newly created key derivation function.\n *\n * @param {Object} cfg (Optional) The configuration options to use for the derivation.\n *\n * @example\n *\n * var kdf = CryptoJS.algo.EvpKDF.create();\n * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8 });\n * var kdf = CryptoJS.algo.EvpKDF.create({ keySize: 8, iterations: 1000 });\n */\n init: function (cfg) {\n this.cfg = this.cfg.extend(cfg);\n },\n /**\n * Derives a key from a password.\n *\n * @param {WordArray|string} password The password.\n * @param {WordArray|string} salt A salt.\n *\n * @return {WordArray} The derived key.\n *\n * @example\n *\n * var key = kdf.compute(password, salt);\n */\n compute: function (password, salt) {\n var block;\n\n // Shortcut\n var cfg = this.cfg;\n\n // Init hasher\n var hasher = cfg.hasher.create();\n\n // Initial values\n var derivedKey = WordArray.create();\n\n // Shortcuts\n var derivedKeyWords = derivedKey.words;\n var keySize = cfg.keySize;\n var iterations = cfg.iterations;\n\n // Generate key\n while (derivedKeyWords.length < keySize) {\n if (block) {\n hasher.update(block);\n }\n block = hasher.update(password).finalize(salt);\n hasher.reset();\n\n // Iterations\n for (var i = 1; i < iterations; i++) {\n block = hasher.finalize(block);\n hasher.reset();\n }\n derivedKey.concat(block);\n }\n derivedKey.sigBytes = keySize * 4;\n return derivedKey;\n }\n });\n\n /**\n * Derives a key from a password.\n *\n * @param {WordArray|string} password The password.\n * @param {WordArray|string} salt A salt.\n * @param {Object} cfg (Optional) The configuration options to use for this computation.\n *\n * @return {WordArray} The derived key.\n *\n * @static\n *\n * @example\n *\n * var key = CryptoJS.EvpKDF(password, salt);\n * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8 });\n * var key = CryptoJS.EvpKDF(password, salt, { keySize: 8, iterations: 1000 });\n */\n C.EvpKDF = function (password, salt, cfg) {\n return EvpKDF.create(cfg).compute(password, salt);\n };\n })();\n return CryptoJS.EvpKDF;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./evpkdf\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./evpkdf\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Cipher core components.\n */\n CryptoJS.lib.Cipher || function (undefined) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var Base = C_lib.Base;\n var WordArray = C_lib.WordArray;\n var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;\n var C_enc = C.enc;\n var Utf8 = C_enc.Utf8;\n var Base64 = C_enc.Base64;\n var C_algo = C.algo;\n var EvpKDF = C_algo.EvpKDF;\n\n /**\n * Abstract base cipher template.\n *\n * @property {number} keySize This cipher's key size. Default: 4 (128 bits)\n * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits)\n * @property {number} _ENC_XFORM_MODE A constant representing encryption mode.\n * @property {number} _DEC_XFORM_MODE A constant representing decryption mode.\n */\n var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({\n /**\n * Configuration options.\n *\n * @property {WordArray} iv The IV to use for this operation.\n */\n cfg: Base.extend(),\n /**\n * Creates this cipher in encryption mode.\n *\n * @param {WordArray} key The key.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {Cipher} A cipher instance.\n *\n * @static\n *\n * @example\n *\n * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });\n */\n createEncryptor: function (key, cfg) {\n return this.create(this._ENC_XFORM_MODE, key, cfg);\n },\n /**\n * Creates this cipher in decryption mode.\n *\n * @param {WordArray} key The key.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {Cipher} A cipher instance.\n *\n * @static\n *\n * @example\n *\n * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });\n */\n createDecryptor: function (key, cfg) {\n return this.create(this._DEC_XFORM_MODE, key, cfg);\n },\n /**\n * Initializes a newly created cipher.\n *\n * @param {number} xformMode Either the encryption or decryption transormation mode constant.\n * @param {WordArray} key The key.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @example\n *\n * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });\n */\n init: function (xformMode, key, cfg) {\n // Apply config defaults\n this.cfg = this.cfg.extend(cfg);\n\n // Store transform mode and key\n this._xformMode = xformMode;\n this._key = key;\n\n // Set initial values\n this.reset();\n },\n /**\n * Resets this cipher to its initial state.\n *\n * @example\n *\n * cipher.reset();\n */\n reset: function () {\n // Reset data buffer\n BufferedBlockAlgorithm.reset.call(this);\n\n // Perform concrete-cipher logic\n this._doReset();\n },\n /**\n * Adds data to be encrypted or decrypted.\n *\n * @param {WordArray|string} dataUpdate The data to encrypt or decrypt.\n *\n * @return {WordArray} The data after processing.\n *\n * @example\n *\n * var encrypted = cipher.process('data');\n * var encrypted = cipher.process(wordArray);\n */\n process: function (dataUpdate) {\n // Append\n this._append(dataUpdate);\n\n // Process available blocks\n return this._process();\n },\n /**\n * Finalizes the encryption or decryption process.\n * Note that the finalize operation is effectively a destructive, read-once operation.\n *\n * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt.\n *\n * @return {WordArray} The data after final processing.\n *\n * @example\n *\n * var encrypted = cipher.finalize();\n * var encrypted = cipher.finalize('data');\n * var encrypted = cipher.finalize(wordArray);\n */\n finalize: function (dataUpdate) {\n // Final data update\n if (dataUpdate) {\n this._append(dataUpdate);\n }\n\n // Perform concrete-cipher logic\n var finalProcessedData = this._doFinalize();\n return finalProcessedData;\n },\n keySize: 128 / 32,\n ivSize: 128 / 32,\n _ENC_XFORM_MODE: 1,\n _DEC_XFORM_MODE: 2,\n /**\n * Creates shortcut functions to a cipher's object interface.\n *\n * @param {Cipher} cipher The cipher to create a helper for.\n *\n * @return {Object} An object with encrypt and decrypt shortcut functions.\n *\n * @static\n *\n * @example\n *\n * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);\n */\n _createHelper: function () {\n function selectCipherStrategy(key) {\n if (typeof key == 'string') {\n return PasswordBasedCipher;\n } else {\n return SerializableCipher;\n }\n }\n return function (cipher) {\n return {\n encrypt: function (message, key, cfg) {\n return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);\n },\n decrypt: function (ciphertext, key, cfg) {\n return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);\n }\n };\n };\n }()\n });\n\n /**\n * Abstract base stream cipher template.\n *\n * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits)\n */\n var StreamCipher = C_lib.StreamCipher = Cipher.extend({\n _doFinalize: function () {\n // Process partial blocks\n var finalProcessedBlocks = this._process(!!'flush');\n return finalProcessedBlocks;\n },\n blockSize: 1\n });\n\n /**\n * Mode namespace.\n */\n var C_mode = C.mode = {};\n\n /**\n * Abstract base block cipher mode template.\n */\n var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({\n /**\n * Creates this mode for encryption.\n *\n * @param {Cipher} cipher A block cipher instance.\n * @param {Array} iv The IV words.\n *\n * @static\n *\n * @example\n *\n * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);\n */\n createEncryptor: function (cipher, iv) {\n return this.Encryptor.create(cipher, iv);\n },\n /**\n * Creates this mode for decryption.\n *\n * @param {Cipher} cipher A block cipher instance.\n * @param {Array} iv The IV words.\n *\n * @static\n *\n * @example\n *\n * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);\n */\n createDecryptor: function (cipher, iv) {\n return this.Decryptor.create(cipher, iv);\n },\n /**\n * Initializes a newly created mode.\n *\n * @param {Cipher} cipher A block cipher instance.\n * @param {Array} iv The IV words.\n *\n * @example\n *\n * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);\n */\n init: function (cipher, iv) {\n this._cipher = cipher;\n this._iv = iv;\n }\n });\n\n /**\n * Cipher Block Chaining mode.\n */\n var CBC = C_mode.CBC = function () {\n /**\n * Abstract base CBC mode.\n */\n var CBC = BlockCipherMode.extend();\n\n /**\n * CBC encryptor.\n */\n CBC.Encryptor = CBC.extend({\n /**\n * Processes the data block at offset.\n *\n * @param {Array} words The data words to operate on.\n * @param {number} offset The offset where the block starts.\n *\n * @example\n *\n * mode.processBlock(data.words, offset);\n */\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n\n // XOR and encrypt\n xorBlock.call(this, words, offset, blockSize);\n cipher.encryptBlock(words, offset);\n\n // Remember this block to use with next block\n this._prevBlock = words.slice(offset, offset + blockSize);\n }\n });\n\n /**\n * CBC decryptor.\n */\n CBC.Decryptor = CBC.extend({\n /**\n * Processes the data block at offset.\n *\n * @param {Array} words The data words to operate on.\n * @param {number} offset The offset where the block starts.\n *\n * @example\n *\n * mode.processBlock(data.words, offset);\n */\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n\n // Remember this block to use with next block\n var thisBlock = words.slice(offset, offset + blockSize);\n\n // Decrypt and XOR\n cipher.decryptBlock(words, offset);\n xorBlock.call(this, words, offset, blockSize);\n\n // This block becomes the previous block\n this._prevBlock = thisBlock;\n }\n });\n function xorBlock(words, offset, blockSize) {\n var block;\n\n // Shortcut\n var iv = this._iv;\n\n // Choose mixing block\n if (iv) {\n block = iv;\n\n // Remove IV for subsequent blocks\n this._iv = undefined;\n } else {\n block = this._prevBlock;\n }\n\n // XOR blocks\n for (var i = 0; i < blockSize; i++) {\n words[offset + i] ^= block[i];\n }\n }\n return CBC;\n }();\n\n /**\n * Padding namespace.\n */\n var C_pad = C.pad = {};\n\n /**\n * PKCS #5/7 padding strategy.\n */\n var Pkcs7 = C_pad.Pkcs7 = {\n /**\n * Pads data using the algorithm defined in PKCS #5/7.\n *\n * @param {WordArray} data The data to pad.\n * @param {number} blockSize The multiple that the data should be padded to.\n *\n * @static\n *\n * @example\n *\n * CryptoJS.pad.Pkcs7.pad(wordArray, 4);\n */\n pad: function (data, blockSize) {\n // Shortcut\n var blockSizeBytes = blockSize * 4;\n\n // Count padding bytes\n var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;\n\n // Create padding word\n var paddingWord = nPaddingBytes << 24 | nPaddingBytes << 16 | nPaddingBytes << 8 | nPaddingBytes;\n\n // Create padding\n var paddingWords = [];\n for (var i = 0; i < nPaddingBytes; i += 4) {\n paddingWords.push(paddingWord);\n }\n var padding = WordArray.create(paddingWords, nPaddingBytes);\n\n // Add padding\n data.concat(padding);\n },\n /**\n * Unpads data that had been padded using the algorithm defined in PKCS #5/7.\n *\n * @param {WordArray} data The data to unpad.\n *\n * @static\n *\n * @example\n *\n * CryptoJS.pad.Pkcs7.unpad(wordArray);\n */\n unpad: function (data) {\n // Get number of padding bytes from last byte\n var nPaddingBytes = data.words[data.sigBytes - 1 >>> 2] & 0xff;\n\n // Remove padding\n data.sigBytes -= nPaddingBytes;\n }\n };\n\n /**\n * Abstract base block cipher template.\n *\n * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits)\n */\n var BlockCipher = C_lib.BlockCipher = Cipher.extend({\n /**\n * Configuration options.\n *\n * @property {Mode} mode The block mode to use. Default: CBC\n * @property {Padding} padding The padding strategy to use. Default: Pkcs7\n */\n cfg: Cipher.cfg.extend({\n mode: CBC,\n padding: Pkcs7\n }),\n reset: function () {\n var modeCreator;\n\n // Reset cipher\n Cipher.reset.call(this);\n\n // Shortcuts\n var cfg = this.cfg;\n var iv = cfg.iv;\n var mode = cfg.mode;\n\n // Reset block mode\n if (this._xformMode == this._ENC_XFORM_MODE) {\n modeCreator = mode.createEncryptor;\n } else /* if (this._xformMode == this._DEC_XFORM_MODE) */{\n modeCreator = mode.createDecryptor;\n // Keep at least one block in the buffer for unpadding\n this._minBufferSize = 1;\n }\n if (this._mode && this._mode.__creator == modeCreator) {\n this._mode.init(this, iv && iv.words);\n } else {\n this._mode = modeCreator.call(mode, this, iv && iv.words);\n this._mode.__creator = modeCreator;\n }\n },\n _doProcessBlock: function (words, offset) {\n this._mode.processBlock(words, offset);\n },\n _doFinalize: function () {\n var finalProcessedBlocks;\n\n // Shortcut\n var padding = this.cfg.padding;\n\n // Finalize\n if (this._xformMode == this._ENC_XFORM_MODE) {\n // Pad data\n padding.pad(this._data, this.blockSize);\n\n // Process final blocks\n finalProcessedBlocks = this._process(!!'flush');\n } else /* if (this._xformMode == this._DEC_XFORM_MODE) */{\n // Process final blocks\n finalProcessedBlocks = this._process(!!'flush');\n\n // Unpad data\n padding.unpad(finalProcessedBlocks);\n }\n return finalProcessedBlocks;\n },\n blockSize: 128 / 32\n });\n\n /**\n * A collection of cipher parameters.\n *\n * @property {WordArray} ciphertext The raw ciphertext.\n * @property {WordArray} key The key to this ciphertext.\n * @property {WordArray} iv The IV used in the ciphering operation.\n * @property {WordArray} salt The salt used with a key derivation function.\n * @property {Cipher} algorithm The cipher algorithm.\n * @property {Mode} mode The block mode used in the ciphering operation.\n * @property {Padding} padding The padding scheme used in the ciphering operation.\n * @property {number} blockSize The block size of the cipher.\n * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string.\n */\n var CipherParams = C_lib.CipherParams = Base.extend({\n /**\n * Initializes a newly created cipher params object.\n *\n * @param {Object} cipherParams An object with any of the possible cipher parameters.\n *\n * @example\n *\n * var cipherParams = CryptoJS.lib.CipherParams.create({\n * ciphertext: ciphertextWordArray,\n * key: keyWordArray,\n * iv: ivWordArray,\n * salt: saltWordArray,\n * algorithm: CryptoJS.algo.AES,\n * mode: CryptoJS.mode.CBC,\n * padding: CryptoJS.pad.PKCS7,\n * blockSize: 4,\n * formatter: CryptoJS.format.OpenSSL\n * });\n */\n init: function (cipherParams) {\n this.mixIn(cipherParams);\n },\n /**\n * Converts this cipher params object to a string.\n *\n * @param {Format} formatter (Optional) The formatting strategy to use.\n *\n * @return {string} The stringified cipher params.\n *\n * @throws Error If neither the formatter nor the default formatter is set.\n *\n * @example\n *\n * var string = cipherParams + '';\n * var string = cipherParams.toString();\n * var string = cipherParams.toString(CryptoJS.format.OpenSSL);\n */\n toString: function (formatter) {\n return (formatter || this.formatter).stringify(this);\n }\n });\n\n /**\n * Format namespace.\n */\n var C_format = C.format = {};\n\n /**\n * OpenSSL formatting strategy.\n */\n var OpenSSLFormatter = C_format.OpenSSL = {\n /**\n * Converts a cipher params object to an OpenSSL-compatible string.\n *\n * @param {CipherParams} cipherParams The cipher params object.\n *\n * @return {string} The OpenSSL-compatible string.\n *\n * @static\n *\n * @example\n *\n * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);\n */\n stringify: function (cipherParams) {\n var wordArray;\n\n // Shortcuts\n var ciphertext = cipherParams.ciphertext;\n var salt = cipherParams.salt;\n\n // Format\n if (salt) {\n wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);\n } else {\n wordArray = ciphertext;\n }\n return wordArray.toString(Base64);\n },\n /**\n * Converts an OpenSSL-compatible string to a cipher params object.\n *\n * @param {string} openSSLStr The OpenSSL-compatible string.\n *\n * @return {CipherParams} The cipher params object.\n *\n * @static\n *\n * @example\n *\n * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);\n */\n parse: function (openSSLStr) {\n var salt;\n\n // Parse base64\n var ciphertext = Base64.parse(openSSLStr);\n\n // Shortcut\n var ciphertextWords = ciphertext.words;\n\n // Test for salt\n if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {\n // Extract salt\n salt = WordArray.create(ciphertextWords.slice(2, 4));\n\n // Remove salt from ciphertext\n ciphertextWords.splice(0, 4);\n ciphertext.sigBytes -= 16;\n }\n return CipherParams.create({\n ciphertext: ciphertext,\n salt: salt\n });\n }\n };\n\n /**\n * A cipher wrapper that returns ciphertext as a serializable cipher params object.\n */\n var SerializableCipher = C_lib.SerializableCipher = Base.extend({\n /**\n * Configuration options.\n *\n * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL\n */\n cfg: Base.extend({\n format: OpenSSLFormatter\n }),\n /**\n * Encrypts a message.\n *\n * @param {Cipher} cipher The cipher algorithm to use.\n * @param {WordArray|string} message The message to encrypt.\n * @param {WordArray} key The key.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {CipherParams} A cipher params object.\n *\n * @static\n *\n * @example\n *\n * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);\n * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });\n * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });\n */\n encrypt: function (cipher, message, key, cfg) {\n // Apply config defaults\n cfg = this.cfg.extend(cfg);\n\n // Encrypt\n var encryptor = cipher.createEncryptor(key, cfg);\n var ciphertext = encryptor.finalize(message);\n\n // Shortcut\n var cipherCfg = encryptor.cfg;\n\n // Create and return serializable cipher params\n return CipherParams.create({\n ciphertext: ciphertext,\n key: key,\n iv: cipherCfg.iv,\n algorithm: cipher,\n mode: cipherCfg.mode,\n padding: cipherCfg.padding,\n blockSize: cipher.blockSize,\n formatter: cfg.format\n });\n },\n /**\n * Decrypts serialized ciphertext.\n *\n * @param {Cipher} cipher The cipher algorithm to use.\n * @param {CipherParams|string} ciphertext The ciphertext to decrypt.\n * @param {WordArray} key The key.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {WordArray} The plaintext.\n *\n * @static\n *\n * @example\n *\n * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });\n * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });\n */\n decrypt: function (cipher, ciphertext, key, cfg) {\n // Apply config defaults\n cfg = this.cfg.extend(cfg);\n\n // Convert string to CipherParams\n ciphertext = this._parse(ciphertext, cfg.format);\n\n // Decrypt\n var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);\n return plaintext;\n },\n /**\n * Converts serialized ciphertext to CipherParams,\n * else assumed CipherParams already and returns ciphertext unchanged.\n *\n * @param {CipherParams|string} ciphertext The ciphertext.\n * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext.\n *\n * @return {CipherParams} The unserialized ciphertext.\n *\n * @static\n *\n * @example\n *\n * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);\n */\n _parse: function (ciphertext, format) {\n if (typeof ciphertext == 'string') {\n return format.parse(ciphertext, this);\n } else {\n return ciphertext;\n }\n }\n });\n\n /**\n * Key derivation function namespace.\n */\n var C_kdf = C.kdf = {};\n\n /**\n * OpenSSL key derivation function.\n */\n var OpenSSLKdf = C_kdf.OpenSSL = {\n /**\n * Derives a key and IV from a password.\n *\n * @param {string} password The password to derive from.\n * @param {number} keySize The size in words of the key to generate.\n * @param {number} ivSize The size in words of the IV to generate.\n * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.\n *\n * @return {CipherParams} A cipher params object with the key, IV, and salt.\n *\n * @static\n *\n * @example\n *\n * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);\n * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');\n */\n execute: function (password, keySize, ivSize, salt, hasher) {\n // Generate random salt\n if (!salt) {\n salt = WordArray.random(64 / 8);\n }\n\n // Derive key and IV\n if (!hasher) {\n var key = EvpKDF.create({\n keySize: keySize + ivSize\n }).compute(password, salt);\n } else {\n var key = EvpKDF.create({\n keySize: keySize + ivSize,\n hasher: hasher\n }).compute(password, salt);\n }\n\n // Separate key and IV\n var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);\n key.sigBytes = keySize * 4;\n\n // Return params\n return CipherParams.create({\n key: key,\n iv: iv,\n salt: salt\n });\n }\n };\n\n /**\n * A serializable cipher wrapper that derives the key from a password,\n * and returns ciphertext as a serializable cipher params object.\n */\n var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({\n /**\n * Configuration options.\n *\n * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL\n */\n cfg: SerializableCipher.cfg.extend({\n kdf: OpenSSLKdf\n }),\n /**\n * Encrypts a message using a password.\n *\n * @param {Cipher} cipher The cipher algorithm to use.\n * @param {WordArray|string} message The message to encrypt.\n * @param {string} password The password.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {CipherParams} A cipher params object.\n *\n * @static\n *\n * @example\n *\n * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');\n * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });\n */\n encrypt: function (cipher, message, password, cfg) {\n // Apply config defaults\n cfg = this.cfg.extend(cfg);\n\n // Derive key and other params\n var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, cfg.salt, cfg.hasher);\n\n // Add IV to config\n cfg.iv = derivedParams.iv;\n\n // Encrypt\n var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg);\n\n // Mix in derived params\n ciphertext.mixIn(derivedParams);\n return ciphertext;\n },\n /**\n * Decrypts serialized ciphertext using a password.\n *\n * @param {Cipher} cipher The cipher algorithm to use.\n * @param {CipherParams|string} ciphertext The ciphertext to decrypt.\n * @param {string} password The password.\n * @param {Object} cfg (Optional) The configuration options to use for this operation.\n *\n * @return {WordArray} The plaintext.\n *\n * @static\n *\n * @example\n *\n * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });\n * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });\n */\n decrypt: function (cipher, ciphertext, password, cfg) {\n // Apply config defaults\n cfg = this.cfg.extend(cfg);\n\n // Convert string to CipherParams\n ciphertext = this._parse(ciphertext, cfg.format);\n\n // Derive key and other params\n var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt, cfg.hasher);\n\n // Add IV to config\n cfg.iv = derivedParams.iv;\n\n // Decrypt\n var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);\n return plaintext;\n }\n });\n }();\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Cipher Feedback block mode.\n */\n CryptoJS.mode.CFB = function () {\n var CFB = CryptoJS.lib.BlockCipherMode.extend();\n CFB.Encryptor = CFB.extend({\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);\n\n // Remember this block to use with next block\n this._prevBlock = words.slice(offset, offset + blockSize);\n }\n });\n CFB.Decryptor = CFB.extend({\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n\n // Remember this block to use with next block\n var thisBlock = words.slice(offset, offset + blockSize);\n generateKeystreamAndEncrypt.call(this, words, offset, blockSize, cipher);\n\n // This block becomes the previous block\n this._prevBlock = thisBlock;\n }\n });\n function generateKeystreamAndEncrypt(words, offset, blockSize, cipher) {\n var keystream;\n\n // Shortcut\n var iv = this._iv;\n\n // Generate keystream\n if (iv) {\n keystream = iv.slice(0);\n\n // Remove IV for subsequent blocks\n this._iv = undefined;\n } else {\n keystream = this._prevBlock;\n }\n cipher.encryptBlock(keystream, 0);\n\n // Encrypt\n for (var i = 0; i < blockSize; i++) {\n words[offset + i] ^= keystream[i];\n }\n }\n return CFB;\n }();\n return CryptoJS.mode.CFB;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Counter block mode.\n */\n CryptoJS.mode.CTR = function () {\n var CTR = CryptoJS.lib.BlockCipherMode.extend();\n var Encryptor = CTR.Encryptor = CTR.extend({\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n var iv = this._iv;\n var counter = this._counter;\n\n // Generate keystream\n if (iv) {\n counter = this._counter = iv.slice(0);\n\n // Remove IV for subsequent blocks\n this._iv = undefined;\n }\n var keystream = counter.slice(0);\n cipher.encryptBlock(keystream, 0);\n\n // Increment counter\n counter[blockSize - 1] = counter[blockSize - 1] + 1 | 0;\n\n // Encrypt\n for (var i = 0; i < blockSize; i++) {\n words[offset + i] ^= keystream[i];\n }\n }\n });\n CTR.Decryptor = Encryptor;\n return CTR;\n }();\n return CryptoJS.mode.CTR;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /** @preserve\n * Counter block mode compatible with Dr Brian Gladman fileenc.c\n * derived from CryptoJS.mode.CTR\n * Jan Hruby jhruby.web@gmail.com\n */\n CryptoJS.mode.CTRGladman = function () {\n var CTRGladman = CryptoJS.lib.BlockCipherMode.extend();\n function incWord(word) {\n if ((word >> 24 & 0xff) === 0xff) {\n //overflow\n var b1 = word >> 16 & 0xff;\n var b2 = word >> 8 & 0xff;\n var b3 = word & 0xff;\n if (b1 === 0xff)\n // overflow b1\n {\n b1 = 0;\n if (b2 === 0xff) {\n b2 = 0;\n if (b3 === 0xff) {\n b3 = 0;\n } else {\n ++b3;\n }\n } else {\n ++b2;\n }\n } else {\n ++b1;\n }\n word = 0;\n word += b1 << 16;\n word += b2 << 8;\n word += b3;\n } else {\n word += 0x01 << 24;\n }\n return word;\n }\n function incCounter(counter) {\n if ((counter[0] = incWord(counter[0])) === 0) {\n // encr_data in fileenc.c from Dr Brian Gladman's counts only with DWORD j < 8\n counter[1] = incWord(counter[1]);\n }\n return counter;\n }\n var Encryptor = CTRGladman.Encryptor = CTRGladman.extend({\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n var iv = this._iv;\n var counter = this._counter;\n\n // Generate keystream\n if (iv) {\n counter = this._counter = iv.slice(0);\n\n // Remove IV for subsequent blocks\n this._iv = undefined;\n }\n incCounter(counter);\n var keystream = counter.slice(0);\n cipher.encryptBlock(keystream, 0);\n\n // Encrypt\n for (var i = 0; i < blockSize; i++) {\n words[offset + i] ^= keystream[i];\n }\n }\n });\n CTRGladman.Decryptor = Encryptor;\n return CTRGladman;\n }();\n return CryptoJS.mode.CTRGladman;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Output Feedback block mode.\n */\n CryptoJS.mode.OFB = function () {\n var OFB = CryptoJS.lib.BlockCipherMode.extend();\n var Encryptor = OFB.Encryptor = OFB.extend({\n processBlock: function (words, offset) {\n // Shortcuts\n var cipher = this._cipher;\n var blockSize = cipher.blockSize;\n var iv = this._iv;\n var keystream = this._keystream;\n\n // Generate keystream\n if (iv) {\n keystream = this._keystream = iv.slice(0);\n\n // Remove IV for subsequent blocks\n this._iv = undefined;\n }\n cipher.encryptBlock(keystream, 0);\n\n // Encrypt\n for (var i = 0; i < blockSize; i++) {\n words[offset + i] ^= keystream[i];\n }\n }\n });\n OFB.Decryptor = Encryptor;\n return OFB;\n }();\n return CryptoJS.mode.OFB;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Electronic Codebook block mode.\n */\n CryptoJS.mode.ECB = function () {\n var ECB = CryptoJS.lib.BlockCipherMode.extend();\n ECB.Encryptor = ECB.extend({\n processBlock: function (words, offset) {\n this._cipher.encryptBlock(words, offset);\n }\n });\n ECB.Decryptor = ECB.extend({\n processBlock: function (words, offset) {\n this._cipher.decryptBlock(words, offset);\n }\n });\n return ECB;\n }();\n return CryptoJS.mode.ECB;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * ANSI X.923 padding strategy.\n */\n CryptoJS.pad.AnsiX923 = {\n pad: function (data, blockSize) {\n // Shortcuts\n var dataSigBytes = data.sigBytes;\n var blockSizeBytes = blockSize * 4;\n\n // Count padding bytes\n var nPaddingBytes = blockSizeBytes - dataSigBytes % blockSizeBytes;\n\n // Compute last byte position\n var lastBytePos = dataSigBytes + nPaddingBytes - 1;\n\n // Pad\n data.clamp();\n data.words[lastBytePos >>> 2] |= nPaddingBytes << 24 - lastBytePos % 4 * 8;\n data.sigBytes += nPaddingBytes;\n },\n unpad: function (data) {\n // Get number of padding bytes from last byte\n var nPaddingBytes = data.words[data.sigBytes - 1 >>> 2] & 0xff;\n\n // Remove padding\n data.sigBytes -= nPaddingBytes;\n }\n };\n return CryptoJS.pad.Ansix923;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * ISO 10126 padding strategy.\n */\n CryptoJS.pad.Iso10126 = {\n pad: function (data, blockSize) {\n // Shortcut\n var blockSizeBytes = blockSize * 4;\n\n // Count padding bytes\n var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;\n\n // Pad\n data.concat(CryptoJS.lib.WordArray.random(nPaddingBytes - 1)).concat(CryptoJS.lib.WordArray.create([nPaddingBytes << 24], 1));\n },\n unpad: function (data) {\n // Get number of padding bytes from last byte\n var nPaddingBytes = data.words[data.sigBytes - 1 >>> 2] & 0xff;\n\n // Remove padding\n data.sigBytes -= nPaddingBytes;\n }\n };\n return CryptoJS.pad.Iso10126;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * ISO/IEC 9797-1 Padding Method 2.\n */\n CryptoJS.pad.Iso97971 = {\n pad: function (data, blockSize) {\n // Add 0x80 byte\n data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1));\n\n // Zero pad the rest\n CryptoJS.pad.ZeroPadding.pad(data, blockSize);\n },\n unpad: function (data) {\n // Remove zero padding\n CryptoJS.pad.ZeroPadding.unpad(data);\n\n // Remove one more byte -- the 0x80 byte\n data.sigBytes--;\n }\n };\n return CryptoJS.pad.Iso97971;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * Zero padding strategy.\n */\n CryptoJS.pad.ZeroPadding = {\n pad: function (data, blockSize) {\n // Shortcut\n var blockSizeBytes = blockSize * 4;\n\n // Pad\n data.clamp();\n data.sigBytes += blockSizeBytes - (data.sigBytes % blockSizeBytes || blockSizeBytes);\n },\n unpad: function (data) {\n // Shortcut\n var dataWords = data.words;\n\n // Unpad\n var i = data.sigBytes - 1;\n for (var i = data.sigBytes - 1; i >= 0; i--) {\n if (dataWords[i >>> 2] >>> 24 - i % 4 * 8 & 0xff) {\n data.sigBytes = i + 1;\n break;\n }\n }\n }\n };\n return CryptoJS.pad.ZeroPadding;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n /**\n * A noop padding strategy.\n */\n CryptoJS.pad.NoPadding = {\n pad: function () {},\n unpad: function () {}\n };\n return CryptoJS.pad.NoPadding;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function (undefined) {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var CipherParams = C_lib.CipherParams;\n var C_enc = C.enc;\n var Hex = C_enc.Hex;\n var C_format = C.format;\n var HexFormatter = C_format.Hex = {\n /**\n * Converts the ciphertext of a cipher params object to a hexadecimally encoded string.\n *\n * @param {CipherParams} cipherParams The cipher params object.\n *\n * @return {string} The hexadecimally encoded string.\n *\n * @static\n *\n * @example\n *\n * var hexString = CryptoJS.format.Hex.stringify(cipherParams);\n */\n stringify: function (cipherParams) {\n return cipherParams.ciphertext.toString(Hex);\n },\n /**\n * Converts a hexadecimally encoded ciphertext string to a cipher params object.\n *\n * @param {string} input The hexadecimally encoded string.\n *\n * @return {CipherParams} The cipher params object.\n *\n * @static\n *\n * @example\n *\n * var cipherParams = CryptoJS.format.Hex.parse(hexString);\n */\n parse: function (input) {\n var ciphertext = Hex.parse(input);\n return CipherParams.create({\n ciphertext: ciphertext\n });\n }\n };\n })();\n return CryptoJS.format.Hex;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var BlockCipher = C_lib.BlockCipher;\n var C_algo = C.algo;\n\n // Lookup tables\n var SBOX = [];\n var INV_SBOX = [];\n var SUB_MIX_0 = [];\n var SUB_MIX_1 = [];\n var SUB_MIX_2 = [];\n var SUB_MIX_3 = [];\n var INV_SUB_MIX_0 = [];\n var INV_SUB_MIX_1 = [];\n var INV_SUB_MIX_2 = [];\n var INV_SUB_MIX_3 = [];\n\n // Compute lookup tables\n (function () {\n // Compute double table\n var d = [];\n for (var i = 0; i < 256; i++) {\n if (i < 128) {\n d[i] = i << 1;\n } else {\n d[i] = i << 1 ^ 0x11b;\n }\n }\n\n // Walk GF(2^8)\n var x = 0;\n var xi = 0;\n for (var i = 0; i < 256; i++) {\n // Compute sbox\n var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;\n sx = sx >>> 8 ^ sx & 0xff ^ 0x63;\n SBOX[x] = sx;\n INV_SBOX[sx] = x;\n\n // Compute multiplication\n var x2 = d[x];\n var x4 = d[x2];\n var x8 = d[x4];\n\n // Compute sub bytes, mix columns tables\n var t = d[sx] * 0x101 ^ sx * 0x1010100;\n SUB_MIX_0[x] = t << 24 | t >>> 8;\n SUB_MIX_1[x] = t << 16 | t >>> 16;\n SUB_MIX_2[x] = t << 8 | t >>> 24;\n SUB_MIX_3[x] = t;\n\n // Compute inv sub bytes, inv mix columns tables\n var t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;\n INV_SUB_MIX_0[sx] = t << 24 | t >>> 8;\n INV_SUB_MIX_1[sx] = t << 16 | t >>> 16;\n INV_SUB_MIX_2[sx] = t << 8 | t >>> 24;\n INV_SUB_MIX_3[sx] = t;\n\n // Compute next counter\n if (!x) {\n x = xi = 1;\n } else {\n x = x2 ^ d[d[d[x8 ^ x2]]];\n xi ^= d[d[xi]];\n }\n }\n })();\n\n // Precomputed Rcon lookup\n var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];\n\n /**\n * AES block cipher algorithm.\n */\n var AES = C_algo.AES = BlockCipher.extend({\n _doReset: function () {\n var t;\n\n // Skip reset of nRounds has been set before and key did not change\n if (this._nRounds && this._keyPriorReset === this._key) {\n return;\n }\n\n // Shortcuts\n var key = this._keyPriorReset = this._key;\n var keyWords = key.words;\n var keySize = key.sigBytes / 4;\n\n // Compute number of rounds\n var nRounds = this._nRounds = keySize + 6;\n\n // Compute number of key schedule rows\n var ksRows = (nRounds + 1) * 4;\n\n // Compute key schedule\n var keySchedule = this._keySchedule = [];\n for (var ksRow = 0; ksRow < ksRows; ksRow++) {\n if (ksRow < keySize) {\n keySchedule[ksRow] = keyWords[ksRow];\n } else {\n t = keySchedule[ksRow - 1];\n if (!(ksRow % keySize)) {\n // Rot word\n t = t << 8 | t >>> 24;\n\n // Sub word\n t = SBOX[t >>> 24] << 24 | SBOX[t >>> 16 & 0xff] << 16 | SBOX[t >>> 8 & 0xff] << 8 | SBOX[t & 0xff];\n\n // Mix Rcon\n t ^= RCON[ksRow / keySize | 0] << 24;\n } else if (keySize > 6 && ksRow % keySize == 4) {\n // Sub word\n t = SBOX[t >>> 24] << 24 | SBOX[t >>> 16 & 0xff] << 16 | SBOX[t >>> 8 & 0xff] << 8 | SBOX[t & 0xff];\n }\n keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;\n }\n }\n\n // Compute inv key schedule\n var invKeySchedule = this._invKeySchedule = [];\n for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) {\n var ksRow = ksRows - invKsRow;\n if (invKsRow % 4) {\n var t = keySchedule[ksRow];\n } else {\n var t = keySchedule[ksRow - 4];\n }\n if (invKsRow < 4 || ksRow <= 4) {\n invKeySchedule[invKsRow] = t;\n } else {\n invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[t >>> 16 & 0xff]] ^ INV_SUB_MIX_2[SBOX[t >>> 8 & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]];\n }\n }\n },\n encryptBlock: function (M, offset) {\n this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX);\n },\n decryptBlock: function (M, offset) {\n // Swap 2nd and 4th rows\n var t = M[offset + 1];\n M[offset + 1] = M[offset + 3];\n M[offset + 3] = t;\n this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);\n\n // Inv swap 2nd and 4th rows\n var t = M[offset + 1];\n M[offset + 1] = M[offset + 3];\n M[offset + 3] = t;\n },\n _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {\n // Shortcut\n var nRounds = this._nRounds;\n\n // Get input, add round key\n var s0 = M[offset] ^ keySchedule[0];\n var s1 = M[offset + 1] ^ keySchedule[1];\n var s2 = M[offset + 2] ^ keySchedule[2];\n var s3 = M[offset + 3] ^ keySchedule[3];\n\n // Key schedule row counter\n var ksRow = 4;\n\n // Rounds\n for (var round = 1; round < nRounds; round++) {\n // Shift rows, sub bytes, mix columns, add round key\n var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[s1 >>> 16 & 0xff] ^ SUB_MIX_2[s2 >>> 8 & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++];\n var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[s2 >>> 16 & 0xff] ^ SUB_MIX_2[s3 >>> 8 & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++];\n var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[s3 >>> 16 & 0xff] ^ SUB_MIX_2[s0 >>> 8 & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++];\n var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[s0 >>> 16 & 0xff] ^ SUB_MIX_2[s1 >>> 8 & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++];\n\n // Update state\n s0 = t0;\n s1 = t1;\n s2 = t2;\n s3 = t3;\n }\n\n // Shift rows, sub bytes, add round key\n var t0 = (SBOX[s0 >>> 24] << 24 | SBOX[s1 >>> 16 & 0xff] << 16 | SBOX[s2 >>> 8 & 0xff] << 8 | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++];\n var t1 = (SBOX[s1 >>> 24] << 24 | SBOX[s2 >>> 16 & 0xff] << 16 | SBOX[s3 >>> 8 & 0xff] << 8 | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++];\n var t2 = (SBOX[s2 >>> 24] << 24 | SBOX[s3 >>> 16 & 0xff] << 16 | SBOX[s0 >>> 8 & 0xff] << 8 | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++];\n var t3 = (SBOX[s3 >>> 24] << 24 | SBOX[s0 >>> 16 & 0xff] << 16 | SBOX[s1 >>> 8 & 0xff] << 8 | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++];\n\n // Set output\n M[offset] = t0;\n M[offset + 1] = t1;\n M[offset + 2] = t2;\n M[offset + 3] = t3;\n },\n keySize: 256 / 32\n });\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg);\n */\n C.AES = BlockCipher._createHelper(AES);\n })();\n return CryptoJS.AES;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var WordArray = C_lib.WordArray;\n var BlockCipher = C_lib.BlockCipher;\n var C_algo = C.algo;\n\n // Permuted Choice 1 constants\n var PC1 = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4];\n\n // Permuted Choice 2 constants\n var PC2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32];\n\n // Cumulative bit shift constants\n var BIT_SHIFTS = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28];\n\n // SBOXes and round permutation constants\n var SBOX_P = [{\n 0x0: 0x808200,\n 0x10000000: 0x8000,\n 0x20000000: 0x808002,\n 0x30000000: 0x2,\n 0x40000000: 0x200,\n 0x50000000: 0x808202,\n 0x60000000: 0x800202,\n 0x70000000: 0x800000,\n 0x80000000: 0x202,\n 0x90000000: 0x800200,\n 0xa0000000: 0x8200,\n 0xb0000000: 0x808000,\n 0xc0000000: 0x8002,\n 0xd0000000: 0x800002,\n 0xe0000000: 0x0,\n 0xf0000000: 0x8202,\n 0x8000000: 0x0,\n 0x18000000: 0x808202,\n 0x28000000: 0x8202,\n 0x38000000: 0x8000,\n 0x48000000: 0x808200,\n 0x58000000: 0x200,\n 0x68000000: 0x808002,\n 0x78000000: 0x2,\n 0x88000000: 0x800200,\n 0x98000000: 0x8200,\n 0xa8000000: 0x808000,\n 0xb8000000: 0x800202,\n 0xc8000000: 0x800002,\n 0xd8000000: 0x8002,\n 0xe8000000: 0x202,\n 0xf8000000: 0x800000,\n 0x1: 0x8000,\n 0x10000001: 0x2,\n 0x20000001: 0x808200,\n 0x30000001: 0x800000,\n 0x40000001: 0x808002,\n 0x50000001: 0x8200,\n 0x60000001: 0x200,\n 0x70000001: 0x800202,\n 0x80000001: 0x808202,\n 0x90000001: 0x808000,\n 0xa0000001: 0x800002,\n 0xb0000001: 0x8202,\n 0xc0000001: 0x202,\n 0xd0000001: 0x800200,\n 0xe0000001: 0x8002,\n 0xf0000001: 0x0,\n 0x8000001: 0x808202,\n 0x18000001: 0x808000,\n 0x28000001: 0x800000,\n 0x38000001: 0x200,\n 0x48000001: 0x8000,\n 0x58000001: 0x800002,\n 0x68000001: 0x2,\n 0x78000001: 0x8202,\n 0x88000001: 0x8002,\n 0x98000001: 0x800202,\n 0xa8000001: 0x202,\n 0xb8000001: 0x808200,\n 0xc8000001: 0x800200,\n 0xd8000001: 0x0,\n 0xe8000001: 0x8200,\n 0xf8000001: 0x808002\n }, {\n 0x0: 0x40084010,\n 0x1000000: 0x4000,\n 0x2000000: 0x80000,\n 0x3000000: 0x40080010,\n 0x4000000: 0x40000010,\n 0x5000000: 0x40084000,\n 0x6000000: 0x40004000,\n 0x7000000: 0x10,\n 0x8000000: 0x84000,\n 0x9000000: 0x40004010,\n 0xa000000: 0x40000000,\n 0xb000000: 0x84010,\n 0xc000000: 0x80010,\n 0xd000000: 0x0,\n 0xe000000: 0x4010,\n 0xf000000: 0x40080000,\n 0x800000: 0x40004000,\n 0x1800000: 0x84010,\n 0x2800000: 0x10,\n 0x3800000: 0x40004010,\n 0x4800000: 0x40084010,\n 0x5800000: 0x40000000,\n 0x6800000: 0x80000,\n 0x7800000: 0x40080010,\n 0x8800000: 0x80010,\n 0x9800000: 0x0,\n 0xa800000: 0x4000,\n 0xb800000: 0x40080000,\n 0xc800000: 0x40000010,\n 0xd800000: 0x84000,\n 0xe800000: 0x40084000,\n 0xf800000: 0x4010,\n 0x10000000: 0x0,\n 0x11000000: 0x40080010,\n 0x12000000: 0x40004010,\n 0x13000000: 0x40084000,\n 0x14000000: 0x40080000,\n 0x15000000: 0x10,\n 0x16000000: 0x84010,\n 0x17000000: 0x4000,\n 0x18000000: 0x4010,\n 0x19000000: 0x80000,\n 0x1a000000: 0x80010,\n 0x1b000000: 0x40000010,\n 0x1c000000: 0x84000,\n 0x1d000000: 0x40004000,\n 0x1e000000: 0x40000000,\n 0x1f000000: 0x40084010,\n 0x10800000: 0x84010,\n 0x11800000: 0x80000,\n 0x12800000: 0x40080000,\n 0x13800000: 0x4000,\n 0x14800000: 0x40004000,\n 0x15800000: 0x40084010,\n 0x16800000: 0x10,\n 0x17800000: 0x40000000,\n 0x18800000: 0x40084000,\n 0x19800000: 0x40000010,\n 0x1a800000: 0x40004010,\n 0x1b800000: 0x80010,\n 0x1c800000: 0x0,\n 0x1d800000: 0x4010,\n 0x1e800000: 0x40080010,\n 0x1f800000: 0x84000\n }, {\n 0x0: 0x104,\n 0x100000: 0x0,\n 0x200000: 0x4000100,\n 0x300000: 0x10104,\n 0x400000: 0x10004,\n 0x500000: 0x4000004,\n 0x600000: 0x4010104,\n 0x700000: 0x4010000,\n 0x800000: 0x4000000,\n 0x900000: 0x4010100,\n 0xa00000: 0x10100,\n 0xb00000: 0x4010004,\n 0xc00000: 0x4000104,\n 0xd00000: 0x10000,\n 0xe00000: 0x4,\n 0xf00000: 0x100,\n 0x80000: 0x4010100,\n 0x180000: 0x4010004,\n 0x280000: 0x0,\n 0x380000: 0x4000100,\n 0x480000: 0x4000004,\n 0x580000: 0x10000,\n 0x680000: 0x10004,\n 0x780000: 0x104,\n 0x880000: 0x4,\n 0x980000: 0x100,\n 0xa80000: 0x4010000,\n 0xb80000: 0x10104,\n 0xc80000: 0x10100,\n 0xd80000: 0x4000104,\n 0xe80000: 0x4010104,\n 0xf80000: 0x4000000,\n 0x1000000: 0x4010100,\n 0x1100000: 0x10004,\n 0x1200000: 0x10000,\n 0x1300000: 0x4000100,\n 0x1400000: 0x100,\n 0x1500000: 0x4010104,\n 0x1600000: 0x4000004,\n 0x1700000: 0x0,\n 0x1800000: 0x4000104,\n 0x1900000: 0x4000000,\n 0x1a00000: 0x4,\n 0x1b00000: 0x10100,\n 0x1c00000: 0x4010000,\n 0x1d00000: 0x104,\n 0x1e00000: 0x10104,\n 0x1f00000: 0x4010004,\n 0x1080000: 0x4000000,\n 0x1180000: 0x104,\n 0x1280000: 0x4010100,\n 0x1380000: 0x0,\n 0x1480000: 0x10004,\n 0x1580000: 0x4000100,\n 0x1680000: 0x100,\n 0x1780000: 0x4010004,\n 0x1880000: 0x10000,\n 0x1980000: 0x4010104,\n 0x1a80000: 0x10104,\n 0x1b80000: 0x4000004,\n 0x1c80000: 0x4000104,\n 0x1d80000: 0x4010000,\n 0x1e80000: 0x4,\n 0x1f80000: 0x10100\n }, {\n 0x0: 0x80401000,\n 0x10000: 0x80001040,\n 0x20000: 0x401040,\n 0x30000: 0x80400000,\n 0x40000: 0x0,\n 0x50000: 0x401000,\n 0x60000: 0x80000040,\n 0x70000: 0x400040,\n 0x80000: 0x80000000,\n 0x90000: 0x400000,\n 0xa0000: 0x40,\n 0xb0000: 0x80001000,\n 0xc0000: 0x80400040,\n 0xd0000: 0x1040,\n 0xe0000: 0x1000,\n 0xf0000: 0x80401040,\n 0x8000: 0x80001040,\n 0x18000: 0x40,\n 0x28000: 0x80400040,\n 0x38000: 0x80001000,\n 0x48000: 0x401000,\n 0x58000: 0x80401040,\n 0x68000: 0x0,\n 0x78000: 0x80400000,\n 0x88000: 0x1000,\n 0x98000: 0x80401000,\n 0xa8000: 0x400000,\n 0xb8000: 0x1040,\n 0xc8000: 0x80000000,\n 0xd8000: 0x400040,\n 0xe8000: 0x401040,\n 0xf8000: 0x80000040,\n 0x100000: 0x400040,\n 0x110000: 0x401000,\n 0x120000: 0x80000040,\n 0x130000: 0x0,\n 0x140000: 0x1040,\n 0x150000: 0x80400040,\n 0x160000: 0x80401000,\n 0x170000: 0x80001040,\n 0x180000: 0x80401040,\n 0x190000: 0x80000000,\n 0x1a0000: 0x80400000,\n 0x1b0000: 0x401040,\n 0x1c0000: 0x80001000,\n 0x1d0000: 0x400000,\n 0x1e0000: 0x40,\n 0x1f0000: 0x1000,\n 0x108000: 0x80400000,\n 0x118000: 0x80401040,\n 0x128000: 0x0,\n 0x138000: 0x401000,\n 0x148000: 0x400040,\n 0x158000: 0x80000000,\n 0x168000: 0x80001040,\n 0x178000: 0x40,\n 0x188000: 0x80000040,\n 0x198000: 0x1000,\n 0x1a8000: 0x80001000,\n 0x1b8000: 0x80400040,\n 0x1c8000: 0x1040,\n 0x1d8000: 0x80401000,\n 0x1e8000: 0x400000,\n 0x1f8000: 0x401040\n }, {\n 0x0: 0x80,\n 0x1000: 0x1040000,\n 0x2000: 0x40000,\n 0x3000: 0x20000000,\n 0x4000: 0x20040080,\n 0x5000: 0x1000080,\n 0x6000: 0x21000080,\n 0x7000: 0x40080,\n 0x8000: 0x1000000,\n 0x9000: 0x20040000,\n 0xa000: 0x20000080,\n 0xb000: 0x21040080,\n 0xc000: 0x21040000,\n 0xd000: 0x0,\n 0xe000: 0x1040080,\n 0xf000: 0x21000000,\n 0x800: 0x1040080,\n 0x1800: 0x21000080,\n 0x2800: 0x80,\n 0x3800: 0x1040000,\n 0x4800: 0x40000,\n 0x5800: 0x20040080,\n 0x6800: 0x21040000,\n 0x7800: 0x20000000,\n 0x8800: 0x20040000,\n 0x9800: 0x0,\n 0xa800: 0x21040080,\n 0xb800: 0x1000080,\n 0xc800: 0x20000080,\n 0xd800: 0x21000000,\n 0xe800: 0x1000000,\n 0xf800: 0x40080,\n 0x10000: 0x40000,\n 0x11000: 0x80,\n 0x12000: 0x20000000,\n 0x13000: 0x21000080,\n 0x14000: 0x1000080,\n 0x15000: 0x21040000,\n 0x16000: 0x20040080,\n 0x17000: 0x1000000,\n 0x18000: 0x21040080,\n 0x19000: 0x21000000,\n 0x1a000: 0x1040000,\n 0x1b000: 0x20040000,\n 0x1c000: 0x40080,\n 0x1d000: 0x20000080,\n 0x1e000: 0x0,\n 0x1f000: 0x1040080,\n 0x10800: 0x21000080,\n 0x11800: 0x1000000,\n 0x12800: 0x1040000,\n 0x13800: 0x20040080,\n 0x14800: 0x20000000,\n 0x15800: 0x1040080,\n 0x16800: 0x80,\n 0x17800: 0x21040000,\n 0x18800: 0x40080,\n 0x19800: 0x21040080,\n 0x1a800: 0x0,\n 0x1b800: 0x21000000,\n 0x1c800: 0x1000080,\n 0x1d800: 0x40000,\n 0x1e800: 0x20040000,\n 0x1f800: 0x20000080\n }, {\n 0x0: 0x10000008,\n 0x100: 0x2000,\n 0x200: 0x10200000,\n 0x300: 0x10202008,\n 0x400: 0x10002000,\n 0x500: 0x200000,\n 0x600: 0x200008,\n 0x700: 0x10000000,\n 0x800: 0x0,\n 0x900: 0x10002008,\n 0xa00: 0x202000,\n 0xb00: 0x8,\n 0xc00: 0x10200008,\n 0xd00: 0x202008,\n 0xe00: 0x2008,\n 0xf00: 0x10202000,\n 0x80: 0x10200000,\n 0x180: 0x10202008,\n 0x280: 0x8,\n 0x380: 0x200000,\n 0x480: 0x202008,\n 0x580: 0x10000008,\n 0x680: 0x10002000,\n 0x780: 0x2008,\n 0x880: 0x200008,\n 0x980: 0x2000,\n 0xa80: 0x10002008,\n 0xb80: 0x10200008,\n 0xc80: 0x0,\n 0xd80: 0x10202000,\n 0xe80: 0x202000,\n 0xf80: 0x10000000,\n 0x1000: 0x10002000,\n 0x1100: 0x10200008,\n 0x1200: 0x10202008,\n 0x1300: 0x2008,\n 0x1400: 0x200000,\n 0x1500: 0x10000000,\n 0x1600: 0x10000008,\n 0x1700: 0x202000,\n 0x1800: 0x202008,\n 0x1900: 0x0,\n 0x1a00: 0x8,\n 0x1b00: 0x10200000,\n 0x1c00: 0x2000,\n 0x1d00: 0x10002008,\n 0x1e00: 0x10202000,\n 0x1f00: 0x200008,\n 0x1080: 0x8,\n 0x1180: 0x202000,\n 0x1280: 0x200000,\n 0x1380: 0x10000008,\n 0x1480: 0x10002000,\n 0x1580: 0x2008,\n 0x1680: 0x10202008,\n 0x1780: 0x10200000,\n 0x1880: 0x10202000,\n 0x1980: 0x10200008,\n 0x1a80: 0x2000,\n 0x1b80: 0x202008,\n 0x1c80: 0x200008,\n 0x1d80: 0x0,\n 0x1e80: 0x10000000,\n 0x1f80: 0x10002008\n }, {\n 0x0: 0x100000,\n 0x10: 0x2000401,\n 0x20: 0x400,\n 0x30: 0x100401,\n 0x40: 0x2100401,\n 0x50: 0x0,\n 0x60: 0x1,\n 0x70: 0x2100001,\n 0x80: 0x2000400,\n 0x90: 0x100001,\n 0xa0: 0x2000001,\n 0xb0: 0x2100400,\n 0xc0: 0x2100000,\n 0xd0: 0x401,\n 0xe0: 0x100400,\n 0xf0: 0x2000000,\n 0x8: 0x2100001,\n 0x18: 0x0,\n 0x28: 0x2000401,\n 0x38: 0x2100400,\n 0x48: 0x100000,\n 0x58: 0x2000001,\n 0x68: 0x2000000,\n 0x78: 0x401,\n 0x88: 0x100401,\n 0x98: 0x2000400,\n 0xa8: 0x2100000,\n 0xb8: 0x100001,\n 0xc8: 0x400,\n 0xd8: 0x2100401,\n 0xe8: 0x1,\n 0xf8: 0x100400,\n 0x100: 0x2000000,\n 0x110: 0x100000,\n 0x120: 0x2000401,\n 0x130: 0x2100001,\n 0x140: 0x100001,\n 0x150: 0x2000400,\n 0x160: 0x2100400,\n 0x170: 0x100401,\n 0x180: 0x401,\n 0x190: 0x2100401,\n 0x1a0: 0x100400,\n 0x1b0: 0x1,\n 0x1c0: 0x0,\n 0x1d0: 0x2100000,\n 0x1e0: 0x2000001,\n 0x1f0: 0x400,\n 0x108: 0x100400,\n 0x118: 0x2000401,\n 0x128: 0x2100001,\n 0x138: 0x1,\n 0x148: 0x2000000,\n 0x158: 0x100000,\n 0x168: 0x401,\n 0x178: 0x2100400,\n 0x188: 0x2000001,\n 0x198: 0x2100000,\n 0x1a8: 0x0,\n 0x1b8: 0x2100401,\n 0x1c8: 0x100401,\n 0x1d8: 0x400,\n 0x1e8: 0x2000400,\n 0x1f8: 0x100001\n }, {\n 0x0: 0x8000820,\n 0x1: 0x20000,\n 0x2: 0x8000000,\n 0x3: 0x20,\n 0x4: 0x20020,\n 0x5: 0x8020820,\n 0x6: 0x8020800,\n 0x7: 0x800,\n 0x8: 0x8020000,\n 0x9: 0x8000800,\n 0xa: 0x20800,\n 0xb: 0x8020020,\n 0xc: 0x820,\n 0xd: 0x0,\n 0xe: 0x8000020,\n 0xf: 0x20820,\n 0x80000000: 0x800,\n 0x80000001: 0x8020820,\n 0x80000002: 0x8000820,\n 0x80000003: 0x8000000,\n 0x80000004: 0x8020000,\n 0x80000005: 0x20800,\n 0x80000006: 0x20820,\n 0x80000007: 0x20,\n 0x80000008: 0x8000020,\n 0x80000009: 0x820,\n 0x8000000a: 0x20020,\n 0x8000000b: 0x8020800,\n 0x8000000c: 0x0,\n 0x8000000d: 0x8020020,\n 0x8000000e: 0x8000800,\n 0x8000000f: 0x20000,\n 0x10: 0x20820,\n 0x11: 0x8020800,\n 0x12: 0x20,\n 0x13: 0x800,\n 0x14: 0x8000800,\n 0x15: 0x8000020,\n 0x16: 0x8020020,\n 0x17: 0x20000,\n 0x18: 0x0,\n 0x19: 0x20020,\n 0x1a: 0x8020000,\n 0x1b: 0x8000820,\n 0x1c: 0x8020820,\n 0x1d: 0x20800,\n 0x1e: 0x820,\n 0x1f: 0x8000000,\n 0x80000010: 0x20000,\n 0x80000011: 0x800,\n 0x80000012: 0x8020020,\n 0x80000013: 0x20820,\n 0x80000014: 0x20,\n 0x80000015: 0x8020000,\n 0x80000016: 0x8000000,\n 0x80000017: 0x8000820,\n 0x80000018: 0x8020820,\n 0x80000019: 0x8000020,\n 0x8000001a: 0x8000800,\n 0x8000001b: 0x0,\n 0x8000001c: 0x20800,\n 0x8000001d: 0x820,\n 0x8000001e: 0x20020,\n 0x8000001f: 0x8020800\n }];\n\n // Masks that select the SBOX input\n var SBOX_MASK = [0xf8000001, 0x1f800000, 0x01f80000, 0x001f8000, 0x0001f800, 0x00001f80, 0x000001f8, 0x8000001f];\n\n /**\n * DES block cipher algorithm.\n */\n var DES = C_algo.DES = BlockCipher.extend({\n _doReset: function () {\n // Shortcuts\n var key = this._key;\n var keyWords = key.words;\n\n // Select 56 bits according to PC1\n var keyBits = [];\n for (var i = 0; i < 56; i++) {\n var keyBitPos = PC1[i] - 1;\n keyBits[i] = keyWords[keyBitPos >>> 5] >>> 31 - keyBitPos % 32 & 1;\n }\n\n // Assemble 16 subkeys\n var subKeys = this._subKeys = [];\n for (var nSubKey = 0; nSubKey < 16; nSubKey++) {\n // Create subkey\n var subKey = subKeys[nSubKey] = [];\n\n // Shortcut\n var bitShift = BIT_SHIFTS[nSubKey];\n\n // Select 48 bits according to PC2\n for (var i = 0; i < 24; i++) {\n // Select from the left 28 key bits\n subKey[i / 6 | 0] |= keyBits[(PC2[i] - 1 + bitShift) % 28] << 31 - i % 6;\n\n // Select from the right 28 key bits\n subKey[4 + (i / 6 | 0)] |= keyBits[28 + (PC2[i + 24] - 1 + bitShift) % 28] << 31 - i % 6;\n }\n\n // Since each subkey is applied to an expanded 32-bit input,\n // the subkey can be broken into 8 values scaled to 32-bits,\n // which allows the key to be used without expansion\n subKey[0] = subKey[0] << 1 | subKey[0] >>> 31;\n for (var i = 1; i < 7; i++) {\n subKey[i] = subKey[i] >>> (i - 1) * 4 + 3;\n }\n subKey[7] = subKey[7] << 5 | subKey[7] >>> 27;\n }\n\n // Compute inverse subkeys\n var invSubKeys = this._invSubKeys = [];\n for (var i = 0; i < 16; i++) {\n invSubKeys[i] = subKeys[15 - i];\n }\n },\n encryptBlock: function (M, offset) {\n this._doCryptBlock(M, offset, this._subKeys);\n },\n decryptBlock: function (M, offset) {\n this._doCryptBlock(M, offset, this._invSubKeys);\n },\n _doCryptBlock: function (M, offset, subKeys) {\n // Get input\n this._lBlock = M[offset];\n this._rBlock = M[offset + 1];\n\n // Initial permutation\n exchangeLR.call(this, 4, 0x0f0f0f0f);\n exchangeLR.call(this, 16, 0x0000ffff);\n exchangeRL.call(this, 2, 0x33333333);\n exchangeRL.call(this, 8, 0x00ff00ff);\n exchangeLR.call(this, 1, 0x55555555);\n\n // Rounds\n for (var round = 0; round < 16; round++) {\n // Shortcuts\n var subKey = subKeys[round];\n var lBlock = this._lBlock;\n var rBlock = this._rBlock;\n\n // Feistel function\n var f = 0;\n for (var i = 0; i < 8; i++) {\n f |= SBOX_P[i][((rBlock ^ subKey[i]) & SBOX_MASK[i]) >>> 0];\n }\n this._lBlock = rBlock;\n this._rBlock = lBlock ^ f;\n }\n\n // Undo swap from last round\n var t = this._lBlock;\n this._lBlock = this._rBlock;\n this._rBlock = t;\n\n // Final permutation\n exchangeLR.call(this, 1, 0x55555555);\n exchangeRL.call(this, 8, 0x00ff00ff);\n exchangeRL.call(this, 2, 0x33333333);\n exchangeLR.call(this, 16, 0x0000ffff);\n exchangeLR.call(this, 4, 0x0f0f0f0f);\n\n // Set output\n M[offset] = this._lBlock;\n M[offset + 1] = this._rBlock;\n },\n keySize: 64 / 32,\n ivSize: 64 / 32,\n blockSize: 64 / 32\n });\n\n // Swap bits across the left and right words\n function exchangeLR(offset, mask) {\n var t = (this._lBlock >>> offset ^ this._rBlock) & mask;\n this._rBlock ^= t;\n this._lBlock ^= t << offset;\n }\n function exchangeRL(offset, mask) {\n var t = (this._rBlock >>> offset ^ this._lBlock) & mask;\n this._lBlock ^= t;\n this._rBlock ^= t << offset;\n }\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.DES.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.DES.decrypt(ciphertext, key, cfg);\n */\n C.DES = BlockCipher._createHelper(DES);\n\n /**\n * Triple-DES block cipher algorithm.\n */\n var TripleDES = C_algo.TripleDES = BlockCipher.extend({\n _doReset: function () {\n // Shortcuts\n var key = this._key;\n var keyWords = key.words;\n // Make sure the key length is valid (64, 128 or >= 192 bit)\n if (keyWords.length !== 2 && keyWords.length !== 4 && keyWords.length < 6) {\n throw new Error('Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.');\n }\n\n // Extend the key according to the keying options defined in 3DES standard\n var key1 = keyWords.slice(0, 2);\n var key2 = keyWords.length < 4 ? keyWords.slice(0, 2) : keyWords.slice(2, 4);\n var key3 = keyWords.length < 6 ? keyWords.slice(0, 2) : keyWords.slice(4, 6);\n\n // Create DES instances\n this._des1 = DES.createEncryptor(WordArray.create(key1));\n this._des2 = DES.createEncryptor(WordArray.create(key2));\n this._des3 = DES.createEncryptor(WordArray.create(key3));\n },\n encryptBlock: function (M, offset) {\n this._des1.encryptBlock(M, offset);\n this._des2.decryptBlock(M, offset);\n this._des3.encryptBlock(M, offset);\n },\n decryptBlock: function (M, offset) {\n this._des3.decryptBlock(M, offset);\n this._des2.encryptBlock(M, offset);\n this._des1.decryptBlock(M, offset);\n },\n keySize: 192 / 32,\n ivSize: 64 / 32,\n blockSize: 64 / 32\n });\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.TripleDES.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.TripleDES.decrypt(ciphertext, key, cfg);\n */\n C.TripleDES = BlockCipher._createHelper(TripleDES);\n })();\n return CryptoJS.TripleDES;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var StreamCipher = C_lib.StreamCipher;\n var C_algo = C.algo;\n\n /**\n * RC4 stream cipher algorithm.\n */\n var RC4 = C_algo.RC4 = StreamCipher.extend({\n _doReset: function () {\n // Shortcuts\n var key = this._key;\n var keyWords = key.words;\n var keySigBytes = key.sigBytes;\n\n // Init sbox\n var S = this._S = [];\n for (var i = 0; i < 256; i++) {\n S[i] = i;\n }\n\n // Key setup\n for (var i = 0, j = 0; i < 256; i++) {\n var keyByteIndex = i % keySigBytes;\n var keyByte = keyWords[keyByteIndex >>> 2] >>> 24 - keyByteIndex % 4 * 8 & 0xff;\n j = (j + S[i] + keyByte) % 256;\n\n // Swap\n var t = S[i];\n S[i] = S[j];\n S[j] = t;\n }\n\n // Counters\n this._i = this._j = 0;\n },\n _doProcessBlock: function (M, offset) {\n M[offset] ^= generateKeystreamWord.call(this);\n },\n keySize: 256 / 32,\n ivSize: 0\n });\n function generateKeystreamWord() {\n // Shortcuts\n var S = this._S;\n var i = this._i;\n var j = this._j;\n\n // Generate keystream word\n var keystreamWord = 0;\n for (var n = 0; n < 4; n++) {\n i = (i + 1) % 256;\n j = (j + S[i]) % 256;\n\n // Swap\n var t = S[i];\n S[i] = S[j];\n S[j] = t;\n keystreamWord |= S[(S[i] + S[j]) % 256] << 24 - n * 8;\n }\n\n // Update counters\n this._i = i;\n this._j = j;\n return keystreamWord;\n }\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.RC4.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.RC4.decrypt(ciphertext, key, cfg);\n */\n C.RC4 = StreamCipher._createHelper(RC4);\n\n /**\n * Modified RC4 stream cipher algorithm.\n */\n var RC4Drop = C_algo.RC4Drop = RC4.extend({\n /**\n * Configuration options.\n *\n * @property {number} drop The number of keystream words to drop. Default 192\n */\n cfg: RC4.cfg.extend({\n drop: 192\n }),\n _doReset: function () {\n RC4._doReset.call(this);\n\n // Drop\n for (var i = this.cfg.drop; i > 0; i--) {\n generateKeystreamWord.call(this);\n }\n }\n });\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.RC4Drop.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.RC4Drop.decrypt(ciphertext, key, cfg);\n */\n C.RC4Drop = StreamCipher._createHelper(RC4Drop);\n })();\n return CryptoJS.RC4;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var StreamCipher = C_lib.StreamCipher;\n var C_algo = C.algo;\n\n // Reusable objects\n var S = [];\n var C_ = [];\n var G = [];\n\n /**\n * Rabbit stream cipher algorithm\n */\n var Rabbit = C_algo.Rabbit = StreamCipher.extend({\n _doReset: function () {\n // Shortcuts\n var K = this._key.words;\n var iv = this.cfg.iv;\n\n // Swap endian\n for (var i = 0; i < 4; i++) {\n K[i] = (K[i] << 8 | K[i] >>> 24) & 0x00ff00ff | (K[i] << 24 | K[i] >>> 8) & 0xff00ff00;\n }\n\n // Generate initial state values\n var X = this._X = [K[0], K[3] << 16 | K[2] >>> 16, K[1], K[0] << 16 | K[3] >>> 16, K[2], K[1] << 16 | K[0] >>> 16, K[3], K[2] << 16 | K[1] >>> 16];\n\n // Generate initial counter values\n var C = this._C = [K[2] << 16 | K[2] >>> 16, K[0] & 0xffff0000 | K[1] & 0x0000ffff, K[3] << 16 | K[3] >>> 16, K[1] & 0xffff0000 | K[2] & 0x0000ffff, K[0] << 16 | K[0] >>> 16, K[2] & 0xffff0000 | K[3] & 0x0000ffff, K[1] << 16 | K[1] >>> 16, K[3] & 0xffff0000 | K[0] & 0x0000ffff];\n\n // Carry bit\n this._b = 0;\n\n // Iterate the system four times\n for (var i = 0; i < 4; i++) {\n nextState.call(this);\n }\n\n // Modify the counters\n for (var i = 0; i < 8; i++) {\n C[i] ^= X[i + 4 & 7];\n }\n\n // IV setup\n if (iv) {\n // Shortcuts\n var IV = iv.words;\n var IV_0 = IV[0];\n var IV_1 = IV[1];\n\n // Generate four subvectors\n var i0 = (IV_0 << 8 | IV_0 >>> 24) & 0x00ff00ff | (IV_0 << 24 | IV_0 >>> 8) & 0xff00ff00;\n var i2 = (IV_1 << 8 | IV_1 >>> 24) & 0x00ff00ff | (IV_1 << 24 | IV_1 >>> 8) & 0xff00ff00;\n var i1 = i0 >>> 16 | i2 & 0xffff0000;\n var i3 = i2 << 16 | i0 & 0x0000ffff;\n\n // Modify counter values\n C[0] ^= i0;\n C[1] ^= i1;\n C[2] ^= i2;\n C[3] ^= i3;\n C[4] ^= i0;\n C[5] ^= i1;\n C[6] ^= i2;\n C[7] ^= i3;\n\n // Iterate the system four times\n for (var i = 0; i < 4; i++) {\n nextState.call(this);\n }\n }\n },\n _doProcessBlock: function (M, offset) {\n // Shortcut\n var X = this._X;\n\n // Iterate the system\n nextState.call(this);\n\n // Generate four keystream words\n S[0] = X[0] ^ X[5] >>> 16 ^ X[3] << 16;\n S[1] = X[2] ^ X[7] >>> 16 ^ X[5] << 16;\n S[2] = X[4] ^ X[1] >>> 16 ^ X[7] << 16;\n S[3] = X[6] ^ X[3] >>> 16 ^ X[1] << 16;\n for (var i = 0; i < 4; i++) {\n // Swap endian\n S[i] = (S[i] << 8 | S[i] >>> 24) & 0x00ff00ff | (S[i] << 24 | S[i] >>> 8) & 0xff00ff00;\n\n // Encrypt\n M[offset + i] ^= S[i];\n }\n },\n blockSize: 128 / 32,\n ivSize: 64 / 32\n });\n function nextState() {\n // Shortcuts\n var X = this._X;\n var C = this._C;\n\n // Save old counter values\n for (var i = 0; i < 8; i++) {\n C_[i] = C[i];\n }\n\n // Calculate new counter values\n C[0] = C[0] + 0x4d34d34d + this._b | 0;\n C[1] = C[1] + 0xd34d34d3 + (C[0] >>> 0 < C_[0] >>> 0 ? 1 : 0) | 0;\n C[2] = C[2] + 0x34d34d34 + (C[1] >>> 0 < C_[1] >>> 0 ? 1 : 0) | 0;\n C[3] = C[3] + 0x4d34d34d + (C[2] >>> 0 < C_[2] >>> 0 ? 1 : 0) | 0;\n C[4] = C[4] + 0xd34d34d3 + (C[3] >>> 0 < C_[3] >>> 0 ? 1 : 0) | 0;\n C[5] = C[5] + 0x34d34d34 + (C[4] >>> 0 < C_[4] >>> 0 ? 1 : 0) | 0;\n C[6] = C[6] + 0x4d34d34d + (C[5] >>> 0 < C_[5] >>> 0 ? 1 : 0) | 0;\n C[7] = C[7] + 0xd34d34d3 + (C[6] >>> 0 < C_[6] >>> 0 ? 1 : 0) | 0;\n this._b = C[7] >>> 0 < C_[7] >>> 0 ? 1 : 0;\n\n // Calculate the g-values\n for (var i = 0; i < 8; i++) {\n var gx = X[i] + C[i];\n\n // Construct high and low argument for squaring\n var ga = gx & 0xffff;\n var gb = gx >>> 16;\n\n // Calculate high and low result of squaring\n var gh = ((ga * ga >>> 17) + ga * gb >>> 15) + gb * gb;\n var gl = ((gx & 0xffff0000) * gx | 0) + ((gx & 0x0000ffff) * gx | 0);\n\n // High XOR low\n G[i] = gh ^ gl;\n }\n\n // Calculate new state values\n X[0] = G[0] + (G[7] << 16 | G[7] >>> 16) + (G[6] << 16 | G[6] >>> 16) | 0;\n X[1] = G[1] + (G[0] << 8 | G[0] >>> 24) + G[7] | 0;\n X[2] = G[2] + (G[1] << 16 | G[1] >>> 16) + (G[0] << 16 | G[0] >>> 16) | 0;\n X[3] = G[3] + (G[2] << 8 | G[2] >>> 24) + G[1] | 0;\n X[4] = G[4] + (G[3] << 16 | G[3] >>> 16) + (G[2] << 16 | G[2] >>> 16) | 0;\n X[5] = G[5] + (G[4] << 8 | G[4] >>> 24) + G[3] | 0;\n X[6] = G[6] + (G[5] << 16 | G[5] >>> 16) + (G[4] << 16 | G[4] >>> 16) | 0;\n X[7] = G[7] + (G[6] << 8 | G[6] >>> 24) + G[5] | 0;\n }\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.Rabbit.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.Rabbit.decrypt(ciphertext, key, cfg);\n */\n C.Rabbit = StreamCipher._createHelper(Rabbit);\n })();\n return CryptoJS.Rabbit;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var StreamCipher = C_lib.StreamCipher;\n var C_algo = C.algo;\n\n // Reusable objects\n var S = [];\n var C_ = [];\n var G = [];\n\n /**\n * Rabbit stream cipher algorithm.\n *\n * This is a legacy version that neglected to convert the key to little-endian.\n * This error doesn't affect the cipher's security,\n * but it does affect its compatibility with other implementations.\n */\n var RabbitLegacy = C_algo.RabbitLegacy = StreamCipher.extend({\n _doReset: function () {\n // Shortcuts\n var K = this._key.words;\n var iv = this.cfg.iv;\n\n // Generate initial state values\n var X = this._X = [K[0], K[3] << 16 | K[2] >>> 16, K[1], K[0] << 16 | K[3] >>> 16, K[2], K[1] << 16 | K[0] >>> 16, K[3], K[2] << 16 | K[1] >>> 16];\n\n // Generate initial counter values\n var C = this._C = [K[2] << 16 | K[2] >>> 16, K[0] & 0xffff0000 | K[1] & 0x0000ffff, K[3] << 16 | K[3] >>> 16, K[1] & 0xffff0000 | K[2] & 0x0000ffff, K[0] << 16 | K[0] >>> 16, K[2] & 0xffff0000 | K[3] & 0x0000ffff, K[1] << 16 | K[1] >>> 16, K[3] & 0xffff0000 | K[0] & 0x0000ffff];\n\n // Carry bit\n this._b = 0;\n\n // Iterate the system four times\n for (var i = 0; i < 4; i++) {\n nextState.call(this);\n }\n\n // Modify the counters\n for (var i = 0; i < 8; i++) {\n C[i] ^= X[i + 4 & 7];\n }\n\n // IV setup\n if (iv) {\n // Shortcuts\n var IV = iv.words;\n var IV_0 = IV[0];\n var IV_1 = IV[1];\n\n // Generate four subvectors\n var i0 = (IV_0 << 8 | IV_0 >>> 24) & 0x00ff00ff | (IV_0 << 24 | IV_0 >>> 8) & 0xff00ff00;\n var i2 = (IV_1 << 8 | IV_1 >>> 24) & 0x00ff00ff | (IV_1 << 24 | IV_1 >>> 8) & 0xff00ff00;\n var i1 = i0 >>> 16 | i2 & 0xffff0000;\n var i3 = i2 << 16 | i0 & 0x0000ffff;\n\n // Modify counter values\n C[0] ^= i0;\n C[1] ^= i1;\n C[2] ^= i2;\n C[3] ^= i3;\n C[4] ^= i0;\n C[5] ^= i1;\n C[6] ^= i2;\n C[7] ^= i3;\n\n // Iterate the system four times\n for (var i = 0; i < 4; i++) {\n nextState.call(this);\n }\n }\n },\n _doProcessBlock: function (M, offset) {\n // Shortcut\n var X = this._X;\n\n // Iterate the system\n nextState.call(this);\n\n // Generate four keystream words\n S[0] = X[0] ^ X[5] >>> 16 ^ X[3] << 16;\n S[1] = X[2] ^ X[7] >>> 16 ^ X[5] << 16;\n S[2] = X[4] ^ X[1] >>> 16 ^ X[7] << 16;\n S[3] = X[6] ^ X[3] >>> 16 ^ X[1] << 16;\n for (var i = 0; i < 4; i++) {\n // Swap endian\n S[i] = (S[i] << 8 | S[i] >>> 24) & 0x00ff00ff | (S[i] << 24 | S[i] >>> 8) & 0xff00ff00;\n\n // Encrypt\n M[offset + i] ^= S[i];\n }\n },\n blockSize: 128 / 32,\n ivSize: 64 / 32\n });\n function nextState() {\n // Shortcuts\n var X = this._X;\n var C = this._C;\n\n // Save old counter values\n for (var i = 0; i < 8; i++) {\n C_[i] = C[i];\n }\n\n // Calculate new counter values\n C[0] = C[0] + 0x4d34d34d + this._b | 0;\n C[1] = C[1] + 0xd34d34d3 + (C[0] >>> 0 < C_[0] >>> 0 ? 1 : 0) | 0;\n C[2] = C[2] + 0x34d34d34 + (C[1] >>> 0 < C_[1] >>> 0 ? 1 : 0) | 0;\n C[3] = C[3] + 0x4d34d34d + (C[2] >>> 0 < C_[2] >>> 0 ? 1 : 0) | 0;\n C[4] = C[4] + 0xd34d34d3 + (C[3] >>> 0 < C_[3] >>> 0 ? 1 : 0) | 0;\n C[5] = C[5] + 0x34d34d34 + (C[4] >>> 0 < C_[4] >>> 0 ? 1 : 0) | 0;\n C[6] = C[6] + 0x4d34d34d + (C[5] >>> 0 < C_[5] >>> 0 ? 1 : 0) | 0;\n C[7] = C[7] + 0xd34d34d3 + (C[6] >>> 0 < C_[6] >>> 0 ? 1 : 0) | 0;\n this._b = C[7] >>> 0 < C_[7] >>> 0 ? 1 : 0;\n\n // Calculate the g-values\n for (var i = 0; i < 8; i++) {\n var gx = X[i] + C[i];\n\n // Construct high and low argument for squaring\n var ga = gx & 0xffff;\n var gb = gx >>> 16;\n\n // Calculate high and low result of squaring\n var gh = ((ga * ga >>> 17) + ga * gb >>> 15) + gb * gb;\n var gl = ((gx & 0xffff0000) * gx | 0) + ((gx & 0x0000ffff) * gx | 0);\n\n // High XOR low\n G[i] = gh ^ gl;\n }\n\n // Calculate new state values\n X[0] = G[0] + (G[7] << 16 | G[7] >>> 16) + (G[6] << 16 | G[6] >>> 16) | 0;\n X[1] = G[1] + (G[0] << 8 | G[0] >>> 24) + G[7] | 0;\n X[2] = G[2] + (G[1] << 16 | G[1] >>> 16) + (G[0] << 16 | G[0] >>> 16) | 0;\n X[3] = G[3] + (G[2] << 8 | G[2] >>> 24) + G[1] | 0;\n X[4] = G[4] + (G[3] << 16 | G[3] >>> 16) + (G[2] << 16 | G[2] >>> 16) | 0;\n X[5] = G[5] + (G[4] << 8 | G[4] >>> 24) + G[3] | 0;\n X[6] = G[6] + (G[5] << 16 | G[5] >>> 16) + (G[4] << 16 | G[4] >>> 16) | 0;\n X[7] = G[7] + (G[6] << 8 | G[6] >>> 24) + G[5] | 0;\n }\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.RabbitLegacy.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.RabbitLegacy.decrypt(ciphertext, key, cfg);\n */\n C.RabbitLegacy = StreamCipher._createHelper(RabbitLegacy);\n })();\n return CryptoJS.RabbitLegacy;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./enc-base64\"), require(\"./md5\"), require(\"./evpkdf\"), require(\"./cipher-core\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./enc-base64\", \"./md5\", \"./evpkdf\", \"./cipher-core\"], factory);\n } else {\n // Global (browser)\n factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n (function () {\n // Shortcuts\n var C = CryptoJS;\n var C_lib = C.lib;\n var BlockCipher = C_lib.BlockCipher;\n var C_algo = C.algo;\n const N = 16;\n\n //Origin pbox and sbox, derived from PI\n const ORIG_P = [0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B];\n const ORIG_S = [[0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A], [0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7], [0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0], [0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6]];\n var BLOWFISH_CTX = {\n pbox: [],\n sbox: []\n };\n function F(ctx, x) {\n let a = x >> 24 & 0xFF;\n let b = x >> 16 & 0xFF;\n let c = x >> 8 & 0xFF;\n let d = x & 0xFF;\n let y = ctx.sbox[0][a] + ctx.sbox[1][b];\n y = y ^ ctx.sbox[2][c];\n y = y + ctx.sbox[3][d];\n return y;\n }\n function BlowFish_Encrypt(ctx, left, right) {\n let Xl = left;\n let Xr = right;\n let temp;\n for (let i = 0; i < N; ++i) {\n Xl = Xl ^ ctx.pbox[i];\n Xr = F(ctx, Xl) ^ Xr;\n temp = Xl;\n Xl = Xr;\n Xr = temp;\n }\n temp = Xl;\n Xl = Xr;\n Xr = temp;\n Xr = Xr ^ ctx.pbox[N];\n Xl = Xl ^ ctx.pbox[N + 1];\n return {\n left: Xl,\n right: Xr\n };\n }\n function BlowFish_Decrypt(ctx, left, right) {\n let Xl = left;\n let Xr = right;\n let temp;\n for (let i = N + 1; i > 1; --i) {\n Xl = Xl ^ ctx.pbox[i];\n Xr = F(ctx, Xl) ^ Xr;\n temp = Xl;\n Xl = Xr;\n Xr = temp;\n }\n temp = Xl;\n Xl = Xr;\n Xr = temp;\n Xr = Xr ^ ctx.pbox[1];\n Xl = Xl ^ ctx.pbox[0];\n return {\n left: Xl,\n right: Xr\n };\n }\n\n /**\n * Initialization ctx's pbox and sbox.\n *\n * @param {Object} ctx The object has pbox and sbox.\n * @param {Array} key An array of 32-bit words.\n * @param {int} keysize The length of the key.\n *\n * @example\n *\n * BlowFishInit(BLOWFISH_CTX, key, 128/32);\n */\n function BlowFishInit(ctx, key, keysize) {\n for (let Row = 0; Row < 4; Row++) {\n ctx.sbox[Row] = [];\n for (let Col = 0; Col < 256; Col++) {\n ctx.sbox[Row][Col] = ORIG_S[Row][Col];\n }\n }\n let keyIndex = 0;\n for (let index = 0; index < N + 2; index++) {\n ctx.pbox[index] = ORIG_P[index] ^ key[keyIndex];\n keyIndex++;\n if (keyIndex >= keysize) {\n keyIndex = 0;\n }\n }\n let Data1 = 0;\n let Data2 = 0;\n let res = 0;\n for (let i = 0; i < N + 2; i += 2) {\n res = BlowFish_Encrypt(ctx, Data1, Data2);\n Data1 = res.left;\n Data2 = res.right;\n ctx.pbox[i] = Data1;\n ctx.pbox[i + 1] = Data2;\n }\n for (let i = 0; i < 4; i++) {\n for (let j = 0; j < 256; j += 2) {\n res = BlowFish_Encrypt(ctx, Data1, Data2);\n Data1 = res.left;\n Data2 = res.right;\n ctx.sbox[i][j] = Data1;\n ctx.sbox[i][j + 1] = Data2;\n }\n }\n return true;\n }\n\n /**\n * Blowfish block cipher algorithm.\n */\n var Blowfish = C_algo.Blowfish = BlockCipher.extend({\n _doReset: function () {\n // Skip reset of nRounds has been set before and key did not change\n if (this._keyPriorReset === this._key) {\n return;\n }\n\n // Shortcuts\n var key = this._keyPriorReset = this._key;\n var keyWords = key.words;\n var keySize = key.sigBytes / 4;\n\n //Initialization pbox and sbox\n BlowFishInit(BLOWFISH_CTX, keyWords, keySize);\n },\n encryptBlock: function (M, offset) {\n var res = BlowFish_Encrypt(BLOWFISH_CTX, M[offset], M[offset + 1]);\n M[offset] = res.left;\n M[offset + 1] = res.right;\n },\n decryptBlock: function (M, offset) {\n var res = BlowFish_Decrypt(BLOWFISH_CTX, M[offset], M[offset + 1]);\n M[offset] = res.left;\n M[offset + 1] = res.right;\n },\n blockSize: 64 / 32,\n keySize: 128 / 32,\n ivSize: 64 / 32\n });\n\n /**\n * Shortcut functions to the cipher's object interface.\n *\n * @example\n *\n * var ciphertext = CryptoJS.Blowfish.encrypt(message, key, cfg);\n * var plaintext = CryptoJS.Blowfish.decrypt(ciphertext, key, cfg);\n */\n C.Blowfish = BlockCipher._createHelper(Blowfish);\n })();\n return CryptoJS.Blowfish;\n});",";\n(function (root, factory, undef) {\n if (typeof exports === \"object\") {\n // CommonJS\n module.exports = exports = factory(require(\"./core\"), require(\"./x64-core\"), require(\"./lib-typedarrays\"), require(\"./enc-utf16\"), require(\"./enc-base64\"), require(\"./enc-base64url\"), require(\"./md5\"), require(\"./sha1\"), require(\"./sha256\"), require(\"./sha224\"), require(\"./sha512\"), require(\"./sha384\"), require(\"./sha3\"), require(\"./ripemd160\"), require(\"./hmac\"), require(\"./pbkdf2\"), require(\"./evpkdf\"), require(\"./cipher-core\"), require(\"./mode-cfb\"), require(\"./mode-ctr\"), require(\"./mode-ctr-gladman\"), require(\"./mode-ofb\"), require(\"./mode-ecb\"), require(\"./pad-ansix923\"), require(\"./pad-iso10126\"), require(\"./pad-iso97971\"), require(\"./pad-zeropadding\"), require(\"./pad-nopadding\"), require(\"./format-hex\"), require(\"./aes\"), require(\"./tripledes\"), require(\"./rc4\"), require(\"./rabbit\"), require(\"./rabbit-legacy\"), require(\"./blowfish\"));\n } else if (typeof define === \"function\" && define.amd) {\n // AMD\n define([\"./core\", \"./x64-core\", \"./lib-typedarrays\", \"./enc-utf16\", \"./enc-base64\", \"./enc-base64url\", \"./md5\", \"./sha1\", \"./sha256\", \"./sha224\", \"./sha512\", \"./sha384\", \"./sha3\", \"./ripemd160\", \"./hmac\", \"./pbkdf2\", \"./evpkdf\", \"./cipher-core\", \"./mode-cfb\", \"./mode-ctr\", \"./mode-ctr-gladman\", \"./mode-ofb\", \"./mode-ecb\", \"./pad-ansix923\", \"./pad-iso10126\", \"./pad-iso97971\", \"./pad-zeropadding\", \"./pad-nopadding\", \"./format-hex\", \"./aes\", \"./tripledes\", \"./rc4\", \"./rabbit\", \"./rabbit-legacy\", \"./blowfish\"], factory);\n } else {\n // Global (browser)\n root.CryptoJS = factory(root.CryptoJS);\n }\n})(this, function (CryptoJS) {\n return CryptoJS;\n});","'use strict';\n\nexports.byteLength = byteLength;\nexports.toByteArray = toByteArray;\nexports.fromByteArray = fromByteArray;\nvar lookup = [];\nvar revLookup = [];\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i];\n revLookup[code.charCodeAt(i)] = i;\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62;\nrevLookup['_'.charCodeAt(0)] = 63;\nfunction getLens(b64) {\n var len = b64.length;\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4');\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=');\n if (validLen === -1) validLen = len;\n var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4;\n return [validLen, placeHoldersLen];\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength(b64) {\n var lens = getLens(b64);\n var validLen = lens[0];\n var placeHoldersLen = lens[1];\n return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;\n}\nfunction _byteLength(b64, validLen, placeHoldersLen) {\n return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;\n}\nfunction toByteArray(b64) {\n var tmp;\n var lens = getLens(b64);\n var validLen = lens[0];\n var placeHoldersLen = lens[1];\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen));\n var curByte = 0;\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0 ? validLen - 4 : validLen;\n var i;\n for (i = 0; i < len; i += 4) {\n tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)];\n arr[curByte++] = tmp >> 16 & 0xFF;\n arr[curByte++] = tmp >> 8 & 0xFF;\n arr[curByte++] = tmp & 0xFF;\n }\n if (placeHoldersLen === 2) {\n tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4;\n arr[curByte++] = tmp & 0xFF;\n }\n if (placeHoldersLen === 1) {\n tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2;\n arr[curByte++] = tmp >> 8 & 0xFF;\n arr[curByte++] = tmp & 0xFF;\n }\n return arr;\n}\nfunction tripletToBase64(num) {\n return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F];\n}\nfunction encodeChunk(uint8, start, end) {\n var tmp;\n var output = [];\n for (var i = start; i < end; i += 3) {\n tmp = (uint8[i] << 16 & 0xFF0000) + (uint8[i + 1] << 8 & 0xFF00) + (uint8[i + 2] & 0xFF);\n output.push(tripletToBase64(tmp));\n }\n return output.join('');\n}\nfunction fromByteArray(uint8) {\n var tmp;\n var len = uint8.length;\n var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes\n var parts = [];\n var maxChunkLength = 16383; // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1];\n parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 0x3F] + '==');\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1];\n parts.push(lookup[tmp >> 10] + lookup[tmp >> 4 & 0x3F] + lookup[tmp << 2 & 0x3F] + '=');\n }\n return parts.join('');\n}","/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */\nexports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m;\n var eLen = nBytes * 8 - mLen - 1;\n var eMax = (1 << eLen) - 1;\n var eBias = eMax >> 1;\n var nBits = -7;\n var i = isLE ? nBytes - 1 : 0;\n var d = isLE ? -1 : 1;\n var s = buffer[offset + i];\n i += d;\n e = s & (1 << -nBits) - 1;\n s >>= -nBits;\n nBits += eLen;\n for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}\n m = e & (1 << -nBits) - 1;\n e >>= -nBits;\n nBits += mLen;\n for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}\n if (e === 0) {\n e = 1 - eBias;\n } else if (e === eMax) {\n return m ? NaN : (s ? -1 : 1) * Infinity;\n } else {\n m = m + Math.pow(2, mLen);\n e = e - eBias;\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen);\n};\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c;\n var eLen = nBytes * 8 - mLen - 1;\n var eMax = (1 << eLen) - 1;\n var eBias = eMax >> 1;\n var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;\n var i = isLE ? 0 : nBytes - 1;\n var d = isLE ? 1 : -1;\n var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;\n value = Math.abs(value);\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0;\n e = eMax;\n } else {\n e = Math.floor(Math.log(value) / Math.LN2);\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--;\n c *= 2;\n }\n if (e + eBias >= 1) {\n value += rt / c;\n } else {\n value += rt * Math.pow(2, 1 - eBias);\n }\n if (value * c >= 2) {\n e++;\n c /= 2;\n }\n if (e + eBias >= eMax) {\n m = 0;\n e = eMax;\n } else if (e + eBias >= 1) {\n m = (value * c - 1) * Math.pow(2, mLen);\n e = e + eBias;\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);\n e = 0;\n }\n }\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n e = e << mLen | m;\n eLen += mLen;\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n buffer[offset + i - d] |= s * 128;\n};","/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n'use strict';\n\nvar base64 = require('base64-js');\nvar ieee754 = require('ieee754');\nvar customInspectSymbol = typeof Symbol === 'function' && typeof Symbol['for'] === 'function' // eslint-disable-line dot-notation\n? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation\n: null;\nexports.Buffer = Buffer;\nexports.SlowBuffer = SlowBuffer;\nexports.INSPECT_MAX_BYTES = 50;\nvar K_MAX_LENGTH = 0x7fffffff;\nexports.kMaxLength = K_MAX_LENGTH;\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Print warning and recommend using `buffer` v4.x which has an Object\n * implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * We report that the browser does not support typed arrays if the are not subclassable\n * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`\n * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support\n * for __proto__ and has a buggy typed array implementation.\n */\nBuffer.TYPED_ARRAY_SUPPORT = typedArraySupport();\nif (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && typeof console.error === 'function') {\n console.error('This browser lacks typed array (Uint8Array) support which is required by ' + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.');\n}\nfunction typedArraySupport() {\n // Can typed array instances can be augmented?\n try {\n var arr = new Uint8Array(1);\n var proto = {\n foo: function () {\n return 42;\n }\n };\n Object.setPrototypeOf(proto, Uint8Array.prototype);\n Object.setPrototypeOf(arr, proto);\n return arr.foo() === 42;\n } catch (e) {\n return false;\n }\n}\nObject.defineProperty(Buffer.prototype, 'parent', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined;\n return this.buffer;\n }\n});\nObject.defineProperty(Buffer.prototype, 'offset', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined;\n return this.byteOffset;\n }\n});\nfunction createBuffer(length) {\n if (length > K_MAX_LENGTH) {\n throw new RangeError('The value \"' + length + '\" is invalid for option \"size\"');\n }\n // Return an augmented `Uint8Array` instance\n var buf = new Uint8Array(length);\n Object.setPrototypeOf(buf, Buffer.prototype);\n return buf;\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer(arg, encodingOrOffset, length) {\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new TypeError('The \"string\" argument must be of type string. Received type number');\n }\n return allocUnsafe(arg);\n }\n return from(arg, encodingOrOffset, length);\n}\nBuffer.poolSize = 8192; // not used by this implementation\n\nfunction from(value, encodingOrOffset, length) {\n if (typeof value === 'string') {\n return fromString(value, encodingOrOffset);\n }\n if (ArrayBuffer.isView(value)) {\n return fromArrayView(value);\n }\n if (value == null) {\n throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + 'or Array-like Object. Received type ' + typeof value);\n }\n if (isInstance(value, ArrayBuffer) || value && isInstance(value.buffer, ArrayBuffer)) {\n return fromArrayBuffer(value, encodingOrOffset, length);\n }\n if (typeof SharedArrayBuffer !== 'undefined' && (isInstance(value, SharedArrayBuffer) || value && isInstance(value.buffer, SharedArrayBuffer))) {\n return fromArrayBuffer(value, encodingOrOffset, length);\n }\n if (typeof value === 'number') {\n throw new TypeError('The \"value\" argument must not be of type number. Received type number');\n }\n var valueOf = value.valueOf && value.valueOf();\n if (valueOf != null && valueOf !== value) {\n return Buffer.from(valueOf, encodingOrOffset, length);\n }\n var b = fromObject(value);\n if (b) return b;\n if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && typeof value[Symbol.toPrimitive] === 'function') {\n return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length);\n }\n throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + 'or Array-like Object. Received type ' + typeof value);\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(value, encodingOrOffset, length);\n};\n\n// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:\n// https://github.com/feross/buffer/pull/148\nObject.setPrototypeOf(Buffer.prototype, Uint8Array.prototype);\nObject.setPrototypeOf(Buffer, Uint8Array);\nfunction assertSize(size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be of type number');\n } else if (size < 0) {\n throw new RangeError('The value \"' + size + '\" is invalid for option \"size\"');\n }\n}\nfunction alloc(size, fill, encoding) {\n assertSize(size);\n if (size <= 0) {\n return createBuffer(size);\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpreted as a start offset.\n return typeof encoding === 'string' ? createBuffer(size).fill(fill, encoding) : createBuffer(size).fill(fill);\n }\n return createBuffer(size);\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(size, fill, encoding);\n};\nfunction allocUnsafe(size) {\n assertSize(size);\n return createBuffer(size < 0 ? 0 : checked(size) | 0);\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(size);\n};\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(size);\n};\nfunction fromString(string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8';\n }\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding);\n }\n var length = byteLength(string, encoding) | 0;\n var buf = createBuffer(length);\n var actual = buf.write(string, encoding);\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n buf = buf.slice(0, actual);\n }\n return buf;\n}\nfunction fromArrayLike(array) {\n var length = array.length < 0 ? 0 : checked(array.length) | 0;\n var buf = createBuffer(length);\n for (var i = 0; i < length; i += 1) {\n buf[i] = array[i] & 255;\n }\n return buf;\n}\nfunction fromArrayView(arrayView) {\n if (isInstance(arrayView, Uint8Array)) {\n var copy = new Uint8Array(arrayView);\n return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength);\n }\n return fromArrayLike(arrayView);\n}\nfunction fromArrayBuffer(array, byteOffset, length) {\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\"offset\" is outside of buffer bounds');\n }\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\"length\" is outside of buffer bounds');\n }\n var buf;\n if (byteOffset === undefined && length === undefined) {\n buf = new Uint8Array(array);\n } else if (length === undefined) {\n buf = new Uint8Array(array, byteOffset);\n } else {\n buf = new Uint8Array(array, byteOffset, length);\n }\n\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(buf, Buffer.prototype);\n return buf;\n}\nfunction fromObject(obj) {\n if (Buffer.isBuffer(obj)) {\n var len = checked(obj.length) | 0;\n var buf = createBuffer(len);\n if (buf.length === 0) {\n return buf;\n }\n obj.copy(buf, 0, 0, len);\n return buf;\n }\n if (obj.length !== undefined) {\n if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {\n return createBuffer(0);\n }\n return fromArrayLike(obj);\n }\n if (obj.type === 'Buffer' && Array.isArray(obj.data)) {\n return fromArrayLike(obj.data);\n }\n}\nfunction checked(length) {\n // Note: cannot use `length < K_MAX_LENGTH` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= K_MAX_LENGTH) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes');\n }\n return length | 0;\n}\nfunction SlowBuffer(length) {\n if (+length != length) {\n // eslint-disable-line eqeqeq\n length = 0;\n }\n return Buffer.alloc(+length);\n}\nBuffer.isBuffer = function isBuffer(b) {\n return b != null && b._isBuffer === true && b !== Buffer.prototype; // so Buffer.isBuffer(Buffer.prototype) will be false\n};\nBuffer.compare = function compare(a, b) {\n if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength);\n if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength);\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError('The \"buf1\", \"buf2\" arguments must be one of type Buffer or Uint8Array');\n }\n if (a === b) return 0;\n var x = a.length;\n var y = b.length;\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i];\n y = b[i];\n break;\n }\n }\n if (x < y) return -1;\n if (y < x) return 1;\n return 0;\n};\nBuffer.isEncoding = function isEncoding(encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true;\n default:\n return false;\n }\n};\nBuffer.concat = function concat(list, length) {\n if (!Array.isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers');\n }\n if (list.length === 0) {\n return Buffer.alloc(0);\n }\n var i;\n if (length === undefined) {\n length = 0;\n for (i = 0; i < list.length; ++i) {\n length += list[i].length;\n }\n }\n var buffer = Buffer.allocUnsafe(length);\n var pos = 0;\n for (i = 0; i < list.length; ++i) {\n var buf = list[i];\n if (isInstance(buf, Uint8Array)) {\n if (pos + buf.length > buffer.length) {\n Buffer.from(buf).copy(buffer, pos);\n } else {\n Uint8Array.prototype.set.call(buffer, buf, pos);\n }\n } else if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers');\n } else {\n buf.copy(buffer, pos);\n }\n pos += buf.length;\n }\n return buffer;\n};\nfunction byteLength(string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length;\n }\n if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {\n return string.byteLength;\n }\n if (typeof string !== 'string') {\n throw new TypeError('The \"string\" argument must be one of type string, Buffer, or ArrayBuffer. ' + 'Received type ' + typeof string);\n }\n var len = string.length;\n var mustMatch = arguments.length > 2 && arguments[2] === true;\n if (!mustMatch && len === 0) return 0;\n\n // Use a for loop to avoid recursion\n var loweredCase = false;\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len;\n case 'utf8':\n case 'utf-8':\n return utf8ToBytes(string).length;\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2;\n case 'hex':\n return len >>> 1;\n case 'base64':\n return base64ToBytes(string).length;\n default:\n if (loweredCase) {\n return mustMatch ? -1 : utf8ToBytes(string).length; // assume utf8\n }\n encoding = ('' + encoding).toLowerCase();\n loweredCase = true;\n }\n }\n}\nBuffer.byteLength = byteLength;\nfunction slowToString(encoding, start, end) {\n var loweredCase = false;\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0;\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return '';\n }\n if (end === undefined || end > this.length) {\n end = this.length;\n }\n if (end <= 0) {\n return '';\n }\n\n // Force coercion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0;\n start >>>= 0;\n if (end <= start) {\n return '';\n }\n if (!encoding) encoding = 'utf8';\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end);\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end);\n case 'ascii':\n return asciiSlice(this, start, end);\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end);\n case 'base64':\n return base64Slice(this, start, end);\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end);\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding);\n encoding = (encoding + '').toLowerCase();\n loweredCase = true;\n }\n }\n}\n\n// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)\n// to detect a Buffer instance. It's not possible to use `instanceof Buffer`\n// reliably in a browserify context because there could be multiple different\n// copies of the 'buffer' package in use. This method works even for Buffer\n// instances that were created from another copy of the `buffer` package.\n// See: https://github.com/feross/buffer/issues/154\nBuffer.prototype._isBuffer = true;\nfunction swap(b, n, m) {\n var i = b[n];\n b[n] = b[m];\n b[m] = i;\n}\nBuffer.prototype.swap16 = function swap16() {\n var len = this.length;\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits');\n }\n for (var i = 0; i < len; i += 2) {\n swap(this, i, i + 1);\n }\n return this;\n};\nBuffer.prototype.swap32 = function swap32() {\n var len = this.length;\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits');\n }\n for (var i = 0; i < len; i += 4) {\n swap(this, i, i + 3);\n swap(this, i + 1, i + 2);\n }\n return this;\n};\nBuffer.prototype.swap64 = function swap64() {\n var len = this.length;\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits');\n }\n for (var i = 0; i < len; i += 8) {\n swap(this, i, i + 7);\n swap(this, i + 1, i + 6);\n swap(this, i + 2, i + 5);\n swap(this, i + 3, i + 4);\n }\n return this;\n};\nBuffer.prototype.toString = function toString() {\n var length = this.length;\n if (length === 0) return '';\n if (arguments.length === 0) return utf8Slice(this, 0, length);\n return slowToString.apply(this, arguments);\n};\nBuffer.prototype.toLocaleString = Buffer.prototype.toString;\nBuffer.prototype.equals = function equals(b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer');\n if (this === b) return true;\n return Buffer.compare(this, b) === 0;\n};\nBuffer.prototype.inspect = function inspect() {\n var str = '';\n var max = exports.INSPECT_MAX_BYTES;\n str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim();\n if (this.length > max) str += ' ... ';\n return '';\n};\nif (customInspectSymbol) {\n Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect;\n}\nBuffer.prototype.compare = function compare(target, start, end, thisStart, thisEnd) {\n if (isInstance(target, Uint8Array)) {\n target = Buffer.from(target, target.offset, target.byteLength);\n }\n if (!Buffer.isBuffer(target)) {\n throw new TypeError('The \"target\" argument must be one of type Buffer or Uint8Array. ' + 'Received type ' + typeof target);\n }\n if (start === undefined) {\n start = 0;\n }\n if (end === undefined) {\n end = target ? target.length : 0;\n }\n if (thisStart === undefined) {\n thisStart = 0;\n }\n if (thisEnd === undefined) {\n thisEnd = this.length;\n }\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index');\n }\n if (thisStart >= thisEnd && start >= end) {\n return 0;\n }\n if (thisStart >= thisEnd) {\n return -1;\n }\n if (start >= end) {\n return 1;\n }\n start >>>= 0;\n end >>>= 0;\n thisStart >>>= 0;\n thisEnd >>>= 0;\n if (this === target) return 0;\n var x = thisEnd - thisStart;\n var y = end - start;\n var len = Math.min(x, y);\n var thisCopy = this.slice(thisStart, thisEnd);\n var targetCopy = target.slice(start, end);\n for (var i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i];\n y = targetCopy[i];\n break;\n }\n }\n if (x < y) return -1;\n if (y < x) return 1;\n return 0;\n};\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1;\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset;\n byteOffset = 0;\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff;\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000;\n }\n byteOffset = +byteOffset; // Coerce to Number.\n if (numberIsNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : buffer.length - 1;\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset;\n if (byteOffset >= buffer.length) {\n if (dir) return -1;else byteOffset = buffer.length - 1;\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0;else return -1;\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding);\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1;\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir);\n } else if (typeof val === 'number') {\n val = val & 0xFF; // Search for a byte value [0-255]\n if (typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset);\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset);\n }\n }\n return arrayIndexOf(buffer, [val], byteOffset, encoding, dir);\n }\n throw new TypeError('val must be string, number or Buffer');\n}\nfunction arrayIndexOf(arr, val, byteOffset, encoding, dir) {\n var indexSize = 1;\n var arrLength = arr.length;\n var valLength = val.length;\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase();\n if (encoding === 'ucs2' || encoding === 'ucs-2' || encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1;\n }\n indexSize = 2;\n arrLength /= 2;\n valLength /= 2;\n byteOffset /= 2;\n }\n }\n function read(buf, i) {\n if (indexSize === 1) {\n return buf[i];\n } else {\n return buf.readUInt16BE(i * indexSize);\n }\n }\n var i;\n if (dir) {\n var foundIndex = -1;\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i;\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize;\n } else {\n if (foundIndex !== -1) i -= i - foundIndex;\n foundIndex = -1;\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength;\n for (i = byteOffset; i >= 0; i--) {\n var found = true;\n for (var j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false;\n break;\n }\n }\n if (found) return i;\n }\n }\n return -1;\n}\nBuffer.prototype.includes = function includes(val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1;\n};\nBuffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true);\n};\nBuffer.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false);\n};\nfunction hexWrite(buf, string, offset, length) {\n offset = Number(offset) || 0;\n var remaining = buf.length - offset;\n if (!length) {\n length = remaining;\n } else {\n length = Number(length);\n if (length > remaining) {\n length = remaining;\n }\n }\n var strLen = string.length;\n if (length > strLen / 2) {\n length = strLen / 2;\n }\n for (var i = 0; i < length; ++i) {\n var parsed = parseInt(string.substr(i * 2, 2), 16);\n if (numberIsNaN(parsed)) return i;\n buf[offset + i] = parsed;\n }\n return i;\n}\nfunction utf8Write(buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length);\n}\nfunction asciiWrite(buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length);\n}\nfunction base64Write(buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length);\n}\nfunction ucs2Write(buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length);\n}\nBuffer.prototype.write = function write(string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8';\n length = this.length;\n offset = 0;\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset;\n length = this.length;\n offset = 0;\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset >>> 0;\n if (isFinite(length)) {\n length = length >>> 0;\n if (encoding === undefined) encoding = 'utf8';\n } else {\n encoding = length;\n length = undefined;\n }\n } else {\n throw new Error('Buffer.write(string, encoding, offset[, length]) is no longer supported');\n }\n var remaining = this.length - offset;\n if (length === undefined || length > remaining) length = remaining;\n if (string.length > 0 && (length < 0 || offset < 0) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds');\n }\n if (!encoding) encoding = 'utf8';\n var loweredCase = false;\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length);\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length);\n case 'ascii':\n case 'latin1':\n case 'binary':\n return asciiWrite(this, string, offset, length);\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length);\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length);\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding);\n encoding = ('' + encoding).toLowerCase();\n loweredCase = true;\n }\n }\n};\nBuffer.prototype.toJSON = function toJSON() {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n };\n};\nfunction base64Slice(buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf);\n } else {\n return base64.fromByteArray(buf.slice(start, end));\n }\n}\nfunction utf8Slice(buf, start, end) {\n end = Math.min(buf.length, end);\n var res = [];\n var i = start;\n while (i < end) {\n var firstByte = buf[i];\n var codePoint = null;\n var bytesPerSequence = firstByte > 0xEF ? 4 : firstByte > 0xDF ? 3 : firstByte > 0xBF ? 2 : 1;\n if (i + bytesPerSequence <= end) {\n var secondByte, thirdByte, fourthByte, tempCodePoint;\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte;\n }\n break;\n case 2:\n secondByte = buf[i + 1];\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | secondByte & 0x3F;\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint;\n }\n }\n break;\n case 3:\n secondByte = buf[i + 1];\n thirdByte = buf[i + 2];\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | thirdByte & 0x3F;\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint;\n }\n }\n break;\n case 4:\n secondByte = buf[i + 1];\n thirdByte = buf[i + 2];\n fourthByte = buf[i + 3];\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | fourthByte & 0x3F;\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint;\n }\n }\n }\n }\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD;\n bytesPerSequence = 1;\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000;\n res.push(codePoint >>> 10 & 0x3FF | 0xD800);\n codePoint = 0xDC00 | codePoint & 0x3FF;\n }\n res.push(codePoint);\n i += bytesPerSequence;\n }\n return decodeCodePointsArray(res);\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000;\nfunction decodeCodePointsArray(codePoints) {\n var len = codePoints.length;\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints); // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n var res = '';\n var i = 0;\n while (i < len) {\n res += String.fromCharCode.apply(String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH));\n }\n return res;\n}\nfunction asciiSlice(buf, start, end) {\n var ret = '';\n end = Math.min(buf.length, end);\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F);\n }\n return ret;\n}\nfunction latin1Slice(buf, start, end) {\n var ret = '';\n end = Math.min(buf.length, end);\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i]);\n }\n return ret;\n}\nfunction hexSlice(buf, start, end) {\n var len = buf.length;\n if (!start || start < 0) start = 0;\n if (!end || end < 0 || end > len) end = len;\n var out = '';\n for (var i = start; i < end; ++i) {\n out += hexSliceLookupTable[buf[i]];\n }\n return out;\n}\nfunction utf16leSlice(buf, start, end) {\n var bytes = buf.slice(start, end);\n var res = '';\n // If bytes.length is odd, the last 8 bits must be ignored (same as node.js)\n for (var i = 0; i < bytes.length - 1; i += 2) {\n res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256);\n }\n return res;\n}\nBuffer.prototype.slice = function slice(start, end) {\n var len = this.length;\n start = ~~start;\n end = end === undefined ? len : ~~end;\n if (start < 0) {\n start += len;\n if (start < 0) start = 0;\n } else if (start > len) {\n start = len;\n }\n if (end < 0) {\n end += len;\n if (end < 0) end = 0;\n } else if (end > len) {\n end = len;\n }\n if (end < start) end = start;\n var newBuf = this.subarray(start, end);\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(newBuf, Buffer.prototype);\n return newBuf;\n};\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset(offset, ext, length) {\n if (offset % 1 !== 0 || offset < 0) throw new RangeError('offset is not uint');\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length');\n}\nBuffer.prototype.readUintLE = Buffer.prototype.readUIntLE = function readUIntLE(offset, byteLength, noAssert) {\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) checkOffset(offset, byteLength, this.length);\n var val = this[offset];\n var mul = 1;\n var i = 0;\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul;\n }\n return val;\n};\nBuffer.prototype.readUintBE = Buffer.prototype.readUIntBE = function readUIntBE(offset, byteLength, noAssert) {\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length);\n }\n var val = this[offset + --byteLength];\n var mul = 1;\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul;\n }\n return val;\n};\nBuffer.prototype.readUint8 = Buffer.prototype.readUInt8 = function readUInt8(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 1, this.length);\n return this[offset];\n};\nBuffer.prototype.readUint16LE = Buffer.prototype.readUInt16LE = function readUInt16LE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 2, this.length);\n return this[offset] | this[offset + 1] << 8;\n};\nBuffer.prototype.readUint16BE = Buffer.prototype.readUInt16BE = function readUInt16BE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 2, this.length);\n return this[offset] << 8 | this[offset + 1];\n};\nBuffer.prototype.readUint32LE = Buffer.prototype.readUInt32LE = function readUInt32LE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return (this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16) + this[offset + 3] * 0x1000000;\n};\nBuffer.prototype.readUint32BE = Buffer.prototype.readUInt32BE = function readUInt32BE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return this[offset] * 0x1000000 + (this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3]);\n};\nBuffer.prototype.readIntLE = function readIntLE(offset, byteLength, noAssert) {\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) checkOffset(offset, byteLength, this.length);\n var val = this[offset];\n var mul = 1;\n var i = 0;\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul;\n }\n mul *= 0x80;\n if (val >= mul) val -= Math.pow(2, 8 * byteLength);\n return val;\n};\nBuffer.prototype.readIntBE = function readIntBE(offset, byteLength, noAssert) {\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) checkOffset(offset, byteLength, this.length);\n var i = byteLength;\n var mul = 1;\n var val = this[offset + --i];\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul;\n }\n mul *= 0x80;\n if (val >= mul) val -= Math.pow(2, 8 * byteLength);\n return val;\n};\nBuffer.prototype.readInt8 = function readInt8(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 1, this.length);\n if (!(this[offset] & 0x80)) return this[offset];\n return (0xff - this[offset] + 1) * -1;\n};\nBuffer.prototype.readInt16LE = function readInt16LE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 2, this.length);\n var val = this[offset] | this[offset + 1] << 8;\n return val & 0x8000 ? val | 0xFFFF0000 : val;\n};\nBuffer.prototype.readInt16BE = function readInt16BE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 2, this.length);\n var val = this[offset + 1] | this[offset] << 8;\n return val & 0x8000 ? val | 0xFFFF0000 : val;\n};\nBuffer.prototype.readInt32LE = function readInt32LE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16 | this[offset + 3] << 24;\n};\nBuffer.prototype.readInt32BE = function readInt32BE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return this[offset] << 24 | this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3];\n};\nBuffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return ieee754.read(this, offset, true, 23, 4);\n};\nBuffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 4, this.length);\n return ieee754.read(this, offset, false, 23, 4);\n};\nBuffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 8, this.length);\n return ieee754.read(this, offset, true, 52, 8);\n};\nBuffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) {\n offset = offset >>> 0;\n if (!noAssert) checkOffset(offset, 8, this.length);\n return ieee754.read(this, offset, false, 52, 8);\n};\nfunction checkInt(buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance');\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds');\n if (offset + ext > buf.length) throw new RangeError('Index out of range');\n}\nBuffer.prototype.writeUintLE = Buffer.prototype.writeUIntLE = function writeUIntLE(value, offset, byteLength, noAssert) {\n value = +value;\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1;\n checkInt(this, value, offset, byteLength, maxBytes, 0);\n }\n var mul = 1;\n var i = 0;\n this[offset] = value & 0xFF;\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = value / mul & 0xFF;\n }\n return offset + byteLength;\n};\nBuffer.prototype.writeUintBE = Buffer.prototype.writeUIntBE = function writeUIntBE(value, offset, byteLength, noAssert) {\n value = +value;\n offset = offset >>> 0;\n byteLength = byteLength >>> 0;\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1;\n checkInt(this, value, offset, byteLength, maxBytes, 0);\n }\n var i = byteLength - 1;\n var mul = 1;\n this[offset + i] = value & 0xFF;\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = value / mul & 0xFF;\n }\n return offset + byteLength;\n};\nBuffer.prototype.writeUint8 = Buffer.prototype.writeUInt8 = function writeUInt8(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0);\n this[offset] = value & 0xff;\n return offset + 1;\n};\nBuffer.prototype.writeUint16LE = Buffer.prototype.writeUInt16LE = function writeUInt16LE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0);\n this[offset] = value & 0xff;\n this[offset + 1] = value >>> 8;\n return offset + 2;\n};\nBuffer.prototype.writeUint16BE = Buffer.prototype.writeUInt16BE = function writeUInt16BE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0);\n this[offset] = value >>> 8;\n this[offset + 1] = value & 0xff;\n return offset + 2;\n};\nBuffer.prototype.writeUint32LE = Buffer.prototype.writeUInt32LE = function writeUInt32LE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0);\n this[offset + 3] = value >>> 24;\n this[offset + 2] = value >>> 16;\n this[offset + 1] = value >>> 8;\n this[offset] = value & 0xff;\n return offset + 4;\n};\nBuffer.prototype.writeUint32BE = Buffer.prototype.writeUInt32BE = function writeUInt32BE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0);\n this[offset] = value >>> 24;\n this[offset + 1] = value >>> 16;\n this[offset + 2] = value >>> 8;\n this[offset + 3] = value & 0xff;\n return offset + 4;\n};\nBuffer.prototype.writeIntLE = function writeIntLE(value, offset, byteLength, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1);\n checkInt(this, value, offset, byteLength, limit - 1, -limit);\n }\n var i = 0;\n var mul = 1;\n var sub = 0;\n this[offset] = value & 0xFF;\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1;\n }\n this[offset + i] = (value / mul >> 0) - sub & 0xFF;\n }\n return offset + byteLength;\n};\nBuffer.prototype.writeIntBE = function writeIntBE(value, offset, byteLength, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1);\n checkInt(this, value, offset, byteLength, limit - 1, -limit);\n }\n var i = byteLength - 1;\n var mul = 1;\n var sub = 0;\n this[offset + i] = value & 0xFF;\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1;\n }\n this[offset + i] = (value / mul >> 0) - sub & 0xFF;\n }\n return offset + byteLength;\n};\nBuffer.prototype.writeInt8 = function writeInt8(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80);\n if (value < 0) value = 0xff + value + 1;\n this[offset] = value & 0xff;\n return offset + 1;\n};\nBuffer.prototype.writeInt16LE = function writeInt16LE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);\n this[offset] = value & 0xff;\n this[offset + 1] = value >>> 8;\n return offset + 2;\n};\nBuffer.prototype.writeInt16BE = function writeInt16BE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);\n this[offset] = value >>> 8;\n this[offset + 1] = value & 0xff;\n return offset + 2;\n};\nBuffer.prototype.writeInt32LE = function writeInt32LE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);\n this[offset] = value & 0xff;\n this[offset + 1] = value >>> 8;\n this[offset + 2] = value >>> 16;\n this[offset + 3] = value >>> 24;\n return offset + 4;\n};\nBuffer.prototype.writeInt32BE = function writeInt32BE(value, offset, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);\n if (value < 0) value = 0xffffffff + value + 1;\n this[offset] = value >>> 24;\n this[offset + 1] = value >>> 16;\n this[offset + 2] = value >>> 8;\n this[offset + 3] = value & 0xff;\n return offset + 4;\n};\nfunction checkIEEE754(buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range');\n if (offset < 0) throw new RangeError('Index out of range');\n}\nfunction writeFloat(buf, value, offset, littleEndian, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38);\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4);\n return offset + 4;\n}\nBuffer.prototype.writeFloatLE = function writeFloatLE(value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert);\n};\nBuffer.prototype.writeFloatBE = function writeFloatBE(value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert);\n};\nfunction writeDouble(buf, value, offset, littleEndian, noAssert) {\n value = +value;\n offset = offset >>> 0;\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308);\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8);\n return offset + 8;\n}\nBuffer.prototype.writeDoubleLE = function writeDoubleLE(value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert);\n};\nBuffer.prototype.writeDoubleBE = function writeDoubleBE(value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert);\n};\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy(target, targetStart, start, end) {\n if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer');\n if (!start) start = 0;\n if (!end && end !== 0) end = this.length;\n if (targetStart >= target.length) targetStart = target.length;\n if (!targetStart) targetStart = 0;\n if (end > 0 && end < start) end = start;\n\n // Copy 0 bytes; we're done\n if (end === start) return 0;\n if (target.length === 0 || this.length === 0) return 0;\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds');\n }\n if (start < 0 || start >= this.length) throw new RangeError('Index out of range');\n if (end < 0) throw new RangeError('sourceEnd out of bounds');\n\n // Are we oob?\n if (end > this.length) end = this.length;\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start;\n }\n var len = end - start;\n if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {\n // Use built-in when available, missing from IE11\n this.copyWithin(targetStart, start, end);\n } else {\n Uint8Array.prototype.set.call(target, this.subarray(start, end), targetStart);\n }\n return len;\n};\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill(val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start;\n start = 0;\n end = this.length;\n } else if (typeof end === 'string') {\n encoding = end;\n end = this.length;\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string');\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding);\n }\n if (val.length === 1) {\n var code = val.charCodeAt(0);\n if (encoding === 'utf8' && code < 128 || encoding === 'latin1') {\n // Fast path: If `val` fits into a single byte, use that numeric value.\n val = code;\n }\n }\n } else if (typeof val === 'number') {\n val = val & 255;\n } else if (typeof val === 'boolean') {\n val = Number(val);\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index');\n }\n if (end <= start) {\n return this;\n }\n start = start >>> 0;\n end = end === undefined ? this.length : end >>> 0;\n if (!val) val = 0;\n var i;\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val;\n }\n } else {\n var bytes = Buffer.isBuffer(val) ? val : Buffer.from(val, encoding);\n var len = bytes.length;\n if (len === 0) {\n throw new TypeError('The value \"' + val + '\" is invalid for argument \"value\"');\n }\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len];\n }\n }\n return this;\n};\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g;\nfunction base64clean(str) {\n // Node takes equal signs as end of the Base64 encoding\n str = str.split('=')[0];\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = str.trim().replace(INVALID_BASE64_RE, '');\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return '';\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '=';\n }\n return str;\n}\nfunction utf8ToBytes(string, units) {\n units = units || Infinity;\n var codePoint;\n var length = string.length;\n var leadSurrogate = null;\n var bytes = [];\n for (var i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i);\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);\n continue;\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);\n continue;\n }\n\n // valid lead\n leadSurrogate = codePoint;\n continue;\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);\n leadSurrogate = codePoint;\n continue;\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000;\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);\n }\n leadSurrogate = null;\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break;\n bytes.push(codePoint);\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break;\n bytes.push(codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80);\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break;\n bytes.push(codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80);\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break;\n bytes.push(codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80);\n } else {\n throw new Error('Invalid code point');\n }\n }\n return bytes;\n}\nfunction asciiToBytes(str) {\n var byteArray = [];\n for (var i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF);\n }\n return byteArray;\n}\nfunction utf16leToBytes(str, units) {\n var c, hi, lo;\n var byteArray = [];\n for (var i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break;\n c = str.charCodeAt(i);\n hi = c >> 8;\n lo = c % 256;\n byteArray.push(lo);\n byteArray.push(hi);\n }\n return byteArray;\n}\nfunction base64ToBytes(str) {\n return base64.toByteArray(base64clean(str));\n}\nfunction blitBuffer(src, dst, offset, length) {\n for (var i = 0; i < length; ++i) {\n if (i + offset >= dst.length || i >= src.length) break;\n dst[i + offset] = src[i];\n }\n return i;\n}\n\n// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass\n// the `instanceof` check but they should be treated as of that type.\n// See: https://github.com/feross/buffer/issues/166\nfunction isInstance(obj, type) {\n return obj instanceof type || obj != null && obj.constructor != null && obj.constructor.name != null && obj.constructor.name === type.name;\n}\nfunction numberIsNaN(obj) {\n // For IE11 support\n return obj !== obj; // eslint-disable-line no-self-compare\n}\n\n// Create lookup table for `toString('hex')`\n// See: https://github.com/feross/buffer/issues/219\nvar hexSliceLookupTable = function () {\n var alphabet = '0123456789abcdef';\n var table = new Array(256);\n for (var i = 0; i < 16; ++i) {\n var i16 = i * 16;\n for (var j = 0; j < 16; ++j) {\n table[i16 + j] = alphabet[i] + alphabet[j];\n }\n }\n return table;\n}();","/**\n * [js-sha256]{@link https://github.com/emn178/js-sha256}\n *\n * @version 0.11.0\n * @author Chen, Yi-Cyuan [emn178@gmail.com]\n * @copyright Chen, Yi-Cyuan 2014-2024\n * @license MIT\n */\n/*jslint bitwise: true */\n(function () {\n 'use strict';\n\n var ERROR = 'input is invalid type';\n var WINDOW = typeof window === 'object';\n var root = WINDOW ? window : {};\n if (root.JS_SHA256_NO_WINDOW) {\n WINDOW = false;\n }\n var WEB_WORKER = !WINDOW && typeof self === 'object';\n var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;\n if (NODE_JS) {\n root = global;\n } else if (WEB_WORKER) {\n root = self;\n }\n var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports;\n var AMD = typeof define === 'function' && define.amd;\n var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';\n var HEX_CHARS = '0123456789abcdef'.split('');\n var EXTRA = [-2147483648, 8388608, 32768, 128];\n var SHIFT = [24, 16, 8, 0];\n var K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];\n var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer'];\n var blocks = [];\n if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {\n Array.isArray = function (obj) {\n return Object.prototype.toString.call(obj) === '[object Array]';\n };\n }\n if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {\n ArrayBuffer.isView = function (obj) {\n return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;\n };\n }\n var createOutputMethod = function (outputType, is224) {\n return function (message) {\n return new Sha256(is224, true).update(message)[outputType]();\n };\n };\n var createMethod = function (is224) {\n var method = createOutputMethod('hex', is224);\n if (NODE_JS) {\n method = nodeWrap(method, is224);\n }\n method.create = function () {\n return new Sha256(is224);\n };\n method.update = function (message) {\n return method.create().update(message);\n };\n for (var i = 0; i < OUTPUT_TYPES.length; ++i) {\n var type = OUTPUT_TYPES[i];\n method[type] = createOutputMethod(type, is224);\n }\n return method;\n };\n var nodeWrap = function (method, is224) {\n var crypto = require('crypto');\n var Buffer = require('buffer').Buffer;\n var algorithm = is224 ? 'sha224' : 'sha256';\n var bufferFrom;\n if (Buffer.from && !root.JS_SHA256_NO_BUFFER_FROM) {\n bufferFrom = Buffer.from;\n } else {\n bufferFrom = function (message) {\n return new Buffer(message);\n };\n }\n var nodeMethod = function (message) {\n if (typeof message === 'string') {\n return crypto.createHash(algorithm).update(message, 'utf8').digest('hex');\n } else {\n if (message === null || message === undefined) {\n throw new Error(ERROR);\n } else if (message.constructor === ArrayBuffer) {\n message = new Uint8Array(message);\n }\n }\n if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) {\n return crypto.createHash(algorithm).update(bufferFrom(message)).digest('hex');\n } else {\n return method(message);\n }\n };\n return nodeMethod;\n };\n var createHmacOutputMethod = function (outputType, is224) {\n return function (key, message) {\n return new HmacSha256(key, is224, true).update(message)[outputType]();\n };\n };\n var createHmacMethod = function (is224) {\n var method = createHmacOutputMethod('hex', is224);\n method.create = function (key) {\n return new HmacSha256(key, is224);\n };\n method.update = function (key, message) {\n return method.create(key).update(message);\n };\n for (var i = 0; i < OUTPUT_TYPES.length; ++i) {\n var type = OUTPUT_TYPES[i];\n method[type] = createHmacOutputMethod(type, is224);\n }\n return method;\n };\n function Sha256(is224, sharedMemory) {\n if (sharedMemory) {\n blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n this.blocks = blocks;\n } else {\n this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n }\n if (is224) {\n this.h0 = 0xc1059ed8;\n this.h1 = 0x367cd507;\n this.h2 = 0x3070dd17;\n this.h3 = 0xf70e5939;\n this.h4 = 0xffc00b31;\n this.h5 = 0x68581511;\n this.h6 = 0x64f98fa7;\n this.h7 = 0xbefa4fa4;\n } else {\n // 256\n this.h0 = 0x6a09e667;\n this.h1 = 0xbb67ae85;\n this.h2 = 0x3c6ef372;\n this.h3 = 0xa54ff53a;\n this.h4 = 0x510e527f;\n this.h5 = 0x9b05688c;\n this.h6 = 0x1f83d9ab;\n this.h7 = 0x5be0cd19;\n }\n this.block = this.start = this.bytes = this.hBytes = 0;\n this.finalized = this.hashed = false;\n this.first = true;\n this.is224 = is224;\n }\n Sha256.prototype.update = function (message) {\n if (this.finalized) {\n return;\n }\n var notString,\n type = typeof message;\n if (type !== 'string') {\n if (type === 'object') {\n if (message === null) {\n throw new Error(ERROR);\n } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {\n message = new Uint8Array(message);\n } else if (!Array.isArray(message)) {\n if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {\n throw new Error(ERROR);\n }\n }\n } else {\n throw new Error(ERROR);\n }\n notString = true;\n }\n var code,\n index = 0,\n i,\n length = message.length,\n blocks = this.blocks;\n while (index < length) {\n if (this.hashed) {\n this.hashed = false;\n blocks[0] = this.block;\n this.block = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n }\n if (notString) {\n for (i = this.start; index < length && i < 64; ++index) {\n blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3];\n }\n } else {\n for (i = this.start; index < length && i < 64; ++index) {\n code = message.charCodeAt(index);\n if (code < 0x80) {\n blocks[i >>> 2] |= code << SHIFT[i++ & 3];\n } else if (code < 0x800) {\n blocks[i >>> 2] |= (0xc0 | code >>> 6) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];\n } else if (code < 0xd800 || code >= 0xe000) {\n blocks[i >>> 2] |= (0xe0 | code >>> 12) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code >>> 6 & 0x3f) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];\n } else {\n code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);\n blocks[i >>> 2] |= (0xf0 | code >>> 18) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code >>> 12 & 0x3f) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code >>> 6 & 0x3f) << SHIFT[i++ & 3];\n blocks[i >>> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];\n }\n }\n }\n this.lastByteIndex = i;\n this.bytes += i - this.start;\n if (i >= 64) {\n this.block = blocks[16];\n this.start = i - 64;\n this.hash();\n this.hashed = true;\n } else {\n this.start = i;\n }\n }\n if (this.bytes > 4294967295) {\n this.hBytes += this.bytes / 4294967296 << 0;\n this.bytes = this.bytes % 4294967296;\n }\n return this;\n };\n Sha256.prototype.finalize = function () {\n if (this.finalized) {\n return;\n }\n this.finalized = true;\n var blocks = this.blocks,\n i = this.lastByteIndex;\n blocks[16] = this.block;\n blocks[i >>> 2] |= EXTRA[i & 3];\n this.block = blocks[16];\n if (i >= 56) {\n if (!this.hashed) {\n this.hash();\n }\n blocks[0] = this.block;\n blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n }\n blocks[14] = this.hBytes << 3 | this.bytes >>> 29;\n blocks[15] = this.bytes << 3;\n this.hash();\n };\n Sha256.prototype.hash = function () {\n var a = this.h0,\n b = this.h1,\n c = this.h2,\n d = this.h3,\n e = this.h4,\n f = this.h5,\n g = this.h6,\n h = this.h7,\n blocks = this.blocks,\n j,\n s0,\n s1,\n maj,\n t1,\n t2,\n ch,\n ab,\n da,\n cd,\n bc;\n for (j = 16; j < 64; ++j) {\n // rightrotate\n t1 = blocks[j - 15];\n s0 = (t1 >>> 7 | t1 << 25) ^ (t1 >>> 18 | t1 << 14) ^ t1 >>> 3;\n t1 = blocks[j - 2];\n s1 = (t1 >>> 17 | t1 << 15) ^ (t1 >>> 19 | t1 << 13) ^ t1 >>> 10;\n blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;\n }\n bc = b & c;\n for (j = 0; j < 64; j += 4) {\n if (this.first) {\n if (this.is224) {\n ab = 300032;\n t1 = blocks[0] - 1413257819;\n h = t1 - 150054599 << 0;\n d = t1 + 24177077 << 0;\n } else {\n ab = 704751109;\n t1 = blocks[0] - 210244248;\n h = t1 - 1521486534 << 0;\n d = t1 + 143694565 << 0;\n }\n this.first = false;\n } else {\n s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);\n s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);\n ab = a & b;\n maj = ab ^ a & c ^ bc;\n ch = e & f ^ ~e & g;\n t1 = h + s1 + ch + K[j] + blocks[j];\n t2 = s0 + maj;\n h = d + t1 << 0;\n d = t1 + t2 << 0;\n }\n s0 = (d >>> 2 | d << 30) ^ (d >>> 13 | d << 19) ^ (d >>> 22 | d << 10);\n s1 = (h >>> 6 | h << 26) ^ (h >>> 11 | h << 21) ^ (h >>> 25 | h << 7);\n da = d & a;\n maj = da ^ d & b ^ ab;\n ch = h & e ^ ~h & f;\n t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];\n t2 = s0 + maj;\n g = c + t1 << 0;\n c = t1 + t2 << 0;\n s0 = (c >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10);\n s1 = (g >>> 6 | g << 26) ^ (g >>> 11 | g << 21) ^ (g >>> 25 | g << 7);\n cd = c & d;\n maj = cd ^ c & a ^ da;\n ch = g & h ^ ~g & e;\n t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];\n t2 = s0 + maj;\n f = b + t1 << 0;\n b = t1 + t2 << 0;\n s0 = (b >>> 2 | b << 30) ^ (b >>> 13 | b << 19) ^ (b >>> 22 | b << 10);\n s1 = (f >>> 6 | f << 26) ^ (f >>> 11 | f << 21) ^ (f >>> 25 | f << 7);\n bc = b & c;\n maj = bc ^ b & d ^ cd;\n ch = f & g ^ ~f & h;\n t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];\n t2 = s0 + maj;\n e = a + t1 << 0;\n a = t1 + t2 << 0;\n this.chromeBugWorkAround = true;\n }\n this.h0 = this.h0 + a << 0;\n this.h1 = this.h1 + b << 0;\n this.h2 = this.h2 + c << 0;\n this.h3 = this.h3 + d << 0;\n this.h4 = this.h4 + e << 0;\n this.h5 = this.h5 + f << 0;\n this.h6 = this.h6 + g << 0;\n this.h7 = this.h7 + h << 0;\n };\n Sha256.prototype.hex = function () {\n this.finalize();\n var h0 = this.h0,\n h1 = this.h1,\n h2 = this.h2,\n h3 = this.h3,\n h4 = this.h4,\n h5 = this.h5,\n h6 = this.h6,\n h7 = this.h7;\n var hex = HEX_CHARS[h0 >>> 28 & 0x0F] + HEX_CHARS[h0 >>> 24 & 0x0F] + HEX_CHARS[h0 >>> 20 & 0x0F] + HEX_CHARS[h0 >>> 16 & 0x0F] + HEX_CHARS[h0 >>> 12 & 0x0F] + HEX_CHARS[h0 >>> 8 & 0x0F] + HEX_CHARS[h0 >>> 4 & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[h1 >>> 28 & 0x0F] + HEX_CHARS[h1 >>> 24 & 0x0F] + HEX_CHARS[h1 >>> 20 & 0x0F] + HEX_CHARS[h1 >>> 16 & 0x0F] + HEX_CHARS[h1 >>> 12 & 0x0F] + HEX_CHARS[h1 >>> 8 & 0x0F] + HEX_CHARS[h1 >>> 4 & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[h2 >>> 28 & 0x0F] + HEX_CHARS[h2 >>> 24 & 0x0F] + HEX_CHARS[h2 >>> 20 & 0x0F] + HEX_CHARS[h2 >>> 16 & 0x0F] + HEX_CHARS[h2 >>> 12 & 0x0F] + HEX_CHARS[h2 >>> 8 & 0x0F] + HEX_CHARS[h2 >>> 4 & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[h3 >>> 28 & 0x0F] + HEX_CHARS[h3 >>> 24 & 0x0F] + HEX_CHARS[h3 >>> 20 & 0x0F] + HEX_CHARS[h3 >>> 16 & 0x0F] + HEX_CHARS[h3 >>> 12 & 0x0F] + HEX_CHARS[h3 >>> 8 & 0x0F] + HEX_CHARS[h3 >>> 4 & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[h4 >>> 28 & 0x0F] + HEX_CHARS[h4 >>> 24 & 0x0F] + HEX_CHARS[h4 >>> 20 & 0x0F] + HEX_CHARS[h4 >>> 16 & 0x0F] + HEX_CHARS[h4 >>> 12 & 0x0F] + HEX_CHARS[h4 >>> 8 & 0x0F] + HEX_CHARS[h4 >>> 4 & 0x0F] + HEX_CHARS[h4 & 0x0F] + HEX_CHARS[h5 >>> 28 & 0x0F] + HEX_CHARS[h5 >>> 24 & 0x0F] + HEX_CHARS[h5 >>> 20 & 0x0F] + HEX_CHARS[h5 >>> 16 & 0x0F] + HEX_CHARS[h5 >>> 12 & 0x0F] + HEX_CHARS[h5 >>> 8 & 0x0F] + HEX_CHARS[h5 >>> 4 & 0x0F] + HEX_CHARS[h5 & 0x0F] + HEX_CHARS[h6 >>> 28 & 0x0F] + HEX_CHARS[h6 >>> 24 & 0x0F] + HEX_CHARS[h6 >>> 20 & 0x0F] + HEX_CHARS[h6 >>> 16 & 0x0F] + HEX_CHARS[h6 >>> 12 & 0x0F] + HEX_CHARS[h6 >>> 8 & 0x0F] + HEX_CHARS[h6 >>> 4 & 0x0F] + HEX_CHARS[h6 & 0x0F];\n if (!this.is224) {\n hex += HEX_CHARS[h7 >>> 28 & 0x0F] + HEX_CHARS[h7 >>> 24 & 0x0F] + HEX_CHARS[h7 >>> 20 & 0x0F] + HEX_CHARS[h7 >>> 16 & 0x0F] + HEX_CHARS[h7 >>> 12 & 0x0F] + HEX_CHARS[h7 >>> 8 & 0x0F] + HEX_CHARS[h7 >>> 4 & 0x0F] + HEX_CHARS[h7 & 0x0F];\n }\n return hex;\n };\n Sha256.prototype.toString = Sha256.prototype.hex;\n Sha256.prototype.digest = function () {\n this.finalize();\n var h0 = this.h0,\n h1 = this.h1,\n h2 = this.h2,\n h3 = this.h3,\n h4 = this.h4,\n h5 = this.h5,\n h6 = this.h6,\n h7 = this.h7;\n var arr = [h0 >>> 24 & 0xFF, h0 >>> 16 & 0xFF, h0 >>> 8 & 0xFF, h0 & 0xFF, h1 >>> 24 & 0xFF, h1 >>> 16 & 0xFF, h1 >>> 8 & 0xFF, h1 & 0xFF, h2 >>> 24 & 0xFF, h2 >>> 16 & 0xFF, h2 >>> 8 & 0xFF, h2 & 0xFF, h3 >>> 24 & 0xFF, h3 >>> 16 & 0xFF, h3 >>> 8 & 0xFF, h3 & 0xFF, h4 >>> 24 & 0xFF, h4 >>> 16 & 0xFF, h4 >>> 8 & 0xFF, h4 & 0xFF, h5 >>> 24 & 0xFF, h5 >>> 16 & 0xFF, h5 >>> 8 & 0xFF, h5 & 0xFF, h6 >>> 24 & 0xFF, h6 >>> 16 & 0xFF, h6 >>> 8 & 0xFF, h6 & 0xFF];\n if (!this.is224) {\n arr.push(h7 >>> 24 & 0xFF, h7 >>> 16 & 0xFF, h7 >>> 8 & 0xFF, h7 & 0xFF);\n }\n return arr;\n };\n Sha256.prototype.array = Sha256.prototype.digest;\n Sha256.prototype.arrayBuffer = function () {\n this.finalize();\n var buffer = new ArrayBuffer(this.is224 ? 28 : 32);\n var dataView = new DataView(buffer);\n dataView.setUint32(0, this.h0);\n dataView.setUint32(4, this.h1);\n dataView.setUint32(8, this.h2);\n dataView.setUint32(12, this.h3);\n dataView.setUint32(16, this.h4);\n dataView.setUint32(20, this.h5);\n dataView.setUint32(24, this.h6);\n if (!this.is224) {\n dataView.setUint32(28, this.h7);\n }\n return buffer;\n };\n function HmacSha256(key, is224, sharedMemory) {\n var i,\n type = typeof key;\n if (type === 'string') {\n var bytes = [],\n length = key.length,\n index = 0,\n code;\n for (i = 0; i < length; ++i) {\n code = key.charCodeAt(i);\n if (code < 0x80) {\n bytes[index++] = code;\n } else if (code < 0x800) {\n bytes[index++] = 0xc0 | code >>> 6;\n bytes[index++] = 0x80 | code & 0x3f;\n } else if (code < 0xd800 || code >= 0xe000) {\n bytes[index++] = 0xe0 | code >>> 12;\n bytes[index++] = 0x80 | code >>> 6 & 0x3f;\n bytes[index++] = 0x80 | code & 0x3f;\n } else {\n code = 0x10000 + ((code & 0x3ff) << 10 | key.charCodeAt(++i) & 0x3ff);\n bytes[index++] = 0xf0 | code >>> 18;\n bytes[index++] = 0x80 | code >>> 12 & 0x3f;\n bytes[index++] = 0x80 | code >>> 6 & 0x3f;\n bytes[index++] = 0x80 | code & 0x3f;\n }\n }\n key = bytes;\n } else {\n if (type === 'object') {\n if (key === null) {\n throw new Error(ERROR);\n } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) {\n key = new Uint8Array(key);\n } else if (!Array.isArray(key)) {\n if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) {\n throw new Error(ERROR);\n }\n }\n } else {\n throw new Error(ERROR);\n }\n }\n if (key.length > 64) {\n key = new Sha256(is224, true).update(key).array();\n }\n var oKeyPad = [],\n iKeyPad = [];\n for (i = 0; i < 64; ++i) {\n var b = key[i] || 0;\n oKeyPad[i] = 0x5c ^ b;\n iKeyPad[i] = 0x36 ^ b;\n }\n Sha256.call(this, is224, sharedMemory);\n this.update(iKeyPad);\n this.oKeyPad = oKeyPad;\n this.inner = true;\n this.sharedMemory = sharedMemory;\n }\n HmacSha256.prototype = new Sha256();\n HmacSha256.prototype.finalize = function () {\n Sha256.prototype.finalize.call(this);\n if (this.inner) {\n this.inner = false;\n var innerHash = this.array();\n Sha256.call(this, this.is224, this.sharedMemory);\n this.update(this.oKeyPad);\n this.update(innerHash);\n Sha256.prototype.finalize.call(this);\n }\n };\n var exports = createMethod();\n exports.sha256 = exports;\n exports.sha224 = createMethod(true);\n exports.sha256.hmac = createHmacMethod();\n exports.sha224.hmac = createHmacMethod(true);\n if (COMMON_JS) {\n module.exports = exports;\n } else {\n root.sha256 = exports.sha256;\n root.sha224 = exports.sha224;\n if (AMD) {\n define(function () {\n return exports;\n });\n }\n }\n})();","/**\n * @license\n * Lodash \n * Copyright OpenJS Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n;\n(function () {\n /** Used as a safe reference for `undefined` in pre-ES5 environments. */\n var undefined;\n\n /** Used as the semantic version number. */\n var VERSION = '4.17.21';\n\n /** Used as the size to enable large array optimizations. */\n var LARGE_ARRAY_SIZE = 200;\n\n /** Error message constants. */\n var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',\n FUNC_ERROR_TEXT = 'Expected a function',\n INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`';\n\n /** Used to stand-in for `undefined` hash values. */\n var HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n /** Used as the maximum memoize cache size. */\n var MAX_MEMOIZE_SIZE = 500;\n\n /** Used as the internal argument placeholder. */\n var PLACEHOLDER = '__lodash_placeholder__';\n\n /** Used to compose bitmasks for cloning. */\n var CLONE_DEEP_FLAG = 1,\n CLONE_FLAT_FLAG = 2,\n CLONE_SYMBOLS_FLAG = 4;\n\n /** Used to compose bitmasks for value comparisons. */\n var COMPARE_PARTIAL_FLAG = 1,\n COMPARE_UNORDERED_FLAG = 2;\n\n /** Used to compose bitmasks for function metadata. */\n var WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_BOUND_FLAG = 4,\n WRAP_CURRY_FLAG = 8,\n WRAP_CURRY_RIGHT_FLAG = 16,\n WRAP_PARTIAL_FLAG = 32,\n WRAP_PARTIAL_RIGHT_FLAG = 64,\n WRAP_ARY_FLAG = 128,\n WRAP_REARG_FLAG = 256,\n WRAP_FLIP_FLAG = 512;\n\n /** Used as default options for `_.truncate`. */\n var DEFAULT_TRUNC_LENGTH = 30,\n DEFAULT_TRUNC_OMISSION = '...';\n\n /** Used to detect hot functions by number of calls within a span of milliseconds. */\n var HOT_COUNT = 800,\n HOT_SPAN = 16;\n\n /** Used to indicate the type of lazy iteratees. */\n var LAZY_FILTER_FLAG = 1,\n LAZY_MAP_FLAG = 2,\n LAZY_WHILE_FLAG = 3;\n\n /** Used as references for various `Number` constants. */\n var INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991,\n MAX_INTEGER = 1.7976931348623157e+308,\n NAN = 0 / 0;\n\n /** Used as references for the maximum length and index of an array. */\n var MAX_ARRAY_LENGTH = 4294967295,\n MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,\n HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;\n\n /** Used to associate wrap methods with their bit flags. */\n var wrapFlags = [['ary', WRAP_ARY_FLAG], ['bind', WRAP_BIND_FLAG], ['bindKey', WRAP_BIND_KEY_FLAG], ['curry', WRAP_CURRY_FLAG], ['curryRight', WRAP_CURRY_RIGHT_FLAG], ['flip', WRAP_FLIP_FLAG], ['partial', WRAP_PARTIAL_FLAG], ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], ['rearg', WRAP_REARG_FLAG]];\n\n /** `Object#toString` result references. */\n var argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n asyncTag = '[object AsyncFunction]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n domExcTag = '[object DOMException]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n nullTag = '[object Null]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n proxyTag = '[object Proxy]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n undefinedTag = '[object Undefined]',\n weakMapTag = '[object WeakMap]',\n weakSetTag = '[object WeakSet]';\n var arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n /** Used to match empty string literals in compiled template source. */\n var reEmptyStringLeading = /\\b__p \\+= '';/g,\n reEmptyStringMiddle = /\\b(__p \\+=) '' \\+/g,\n reEmptyStringTrailing = /(__e\\(.*?\\)|\\b__t\\)) \\+\\n'';/g;\n\n /** Used to match HTML entities and HTML characters. */\n var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,\n reUnescapedHtml = /[&<>\"']/g,\n reHasEscapedHtml = RegExp(reEscapedHtml.source),\n reHasUnescapedHtml = RegExp(reUnescapedHtml.source);\n\n /** Used to match template delimiters. */\n var reEscape = /<%-([\\s\\S]+?)%>/g,\n reEvaluate = /<%([\\s\\S]+?)%>/g,\n reInterpolate = /<%=([\\s\\S]+?)%>/g;\n\n /** Used to match property names within property paths. */\n var reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n reIsPlainProp = /^\\w*$/,\n rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n\n /**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\n var reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g,\n reHasRegExpChar = RegExp(reRegExpChar.source);\n\n /** Used to match leading whitespace. */\n var reTrimStart = /^\\s+/;\n\n /** Used to match a single whitespace character. */\n var reWhitespace = /\\s/;\n\n /** Used to match wrap detail comments. */\n var reWrapComment = /\\{(?:\\n\\/\\* \\[wrapped with .+\\] \\*\\/)?\\n?/,\n reWrapDetails = /\\{\\n\\/\\* \\[wrapped with (.+)\\] \\*/,\n reSplitDetails = /,? & /;\n\n /** Used to match words composed of alphanumeric characters. */\n var reAsciiWord = /[^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]+/g;\n\n /**\n * Used to validate the `validate` option in `_.template` variable.\n *\n * Forbids characters which could potentially change the meaning of the function argument definition:\n * - \"(),\" (modification of function parameters)\n * - \"=\" (default value)\n * - \"[]{}\" (destructuring of function parameters)\n * - \"/\" (beginning of a comment)\n * - whitespace\n */\n var reForbiddenIdentifierChars = /[()=,{}\\[\\]\\/\\s]/;\n\n /** Used to match backslashes in property paths. */\n var reEscapeChar = /\\\\(\\\\)?/g;\n\n /**\n * Used to match\n * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).\n */\n var reEsTemplate = /\\$\\{([^\\\\}]*(?:\\\\.[^\\\\}]*)*)\\}/g;\n\n /** Used to match `RegExp` flags from their coerced string values. */\n var reFlags = /\\w*$/;\n\n /** Used to detect bad signed hexadecimal string values. */\n var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n /** Used to detect binary string values. */\n var reIsBinary = /^0b[01]+$/i;\n\n /** Used to detect host constructors (Safari). */\n var reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n /** Used to detect octal string values. */\n var reIsOctal = /^0o[0-7]+$/i;\n\n /** Used to detect unsigned integer values. */\n var reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n /** Used to match Latin Unicode letters (excluding mathematical operators). */\n var reLatin = /[\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\xff\\u0100-\\u017f]/g;\n\n /** Used to ensure capturing order of template delimiters. */\n var reNoMatch = /($^)/;\n\n /** Used to match unescaped characters in compiled string literals. */\n var reUnescapedString = /['\\n\\r\\u2028\\u2029\\\\]/g;\n\n /** Used to compose unicode character classes. */\n var rsAstralRange = '\\\\ud800-\\\\udfff',\n rsComboMarksRange = '\\\\u0300-\\\\u036f',\n reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\n rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\n rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,\n rsDingbatRange = '\\\\u2700-\\\\u27bf',\n rsLowerRange = 'a-z\\\\xdf-\\\\xf6\\\\xf8-\\\\xff',\n rsMathOpRange = '\\\\xac\\\\xb1\\\\xd7\\\\xf7',\n rsNonCharRange = '\\\\x00-\\\\x2f\\\\x3a-\\\\x40\\\\x5b-\\\\x60\\\\x7b-\\\\xbf',\n rsPunctuationRange = '\\\\u2000-\\\\u206f',\n rsSpaceRange = ' \\\\t\\\\x0b\\\\f\\\\xa0\\\\ufeff\\\\n\\\\r\\\\u2028\\\\u2029\\\\u1680\\\\u180e\\\\u2000\\\\u2001\\\\u2002\\\\u2003\\\\u2004\\\\u2005\\\\u2006\\\\u2007\\\\u2008\\\\u2009\\\\u200a\\\\u202f\\\\u205f\\\\u3000',\n rsUpperRange = 'A-Z\\\\xc0-\\\\xd6\\\\xd8-\\\\xde',\n rsVarRange = '\\\\ufe0e\\\\ufe0f',\n rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;\n\n /** Used to compose unicode capture groups. */\n var rsApos = \"['\\u2019]\",\n rsAstral = '[' + rsAstralRange + ']',\n rsBreak = '[' + rsBreakRange + ']',\n rsCombo = '[' + rsComboRange + ']',\n rsDigits = '\\\\d+',\n rsDingbat = '[' + rsDingbatRange + ']',\n rsLower = '[' + rsLowerRange + ']',\n rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',\n rsFitz = '\\\\ud83c[\\\\udffb-\\\\udfff]',\n rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',\n rsNonAstral = '[^' + rsAstralRange + ']',\n rsRegional = '(?:\\\\ud83c[\\\\udde6-\\\\uddff]){2}',\n rsSurrPair = '[\\\\ud800-\\\\udbff][\\\\udc00-\\\\udfff]',\n rsUpper = '[' + rsUpperRange + ']',\n rsZWJ = '\\\\u200d';\n\n /** Used to compose unicode regexes. */\n var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',\n rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',\n rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',\n rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',\n reOptMod = rsModifier + '?',\n rsOptVar = '[' + rsVarRange + ']?',\n rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',\n rsOrdLower = '\\\\d*(?:1st|2nd|3rd|(?![123])\\\\dth)(?=\\\\b|[A-Z_])',\n rsOrdUpper = '\\\\d*(?:1ST|2ND|3RD|(?![123])\\\\dTH)(?=\\\\b|[a-z_])',\n rsSeq = rsOptVar + reOptMod + rsOptJoin,\n rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,\n rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';\n\n /** Used to match apostrophes. */\n var reApos = RegExp(rsApos, 'g');\n\n /**\n * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\n * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\n */\n var reComboMark = RegExp(rsCombo, 'g');\n\n /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */\n var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');\n\n /** Used to match complex or compound words. */\n var reUnicodeWord = RegExp([rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, rsUpper + '+' + rsOptContrUpper, rsOrdUpper, rsOrdLower, rsDigits, rsEmoji].join('|'), 'g');\n\n /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */\n var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');\n\n /** Used to detect strings that need a more robust regexp to match words. */\n var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;\n\n /** Used to assign default `context` object properties. */\n var contextProps = ['Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'];\n\n /** Used to make template sourceURLs easier to identify. */\n var templateCounter = -1;\n\n /** Used to identify `toStringTag` values of typed arrays. */\n var typedArrayTags = {};\n typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;\n typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;\n\n /** Used to identify `toStringTag` values supported by `_.clone`. */\n var cloneableTags = {};\n cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\n cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false;\n\n /** Used to map Latin Unicode letters to basic Latin letters. */\n var deburredLetters = {\n // Latin-1 Supplement block.\n '\\xc0': 'A',\n '\\xc1': 'A',\n '\\xc2': 'A',\n '\\xc3': 'A',\n '\\xc4': 'A',\n '\\xc5': 'A',\n '\\xe0': 'a',\n '\\xe1': 'a',\n '\\xe2': 'a',\n '\\xe3': 'a',\n '\\xe4': 'a',\n '\\xe5': 'a',\n '\\xc7': 'C',\n '\\xe7': 'c',\n '\\xd0': 'D',\n '\\xf0': 'd',\n '\\xc8': 'E',\n '\\xc9': 'E',\n '\\xca': 'E',\n '\\xcb': 'E',\n '\\xe8': 'e',\n '\\xe9': 'e',\n '\\xea': 'e',\n '\\xeb': 'e',\n '\\xcc': 'I',\n '\\xcd': 'I',\n '\\xce': 'I',\n '\\xcf': 'I',\n '\\xec': 'i',\n '\\xed': 'i',\n '\\xee': 'i',\n '\\xef': 'i',\n '\\xd1': 'N',\n '\\xf1': 'n',\n '\\xd2': 'O',\n '\\xd3': 'O',\n '\\xd4': 'O',\n '\\xd5': 'O',\n '\\xd6': 'O',\n '\\xd8': 'O',\n '\\xf2': 'o',\n '\\xf3': 'o',\n '\\xf4': 'o',\n '\\xf5': 'o',\n '\\xf6': 'o',\n '\\xf8': 'o',\n '\\xd9': 'U',\n '\\xda': 'U',\n '\\xdb': 'U',\n '\\xdc': 'U',\n '\\xf9': 'u',\n '\\xfa': 'u',\n '\\xfb': 'u',\n '\\xfc': 'u',\n '\\xdd': 'Y',\n '\\xfd': 'y',\n '\\xff': 'y',\n '\\xc6': 'Ae',\n '\\xe6': 'ae',\n '\\xde': 'Th',\n '\\xfe': 'th',\n '\\xdf': 'ss',\n // Latin Extended-A block.\n '\\u0100': 'A',\n '\\u0102': 'A',\n '\\u0104': 'A',\n '\\u0101': 'a',\n '\\u0103': 'a',\n '\\u0105': 'a',\n '\\u0106': 'C',\n '\\u0108': 'C',\n '\\u010a': 'C',\n '\\u010c': 'C',\n '\\u0107': 'c',\n '\\u0109': 'c',\n '\\u010b': 'c',\n '\\u010d': 'c',\n '\\u010e': 'D',\n '\\u0110': 'D',\n '\\u010f': 'd',\n '\\u0111': 'd',\n '\\u0112': 'E',\n '\\u0114': 'E',\n '\\u0116': 'E',\n '\\u0118': 'E',\n '\\u011a': 'E',\n '\\u0113': 'e',\n '\\u0115': 'e',\n '\\u0117': 'e',\n '\\u0119': 'e',\n '\\u011b': 'e',\n '\\u011c': 'G',\n '\\u011e': 'G',\n '\\u0120': 'G',\n '\\u0122': 'G',\n '\\u011d': 'g',\n '\\u011f': 'g',\n '\\u0121': 'g',\n '\\u0123': 'g',\n '\\u0124': 'H',\n '\\u0126': 'H',\n '\\u0125': 'h',\n '\\u0127': 'h',\n '\\u0128': 'I',\n '\\u012a': 'I',\n '\\u012c': 'I',\n '\\u012e': 'I',\n '\\u0130': 'I',\n '\\u0129': 'i',\n '\\u012b': 'i',\n '\\u012d': 'i',\n '\\u012f': 'i',\n '\\u0131': 'i',\n '\\u0134': 'J',\n '\\u0135': 'j',\n '\\u0136': 'K',\n '\\u0137': 'k',\n '\\u0138': 'k',\n '\\u0139': 'L',\n '\\u013b': 'L',\n '\\u013d': 'L',\n '\\u013f': 'L',\n '\\u0141': 'L',\n '\\u013a': 'l',\n '\\u013c': 'l',\n '\\u013e': 'l',\n '\\u0140': 'l',\n '\\u0142': 'l',\n '\\u0143': 'N',\n '\\u0145': 'N',\n '\\u0147': 'N',\n '\\u014a': 'N',\n '\\u0144': 'n',\n '\\u0146': 'n',\n '\\u0148': 'n',\n '\\u014b': 'n',\n '\\u014c': 'O',\n '\\u014e': 'O',\n '\\u0150': 'O',\n '\\u014d': 'o',\n '\\u014f': 'o',\n '\\u0151': 'o',\n '\\u0154': 'R',\n '\\u0156': 'R',\n '\\u0158': 'R',\n '\\u0155': 'r',\n '\\u0157': 'r',\n '\\u0159': 'r',\n '\\u015a': 'S',\n '\\u015c': 'S',\n '\\u015e': 'S',\n '\\u0160': 'S',\n '\\u015b': 's',\n '\\u015d': 's',\n '\\u015f': 's',\n '\\u0161': 's',\n '\\u0162': 'T',\n '\\u0164': 'T',\n '\\u0166': 'T',\n '\\u0163': 't',\n '\\u0165': 't',\n '\\u0167': 't',\n '\\u0168': 'U',\n '\\u016a': 'U',\n '\\u016c': 'U',\n '\\u016e': 'U',\n '\\u0170': 'U',\n '\\u0172': 'U',\n '\\u0169': 'u',\n '\\u016b': 'u',\n '\\u016d': 'u',\n '\\u016f': 'u',\n '\\u0171': 'u',\n '\\u0173': 'u',\n '\\u0174': 'W',\n '\\u0175': 'w',\n '\\u0176': 'Y',\n '\\u0177': 'y',\n '\\u0178': 'Y',\n '\\u0179': 'Z',\n '\\u017b': 'Z',\n '\\u017d': 'Z',\n '\\u017a': 'z',\n '\\u017c': 'z',\n '\\u017e': 'z',\n '\\u0132': 'IJ',\n '\\u0133': 'ij',\n '\\u0152': 'Oe',\n '\\u0153': 'oe',\n '\\u0149': \"'n\",\n '\\u017f': 's'\n };\n\n /** Used to map characters to HTML entities. */\n var htmlEscapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n };\n\n /** Used to map HTML entities to characters. */\n var htmlUnescapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\"\n };\n\n /** Used to escape characters for inclusion in compiled string literals. */\n var stringEscapes = {\n '\\\\': '\\\\',\n \"'\": \"'\",\n '\\n': 'n',\n '\\r': 'r',\n '\\u2028': 'u2028',\n '\\u2029': 'u2029'\n };\n\n /** Built-in method references without a dependency on `root`. */\n var freeParseFloat = parseFloat,\n freeParseInt = parseInt;\n\n /** Detect free variable `global` from Node.js. */\n var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n /** Detect free variable `self`. */\n var freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n /** Used as a reference to the global object. */\n var root = freeGlobal || freeSelf || Function('return this')();\n\n /** Detect free variable `exports`. */\n var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n /** Detect free variable `module`. */\n var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n /** Detect the popular CommonJS extension `module.exports`. */\n var moduleExports = freeModule && freeModule.exports === freeExports;\n\n /** Detect free variable `process` from Node.js. */\n var freeProcess = moduleExports && freeGlobal.process;\n\n /** Used to access faster Node.js helpers. */\n var nodeUtil = function () {\n try {\n // Use `util.types` for Node.js 10+.\n var types = freeModule && freeModule.require && freeModule.require('util').types;\n if (types) {\n return types;\n }\n\n // Legacy `process.binding('util')` for Node.js < 10.\n return freeProcess && freeProcess.binding && freeProcess.binding('util');\n } catch (e) {}\n }();\n\n /* Node.js helper references. */\n var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,\n nodeIsDate = nodeUtil && nodeUtil.isDate,\n nodeIsMap = nodeUtil && nodeUtil.isMap,\n nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,\n nodeIsSet = nodeUtil && nodeUtil.isSet,\n nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\n function apply(func, thisArg, args) {\n switch (args.length) {\n case 0:\n return func.call(thisArg);\n case 1:\n return func.call(thisArg, args[0]);\n case 2:\n return func.call(thisArg, args[0], args[1]);\n case 3:\n return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n }\n\n /**\n * A specialized version of `baseAggregator` for arrays.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function arrayAggregator(array, setter, iteratee, accumulator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n while (++index < length) {\n var value = array[index];\n setter(accumulator, value, iteratee(value), array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEach(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length;\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.forEachRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEachRight(array, iteratee) {\n var length = array == null ? 0 : array.length;\n while (length--) {\n if (iteratee(array[length], length, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.every` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n */\n function arrayEvery(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n while (++index < length) {\n if (!predicate(array[index], index, array)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * A specialized version of `_.filter` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function arrayFilter(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludes(array, value) {\n var length = array == null ? 0 : array.length;\n return !!length && baseIndexOf(array, value, 0) > -1;\n }\n\n /**\n * This function is like `arrayIncludes` except that it accepts a comparator.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludesWith(array, value, comparator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n while (++index < length) {\n if (comparator(value, array[index])) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `_.map` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function arrayMap(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length,\n result = Array(length);\n while (++index < length) {\n result[index] = iteratee(array[index], index, array);\n }\n return result;\n }\n\n /**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\n function arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n }\n\n /**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduce(array, iteratee, accumulator, initAccum) {\n var index = -1,\n length = array == null ? 0 : array.length;\n if (initAccum && length) {\n accumulator = array[++index];\n }\n while (++index < length) {\n accumulator = iteratee(accumulator, array[index], index, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.reduceRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the last element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduceRight(array, iteratee, accumulator, initAccum) {\n var length = array == null ? 0 : array.length;\n if (initAccum && length) {\n accumulator = array[--length];\n }\n while (length--) {\n accumulator = iteratee(accumulator, array[length], length, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function arraySome(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Gets the size of an ASCII `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n var asciiSize = baseProperty('length');\n\n /**\n * Converts an ASCII `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function asciiToArray(string) {\n return string.split('');\n }\n\n /**\n * Splits an ASCII `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function asciiWords(string) {\n return string.match(reAsciiWord) || [];\n }\n\n /**\n * The base implementation of methods like `_.findKey` and `_.findLastKey`,\n * without support for iteratee shorthands, which iterates over `collection`\n * using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the found element or its key, else `undefined`.\n */\n function baseFindKey(collection, predicate, eachFunc) {\n var result;\n eachFunc(collection, function (value, key, collection) {\n if (predicate(value, key, collection)) {\n result = key;\n return false;\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n while (fromRight ? index-- : ++index < length) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOf(array, value, fromIndex) {\n return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex);\n }\n\n /**\n * This function is like `baseIndexOf` except that it accepts a comparator.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOfWith(array, value, fromIndex, comparator) {\n var index = fromIndex - 1,\n length = array.length;\n while (++index < length) {\n if (comparator(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.isNaN` without support for number objects.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n */\n function baseIsNaN(value) {\n return value !== value;\n }\n\n /**\n * The base implementation of `_.mean` and `_.meanBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the mean.\n */\n function baseMean(array, iteratee) {\n var length = array == null ? 0 : array.length;\n return length ? baseSum(array, iteratee) / length : NAN;\n }\n\n /**\n * The base implementation of `_.property` without support for deep paths.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function baseProperty(key) {\n return function (object) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.propertyOf` without support for deep paths.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyOf(object) {\n return function (key) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.reduce` and `_.reduceRight`, without support\n * for iteratee shorthands, which iterates over `collection` using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} accumulator The initial value.\n * @param {boolean} initAccum Specify using the first or last element of\n * `collection` as the initial value.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the accumulated value.\n */\n function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {\n eachFunc(collection, function (value, index, collection) {\n accumulator = initAccum ? (initAccum = false, value) : iteratee(accumulator, value, index, collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.sortBy` which uses `comparer` to define the\n * sort order of `array` and replaces criteria objects with their corresponding\n * values.\n *\n * @private\n * @param {Array} array The array to sort.\n * @param {Function} comparer The function to define sort order.\n * @returns {Array} Returns `array`.\n */\n function baseSortBy(array, comparer) {\n var length = array.length;\n array.sort(comparer);\n while (length--) {\n array[length] = array[length].value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.sum` and `_.sumBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the sum.\n */\n function baseSum(array, iteratee) {\n var result,\n index = -1,\n length = array.length;\n while (++index < length) {\n var current = iteratee(array[index]);\n if (current !== undefined) {\n result = result === undefined ? current : result + current;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\n function baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array\n * of key-value pairs for `object` corresponding to the property names of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the key-value pairs.\n */\n function baseToPairs(object, props) {\n return arrayMap(props, function (key) {\n return [key, object[key]];\n });\n }\n\n /**\n * The base implementation of `_.trim`.\n *\n * @private\n * @param {string} string The string to trim.\n * @returns {string} Returns the trimmed string.\n */\n function baseTrim(string) {\n return string ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') : string;\n }\n\n /**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\n function baseUnary(func) {\n return function (value) {\n return func(value);\n };\n }\n\n /**\n * The base implementation of `_.values` and `_.valuesIn` which creates an\n * array of `object` property values corresponding to the property names\n * of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the array of property values.\n */\n function baseValues(object, props) {\n return arrayMap(props, function (key) {\n return object[key];\n });\n }\n\n /**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function cacheHas(cache, key) {\n return cache.has(key);\n }\n\n /**\n * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the first unmatched string symbol.\n */\n function charsStartIndex(strSymbols, chrSymbols) {\n var index = -1,\n length = strSymbols.length;\n while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the last unmatched string symbol.\n */\n function charsEndIndex(strSymbols, chrSymbols) {\n var index = strSymbols.length;\n while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Gets the number of `placeholder` occurrences in `array`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} placeholder The placeholder to search for.\n * @returns {number} Returns the placeholder count.\n */\n function countHolders(array, placeholder) {\n var length = array.length,\n result = 0;\n while (length--) {\n if (array[length] === placeholder) {\n ++result;\n }\n }\n return result;\n }\n\n /**\n * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A\n * letters to basic Latin letters.\n *\n * @private\n * @param {string} letter The matched letter to deburr.\n * @returns {string} Returns the deburred letter.\n */\n var deburrLetter = basePropertyOf(deburredLetters);\n\n /**\n * Used by `_.escape` to convert characters to HTML entities.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n var escapeHtmlChar = basePropertyOf(htmlEscapes);\n\n /**\n * Used by `_.template` to escape characters for inclusion in compiled string literals.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n function escapeStringChar(chr) {\n return '\\\\' + stringEscapes[chr];\n }\n\n /**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function getValue(object, key) {\n return object == null ? undefined : object[key];\n }\n\n /**\n * Checks if `string` contains Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a symbol is found, else `false`.\n */\n function hasUnicode(string) {\n return reHasUnicode.test(string);\n }\n\n /**\n * Checks if `string` contains a word composed of Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a word is found, else `false`.\n */\n function hasUnicodeWord(string) {\n return reHasUnicodeWord.test(string);\n }\n\n /**\n * Converts `iterator` to an array.\n *\n * @private\n * @param {Object} iterator The iterator to convert.\n * @returns {Array} Returns the converted array.\n */\n function iteratorToArray(iterator) {\n var data,\n result = [];\n while (!(data = iterator.next()).done) {\n result.push(data.value);\n }\n return result;\n }\n\n /**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\n function mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n map.forEach(function (value, key) {\n result[++index] = [key, value];\n });\n return result;\n }\n\n /**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\n function overArg(func, transform) {\n return function (arg) {\n return func(transform(arg));\n };\n }\n\n /**\n * Replaces all `placeholder` elements in `array` with an internal placeholder\n * and returns an array of their indexes.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {*} placeholder The placeholder to replace.\n * @returns {Array} Returns the new array of placeholder indexes.\n */\n function replaceHolders(array, placeholder) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n while (++index < length) {\n var value = array[index];\n if (value === placeholder || value === PLACEHOLDER) {\n array[index] = PLACEHOLDER;\n result[resIndex++] = index;\n }\n }\n return result;\n }\n\n /**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\n function setToArray(set) {\n var index = -1,\n result = Array(set.size);\n set.forEach(function (value) {\n result[++index] = value;\n });\n return result;\n }\n\n /**\n * Converts `set` to its value-value pairs.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the value-value pairs.\n */\n function setToPairs(set) {\n var index = -1,\n result = Array(set.size);\n set.forEach(function (value) {\n result[++index] = [value, value];\n });\n return result;\n }\n\n /**\n * A specialized version of `_.indexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictIndexOf(array, value, fromIndex) {\n var index = fromIndex - 1,\n length = array.length;\n while (++index < length) {\n if (array[index] === value) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * A specialized version of `_.lastIndexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictLastIndexOf(array, value, fromIndex) {\n var index = fromIndex + 1;\n while (index--) {\n if (array[index] === value) {\n return index;\n }\n }\n return index;\n }\n\n /**\n * Gets the number of symbols in `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the string size.\n */\n function stringSize(string) {\n return hasUnicode(string) ? unicodeSize(string) : asciiSize(string);\n }\n\n /**\n * Converts `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function stringToArray(string) {\n return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);\n }\n\n /**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace\n * character of `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the index of the last non-whitespace character.\n */\n function trimmedEndIndex(string) {\n var index = string.length;\n while (index-- && reWhitespace.test(string.charAt(index))) {}\n return index;\n }\n\n /**\n * Used by `_.unescape` to convert HTML entities to characters.\n *\n * @private\n * @param {string} chr The matched character to unescape.\n * @returns {string} Returns the unescaped character.\n */\n var unescapeHtmlChar = basePropertyOf(htmlUnescapes);\n\n /**\n * Gets the size of a Unicode `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n function unicodeSize(string) {\n var result = reUnicode.lastIndex = 0;\n while (reUnicode.test(string)) {\n ++result;\n }\n return result;\n }\n\n /**\n * Converts a Unicode `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function unicodeToArray(string) {\n return string.match(reUnicode) || [];\n }\n\n /**\n * Splits a Unicode `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function unicodeWords(string) {\n return string.match(reUnicodeWord) || [];\n }\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * Create a new pristine `lodash` function using the `context` object.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Util\n * @param {Object} [context=root] The context object.\n * @returns {Function} Returns a new `lodash` function.\n * @example\n *\n * _.mixin({ 'foo': _.constant('foo') });\n *\n * var lodash = _.runInContext();\n * lodash.mixin({ 'bar': lodash.constant('bar') });\n *\n * _.isFunction(_.foo);\n * // => true\n * _.isFunction(_.bar);\n * // => false\n *\n * lodash.isFunction(lodash.foo);\n * // => false\n * lodash.isFunction(lodash.bar);\n * // => true\n *\n * // Create a suped-up `defer` in Node.js.\n * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;\n */\n var runInContext = function runInContext(context) {\n context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));\n\n /** Built-in constructor references. */\n var Array = context.Array,\n Date = context.Date,\n Error = context.Error,\n Function = context.Function,\n Math = context.Math,\n Object = context.Object,\n RegExp = context.RegExp,\n String = context.String,\n TypeError = context.TypeError;\n\n /** Used for built-in method references. */\n var arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n /** Used to detect overreaching core-js shims. */\n var coreJsData = context['__core-js_shared__'];\n\n /** Used to resolve the decompiled source of functions. */\n var funcToString = funcProto.toString;\n\n /** Used to check objects for own properties. */\n var hasOwnProperty = objectProto.hasOwnProperty;\n\n /** Used to generate unique IDs. */\n var idCounter = 0;\n\n /** Used to detect methods masquerading as native. */\n var maskSrcKey = function () {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? 'Symbol(src)_1.' + uid : '';\n }();\n\n /**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\n var nativeObjectToString = objectProto.toString;\n\n /** Used to infer the `Object` constructor. */\n var objectCtorString = funcToString.call(Object);\n\n /** Used to restore the original `_` reference in `_.noConflict`. */\n var oldDash = root._;\n\n /** Used to detect if a method is native. */\n var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&').replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$');\n\n /** Built-in value references. */\n var Buffer = moduleExports ? context.Buffer : undefined,\n Symbol = context.Symbol,\n Uint8Array = context.Uint8Array,\n allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,\n getPrototype = overArg(Object.getPrototypeOf, Object),\n objectCreate = Object.create,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice,\n spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,\n symIterator = Symbol ? Symbol.iterator : undefined,\n symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n var defineProperty = function () {\n try {\n var func = getNative(Object, 'defineProperty');\n func({}, '', {});\n return func;\n } catch (e) {}\n }();\n\n /** Mocked built-ins. */\n var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,\n ctxNow = Date && Date.now !== root.Date.now && Date.now,\n ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;\n\n /* Built-in method references for those with the same name as other `lodash` methods. */\n var nativeCeil = Math.ceil,\n nativeFloor = Math.floor,\n nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n nativeIsFinite = context.isFinite,\n nativeJoin = arrayProto.join,\n nativeKeys = overArg(Object.keys, Object),\n nativeMax = Math.max,\n nativeMin = Math.min,\n nativeNow = Date.now,\n nativeParseInt = context.parseInt,\n nativeRandom = Math.random,\n nativeReverse = arrayProto.reverse;\n\n /* Built-in method references that are verified to be native. */\n var DataView = getNative(context, 'DataView'),\n Map = getNative(context, 'Map'),\n Promise = getNative(context, 'Promise'),\n Set = getNative(context, 'Set'),\n WeakMap = getNative(context, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n /** Used to store function metadata. */\n var metaMap = WeakMap && new WeakMap();\n\n /** Used to lookup unminified function names. */\n var realNames = {};\n\n /** Used to detect maps, sets, and weakmaps. */\n var dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n /** Used to convert symbols to primitives and strings. */\n var symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,\n symbolToString = symbolProto ? symbolProto.toString : undefined;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` object which wraps `value` to enable implicit method\n * chain sequences. Methods that operate on and return arrays, collections,\n * and functions can be chained together. Methods that retrieve a single value\n * or may return a primitive value will automatically end the chain sequence\n * and return the unwrapped value. Otherwise, the value must be unwrapped\n * with `_#value`.\n *\n * Explicit chain sequences, which must be unwrapped with `_#value`, may be\n * enabled using `_.chain`.\n *\n * The execution of chained methods is lazy, that is, it's deferred until\n * `_#value` is implicitly or explicitly called.\n *\n * Lazy evaluation allows several methods to support shortcut fusion.\n * Shortcut fusion is an optimization to merge iteratee calls; this avoids\n * the creation of intermediate arrays and can greatly reduce the number of\n * iteratee executions. Sections of a chain sequence qualify for shortcut\n * fusion if the section is applied to an array and iteratees accept only\n * one argument. The heuristic for whether a section qualifies for shortcut\n * fusion is subject to change.\n *\n * Chaining is supported in custom builds as long as the `_#value` method is\n * directly or indirectly included in the build.\n *\n * In addition to lodash methods, wrappers have `Array` and `String` methods.\n *\n * The wrapper `Array` methods are:\n * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`\n *\n * The wrapper `String` methods are:\n * `replace` and `split`\n *\n * The wrapper methods that support shortcut fusion are:\n * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,\n * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,\n * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`\n *\n * The chainable wrapper methods are:\n * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,\n * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,\n * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,\n * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,\n * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,\n * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,\n * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,\n * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,\n * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,\n * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,\n * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,\n * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,\n * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,\n * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,\n * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,\n * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,\n * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,\n * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,\n * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,\n * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,\n * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,\n * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,\n * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,\n * `zipObject`, `zipObjectDeep`, and `zipWith`\n *\n * The wrapper methods that are **not** chainable by default are:\n * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,\n * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,\n * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,\n * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,\n * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,\n * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,\n * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,\n * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,\n * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,\n * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,\n * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,\n * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,\n * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,\n * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,\n * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,\n * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,\n * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,\n * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,\n * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,\n * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,\n * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,\n * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,\n * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,\n * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,\n * `upperFirst`, `value`, and `words`\n *\n * @name _\n * @constructor\n * @category Seq\n * @param {*} value The value to wrap in a `lodash` instance.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2, 3]);\n *\n * // Returns an unwrapped value.\n * wrapped.reduce(_.add);\n * // => 6\n *\n * // Returns a wrapped value.\n * var squares = wrapped.map(square);\n *\n * _.isArray(squares);\n * // => false\n *\n * _.isArray(squares.value());\n * // => true\n */\n function lodash(value) {\n if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {\n if (value instanceof LodashWrapper) {\n return value;\n }\n if (hasOwnProperty.call(value, '__wrapped__')) {\n return wrapperClone(value);\n }\n }\n return new LodashWrapper(value);\n }\n\n /**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} proto The object to inherit from.\n * @returns {Object} Returns the new object.\n */\n var baseCreate = function () {\n function object() {}\n return function (proto) {\n if (!isObject(proto)) {\n return {};\n }\n if (objectCreate) {\n return objectCreate(proto);\n }\n object.prototype = proto;\n var result = new object();\n object.prototype = undefined;\n return result;\n };\n }();\n\n /**\n * The function whose prototype chain sequence wrappers inherit from.\n *\n * @private\n */\n function baseLodash() {\n // No operation performed.\n }\n\n /**\n * The base constructor for creating `lodash` wrapper objects.\n *\n * @private\n * @param {*} value The value to wrap.\n * @param {boolean} [chainAll] Enable explicit method chain sequences.\n */\n function LodashWrapper(value, chainAll) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__chain__ = !!chainAll;\n this.__index__ = 0;\n this.__values__ = undefined;\n }\n\n /**\n * By default, the template delimiters used by lodash are like those in\n * embedded Ruby (ERB) as well as ES2015 template strings. Change the\n * following template settings to use alternative delimiters.\n *\n * @static\n * @memberOf _\n * @type {Object}\n */\n lodash.templateSettings = {\n /**\n * Used to detect `data` property values to be HTML-escaped.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'escape': reEscape,\n /**\n * Used to detect code to be evaluated.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'evaluate': reEvaluate,\n /**\n * Used to detect `data` property values to inject.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'interpolate': reInterpolate,\n /**\n * Used to reference the data object in the template text.\n *\n * @memberOf _.templateSettings\n * @type {string}\n */\n 'variable': '',\n /**\n * Used to import variables into the compiled template.\n *\n * @memberOf _.templateSettings\n * @type {Object}\n */\n 'imports': {\n /**\n * A reference to the `lodash` function.\n *\n * @memberOf _.templateSettings.imports\n * @type {Function}\n */\n '_': lodash\n }\n };\n\n // Ensure wrappers are instances of `baseLodash`.\n lodash.prototype = baseLodash.prototype;\n lodash.prototype.constructor = lodash;\n LodashWrapper.prototype = baseCreate(baseLodash.prototype);\n LodashWrapper.prototype.constructor = LodashWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.\n *\n * @private\n * @constructor\n * @param {*} value The value to wrap.\n */\n function LazyWrapper(value) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__dir__ = 1;\n this.__filtered__ = false;\n this.__iteratees__ = [];\n this.__takeCount__ = MAX_ARRAY_LENGTH;\n this.__views__ = [];\n }\n\n /**\n * Creates a clone of the lazy wrapper object.\n *\n * @private\n * @name clone\n * @memberOf LazyWrapper\n * @returns {Object} Returns the cloned `LazyWrapper` object.\n */\n function lazyClone() {\n var result = new LazyWrapper(this.__wrapped__);\n result.__actions__ = copyArray(this.__actions__);\n result.__dir__ = this.__dir__;\n result.__filtered__ = this.__filtered__;\n result.__iteratees__ = copyArray(this.__iteratees__);\n result.__takeCount__ = this.__takeCount__;\n result.__views__ = copyArray(this.__views__);\n return result;\n }\n\n /**\n * Reverses the direction of lazy iteration.\n *\n * @private\n * @name reverse\n * @memberOf LazyWrapper\n * @returns {Object} Returns the new reversed `LazyWrapper` object.\n */\n function lazyReverse() {\n if (this.__filtered__) {\n var result = new LazyWrapper(this);\n result.__dir__ = -1;\n result.__filtered__ = true;\n } else {\n result = this.clone();\n result.__dir__ *= -1;\n }\n return result;\n }\n\n /**\n * Extracts the unwrapped value from its lazy wrapper.\n *\n * @private\n * @name value\n * @memberOf LazyWrapper\n * @returns {*} Returns the unwrapped value.\n */\n function lazyValue() {\n var array = this.__wrapped__.value(),\n dir = this.__dir__,\n isArr = isArray(array),\n isRight = dir < 0,\n arrLength = isArr ? array.length : 0,\n view = getView(0, arrLength, this.__views__),\n start = view.start,\n end = view.end,\n length = end - start,\n index = isRight ? end : start - 1,\n iteratees = this.__iteratees__,\n iterLength = iteratees.length,\n resIndex = 0,\n takeCount = nativeMin(length, this.__takeCount__);\n if (!isArr || !isRight && arrLength == length && takeCount == length) {\n return baseWrapperValue(array, this.__actions__);\n }\n var result = [];\n outer: while (length-- && resIndex < takeCount) {\n index += dir;\n var iterIndex = -1,\n value = array[index];\n while (++iterIndex < iterLength) {\n var data = iteratees[iterIndex],\n iteratee = data.iteratee,\n type = data.type,\n computed = iteratee(value);\n if (type == LAZY_MAP_FLAG) {\n value = computed;\n } else if (!computed) {\n if (type == LAZY_FILTER_FLAG) {\n continue outer;\n } else {\n break outer;\n }\n }\n }\n result[resIndex++] = value;\n }\n return result;\n }\n\n // Ensure `LazyWrapper` is an instance of `baseLodash`.\n LazyWrapper.prototype = baseCreate(baseLodash.prototype);\n LazyWrapper.prototype.constructor = LazyWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Hash(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\n function hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function hashDelete(key) {\n var result = this.has(key) && delete this.__data__[key];\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n }\n\n /**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n }\n\n /**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\n function hashSet(key, value) {\n var data = this.__data__;\n this.size += this.has(key) ? 0 : 1;\n data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED : value;\n return this;\n }\n\n // Add methods to `Hash`.\n Hash.prototype.clear = hashClear;\n Hash.prototype['delete'] = hashDelete;\n Hash.prototype.get = hashGet;\n Hash.prototype.has = hashHas;\n Hash.prototype.set = hashSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function ListCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\n function listCacheClear() {\n this.__data__ = [];\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n --this.size;\n return true;\n }\n\n /**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n return index < 0 ? undefined : data[index][1];\n }\n\n /**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n }\n\n /**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\n function listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n if (index < 0) {\n ++this.size;\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n }\n\n // Add methods to `ListCache`.\n ListCache.prototype.clear = listCacheClear;\n ListCache.prototype['delete'] = listCacheDelete;\n ListCache.prototype.get = listCacheGet;\n ListCache.prototype.has = listCacheHas;\n ListCache.prototype.set = listCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function MapCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\n function mapCacheClear() {\n this.size = 0;\n this.__data__ = {\n 'hash': new Hash(),\n 'map': new (Map || ListCache)(),\n 'string': new Hash()\n };\n }\n\n /**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function mapCacheDelete(key) {\n var result = getMapData(this, key)['delete'](key);\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function mapCacheGet(key) {\n return getMapData(this, key).get(key);\n }\n\n /**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function mapCacheHas(key) {\n return getMapData(this, key).has(key);\n }\n\n /**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\n function mapCacheSet(key, value) {\n var data = getMapData(this, key),\n size = data.size;\n data.set(key, value);\n this.size += data.size == size ? 0 : 1;\n return this;\n }\n\n // Add methods to `MapCache`.\n MapCache.prototype.clear = mapCacheClear;\n MapCache.prototype['delete'] = mapCacheDelete;\n MapCache.prototype.get = mapCacheGet;\n MapCache.prototype.has = mapCacheHas;\n MapCache.prototype.set = mapCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\n function SetCache(values) {\n var index = -1,\n length = values == null ? 0 : values.length;\n this.__data__ = new MapCache();\n while (++index < length) {\n this.add(values[index]);\n }\n }\n\n /**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\n function setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n }\n\n /**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\n function setCacheHas(value) {\n return this.__data__.has(value);\n }\n\n // Add methods to `SetCache`.\n SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\n SetCache.prototype.has = setCacheHas;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Stack(entries) {\n var data = this.__data__ = new ListCache(entries);\n this.size = data.size;\n }\n\n /**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\n function stackClear() {\n this.__data__ = new ListCache();\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function stackDelete(key) {\n var data = this.__data__,\n result = data['delete'](key);\n this.size = data.size;\n return result;\n }\n\n /**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function stackGet(key) {\n return this.__data__.get(key);\n }\n\n /**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function stackHas(key) {\n return this.__data__.has(key);\n }\n\n /**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\n function stackSet(key, value) {\n var data = this.__data__;\n if (data instanceof ListCache) {\n var pairs = data.__data__;\n if (!Map || pairs.length < LARGE_ARRAY_SIZE - 1) {\n pairs.push([key, value]);\n this.size = ++data.size;\n return this;\n }\n data = this.__data__ = new MapCache(pairs);\n }\n data.set(key, value);\n this.size = data.size;\n return this;\n }\n\n // Add methods to `Stack`.\n Stack.prototype.clear = stackClear;\n Stack.prototype['delete'] = stackDelete;\n Stack.prototype.get = stackGet;\n Stack.prototype.has = stackHas;\n Stack.prototype.set = stackSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\n function arrayLikeKeys(value, inherited) {\n var isArr = isArray(value),\n isArg = !isArr && isArguments(value),\n isBuff = !isArr && !isArg && isBuffer(value),\n isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n skipIndexes = isArr || isArg || isBuff || isType,\n result = skipIndexes ? baseTimes(value.length, String) : [],\n length = result.length;\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (\n // Safari 9 has enumerable `arguments.length` in strict mode.\n key == 'length' ||\n // Node.js 0.10 has enumerable non-index properties on buffers.\n isBuff && (key == 'offset' || key == 'parent') ||\n // PhantomJS 2 has enumerable non-index properties on typed arrays.\n isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') ||\n // Skip index properties.\n isIndex(key, length)))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.sample` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @returns {*} Returns the random element.\n */\n function arraySample(array) {\n var length = array.length;\n return length ? array[baseRandom(0, length - 1)] : undefined;\n }\n\n /**\n * A specialized version of `_.sampleSize` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function arraySampleSize(array, n) {\n return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));\n }\n\n /**\n * A specialized version of `_.shuffle` for arrays.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function arrayShuffle(array) {\n return shuffleSelf(copyArray(array));\n }\n\n /**\n * This function is like `assignValue` except that it doesn't assign\n * `undefined` values.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignMergeValue(object, key, value) {\n if (value !== undefined && !eq(object[key], value) || value === undefined && !(key in object)) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || value === undefined && !(key in object)) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n }\n\n /**\n * Aggregates elements of `collection` on `accumulator` with keys transformed\n * by `iteratee` and values set by `setter`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseAggregator(collection, setter, iteratee, accumulator) {\n baseEach(collection, function (value, key, collection) {\n setter(accumulator, value, iteratee(value), collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssign(object, source) {\n return object && copyObject(source, keys(source), object);\n }\n\n /**\n * The base implementation of `_.assignIn` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssignIn(object, source) {\n return object && copyObject(source, keysIn(source), object);\n }\n\n /**\n * The base implementation of `assignValue` and `assignMergeValue` without\n * value checks.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function baseAssignValue(object, key, value) {\n if (key == '__proto__' && defineProperty) {\n defineProperty(object, key, {\n 'configurable': true,\n 'enumerable': true,\n 'value': value,\n 'writable': true\n });\n } else {\n object[key] = value;\n }\n }\n\n /**\n * The base implementation of `_.at` without support for individual paths.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {string[]} paths The property paths to pick.\n * @returns {Array} Returns the picked elements.\n */\n function baseAt(object, paths) {\n var index = -1,\n length = paths.length,\n result = Array(length),\n skip = object == null;\n while (++index < length) {\n result[index] = skip ? undefined : get(object, paths[index]);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.clamp` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n */\n function baseClamp(number, lower, upper) {\n if (number === number) {\n if (upper !== undefined) {\n number = number <= upper ? number : upper;\n }\n if (lower !== undefined) {\n number = number >= lower ? number : lower;\n }\n }\n return number;\n }\n\n /**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Deep clone\n * 2 - Flatten inherited properties\n * 4 - Clone symbols\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\n function baseClone(value, bitmask, customizer, key, object, stack) {\n var result,\n isDeep = bitmask & CLONE_DEEP_FLAG,\n isFlat = bitmask & CLONE_FLAT_FLAG,\n isFull = bitmask & CLONE_SYMBOLS_FLAG;\n if (customizer) {\n result = object ? customizer(value, key, object, stack) : customizer(value);\n }\n if (result !== undefined) {\n return result;\n }\n if (!isObject(value)) {\n return value;\n }\n var isArr = isArray(value);\n if (isArr) {\n result = initCloneArray(value);\n if (!isDeep) {\n return copyArray(value, result);\n }\n } else {\n var tag = getTag(value),\n isFunc = tag == funcTag || tag == genTag;\n if (isBuffer(value)) {\n return cloneBuffer(value, isDeep);\n }\n if (tag == objectTag || tag == argsTag || isFunc && !object) {\n result = isFlat || isFunc ? {} : initCloneObject(value);\n if (!isDeep) {\n return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value));\n }\n } else {\n if (!cloneableTags[tag]) {\n return object ? value : {};\n }\n result = initCloneByTag(value, tag, isDeep);\n }\n }\n // Check for circular references and return its corresponding clone.\n stack || (stack = new Stack());\n var stacked = stack.get(value);\n if (stacked) {\n return stacked;\n }\n stack.set(value, result);\n if (isSet(value)) {\n value.forEach(function (subValue) {\n result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));\n });\n } else if (isMap(value)) {\n value.forEach(function (subValue, key) {\n result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n }\n var keysFunc = isFull ? isFlat ? getAllKeysIn : getAllKeys : isFlat ? keysIn : keys;\n var props = isArr ? undefined : keysFunc(value);\n arrayEach(props || value, function (subValue, key) {\n if (props) {\n key = subValue;\n subValue = value[key];\n }\n // Recursively populate clone (susceptible to call stack limits).\n assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n return result;\n }\n\n /**\n * The base implementation of `_.conforms` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property predicates to conform to.\n * @returns {Function} Returns the new spec function.\n */\n function baseConforms(source) {\n var props = keys(source);\n return function (object) {\n return baseConformsTo(object, source, props);\n };\n }\n\n /**\n * The base implementation of `_.conformsTo` which accepts `props` to check.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property predicates to conform to.\n * @returns {boolean} Returns `true` if `object` conforms, else `false`.\n */\n function baseConformsTo(object, source, props) {\n var length = props.length;\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (length--) {\n var key = props[length],\n predicate = source[key],\n value = object[key];\n if (value === undefined && !(key in object) || !predicate(value)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.delay` and `_.defer` which accepts `args`\n * to provide to `func`.\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {Array} args The arguments to provide to `func`.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n function baseDelay(func, wait, args) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return setTimeout(function () {\n func.apply(undefined, args);\n }, wait);\n }\n\n /**\n * The base implementation of methods like `_.difference` without support\n * for excluding multiple arrays or iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Array} values The values to exclude.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n */\n function baseDifference(array, values, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n isCommon = true,\n length = array.length,\n result = [],\n valuesLength = values.length;\n if (!length) {\n return result;\n }\n if (iteratee) {\n values = arrayMap(values, baseUnary(iteratee));\n }\n if (comparator) {\n includes = arrayIncludesWith;\n isCommon = false;\n } else if (values.length >= LARGE_ARRAY_SIZE) {\n includes = cacheHas;\n isCommon = false;\n values = new SetCache(values);\n }\n outer: while (++index < length) {\n var value = array[index],\n computed = iteratee == null ? value : iteratee(value);\n value = comparator || value !== 0 ? value : 0;\n if (isCommon && computed === computed) {\n var valuesIndex = valuesLength;\n while (valuesIndex--) {\n if (values[valuesIndex] === computed) {\n continue outer;\n }\n }\n result.push(value);\n } else if (!includes(values, computed, comparator)) {\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.forEach` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEach = createBaseEach(baseForOwn);\n\n /**\n * The base implementation of `_.forEachRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEachRight = createBaseEach(baseForOwnRight, true);\n\n /**\n * The base implementation of `_.every` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`\n */\n function baseEvery(collection, predicate) {\n var result = true;\n baseEach(collection, function (value, index, collection) {\n result = !!predicate(value, index, collection);\n return result;\n });\n return result;\n }\n\n /**\n * The base implementation of methods like `_.max` and `_.min` which accepts a\n * `comparator` to determine the extremum value.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The iteratee invoked per iteration.\n * @param {Function} comparator The comparator used to compare values.\n * @returns {*} Returns the extremum value.\n */\n function baseExtremum(array, iteratee, comparator) {\n var index = -1,\n length = array.length;\n while (++index < length) {\n var value = array[index],\n current = iteratee(value);\n if (current != null && (computed === undefined ? current === current && !isSymbol(current) : comparator(current, computed))) {\n var computed = current,\n result = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.fill` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n */\n function baseFill(array, value, start, end) {\n var length = array.length;\n start = toInteger(start);\n if (start < 0) {\n start = -start > length ? 0 : length + start;\n }\n end = end === undefined || end > length ? length : toInteger(end);\n if (end < 0) {\n end += length;\n }\n end = start > end ? 0 : toLength(end);\n while (start < end) {\n array[start++] = value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.filter` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function baseFilter(collection, predicate) {\n var result = [];\n baseEach(collection, function (value, index, collection) {\n if (predicate(value, index, collection)) {\n result.push(value);\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\n function baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n predicate || (predicate = isFlattenable);\n result || (result = []);\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `baseForOwn` which iterates over `object`\n * properties returned by `keysFunc` and invokes `iteratee` for each property.\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseFor = createBaseFor();\n\n /**\n * This function is like `baseFor` except that it iterates over properties\n * in the opposite order.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseForRight = createBaseFor(true);\n\n /**\n * The base implementation of `_.forOwn` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwn(object, iteratee) {\n return object && baseFor(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.forOwnRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwnRight(object, iteratee) {\n return object && baseForRight(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.functions` which creates an array of\n * `object` function property names filtered from `props`.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Array} props The property names to filter.\n * @returns {Array} Returns the function names.\n */\n function baseFunctions(object, props) {\n return arrayFilter(props, function (key) {\n return isFunction(object[key]);\n });\n }\n\n /**\n * The base implementation of `_.get` without support for default values.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @returns {*} Returns the resolved value.\n */\n function baseGet(object, path) {\n path = castPath(path, object);\n var index = 0,\n length = path.length;\n while (object != null && index < length) {\n object = object[toKey(path[index++])];\n }\n return index && index == length ? object : undefined;\n }\n\n /**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n }\n\n /**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n function baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);\n }\n\n /**\n * The base implementation of `_.gt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n */\n function baseGt(value, other) {\n return value > other;\n }\n\n /**\n * The base implementation of `_.has` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHas(object, key) {\n return object != null && hasOwnProperty.call(object, key);\n }\n\n /**\n * The base implementation of `_.hasIn` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHasIn(object, key) {\n return object != null && key in Object(object);\n }\n\n /**\n * The base implementation of `_.inRange` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to check.\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n */\n function baseInRange(number, start, end) {\n return number >= nativeMin(start, end) && number < nativeMax(start, end);\n }\n\n /**\n * The base implementation of methods like `_.intersection`, without support\n * for iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of shared values.\n */\n function baseIntersection(arrays, iteratee, comparator) {\n var includes = comparator ? arrayIncludesWith : arrayIncludes,\n length = arrays[0].length,\n othLength = arrays.length,\n othIndex = othLength,\n caches = Array(othLength),\n maxLength = Infinity,\n result = [];\n while (othIndex--) {\n var array = arrays[othIndex];\n if (othIndex && iteratee) {\n array = arrayMap(array, baseUnary(iteratee));\n }\n maxLength = nativeMin(array.length, maxLength);\n caches[othIndex] = !comparator && (iteratee || length >= 120 && array.length >= 120) ? new SetCache(othIndex && array) : undefined;\n }\n array = arrays[0];\n var index = -1,\n seen = caches[0];\n outer: while (++index < length && result.length < maxLength) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n value = comparator || value !== 0 ? value : 0;\n if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator))) {\n othIndex = othLength;\n while (--othIndex) {\n var cache = caches[othIndex];\n if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator))) {\n continue outer;\n }\n }\n if (seen) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.invert` and `_.invertBy` which inverts\n * `object` with values transformed by `iteratee` and set by `setter`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform values.\n * @param {Object} accumulator The initial inverted object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseInverter(object, setter, iteratee, accumulator) {\n baseForOwn(object, function (value, key, object) {\n setter(accumulator, iteratee(value), key, object);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.invoke` without support for individual\n * method arguments.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the method to invoke.\n * @param {Array} args The arguments to invoke the method with.\n * @returns {*} Returns the result of the invoked method.\n */\n function baseInvoke(object, path, args) {\n path = castPath(path, object);\n object = parent(object, path);\n var func = object == null ? object : object[toKey(last(path))];\n return func == null ? undefined : apply(func, object, args);\n }\n\n /**\n * The base implementation of `_.isArguments`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n */\n function baseIsArguments(value) {\n return isObjectLike(value) && baseGetTag(value) == argsTag;\n }\n\n /**\n * The base implementation of `_.isArrayBuffer` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.\n */\n function baseIsArrayBuffer(value) {\n return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;\n }\n\n /**\n * The base implementation of `_.isDate` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a date object, else `false`.\n */\n function baseIsDate(value) {\n return isObjectLike(value) && baseGetTag(value) == dateTag;\n }\n\n /**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\n function baseIsEqual(value, other, bitmask, customizer, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || !isObjectLike(value) && !isObjectLike(other)) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n }\n\n /**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {\n var objIsArr = isArray(object),\n othIsArr = isArray(other),\n objTag = objIsArr ? arrayTag : getTag(object),\n othTag = othIsArr ? arrayTag : getTag(other);\n objTag = objTag == argsTag ? objectTag : objTag;\n othTag = othTag == argsTag ? objectTag : othTag;\n var objIsObj = objTag == objectTag,\n othIsObj = othTag == objectTag,\n isSameTag = objTag == othTag;\n if (isSameTag && isBuffer(object)) {\n if (!isBuffer(other)) {\n return false;\n }\n objIsArr = true;\n objIsObj = false;\n }\n if (isSameTag && !objIsObj) {\n stack || (stack = new Stack());\n return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);\n }\n if (!(bitmask & COMPARE_PARTIAL_FLAG)) {\n var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n if (objIsWrapped || othIsWrapped) {\n var objUnwrapped = objIsWrapped ? object.value() : object,\n othUnwrapped = othIsWrapped ? other.value() : other;\n stack || (stack = new Stack());\n return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);\n }\n }\n if (!isSameTag) {\n return false;\n }\n stack || (stack = new Stack());\n return equalObjects(object, other, bitmask, customizer, equalFunc, stack);\n }\n\n /**\n * The base implementation of `_.isMap` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n */\n function baseIsMap(value) {\n return isObjectLike(value) && getTag(value) == mapTag;\n }\n\n /**\n * The base implementation of `_.isMatch` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Array} matchData The property names, values, and compare flags to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n */\n function baseIsMatch(object, source, matchData, customizer) {\n var index = matchData.length,\n length = index,\n noCustomizer = !customizer;\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (index--) {\n var data = matchData[index];\n if (noCustomizer && data[2] ? data[1] !== object[data[0]] : !(data[0] in object)) {\n return false;\n }\n }\n while (++index < length) {\n data = matchData[index];\n var key = data[0],\n objValue = object[key],\n srcValue = data[1];\n if (noCustomizer && data[2]) {\n if (objValue === undefined && !(key in object)) {\n return false;\n }\n } else {\n var stack = new Stack();\n if (customizer) {\n var result = customizer(objValue, srcValue, key, object, source, stack);\n }\n if (!(result === undefined ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) : result)) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\n function baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = isFunction(value) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n }\n\n /**\n * The base implementation of `_.isRegExp` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.\n */\n function baseIsRegExp(value) {\n return isObjectLike(value) && baseGetTag(value) == regexpTag;\n }\n\n /**\n * The base implementation of `_.isSet` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a set, else `false`.\n */\n function baseIsSet(value) {\n return isObjectLike(value) && getTag(value) == setTag;\n }\n\n /**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\n function baseIsTypedArray(value) {\n return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];\n }\n\n /**\n * The base implementation of `_.iteratee`.\n *\n * @private\n * @param {*} [value=_.identity] The value to convert to an iteratee.\n * @returns {Function} Returns the iteratee.\n */\n function baseIteratee(value) {\n // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n if (typeof value == 'function') {\n return value;\n }\n if (value == null) {\n return identity;\n }\n if (typeof value == 'object') {\n return isArray(value) ? baseMatchesProperty(value[0], value[1]) : baseMatches(value);\n }\n return property(value);\n }\n\n /**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeysIn(object) {\n if (!isObject(object)) {\n return nativeKeysIn(object);\n }\n var isProto = isPrototype(object),\n result = [];\n for (var key in object) {\n if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.lt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than `other`,\n * else `false`.\n */\n function baseLt(value, other) {\n return value < other;\n }\n\n /**\n * The base implementation of `_.map` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function baseMap(collection, iteratee) {\n var index = -1,\n result = isArrayLike(collection) ? Array(collection.length) : [];\n baseEach(collection, function (value, key, collection) {\n result[++index] = iteratee(value, key, collection);\n });\n return result;\n }\n\n /**\n * The base implementation of `_.matches` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property values to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatches(source) {\n var matchData = getMatchData(source);\n if (matchData.length == 1 && matchData[0][2]) {\n return matchesStrictComparable(matchData[0][0], matchData[0][1]);\n }\n return function (object) {\n return object === source || baseIsMatch(object, source, matchData);\n };\n }\n\n /**\n * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.\n *\n * @private\n * @param {string} path The path of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatchesProperty(path, srcValue) {\n if (isKey(path) && isStrictComparable(srcValue)) {\n return matchesStrictComparable(toKey(path), srcValue);\n }\n return function (object) {\n var objValue = get(object, path);\n return objValue === undefined && objValue === srcValue ? hasIn(object, path) : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);\n };\n }\n\n /**\n * The base implementation of `_.merge` without support for multiple sources.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} [customizer] The function to customize merged values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMerge(object, source, srcIndex, customizer, stack) {\n if (object === source) {\n return;\n }\n baseFor(source, function (srcValue, key) {\n stack || (stack = new Stack());\n if (isObject(srcValue)) {\n baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);\n } else {\n var newValue = customizer ? customizer(safeGet(object, key), srcValue, key + '', object, source, stack) : undefined;\n if (newValue === undefined) {\n newValue = srcValue;\n }\n assignMergeValue(object, key, newValue);\n }\n }, keysIn);\n }\n\n /**\n * A specialized version of `baseMerge` for arrays and objects which performs\n * deep merges and tracks traversed objects enabling objects with circular\n * references to be merged.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {string} key The key of the value to merge.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} mergeFunc The function to merge values.\n * @param {Function} [customizer] The function to customize assigned values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {\n var objValue = safeGet(object, key),\n srcValue = safeGet(source, key),\n stacked = stack.get(srcValue);\n if (stacked) {\n assignMergeValue(object, key, stacked);\n return;\n }\n var newValue = customizer ? customizer(objValue, srcValue, key + '', object, source, stack) : undefined;\n var isCommon = newValue === undefined;\n if (isCommon) {\n var isArr = isArray(srcValue),\n isBuff = !isArr && isBuffer(srcValue),\n isTyped = !isArr && !isBuff && isTypedArray(srcValue);\n newValue = srcValue;\n if (isArr || isBuff || isTyped) {\n if (isArray(objValue)) {\n newValue = objValue;\n } else if (isArrayLikeObject(objValue)) {\n newValue = copyArray(objValue);\n } else if (isBuff) {\n isCommon = false;\n newValue = cloneBuffer(srcValue, true);\n } else if (isTyped) {\n isCommon = false;\n newValue = cloneTypedArray(srcValue, true);\n } else {\n newValue = [];\n }\n } else if (isPlainObject(srcValue) || isArguments(srcValue)) {\n newValue = objValue;\n if (isArguments(objValue)) {\n newValue = toPlainObject(objValue);\n } else if (!isObject(objValue) || isFunction(objValue)) {\n newValue = initCloneObject(srcValue);\n }\n } else {\n isCommon = false;\n }\n }\n if (isCommon) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, newValue);\n mergeFunc(newValue, srcValue, srcIndex, customizer, stack);\n stack['delete'](srcValue);\n }\n assignMergeValue(object, key, newValue);\n }\n\n /**\n * The base implementation of `_.nth` which doesn't coerce arguments.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {number} n The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n */\n function baseNth(array, n) {\n var length = array.length;\n if (!length) {\n return;\n }\n n += n < 0 ? length : 0;\n return isIndex(n, length) ? array[n] : undefined;\n }\n\n /**\n * The base implementation of `_.orderBy` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n * @param {string[]} orders The sort orders of `iteratees`.\n * @returns {Array} Returns the new sorted array.\n */\n function baseOrderBy(collection, iteratees, orders) {\n if (iteratees.length) {\n iteratees = arrayMap(iteratees, function (iteratee) {\n if (isArray(iteratee)) {\n return function (value) {\n return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);\n };\n }\n return iteratee;\n });\n } else {\n iteratees = [identity];\n }\n var index = -1;\n iteratees = arrayMap(iteratees, baseUnary(getIteratee()));\n var result = baseMap(collection, function (value, key, collection) {\n var criteria = arrayMap(iteratees, function (iteratee) {\n return iteratee(value);\n });\n return {\n 'criteria': criteria,\n 'index': ++index,\n 'value': value\n };\n });\n return baseSortBy(result, function (object, other) {\n return compareMultiple(object, other, orders);\n });\n }\n\n /**\n * The base implementation of `_.pick` without support for individual\n * property identifiers.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @returns {Object} Returns the new object.\n */\n function basePick(object, paths) {\n return basePickBy(object, paths, function (value, path) {\n return hasIn(object, path);\n });\n }\n\n /**\n * The base implementation of `_.pickBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @param {Function} predicate The function invoked per property.\n * @returns {Object} Returns the new object.\n */\n function basePickBy(object, paths, predicate) {\n var index = -1,\n length = paths.length,\n result = {};\n while (++index < length) {\n var path = paths[index],\n value = baseGet(object, path);\n if (predicate(value, path)) {\n baseSet(result, castPath(path, object), value);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `baseProperty` which supports deep paths.\n *\n * @private\n * @param {Array|string} path The path of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyDeep(path) {\n return function (object) {\n return baseGet(object, path);\n };\n }\n\n /**\n * The base implementation of `_.pullAllBy` without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n */\n function basePullAll(array, values, iteratee, comparator) {\n var indexOf = comparator ? baseIndexOfWith : baseIndexOf,\n index = -1,\n length = values.length,\n seen = array;\n if (array === values) {\n values = copyArray(values);\n }\n if (iteratee) {\n seen = arrayMap(array, baseUnary(iteratee));\n }\n while (++index < length) {\n var fromIndex = 0,\n value = values[index],\n computed = iteratee ? iteratee(value) : value;\n while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {\n if (seen !== array) {\n splice.call(seen, fromIndex, 1);\n }\n splice.call(array, fromIndex, 1);\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.pullAt` without support for individual\n * indexes or capturing the removed elements.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {number[]} indexes The indexes of elements to remove.\n * @returns {Array} Returns `array`.\n */\n function basePullAt(array, indexes) {\n var length = array ? indexes.length : 0,\n lastIndex = length - 1;\n while (length--) {\n var index = indexes[length];\n if (length == lastIndex || index !== previous) {\n var previous = index;\n if (isIndex(index)) {\n splice.call(array, index, 1);\n } else {\n baseUnset(array, index);\n }\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.random` without support for returning\n * floating-point numbers.\n *\n * @private\n * @param {number} lower The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the random number.\n */\n function baseRandom(lower, upper) {\n return lower + nativeFloor(nativeRandom() * (upper - lower + 1));\n }\n\n /**\n * The base implementation of `_.range` and `_.rangeRight` which doesn't\n * coerce arguments.\n *\n * @private\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @param {number} step The value to increment or decrement by.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the range of numbers.\n */\n function baseRange(start, end, step, fromRight) {\n var index = -1,\n length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),\n result = Array(length);\n while (length--) {\n result[fromRight ? length : ++index] = start;\n start += step;\n }\n return result;\n }\n\n /**\n * The base implementation of `_.repeat` which doesn't coerce arguments.\n *\n * @private\n * @param {string} string The string to repeat.\n * @param {number} n The number of times to repeat the string.\n * @returns {string} Returns the repeated string.\n */\n function baseRepeat(string, n) {\n var result = '';\n if (!string || n < 1 || n > MAX_SAFE_INTEGER) {\n return result;\n }\n // Leverage the exponentiation by squaring algorithm for a faster repeat.\n // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.\n do {\n if (n % 2) {\n result += string;\n }\n n = nativeFloor(n / 2);\n if (n) {\n string += string;\n }\n } while (n);\n return result;\n }\n\n /**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\n function baseRest(func, start) {\n return setToString(overRest(func, start, identity), func + '');\n }\n\n /**\n * The base implementation of `_.sample`.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n */\n function baseSample(collection) {\n return arraySample(values(collection));\n }\n\n /**\n * The base implementation of `_.sampleSize` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function baseSampleSize(collection, n) {\n var array = values(collection);\n return shuffleSelf(array, baseClamp(n, 0, array.length));\n }\n\n /**\n * The base implementation of `_.set`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseSet(object, path, value, customizer) {\n if (!isObject(object)) {\n return object;\n }\n path = castPath(path, object);\n var index = -1,\n length = path.length,\n lastIndex = length - 1,\n nested = object;\n while (nested != null && ++index < length) {\n var key = toKey(path[index]),\n newValue = value;\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n return object;\n }\n if (index != lastIndex) {\n var objValue = nested[key];\n newValue = customizer ? customizer(objValue, key, nested) : undefined;\n if (newValue === undefined) {\n newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};\n }\n }\n assignValue(nested, key, newValue);\n nested = nested[key];\n }\n return object;\n }\n\n /**\n * The base implementation of `setData` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var baseSetData = !metaMap ? identity : function (func, data) {\n metaMap.set(func, data);\n return func;\n };\n\n /**\n * The base implementation of `setToString` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var baseSetToString = !defineProperty ? identity : function (func, string) {\n return defineProperty(func, 'toString', {\n 'configurable': true,\n 'enumerable': false,\n 'value': constant(string),\n 'writable': true\n });\n };\n\n /**\n * The base implementation of `_.shuffle`.\n *\n * @private\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function baseShuffle(collection) {\n return shuffleSelf(values(collection));\n }\n\n /**\n * The base implementation of `_.slice` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseSlice(array, start, end) {\n var index = -1,\n length = array.length;\n if (start < 0) {\n start = -start > length ? 0 : length + start;\n }\n end = end > length ? length : end;\n if (end < 0) {\n end += length;\n }\n length = start > end ? 0 : end - start >>> 0;\n start >>>= 0;\n var result = Array(length);\n while (++index < length) {\n result[index] = array[index + start];\n }\n return result;\n }\n\n /**\n * The base implementation of `_.some` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function baseSome(collection, predicate) {\n var result;\n baseEach(collection, function (value, index, collection) {\n result = predicate(value, index, collection);\n return !result;\n });\n return !!result;\n }\n\n /**\n * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which\n * performs a binary search of `array` to determine the index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndex(array, value, retHighest) {\n var low = 0,\n high = array == null ? low : array.length;\n if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {\n while (low < high) {\n var mid = low + high >>> 1,\n computed = array[mid];\n if (computed !== null && !isSymbol(computed) && (retHighest ? computed <= value : computed < value)) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return high;\n }\n return baseSortedIndexBy(array, value, identity, retHighest);\n }\n\n /**\n * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`\n * which invokes `iteratee` for `value` and each element of `array` to compute\n * their sort ranking. The iteratee is invoked with one argument; (value).\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} iteratee The iteratee invoked per element.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndexBy(array, value, iteratee, retHighest) {\n var low = 0,\n high = array == null ? 0 : array.length;\n if (high === 0) {\n return 0;\n }\n value = iteratee(value);\n var valIsNaN = value !== value,\n valIsNull = value === null,\n valIsSymbol = isSymbol(value),\n valIsUndefined = value === undefined;\n while (low < high) {\n var mid = nativeFloor((low + high) / 2),\n computed = iteratee(array[mid]),\n othIsDefined = computed !== undefined,\n othIsNull = computed === null,\n othIsReflexive = computed === computed,\n othIsSymbol = isSymbol(computed);\n if (valIsNaN) {\n var setLow = retHighest || othIsReflexive;\n } else if (valIsUndefined) {\n setLow = othIsReflexive && (retHighest || othIsDefined);\n } else if (valIsNull) {\n setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);\n } else if (valIsSymbol) {\n setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);\n } else if (othIsNull || othIsSymbol) {\n setLow = false;\n } else {\n setLow = retHighest ? computed <= value : computed < value;\n }\n if (setLow) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return nativeMin(high, MAX_ARRAY_INDEX);\n }\n\n /**\n * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseSortedUniq(array, iteratee) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n if (!index || !eq(computed, seen)) {\n var seen = computed;\n result[resIndex++] = value === 0 ? 0 : value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toNumber` which doesn't ensure correct\n * conversions of binary, hexadecimal, or octal string values.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n */\n function baseToNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n return +value;\n }\n\n /**\n * The base implementation of `_.toString` which doesn't convert nullish\n * values to empty strings.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n */\n function baseToString(value) {\n // Exit early for strings to avoid a performance hit in some environments.\n if (typeof value == 'string') {\n return value;\n }\n if (isArray(value)) {\n // Recursively convert values (susceptible to call stack limits).\n return arrayMap(value, baseToString) + '';\n }\n if (isSymbol(value)) {\n return symbolToString ? symbolToString.call(value) : '';\n }\n var result = value + '';\n return result == '0' && 1 / value == -INFINITY ? '-0' : result;\n }\n\n /**\n * The base implementation of `_.uniqBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseUniq(array, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n length = array.length,\n isCommon = true,\n result = [],\n seen = result;\n if (comparator) {\n isCommon = false;\n includes = arrayIncludesWith;\n } else if (length >= LARGE_ARRAY_SIZE) {\n var set = iteratee ? null : createSet(array);\n if (set) {\n return setToArray(set);\n }\n isCommon = false;\n includes = cacheHas;\n seen = new SetCache();\n } else {\n seen = iteratee ? [] : result;\n }\n outer: while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n value = comparator || value !== 0 ? value : 0;\n if (isCommon && computed === computed) {\n var seenIndex = seen.length;\n while (seenIndex--) {\n if (seen[seenIndex] === computed) {\n continue outer;\n }\n }\n if (iteratee) {\n seen.push(computed);\n }\n result.push(value);\n } else if (!includes(seen, computed, comparator)) {\n if (seen !== result) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.unset`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The property path to unset.\n * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n */\n function baseUnset(object, path) {\n path = castPath(path, object);\n object = parent(object, path);\n return object == null || delete object[toKey(last(path))];\n }\n\n /**\n * The base implementation of `_.update`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to update.\n * @param {Function} updater The function to produce the updated value.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseUpdate(object, path, updater, customizer) {\n return baseSet(object, path, updater(baseGet(object, path)), customizer);\n }\n\n /**\n * The base implementation of methods like `_.dropWhile` and `_.takeWhile`\n * without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {Function} predicate The function invoked per iteration.\n * @param {boolean} [isDrop] Specify dropping elements instead of taking them.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseWhile(array, predicate, isDrop, fromRight) {\n var length = array.length,\n index = fromRight ? length : -1;\n while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {}\n return isDrop ? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length) : baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index);\n }\n\n /**\n * The base implementation of `wrapperValue` which returns the result of\n * performing a sequence of actions on the unwrapped `value`, where each\n * successive action is supplied the return value of the previous.\n *\n * @private\n * @param {*} value The unwrapped value.\n * @param {Array} actions Actions to perform to resolve the unwrapped value.\n * @returns {*} Returns the resolved value.\n */\n function baseWrapperValue(value, actions) {\n var result = value;\n if (result instanceof LazyWrapper) {\n result = result.value();\n }\n return arrayReduce(actions, function (result, action) {\n return action.func.apply(action.thisArg, arrayPush([result], action.args));\n }, result);\n }\n\n /**\n * The base implementation of methods like `_.xor`, without support for\n * iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of values.\n */\n function baseXor(arrays, iteratee, comparator) {\n var length = arrays.length;\n if (length < 2) {\n return length ? baseUniq(arrays[0]) : [];\n }\n var index = -1,\n result = Array(length);\n while (++index < length) {\n var array = arrays[index],\n othIndex = -1;\n while (++othIndex < length) {\n if (othIndex != index) {\n result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);\n }\n }\n }\n return baseUniq(baseFlatten(result, 1), iteratee, comparator);\n }\n\n /**\n * This base implementation of `_.zipObject` which assigns values using `assignFunc`.\n *\n * @private\n * @param {Array} props The property identifiers.\n * @param {Array} values The property values.\n * @param {Function} assignFunc The function to assign values.\n * @returns {Object} Returns the new object.\n */\n function baseZipObject(props, values, assignFunc) {\n var index = -1,\n length = props.length,\n valsLength = values.length,\n result = {};\n while (++index < length) {\n var value = index < valsLength ? values[index] : undefined;\n assignFunc(result, props[index], value);\n }\n return result;\n }\n\n /**\n * Casts `value` to an empty array if it's not an array like object.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Array|Object} Returns the cast array-like object.\n */\n function castArrayLikeObject(value) {\n return isArrayLikeObject(value) ? value : [];\n }\n\n /**\n * Casts `value` to `identity` if it's not a function.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Function} Returns cast function.\n */\n function castFunction(value) {\n return typeof value == 'function' ? value : identity;\n }\n\n /**\n * Casts `value` to a path array if it's not one.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {Object} [object] The object to query keys on.\n * @returns {Array} Returns the cast property path array.\n */\n function castPath(value, object) {\n if (isArray(value)) {\n return value;\n }\n return isKey(value, object) ? [value] : stringToPath(toString(value));\n }\n\n /**\n * A `baseRest` alias which can be replaced with `identity` by module\n * replacement plugins.\n *\n * @private\n * @type {Function}\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n var castRest = baseRest;\n\n /**\n * Casts `array` to a slice if it's needed.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {number} start The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the cast slice.\n */\n function castSlice(array, start, end) {\n var length = array.length;\n end = end === undefined ? length : end;\n return !start && end >= length ? array : baseSlice(array, start, end);\n }\n\n /**\n * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).\n *\n * @private\n * @param {number|Object} id The timer id or timeout object of the timer to clear.\n */\n var clearTimeout = ctxClearTimeout || function (id) {\n return root.clearTimeout(id);\n };\n\n /**\n * Creates a clone of `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\n function cloneBuffer(buffer, isDeep) {\n if (isDeep) {\n return buffer.slice();\n }\n var length = buffer.length,\n result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);\n buffer.copy(result);\n return result;\n }\n\n /**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\n function cloneArrayBuffer(arrayBuffer) {\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n return result;\n }\n\n /**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\n function cloneDataView(dataView, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n }\n\n /**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\n function cloneRegExp(regexp) {\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n result.lastIndex = regexp.lastIndex;\n return result;\n }\n\n /**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\n function cloneSymbol(symbol) {\n return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n }\n\n /**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\n function cloneTypedArray(typedArray, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n }\n\n /**\n * Compares values to sort them in ascending order.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {number} Returns the sort order indicator for `value`.\n */\n function compareAscending(value, other) {\n if (value !== other) {\n var valIsDefined = value !== undefined,\n valIsNull = value === null,\n valIsReflexive = value === value,\n valIsSymbol = isSymbol(value);\n var othIsDefined = other !== undefined,\n othIsNull = other === null,\n othIsReflexive = other === other,\n othIsSymbol = isSymbol(other);\n if (!othIsNull && !othIsSymbol && !valIsSymbol && value > other || valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol || valIsNull && othIsDefined && othIsReflexive || !valIsDefined && othIsReflexive || !valIsReflexive) {\n return 1;\n }\n if (!valIsNull && !valIsSymbol && !othIsSymbol && value < other || othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol || othIsNull && valIsDefined && valIsReflexive || !othIsDefined && valIsReflexive || !othIsReflexive) {\n return -1;\n }\n }\n return 0;\n }\n\n /**\n * Used by `_.orderBy` to compare multiple properties of a value to another\n * and stable sort them.\n *\n * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,\n * specify an order of \"desc\" for descending or \"asc\" for ascending sort order\n * of corresponding values.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {boolean[]|string[]} orders The order to sort by for each property.\n * @returns {number} Returns the sort order indicator for `object`.\n */\n function compareMultiple(object, other, orders) {\n var index = -1,\n objCriteria = object.criteria,\n othCriteria = other.criteria,\n length = objCriteria.length,\n ordersLength = orders.length;\n while (++index < length) {\n var result = compareAscending(objCriteria[index], othCriteria[index]);\n if (result) {\n if (index >= ordersLength) {\n return result;\n }\n var order = orders[index];\n return result * (order == 'desc' ? -1 : 1);\n }\n }\n // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications\n // that causes it, under certain circumstances, to provide the same value for\n // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247\n // for more details.\n //\n // This also ensures a stable sort in V8 and other engines.\n // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.\n return object.index - other.index;\n }\n\n /**\n * Creates an array that is the composition of partially applied arguments,\n * placeholders, and provided arguments into a single array of arguments.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to prepend to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgs(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersLength = holders.length,\n leftIndex = -1,\n leftLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(leftLength + rangeLength),\n isUncurried = !isCurried;\n while (++leftIndex < leftLength) {\n result[leftIndex] = partials[leftIndex];\n }\n while (++argsIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[holders[argsIndex]] = args[argsIndex];\n }\n }\n while (rangeLength--) {\n result[leftIndex++] = args[argsIndex++];\n }\n return result;\n }\n\n /**\n * This function is like `composeArgs` except that the arguments composition\n * is tailored for `_.partialRight`.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to append to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgsRight(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersIndex = -1,\n holdersLength = holders.length,\n rightIndex = -1,\n rightLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(rangeLength + rightLength),\n isUncurried = !isCurried;\n while (++argsIndex < rangeLength) {\n result[argsIndex] = args[argsIndex];\n }\n var offset = argsIndex;\n while (++rightIndex < rightLength) {\n result[offset + rightIndex] = partials[rightIndex];\n }\n while (++holdersIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[offset + holders[holdersIndex]] = args[argsIndex++];\n }\n }\n return result;\n }\n\n /**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\n function copyArray(source, array) {\n var index = -1,\n length = source.length;\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n }\n\n /**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\n function copyObject(source, props, object, customizer) {\n var isNew = !object;\n object || (object = {});\n var index = -1,\n length = props.length;\n while (++index < length) {\n var key = props[index];\n var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined;\n if (newValue === undefined) {\n newValue = source[key];\n }\n if (isNew) {\n baseAssignValue(object, key, newValue);\n } else {\n assignValue(object, key, newValue);\n }\n }\n return object;\n }\n\n /**\n * Copies own symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbols(source, object) {\n return copyObject(source, getSymbols(source), object);\n }\n\n /**\n * Copies own and inherited symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbolsIn(source, object) {\n return copyObject(source, getSymbolsIn(source), object);\n }\n\n /**\n * Creates a function like `_.groupBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} [initializer] The accumulator object initializer.\n * @returns {Function} Returns the new aggregator function.\n */\n function createAggregator(setter, initializer) {\n return function (collection, iteratee) {\n var func = isArray(collection) ? arrayAggregator : baseAggregator,\n accumulator = initializer ? initializer() : {};\n return func(collection, setter, getIteratee(iteratee, 2), accumulator);\n };\n }\n\n /**\n * Creates a function like `_.assign`.\n *\n * @private\n * @param {Function} assigner The function to assign values.\n * @returns {Function} Returns the new assigner function.\n */\n function createAssigner(assigner) {\n return baseRest(function (object, sources) {\n var index = -1,\n length = sources.length,\n customizer = length > 1 ? sources[length - 1] : undefined,\n guard = length > 2 ? sources[2] : undefined;\n customizer = assigner.length > 3 && typeof customizer == 'function' ? (length--, customizer) : undefined;\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n customizer = length < 3 ? undefined : customizer;\n length = 1;\n }\n object = Object(object);\n while (++index < length) {\n var source = sources[index];\n if (source) {\n assigner(object, source, index, customizer);\n }\n }\n return object;\n });\n }\n\n /**\n * Creates a `baseEach` or `baseEachRight` function.\n *\n * @private\n * @param {Function} eachFunc The function to iterate over a collection.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseEach(eachFunc, fromRight) {\n return function (collection, iteratee) {\n if (collection == null) {\n return collection;\n }\n if (!isArrayLike(collection)) {\n return eachFunc(collection, iteratee);\n }\n var length = collection.length,\n index = fromRight ? length : -1,\n iterable = Object(collection);\n while (fromRight ? index-- : ++index < length) {\n if (iteratee(iterable[index], index, iterable) === false) {\n break;\n }\n }\n return collection;\n };\n }\n\n /**\n * Creates a base function for methods like `_.forIn` and `_.forOwn`.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseFor(fromRight) {\n return function (object, iteratee, keysFunc) {\n var index = -1,\n iterable = Object(object),\n props = keysFunc(object),\n length = props.length;\n while (length--) {\n var key = props[fromRight ? length : ++index];\n if (iteratee(iterable[key], key, iterable) === false) {\n break;\n }\n }\n return object;\n };\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the optional `this`\n * binding of `thisArg`.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createBind(func, bitmask, thisArg) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n function wrapper() {\n var fn = this && this !== root && this instanceof wrapper ? Ctor : func;\n return fn.apply(isBind ? thisArg : this, arguments);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.lowerFirst`.\n *\n * @private\n * @param {string} methodName The name of the `String` case method to use.\n * @returns {Function} Returns the new case function.\n */\n function createCaseFirst(methodName) {\n return function (string) {\n string = toString(string);\n var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined;\n var chr = strSymbols ? strSymbols[0] : string.charAt(0);\n var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1);\n return chr[methodName]() + trailing;\n };\n }\n\n /**\n * Creates a function like `_.camelCase`.\n *\n * @private\n * @param {Function} callback The function to combine each word.\n * @returns {Function} Returns the new compounder function.\n */\n function createCompounder(callback) {\n return function (string) {\n return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');\n };\n }\n\n /**\n * Creates a function that produces an instance of `Ctor` regardless of\n * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n *\n * @private\n * @param {Function} Ctor The constructor to wrap.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCtor(Ctor) {\n return function () {\n // Use a `switch` statement to work with class constructors. See\n // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n // for more details.\n var args = arguments;\n switch (args.length) {\n case 0:\n return new Ctor();\n case 1:\n return new Ctor(args[0]);\n case 2:\n return new Ctor(args[0], args[1]);\n case 3:\n return new Ctor(args[0], args[1], args[2]);\n case 4:\n return new Ctor(args[0], args[1], args[2], args[3]);\n case 5:\n return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n case 6:\n return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n case 7:\n return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n }\n var thisBinding = baseCreate(Ctor.prototype),\n result = Ctor.apply(thisBinding, args);\n\n // Mimic the constructor's `return` behavior.\n // See https://es5.github.io/#x13.2.2 for more details.\n return isObject(result) ? result : thisBinding;\n };\n }\n\n /**\n * Creates a function that wraps `func` to enable currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {number} arity The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCurry(func, bitmask, arity) {\n var Ctor = createCtor(func);\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length,\n placeholder = getHolder(wrapper);\n while (index--) {\n args[index] = arguments[index];\n }\n var holders = length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder ? [] : replaceHolders(args, placeholder);\n length -= holders.length;\n if (length < arity) {\n return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, undefined, args, holders, undefined, undefined, arity - length);\n }\n var fn = this && this !== root && this instanceof wrapper ? Ctor : func;\n return apply(fn, this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.find` or `_.findLast` function.\n *\n * @private\n * @param {Function} findIndexFunc The function to find the collection index.\n * @returns {Function} Returns the new find function.\n */\n function createFind(findIndexFunc) {\n return function (collection, predicate, fromIndex) {\n var iterable = Object(collection);\n if (!isArrayLike(collection)) {\n var iteratee = getIteratee(predicate, 3);\n collection = keys(collection);\n predicate = function (key) {\n return iteratee(iterable[key], key, iterable);\n };\n }\n var index = findIndexFunc(collection, predicate, fromIndex);\n return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;\n };\n }\n\n /**\n * Creates a `_.flow` or `_.flowRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new flow function.\n */\n function createFlow(fromRight) {\n return flatRest(function (funcs) {\n var length = funcs.length,\n index = length,\n prereq = LodashWrapper.prototype.thru;\n if (fromRight) {\n funcs.reverse();\n }\n while (index--) {\n var func = funcs[index];\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (prereq && !wrapper && getFuncName(func) == 'wrapper') {\n var wrapper = new LodashWrapper([], true);\n }\n }\n index = wrapper ? index : length;\n while (++index < length) {\n func = funcs[index];\n var funcName = getFuncName(func),\n data = funcName == 'wrapper' ? getData(func) : undefined;\n if (data && isLaziable(data[0]) && data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && !data[4].length && data[9] == 1) {\n wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);\n } else {\n wrapper = func.length == 1 && isLaziable(func) ? wrapper[funcName]() : wrapper.thru(func);\n }\n }\n return function () {\n var args = arguments,\n value = args[0];\n if (wrapper && args.length == 1 && isArray(value)) {\n return wrapper.plant(value).value();\n }\n var index = 0,\n result = length ? funcs[index].apply(this, args) : value;\n while (++index < length) {\n result = funcs[index].call(this, result);\n }\n return result;\n };\n });\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with optional `this`\n * binding of `thisArg`, partial application, and currying.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [partialsRight] The arguments to append to those provided\n * to the new function.\n * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n var isAry = bitmask & WRAP_ARY_FLAG,\n isBind = bitmask & WRAP_BIND_FLAG,\n isBindKey = bitmask & WRAP_BIND_KEY_FLAG,\n isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),\n isFlip = bitmask & WRAP_FLIP_FLAG,\n Ctor = isBindKey ? undefined : createCtor(func);\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length;\n while (index--) {\n args[index] = arguments[index];\n }\n if (isCurried) {\n var placeholder = getHolder(wrapper),\n holdersCount = countHolders(args, placeholder);\n }\n if (partials) {\n args = composeArgs(args, partials, holders, isCurried);\n }\n if (partialsRight) {\n args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n }\n length -= holdersCount;\n if (isCurried && length < arity) {\n var newHolders = replaceHolders(args, placeholder);\n return createRecurry(func, bitmask, createHybrid, wrapper.placeholder, thisArg, args, newHolders, argPos, ary, arity - length);\n }\n var thisBinding = isBind ? thisArg : this,\n fn = isBindKey ? thisBinding[func] : func;\n length = args.length;\n if (argPos) {\n args = reorder(args, argPos);\n } else if (isFlip && length > 1) {\n args.reverse();\n }\n if (isAry && ary < length) {\n args.length = ary;\n }\n if (this && this !== root && this instanceof wrapper) {\n fn = Ctor || createCtor(fn);\n }\n return fn.apply(thisBinding, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.invertBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} toIteratee The function to resolve iteratees.\n * @returns {Function} Returns the new inverter function.\n */\n function createInverter(setter, toIteratee) {\n return function (object, iteratee) {\n return baseInverter(object, setter, toIteratee(iteratee), {});\n };\n }\n\n /**\n * Creates a function that performs a mathematical operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @param {number} [defaultValue] The value used for `undefined` arguments.\n * @returns {Function} Returns the new mathematical operation function.\n */\n function createMathOperation(operator, defaultValue) {\n return function (value, other) {\n var result;\n if (value === undefined && other === undefined) {\n return defaultValue;\n }\n if (value !== undefined) {\n result = value;\n }\n if (other !== undefined) {\n if (result === undefined) {\n return other;\n }\n if (typeof value == 'string' || typeof other == 'string') {\n value = baseToString(value);\n other = baseToString(other);\n } else {\n value = baseToNumber(value);\n other = baseToNumber(other);\n }\n result = operator(value, other);\n }\n return result;\n };\n }\n\n /**\n * Creates a function like `_.over`.\n *\n * @private\n * @param {Function} arrayFunc The function to iterate over iteratees.\n * @returns {Function} Returns the new over function.\n */\n function createOver(arrayFunc) {\n return flatRest(function (iteratees) {\n iteratees = arrayMap(iteratees, baseUnary(getIteratee()));\n return baseRest(function (args) {\n var thisArg = this;\n return arrayFunc(iteratees, function (iteratee) {\n return apply(iteratee, thisArg, args);\n });\n });\n });\n }\n\n /**\n * Creates the padding for `string` based on `length`. The `chars` string\n * is truncated if the number of characters exceeds `length`.\n *\n * @private\n * @param {number} length The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padding for `string`.\n */\n function createPadding(length, chars) {\n chars = chars === undefined ? ' ' : baseToString(chars);\n var charsLength = chars.length;\n if (charsLength < 2) {\n return charsLength ? baseRepeat(chars, length) : chars;\n }\n var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));\n return hasUnicode(chars) ? castSlice(stringToArray(result), 0, length).join('') : result.slice(0, length);\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the `this` binding\n * of `thisArg` and `partials` prepended to the arguments it receives.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} partials The arguments to prepend to those provided to\n * the new function.\n * @returns {Function} Returns the new wrapped function.\n */\n function createPartial(func, bitmask, thisArg, partials) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n function wrapper() {\n var argsIndex = -1,\n argsLength = arguments.length,\n leftIndex = -1,\n leftLength = partials.length,\n args = Array(leftLength + argsLength),\n fn = this && this !== root && this instanceof wrapper ? Ctor : func;\n while (++leftIndex < leftLength) {\n args[leftIndex] = partials[leftIndex];\n }\n while (argsLength--) {\n args[leftIndex++] = arguments[++argsIndex];\n }\n return apply(fn, isBind ? thisArg : this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.range` or `_.rangeRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new range function.\n */\n function createRange(fromRight) {\n return function (start, end, step) {\n if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {\n end = step = undefined;\n }\n // Ensure the sign of `-0` is preserved.\n start = toFinite(start);\n if (end === undefined) {\n end = start;\n start = 0;\n } else {\n end = toFinite(end);\n }\n step = step === undefined ? start < end ? 1 : -1 : toFinite(step);\n return baseRange(start, end, step, fromRight);\n };\n }\n\n /**\n * Creates a function that performs a relational operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @returns {Function} Returns the new relational operation function.\n */\n function createRelationalOperation(operator) {\n return function (value, other) {\n if (!(typeof value == 'string' && typeof other == 'string')) {\n value = toNumber(value);\n other = toNumber(other);\n }\n return operator(value, other);\n };\n }\n\n /**\n * Creates a function that wraps `func` to continue currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {Function} wrapFunc The function to create the `func` wrapper.\n * @param {*} placeholder The placeholder value.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n var isCurry = bitmask & WRAP_CURRY_FLAG,\n newHolders = isCurry ? holders : undefined,\n newHoldersRight = isCurry ? undefined : holders,\n newPartials = isCurry ? partials : undefined,\n newPartialsRight = isCurry ? undefined : partials;\n bitmask |= isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG;\n bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);\n if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {\n bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);\n }\n var newData = [func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, newHoldersRight, argPos, ary, arity];\n var result = wrapFunc.apply(undefined, newData);\n if (isLaziable(func)) {\n setData(result, newData);\n }\n result.placeholder = placeholder;\n return setWrapToString(result, func, bitmask);\n }\n\n /**\n * Creates a function like `_.round`.\n *\n * @private\n * @param {string} methodName The name of the `Math` method to use when rounding.\n * @returns {Function} Returns the new round function.\n */\n function createRound(methodName) {\n var func = Math[methodName];\n return function (number, precision) {\n number = toNumber(number);\n precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);\n if (precision && nativeIsFinite(number)) {\n // Shift with exponential notation to avoid floating-point issues.\n // See [MDN](https://mdn.io/round#Examples) for more details.\n var pair = (toString(number) + 'e').split('e'),\n value = func(pair[0] + 'e' + (+pair[1] + precision));\n pair = (toString(value) + 'e').split('e');\n return +(pair[0] + 'e' + (+pair[1] - precision));\n }\n return func(number);\n };\n }\n\n /**\n * Creates a set object of `values`.\n *\n * @private\n * @param {Array} values The values to add to the set.\n * @returns {Object} Returns the new set.\n */\n var createSet = !(Set && 1 / setToArray(new Set([, -0]))[1] == INFINITY) ? noop : function (values) {\n return new Set(values);\n };\n\n /**\n * Creates a `_.toPairs` or `_.toPairsIn` function.\n *\n * @private\n * @param {Function} keysFunc The function to get the keys of a given object.\n * @returns {Function} Returns the new pairs function.\n */\n function createToPairs(keysFunc) {\n return function (object) {\n var tag = getTag(object);\n if (tag == mapTag) {\n return mapToArray(object);\n }\n if (tag == setTag) {\n return setToPairs(object);\n }\n return baseToPairs(object, keysFunc(object));\n };\n }\n\n /**\n * Creates a function that either curries or invokes `func` with optional\n * `this` binding and partially applied arguments.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags.\n * 1 - `_.bind`\n * 2 - `_.bindKey`\n * 4 - `_.curry` or `_.curryRight` of a bound function\n * 8 - `_.curry`\n * 16 - `_.curryRight`\n * 32 - `_.partial`\n * 64 - `_.partialRight`\n * 128 - `_.rearg`\n * 256 - `_.ary`\n * 512 - `_.flip`\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to be partially applied.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;\n if (!isBindKey && typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var length = partials ? partials.length : 0;\n if (!length) {\n bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);\n partials = holders = undefined;\n }\n ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n arity = arity === undefined ? arity : toInteger(arity);\n length -= holders ? holders.length : 0;\n if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {\n var partialsRight = partials,\n holdersRight = holders;\n partials = holders = undefined;\n }\n var data = isBindKey ? undefined : getData(func);\n var newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];\n if (data) {\n mergeData(newData, data);\n }\n func = newData[0];\n bitmask = newData[1];\n thisArg = newData[2];\n partials = newData[3];\n holders = newData[4];\n arity = newData[9] = newData[9] === undefined ? isBindKey ? 0 : func.length : nativeMax(newData[9] - length, 0);\n if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {\n bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);\n }\n if (!bitmask || bitmask == WRAP_BIND_FLAG) {\n var result = createBind(func, bitmask, thisArg);\n } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {\n result = createCurry(func, bitmask, arity);\n } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {\n result = createPartial(func, bitmask, thisArg, partials);\n } else {\n result = createHybrid.apply(undefined, newData);\n }\n var setter = data ? baseSetData : setData;\n return setWrapToString(setter(result, newData), func, bitmask);\n }\n\n /**\n * Used by `_.defaults` to customize its `_.assignIn` use to assign properties\n * of source objects to the destination object for all destination properties\n * that resolve to `undefined`.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to assign.\n * @param {Object} object The parent object of `objValue`.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsAssignIn(objValue, srcValue, key, object) {\n if (objValue === undefined || eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key)) {\n return srcValue;\n }\n return objValue;\n }\n\n /**\n * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source\n * objects into destination objects that are passed thru.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to merge.\n * @param {Object} object The parent object of `objValue`.\n * @param {Object} source The parent object of `srcValue`.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {\n if (isObject(objValue) && isObject(srcValue)) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, objValue);\n baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);\n stack['delete'](srcValue);\n }\n return objValue;\n }\n\n /**\n * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain\n * objects.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {string} key The key of the property to inspect.\n * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.\n */\n function customOmitClone(value) {\n return isPlainObject(value) ? undefined : value;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\n function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n arrLength = array.length,\n othLength = other.length;\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Check that cyclic values are equal.\n var arrStacked = stack.get(array);\n var othStacked = stack.get(other);\n if (arrStacked && othStacked) {\n return arrStacked == other && othStacked == array;\n }\n var index = -1,\n result = true,\n seen = bitmask & COMPARE_UNORDERED_FLAG ? new SetCache() : undefined;\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n if (customizer) {\n var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function (othValue, othIndex) {\n if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n return seen.push(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {\n switch (tag) {\n case dataViewTag:\n if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {\n return false;\n }\n object = object.buffer;\n other = other.buffer;\n case arrayBufferTag:\n if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n return false;\n }\n return true;\n case boolTag:\n case dateTag:\n case numberTag:\n // Coerce booleans to `1` or `0` and dates to milliseconds.\n // Invalid dates are coerced to `NaN`.\n return eq(+object, +other);\n case errorTag:\n return object.name == other.name && object.message == other.message;\n case regexpTag:\n case stringTag:\n // Coerce regexes to strings and treat strings, primitives and objects,\n // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n // for more details.\n return object == other + '';\n case mapTag:\n var convert = mapToArray;\n case setTag:\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG;\n convert || (convert = setToArray);\n if (object.size != other.size && !isPartial) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked) {\n return stacked == other;\n }\n bitmask |= COMPARE_UNORDERED_FLAG;\n\n // Recursively compare objects (susceptible to call stack limits).\n stack.set(object, other);\n var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);\n stack['delete'](object);\n return result;\n case symbolTag:\n if (symbolValueOf) {\n return symbolValueOf.call(object) == symbolValueOf.call(other);\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n objProps = getAllKeys(object),\n objLength = objProps.length,\n othProps = getAllKeys(other),\n othLength = othProps.length;\n if (objLength != othLength && !isPartial) {\n return false;\n }\n var index = objLength;\n while (index--) {\n var key = objProps[index];\n if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n return false;\n }\n }\n // Check that cyclic values are equal.\n var objStacked = stack.get(object);\n var othStacked = stack.get(other);\n if (objStacked && othStacked) {\n return objStacked == other && othStacked == object;\n }\n var result = true;\n stack.set(object, other);\n stack.set(other, object);\n var skipCtor = isPartial;\n while (++index < objLength) {\n key = objProps[index];\n var objValue = object[key],\n othValue = other[key];\n if (customizer) {\n var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack);\n }\n // Recursively compare objects (susceptible to call stack limits).\n if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) {\n result = false;\n break;\n }\n skipCtor || (skipCtor = key == 'constructor');\n }\n if (result && !skipCtor) {\n var objCtor = object.constructor,\n othCtor = other.constructor;\n\n // Non `Object` object instances with different constructors are not equal.\n if (objCtor != othCtor && 'constructor' in object && 'constructor' in other && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n result = false;\n }\n }\n stack['delete'](object);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseRest` which flattens the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n function flatRest(func) {\n return setToString(overRest(func, undefined, flatten), func + '');\n }\n\n /**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n }\n\n /**\n * Creates an array of own and inherited enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeysIn(object) {\n return baseGetAllKeys(object, keysIn, getSymbolsIn);\n }\n\n /**\n * Gets metadata for `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {*} Returns the metadata for `func`.\n */\n var getData = !metaMap ? noop : function (func) {\n return metaMap.get(func);\n };\n\n /**\n * Gets the name of `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {string} Returns the function name.\n */\n function getFuncName(func) {\n var result = func.name + '',\n array = realNames[result],\n length = hasOwnProperty.call(realNames, result) ? array.length : 0;\n while (length--) {\n var data = array[length],\n otherFunc = data.func;\n if (otherFunc == null || otherFunc == func) {\n return data.name;\n }\n }\n return result;\n }\n\n /**\n * Gets the argument placeholder value for `func`.\n *\n * @private\n * @param {Function} func The function to inspect.\n * @returns {*} Returns the placeholder value.\n */\n function getHolder(func) {\n var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;\n return object.placeholder;\n }\n\n /**\n * Gets the appropriate \"iteratee\" function. If `_.iteratee` is customized,\n * this function returns the custom method, otherwise it returns `baseIteratee`.\n * If arguments are provided, the chosen function is invoked with them and\n * its result is returned.\n *\n * @private\n * @param {*} [value] The value to convert to an iteratee.\n * @param {number} [arity] The arity of the created iteratee.\n * @returns {Function} Returns the chosen function or its result.\n */\n function getIteratee() {\n var result = lodash.iteratee || iteratee;\n result = result === iteratee ? baseIteratee : result;\n return arguments.length ? result(arguments[0], arguments[1]) : result;\n }\n\n /**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\n function getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map;\n }\n\n /**\n * Gets the property names, values, and compare flags of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the match data of `object`.\n */\n function getMatchData(object) {\n var result = keys(object),\n length = result.length;\n while (length--) {\n var key = result[length],\n value = object[key];\n result[length] = [key, value, isStrictComparable(value)];\n }\n return result;\n }\n\n /**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\n function getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n }\n\n /**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\n function getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n }\n\n /**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbols = !nativeGetSymbols ? stubArray : function (object) {\n if (object == null) {\n return [];\n }\n object = Object(object);\n return arrayFilter(nativeGetSymbols(object), function (symbol) {\n return propertyIsEnumerable.call(object, symbol);\n });\n };\n\n /**\n * Creates an array of the own and inherited enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbolsIn = !nativeGetSymbols ? stubArray : function (object) {\n var result = [];\n while (object) {\n arrayPush(result, getSymbols(object));\n object = getPrototype(object);\n }\n return result;\n };\n\n /**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n var getTag = baseGetTag;\n\n // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\n if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map && getTag(new Map()) != mapTag || Promise && getTag(Promise.resolve()) != promiseTag || Set && getTag(new Set()) != setTag || WeakMap && getTag(new WeakMap()) != weakMapTag) {\n getTag = function (value) {\n var result = baseGetTag(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : '';\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString:\n return dataViewTag;\n case mapCtorString:\n return mapTag;\n case promiseCtorString:\n return promiseTag;\n case setCtorString:\n return setTag;\n case weakMapCtorString:\n return weakMapTag;\n }\n }\n return result;\n };\n }\n\n /**\n * Gets the view, applying any `transforms` to the `start` and `end` positions.\n *\n * @private\n * @param {number} start The start of the view.\n * @param {number} end The end of the view.\n * @param {Array} transforms The transformations to apply to the view.\n * @returns {Object} Returns an object containing the `start` and `end`\n * positions of the view.\n */\n function getView(start, end, transforms) {\n var index = -1,\n length = transforms.length;\n while (++index < length) {\n var data = transforms[index],\n size = data.size;\n switch (data.type) {\n case 'drop':\n start += size;\n break;\n case 'dropRight':\n end -= size;\n break;\n case 'take':\n end = nativeMin(end, start + size);\n break;\n case 'takeRight':\n start = nativeMax(start, end - size);\n break;\n }\n }\n return {\n 'start': start,\n 'end': end\n };\n }\n\n /**\n * Extracts wrapper details from the `source` body comment.\n *\n * @private\n * @param {string} source The source to inspect.\n * @returns {Array} Returns the wrapper details.\n */\n function getWrapDetails(source) {\n var match = source.match(reWrapDetails);\n return match ? match[1].split(reSplitDetails) : [];\n }\n\n /**\n * Checks if `path` exists on `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @param {Function} hasFunc The function to check properties.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n */\n function hasPath(object, path, hasFunc) {\n path = castPath(path, object);\n var index = -1,\n length = path.length,\n result = false;\n while (++index < length) {\n var key = toKey(path[index]);\n if (!(result = object != null && hasFunc(object, key))) {\n break;\n }\n object = object[key];\n }\n if (result || ++index != length) {\n return result;\n }\n length = object == null ? 0 : object.length;\n return !!length && isLength(length) && isIndex(key, length) && (isArray(object) || isArguments(object));\n }\n\n /**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\n function initCloneArray(array) {\n var length = array.length,\n result = new array.constructor(length);\n\n // Add properties assigned by `RegExp#exec`.\n if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n result.index = array.index;\n result.input = array.input;\n }\n return result;\n }\n\n /**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneObject(object) {\n return typeof object.constructor == 'function' && !isPrototype(object) ? baseCreate(getPrototype(object)) : {};\n }\n\n /**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneByTag(object, tag, isDeep) {\n var Ctor = object.constructor;\n switch (tag) {\n case arrayBufferTag:\n return cloneArrayBuffer(object);\n case boolTag:\n case dateTag:\n return new Ctor(+object);\n case dataViewTag:\n return cloneDataView(object, isDeep);\n case float32Tag:\n case float64Tag:\n case int8Tag:\n case int16Tag:\n case int32Tag:\n case uint8Tag:\n case uint8ClampedTag:\n case uint16Tag:\n case uint32Tag:\n return cloneTypedArray(object, isDeep);\n case mapTag:\n return new Ctor();\n case numberTag:\n case stringTag:\n return new Ctor(object);\n case regexpTag:\n return cloneRegExp(object);\n case setTag:\n return new Ctor();\n case symbolTag:\n return cloneSymbol(object);\n }\n }\n\n /**\n * Inserts wrapper `details` in a comment at the top of the `source` body.\n *\n * @private\n * @param {string} source The source to modify.\n * @returns {Array} details The details to insert.\n * @returns {string} Returns the modified source.\n */\n function insertWrapDetails(source, details) {\n var length = details.length;\n if (!length) {\n return source;\n }\n var lastIndex = length - 1;\n details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];\n details = details.join(length > 2 ? ', ' : ' ');\n return source.replace(reWrapComment, '{\\n/* [wrapped with ' + details + '] */\\n');\n }\n\n /**\n * Checks if `value` is a flattenable `arguments` object or array.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n */\n function isFlattenable(value) {\n return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]);\n }\n\n /**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\n function isIndex(value, length) {\n var type = typeof value;\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length && (type == 'number' || type != 'symbol' && reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length;\n }\n\n /**\n * Checks if the given arguments are from an iteratee call.\n *\n * @private\n * @param {*} value The potential iteratee value argument.\n * @param {*} index The potential iteratee index or key argument.\n * @param {*} object The potential iteratee object argument.\n * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n * else `false`.\n */\n function isIterateeCall(value, index, object) {\n if (!isObject(object)) {\n return false;\n }\n var type = typeof index;\n if (type == 'number' ? isArrayLike(object) && isIndex(index, object.length) : type == 'string' && index in object) {\n return eq(object[index], value);\n }\n return false;\n }\n\n /**\n * Checks if `value` is a property name and not a property path.\n *\n * @private\n * @param {*} value The value to check.\n * @param {Object} [object] The object to query keys on.\n * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n */\n function isKey(value, object) {\n if (isArray(value)) {\n return false;\n }\n var type = typeof value;\n if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) {\n return true;\n }\n return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);\n }\n\n /**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\n function isKeyable(value) {\n var type = typeof value;\n return type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean' ? value !== '__proto__' : value === null;\n }\n\n /**\n * Checks if `func` has a lazy counterpart.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` has a lazy counterpart,\n * else `false`.\n */\n function isLaziable(func) {\n var funcName = getFuncName(func),\n other = lodash[funcName];\n if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {\n return false;\n }\n if (func === other) {\n return true;\n }\n var data = getData(other);\n return !!data && func === data[0];\n }\n\n /**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\n function isMasked(func) {\n return !!maskSrcKey && maskSrcKey in func;\n }\n\n /**\n * Checks if `func` is capable of being masked.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `func` is maskable, else `false`.\n */\n var isMaskable = coreJsData ? isFunction : stubFalse;\n\n /**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\n function isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = typeof Ctor == 'function' && Ctor.prototype || objectProto;\n return value === proto;\n }\n\n /**\n * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` if suitable for strict\n * equality comparisons, else `false`.\n */\n function isStrictComparable(value) {\n return value === value && !isObject(value);\n }\n\n /**\n * A specialized version of `matchesProperty` for source values suitable\n * for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function matchesStrictComparable(key, srcValue) {\n return function (object) {\n if (object == null) {\n return false;\n }\n return object[key] === srcValue && (srcValue !== undefined || key in Object(object));\n };\n }\n\n /**\n * A specialized version of `_.memoize` which clears the memoized function's\n * cache when it exceeds `MAX_MEMOIZE_SIZE`.\n *\n * @private\n * @param {Function} func The function to have its output memoized.\n * @returns {Function} Returns the new memoized function.\n */\n function memoizeCapped(func) {\n var result = memoize(func, function (key) {\n if (cache.size === MAX_MEMOIZE_SIZE) {\n cache.clear();\n }\n return key;\n });\n var cache = result.cache;\n return result;\n }\n\n /**\n * Merges the function metadata of `source` into `data`.\n *\n * Merging metadata reduces the number of wrappers used to invoke a function.\n * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`\n * may be applied regardless of execution order. Methods like `_.ary` and\n * `_.rearg` modify function arguments, making the order in which they are\n * executed important, preventing the merging of metadata. However, we make\n * an exception for a safe combined case where curried functions have `_.ary`\n * and or `_.rearg` applied.\n *\n * @private\n * @param {Array} data The destination metadata.\n * @param {Array} source The source metadata.\n * @returns {Array} Returns `data`.\n */\n function mergeData(data, source) {\n var bitmask = data[1],\n srcBitmask = source[1],\n newBitmask = bitmask | srcBitmask,\n isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);\n var isCombo = srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_CURRY_FLAG || srcBitmask == WRAP_ARY_FLAG && bitmask == WRAP_REARG_FLAG && data[7].length <= source[8] || srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG) && source[7].length <= source[8] && bitmask == WRAP_CURRY_FLAG;\n\n // Exit early if metadata can't be merged.\n if (!(isCommon || isCombo)) {\n return data;\n }\n // Use source `thisArg` if available.\n if (srcBitmask & WRAP_BIND_FLAG) {\n data[2] = source[2];\n // Set when currying a bound function.\n newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;\n }\n // Compose partial arguments.\n var value = source[3];\n if (value) {\n var partials = data[3];\n data[3] = partials ? composeArgs(partials, value, source[4]) : value;\n data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];\n }\n // Compose partial right arguments.\n value = source[5];\n if (value) {\n partials = data[5];\n data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;\n data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];\n }\n // Use source `argPos` if available.\n value = source[7];\n if (value) {\n data[7] = value;\n }\n // Use source `ary` if it's smaller.\n if (srcBitmask & WRAP_ARY_FLAG) {\n data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);\n }\n // Use source `arity` if one is not provided.\n if (data[9] == null) {\n data[9] = source[9];\n }\n // Use source `func` and merge bitmasks.\n data[0] = source[0];\n data[1] = newBitmask;\n return data;\n }\n\n /**\n * This function is like\n * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * except that it includes inherited enumerable properties.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function nativeKeysIn(object) {\n var result = [];\n if (object != null) {\n for (var key in Object(object)) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\n function objectToString(value) {\n return nativeObjectToString.call(value);\n }\n\n /**\n * A specialized version of `baseRest` which transforms the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @param {Function} transform The rest array transform.\n * @returns {Function} Returns the new function.\n */\n function overRest(func, start, transform) {\n start = nativeMax(start === undefined ? func.length - 1 : start, 0);\n return function () {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = transform(array);\n return apply(func, this, otherArgs);\n };\n }\n\n /**\n * Gets the parent value at `path` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} path The path to get the parent value of.\n * @returns {*} Returns the parent value.\n */\n function parent(object, path) {\n return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));\n }\n\n /**\n * Reorder `array` according to the specified indexes where the element at\n * the first index is assigned as the first element, the element at\n * the second index is assigned as the second element, and so on.\n *\n * @private\n * @param {Array} array The array to reorder.\n * @param {Array} indexes The arranged array indexes.\n * @returns {Array} Returns `array`.\n */\n function reorder(array, indexes) {\n var arrLength = array.length,\n length = nativeMin(indexes.length, arrLength),\n oldArray = copyArray(array);\n while (length--) {\n var index = indexes[length];\n array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;\n }\n return array;\n }\n\n /**\n * Gets the value at `key`, unless `key` is \"__proto__\" or \"constructor\".\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function safeGet(object, key) {\n if (key === 'constructor' && typeof object[key] === 'function') {\n return;\n }\n if (key == '__proto__') {\n return;\n }\n return object[key];\n }\n\n /**\n * Sets metadata for `func`.\n *\n * **Note:** If this function becomes hot, i.e. is invoked a lot in a short\n * period of time, it will trip its breaker and transition to an identity\n * function to avoid garbage collection pauses in V8. See\n * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)\n * for more details.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var setData = shortOut(baseSetData);\n\n /**\n * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n var setTimeout = ctxSetTimeout || function (func, wait) {\n return root.setTimeout(func, wait);\n };\n\n /**\n * Sets the `toString` method of `func` to return `string`.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var setToString = shortOut(baseSetToString);\n\n /**\n * Sets the `toString` method of `wrapper` to mimic the source of `reference`\n * with wrapper details in a comment at the top of the source body.\n *\n * @private\n * @param {Function} wrapper The function to modify.\n * @param {Function} reference The reference function.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Function} Returns `wrapper`.\n */\n function setWrapToString(wrapper, reference, bitmask) {\n var source = reference + '';\n return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));\n }\n\n /**\n * Creates a function that'll short out and invoke `identity` instead\n * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`\n * milliseconds.\n *\n * @private\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new shortable function.\n */\n function shortOut(func) {\n var count = 0,\n lastCalled = 0;\n return function () {\n var stamp = nativeNow(),\n remaining = HOT_SPAN - (stamp - lastCalled);\n lastCalled = stamp;\n if (remaining > 0) {\n if (++count >= HOT_COUNT) {\n return arguments[0];\n }\n } else {\n count = 0;\n }\n return func.apply(undefined, arguments);\n };\n }\n\n /**\n * A specialized version of `_.shuffle` which mutates and sets the size of `array`.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @param {number} [size=array.length] The size of `array`.\n * @returns {Array} Returns `array`.\n */\n function shuffleSelf(array, size) {\n var index = -1,\n length = array.length,\n lastIndex = length - 1;\n size = size === undefined ? length : size;\n while (++index < size) {\n var rand = baseRandom(index, lastIndex),\n value = array[rand];\n array[rand] = array[index];\n array[index] = value;\n }\n array.length = size;\n return array;\n }\n\n /**\n * Converts `string` to a property path array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the property path array.\n */\n var stringToPath = memoizeCapped(function (string) {\n var result = [];\n if (string.charCodeAt(0) === 46 /* . */) {\n result.push('');\n }\n string.replace(rePropName, function (match, number, quote, subString) {\n result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match);\n });\n return result;\n });\n\n /**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\n function toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = value + '';\n return result == '0' && 1 / value == -INFINITY ? '-0' : result;\n }\n\n /**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\n function toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return func + '';\n } catch (e) {}\n }\n return '';\n }\n\n /**\n * Updates wrapper `details` based on `bitmask` flags.\n *\n * @private\n * @returns {Array} details The details to modify.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Array} Returns `details`.\n */\n function updateWrapDetails(details, bitmask) {\n arrayEach(wrapFlags, function (pair) {\n var value = '_.' + pair[0];\n if (bitmask & pair[1] && !arrayIncludes(details, value)) {\n details.push(value);\n }\n });\n return details.sort();\n }\n\n /**\n * Creates a clone of `wrapper`.\n *\n * @private\n * @param {Object} wrapper The wrapper to clone.\n * @returns {Object} Returns the cloned wrapper.\n */\n function wrapperClone(wrapper) {\n if (wrapper instanceof LazyWrapper) {\n return wrapper.clone();\n }\n var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);\n result.__actions__ = copyArray(wrapper.__actions__);\n result.__index__ = wrapper.__index__;\n result.__values__ = wrapper.__values__;\n return result;\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of elements split into groups the length of `size`.\n * If `array` can't be split evenly, the final chunk will be the remaining\n * elements.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to process.\n * @param {number} [size=1] The length of each chunk\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the new array of chunks.\n * @example\n *\n * _.chunk(['a', 'b', 'c', 'd'], 2);\n * // => [['a', 'b'], ['c', 'd']]\n *\n * _.chunk(['a', 'b', 'c', 'd'], 3);\n * // => [['a', 'b', 'c'], ['d']]\n */\n function chunk(array, size, guard) {\n if (guard ? isIterateeCall(array, size, guard) : size === undefined) {\n size = 1;\n } else {\n size = nativeMax(toInteger(size), 0);\n }\n var length = array == null ? 0 : array.length;\n if (!length || size < 1) {\n return [];\n }\n var index = 0,\n resIndex = 0,\n result = Array(nativeCeil(length / size));\n while (index < length) {\n result[resIndex++] = baseSlice(array, index, index += size);\n }\n return result;\n }\n\n /**\n * Creates an array with all falsey values removed. The values `false`, `null`,\n * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to compact.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.compact([0, 1, false, 2, '', 3]);\n * // => [1, 2, 3]\n */\n function compact(array) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n while (++index < length) {\n var value = array[index];\n if (value) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * Creates a new array concatenating `array` with any additional arrays\n * and/or values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to concatenate.\n * @param {...*} [values] The values to concatenate.\n * @returns {Array} Returns the new concatenated array.\n * @example\n *\n * var array = [1];\n * var other = _.concat(array, 2, [3], [[4]]);\n *\n * console.log(other);\n * // => [1, 2, 3, [4]]\n *\n * console.log(array);\n * // => [1]\n */\n function concat() {\n var length = arguments.length;\n if (!length) {\n return [];\n }\n var args = Array(length - 1),\n array = arguments[0],\n index = length;\n while (index--) {\n args[index - 1] = arguments[index];\n }\n return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));\n }\n\n /**\n * Creates an array of `array` values not included in the other given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * **Note:** Unlike `_.pullAll`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.without, _.xor\n * @example\n *\n * _.difference([2, 1], [2, 3]);\n * // => [1]\n */\n var difference = baseRest(function (array, values) {\n return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `iteratee` which\n * is invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * **Note:** Unlike `_.pullAllBy`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var differenceBy = baseRest(function (array, values) {\n var iteratee = last(values);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `comparator`\n * which is invoked to compare elements of `array` to `values`. The order and\n * references of result values are determined by the first array. The comparator\n * is invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.pullAllWith`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n *\n * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }]\n */\n var differenceWith = baseRest(function (array, values) {\n var comparator = last(values);\n if (isArrayLikeObject(comparator)) {\n comparator = undefined;\n }\n return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : [];\n });\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.drop([1, 2, 3]);\n * // => [2, 3]\n *\n * _.drop([1, 2, 3], 2);\n * // => [3]\n *\n * _.drop([1, 2, 3], 5);\n * // => []\n *\n * _.drop([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function drop(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = guard || n === undefined ? 1 : toInteger(n);\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.dropRight([1, 2, 3]);\n * // => [1, 2]\n *\n * _.dropRight([1, 2, 3], 2);\n * // => [1]\n *\n * _.dropRight([1, 2, 3], 5);\n * // => []\n *\n * _.dropRight([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function dropRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = guard || n === undefined ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the end.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.dropRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropRightWhile(users, ['active', false]);\n * // => objects for ['barney']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropRightWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropRightWhile(array, predicate) {\n return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true, true) : [];\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the beginning.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.dropWhile(users, function(o) { return !o.active; });\n * // => objects for ['pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropWhile(users, ['active', false]);\n * // => objects for ['pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropWhile(array, predicate) {\n return array && array.length ? baseWhile(array, getIteratee(predicate, 3), true) : [];\n }\n\n /**\n * Fills elements of `array` with `value` from `start` up to, but not\n * including, `end`.\n *\n * **Note:** This method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Array\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.fill(array, 'a');\n * console.log(array);\n * // => ['a', 'a', 'a']\n *\n * _.fill(Array(3), 2);\n * // => [2, 2, 2]\n *\n * _.fill([4, 6, 8, 10], '*', 1, 3);\n * // => [4, '*', '*', 10]\n */\n function fill(array, value, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {\n start = 0;\n end = length;\n }\n return baseFill(array, value, start, end);\n }\n\n /**\n * This method is like `_.find` except that it returns the index of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.findIndex(users, function(o) { return o.user == 'barney'; });\n * // => 0\n *\n * // The `_.matches` iteratee shorthand.\n * _.findIndex(users, { 'user': 'fred', 'active': false });\n * // => 1\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findIndex(users, ['active', false]);\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.findIndex(users, 'active');\n * // => 2\n */\n function findIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index);\n }\n\n /**\n * This method is like `_.findIndex` except that it iterates over elements\n * of `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });\n * // => 2\n *\n * // The `_.matches` iteratee shorthand.\n * _.findLastIndex(users, { 'user': 'barney', 'active': true });\n * // => 0\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findLastIndex(users, ['active', false]);\n * // => 2\n *\n * // The `_.property` iteratee shorthand.\n * _.findLastIndex(users, 'active');\n * // => 0\n */\n function findLastIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length - 1;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index, true);\n }\n\n /**\n * Flattens `array` a single level deep.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flatten([1, [2, [3, [4]], 5]]);\n * // => [1, 2, [3, [4]], 5]\n */\n function flatten(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, 1) : [];\n }\n\n /**\n * Recursively flattens `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flattenDeep([1, [2, [3, [4]], 5]]);\n * // => [1, 2, 3, 4, 5]\n */\n function flattenDeep(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, INFINITY) : [];\n }\n\n /**\n * Recursively flatten `array` up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.4.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * var array = [1, [2, [3, [4]], 5]];\n *\n * _.flattenDepth(array, 1);\n * // => [1, 2, [3, [4]], 5]\n *\n * _.flattenDepth(array, 2);\n * // => [1, 2, 3, [4], 5]\n */\n function flattenDepth(array, depth) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(array, depth);\n }\n\n /**\n * The inverse of `_.toPairs`; this method returns an object composed\n * from key-value `pairs`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} pairs The key-value pairs.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.fromPairs([['a', 1], ['b', 2]]);\n * // => { 'a': 1, 'b': 2 }\n */\n function fromPairs(pairs) {\n var index = -1,\n length = pairs == null ? 0 : pairs.length,\n result = {};\n while (++index < length) {\n var pair = pairs[index];\n result[pair[0]] = pair[1];\n }\n return result;\n }\n\n /**\n * Gets the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias first\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the first element of `array`.\n * @example\n *\n * _.head([1, 2, 3]);\n * // => 1\n *\n * _.head([]);\n * // => undefined\n */\n function head(array) {\n return array && array.length ? array[0] : undefined;\n }\n\n /**\n * Gets the index at which the first occurrence of `value` is found in `array`\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. If `fromIndex` is negative, it's used as the\n * offset from the end of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.indexOf([1, 2, 1, 2], 2);\n * // => 1\n *\n * // Search from the `fromIndex`.\n * _.indexOf([1, 2, 1, 2], 2, 2);\n * // => 3\n */\n function indexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseIndexOf(array, value, index);\n }\n\n /**\n * Gets all but the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.initial([1, 2, 3]);\n * // => [1, 2]\n */\n function initial(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 0, -1) : [];\n }\n\n /**\n * Creates an array of unique values that are included in all given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersection([2, 1], [2, 3]);\n * // => [2]\n */\n var intersection = baseRest(function (arrays) {\n var mapped = arrayMap(arrays, castArrayLikeObject);\n return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped) : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `iteratee`\n * which is invoked for each element of each `arrays` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [2.1]\n *\n * // The `_.property` iteratee shorthand.\n * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }]\n */\n var intersectionBy = baseRest(function (arrays) {\n var iteratee = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n if (iteratee === last(mapped)) {\n iteratee = undefined;\n } else {\n mapped.pop();\n }\n return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, getIteratee(iteratee, 2)) : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `comparator`\n * which is invoked to compare elements of `arrays`. The order and references\n * of result values are determined by the first array. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.intersectionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }]\n */\n var intersectionWith = baseRest(function (arrays) {\n var comparator = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n if (comparator) {\n mapped.pop();\n }\n return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined, comparator) : [];\n });\n\n /**\n * Converts all elements in `array` into a string separated by `separator`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to convert.\n * @param {string} [separator=','] The element separator.\n * @returns {string} Returns the joined string.\n * @example\n *\n * _.join(['a', 'b', 'c'], '~');\n * // => 'a~b~c'\n */\n function join(array, separator) {\n return array == null ? '' : nativeJoin.call(array, separator);\n }\n\n /**\n * Gets the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the last element of `array`.\n * @example\n *\n * _.last([1, 2, 3]);\n * // => 3\n */\n function last(array) {\n var length = array == null ? 0 : array.length;\n return length ? array[length - 1] : undefined;\n }\n\n /**\n * This method is like `_.indexOf` except that it iterates over elements of\n * `array` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.lastIndexOf([1, 2, 1, 2], 2);\n * // => 3\n *\n * // Search from the `fromIndex`.\n * _.lastIndexOf([1, 2, 1, 2], 2, 2);\n * // => 1\n */\n function lastIndexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);\n }\n return value === value ? strictLastIndexOf(array, value, index) : baseFindIndex(array, baseIsNaN, index, true);\n }\n\n /**\n * Gets the element at index `n` of `array`. If `n` is negative, the nth\n * element from the end is returned.\n *\n * @static\n * @memberOf _\n * @since 4.11.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=0] The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n *\n * _.nth(array, 1);\n * // => 'b'\n *\n * _.nth(array, -2);\n * // => 'c';\n */\n function nth(array, n) {\n return array && array.length ? baseNth(array, toInteger(n)) : undefined;\n }\n\n /**\n * Removes all given values from `array` using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`\n * to remove elements from an array by predicate.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...*} [values] The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pull(array, 'a', 'c');\n * console.log(array);\n * // => ['b', 'b']\n */\n var pull = baseRest(pullAll);\n\n /**\n * This method is like `_.pull` except that it accepts an array of values to remove.\n *\n * **Note:** Unlike `_.difference`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pullAll(array, ['a', 'c']);\n * console.log(array);\n * // => ['b', 'b']\n */\n function pullAll(array, values) {\n return array && array.length && values && values.length ? basePullAll(array, values) : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `iteratee` which is\n * invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The iteratee is invoked with one argument: (value).\n *\n * **Note:** Unlike `_.differenceBy`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];\n *\n * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');\n * console.log(array);\n * // => [{ 'x': 2 }]\n */\n function pullAllBy(array, values, iteratee) {\n return array && array.length && values && values.length ? basePullAll(array, values, getIteratee(iteratee, 2)) : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `comparator` which\n * is invoked to compare elements of `array` to `values`. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.differenceWith`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];\n *\n * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);\n * console.log(array);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]\n */\n function pullAllWith(array, values, comparator) {\n return array && array.length && values && values.length ? basePullAll(array, values, undefined, comparator) : array;\n }\n\n /**\n * Removes elements from `array` corresponding to `indexes` and returns an\n * array of removed elements.\n *\n * **Note:** Unlike `_.at`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...(number|number[])} [indexes] The indexes of elements to remove.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n * var pulled = _.pullAt(array, [1, 3]);\n *\n * console.log(array);\n * // => ['a', 'c']\n *\n * console.log(pulled);\n * // => ['b', 'd']\n */\n var pullAt = flatRest(function (array, indexes) {\n var length = array == null ? 0 : array.length,\n result = baseAt(array, indexes);\n basePullAt(array, arrayMap(indexes, function (index) {\n return isIndex(index, length) ? +index : index;\n }).sort(compareAscending));\n return result;\n });\n\n /**\n * Removes all elements from `array` that `predicate` returns truthy for\n * and returns an array of the removed elements. The predicate is invoked\n * with three arguments: (value, index, array).\n *\n * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`\n * to pull elements from an array by value.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = [1, 2, 3, 4];\n * var evens = _.remove(array, function(n) {\n * return n % 2 == 0;\n * });\n *\n * console.log(array);\n * // => [1, 3]\n *\n * console.log(evens);\n * // => [2, 4]\n */\n function remove(array, predicate) {\n var result = [];\n if (!(array && array.length)) {\n return result;\n }\n var index = -1,\n indexes = [],\n length = array.length;\n predicate = getIteratee(predicate, 3);\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result.push(value);\n indexes.push(index);\n }\n }\n basePullAt(array, indexes);\n return result;\n }\n\n /**\n * Reverses `array` so that the first element becomes the last, the second\n * element becomes the second to last, and so on.\n *\n * **Note:** This method mutates `array` and is based on\n * [`Array#reverse`](https://mdn.io/Array/reverse).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.reverse(array);\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function reverse(array) {\n return array == null ? array : nativeReverse.call(array);\n }\n\n /**\n * Creates a slice of `array` from `start` up to, but not including, `end`.\n *\n * **Note:** This method is used instead of\n * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are\n * returned.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function slice(array, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {\n start = 0;\n end = length;\n } else {\n start = start == null ? 0 : toInteger(start);\n end = end === undefined ? length : toInteger(end);\n }\n return baseSlice(array, start, end);\n }\n\n /**\n * Uses a binary search to determine the lowest index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedIndex([30, 50], 40);\n * // => 1\n */\n function sortedIndex(array, value) {\n return baseSortedIndex(array, value);\n }\n\n /**\n * This method is like `_.sortedIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedIndexBy(objects, { 'x': 4 }, 'x');\n * // => 0\n */\n function sortedIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));\n }\n\n /**\n * This method is like `_.indexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedIndexOf([4, 5, 5, 5, 6], 5);\n * // => 1\n */\n function sortedIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value);\n if (index < length && eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.sortedIndex` except that it returns the highest\n * index at which `value` should be inserted into `array` in order to\n * maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedLastIndex([4, 5, 5, 5, 6], 5);\n * // => 4\n */\n function sortedLastIndex(array, value) {\n return baseSortedIndex(array, value, true);\n }\n\n /**\n * This method is like `_.sortedLastIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 1\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');\n * // => 1\n */\n function sortedLastIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);\n }\n\n /**\n * This method is like `_.lastIndexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);\n * // => 3\n */\n function sortedLastIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value, true) - 1;\n if (eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.uniq` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniq([1, 1, 2]);\n * // => [1, 2]\n */\n function sortedUniq(array) {\n return array && array.length ? baseSortedUniq(array) : [];\n }\n\n /**\n * This method is like `_.uniqBy` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);\n * // => [1.1, 2.3]\n */\n function sortedUniqBy(array, iteratee) {\n return array && array.length ? baseSortedUniq(array, getIteratee(iteratee, 2)) : [];\n }\n\n /**\n * Gets all but the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.tail([1, 2, 3]);\n * // => [2, 3]\n */\n function tail(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 1, length) : [];\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.take([1, 2, 3]);\n * // => [1]\n *\n * _.take([1, 2, 3], 2);\n * // => [1, 2]\n *\n * _.take([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.take([1, 2, 3], 0);\n * // => []\n */\n function take(array, n, guard) {\n if (!(array && array.length)) {\n return [];\n }\n n = guard || n === undefined ? 1 : toInteger(n);\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.takeRight([1, 2, 3]);\n * // => [3]\n *\n * _.takeRight([1, 2, 3], 2);\n * // => [2, 3]\n *\n * _.takeRight([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.takeRight([1, 2, 3], 0);\n * // => []\n */\n function takeRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = guard || n === undefined ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with elements taken from the end. Elements are\n * taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.takeRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeRightWhile(users, ['active', false]);\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeRightWhile(users, 'active');\n * // => []\n */\n function takeRightWhile(array, predicate) {\n return array && array.length ? baseWhile(array, getIteratee(predicate, 3), false, true) : [];\n }\n\n /**\n * Creates a slice of `array` with elements taken from the beginning. Elements\n * are taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.takeWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeWhile(users, ['active', false]);\n * // => objects for ['barney', 'fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeWhile(users, 'active');\n * // => []\n */\n function takeWhile(array, predicate) {\n return array && array.length ? baseWhile(array, getIteratee(predicate, 3)) : [];\n }\n\n /**\n * Creates an array of unique values, in order, from all given arrays using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.union([2], [1, 2]);\n * // => [2, 1]\n */\n var union = baseRest(function (arrays) {\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));\n });\n\n /**\n * This method is like `_.union` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which uniqueness is computed. Result values are chosen from the first\n * array in which the value occurs. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.unionBy([2.1], [1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n var unionBy = baseRest(function (arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.union` except that it accepts `comparator` which\n * is invoked to compare elements of `arrays`. Result values are chosen from\n * the first array in which the value occurs. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.unionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var unionWith = baseRest(function (arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);\n });\n\n /**\n * Creates a duplicate-free version of an array, using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons, in which only the first occurrence of each element\n * is kept. The order of result values is determined by the order they occur\n * in the array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniq([2, 1, 2]);\n * // => [2, 1]\n */\n function uniq(array) {\n return array && array.length ? baseUniq(array) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The order of result values is determined by the\n * order they occur in the array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n function uniqBy(array, iteratee) {\n return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `comparator` which\n * is invoked to compare elements of `array`. The order of result values is\n * determined by the order they occur in the array.The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.uniqWith(objects, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]\n */\n function uniqWith(array, comparator) {\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return array && array.length ? baseUniq(array, undefined, comparator) : [];\n }\n\n /**\n * This method is like `_.zip` except that it accepts an array of grouped\n * elements and creates an array regrouping the elements to their pre-zip\n * configuration.\n *\n * @static\n * @memberOf _\n * @since 1.2.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n *\n * _.unzip(zipped);\n * // => [['a', 'b'], [1, 2], [true, false]]\n */\n function unzip(array) {\n if (!(array && array.length)) {\n return [];\n }\n var length = 0;\n array = arrayFilter(array, function (group) {\n if (isArrayLikeObject(group)) {\n length = nativeMax(group.length, length);\n return true;\n }\n });\n return baseTimes(length, function (index) {\n return arrayMap(array, baseProperty(index));\n });\n }\n\n /**\n * This method is like `_.unzip` except that it accepts `iteratee` to specify\n * how regrouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * regrouped values.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip([1, 2], [10, 20], [100, 200]);\n * // => [[1, 10, 100], [2, 20, 200]]\n *\n * _.unzipWith(zipped, _.add);\n * // => [3, 30, 300]\n */\n function unzipWith(array, iteratee) {\n if (!(array && array.length)) {\n return [];\n }\n var result = unzip(array);\n if (iteratee == null) {\n return result;\n }\n return arrayMap(result, function (group) {\n return apply(iteratee, undefined, group);\n });\n }\n\n /**\n * Creates an array excluding all given values using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.pull`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...*} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.xor\n * @example\n *\n * _.without([2, 1, 2, 3], 1, 2);\n * // => [3]\n */\n var without = baseRest(function (array, values) {\n return isArrayLikeObject(array) ? baseDifference(array, values) : [];\n });\n\n /**\n * Creates an array of unique values that is the\n * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)\n * of the given arrays. The order of result values is determined by the order\n * they occur in the arrays.\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.without\n * @example\n *\n * _.xor([2, 1], [2, 3]);\n * // => [1, 3]\n */\n var xor = baseRest(function (arrays) {\n return baseXor(arrayFilter(arrays, isArrayLikeObject));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which by which they're compared. The order of result values is determined\n * by the order they occur in the arrays. The iteratee is invoked with one\n * argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2, 3.4]\n *\n * // The `_.property` iteratee shorthand.\n * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var xorBy = baseRest(function (arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `comparator` which is\n * invoked to compare elements of `arrays`. The order of result values is\n * determined by the order they occur in the arrays. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.xorWith(objects, others, _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var xorWith = baseRest(function (arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);\n });\n\n /**\n * Creates an array of grouped elements, the first of which contains the\n * first elements of the given arrays, the second of which contains the\n * second elements of the given arrays, and so on.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n */\n var zip = baseRest(unzip);\n\n /**\n * This method is like `_.fromPairs` except that it accepts two arrays,\n * one of property identifiers and one of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 0.4.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObject(['a', 'b'], [1, 2]);\n * // => { 'a': 1, 'b': 2 }\n */\n function zipObject(props, values) {\n return baseZipObject(props || [], values || [], assignValue);\n }\n\n /**\n * This method is like `_.zipObject` except that it supports property paths.\n *\n * @static\n * @memberOf _\n * @since 4.1.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);\n * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }\n */\n function zipObjectDeep(props, values) {\n return baseZipObject(props || [], values || [], baseSet);\n }\n\n /**\n * This method is like `_.zip` except that it accepts `iteratee` to specify\n * how grouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * grouped values.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {\n * return a + b + c;\n * });\n * // => [111, 222]\n */\n var zipWith = baseRest(function (arrays) {\n var length = arrays.length,\n iteratee = length > 1 ? arrays[length - 1] : undefined;\n iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;\n return unzipWith(arrays, iteratee);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` wrapper instance that wraps `value` with explicit method\n * chain sequences enabled. The result of such sequences must be unwrapped\n * with `_#value`.\n *\n * @static\n * @memberOf _\n * @since 1.3.0\n * @category Seq\n * @param {*} value The value to wrap.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'pebbles', 'age': 1 }\n * ];\n *\n * var youngest = _\n * .chain(users)\n * .sortBy('age')\n * .map(function(o) {\n * return o.user + ' is ' + o.age;\n * })\n * .head()\n * .value();\n * // => 'pebbles is 1'\n */\n function chain(value) {\n var result = lodash(value);\n result.__chain__ = true;\n return result;\n }\n\n /**\n * This method invokes `interceptor` and returns `value`. The interceptor\n * is invoked with one argument; (value). The purpose of this method is to\n * \"tap into\" a method chain sequence in order to modify intermediate results.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns `value`.\n * @example\n *\n * _([1, 2, 3])\n * .tap(function(array) {\n * // Mutate input array.\n * array.pop();\n * })\n * .reverse()\n * .value();\n * // => [2, 1]\n */\n function tap(value, interceptor) {\n interceptor(value);\n return value;\n }\n\n /**\n * This method is like `_.tap` except that it returns the result of `interceptor`.\n * The purpose of this method is to \"pass thru\" values replacing intermediate\n * results in a method chain sequence.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns the result of `interceptor`.\n * @example\n *\n * _(' abc ')\n * .chain()\n * .trim()\n * .thru(function(value) {\n * return [value];\n * })\n * .value();\n * // => ['abc']\n */\n function thru(value, interceptor) {\n return interceptor(value);\n }\n\n /**\n * This method is the wrapper version of `_.at`.\n *\n * @name at\n * @memberOf _\n * @since 1.0.0\n * @category Seq\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n *\n * _(object).at(['a[0].b.c', 'a[1]']).value();\n * // => [3, 4]\n */\n var wrapperAt = flatRest(function (paths) {\n var length = paths.length,\n start = length ? paths[0] : 0,\n value = this.__wrapped__,\n interceptor = function (object) {\n return baseAt(object, paths);\n };\n if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) {\n return this.thru(interceptor);\n }\n value = value.slice(start, +start + (length ? 1 : 0));\n value.__actions__.push({\n 'func': thru,\n 'args': [interceptor],\n 'thisArg': undefined\n });\n return new LodashWrapper(value, this.__chain__).thru(function (array) {\n if (length && !array.length) {\n array.push(undefined);\n }\n return array;\n });\n });\n\n /**\n * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.\n *\n * @name chain\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 }\n * ];\n *\n * // A sequence without explicit chaining.\n * _(users).head();\n * // => { 'user': 'barney', 'age': 36 }\n *\n * // A sequence with explicit chaining.\n * _(users)\n * .chain()\n * .head()\n * .pick('user')\n * .value();\n * // => { 'user': 'barney' }\n */\n function wrapperChain() {\n return chain(this);\n }\n\n /**\n * Executes the chain sequence and returns the wrapped result.\n *\n * @name commit\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2];\n * var wrapped = _(array).push(3);\n *\n * console.log(array);\n * // => [1, 2]\n *\n * wrapped = wrapped.commit();\n * console.log(array);\n * // => [1, 2, 3]\n *\n * wrapped.last();\n * // => 3\n *\n * console.log(array);\n * // => [1, 2, 3]\n */\n function wrapperCommit() {\n return new LodashWrapper(this.value(), this.__chain__);\n }\n\n /**\n * Gets the next value on a wrapped object following the\n * [iterator protocol](https://mdn.io/iteration_protocols#iterator).\n *\n * @name next\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the next iterator value.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 1 }\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 2 }\n *\n * wrapped.next();\n * // => { 'done': true, 'value': undefined }\n */\n function wrapperNext() {\n if (this.__values__ === undefined) {\n this.__values__ = toArray(this.value());\n }\n var done = this.__index__ >= this.__values__.length,\n value = done ? undefined : this.__values__[this.__index__++];\n return {\n 'done': done,\n 'value': value\n };\n }\n\n /**\n * Enables the wrapper to be iterable.\n *\n * @name Symbol.iterator\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the wrapper object.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped[Symbol.iterator]() === wrapped;\n * // => true\n *\n * Array.from(wrapped);\n * // => [1, 2]\n */\n function wrapperToIterator() {\n return this;\n }\n\n /**\n * Creates a clone of the chain sequence planting `value` as the wrapped value.\n *\n * @name plant\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @param {*} value The value to plant.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2]).map(square);\n * var other = wrapped.plant([3, 4]);\n *\n * other.value();\n * // => [9, 16]\n *\n * wrapped.value();\n * // => [1, 4]\n */\n function wrapperPlant(value) {\n var result,\n parent = this;\n while (parent instanceof baseLodash) {\n var clone = wrapperClone(parent);\n clone.__index__ = 0;\n clone.__values__ = undefined;\n if (result) {\n previous.__wrapped__ = clone;\n } else {\n result = clone;\n }\n var previous = clone;\n parent = parent.__wrapped__;\n }\n previous.__wrapped__ = value;\n return result;\n }\n\n /**\n * This method is the wrapper version of `_.reverse`.\n *\n * **Note:** This method mutates the wrapped array.\n *\n * @name reverse\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _(array).reverse().value()\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function wrapperReverse() {\n var value = this.__wrapped__;\n if (value instanceof LazyWrapper) {\n var wrapped = value;\n if (this.__actions__.length) {\n wrapped = new LazyWrapper(this);\n }\n wrapped = wrapped.reverse();\n wrapped.__actions__.push({\n 'func': thru,\n 'args': [reverse],\n 'thisArg': undefined\n });\n return new LodashWrapper(wrapped, this.__chain__);\n }\n return this.thru(reverse);\n }\n\n /**\n * Executes the chain sequence to resolve the unwrapped value.\n *\n * @name value\n * @memberOf _\n * @since 0.1.0\n * @alias toJSON, valueOf\n * @category Seq\n * @returns {*} Returns the resolved unwrapped value.\n * @example\n *\n * _([1, 2, 3]).value();\n * // => [1, 2, 3]\n */\n function wrapperValue() {\n return baseWrapperValue(this.__wrapped__, this.__actions__);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the number of times the key was returned by `iteratee`. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.countBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': 1, '6': 2 }\n *\n * // The `_.property` iteratee shorthand.\n * _.countBy(['one', 'two', 'three'], 'length');\n * // => { '3': 2, '5': 1 }\n */\n var countBy = createAggregator(function (result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n ++result[key];\n } else {\n baseAssignValue(result, key, 1);\n }\n });\n\n /**\n * Checks if `predicate` returns truthy for **all** elements of `collection`.\n * Iteration is stopped once `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * **Note:** This method returns `true` for\n * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because\n * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of\n * elements of empty collections.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n * @example\n *\n * _.every([true, 1, null, 'yes'], Boolean);\n * // => false\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.every(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.every(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.every(users, 'active');\n * // => false\n */\n function every(collection, predicate, guard) {\n var func = isArray(collection) ? arrayEvery : baseEvery;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning an array of all elements\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * **Note:** Unlike `_.remove`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.reject\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * _.filter(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.filter(users, { 'age': 36, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.filter(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.filter(users, 'active');\n * // => objects for ['barney']\n *\n * // Combining several predicates using `_.overEvery` or `_.overSome`.\n * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));\n * // => objects for ['fred', 'barney']\n */\n function filter(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning the first element\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false },\n * { 'user': 'pebbles', 'age': 1, 'active': true }\n * ];\n *\n * _.find(users, function(o) { return o.age < 40; });\n * // => object for 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.find(users, { 'age': 1, 'active': true });\n * // => object for 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.find(users, ['active', false]);\n * // => object for 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.find(users, 'active');\n * // => object for 'barney'\n */\n var find = createFind(findIndex);\n\n /**\n * This method is like `_.find` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=collection.length-1] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * _.findLast([1, 2, 3, 4], function(n) {\n * return n % 2 == 1;\n * });\n * // => 3\n */\n var findLast = createFind(findLastIndex);\n\n /**\n * Creates a flattened array of values by running each element in `collection`\n * thru `iteratee` and flattening the mapped results. The iteratee is invoked\n * with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [n, n];\n * }\n *\n * _.flatMap([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMap(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), 1);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDeep([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMapDeep(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), INFINITY);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDepth([1, 2], duplicate, 2);\n * // => [[1, 1], [2, 2]]\n */\n function flatMapDepth(collection, iteratee, depth) {\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(map(collection, iteratee), depth);\n }\n\n /**\n * Iterates over elements of `collection` and invokes `iteratee` for each element.\n * The iteratee is invoked with three arguments: (value, index|key, collection).\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * **Note:** As with other \"Collections\" methods, objects with a \"length\"\n * property are iterated like arrays. To avoid this behavior use `_.forIn`\n * or `_.forOwn` for object iteration.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias each\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEachRight\n * @example\n *\n * _.forEach([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `1` then `2`.\n *\n * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n */\n function forEach(collection, iteratee) {\n var func = isArray(collection) ? arrayEach : baseEach;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.forEach` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @alias eachRight\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEach\n * @example\n *\n * _.forEachRight([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `2` then `1`.\n */\n function forEachRight(collection, iteratee) {\n var func = isArray(collection) ? arrayEachRight : baseEachRight;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The order of grouped values\n * is determined by the order they occur in `collection`. The corresponding\n * value of each key is an array of elements responsible for generating the\n * key. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.groupBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': [4.2], '6': [6.1, 6.3] }\n *\n * // The `_.property` iteratee shorthand.\n * _.groupBy(['one', 'two', 'three'], 'length');\n * // => { '3': ['one', 'two'], '5': ['three'] }\n */\n var groupBy = createAggregator(function (result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n result[key].push(value);\n } else {\n baseAssignValue(result, key, [value]);\n }\n });\n\n /**\n * Checks if `value` is in `collection`. If `collection` is a string, it's\n * checked for a substring of `value`, otherwise\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * is used for equality comparisons. If `fromIndex` is negative, it's used as\n * the offset from the end of `collection`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {boolean} Returns `true` if `value` is found, else `false`.\n * @example\n *\n * _.includes([1, 2, 3], 1);\n * // => true\n *\n * _.includes([1, 2, 3], 1, 2);\n * // => false\n *\n * _.includes({ 'a': 1, 'b': 2 }, 1);\n * // => true\n *\n * _.includes('abcd', 'bc');\n * // => true\n */\n function includes(collection, value, fromIndex, guard) {\n collection = isArrayLike(collection) ? collection : values(collection);\n fromIndex = fromIndex && !guard ? toInteger(fromIndex) : 0;\n var length = collection.length;\n if (fromIndex < 0) {\n fromIndex = nativeMax(length + fromIndex, 0);\n }\n return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1;\n }\n\n /**\n * Invokes the method at `path` of each element in `collection`, returning\n * an array of the results of each invoked method. Any additional arguments\n * are provided to each invoked method. If `path` is a function, it's invoked\n * for, and `this` bound to, each element in `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array|Function|string} path The path of the method to invoke or\n * the function invoked per iteration.\n * @param {...*} [args] The arguments to invoke each method with.\n * @returns {Array} Returns the array of results.\n * @example\n *\n * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');\n * // => [[1, 5, 7], [1, 2, 3]]\n *\n * _.invokeMap([123, 456], String.prototype.split, '');\n * // => [['1', '2', '3'], ['4', '5', '6']]\n */\n var invokeMap = baseRest(function (collection, path, args) {\n var index = -1,\n isFunc = typeof path == 'function',\n result = isArrayLike(collection) ? Array(collection.length) : [];\n baseEach(collection, function (value) {\n result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);\n });\n return result;\n });\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the last element responsible for generating the key. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * var array = [\n * { 'dir': 'left', 'code': 97 },\n * { 'dir': 'right', 'code': 100 }\n * ];\n *\n * _.keyBy(array, function(o) {\n * return String.fromCharCode(o.code);\n * });\n * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }\n *\n * _.keyBy(array, 'dir');\n * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }\n */\n var keyBy = createAggregator(function (result, value, key) {\n baseAssignValue(result, key, value);\n });\n\n /**\n * Creates an array of values by running each element in `collection` thru\n * `iteratee`. The iteratee is invoked with three arguments:\n * (value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.\n *\n * The guarded methods are:\n * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,\n * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,\n * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,\n * `template`, `trim`, `trimEnd`, `trimStart`, and `words`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * _.map([4, 8], square);\n * // => [16, 64]\n *\n * _.map({ 'a': 4, 'b': 8 }, square);\n * // => [16, 64] (iteration order is not guaranteed)\n *\n * var users = [\n * { 'user': 'barney' },\n * { 'user': 'fred' }\n * ];\n *\n * // The `_.property` iteratee shorthand.\n * _.map(users, 'user');\n * // => ['barney', 'fred']\n */\n function map(collection, iteratee) {\n var func = isArray(collection) ? arrayMap : baseMap;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.sortBy` except that it allows specifying the sort\n * orders of the iteratees to sort by. If `orders` is unspecified, all values\n * are sorted in ascending order. Otherwise, specify an order of \"desc\" for\n * descending or \"asc\" for ascending sort order of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @param {string[]} [orders] The sort orders of `iteratees`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 34 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'barney', 'age': 36 }\n * ];\n *\n * // Sort by `user` in ascending order and by `age` in descending order.\n * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n */\n function orderBy(collection, iteratees, orders, guard) {\n if (collection == null) {\n return [];\n }\n if (!isArray(iteratees)) {\n iteratees = iteratees == null ? [] : [iteratees];\n }\n orders = guard ? undefined : orders;\n if (!isArray(orders)) {\n orders = orders == null ? [] : [orders];\n }\n return baseOrderBy(collection, iteratees, orders);\n }\n\n /**\n * Creates an array of elements split into two groups, the first of which\n * contains elements `predicate` returns truthy for, the second of which\n * contains elements `predicate` returns falsey for. The predicate is\n * invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the array of grouped elements.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true },\n * { 'user': 'pebbles', 'age': 1, 'active': false }\n * ];\n *\n * _.partition(users, function(o) { return o.active; });\n * // => objects for [['fred'], ['barney', 'pebbles']]\n *\n * // The `_.matches` iteratee shorthand.\n * _.partition(users, { 'age': 1, 'active': false });\n * // => objects for [['pebbles'], ['barney', 'fred']]\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.partition(users, ['active', false]);\n * // => objects for [['barney', 'pebbles'], ['fred']]\n *\n * // The `_.property` iteratee shorthand.\n * _.partition(users, 'active');\n * // => objects for [['fred'], ['barney', 'pebbles']]\n */\n var partition = createAggregator(function (result, value, key) {\n result[key ? 0 : 1].push(value);\n }, function () {\n return [[], []];\n });\n\n /**\n * Reduces `collection` to a value which is the accumulated result of running\n * each element in `collection` thru `iteratee`, where each successive\n * invocation is supplied the return value of the previous. If `accumulator`\n * is not given, the first element of `collection` is used as the initial\n * value. The iteratee is invoked with four arguments:\n * (accumulator, value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.reduce`, `_.reduceRight`, and `_.transform`.\n *\n * The guarded methods are:\n * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,\n * and `sortBy`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduceRight\n * @example\n *\n * _.reduce([1, 2], function(sum, n) {\n * return sum + n;\n * }, 0);\n * // => 3\n *\n * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n * (result[value] || (result[value] = [])).push(key);\n * return result;\n * }, {});\n * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)\n */\n function reduce(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduce : baseReduce,\n initAccum = arguments.length < 3;\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);\n }\n\n /**\n * This method is like `_.reduce` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduce\n * @example\n *\n * var array = [[0, 1], [2, 3], [4, 5]];\n *\n * _.reduceRight(array, function(flattened, other) {\n * return flattened.concat(other);\n * }, []);\n * // => [4, 5, 2, 3, 0, 1]\n */\n function reduceRight(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduceRight : baseReduce,\n initAccum = arguments.length < 3;\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);\n }\n\n /**\n * The opposite of `_.filter`; this method returns the elements of `collection`\n * that `predicate` does **not** return truthy for.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.filter\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true }\n * ];\n *\n * _.reject(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.reject(users, { 'age': 40, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.reject(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.reject(users, 'active');\n * // => objects for ['barney']\n */\n function reject(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, negate(getIteratee(predicate, 3)));\n }\n\n /**\n * Gets a random element from `collection`.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n * @example\n *\n * _.sample([1, 2, 3, 4]);\n * // => 2\n */\n function sample(collection) {\n var func = isArray(collection) ? arraySample : baseSample;\n return func(collection);\n }\n\n /**\n * Gets `n` random elements at unique keys from `collection` up to the\n * size of `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @param {number} [n=1] The number of elements to sample.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the random elements.\n * @example\n *\n * _.sampleSize([1, 2, 3], 2);\n * // => [3, 1]\n *\n * _.sampleSize([1, 2, 3], 4);\n * // => [2, 3, 1]\n */\n function sampleSize(collection, n, guard) {\n if (guard ? isIterateeCall(collection, n, guard) : n === undefined) {\n n = 1;\n } else {\n n = toInteger(n);\n }\n var func = isArray(collection) ? arraySampleSize : baseSampleSize;\n return func(collection, n);\n }\n\n /**\n * Creates an array of shuffled values, using a version of the\n * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n * @example\n *\n * _.shuffle([1, 2, 3, 4]);\n * // => [4, 1, 3, 2]\n */\n function shuffle(collection) {\n var func = isArray(collection) ? arrayShuffle : baseShuffle;\n return func(collection);\n }\n\n /**\n * Gets the size of `collection` by returning its length for array-like\n * values or the number of own enumerable string keyed properties for objects.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @returns {number} Returns the collection size.\n * @example\n *\n * _.size([1, 2, 3]);\n * // => 3\n *\n * _.size({ 'a': 1, 'b': 2 });\n * // => 2\n *\n * _.size('pebbles');\n * // => 7\n */\n function size(collection) {\n if (collection == null) {\n return 0;\n }\n if (isArrayLike(collection)) {\n return isString(collection) ? stringSize(collection) : collection.length;\n }\n var tag = getTag(collection);\n if (tag == mapTag || tag == setTag) {\n return collection.size;\n }\n return baseKeys(collection).length;\n }\n\n /**\n * Checks if `predicate` returns truthy for **any** element of `collection`.\n * Iteration is stopped once `predicate` returns truthy. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n * @example\n *\n * _.some([null, 0, 'yes', false], Boolean);\n * // => true\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.some(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.some(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.some(users, 'active');\n * // => true\n */\n function some(collection, predicate, guard) {\n var func = isArray(collection) ? arraySome : baseSome;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Creates an array of elements, sorted in ascending order by the results of\n * running each element in a collection thru each iteratee. This method\n * performs a stable sort, that is, it preserves the original sort order of\n * equal elements. The iteratees are invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {...(Function|Function[])} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 30 },\n * { 'user': 'barney', 'age': 34 }\n * ];\n *\n * _.sortBy(users, [function(o) { return o.user; }]);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]\n *\n * _.sortBy(users, ['user', 'age']);\n * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]\n */\n var sortBy = baseRest(function (collection, iteratees) {\n if (collection == null) {\n return [];\n }\n var length = iteratees.length;\n if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {\n iteratees = [];\n } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {\n iteratees = [iteratees[0]];\n }\n return baseOrderBy(collection, baseFlatten(iteratees, 1), []);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\n var now = ctxNow || function () {\n return root.Date.now();\n };\n\n /*------------------------------------------------------------------------*/\n\n /**\n * The opposite of `_.before`; this method creates a function that invokes\n * `func` once it's called `n` or more times.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {number} n The number of calls before `func` is invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var saves = ['profile', 'settings'];\n *\n * var done = _.after(saves.length, function() {\n * console.log('done saving!');\n * });\n *\n * _.forEach(saves, function(type) {\n * asyncSave({ 'type': type, 'complete': done });\n * });\n * // => Logs 'done saving!' after the two async saves have completed.\n */\n function after(n, func) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function () {\n if (--n < 1) {\n return func.apply(this, arguments);\n }\n };\n }\n\n /**\n * Creates a function that invokes `func`, with up to `n` arguments,\n * ignoring any additional arguments.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @param {number} [n=func.length] The arity cap.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.ary(parseInt, 1));\n * // => [6, 8, 10]\n */\n function ary(func, n, guard) {\n n = guard ? undefined : n;\n n = func && n == null ? func.length : n;\n return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);\n }\n\n /**\n * Creates a function that invokes `func`, with the `this` binding and arguments\n * of the created function, while it's called less than `n` times. Subsequent\n * calls to the created function return the result of the last `func` invocation.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {number} n The number of calls at which `func` is no longer invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * jQuery(element).on('click', _.before(5, addContactToList));\n * // => Allows adding up to 4 contacts to the list.\n */\n function before(n, func) {\n var result;\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function () {\n if (--n > 0) {\n result = func.apply(this, arguments);\n }\n if (n <= 1) {\n func = undefined;\n }\n return result;\n };\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of `thisArg`\n * and `partials` prepended to the arguments it receives.\n *\n * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for partially applied arguments.\n *\n * **Note:** Unlike native `Function#bind`, this method doesn't set the \"length\"\n * property of bound functions.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to bind.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * function greet(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n *\n * var object = { 'user': 'fred' };\n *\n * var bound = _.bind(greet, object, 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bind(greet, object, _, '!');\n * bound('hi');\n * // => 'hi fred!'\n */\n var bind = baseRest(function (func, thisArg, partials) {\n var bitmask = WRAP_BIND_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bind));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(func, bitmask, thisArg, partials, holders);\n });\n\n /**\n * Creates a function that invokes the method at `object[key]` with `partials`\n * prepended to the arguments it receives.\n *\n * This method differs from `_.bind` by allowing bound functions to reference\n * methods that may be redefined or don't yet exist. See\n * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)\n * for more details.\n *\n * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Function\n * @param {Object} object The object to invoke the method on.\n * @param {string} key The key of the method.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * var object = {\n * 'user': 'fred',\n * 'greet': function(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n * };\n *\n * var bound = _.bindKey(object, 'greet', 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * object.greet = function(greeting, punctuation) {\n * return greeting + 'ya ' + this.user + punctuation;\n * };\n *\n * bound('!');\n * // => 'hiya fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bindKey(object, 'greet', _, '!');\n * bound('hi');\n * // => 'hiya fred!'\n */\n var bindKey = baseRest(function (object, key, partials) {\n var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bindKey));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(key, bitmask, object, partials, holders);\n });\n\n /**\n * Creates a function that accepts arguments of `func` and either invokes\n * `func` returning its result, if at least `arity` number of arguments have\n * been provided, or returns a function that accepts the remaining `func`\n * arguments, and so on. The arity of `func` may be specified if `func.length`\n * is not sufficient.\n *\n * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curry(abc);\n *\n * curried(1)(2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(1)(_, 3)(2);\n * // => [1, 2, 3]\n */\n function curry(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curry.placeholder;\n return result;\n }\n\n /**\n * This method is like `_.curry` except that arguments are applied to `func`\n * in the manner of `_.partialRight` instead of `_.partial`.\n *\n * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curryRight(abc);\n *\n * curried(3)(2)(1);\n * // => [1, 2, 3]\n *\n * curried(2, 3)(1);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(3)(1, _)(2);\n * // => [1, 2, 3]\n */\n function curryRight(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curryRight.placeholder;\n return result;\n }\n\n /**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\n function debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n timeWaiting = wait - timeSinceLastCall;\n return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;\n }\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;\n }\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n clearTimeout(timerId);\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n }\n\n /**\n * Defers invoking the `func` until the current call stack has cleared. Any\n * additional arguments are provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to defer.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.defer(function(text) {\n * console.log(text);\n * }, 'deferred');\n * // => Logs 'deferred' after one millisecond.\n */\n var defer = baseRest(function (func, args) {\n return baseDelay(func, 1, args);\n });\n\n /**\n * Invokes `func` after `wait` milliseconds. Any additional arguments are\n * provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.delay(function(text) {\n * console.log(text);\n * }, 1000, 'later');\n * // => Logs 'later' after one second.\n */\n var delay = baseRest(function (func, wait, args) {\n return baseDelay(func, toNumber(wait) || 0, args);\n });\n\n /**\n * Creates a function that invokes `func` with arguments reversed.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to flip arguments for.\n * @returns {Function} Returns the new flipped function.\n * @example\n *\n * var flipped = _.flip(function() {\n * return _.toArray(arguments);\n * });\n *\n * flipped('a', 'b', 'c', 'd');\n * // => ['d', 'c', 'b', 'a']\n */\n function flip(func) {\n return createWrap(func, WRAP_FLIP_FLAG);\n }\n\n /**\n * Creates a function that memoizes the result of `func`. If `resolver` is\n * provided, it determines the cache key for storing the result based on the\n * arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is used as the map cache key. The `func`\n * is invoked with the `this` binding of the memoized function.\n *\n * **Note:** The cache is exposed as the `cache` property on the memoized\n * function. Its creation may be customized by replacing the `_.memoize.Cache`\n * constructor with one whose instances implement the\n * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)\n * method interface of `clear`, `delete`, `get`, `has`, and `set`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to have its output memoized.\n * @param {Function} [resolver] The function to resolve the cache key.\n * @returns {Function} Returns the new memoized function.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n * var other = { 'c': 3, 'd': 4 };\n *\n * var values = _.memoize(_.values);\n * values(object);\n * // => [1, 2]\n *\n * values(other);\n * // => [3, 4]\n *\n * object.a = 2;\n * values(object);\n * // => [1, 2]\n *\n * // Modify the result cache.\n * values.cache.set(object, ['a', 'b']);\n * values(object);\n * // => ['a', 'b']\n *\n * // Replace `_.memoize.Cache`.\n * _.memoize.Cache = WeakMap;\n */\n function memoize(func, resolver) {\n if (typeof func != 'function' || resolver != null && typeof resolver != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var memoized = function () {\n var args = arguments,\n key = resolver ? resolver.apply(this, args) : args[0],\n cache = memoized.cache;\n if (cache.has(key)) {\n return cache.get(key);\n }\n var result = func.apply(this, args);\n memoized.cache = cache.set(key, result) || cache;\n return result;\n };\n memoized.cache = new (memoize.Cache || MapCache)();\n return memoized;\n }\n\n // Expose `MapCache`.\n memoize.Cache = MapCache;\n\n /**\n * Creates a function that negates the result of the predicate `func`. The\n * `func` predicate is invoked with the `this` binding and arguments of the\n * created function.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} predicate The predicate to negate.\n * @returns {Function} Returns the new negated function.\n * @example\n *\n * function isEven(n) {\n * return n % 2 == 0;\n * }\n *\n * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));\n * // => [1, 3, 5]\n */\n function negate(predicate) {\n if (typeof predicate != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return function () {\n var args = arguments;\n switch (args.length) {\n case 0:\n return !predicate.call(this);\n case 1:\n return !predicate.call(this, args[0]);\n case 2:\n return !predicate.call(this, args[0], args[1]);\n case 3:\n return !predicate.call(this, args[0], args[1], args[2]);\n }\n return !predicate.apply(this, args);\n };\n }\n\n /**\n * Creates a function that is restricted to invoking `func` once. Repeat calls\n * to the function return the value of the first invocation. The `func` is\n * invoked with the `this` binding and arguments of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var initialize = _.once(createApplication);\n * initialize();\n * initialize();\n * // => `createApplication` is invoked once\n */\n function once(func) {\n return before(2, func);\n }\n\n /**\n * Creates a function that invokes `func` with its arguments transformed.\n *\n * @static\n * @since 4.0.0\n * @memberOf _\n * @category Function\n * @param {Function} func The function to wrap.\n * @param {...(Function|Function[])} [transforms=[_.identity]]\n * The argument transforms.\n * @returns {Function} Returns the new function.\n * @example\n *\n * function doubled(n) {\n * return n * 2;\n * }\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var func = _.overArgs(function(x, y) {\n * return [x, y];\n * }, [square, doubled]);\n *\n * func(9, 3);\n * // => [81, 6]\n *\n * func(10, 5);\n * // => [100, 10]\n */\n var overArgs = castRest(function (func, transforms) {\n transforms = transforms.length == 1 && isArray(transforms[0]) ? arrayMap(transforms[0], baseUnary(getIteratee())) : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));\n var funcsLength = transforms.length;\n return baseRest(function (args) {\n var index = -1,\n length = nativeMin(args.length, funcsLength);\n while (++index < length) {\n args[index] = transforms[index].call(this, args[index]);\n }\n return apply(func, this, args);\n });\n });\n\n /**\n * Creates a function that invokes `func` with `partials` prepended to the\n * arguments it receives. This method is like `_.bind` except it does **not**\n * alter the `this` binding.\n *\n * The `_.partial.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 0.2.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var sayHelloTo = _.partial(greet, 'hello');\n * sayHelloTo('fred');\n * // => 'hello fred'\n *\n * // Partially applied with placeholders.\n * var greetFred = _.partial(greet, _, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n */\n var partial = baseRest(function (func, partials) {\n var holders = replaceHolders(partials, getHolder(partial));\n return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);\n });\n\n /**\n * This method is like `_.partial` except that partially applied arguments\n * are appended to the arguments it receives.\n *\n * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var greetFred = _.partialRight(greet, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n *\n * // Partially applied with placeholders.\n * var sayHelloTo = _.partialRight(greet, 'hello', _);\n * sayHelloTo('fred');\n * // => 'hello fred'\n */\n var partialRight = baseRest(function (func, partials) {\n var holders = replaceHolders(partials, getHolder(partialRight));\n return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);\n });\n\n /**\n * Creates a function that invokes `func` with arguments arranged according\n * to the specified `indexes` where the argument value at the first index is\n * provided as the first argument, the argument value at the second index is\n * provided as the second argument, and so on.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to rearrange arguments for.\n * @param {...(number|number[])} indexes The arranged argument indexes.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var rearged = _.rearg(function(a, b, c) {\n * return [a, b, c];\n * }, [2, 0, 1]);\n *\n * rearged('b', 'c', 'a')\n * // => ['a', 'b', 'c']\n */\n var rearg = flatRest(function (func, indexes) {\n return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);\n });\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * created function and arguments from `start` and beyond provided as\n * an array.\n *\n * **Note:** This method is based on the\n * [rest parameter](https://mdn.io/rest_parameters).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.rest(function(what, names) {\n * return what + ' ' + _.initial(names).join(', ') +\n * (_.size(names) > 1 ? ', & ' : '') + _.last(names);\n * });\n *\n * say('hello', 'fred', 'barney', 'pebbles');\n * // => 'hello fred, barney, & pebbles'\n */\n function rest(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start === undefined ? start : toInteger(start);\n return baseRest(func, start);\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * create function and an array of arguments much like\n * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).\n *\n * **Note:** This method is based on the\n * [spread operator](https://mdn.io/spread_operator).\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Function\n * @param {Function} func The function to spread arguments over.\n * @param {number} [start=0] The start position of the spread.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.spread(function(who, what) {\n * return who + ' says ' + what;\n * });\n *\n * say(['fred', 'hello']);\n * // => 'fred says hello'\n *\n * var numbers = Promise.all([\n * Promise.resolve(40),\n * Promise.resolve(36)\n * ]);\n *\n * numbers.then(_.spread(function(x, y) {\n * return x + y;\n * }));\n * // => a Promise of 76\n */\n function spread(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start == null ? 0 : nativeMax(toInteger(start), 0);\n return baseRest(function (args) {\n var array = args[start],\n otherArgs = castSlice(args, 0, start);\n if (array) {\n arrayPush(otherArgs, array);\n }\n return apply(func, this, otherArgs);\n });\n }\n\n /**\n * Creates a throttled function that only invokes `func` at most once per\n * every `wait` milliseconds. The throttled function comes with a `cancel`\n * method to cancel delayed `func` invocations and a `flush` method to\n * immediately invoke them. Provide `options` to indicate whether `func`\n * should be invoked on the leading and/or trailing edge of the `wait`\n * timeout. The `func` is invoked with the last arguments provided to the\n * throttled function. Subsequent calls to the throttled function return the\n * result of the last `func` invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the throttled function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.throttle` and `_.debounce`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to throttle.\n * @param {number} [wait=0] The number of milliseconds to throttle invocations to.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=true]\n * Specify invoking on the leading edge of the timeout.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new throttled function.\n * @example\n *\n * // Avoid excessively updating the position while scrolling.\n * jQuery(window).on('scroll', _.throttle(updatePosition, 100));\n *\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });\n * jQuery(element).on('click', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * jQuery(window).on('popstate', throttled.cancel);\n */\n function throttle(func, wait, options) {\n var leading = true,\n trailing = true;\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (isObject(options)) {\n leading = 'leading' in options ? !!options.leading : leading;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n return debounce(func, wait, {\n 'leading': leading,\n 'maxWait': wait,\n 'trailing': trailing\n });\n }\n\n /**\n * Creates a function that accepts up to one argument, ignoring any\n * additional arguments.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.unary(parseInt));\n * // => [6, 8, 10]\n */\n function unary(func) {\n return ary(func, 1);\n }\n\n /**\n * Creates a function that provides `value` to `wrapper` as its first\n * argument. Any additional arguments provided to the function are appended\n * to those provided to the `wrapper`. The wrapper is invoked with the `this`\n * binding of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {*} value The value to wrap.\n * @param {Function} [wrapper=identity] The wrapper function.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var p = _.wrap(_.escape, function(func, text) {\n * return '

' + func(text) + '

';\n * });\n *\n * p('fred, barney, & pebbles');\n * // => '

fred, barney, & pebbles

'\n */\n function wrap(value, wrapper) {\n return partial(castFunction(wrapper), value);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Casts `value` as an array if it's not one.\n *\n * @static\n * @memberOf _\n * @since 4.4.0\n * @category Lang\n * @param {*} value The value to inspect.\n * @returns {Array} Returns the cast array.\n * @example\n *\n * _.castArray(1);\n * // => [1]\n *\n * _.castArray({ 'a': 1 });\n * // => [{ 'a': 1 }]\n *\n * _.castArray('abc');\n * // => ['abc']\n *\n * _.castArray(null);\n * // => [null]\n *\n * _.castArray(undefined);\n * // => [undefined]\n *\n * _.castArray();\n * // => []\n *\n * var array = [1, 2, 3];\n * console.log(_.castArray(array) === array);\n * // => true\n */\n function castArray() {\n if (!arguments.length) {\n return [];\n }\n var value = arguments[0];\n return isArray(value) ? value : [value];\n }\n\n /**\n * Creates a shallow clone of `value`.\n *\n * **Note:** This method is loosely based on the\n * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)\n * and supports cloning arrays, array buffers, booleans, date objects, maps,\n * numbers, `Object` objects, regexes, sets, strings, symbols, and typed\n * arrays. The own enumerable properties of `arguments` objects are cloned\n * as plain objects. An empty object is returned for uncloneable values such\n * as error objects, functions, DOM nodes, and WeakMaps.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to clone.\n * @returns {*} Returns the cloned value.\n * @see _.cloneDeep\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var shallow = _.clone(objects);\n * console.log(shallow[0] === objects[0]);\n * // => true\n */\n function clone(value) {\n return baseClone(value, CLONE_SYMBOLS_FLAG);\n }\n\n /**\n * This method is like `_.clone` except that it accepts `customizer` which\n * is invoked to produce the cloned value. If `customizer` returns `undefined`,\n * cloning is handled by the method instead. The `customizer` is invoked with\n * up to four arguments; (value [, index|key, object, stack]).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to clone.\n * @param {Function} [customizer] The function to customize cloning.\n * @returns {*} Returns the cloned value.\n * @see _.cloneDeepWith\n * @example\n *\n * function customizer(value) {\n * if (_.isElement(value)) {\n * return value.cloneNode(false);\n * }\n * }\n *\n * var el = _.cloneWith(document.body, customizer);\n *\n * console.log(el === document.body);\n * // => false\n * console.log(el.nodeName);\n * // => 'BODY'\n * console.log(el.childNodes.length);\n * // => 0\n */\n function cloneWith(value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseClone(value, CLONE_SYMBOLS_FLAG, customizer);\n }\n\n /**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\n function cloneDeep(value) {\n return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);\n }\n\n /**\n * This method is like `_.cloneWith` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @param {Function} [customizer] The function to customize cloning.\n * @returns {*} Returns the deep cloned value.\n * @see _.cloneWith\n * @example\n *\n * function customizer(value) {\n * if (_.isElement(value)) {\n * return value.cloneNode(true);\n * }\n * }\n *\n * var el = _.cloneDeepWith(document.body, customizer);\n *\n * console.log(el === document.body);\n * // => false\n * console.log(el.nodeName);\n * // => 'BODY'\n * console.log(el.childNodes.length);\n * // => 20\n */\n function cloneDeepWith(value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer);\n }\n\n /**\n * Checks if `object` conforms to `source` by invoking the predicate\n * properties of `source` with the corresponding property values of `object`.\n *\n * **Note:** This method is equivalent to `_.conforms` when `source` is\n * partially applied.\n *\n * @static\n * @memberOf _\n * @since 4.14.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property predicates to conform to.\n * @returns {boolean} Returns `true` if `object` conforms, else `false`.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n *\n * _.conformsTo(object, { 'b': function(n) { return n > 1; } });\n * // => true\n *\n * _.conformsTo(object, { 'b': function(n) { return n > 2; } });\n * // => false\n */\n function conformsTo(object, source) {\n return source == null || baseConformsTo(object, source, keys(source));\n }\n\n /**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\n function eq(value, other) {\n return value === other || value !== value && other !== other;\n }\n\n /**\n * Checks if `value` is greater than `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n * @see _.lt\n * @example\n *\n * _.gt(3, 1);\n * // => true\n *\n * _.gt(3, 3);\n * // => false\n *\n * _.gt(1, 3);\n * // => false\n */\n var gt = createRelationalOperation(baseGt);\n\n /**\n * Checks if `value` is greater than or equal to `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than or equal to\n * `other`, else `false`.\n * @see _.lte\n * @example\n *\n * _.gte(3, 1);\n * // => true\n *\n * _.gte(3, 3);\n * // => true\n *\n * _.gte(1, 3);\n * // => false\n */\n var gte = createRelationalOperation(function (value, other) {\n return value >= other;\n });\n\n /**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\n var isArguments = baseIsArguments(function () {\n return arguments;\n }()) ? baseIsArguments : function (value) {\n return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');\n };\n\n /**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\n var isArray = Array.isArray;\n\n /**\n * Checks if `value` is classified as an `ArrayBuffer` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.\n * @example\n *\n * _.isArrayBuffer(new ArrayBuffer(2));\n * // => true\n *\n * _.isArrayBuffer(new Array(2));\n * // => false\n */\n var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;\n\n /**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\n function isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n }\n\n /**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\n function isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n }\n\n /**\n * Checks if `value` is classified as a boolean primitive or object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.\n * @example\n *\n * _.isBoolean(false);\n * // => true\n *\n * _.isBoolean(null);\n * // => false\n */\n function isBoolean(value) {\n return value === true || value === false || isObjectLike(value) && baseGetTag(value) == boolTag;\n }\n\n /**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\n var isBuffer = nativeIsBuffer || stubFalse;\n\n /**\n * Checks if `value` is classified as a `Date` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a date object, else `false`.\n * @example\n *\n * _.isDate(new Date);\n * // => true\n *\n * _.isDate('Mon April 23 2012');\n * // => false\n */\n var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;\n\n /**\n * Checks if `value` is likely a DOM element.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.\n * @example\n *\n * _.isElement(document.body);\n * // => true\n *\n * _.isElement('');\n * // => false\n */\n function isElement(value) {\n return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value);\n }\n\n /**\n * Checks if `value` is an empty object, collection, map, or set.\n *\n * Objects are considered empty if they have no own enumerable string keyed\n * properties.\n *\n * Array-like values such as `arguments` objects, arrays, buffers, strings, or\n * jQuery-like collections are considered empty if they have a `length` of `0`.\n * Similarly, maps and sets are considered empty if they have a `size` of `0`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is empty, else `false`.\n * @example\n *\n * _.isEmpty(null);\n * // => true\n *\n * _.isEmpty(true);\n * // => true\n *\n * _.isEmpty(1);\n * // => true\n *\n * _.isEmpty([1, 2, 3]);\n * // => false\n *\n * _.isEmpty({ 'a': 1 });\n * // => false\n */\n function isEmpty(value) {\n if (value == null) {\n return true;\n }\n if (isArrayLike(value) && (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || isBuffer(value) || isTypedArray(value) || isArguments(value))) {\n return !value.length;\n }\n var tag = getTag(value);\n if (tag == mapTag || tag == setTag) {\n return !value.size;\n }\n if (isPrototype(value)) {\n return !baseKeys(value).length;\n }\n for (var key in value) {\n if (hasOwnProperty.call(value, key)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Performs a deep comparison between two values to determine if they are\n * equivalent.\n *\n * **Note:** This method supports comparing arrays, array buffers, booleans,\n * date objects, error objects, maps, numbers, `Object` objects, regexes,\n * sets, strings, symbols, and typed arrays. `Object` objects are compared\n * by their own, not inherited, enumerable properties. Functions and DOM\n * nodes are compared by strict equality, i.e. `===`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.isEqual(object, other);\n * // => true\n *\n * object === other;\n * // => false\n */\n function isEqual(value, other) {\n return baseIsEqual(value, other);\n }\n\n /**\n * This method is like `_.isEqual` except that it accepts `customizer` which\n * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n * are handled by the method instead. The `customizer` is invoked with up to\n * six arguments: (objValue, othValue [, index|key, object, other, stack]).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, othValue) {\n * if (isGreeting(objValue) && isGreeting(othValue)) {\n * return true;\n * }\n * }\n *\n * var array = ['hello', 'goodbye'];\n * var other = ['hi', 'goodbye'];\n *\n * _.isEqualWith(array, other, customizer);\n * // => true\n */\n function isEqualWith(value, other, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n var result = customizer ? customizer(value, other) : undefined;\n return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;\n }\n\n /**\n * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,\n * `SyntaxError`, `TypeError`, or `URIError` object.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an error object, else `false`.\n * @example\n *\n * _.isError(new Error);\n * // => true\n *\n * _.isError(Error);\n * // => false\n */\n function isError(value) {\n if (!isObjectLike(value)) {\n return false;\n }\n var tag = baseGetTag(value);\n return tag == errorTag || tag == domExcTag || typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value);\n }\n\n /**\n * Checks if `value` is a finite primitive number.\n *\n * **Note:** This method is based on\n * [`Number.isFinite`](https://mdn.io/Number/isFinite).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.\n * @example\n *\n * _.isFinite(3);\n * // => true\n *\n * _.isFinite(Number.MIN_VALUE);\n * // => true\n *\n * _.isFinite(Infinity);\n * // => false\n *\n * _.isFinite('3');\n * // => false\n */\n function isFinite(value) {\n return typeof value == 'number' && nativeIsFinite(value);\n }\n\n /**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\n function isFunction(value) {\n if (!isObject(value)) {\n return false;\n }\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 9 which returns 'object' for typed arrays and other constructors.\n var tag = baseGetTag(value);\n return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;\n }\n\n /**\n * Checks if `value` is an integer.\n *\n * **Note:** This method is based on\n * [`Number.isInteger`](https://mdn.io/Number/isInteger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an integer, else `false`.\n * @example\n *\n * _.isInteger(3);\n * // => true\n *\n * _.isInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isInteger(Infinity);\n * // => false\n *\n * _.isInteger('3');\n * // => false\n */\n function isInteger(value) {\n return typeof value == 'number' && value == toInteger(value);\n }\n\n /**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\n function isLength(value) {\n return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n }\n\n /**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\n function isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n }\n\n /**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\n function isObjectLike(value) {\n return value != null && typeof value == 'object';\n }\n\n /**\n * Checks if `value` is classified as a `Map` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n * @example\n *\n * _.isMap(new Map);\n * // => true\n *\n * _.isMap(new WeakMap);\n * // => false\n */\n var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;\n\n /**\n * Performs a partial deep comparison between `object` and `source` to\n * determine if `object` contains equivalent property values.\n *\n * **Note:** This method is equivalent to `_.matches` when `source` is\n * partially applied.\n *\n * Partial comparisons will match empty array and empty object `source`\n * values against any array or object value, respectively. See `_.isEqual`\n * for a list of supported value comparisons.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n *\n * _.isMatch(object, { 'b': 2 });\n * // => true\n *\n * _.isMatch(object, { 'b': 1 });\n * // => false\n */\n function isMatch(object, source) {\n return object === source || baseIsMatch(object, source, getMatchData(source));\n }\n\n /**\n * This method is like `_.isMatch` except that it accepts `customizer` which\n * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n * are handled by the method instead. The `customizer` is invoked with five\n * arguments: (objValue, srcValue, index|key, object, source).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, srcValue) {\n * if (isGreeting(objValue) && isGreeting(srcValue)) {\n * return true;\n * }\n * }\n *\n * var object = { 'greeting': 'hello' };\n * var source = { 'greeting': 'hi' };\n *\n * _.isMatchWith(object, source, customizer);\n * // => true\n */\n function isMatchWith(object, source, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseIsMatch(object, source, getMatchData(source), customizer);\n }\n\n /**\n * Checks if `value` is `NaN`.\n *\n * **Note:** This method is based on\n * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as\n * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for\n * `undefined` and other non-number values.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n * @example\n *\n * _.isNaN(NaN);\n * // => true\n *\n * _.isNaN(new Number(NaN));\n * // => true\n *\n * isNaN(undefined);\n * // => true\n *\n * _.isNaN(undefined);\n * // => false\n */\n function isNaN(value) {\n // An `NaN` primitive is the only value that is not equal to itself.\n // Perform the `toStringTag` check first to avoid errors with some\n // ActiveX objects in IE.\n return isNumber(value) && value != +value;\n }\n\n /**\n * Checks if `value` is a pristine native function.\n *\n * **Note:** This method can't reliably detect native functions in the presence\n * of the core-js package because core-js circumvents this kind of detection.\n * Despite multiple requests, the core-js maintainer has made it clear: any\n * attempt to fix the detection will be obstructed. As a result, we're left\n * with little choice but to throw an error. Unfortunately, this also affects\n * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),\n * which rely on core-js.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n * @example\n *\n * _.isNative(Array.prototype.push);\n * // => true\n *\n * _.isNative(_);\n * // => false\n */\n function isNative(value) {\n if (isMaskable(value)) {\n throw new Error(CORE_ERROR_TEXT);\n }\n return baseIsNative(value);\n }\n\n /**\n * Checks if `value` is `null`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `null`, else `false`.\n * @example\n *\n * _.isNull(null);\n * // => true\n *\n * _.isNull(void 0);\n * // => false\n */\n function isNull(value) {\n return value === null;\n }\n\n /**\n * Checks if `value` is `null` or `undefined`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is nullish, else `false`.\n * @example\n *\n * _.isNil(null);\n * // => true\n *\n * _.isNil(void 0);\n * // => true\n *\n * _.isNil(NaN);\n * // => false\n */\n function isNil(value) {\n return value == null;\n }\n\n /**\n * Checks if `value` is classified as a `Number` primitive or object.\n *\n * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are\n * classified as numbers, use the `_.isFinite` method.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a number, else `false`.\n * @example\n *\n * _.isNumber(3);\n * // => true\n *\n * _.isNumber(Number.MIN_VALUE);\n * // => true\n *\n * _.isNumber(Infinity);\n * // => true\n *\n * _.isNumber('3');\n * // => false\n */\n function isNumber(value) {\n return typeof value == 'number' || isObjectLike(value) && baseGetTag(value) == numberTag;\n }\n\n /**\n * Checks if `value` is a plain object, that is, an object created by the\n * `Object` constructor or one with a `[[Prototype]]` of `null`.\n *\n * @static\n * @memberOf _\n * @since 0.8.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * _.isPlainObject(new Foo);\n * // => false\n *\n * _.isPlainObject([1, 2, 3]);\n * // => false\n *\n * _.isPlainObject({ 'x': 0, 'y': 0 });\n * // => true\n *\n * _.isPlainObject(Object.create(null));\n * // => true\n */\n function isPlainObject(value) {\n if (!isObjectLike(value) || baseGetTag(value) != objectTag) {\n return false;\n }\n var proto = getPrototype(value);\n if (proto === null) {\n return true;\n }\n var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;\n return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString;\n }\n\n /**\n * Checks if `value` is classified as a `RegExp` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.\n * @example\n *\n * _.isRegExp(/abc/);\n * // => true\n *\n * _.isRegExp('/abc/');\n * // => false\n */\n var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;\n\n /**\n * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754\n * double precision number which isn't the result of a rounded unsafe integer.\n *\n * **Note:** This method is based on\n * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.\n * @example\n *\n * _.isSafeInteger(3);\n * // => true\n *\n * _.isSafeInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isSafeInteger(Infinity);\n * // => false\n *\n * _.isSafeInteger('3');\n * // => false\n */\n function isSafeInteger(value) {\n return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;\n }\n\n /**\n * Checks if `value` is classified as a `Set` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a set, else `false`.\n * @example\n *\n * _.isSet(new Set);\n * // => true\n *\n * _.isSet(new WeakSet);\n * // => false\n */\n var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;\n\n /**\n * Checks if `value` is classified as a `String` primitive or object.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a string, else `false`.\n * @example\n *\n * _.isString('abc');\n * // => true\n *\n * _.isString(1);\n * // => false\n */\n function isString(value) {\n return typeof value == 'string' || !isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag;\n }\n\n /**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\n function isSymbol(value) {\n return typeof value == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;\n }\n\n /**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\n var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\n /**\n * Checks if `value` is `undefined`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.\n * @example\n *\n * _.isUndefined(void 0);\n * // => true\n *\n * _.isUndefined(null);\n * // => false\n */\n function isUndefined(value) {\n return value === undefined;\n }\n\n /**\n * Checks if `value` is classified as a `WeakMap` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.\n * @example\n *\n * _.isWeakMap(new WeakMap);\n * // => true\n *\n * _.isWeakMap(new Map);\n * // => false\n */\n function isWeakMap(value) {\n return isObjectLike(value) && getTag(value) == weakMapTag;\n }\n\n /**\n * Checks if `value` is classified as a `WeakSet` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.\n * @example\n *\n * _.isWeakSet(new WeakSet);\n * // => true\n *\n * _.isWeakSet(new Set);\n * // => false\n */\n function isWeakSet(value) {\n return isObjectLike(value) && baseGetTag(value) == weakSetTag;\n }\n\n /**\n * Checks if `value` is less than `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than `other`,\n * else `false`.\n * @see _.gt\n * @example\n *\n * _.lt(1, 3);\n * // => true\n *\n * _.lt(3, 3);\n * // => false\n *\n * _.lt(3, 1);\n * // => false\n */\n var lt = createRelationalOperation(baseLt);\n\n /**\n * Checks if `value` is less than or equal to `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than or equal to\n * `other`, else `false`.\n * @see _.gte\n * @example\n *\n * _.lte(1, 3);\n * // => true\n *\n * _.lte(3, 3);\n * // => true\n *\n * _.lte(3, 1);\n * // => false\n */\n var lte = createRelationalOperation(function (value, other) {\n return value <= other;\n });\n\n /**\n * Converts `value` to an array.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {Array} Returns the converted array.\n * @example\n *\n * _.toArray({ 'a': 1, 'b': 2 });\n * // => [1, 2]\n *\n * _.toArray('abc');\n * // => ['a', 'b', 'c']\n *\n * _.toArray(1);\n * // => []\n *\n * _.toArray(null);\n * // => []\n */\n function toArray(value) {\n if (!value) {\n return [];\n }\n if (isArrayLike(value)) {\n return isString(value) ? stringToArray(value) : copyArray(value);\n }\n if (symIterator && value[symIterator]) {\n return iteratorToArray(value[symIterator]());\n }\n var tag = getTag(value),\n func = tag == mapTag ? mapToArray : tag == setTag ? setToArray : values;\n return func(value);\n }\n\n /**\n * Converts `value` to a finite number.\n *\n * @static\n * @memberOf _\n * @since 4.12.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted number.\n * @example\n *\n * _.toFinite(3.2);\n * // => 3.2\n *\n * _.toFinite(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toFinite(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toFinite('3.2');\n * // => 3.2\n */\n function toFinite(value) {\n if (!value) {\n return value === 0 ? value : 0;\n }\n value = toNumber(value);\n if (value === INFINITY || value === -INFINITY) {\n var sign = value < 0 ? -1 : 1;\n return sign * MAX_INTEGER;\n }\n return value === value ? value : 0;\n }\n\n /**\n * Converts `value` to an integer.\n *\n * **Note:** This method is loosely based on\n * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toInteger(3.2);\n * // => 3\n *\n * _.toInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toInteger(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toInteger('3.2');\n * // => 3\n */\n function toInteger(value) {\n var result = toFinite(value),\n remainder = result % 1;\n return result === result ? remainder ? result - remainder : result : 0;\n }\n\n /**\n * Converts `value` to an integer suitable for use as the length of an\n * array-like object.\n *\n * **Note:** This method is based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toLength(3.2);\n * // => 3\n *\n * _.toLength(Number.MIN_VALUE);\n * // => 0\n *\n * _.toLength(Infinity);\n * // => 4294967295\n *\n * _.toLength('3.2');\n * // => 3\n */\n function toLength(value) {\n return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;\n }\n\n /**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\n function toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? other + '' : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = baseTrim(value);\n var isBinary = reIsBinary.test(value);\n return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;\n }\n\n /**\n * Converts `value` to a plain object flattening inherited enumerable string\n * keyed properties of `value` to own properties of the plain object.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {Object} Returns the converted plain object.\n * @example\n *\n * function Foo() {\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.assign({ 'a': 1 }, new Foo);\n * // => { 'a': 1, 'b': 2 }\n *\n * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));\n * // => { 'a': 1, 'b': 2, 'c': 3 }\n */\n function toPlainObject(value) {\n return copyObject(value, keysIn(value));\n }\n\n /**\n * Converts `value` to a safe integer. A safe integer can be compared and\n * represented correctly.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toSafeInteger(3.2);\n * // => 3\n *\n * _.toSafeInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toSafeInteger(Infinity);\n * // => 9007199254740991\n *\n * _.toSafeInteger('3.2');\n * // => 3\n */\n function toSafeInteger(value) {\n return value ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) : value === 0 ? value : 0;\n }\n\n /**\n * Converts `value` to a string. An empty string is returned for `null`\n * and `undefined` values. The sign of `-0` is preserved.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.toString(null);\n * // => ''\n *\n * _.toString(-0);\n * // => '-0'\n *\n * _.toString([1, 2, 3]);\n * // => '1,2,3'\n */\n function toString(value) {\n return value == null ? '' : baseToString(value);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Assigns own enumerable string keyed properties of source objects to the\n * destination object. Source objects are applied from left to right.\n * Subsequent sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object` and is loosely based on\n * [`Object.assign`](https://mdn.io/Object/assign).\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.assignIn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * function Bar() {\n * this.c = 3;\n * }\n *\n * Foo.prototype.b = 2;\n * Bar.prototype.d = 4;\n *\n * _.assign({ 'a': 0 }, new Foo, new Bar);\n * // => { 'a': 1, 'c': 3 }\n */\n var assign = createAssigner(function (object, source) {\n if (isPrototype(source) || isArrayLike(source)) {\n copyObject(source, keys(source), object);\n return;\n }\n for (var key in source) {\n if (hasOwnProperty.call(source, key)) {\n assignValue(object, key, source[key]);\n }\n }\n });\n\n /**\n * This method is like `_.assign` except that it iterates over own and\n * inherited source properties.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias extend\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.assign\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * function Bar() {\n * this.c = 3;\n * }\n *\n * Foo.prototype.b = 2;\n * Bar.prototype.d = 4;\n *\n * _.assignIn({ 'a': 0 }, new Foo, new Bar);\n * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }\n */\n var assignIn = createAssigner(function (object, source) {\n copyObject(source, keysIn(source), object);\n });\n\n /**\n * This method is like `_.assignIn` except that it accepts `customizer`\n * which is invoked to produce the assigned values. If `customizer` returns\n * `undefined`, assignment is handled by the method instead. The `customizer`\n * is invoked with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias extendWith\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @see _.assignWith\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignInWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var assignInWith = createAssigner(function (object, source, srcIndex, customizer) {\n copyObject(source, keysIn(source), object, customizer);\n });\n\n /**\n * This method is like `_.assign` except that it accepts `customizer`\n * which is invoked to produce the assigned values. If `customizer` returns\n * `undefined`, assignment is handled by the method instead. The `customizer`\n * is invoked with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @see _.assignInWith\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var assignWith = createAssigner(function (object, source, srcIndex, customizer) {\n copyObject(source, keys(source), object, customizer);\n });\n\n /**\n * Creates an array of values corresponding to `paths` of `object`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Array} Returns the picked values.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n *\n * _.at(object, ['a[0].b.c', 'a[1]']);\n * // => [3, 4]\n */\n var at = flatRest(baseAt);\n\n /**\n * Creates an object that inherits from the `prototype` object. If a\n * `properties` object is given, its own enumerable string keyed properties\n * are assigned to the created object.\n *\n * @static\n * @memberOf _\n * @since 2.3.0\n * @category Object\n * @param {Object} prototype The object to inherit from.\n * @param {Object} [properties] The properties to assign to the object.\n * @returns {Object} Returns the new object.\n * @example\n *\n * function Shape() {\n * this.x = 0;\n * this.y = 0;\n * }\n *\n * function Circle() {\n * Shape.call(this);\n * }\n *\n * Circle.prototype = _.create(Shape.prototype, {\n * 'constructor': Circle\n * });\n *\n * var circle = new Circle;\n * circle instanceof Circle;\n * // => true\n *\n * circle instanceof Shape;\n * // => true\n */\n function create(prototype, properties) {\n var result = baseCreate(prototype);\n return properties == null ? result : baseAssign(result, properties);\n }\n\n /**\n * Assigns own and inherited enumerable string keyed properties of source\n * objects to the destination object for all destination properties that\n * resolve to `undefined`. Source objects are applied from left to right.\n * Once a property is set, additional values of the same property are ignored.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.defaultsDeep\n * @example\n *\n * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var defaults = baseRest(function (object, sources) {\n object = Object(object);\n var index = -1;\n var length = sources.length;\n var guard = length > 2 ? sources[2] : undefined;\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n length = 1;\n }\n while (++index < length) {\n var source = sources[index];\n var props = keysIn(source);\n var propsIndex = -1;\n var propsLength = props.length;\n while (++propsIndex < propsLength) {\n var key = props[propsIndex];\n var value = object[key];\n if (value === undefined || eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) {\n object[key] = source[key];\n }\n }\n }\n return object;\n });\n\n /**\n * This method is like `_.defaults` except that it recursively assigns\n * default properties.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 3.10.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.defaults\n * @example\n *\n * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });\n * // => { 'a': { 'b': 2, 'c': 3 } }\n */\n var defaultsDeep = baseRest(function (args) {\n args.push(undefined, customDefaultsMerge);\n return apply(mergeWith, undefined, args);\n });\n\n /**\n * This method is like `_.find` except that it returns the key of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {string|undefined} Returns the key of the matched element,\n * else `undefined`.\n * @example\n *\n * var users = {\n * 'barney': { 'age': 36, 'active': true },\n * 'fred': { 'age': 40, 'active': false },\n * 'pebbles': { 'age': 1, 'active': true }\n * };\n *\n * _.findKey(users, function(o) { return o.age < 40; });\n * // => 'barney' (iteration order is not guaranteed)\n *\n * // The `_.matches` iteratee shorthand.\n * _.findKey(users, { 'age': 1, 'active': true });\n * // => 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findKey(users, ['active', false]);\n * // => 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.findKey(users, 'active');\n * // => 'barney'\n */\n function findKey(object, predicate) {\n return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);\n }\n\n /**\n * This method is like `_.findKey` except that it iterates over elements of\n * a collection in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {string|undefined} Returns the key of the matched element,\n * else `undefined`.\n * @example\n *\n * var users = {\n * 'barney': { 'age': 36, 'active': true },\n * 'fred': { 'age': 40, 'active': false },\n * 'pebbles': { 'age': 1, 'active': true }\n * };\n *\n * _.findLastKey(users, function(o) { return o.age < 40; });\n * // => returns 'pebbles' assuming `_.findKey` returns 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.findLastKey(users, { 'age': 36, 'active': true });\n * // => 'barney'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findLastKey(users, ['active', false]);\n * // => 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.findLastKey(users, 'active');\n * // => 'pebbles'\n */\n function findLastKey(object, predicate) {\n return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);\n }\n\n /**\n * Iterates over own and inherited enumerable string keyed properties of an\n * object and invokes `iteratee` for each property. The iteratee is invoked\n * with three arguments: (value, key, object). Iteratee functions may exit\n * iteration early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 0.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forInRight\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forIn(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).\n */\n function forIn(object, iteratee) {\n return object == null ? object : baseFor(object, getIteratee(iteratee, 3), keysIn);\n }\n\n /**\n * This method is like `_.forIn` except that it iterates over properties of\n * `object` in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forIn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forInRight(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.\n */\n function forInRight(object, iteratee) {\n return object == null ? object : baseForRight(object, getIteratee(iteratee, 3), keysIn);\n }\n\n /**\n * Iterates over own enumerable string keyed properties of an object and\n * invokes `iteratee` for each property. The iteratee is invoked with three\n * arguments: (value, key, object). Iteratee functions may exit iteration\n * early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 0.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forOwnRight\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forOwn(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n */\n function forOwn(object, iteratee) {\n return object && baseForOwn(object, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.forOwn` except that it iterates over properties of\n * `object` in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forOwn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forOwnRight(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.\n */\n function forOwnRight(object, iteratee) {\n return object && baseForOwnRight(object, getIteratee(iteratee, 3));\n }\n\n /**\n * Creates an array of function property names from own enumerable properties\n * of `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the function names.\n * @see _.functionsIn\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functions(new Foo);\n * // => ['a', 'b']\n */\n function functions(object) {\n return object == null ? [] : baseFunctions(object, keys(object));\n }\n\n /**\n * Creates an array of function property names from own and inherited\n * enumerable properties of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the function names.\n * @see _.functions\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functionsIn(new Foo);\n * // => ['a', 'b', 'c']\n */\n function functionsIn(object) {\n return object == null ? [] : baseFunctions(object, keysIn(object));\n }\n\n /**\n * Gets the value at `path` of `object`. If the resolved value is\n * `undefined`, the `defaultValue` is returned in its place.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.get(object, 'a[0].b.c');\n * // => 3\n *\n * _.get(object, ['a', '0', 'b', 'c']);\n * // => 3\n *\n * _.get(object, 'a.b.c', 'default');\n * // => 'default'\n */\n function get(object, path, defaultValue) {\n var result = object == null ? undefined : baseGet(object, path);\n return result === undefined ? defaultValue : result;\n }\n\n /**\n * Checks if `path` is a direct property of `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = { 'a': { 'b': 2 } };\n * var other = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.has(object, 'a');\n * // => true\n *\n * _.has(object, 'a.b');\n * // => true\n *\n * _.has(object, ['a', 'b']);\n * // => true\n *\n * _.has(other, 'a');\n * // => false\n */\n function has(object, path) {\n return object != null && hasPath(object, path, baseHas);\n }\n\n /**\n * Checks if `path` is a direct or inherited property of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.hasIn(object, 'a');\n * // => true\n *\n * _.hasIn(object, 'a.b');\n * // => true\n *\n * _.hasIn(object, ['a', 'b']);\n * // => true\n *\n * _.hasIn(object, 'b');\n * // => false\n */\n function hasIn(object, path) {\n return object != null && hasPath(object, path, baseHasIn);\n }\n\n /**\n * Creates an object composed of the inverted keys and values of `object`.\n * If `object` contains duplicate values, subsequent values overwrite\n * property assignments of previous values.\n *\n * @static\n * @memberOf _\n * @since 0.7.0\n * @category Object\n * @param {Object} object The object to invert.\n * @returns {Object} Returns the new inverted object.\n * @example\n *\n * var object = { 'a': 1, 'b': 2, 'c': 1 };\n *\n * _.invert(object);\n * // => { '1': 'c', '2': 'b' }\n */\n var invert = createInverter(function (result, value, key) {\n if (value != null && typeof value.toString != 'function') {\n value = nativeObjectToString.call(value);\n }\n result[value] = key;\n }, constant(identity));\n\n /**\n * This method is like `_.invert` except that the inverted object is generated\n * from the results of running each element of `object` thru `iteratee`. The\n * corresponding inverted value of each inverted key is an array of keys\n * responsible for generating the inverted value. The iteratee is invoked\n * with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.1.0\n * @category Object\n * @param {Object} object The object to invert.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Object} Returns the new inverted object.\n * @example\n *\n * var object = { 'a': 1, 'b': 2, 'c': 1 };\n *\n * _.invertBy(object);\n * // => { '1': ['a', 'c'], '2': ['b'] }\n *\n * _.invertBy(object, function(value) {\n * return 'group' + value;\n * });\n * // => { 'group1': ['a', 'c'], 'group2': ['b'] }\n */\n var invertBy = createInverter(function (result, value, key) {\n if (value != null && typeof value.toString != 'function') {\n value = nativeObjectToString.call(value);\n }\n if (hasOwnProperty.call(result, value)) {\n result[value].push(key);\n } else {\n result[value] = [key];\n }\n }, getIteratee);\n\n /**\n * Invokes the method at `path` of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the method to invoke.\n * @param {...*} [args] The arguments to invoke the method with.\n * @returns {*} Returns the result of the invoked method.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };\n *\n * _.invoke(object, 'a[0].b.c.slice', 1, 3);\n * // => [2, 3]\n */\n var invoke = baseRest(baseInvoke);\n\n /**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\n function keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n }\n\n /**\n * Creates an array of the own and inherited enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keysIn(new Foo);\n * // => ['a', 'b', 'c'] (iteration order is not guaranteed)\n */\n function keysIn(object) {\n return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);\n }\n\n /**\n * The opposite of `_.mapValues`; this method creates an object with the\n * same values as `object` and keys generated by running each own enumerable\n * string keyed property of `object` thru `iteratee`. The iteratee is invoked\n * with three arguments: (value, key, object).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns the new mapped object.\n * @see _.mapValues\n * @example\n *\n * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {\n * return key + value;\n * });\n * // => { 'a1': 1, 'b2': 2 }\n */\n function mapKeys(object, iteratee) {\n var result = {};\n iteratee = getIteratee(iteratee, 3);\n baseForOwn(object, function (value, key, object) {\n baseAssignValue(result, iteratee(value, key, object), value);\n });\n return result;\n }\n\n /**\n * Creates an object with the same keys as `object` and values generated\n * by running each own enumerable string keyed property of `object` thru\n * `iteratee`. The iteratee is invoked with three arguments:\n * (value, key, object).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns the new mapped object.\n * @see _.mapKeys\n * @example\n *\n * var users = {\n * 'fred': { 'user': 'fred', 'age': 40 },\n * 'pebbles': { 'user': 'pebbles', 'age': 1 }\n * };\n *\n * _.mapValues(users, function(o) { return o.age; });\n * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n *\n * // The `_.property` iteratee shorthand.\n * _.mapValues(users, 'age');\n * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n */\n function mapValues(object, iteratee) {\n var result = {};\n iteratee = getIteratee(iteratee, 3);\n baseForOwn(object, function (value, key, object) {\n baseAssignValue(result, key, iteratee(value, key, object));\n });\n return result;\n }\n\n /**\n * This method is like `_.assign` except that it recursively merges own and\n * inherited enumerable string keyed properties of source objects into the\n * destination object. Source properties that resolve to `undefined` are\n * skipped if a destination value exists. Array and plain object properties\n * are merged recursively. Other objects and value types are overridden by\n * assignment. Source objects are applied from left to right. Subsequent\n * sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {\n * 'a': [{ 'b': 2 }, { 'd': 4 }]\n * };\n *\n * var other = {\n * 'a': [{ 'c': 3 }, { 'e': 5 }]\n * };\n *\n * _.merge(object, other);\n * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }\n */\n var merge = createAssigner(function (object, source, srcIndex) {\n baseMerge(object, source, srcIndex);\n });\n\n /**\n * This method is like `_.merge` except that it accepts `customizer` which\n * is invoked to produce the merged values of the destination and source\n * properties. If `customizer` returns `undefined`, merging is handled by the\n * method instead. The `customizer` is invoked with six arguments:\n * (objValue, srcValue, key, object, source, stack).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} customizer The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function customizer(objValue, srcValue) {\n * if (_.isArray(objValue)) {\n * return objValue.concat(srcValue);\n * }\n * }\n *\n * var object = { 'a': [1], 'b': [2] };\n * var other = { 'a': [3], 'b': [4] };\n *\n * _.mergeWith(object, other, customizer);\n * // => { 'a': [1, 3], 'b': [2, 4] }\n */\n var mergeWith = createAssigner(function (object, source, srcIndex, customizer) {\n baseMerge(object, source, srcIndex, customizer);\n });\n\n /**\n * The opposite of `_.pick`; this method creates an object composed of the\n * own and inherited enumerable property paths of `object` that are not omitted.\n *\n * **Note:** This method is considerably slower than `_.pick`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to omit.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omit(object, ['a', 'c']);\n * // => { 'b': '2' }\n */\n var omit = flatRest(function (object, paths) {\n var result = {};\n if (object == null) {\n return result;\n }\n var isDeep = false;\n paths = arrayMap(paths, function (path) {\n path = castPath(path, object);\n isDeep || (isDeep = path.length > 1);\n return path;\n });\n copyObject(object, getAllKeysIn(object), result);\n if (isDeep) {\n result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);\n }\n var length = paths.length;\n while (length--) {\n baseUnset(result, paths[length]);\n }\n return result;\n });\n\n /**\n * The opposite of `_.pickBy`; this method creates an object composed of\n * the own and inherited enumerable string keyed properties of `object` that\n * `predicate` doesn't return truthy for. The predicate is invoked with two\n * arguments: (value, key).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The source object.\n * @param {Function} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omitBy(object, _.isNumber);\n * // => { 'b': '2' }\n */\n function omitBy(object, predicate) {\n return pickBy(object, negate(getIteratee(predicate)));\n }\n\n /**\n * Creates an object composed of the picked `object` properties.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pick(object, ['a', 'c']);\n * // => { 'a': 1, 'c': 3 }\n */\n var pick = flatRest(function (object, paths) {\n return object == null ? {} : basePick(object, paths);\n });\n\n /**\n * Creates an object composed of the `object` properties `predicate` returns\n * truthy for. The predicate is invoked with two arguments: (value, key).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The source object.\n * @param {Function} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pickBy(object, _.isNumber);\n * // => { 'a': 1, 'c': 3 }\n */\n function pickBy(object, predicate) {\n if (object == null) {\n return {};\n }\n var props = arrayMap(getAllKeysIn(object), function (prop) {\n return [prop];\n });\n predicate = getIteratee(predicate);\n return basePickBy(object, props, function (value, path) {\n return predicate(value, path[0]);\n });\n }\n\n /**\n * This method is like `_.get` except that if the resolved value is a\n * function it's invoked with the `this` binding of its parent object and\n * its result is returned.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to resolve.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };\n *\n * _.result(object, 'a[0].b.c1');\n * // => 3\n *\n * _.result(object, 'a[0].b.c2');\n * // => 4\n *\n * _.result(object, 'a[0].b.c3', 'default');\n * // => 'default'\n *\n * _.result(object, 'a[0].b.c3', _.constant('default'));\n * // => 'default'\n */\n function result(object, path, defaultValue) {\n path = castPath(path, object);\n var index = -1,\n length = path.length;\n\n // Ensure the loop is entered when path is empty.\n if (!length) {\n length = 1;\n object = undefined;\n }\n while (++index < length) {\n var value = object == null ? undefined : object[toKey(path[index])];\n if (value === undefined) {\n index = length;\n value = defaultValue;\n }\n object = isFunction(value) ? value.call(object) : value;\n }\n return object;\n }\n\n /**\n * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,\n * it's created. Arrays are created for missing index properties while objects\n * are created for all other missing properties. Use `_.setWith` to customize\n * `path` creation.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.set(object, 'a[0].b.c', 4);\n * console.log(object.a[0].b.c);\n * // => 4\n *\n * _.set(object, ['x', '0', 'y', 'z'], 5);\n * console.log(object.x[0].y.z);\n * // => 5\n */\n function set(object, path, value) {\n return object == null ? object : baseSet(object, path, value);\n }\n\n /**\n * This method is like `_.set` except that it accepts `customizer` which is\n * invoked to produce the objects of `path`. If `customizer` returns `undefined`\n * path creation is handled by the method instead. The `customizer` is invoked\n * with three arguments: (nsValue, key, nsObject).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {};\n *\n * _.setWith(object, '[0][1]', 'a', Object);\n * // => { '0': { '1': 'a' } }\n */\n function setWith(object, path, value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return object == null ? object : baseSet(object, path, value, customizer);\n }\n\n /**\n * Creates an array of own enumerable string keyed-value pairs for `object`\n * which can be consumed by `_.fromPairs`. If `object` is a map or set, its\n * entries are returned.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias entries\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the key-value pairs.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.toPairs(new Foo);\n * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)\n */\n var toPairs = createToPairs(keys);\n\n /**\n * Creates an array of own and inherited enumerable string keyed-value pairs\n * for `object` which can be consumed by `_.fromPairs`. If `object` is a map\n * or set, its entries are returned.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias entriesIn\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the key-value pairs.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.toPairsIn(new Foo);\n * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)\n */\n var toPairsIn = createToPairs(keysIn);\n\n /**\n * An alternative to `_.reduce`; this method transforms `object` to a new\n * `accumulator` object which is the result of running each of its own\n * enumerable string keyed properties thru `iteratee`, with each invocation\n * potentially mutating the `accumulator` object. If `accumulator` is not\n * provided, a new object with the same `[[Prototype]]` will be used. The\n * iteratee is invoked with four arguments: (accumulator, value, key, object).\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 1.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The custom accumulator value.\n * @returns {*} Returns the accumulated value.\n * @example\n *\n * _.transform([2, 3, 4], function(result, n) {\n * result.push(n *= n);\n * return n % 2 == 0;\n * }, []);\n * // => [4, 9]\n *\n * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n * (result[value] || (result[value] = [])).push(key);\n * }, {});\n * // => { '1': ['a', 'c'], '2': ['b'] }\n */\n function transform(object, iteratee, accumulator) {\n var isArr = isArray(object),\n isArrLike = isArr || isBuffer(object) || isTypedArray(object);\n iteratee = getIteratee(iteratee, 4);\n if (accumulator == null) {\n var Ctor = object && object.constructor;\n if (isArrLike) {\n accumulator = isArr ? new Ctor() : [];\n } else if (isObject(object)) {\n accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};\n } else {\n accumulator = {};\n }\n }\n (isArrLike ? arrayEach : baseForOwn)(object, function (value, index, object) {\n return iteratee(accumulator, value, index, object);\n });\n return accumulator;\n }\n\n /**\n * Removes the property at `path` of `object`.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to unset.\n * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 7 } }] };\n * _.unset(object, 'a[0].b.c');\n * // => true\n *\n * console.log(object);\n * // => { 'a': [{ 'b': {} }] };\n *\n * _.unset(object, ['a', '0', 'b', 'c']);\n * // => true\n *\n * console.log(object);\n * // => { 'a': [{ 'b': {} }] };\n */\n function unset(object, path) {\n return object == null ? true : baseUnset(object, path);\n }\n\n /**\n * This method is like `_.set` except that accepts `updater` to produce the\n * value to set. Use `_.updateWith` to customize `path` creation. The `updater`\n * is invoked with one argument: (value).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {Function} updater The function to produce the updated value.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.update(object, 'a[0].b.c', function(n) { return n * n; });\n * console.log(object.a[0].b.c);\n * // => 9\n *\n * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });\n * console.log(object.x[0].y.z);\n * // => 0\n */\n function update(object, path, updater) {\n return object == null ? object : baseUpdate(object, path, castFunction(updater));\n }\n\n /**\n * This method is like `_.update` except that it accepts `customizer` which is\n * invoked to produce the objects of `path`. If `customizer` returns `undefined`\n * path creation is handled by the method instead. The `customizer` is invoked\n * with three arguments: (nsValue, key, nsObject).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {Function} updater The function to produce the updated value.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {};\n *\n * _.updateWith(object, '[0][1]', _.constant('a'), Object);\n * // => { '0': { '1': 'a' } }\n */\n function updateWith(object, path, updater, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);\n }\n\n /**\n * Creates an array of the own enumerable string keyed property values of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property values.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.values(new Foo);\n * // => [1, 2] (iteration order is not guaranteed)\n *\n * _.values('hi');\n * // => ['h', 'i']\n */\n function values(object) {\n return object == null ? [] : baseValues(object, keys(object));\n }\n\n /**\n * Creates an array of the own and inherited enumerable string keyed property\n * values of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property values.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.valuesIn(new Foo);\n * // => [1, 2, 3] (iteration order is not guaranteed)\n */\n function valuesIn(object) {\n return object == null ? [] : baseValues(object, keysIn(object));\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Clamps `number` within the inclusive `lower` and `upper` bounds.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Number\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n * @example\n *\n * _.clamp(-10, -5, 5);\n * // => -5\n *\n * _.clamp(10, -5, 5);\n * // => 5\n */\n function clamp(number, lower, upper) {\n if (upper === undefined) {\n upper = lower;\n lower = undefined;\n }\n if (upper !== undefined) {\n upper = toNumber(upper);\n upper = upper === upper ? upper : 0;\n }\n if (lower !== undefined) {\n lower = toNumber(lower);\n lower = lower === lower ? lower : 0;\n }\n return baseClamp(toNumber(number), lower, upper);\n }\n\n /**\n * Checks if `n` is between `start` and up to, but not including, `end`. If\n * `end` is not specified, it's set to `start` with `start` then set to `0`.\n * If `start` is greater than `end` the params are swapped to support\n * negative ranges.\n *\n * @static\n * @memberOf _\n * @since 3.3.0\n * @category Number\n * @param {number} number The number to check.\n * @param {number} [start=0] The start of the range.\n * @param {number} end The end of the range.\n * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n * @see _.range, _.rangeRight\n * @example\n *\n * _.inRange(3, 2, 4);\n * // => true\n *\n * _.inRange(4, 8);\n * // => true\n *\n * _.inRange(4, 2);\n * // => false\n *\n * _.inRange(2, 2);\n * // => false\n *\n * _.inRange(1.2, 2);\n * // => true\n *\n * _.inRange(5.2, 4);\n * // => false\n *\n * _.inRange(-3, -2, -6);\n * // => true\n */\n function inRange(number, start, end) {\n start = toFinite(start);\n if (end === undefined) {\n end = start;\n start = 0;\n } else {\n end = toFinite(end);\n }\n number = toNumber(number);\n return baseInRange(number, start, end);\n }\n\n /**\n * Produces a random number between the inclusive `lower` and `upper` bounds.\n * If only one argument is provided a number between `0` and the given number\n * is returned. If `floating` is `true`, or either `lower` or `upper` are\n * floats, a floating-point number is returned instead of an integer.\n *\n * **Note:** JavaScript follows the IEEE-754 standard for resolving\n * floating-point values which can produce unexpected results.\n *\n * @static\n * @memberOf _\n * @since 0.7.0\n * @category Number\n * @param {number} [lower=0] The lower bound.\n * @param {number} [upper=1] The upper bound.\n * @param {boolean} [floating] Specify returning a floating-point number.\n * @returns {number} Returns the random number.\n * @example\n *\n * _.random(0, 5);\n * // => an integer between 0 and 5\n *\n * _.random(5);\n * // => also an integer between 0 and 5\n *\n * _.random(5, true);\n * // => a floating-point number between 0 and 5\n *\n * _.random(1.2, 5.2);\n * // => a floating-point number between 1.2 and 5.2\n */\n function random(lower, upper, floating) {\n if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {\n upper = floating = undefined;\n }\n if (floating === undefined) {\n if (typeof upper == 'boolean') {\n floating = upper;\n upper = undefined;\n } else if (typeof lower == 'boolean') {\n floating = lower;\n lower = undefined;\n }\n }\n if (lower === undefined && upper === undefined) {\n lower = 0;\n upper = 1;\n } else {\n lower = toFinite(lower);\n if (upper === undefined) {\n upper = lower;\n lower = 0;\n } else {\n upper = toFinite(upper);\n }\n }\n if (lower > upper) {\n var temp = lower;\n lower = upper;\n upper = temp;\n }\n if (floating || lower % 1 || upper % 1) {\n var rand = nativeRandom();\n return nativeMin(lower + rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1))), upper);\n }\n return baseRandom(lower, upper);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the camel cased string.\n * @example\n *\n * _.camelCase('Foo Bar');\n * // => 'fooBar'\n *\n * _.camelCase('--foo-bar--');\n * // => 'fooBar'\n *\n * _.camelCase('__FOO_BAR__');\n * // => 'fooBar'\n */\n var camelCase = createCompounder(function (result, word, index) {\n word = word.toLowerCase();\n return result + (index ? capitalize(word) : word);\n });\n\n /**\n * Converts the first character of `string` to upper case and the remaining\n * to lower case.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to capitalize.\n * @returns {string} Returns the capitalized string.\n * @example\n *\n * _.capitalize('FRED');\n * // => 'Fred'\n */\n function capitalize(string) {\n return upperFirst(toString(string).toLowerCase());\n }\n\n /**\n * Deburrs `string` by converting\n * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)\n * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)\n * letters to basic Latin letters and removing\n * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to deburr.\n * @returns {string} Returns the deburred string.\n * @example\n *\n * _.deburr('déjà vu');\n * // => 'deja vu'\n */\n function deburr(string) {\n string = toString(string);\n return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');\n }\n\n /**\n * Checks if `string` ends with the given target string.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to inspect.\n * @param {string} [target] The string to search for.\n * @param {number} [position=string.length] The position to search up to.\n * @returns {boolean} Returns `true` if `string` ends with `target`,\n * else `false`.\n * @example\n *\n * _.endsWith('abc', 'c');\n * // => true\n *\n * _.endsWith('abc', 'b');\n * // => false\n *\n * _.endsWith('abc', 'b', 2);\n * // => true\n */\n function endsWith(string, target, position) {\n string = toString(string);\n target = baseToString(target);\n var length = string.length;\n position = position === undefined ? length : baseClamp(toInteger(position), 0, length);\n var end = position;\n position -= target.length;\n return position >= 0 && string.slice(position, end) == target;\n }\n\n /**\n * Converts the characters \"&\", \"<\", \">\", '\"', and \"'\" in `string` to their\n * corresponding HTML entities.\n *\n * **Note:** No other characters are escaped. To escape additional\n * characters use a third-party library like [_he_](https://mths.be/he).\n *\n * Though the \">\" character is escaped for symmetry, characters like\n * \">\" and \"/\" don't need escaping in HTML and have no special meaning\n * unless they're part of a tag or unquoted attribute value. See\n * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)\n * (under \"semi-related fun fact\") for more details.\n *\n * When working with HTML you should always\n * [quote attribute values](http://wonko.com/post/html-escaping) to reduce\n * XSS vectors.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escape('fred, barney, & pebbles');\n * // => 'fred, barney, & pebbles'\n */\n function escape(string) {\n string = toString(string);\n return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string;\n }\n\n /**\n * Escapes the `RegExp` special characters \"^\", \"$\", \"\\\", \".\", \"*\", \"+\",\n * \"?\", \"(\", \")\", \"[\", \"]\", \"{\", \"}\", and \"|\" in `string`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escapeRegExp('[lodash](https://lodash.com/)');\n * // => '\\[lodash\\]\\(https://lodash\\.com/\\)'\n */\n function escapeRegExp(string) {\n string = toString(string);\n return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, '\\\\$&') : string;\n }\n\n /**\n * Converts `string` to\n * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the kebab cased string.\n * @example\n *\n * _.kebabCase('Foo Bar');\n * // => 'foo-bar'\n *\n * _.kebabCase('fooBar');\n * // => 'foo-bar'\n *\n * _.kebabCase('__FOO_BAR__');\n * // => 'foo-bar'\n */\n var kebabCase = createCompounder(function (result, word, index) {\n return result + (index ? '-' : '') + word.toLowerCase();\n });\n\n /**\n * Converts `string`, as space separated words, to lower case.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the lower cased string.\n * @example\n *\n * _.lowerCase('--Foo-Bar--');\n * // => 'foo bar'\n *\n * _.lowerCase('fooBar');\n * // => 'foo bar'\n *\n * _.lowerCase('__FOO_BAR__');\n * // => 'foo bar'\n */\n var lowerCase = createCompounder(function (result, word, index) {\n return result + (index ? ' ' : '') + word.toLowerCase();\n });\n\n /**\n * Converts the first character of `string` to lower case.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.lowerFirst('Fred');\n * // => 'fred'\n *\n * _.lowerFirst('FRED');\n * // => 'fRED'\n */\n var lowerFirst = createCaseFirst('toLowerCase');\n\n /**\n * Pads `string` on the left and right sides if it's shorter than `length`.\n * Padding characters are truncated if they can't be evenly divided by `length`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.pad('abc', 8);\n * // => ' abc '\n *\n * _.pad('abc', 8, '_-');\n * // => '_-abc_-_'\n *\n * _.pad('abc', 3);\n * // => 'abc'\n */\n function pad(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n var strLength = length ? stringSize(string) : 0;\n if (!length || strLength >= length) {\n return string;\n }\n var mid = (length - strLength) / 2;\n return createPadding(nativeFloor(mid), chars) + string + createPadding(nativeCeil(mid), chars);\n }\n\n /**\n * Pads `string` on the right side if it's shorter than `length`. Padding\n * characters are truncated if they exceed `length`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.padEnd('abc', 6);\n * // => 'abc '\n *\n * _.padEnd('abc', 6, '_-');\n * // => 'abc_-_'\n *\n * _.padEnd('abc', 3);\n * // => 'abc'\n */\n function padEnd(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n var strLength = length ? stringSize(string) : 0;\n return length && strLength < length ? string + createPadding(length - strLength, chars) : string;\n }\n\n /**\n * Pads `string` on the left side if it's shorter than `length`. Padding\n * characters are truncated if they exceed `length`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.padStart('abc', 6);\n * // => ' abc'\n *\n * _.padStart('abc', 6, '_-');\n * // => '_-_abc'\n *\n * _.padStart('abc', 3);\n * // => 'abc'\n */\n function padStart(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n var strLength = length ? stringSize(string) : 0;\n return length && strLength < length ? createPadding(length - strLength, chars) + string : string;\n }\n\n /**\n * Converts `string` to an integer of the specified radix. If `radix` is\n * `undefined` or `0`, a `radix` of `10` is used unless `value` is a\n * hexadecimal, in which case a `radix` of `16` is used.\n *\n * **Note:** This method aligns with the\n * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category String\n * @param {string} string The string to convert.\n * @param {number} [radix=10] The radix to interpret `value` by.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.parseInt('08');\n * // => 8\n *\n * _.map(['6', '08', '10'], _.parseInt);\n * // => [6, 8, 10]\n */\n function parseInt(string, radix, guard) {\n if (guard || radix == null) {\n radix = 0;\n } else if (radix) {\n radix = +radix;\n }\n return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);\n }\n\n /**\n * Repeats the given string `n` times.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to repeat.\n * @param {number} [n=1] The number of times to repeat the string.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {string} Returns the repeated string.\n * @example\n *\n * _.repeat('*', 3);\n * // => '***'\n *\n * _.repeat('abc', 2);\n * // => 'abcabc'\n *\n * _.repeat('abc', 0);\n * // => ''\n */\n function repeat(string, n, guard) {\n if (guard ? isIterateeCall(string, n, guard) : n === undefined) {\n n = 1;\n } else {\n n = toInteger(n);\n }\n return baseRepeat(toString(string), n);\n }\n\n /**\n * Replaces matches for `pattern` in `string` with `replacement`.\n *\n * **Note:** This method is based on\n * [`String#replace`](https://mdn.io/String/replace).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to modify.\n * @param {RegExp|string} pattern The pattern to replace.\n * @param {Function|string} replacement The match replacement.\n * @returns {string} Returns the modified string.\n * @example\n *\n * _.replace('Hi Fred', 'Fred', 'Barney');\n * // => 'Hi Barney'\n */\n function replace() {\n var args = arguments,\n string = toString(args[0]);\n return args.length < 3 ? string : string.replace(args[1], args[2]);\n }\n\n /**\n * Converts `string` to\n * [snake case](https://en.wikipedia.org/wiki/Snake_case).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the snake cased string.\n * @example\n *\n * _.snakeCase('Foo Bar');\n * // => 'foo_bar'\n *\n * _.snakeCase('fooBar');\n * // => 'foo_bar'\n *\n * _.snakeCase('--FOO-BAR--');\n * // => 'foo_bar'\n */\n var snakeCase = createCompounder(function (result, word, index) {\n return result + (index ? '_' : '') + word.toLowerCase();\n });\n\n /**\n * Splits `string` by `separator`.\n *\n * **Note:** This method is based on\n * [`String#split`](https://mdn.io/String/split).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to split.\n * @param {RegExp|string} separator The separator pattern to split by.\n * @param {number} [limit] The length to truncate results to.\n * @returns {Array} Returns the string segments.\n * @example\n *\n * _.split('a-b-c', '-', 2);\n * // => ['a', 'b']\n */\n function split(string, separator, limit) {\n if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {\n separator = limit = undefined;\n }\n limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;\n if (!limit) {\n return [];\n }\n string = toString(string);\n if (string && (typeof separator == 'string' || separator != null && !isRegExp(separator))) {\n separator = baseToString(separator);\n if (!separator && hasUnicode(string)) {\n return castSlice(stringToArray(string), 0, limit);\n }\n }\n return string.split(separator, limit);\n }\n\n /**\n * Converts `string` to\n * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).\n *\n * @static\n * @memberOf _\n * @since 3.1.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the start cased string.\n * @example\n *\n * _.startCase('--foo-bar--');\n * // => 'Foo Bar'\n *\n * _.startCase('fooBar');\n * // => 'Foo Bar'\n *\n * _.startCase('__FOO_BAR__');\n * // => 'FOO BAR'\n */\n var startCase = createCompounder(function (result, word, index) {\n return result + (index ? ' ' : '') + upperFirst(word);\n });\n\n /**\n * Checks if `string` starts with the given target string.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to inspect.\n * @param {string} [target] The string to search for.\n * @param {number} [position=0] The position to search from.\n * @returns {boolean} Returns `true` if `string` starts with `target`,\n * else `false`.\n * @example\n *\n * _.startsWith('abc', 'a');\n * // => true\n *\n * _.startsWith('abc', 'b');\n * // => false\n *\n * _.startsWith('abc', 'b', 1);\n * // => true\n */\n function startsWith(string, target, position) {\n string = toString(string);\n position = position == null ? 0 : baseClamp(toInteger(position), 0, string.length);\n target = baseToString(target);\n return string.slice(position, position + target.length) == target;\n }\n\n /**\n * Creates a compiled template function that can interpolate data properties\n * in \"interpolate\" delimiters, HTML-escape interpolated data properties in\n * \"escape\" delimiters, and execute JavaScript in \"evaluate\" delimiters. Data\n * properties may be accessed as free variables in the template. If a setting\n * object is given, it takes precedence over `_.templateSettings` values.\n *\n * **Note:** In the development build `_.template` utilizes\n * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)\n * for easier debugging.\n *\n * For more information on precompiling templates see\n * [lodash's custom builds documentation](https://lodash.com/custom-builds).\n *\n * For more information on Chrome extension sandboxes see\n * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category String\n * @param {string} [string=''] The template string.\n * @param {Object} [options={}] The options object.\n * @param {RegExp} [options.escape=_.templateSettings.escape]\n * The HTML \"escape\" delimiter.\n * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]\n * The \"evaluate\" delimiter.\n * @param {Object} [options.imports=_.templateSettings.imports]\n * An object to import into the template as free variables.\n * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]\n * The \"interpolate\" delimiter.\n * @param {string} [options.sourceURL='lodash.templateSources[n]']\n * The sourceURL of the compiled template.\n * @param {string} [options.variable='obj']\n * The data object variable name.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the compiled template function.\n * @example\n *\n * // Use the \"interpolate\" delimiter to create a compiled template.\n * var compiled = _.template('hello <%= user %>!');\n * compiled({ 'user': 'fred' });\n * // => 'hello fred!'\n *\n * // Use the HTML \"escape\" delimiter to escape data property values.\n * var compiled = _.template('<%- value %>');\n * compiled({ 'value': '`;\n this.codeForm.controls.code.setValue(link);\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.widget_saved);\n this.codeForm.updateValueAndValidity();\n this.channel.status = '';\n const channelData = {\n ...this.channel,\n name: data.widget_name,\n x_data: {\n link: link,\n key: res.key,\n ...data,\n },\n };\n this.channelService.update(this.channel.id, channelData).subscribe((res: any) => {\n\n });\n });\n }\n if(!this.inputs.length) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_empty_form);\n }\n this.additionalSettingsForm.markAllAsTouched();\n }\n\n onGetAllFunnels() {\n this.crmService.getAllFunnels({ type: FUNNEL_TYPE.DEAL })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (funnels: IFunnel[]) => {\n this.allowedFunnels = [...funnels];\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n copyToClipboard() {\n const codeValue = this.codeForm.controls.code.value;\n if (codeValue) {\n navigator.clipboard.writeText(codeValue).then(() => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.the_code_is_copied_to_the_clipboard);\n }).catch(err => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.failed_to_copy_code);\n });\n }\n }\n\n closeAddInput() {\n this.showAddInput = false;\n this.inputForm.reset();\n }\n\n drop(event: CdkDragDrop) {\n moveItemInArray(this.inputs, event.previousIndex, event.currentIndex);\n if (this.isEditInput) {\n this.editInput(event.currentIndex);\n }\n }\n\n disableTimeOut(){\n this.additionalSettingsForm.controls.time.setValue('now');\n this.additionalSettingsForm.controls.is_timeout.setValue(false);\n }\n\n editInput(index: number) {\n if (this.inputIndex === index) {\n this.showAddInput = !this.showAddInput;\n } else {\n this.showAddInput = true;\n this.isEditInput = true;\n this.inputForm.controls.name.setValue(this.inputs[index].name);\n this.inputForm.controls.type.setValue(this.inputs[index].type);\n this.inputForm.controls.placeholder.setValue(this.inputs[index].placeholder);\n this.inputForm.controls.is_required.setValue(this.inputs[index].is_required);\n }\n\n this.inputIndex = this.showAddInput ? index : null;\n\n if (!this.showAddInput) {\n this.isEditInput = false;\n this.inputForm.reset();\n }\n }\n\n\n saveEditInput() {\n this.inputForm.markAllAsTouched();\n if (this.inputForm.valid) {\n const input = this.inputForm.getRawValue();\n\n if (this.inputIndex !== null) {\n this.inputs[this.inputIndex] = input;\n }\n\n this.inputForm.reset();\n this.showAddInput = false;\n this.isEditInput = false;\n this.inputIndex = null;\n }\n }\n\n}\n","
\n
\n
\n {{ 'edit_widget' | translate }}\n \n close\n \n
\n \n
\n
\n
\n \n \n {{ 'widget_name' | translate }}\n \n \n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n \n \n \n \n {{ 'embed_code' | translate }}\n \n \n {{ 'copy_to_clipboard' | translate }}\n \n \n\n\n\n\n\n\n\n\n\n\n\n\n \n
\n
\n \n \n {{ 'general_appearance' | translate }}:\n \n
\n chat\n \n
\n \n
\n \n \n
\n
\n {{ 'background_color' | translate }}\n \n
\n
\n {{ 'icon_color' | translate }}\n \n
\n
\n
\n
\n \n {{ 'choose_position' | translate }}:\n \n
\n
\n \n \n {{ position.icon }}\n \n \n
\n
\n \n
\n \n {{ 'show_time' | translate }}:\n \n
\n
\n \n \n
\n
\n \n \n \n \n
\n
\n \n
\n \n {{ 'form' | translate }}:\n \n \n
\n
\n
\n {{ input.name }}*\n
\n \n edit\n \n delete\n \n
\n
\n
\n
\n
\n
\n
\n
{{ 'input_name' | translate }}: *
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n
\n
{{ 'type' | translate }}: *
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n
\n
{{ 'placeholder' | translate }}:
\n \n
\n
\n {{ 'required' | translate }}\n
\n
\n {{ 'close' | translate }}\n {{ 'add' | translate }}\n {{ 'save' | translate }}\n
\n
\n \n add\n \n \n
\n \n {{ 'mobile_devices' | translate }}:\n \n {{ 'do_not_show' | translate }}\n \n \n \n \n {{ 'created_in_jecrm' | translate }}:\n \n {{ 'show_caption' | translate }}\n \n \n \n
\n
\n
\n
\n \n
\n {{ 'cancel' | translate }}\n \n
\n {{ 'save' | translate }}\n
\n
\n
\n","import { FormControl } from '@angular/forms';\nimport { DropdownAction } from '../../../../dropdown/dropdown.interface';\n\n\nexport const contactActions: DropdownAction[] = [\n {\n icon: 'create',\n label: 'create_contact',\n value: 'CREATE',\n translate: true\n }\n]\n\nexport interface CardForm {\n name: FormControl;\n price: FormControl;\n funnel_id: FormControl;\n currency: FormControl;\n bg_color: FormControl;\n tx_color: FormControl;\n comment: FormControl;\n}\n\nexport interface ContactForm {\n contact_id: FormControl;\n name: FormControl;\n phone: FormControl;\n email: FormControl;\n address: FormControl;\n}\n\nexport interface ContactInfoList {\n icon: string;\n items: any[];\n}\n","import { ALIAS_EVENTS, ALIAS } from '../../enum/constants';\n\nexport enum TYPE {\n DEAL = 'DEAL',\n LEAD = 'LEAD',\n}\n\nexport enum EVENT {\n ADDED = 'ADDED',\n CREATE = 'CREATE',\n UPDATE = 'UPDATE',\n DELETE = 'DELETE',\n REMOVED = 'REMOVED',\n}\n\nexport const alias: Record = {\n 'column': ALIAS.COLUMN,\n 'column_id': ALIAS.COLUMN_ID,\n 'contact': ALIAS.CONTACT,\n 'contact_id': ALIAS.CONTACT_ID,\n 'funnel': ALIAS.FUNNEL,\n 'funnel_id':ALIAS.FUNNEL_ID,\n 'lead': ALIAS.LEAD,\n 'deal':ALIAS.DEAL,\n 'price':ALIAS.PRICE,\n 'currency': ALIAS.CURRENCY,\n 'title': ALIAS.TITLE,\n 'name': ALIAS.NAME,\n 'bg_color': ALIAS.BG_COLOR,\n 'text_color': ALIAS.TEXT_COLOR,\n 'comment': ALIAS.COMMENT\n}\n\nexport const alias_ = {\n}\n\nexport const alias_events: any = {\n [EVENT.CREATE]: ALIAS_EVENTS.CREATED,\n [EVENT.UPDATE]: ALIAS_EVENTS.UPDATED,\n [EVENT.ADDED]: ALIAS_EVENTS.ADDED,\n [EVENT.REMOVED]: ALIAS_EVENTS.REMOVED,\n}\n\nexport interface IActivity {\n entity_uuid: string;\n entity_type: TYPE;\n event_type: EVENT;\n old_data: any;\n new_data: any;\n}\n","import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { Currency, IColumn, IColumnCard, IContact, IError, IFunnel } from '../../../../../interfaces';\nimport { Subject, takeUntil } from 'rxjs';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { CardForm, contactActions, ContactForm, ContactInfoList } from './deal.interface';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { CURRENCY, DEFAULT_ERROR, ERROR_MESSAGES, FUNNEL_TYPE, languages } from '../../../../../enum/constants';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { _filter, parseCurrency } from '../../../../../helpers';\nimport { ContactService } from '../../../../../services/contact.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { CustomAttributesService } from '../../../../../services/custom-attributes.service';\nimport { ICA } from '../../../../../tabs/tab-deals-settings-fields/tab-deals-settings-fields.interface';\nimport { TYPE } from '../../../../activity/activity.interface';\nimport * as _ from 'lodash';\nimport { Router } from '@angular/router';\n\n\n@Component({\n selector: 'app-deal-details',\n templateUrl: './deal.component.html',\n styleUrls: ['./deal.component.scss']\n})\nexport class DealComponent implements OnInit, OnDestroy {\n// Destroys\n private readonly destroy$: Subject = new Subject();\n\n// Form\n public cardForm!: FormGroup;\n public contactForm!: FormGroup;\n public cardCAForm!: FormGroup;\n public copyCardForm!: any;\n public copyCardCAForm!: any;\n\n// Var-s\n protected readonly CURRENCY: Currency[] = CURRENCY;\n protected readonly parseCurrency = parseCurrency;\n protected readonly _filter = _filter;\n protected readonly contactActions = contactActions;\n\n public cardId!: string;\n public columns: IColumn[] = [];\n public customAttributes: ICA[] = [];\n\n public contacts: IContact[] = [];\n public funnels: IFunnel[] = [];\n public deal!: IColumnCard | any;\n public hoveredBgColor: string | null = null;\n\n public hoveredTextColor: string | null = null;\n public hoveredIndex: number = 0;\n\n public contactPage: number = 1;\n public searchedContact: string = '';\n\n public current_id!: any;\n\n public isContactCreate: boolean = false;\n public isContactUpdate: boolean = false;\n public isContactSelect: boolean = false;\n\n public isFormSubmitted: boolean = false;\n public isAllDeactivate: boolean = false;\n\n public isHasChange: boolean = false;\n public isActiveSomeInput: boolean = false;\n\n public currentColumn!: IColumn | any;\n public currentContact!: IContact | any;\n\n public contactInfoList: ContactInfoList[] = [];\n\n public selectedTab!: any;\n\n private colorTimer!: NodeJS.Timeout;\n\n public tabs = [\n {\n name: 'general',\n value: '',\n count: 0,\n active: true,\n translate: true,\n },\n ];\n\n public colors: any[] = ['#fbc6bd', '#bde2ff', '#ffb4ce', '#d1ffc3', '#fbe9b8', '#f8edae',\n '#a3e1ce', '#b7eded', '#b4b9f3', '#ceb6ef', '#dfe6e9', '#ffffff'];\n\n @ViewChild('updateInput') updateInput!: ElementRef;\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly crmService: CrmService,\n private readonly contactService: ContactService,\n public readonly baseService: BaseService,\n public readonly dialogService: DialogService,\n public readonly customAttributesService: CustomAttributesService,\n public readonly router: Router,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.cardId = options.data.card_id;\n }\n\n ngOnInit(): void {\n this.router.navigate([], {\n queryParams: {\n 'deal': this.cardId,\n },\n });\n this.crmService.getColumnCardById(this.cardId)\n .subscribe(res => {\n this.deal = res;\n if (this.deal.funnel_id) {\n this.onInitAllFunnels();\n this.onInitColumnsByFunnel(this.deal.funnel_id);\n this.onInitCAByFunnel(this.deal.funnel_id);\n this.onInitCard(this.deal?.id);\n\n if (this.deal?.contact_id) {\n this.onInitContact();\n } else {\n this.initContactForm();\n }\n\n this.initForm();\n this.onCreateGroupFormValueChange();\n } else {\n this.rightSidebarService.close();\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_1);\n }\n })\n }\n\n ngOnDestroy(): void {\n this.router.navigate([], {\n queryParams: {\n 'deal': null,\n },\n });\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private onCreateGroupFormValueChange() {\n const initialValueCard = { ...this.cardForm.getRawValue() };\n this.trackFormChanges(this.cardForm, initialValueCard);\n }\n\n private trackFormChanges(form: FormGroup, initialValue: any): void {\n form.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe(value => {\n this.isHasChange = !_.isEqual(value, initialValue);\n });\n }\n\n private initForm(isCA = false) {\n this.cardForm = new FormGroup({\n name: new FormControl(this.deal.name, Validators.required),\n price: new FormControl(this.deal.price, Validators.required),\n funnel_id: new FormControl(this.deal.funnel_id, Validators.required),\n currency: new FormControl(this.deal.currency, Validators.required),\n bg_color: new FormControl(this.deal.bg_color, Validators.required),\n tx_color: new FormControl(this.deal.tx_color, Validators.required),\n comment: new FormControl(this.deal.comment),\n });\n\n if (!isCA)\n this.cardCAForm = new FormGroup({});\n\n this.copyCardForm = { ...this.cardForm.getRawValue() };\n }\n\n private initContactForm() {\n this.getAllContacts(true);\n\n if (this.currentContact) {\n this.contactForm = new FormGroup({\n contact_id: new FormControl(this.currentContact.id),\n name: new FormControl(this.currentContact.name, Validators.required),\n phone: new FormControl(_filter(this.currentContact.phones)[0]),\n email: new FormControl(_filter(this.currentContact.emails)[0], Validators.email),\n address: new FormControl(_filter(this.currentContact.addresses)[0]),\n });\n } else {\n this.contactForm = new FormGroup({\n contact_id: new FormControl(null),\n name: new FormControl(null, Validators.required),\n phone: new FormControl(null),\n email: new FormControl(null, Validators.email),\n address: new FormControl(null),\n });\n }\n }\n\n private getAllContacts(isInit: boolean = false, searchTerm: string = '') {\n this.contactService.getAll({ search: searchTerm, take: 10, page: this.contactPage })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n if (isInit || !!searchTerm) this.contacts = res.items;\n else this.contacts = [...this.contacts, ...res.items]\n\n if (this.currentContact && !this.contacts.some(obj => _.isEqual(obj, this.currentContact)))\n this.contacts.unshift(this.currentContact);\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n public onInitColumnsByFunnel(funnel_id: string): void {\n this.crmService.getAllColumns({\n funnel_id: funnel_id,\n page: 1,\n take: 50\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (resColumn) => {\n if (!resColumn.items) return;\n this.columns = [...resColumn.items];\n this.currentColumn = this.columns.find((v: IColumn): boolean => v.id === this.deal.column_id);\n }\n });\n }\n\n public onInitCAByFunnel(funnel_id: string): void {\n this.customAttributesService.getAllCA({\n funnel_id: funnel_id,\n page: 1,\n take: 50\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (resCA) => {\n if (!resCA.items) return;\n this.customAttributes = [...resCA.items].map(v => ({ ...v, is_active: false }));\n for (const ca of this.customAttributes) {\n this.cardCAForm.addControl(ca.key, new FormControl(this.deal.custom_attributes?.[ca.key] || ''));\n }\n this.copyCardCAForm = { ...this.cardCAForm.getRawValue() };\n\n const initialValueCA = { ...this.cardCAForm.getRawValue() };\n this.trackFormChanges(this.cardCAForm, initialValueCA);\n }\n });\n }\n\n public onInitAllFunnels(): void {\n this.crmService.getAllFunnels({ type: FUNNEL_TYPE.DEAL })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (funnels: IFunnel[]) => {\n this.funnels = [...funnels];\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n public onInitContact(): void {\n this.contactService.getById(this.deal.contact_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n this.currentContact = res;\n this.contactInfoList = [\n {\n icon: 'phone',\n items: this.currentContact?.phones\n },\n {\n icon: 'mail',\n items: this.currentContact?.emails\n },\n {\n icon: 'location_on',\n items: this.currentContact?.addresses\n }\n ]\n this.initContactForm();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n public isHoverArrow(hover: boolean, columnBgColor: string, columnTextColor: string, index: number): void {\n this.hoveredIndex = hover ? index : 0;\n this.hoveredBgColor = hover ? columnBgColor : null;\n this.hoveredTextColor = hover ? columnTextColor : null;\n }\n\n public onGetColumnColor(column: any, i: number, colorType: string) {\n const targetColumn: any = this.columns.find((v: IColumn) => v.id === this.deal.column_id);\n if (!targetColumn) return;\n\n const targetColor = colorType === 'bg' ? targetColumn.bg_color : targetColumn.text_color;\n const hoveredColor = colorType === 'bg' ? this.hoveredBgColor : this.hoveredTextColor;\n\n if (!hoveredColor && (i <= this.columns.indexOf(this.currentColumn))) return targetColor;\n else if (hoveredColor && (!!`${this.hoveredIndex}` && (i <= this.hoveredIndex))) return hoveredColor;\n }\n\n public onSaveUpdate(): void {\n this.isFormSubmitted = true;\n const payload = this.cardForm.getRawValue();\n\n const isEqual = _.isEqual(this.copyCardForm, payload);\n\n if (this.cardForm.valid && !isEqual) {\n this.crmService.updateColumnCard(payload, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = res;\n this.copyCardForm = { ...payload };\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n }\n\n public onUpdate(): void {\n this.isFormSubmitted = true;\n\n const dealName = this.cardForm.controls.name.value.trim();\n const payload: any = this.cardForm.getRawValue();\n const payloadCA = this.cardCAForm.getRawValue();\n\n if (dealName === '') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.name_card_empty);\n } else {\n const isEqual = _.isEqual(this.copyCardForm, payload);\n const isEqualCA = _.isEqual(this.copyCardCAForm, payloadCA);\n\n if ((this.cardForm.valid && !isEqual) || (this.cardCAForm.valid && !isEqualCA)) {\n this.isAllDeactivate = !this.isAllDeactivate;\n this.isActiveSomeInput = false;\n }\n\n if (this.cardForm.valid && !isEqual) {\n const newPayload: Record = {};\n for (const key in payload) {\n if (!_.isEqual(this.copyCardForm[key], payload[key])) {\n newPayload[key] = payload[key];\n }\n }\n this.crmService.updateColumnCard(payload, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = res;\n this.initForm(true);\n this.onCreateGroupFormValueChange();\n\n this.isHasChange = false;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n\n if (this.cardCAForm.valid && !isEqualCA) {\n const newPayloadCA: Record = {};\n for (const key in payloadCA) {\n if (!_.isEqual(this.copyCardCAForm[key], payloadCA[key])) {\n newPayloadCA[key] = payloadCA[key];\n }\n }\n this.crmService.updateColumnCardCABulk(newPayloadCA, this.deal.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.customAttributes = this.customAttributes.map(v => ({ ...v, is_active: false }))\n this.customAttributes.forEach(v => v.is_active = false);\n this.isHasChange = false;\n\n const initialValueCA = { ...this.cardCAForm.getRawValue() };\n this.trackFormChanges(this.cardCAForm, initialValueCA);\n // this.deal = res;\n // this.copyCardForm = { ...payload };\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n }\n }\n\n public onCancelUpdate(): void {\n this.isAllDeactivate = !this.isAllDeactivate;\n this.isActiveSomeInput = false;\n\n this.cardForm.reset(this.deal);\n\n this.cardCAForm.reset(this.copyCardCAForm);\n\n const copyCA = [...this.customAttributes];\n this.customAttributes = [];\n this.customAttributes = [...copyCA];\n this.deal.custom_attributes = { ...this.deal.custom_attributes };\n }\n\n public onChangeColumn(column_id: any): void {\n\n if(column_id === this.current_id) {\n return;\n }\n\n this.current_id = column_id;\n\n this.crmService.updateColumnCard({ column_id }, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = null;\n this.deal = res;\n this.currentColumn = this.columns.find((v: IColumn) => v.id === this.deal.column_id);\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n\n public onChangeTab(selectedTab: any): void {\n this.selectedTab = selectedTab;\n }\n\n public onChangeFunnel(e: any): void {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.deal_1`,\n });\n dialogRef.subscribe((result: any) => {\n if (result) {\n this.crmService.updateColumnCard({ funnel_id: e.id }, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = {} as IColumnCard;\n this.deal = res;\n this.onInitColumnsByFunnel(this.deal.funnel_id);\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n });\n }\n\n public columnStyle(column: IColumn, i: number): Record {\n const bgColor = this.onGetColumnColor(column, i, 'bg');\n const txtColor = this.onGetColumnColor(column, i, 'txt');\n\n return { '--column-bg-color': bgColor, '--column-text-color': txtColor };\n }\n\n public onChangeContact(contact: any) {\n this.contactForm.setValue({\n contact_id: contact.id,\n name: contact.name,\n phone: _filter(contact?.phones)[0] || null,\n email: _filter(contact?.emails)[0] || null,\n address: _filter(contact?.addresses)[0] || null,\n })\n }\n\n private isValid(): boolean {\n let isValid: boolean = true;\n\n if (this.contactForm.controls.name.invalid) {\n this.contactForm.controls.name.markAsTouched();\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.name_field_required);\n\n isValid = false;\n }\n\n if (this.contactForm.controls.email.invalid) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.email_not_expected);\n isValid = false;\n }\n\n if (!!this.contactForm.getRawValue()?.phone?.length && this.contactForm.getRawValue()?.phone?.length < 12) {\n this.contactForm.controls.phone.setErrors({\n 'incorrect': true\n });\n\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.number_invalid);\n isValid = false;\n }\n\n return isValid;\n }\n\n public onSaveContact() {\n const { contact_id, name, phone, email, address } = this.contactForm.getRawValue();\n\n if (this.contactForm.valid && this.isValid()) {\n\n if (this.isContactCreate) {\n this.contactService.create({\n name,\n phone: Array.isArray(phone) ? phone.filter(Boolean) : phone !== null ? [phone] : [],\n email: Array.isArray(email) ? email.filter(Boolean) : email !== null ? [email] : [],\n address: Array.isArray(address) ? address.filter(Boolean) : address !== null ? [address] : [],\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.crmService.updateColumnCard({ contact_id: res.id, }, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (r) => {\n this.deal = {} as IColumnCard;\n this.deal = r;\n\n this.onInitContact();\n this.getAllContacts(true);\n\n this.searchedContact = '';\n this.contactPage = 1;\n\n this.isContactCreate = false;\n this.isContactSelect = false;\n this.isContactUpdate = false;\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n } else if (this.deal.contact_id === contact_id && this.isContactUpdate) {\n this.contactService.update(this.currentContact.id, {\n name,\n phone: Array.isArray(phone) ? phone.filter(Boolean) : phone !== null ? [phone] : [],\n email: Array.isArray(email) ? email.filter(Boolean) : email !== null ? [email] : [],\n address: Array.isArray(address) ? address.filter(Boolean) : address !== null ? [address] : [],\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.currentContact = res;\n\n this.onInitContact();\n\n this.isContactUpdate = false;\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n } else if (this.deal.contact_id !== contact_id) {\n this.crmService.updateColumnCard({ contact_id: contact_id, }, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (r) => {\n this.deal = {} as IColumnCard;\n this.deal = r;\n\n this.onInitContact();\n\n this.isContactCreate = false;\n this.isContactSelect = false;\n this.isContactUpdate = false;\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n }\n }\n\n public onCloseContact(isBack ?: boolean) {\n if (this.contactForm && this.currentContact) {\n this.contactForm.reset({\n contact_id: this.currentContact.id,\n name: this.currentContact.name,\n phone: _filter(this.currentContact.phones)[0],\n email: _filter(this.currentContact.emails)[0],\n address: _filter(this.currentContact.addresses)[0],\n })\n }\n\n if (isBack) this.isContactCreate = false;\n else this.isContactUpdate ? this.isContactUpdate = false : this.isContactSelect = false;\n }\n\n public onDeleteContact() {\n const dialogRef = this.dialogService.openConfirm({\n text: 'dialogs.deal_2'\n });\n dialogRef.subscribe((result: any) => {\n if (result) {\n this.crmService.updateColumnCard({ contact_id: null }, this.deal.id, TYPE.DEAL)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = res;\n this.currentContact = null;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n });\n }\n\n private onInitCard(id: string) {\n this.crmService.getColumnCardById(id)\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = res;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n\n public onCreateContactForm() {\n this.isContactCreate = true;\n if (this.contactForm) this.contactForm.reset();\n }\n\n public onContactChanges(isSelect: boolean = false, isUpdate: boolean = false) {\n if (isSelect) this.isContactSelect = true;\n if (isUpdate) this.isContactUpdate = true;\n }\n\n public onSaveUpdateCA(key: string, value: any) {\n const payload = {\n key,\n value,\n };\n this.crmService.updateColumnCardCA(payload, this.deal.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IColumnCard) => {\n this.deal = res;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n\n public scrollToEndContacts() {\n ++this.contactPage;\n this.getAllContacts(false, this.searchedContact);\n }\n\n public searchContact(e: any): void {\n this.searchedContact = e.term;\n this.getAllContacts(false, this.searchedContact)\n }\n\n public onCloseDropdown() {\n this.searchedContact = '';\n this.contactPage = 1;\n this.getAllContacts()\n }\n\n public onActiveSomeInput() {\n this.isActiveSomeInput = true;\n }\n\n // onUpdateColor() {\n // if (this.colorTimer) clearTimeout(this.colorTimer);\n // this.colorTimer = setTimeout(() => {\n // this.onUpdate();\n // }, 3000);\n // }\n}\n","\n
\n\n
\n
\n\n
\n
\n \n
\n
\n\n \n\n
\n \n\n\n \n \n\n
\n\n
\n
\n\n
\n\n \n
\n
\n
\n {{ column?.name }}\n
\n
\n
\n
\n\n \n \n \n\n
\n \n
\n\n
\n
\n
\n
\n \n \n {{'about_deal' | translate}}\n \n\n \n \n
\n
\n {{'phase' | translate}}\n
\n {{currentColumn?.name}}\n
\n\n
\n
\n {{'amount_and_currency' | translate}}\n
\n
\n \n \n
\n
\n
\n
\n {{ 'сomment' | translate}}\n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n
\n \n
\n {{ field.title }}\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n {{ field.title }}\n \n
\n
\n
\n
\n\n
\n
\n {{'created_at' | translate}}:\n
\n
\n {{ deal.created_at | date: 'dd MMMM YYYY' }}\n
\n
\n
\n
\n
\n\n
\n \n \n {{'contacts' | translate}}\n \n\n \n\n \n
\n
\n {{'contact' | translate}}\n \n

\n {{ currentContact?.name }}\n

\n\n \n \n \n
\n \n {{ contactInfo.icon }}\n \n
\n {{ item }}\n
\n
\n
\n
\n\n
\n \n {{ currentContact?.created_at | date: 'EE, dd MMMM YYYY' }}\n \n
\n
\n
\n\n
\n
\n \n \n edit\n \n \n\n \n \n delete\n \n \n
\n
\n
\n
\n \n
\n \n \n \n\n
\n \n\n \n\n \n\n \n
\n\n
\n {{ 'save' | translate}}\n {{'assign_and_save'| translate}}\n {{'cancel' | translate}}\n \n
\n\n
\n
\n\n \n
\n \n {{'no_one_contact' | translate}}\n \n\n \n \n add\n \n \n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n\n
\n {{'cancel' | translate}}\n {{ 'save' | translate }}\n
\n
\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, tap } from 'rxjs';\nimport { IParams } from '../model/http';\nimport { IContact } from '../interfaces';\nexport const initItemsParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport interface IItemsApiResponse {\n items?: any[];\n total?: number;\n size?: number;\n page?: number;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ContactService {\n constructor(\n private readonly http: HttpClient,\n ) { }\n private readonly itemsSubject$ = new BehaviorSubject({});\n public readonly items$ = this.itemsSubject$.asObservable();\n\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryItemsParams = { ...initItemsParams };\n\n create(payload: any){\n return this.http.post(environment.baseURL + '/contacts', payload)\n .pipe(map(v => v.data));\n }\n\n getAll(params: any) {\n return this.http.get(environment.baseURL + '/contacts', { params })\n .pipe(map(v => v.data));\n }\n\n getById(id: string) {\n const url = environment.baseURL + `/contacts/${id}`;\n return this.http.get(url).pipe(map(v => v.data));\n }\n\n loadItems() {\n this.isLoader$.next(true);\n return this.getAll(this.queryItemsParams)\n .pipe(tap((items) => {\n this.itemsSubject$.next(items);\n this.isLoader$.next(false);\n }));\n }\n\n setQueryItemsParams(params: IParams) {\n const { sort, order, search, take, page } = params;\n this.queryItemsParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryItemsParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryItemsParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryItemsParams.search,\n take: take || this.queryItemsParams.take,\n page: page || this.queryItemsParams.page,\n };\n return this.loadItems();\n }\n\n update(id: string, payload: any){\n const url = environment.baseURL + `/contacts/${id}`;\n return this.http.put(url, payload);\n }\n\n delete(id: string) {\n return this.http.delete(environment.baseURL + `/contacts/${id}`);\n }\n\n destroy() {\n this.queryItemsParams = {...initItemsParams};\n }\n}\n","import { Component, HostListener, Inject, OnInit } from '@angular/core';\nimport { MAT_DIALOG_DATA } from '@angular/material/dialog';\nimport { DomSanitizer } from '@angular/platform-browser';\n\n\n@Component({\n templateUrl: './confirm.component.html',\n styleUrls: ['./confirm.component.scss'],\n})\nexport class ConfirmComponent implements OnInit{\n public isMobile: boolean = false;\n public data: any = {\n confirmText: 'yes',\n cancelText: 'no',\n }\n\n constructor(\n @Inject(MAT_DIALOG_DATA) private modalData: any,\n private sanitizer: DomSanitizer,\n ) {\n this.data = {...this.data, ...modalData};\n }\n\n @HostListener('window:resize', ['$event'])\n onResize() {\n this.isMobile = window.innerWidth <= 700;\n }\n\n ngOnInit(): void {\n this.isMobile = window.innerWidth <= 700;\n }\n\n get sanitizedText() {\n return this.sanitizer.bypassSecurityTrustHtml(this.data.text);\n }\n\n}\n","
\n
\n
\n \n {{ (data.title | translate) || ('warning' | translate) + '!' }}\n \n
\n
\n
\n \n close\n \n
\n
\n
\n \n
\n \n

{{ !data?.isTranslate ? (data.text | translate) || ('warning' | translate) + '!' : data.text}}

\n

До сплати {{ data.price }} грн.

\n
\n \n
\n \n \n {{ data.cancelText | translate }}\n \n \n \n {{ data.confirmText | translate }}\n \n
\n
\n\n","import { Injectable } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { Observable } from 'rxjs';\nimport { ConfirmDialogData } from '../model/dialog-data.model';\nimport { ConfirmComponent } from '../dialogs/confirm/confirm.component';\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class DialogService {\n constructor(\n public dialog: MatDialog,\n ) { }\n\n openConfirm(data: ConfirmDialogData, style?: { width?: string, height?: string }): Observable {\n return this.dialog\n .open(ConfirmComponent, {\n data,\n width: style?.width || 'fit-content',\n height: style?.height,\n minWidth: data?.minWidth || '300px',\n minHeight: data?.minHeight,\n maxWidth: data?.maxWidth || '600px',\n maxHeight: data?.maxHeight,\n //\n panelClass: data.containerClass,\n disableClose: true,\n autoFocus: false,\n })\n .afterClosed();\n }\n\n}\n","import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\nimport { map } from 'rxjs';\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class CustomAttributesService {\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n public getAllCA(params: any) {\n return this.http.get(environment.baseURL + '/ca', { params })\n .pipe(map(v => v.data));\n }\n\n public getCAnById(field_id: string) {\n const url: string = environment.baseURL + `/ca/${field_id}`\n return this.http.get(url).pipe(map(v => v.data));\n }\n\n public createCA(payload: any) {\n return this.http.post(environment.baseURL + '/ca', payload)\n .pipe(map(v => v.data));\n }\n\n public updateCA(payload: any, field_id: string) {\n const url: string = environment.baseURL + `/ca/${field_id}`\n return this.http.put(url, payload)\n .pipe(map(v => v.data));\n }\n\n public deleteCA(field_id: string) {\n const url: string = environment.baseURL + `/ca/${field_id}`\n return this.http.delete(url).pipe(map(v => v.data));\n }\n\n}\n","import {Injectable} from '@angular/core';\nimport { environment } from '../../environments/environment';\n\nexport type Maps = typeof google.maps;\n\n@Injectable({\n providedIn: 'root'\n})\nexport class GoogleAddressService {\n public readonly api = this.load();\n\n private load(): Promise {\n const script = document.createElement('script');\n script.type = 'text/javascript';\n script.async = true;\n script.defer = true;\n // tslint:disable-next-line:no-bitwise\n // const callbackName = `GooglePlaces_cb_` + ((Math.random() * 1e9) >>> 0);\n const callbackName = \"agmLazyMapsAPILoader\" ;\n script.src = this.getScriptSrc(callbackName);\n\n interface MyWindow {\n [name: string]: Function;\n };\n const myWindow: MyWindow = window as any;\n\n const promise = new Promise((resolve, reject) => {\n myWindow[callbackName] = resolve;\n script.onerror = reject;\n });\n document.body.appendChild(script);\n return promise.then(() => google.maps);\n }\n\n private getScriptSrc(callback: string): string {\n interface QueryParams {\n [key: string]: string;\n };\n const query: QueryParams = {\n v: '3',\n callback,\n key: environment.google.maps_key,\n libraries: 'places',\n };\n const params = Object.keys(query).map(key => `${key}=${query[key]}`).join('&');\n return `//maps.googleapis.com/maps/api/js?${params}`;\n // return `https://maps.googleapis.com/maps/api/js?${params}`;\n }\n}\n","import {\n Component,\n ElementRef,\n EventEmitter,\n forwardRef,\n Input,\n NgZone,\n OnChanges,\n Output,\n ViewChild\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { GoogleAddressService, Maps } from '../../services/google-address.service';\n\n\ntype Components = google.maps.places.PlaceResult['address_components'];\n\n@Component({\n selector: 'app-google-address-textfield',\n templateUrl: './google-address-textfield.component.html',\n styleUrls: ['./google-address-textfield.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => GoogleAddressTextFieldComponent)\n },\n ]\n})\nexport class GoogleAddressTextFieldComponent implements OnChanges, ControlValueAccessor {\n// INPUTS\n @Input() input: string = '';\n @Input() label: string | null = null;\n @Input() placeholder!: string;\n\n @Input() isError: boolean = false;\n @Input() isDisabled: boolean = false;\n @Input() isRequired: boolean = false;\n @Input() isAlwaysNeedClean?: boolean = true;\n\n// OUTPUTS\n @Output() onValueChange = new EventEmitter();\n @Output() onFocusOut = new EventEmitter();\n\n// REFS\n @ViewChild('googleAddressSearch') searchElementRef!: ElementRef;\n\n// LOCAL var`s\n private googleCoordinates: any;\n\n constructor(\n public _googleAddressService: GoogleAddressService,\n private ngZone: NgZone\n ) {\n _googleAddressService.api.then((maps) => {\n this.initAutocomplete(maps);\n });\n }\n\n public ngOnChanges(changes: any): void {\n if (changes?.input?.currentValue.trim() !== '' && this.isAlwaysNeedClean) {\n this.input = '';\n }\n }\n\n public onChange = (_data: any) => {\n };\n public onTouch = (_: any) => {\n };\n\n public registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n public registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n public writeValue(value: string) {\n this.input = value;\n this.onChange(value);\n }\n\n public onchange(event: any) {\n this.input = event;\n this.onChange({ address: event });\n this.onValueChange.emit({ address: event });\n }\n\n public focusOutInput(event: any) {\n this.onFocusOut.emit(event);\n }\n\n private initAutocomplete(maps: Maps) {\n let autocomplete = new maps.places.Autocomplete(this.searchElementRef?.nativeElement, {\n componentRestrictions: { country: ['ua'] },\n });\n\n autocomplete.addListener('place_changed', () => {\n this.ngZone.run(() => {\n this.onPlaceChange(autocomplete.getPlace());\n });\n });\n }\n\n private onPlaceChange(place: google.maps.places.PlaceResult) {\n const location = this.locationFromPlace(place);\n\n this.input = place.formatted_address as string;\n const payload = {\n address: place?.formatted_address,\n city: location?.cityName,\n state: location?.stateCode,\n country: location?.countryName,\n zipcode: location?.postal_code,\n latitude: this.googleCoordinates?.latitude,\n longitude: this.googleCoordinates?.longitude,\n }\n\n this.onChange(payload);\n this.onValueChange.emit(payload);\n }\n\n private locationFromPlace(place: google.maps.places.PlaceResult) {\n const components = place.address_components;\n if (components === undefined) {\n return null;\n }\n\n const areaLevel3 = this.getShort(components, 'administrative_area_level_3');\n const locality = this.getLong(components, 'locality');\n\n const cityName = locality || areaLevel3;\n const countryName = this.getLong(components, 'country');\n const countryCode = this.getShort(components, 'country');\n const stateCode = this.getShort(components, 'administrative_area_level_1');\n const postal_code = this.getShort(components, 'postal_code');\n const name = place.name !== cityName ? place.name : null;\n\n const coordinates = {\n latitude: place.geometry?.location?.lat(),\n longitude: place.geometry?.location?.lng(),\n };\n const bounds = place.geometry?.viewport?.toJSON();\n this.googleCoordinates = coordinates;\n\n return {\n name,\n cityName,\n countryName,\n countryCode,\n stateCode,\n bounds,\n coordinates,\n postal_code\n };\n }\n\n private getComponent(components: Components, name: string) {\n return components?.filter((component) => component.types[0] === name)[0];\n }\n\n private getLong(components: Components, name: string) {\n const component = this.getComponent(components, name);\n return component && component.long_name;\n }\n\n private getShort(components: Components, name: string) {\n const component = this.getComponent(components, name);\n return component && component.short_name;\n }\n}\n","
\n \n \n
\n","import { Component, Input, OnDestroy, OnInit } from '@angular/core';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { ActivityService } from './activity.service';\nimport { alias, alias_events, EVENT, IActivity } from './activity.interface';\nimport { environment } from '../../../environments/environment';\nimport { BaseService } from '../../services/base.service';\nimport { RightSidebarService } from '../../services/right-sidebar.service';\n\n\n@Component({\n selector: 'app-activity',\n templateUrl: './activity.component.html',\n styleUrls: ['./activity.component.scss'],\n providers: []\n})\nexport class ActivityComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n @Input() entityUuid!: string;\n @Input() entityType!: string;\n\n public isLoader: boolean = false;\n\n public dataSource$: Observable = this.activityService.items$\n .pipe(map((data) => {\n if (!data?.items) return\n return {\n items: data.items.map((item: any) => {\n let title_activity = this.parseTitle(item);\n let chip_type = this.parseChipType(item);\n if (item.event_type === EVENT.CREATE) {\n return {\n ...item,\n title_activity,\n chip_type,\n create_value: this.parseDesc(item)\n }\n }\n\n if ([EVENT.UPDATE, EVENT.REMOVED, EVENT.ADDED].includes(item.event_type)) {\n return {\n ...item,\n title_activity,\n chip_type,\n values: this.getUpdateData(item)\n }\n }\n\n return { ...item, title_activity, chip_type };\n }),\n size: data?.size || 0,\n index: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n }\n }))\n\n constructor(\n private readonly activityService: ActivityService,\n private readonly rightSidebarService: RightSidebarService,\n public readonly baseService: BaseService\n ) {}\n\n ngOnInit() {\n this.isLoader = true;\n this.activityService.loadItems(this.entityUuid, this.entityType)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n parseTitle(item: IActivity): string {\n let aliasKey = '';\n\n if (item.old_data && item.new_data) {\n const differentKeys = this.getDifferentKeys(item.old_data, item.new_data);\n\n if (differentKeys.length === 1) aliasKey = differentKeys[0];\n if (differentKeys.length >= 2) aliasKey = 'deal';\n }\n\n const oldDataHasContact = !!item.old_data?.contact || item.old_data?.contact === null;\n const newDataHasContact = !!item.new_data?.contact || item.new_data?.contact === null;\n\n if (oldDataHasContact || newDataHasContact) {\n const newDataContactLength = Object.values(item.new_data?.contact || {}).length;\n const oldDataContactLength = Object.values(item.old_data?.contact || {}).length;\n\n if (newDataContactLength && newDataContactLength) {\n return `${alias_events[item.event_type]} ${alias[aliasKey]}`;\n }\n\n if (oldDataHasContact && newDataContactLength > 0) {\n return `${alias_events[EVENT.ADDED]} ${alias[aliasKey]}`;\n } else if (newDataHasContact && oldDataContactLength > 0) {\n return `${alias_events[EVENT.REMOVED]} ${alias[aliasKey]}`;\n }\n }\n\n if (!item.old_data && item.new_data) aliasKey = 'deal';\n return `${alias_events[item.event_type]} ${alias[aliasKey]}`\n }\n\n parseDesc(item: IActivity) {\n if (!item.old_data && item.new_data) return item.new_data.title;\n return '';\n }\n\n getUpdateData(item: IActivity): any[] {\n const differentKeys = this.getDifferentKeys(item.old_data, item.new_data);\n return differentKeys.map(key => {\n if (key === 'column_id') {\n return {\n old_data: item.old_data?.column?.name,\n new_data: item.new_data?.column?.name,\n }\n }\n\n if (key === 'funnel_id') {\n return {\n old_data: item.old_data?.funnel?.name,\n new_data: item.new_data?.funnel?.name,\n }\n }\n\n if (key === 'contact_id') {\n return {\n old_data: item.old_data?.contact?.name,\n new_data: item.new_data?.contact?.name,\n }\n }\n\n return {\n old_data: `${item.old_data[key]}`,\n new_data: `${item.new_data[key]}`,\n }\n });\n }\n\n getDifferentKeys(obj1: any, obj2: any) {\n const keys1 = Object.keys(obj1);\n const keys2 = Object.keys(obj2);\n\n let differentKeys: string[] = [];\n\n keys2.forEach(key => {\n const value2 = obj1[key];\n\n if (key === 'contact') {\n return;\n }\n\n if (!!value2 && (typeof value2 === 'object' || key === 'id')) {\n return;\n }\n\n if (Array.isArray(value2)) {\n return;\n }\n\n if (!keys1.includes(key)) {\n differentKeys.push(key);\n } else {\n if (obj1[key] !== obj2[key]) {\n differentKeys.push(key);\n }\n }\n });\n\n differentKeys = differentKeys.filter(v => !['id'].includes(v))\n\n return differentKeys;\n }\n\n public getUrlFile(id: string, avatar_url: string) {\n if (!avatar_url) return '/assets/svgs/special/profile.svg';\n return `${environment.baseURL}/assets/avatar/${id}`;\n }\n\n onClickChip(item: any, data: any) {\n if (item.chip_type === 'contact_id') {\n this.rightSidebarService.close();\n setTimeout(() => {\n this.rightSidebarService.open('contact', {\n medium: true,\n data: { contact_id: data.contact_id },\n }).subscribe();\n }, 500);\n }\n }\n\n private parseChipType(item: IActivity) {\n let aliasKey = '';\n\n if (item.old_data && item.new_data) {\n const differentKeys = this.getDifferentKeys(item.old_data, item.new_data);\n\n if (differentKeys.length === 1) aliasKey = differentKeys[0];\n if (differentKeys.length >= 2) aliasKey = 'deal';\n }\n\n return aliasKey;\n }\n\n protected readonly EVENT = EVENT;\n}\n","
\n \n \n \n
\n {{ item.title_activity }}\n \n {{ item.created_at | date: 'EE, dd MMMM HH:mm' }}\n \n
\n
\n \n
\n \n
{{item.create_value}}
\n
\n
\n {{ i.old_data }}\n arrow_right_alt\n {{ i.new_data }}\n
\n
\n
\n
\n
\n
\n","import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';\nimport { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { BaseService } from '../../services/base.service';\n\n@Component({\n selector: 'app-color',\n templateUrl: './color.component.html',\n styleUrls: ['./color.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => ColorComponent),\n },\n],\n})\nexport class ColorComponent implements OnInit {\n @Input() label: string = '';\n @Input() input: any;\n @Input() colors: any;\n\n @Output() onValueChange = new EventEmitter();\n @Output() onClearChange = new EventEmitter();\n colorControl!: FormControl;\n\n constructor(public readonly baseService: BaseService) {\n }\n\n onChange = (data: any) => {};\n onTouch = (_: any) => {};\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: string) {\n this.input = value;\n this.onChange(value);\n }\n\n ngOnInit() {\n this.colorControl = new FormControl(this.input);\n }\n\n onColorChange(event: any) {\n this.colorControl.setValue(event);\n this.onChange(event);\n this.onValueChange.emit(event);\n }\n\n onClear(event?: any) {\n this.onClearChange.emit(event);\n }\n\n close() {\n\n }\n\n save() {\n\n }\n\n protected readonly Object = Object;\n}\n","
\n \n
\n
\n \n
\n \n
\n
\n \n \n \n
\n
\n
\n
\n \n\n","import { Component, HostListener, Input, OnDestroy, OnInit } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { MatMenu } from '@angular/material/menu';\n\n\n@Component({\n selector: 'app-sidebar-header',\n templateUrl: './header.component.html',\n styleUrls: ['./header.component.scss']\n})\nexport class HeaderSidebarComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n public isMobile: any;\n @Input() title: string = '';\n @Input() trigger!: any;\n\n constructor(\n public readonly rightSidebarService: RightSidebarService\n ) { }\n\n ngOnInit() {\n this.isMobile = window.innerWidth <= 700;\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n @HostListener('window:resize', ['$event'])\n onResize() {\n this.isMobile = window.innerWidth <= 700;\n }\n\n close(isChats: boolean) {\n if (isChats) this.rightSidebarService.closeChats();\n else this.rightSidebarService.close();\n }\n}\n","
\n
\n \n menu\n \n \"logo\"\n
{{title}}
\n
\n close\n
\n
\n \n
\n","import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n\n@Component({\n selector: 'app-phone-field',\n templateUrl: './phone-field.component.html',\n styleUrls: ['./phone-field.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => PhoneFieldComponent)\n }\n ]\n})\nexport class PhoneFieldComponent implements OnInit, ControlValueAccessor{\n// Input properties\n @Input() type: string | number = 'text';\n @Input() label: string = '';\n @Input() inputValue: string = '';\n @Input() customClass: string = '';\n @Input() placeholder: string = '';\n @Input() error: boolean = false;\n @Input() standard: boolean = false;\n @Input() disabled: boolean = false;\n @Input() required: boolean = false;\n @Input() autofocus: boolean = false;\n\n @ViewChild('inputRef', { static: false }) inputRef!: ElementRef;\n\n// Output property\n @Output() valueChanged = new EventEmitter();\n\n ngOnInit(): void {\n if (this.autofocus) {\n setTimeout(() => this.inputRef.nativeElement.focus());\n }\n }\n\n onChange = (data:any) => {};\n onTouch = (_:any) => {};\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: string) {\n this.inputValue = value;\n this.onChange(value);\n }\n\n// Handles input value changes\n onInputChange(event: any, isValue = false) {\n if (isValue) {\n this.inputValue = event;\n this.onChange(event);\n this.emitValueChanged(event);\n } else {\n const newValue = (event.target as HTMLInputElement).value;\n this.inputValue = newValue;\n this.onChange(newValue);\n this.emitValueChanged(newValue);\n }\n }\n\n// Emits the changed value\n private emitValueChanged(value: string) {\n this.valueChanged.emit(value);\n }\n\n}\n","
\n \n \n
\n","import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';\nimport { HAMMER_GESTURE_CONFIG, HammerGestureConfig } from '@angular/platform-browser';\nenum SWIPE_ACTION {\n LEFT = 'swipeleft',\n RIGHT = 'swiperight',\n}\n\n@Component({\n selector: 'app-tabs',\n templateUrl: './tabs.component.html',\n styleUrls: ['./tabs.component.scss'],\n providers: [{\n provide: HAMMER_GESTURE_CONFIG,\n useClass: HammerGestureConfig\n }]\n})\nexport class TabsComponent implements OnInit {\n // Input's\n @Input() customClass: string = '';\n @Input() type!: string;\n @Input() tabList: any[]=[];\n\n // Output's\n @Output() onSelectTab = new EventEmitter();\n\n selectedIndex: any;\n\n constructor(\n ) {}\n\n ngOnInit() {\n this.tabList.forEach((value: any, index: any) => {\n if (value.active == true) {\n this.selectedIndex = index;\n\n let selectedTab = this.tabList[index];\n this.onSelectTab.emit(selectedTab);\n }\n });\n }\n\n changeTab(event: any) {\n let selectedTab = this.tabList[event.index];\n this.onSelectTab.emit(selectedTab);\n }\n\n selected = 0;\n onSwipe(type: string) {\n if(type === SWIPE_ACTION.LEFT && this.selected > 0){\n this.selected--;\n }\n else if(type === SWIPE_ACTION.RIGHT && this.selected < this.selectedIndex){\n this.selected++;\n }\n }\n}\n","\n \n \n {{ tab.translate ? (tab.name | translate) : tab.name }}\n ({{ tab.count }})\n
{{'coming_soon' | translate}}!
\n
{{'test_mode' | translate}}
\n
\n\n
\n\n","import {\n Component,\n ElementRef,\n EventEmitter,\n forwardRef,\n Input,\n OnChanges,\n Output,\n SimpleChanges,\n ViewChild\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { languages } from \"../../enum/constants\";\nimport { BaseService } from \"../../services/base.service\";\n\n\n@Component({\n selector: 'app-input',\n templateUrl: './input.component.html',\n styleUrls: ['./input.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => InputComponent)\n }\n ]\n})\nexport class InputComponent implements ControlValueAccessor, OnChanges {\n// INPUTS\n @Input() maxLength!: number;\n\n @Input() mask: string = '';\n @Input() input: any = '';\n @Input() label: string = '';\n @Input() inputWidth: string = 'auto';\n @Input() inputHeight: string = 'auto';\n @Input() generalText: string = '';\n @Input() customClass: string = '';\n @Input() placeholder: string = 'Введіть...';\n @Input() defaultValue: string = '';\n @Input() errorText: string = languages[localStorage.getItem('language') || 'ua']['field_cannot_be_empty'];\n @Input() type: string = 'default';\n\n @Input() isBtn: boolean = false;\n @Input() isEnterSave: boolean = false;\n @Input() isError: boolean = false;\n @Input() isDisabled: boolean = false;\n @Input() isRequired: boolean = false;\n\n// OUTPUTS\n @Output() onChangeSave = new EventEmitter();\n @Output() onValueChange = new EventEmitter();\n @Output() onChangeClick = new EventEmitter();\n @Output() onChangeCancel = new EventEmitter();\n\n// REFS\n @ViewChild('inputRef', { static: false }) inputRef!: ElementRef;\n\n// LOCAL var`s\n private copyInput: string = '';\n @Input() active: boolean = false;\n @Output() activeChange = new EventEmitter()\n public isInputActive: boolean = false;\n @Input() isDeactivate: boolean = false\n\n constructor(\n private sanitizer: DomSanitizer,\n private baseService: BaseService) {\n }\n\n ngOnChanges(changes: SimpleChanges | any): void {\n if (changes.isDeactivate && !changes.isDeactivate.isFirstChange()) {\n this.isInputShow(false);\n }\n }\n\n public isInputShow(show: boolean): void {\n this.isInputActive = show;\n }\n\n private onChange = (data: any) => {\n };\n private onTouch = (_: any) => {\n };\n\n public registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n public registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n public writeValue(value: string): void {\n this.input = value;\n this.onChange(value);\n }\n\n public onchange(event: any): void {\n this.input = event;\n this.onChange(event);\n this.onValueChange.emit(event);\n }\n\n public onTextClick(): void {\n if (!this.isInputActive) {\n this.isInputActive = true;\n\n this.copyInput = this.input;\n this.onChangeClick.emit();\n setTimeout(() => {\n this.inputRef.nativeElement.focus();\n }, 250);\n }\n }\n\n public onDoneClick(): void {\n if (this.isError) return;\n\n this.isInputActive = false;\n this.defaultValue = this.input;\n this.onChangeSave.emit(this.input);\n }\n\n public onCancelClick(): void {\n this.isInputActive = false;\n this.input = this.copyInput;\n this.onChangeCancel.emit();\n }\n\n public onFocusOutCancel(): void {\n // setTimeout( () => {\n // if (!this.isInputActive) return;\n // this.onDoneClick();\n // }, 100)\n }\n\n getSafeContent(content: string): SafeHtml {\n return this.sanitizer.bypassSecurityTrustHtml(content);\n }\n\n}\n","\n
\n
\n \n\n \n \n\n \n
\n\n
\n \n \n done\n \n \n\n \n \n close\n \n \n
\n\n
\n {{errorText}}\n
\n
\n
\n\n\n \n \n
\n
\n {{generalText || input}}\n
\n
\n edit\n
\n
\n
\n \n \n \n \n
\n
\n {{generalText || input}}\n
\n edit\n
\n
\n \n
\n \n \n\n\n\n edit\n
\n
\n
\n
\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, Observable, take, tap } from 'rxjs';\nimport { IParams } from '../model/http';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class TicketService {\n private readonly urlAllTickets = `${environment.baseURL}/ticket/all`;\n private readonly urlTicket = `${environment.baseURL}/ticket`;\n private readonly urlMessage = `${environment.baseURL}/ticket/message`;\n\n\n constructor(\n private readonly http: HttpClient,\n ) { }\n\n public addTicket(params: any) {\n return this.http.post(this.urlTicket, params )\n .pipe(map(v => v.data));\n }\n\n public sendMessage(params: any) {\n return this.http.post(this.urlMessage, params )\n .pipe(map(v => v.data));\n }\n\n public getAllTickets(params: any) {\n return this.http.get(this.urlAllTickets, { params } )\n .pipe(map(v => v.data));\n }\n\n public getTicket(params: any) {\n return this.http.get(this.urlTicket, { params: { id: params } } )\n .pipe(map(v => v.data));\n }\n\n public getMessageTicket(params: any) {\n return this.http.get(this.urlMessage, { params } )\n .pipe(map(v => v.data));\n }\n}\n","import * as i0 from '@angular/core';\nimport { Component, ChangeDetectionStrategy, ViewChild, createComponent, Injectable, EventEmitter, Directive, Optional, Input, Output, HostListener, NgModule } from '@angular/core';\nimport PhotoSwipe from 'photoswipe';\nimport PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default';\nconst _c0 = [\"Lightbox\"];\nlet LightboxComponent = /*#__PURE__*/(() => {\n class LightboxComponent {\n constructor() {}\n static {\n this.ɵfac = function LightboxComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || LightboxComponent)();\n };\n }\n static {\n this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: LightboxComponent,\n selectors: [[\"photo-gallery-lightbox\"]],\n viewQuery: function LightboxComponent_Query(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵviewQuery(_c0, 7);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.element = _t.first);\n }\n },\n decls: 26,\n vars: 0,\n consts: [[\"Lightbox\", \"\"], [1, \"lightbox-wrapper\"], [\"tabindex\", \"-1\", \"role\", \"dialog\", \"aria-hidden\", \"true\", 1, \"pswp\"], [1, \"pswp__bg\"], [1, \"pswp__scroll-wrap\"], [1, \"pswp__container\"], [1, \"pswp__item\"], [1, \"pswp__ui\", \"pswp__ui--hidden\"], [1, \"pswp__top-bar\"], [1, \"pswp__counter\"], [\"title\", \"Close (Esc)\", 1, \"pswp__button\", \"pswp__button--close\"], [\"title\", \"Share\", 1, \"pswp__button\", \"pswp__button--share\"], [\"title\", \"Toggle fullscreen\", 1, \"pswp__button\", \"pswp__button--fs\"], [\"title\", \"Zoom in/out\", 1, \"pswp__button\", \"pswp__button--zoom\"], [1, \"pswp__preloader\"], [1, \"pswp__preloader__icn\"], [1, \"pswp__preloader__cut\"], [1, \"pswp__preloader__donut\"], [1, \"pswp__share-modal\", \"pswp__share-modal--hidden\", \"pswp__single-tap\"], [1, \"pswp__share-tooltip\"], [\"title\", \"Previous (arrow left)\", 1, \"pswp__button\", \"pswp__button--arrow--left\"], [\"title\", \"Next (arrow right)\", 1, \"pswp__button\", \"pswp__button--arrow--right\"], [1, \"pswp__caption\"], [1, \"pswp__caption__center\"]],\n template: function LightboxComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\", 1)(1, \"div\", 2, 0);\n i0.ɵɵelement(3, \"div\", 3);\n i0.ɵɵelementStart(4, \"div\", 4)(5, \"div\", 5);\n i0.ɵɵelement(6, \"div\", 6)(7, \"div\", 6)(8, \"div\", 6);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(9, \"div\", 7)(10, \"div\", 8);\n i0.ɵɵelement(11, \"div\", 9)(12, \"button\", 10)(13, \"button\", 11)(14, \"button\", 12)(15, \"button\", 13);\n i0.ɵɵelementStart(16, \"div\", 14)(17, \"div\", 15)(18, \"div\", 16);\n i0.ɵɵelement(19, \"div\", 17);\n i0.ɵɵelementEnd()()()();\n i0.ɵɵelementStart(20, \"div\", 18);\n i0.ɵɵelement(21, \"div\", 19);\n i0.ɵɵelementEnd();\n i0.ɵɵelement(22, \"button\", 20)(23, \"button\", 21);\n i0.ɵɵelementStart(24, \"div\", 22);\n i0.ɵɵelement(25, \"div\", 23);\n i0.ɵɵelementEnd()()()()();\n }\n },\n styles: [\"@charset \\\"UTF-8\\\";.lightbox-wrapper[_ngcontent-%COMP%] {position:relative;z-index:20000}.lightbox-wrapper[_ngcontent-%COMP%] .pswp{display:none;position:absolute;width:100%;height:100%;left:0;top:0;overflow:hidden;-ms-touch-action:none;touch-action:none;z-index:20000;-webkit-text-size-adjust:100%;-webkit-backface-visibility:hidden;outline:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp *{box-sizing:border-box}.lightbox-wrapper[_ngcontent-%COMP%] .pswp img{max-width:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--animate_opacity{opacity:.001;will-change:opacity;transition:opacity .3s cubic-bezier(.4,0,.22,1)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--open{display:block}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--zoom-allowed .pswp__img{cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--zoomed-in .pswp__img{cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--dragging .pswp__img{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__bg{position:absolute;left:0;top:0;width:100%;height:100%;background:#000c;opacity:0;transform:translateZ(0);-webkit-backface-visibility:hidden;will-change:opacity}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__scroll-wrap{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__container, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__zoom-wrap{-ms-touch-action:none;touch-action:none;position:absolute;inset:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__container, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__img{-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-touch-callout:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__zoom-wrap{position:absolute;width:100%;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;transform-origin:left top;transition:transform .3s cubic-bezier(.4,0,.22,1)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__bg{will-change:opacity;transition:opacity .3s cubic-bezier(.4,0,.22,1)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--animated-in .pswp__bg, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--animated-in .pswp__zoom-wrap{-webkit-transition:none;transition:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__container, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__zoom-wrap{-webkit-backface-visibility:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__item{position:absolute;inset:0;overflow:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__img{position:absolute;width:auto;height:auto;top:0;left:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__img--placeholder{-webkit-backface-visibility:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__img--placeholder--blank{background:#333}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--ie .pswp__img{width:100%!important;height:auto!important;left:0;top:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__error-msg{position:absolute;left:0;top:50%;width:100%;text-align:center;font-size:14px;line-height:16px;margin-top:-8px;color:#ccc}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__error-msg a{color:#ccc;text-decoration:underline}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button{width:44px;height:44px;position:relative;background:none;cursor:pointer;overflow:visible;-webkit-appearance:none;display:block;border:0;padding:0;margin:0;float:right;opacity:.75;transition:opacity .2s;box-shadow:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button:focus, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__button:hover{opacity:1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button:active{outline:none;opacity:.9}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button::-moz-focus-inner{padding:0;border:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--over-close .pswp__button--close{opacity:1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left:before, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right:before{background:url(/default-skin.png) 0 0 no-repeat;background-size:264px 88px;width:44px;height:44px}@media (-webkit-min-device-pixel-ratio: 1.1),(min-resolution: 105dpi),(min-resolution: 1.1dppx){.lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--left:before, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--right:before{background-image:url(/default-skin.svg)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--right{background:none}}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--close{background-position:0 -44px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--share{background-position:-44px -44px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--fs{display:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--supports-fs .pswp__button--fs{display:block}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--fs .pswp__button--fs{background-position:-44px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--zoom{display:none;background-position:-88px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--zoom-allowed .pswp__button--zoom{display:block}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--zoomed-in .pswp__button--zoom{background-position:-132px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--touch .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--touch .pswp__button--arrow--right{visibility:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right{background:none;top:50%;margin-top:-50px;width:70px;height:100px;position:absolute}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left{left:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right{right:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left:before, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right:before{content:\\\"\\\";top:35px;background-color:#0000004d;height:30px;width:32px;position:absolute}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left:before{left:6px;background-position:-138px -44px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right:before{right:6px;background-position:-94px -44px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__counter, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-modal{-webkit-user-select:none;-moz-user-select:none;user-select:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-modal{display:block;background:#00000080;width:100%;height:100%;top:0;left:0;padding:10px;position:absolute;z-index:20100;opacity:0;transition:opacity .25s ease-out;-webkit-backface-visibility:hidden;will-change:opacity}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-modal--hidden{display:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-tooltip{z-index:20120;position:absolute;background:#fff;top:56px;border-radius:2px;display:block;width:auto;right:44px;box-shadow:0 2px 5px #00000040;transform:translateY(6px);transition:transform .25s;-webkit-backface-visibility:hidden;will-change:transform}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-tooltip a{display:block;padding:8px 12px;color:#000;text-decoration:none;font-size:14px;line-height:18px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-tooltip a:hover{text-decoration:none;color:#000}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-tooltip a:first-child{border-radius:2px 2px 0 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-tooltip a:last-child{border-radius:0 0 2px 2px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-modal--fade-in{opacity:1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__share-modal--fade-in .pswp__share-tooltip{transform:translateY(0)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--touch .pswp__share-tooltip a{padding:16px 12px}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--facebook:before{content:\\\"\\\";display:block;width:0;height:0;position:absolute;top:-12px;right:15px;border:6px solid rgba(0,0,0,0);border-bottom-color:#fff;-webkit-pointer-events:none;-moz-pointer-events:none;pointer-events:none}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--facebook:hover{background:#3e5c9a;color:#fff}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--facebook:hover:before{border-bottom-color:#3e5c9a}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--twitter:hover{background:#55acee;color:#fff}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--pinterest:hover{background:#ccc;color:#ce272d}.lightbox-wrapper[_ngcontent-%COMP%] a.pswp__share--download:hover{background:#ddd}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__counter{position:absolute;left:0;top:0;height:44px;font-size:13px;line-height:44px;color:#fff;opacity:.75;padding:0 10px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption{position:absolute;left:0;bottom:0;width:100%;min-height:44px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption small{font-size:11px;color:#bbb}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption__center{text-align:left;max-width:420px;margin:0 auto;font-size:13px;padding:10px;line-height:20px;color:#ccc}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption--empty{display:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption--fake{visibility:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__preloader{width:44px;height:44px;position:absolute;top:0;left:50%;margin-left:-22px;opacity:0;transition:opacity .25s ease-out;will-change:opacity;direction:ltr}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__preloader__icn{width:20px;height:20px;margin:12px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__preloader--active{opacity:1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__preloader--active .pswp__preloader__icn{background:url(/preloader.gif) 0 0 no-repeat}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader--active{opacity:1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader--active .pswp__preloader__icn{animation:_ngcontent-%COMP%_clockwise .5s linear infinite}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader--active .pswp__preloader__donut{animation:_ngcontent-%COMP%_donut-rotate 1s cubic-bezier(.4,0,.22,1) infinite}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader__icn{background:none;opacity:.75;width:14px;height:14px;position:absolute;left:15px;top:15px;margin:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader__cut{position:relative;width:7px;height:14px;overflow:hidden}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--css_animation .pswp__preloader__donut{box-sizing:border-box;width:14px;height:14px;border:2px solid #FFF;border-radius:50%;border-left-color:transparent;border-bottom-color:transparent;position:absolute;top:0;left:0;background:none;margin:0}@media screen and (max-width: 1024px){.lightbox-wrapper[_ngcontent-%COMP%] .pswp__preloader{position:relative;left:auto;top:auto;margin:0;float:right}}@keyframes _ngcontent-%COMP%_clockwise{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes _ngcontent-%COMP%_donut-rotate{0%{transform:rotate(0)}50%{transform:rotate(-140deg)}to{transform:rotate(0)}}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui{-webkit-font-smoothing:auto;visibility:visible;opacity:1;z-index:20050}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__top-bar{position:absolute;left:0;top:0;height:44px;width:100%}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__top-bar, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--has_mouse .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--has_mouse .pswp__button--arrow--right{-webkit-backface-visibility:hidden;will-change:opacity;transition:opacity .3s cubic-bezier(.4,0,.22,1)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--has_mouse .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--has_mouse .pswp__button--arrow--right{visibility:visible}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__top-bar, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption{background-color:#00000080}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--fit .pswp__top-bar, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--fit .pswp__caption{background-color:#0000004d}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--idle .pswp__top-bar{opacity:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--idle .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--idle .pswp__button--arrow--right{opacity:0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--hidden .pswp__top-bar, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--hidden .pswp__caption, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--hidden .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--hidden .pswp__button--arrow--right{opacity:.001}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--one-slide .pswp__button--arrow--left, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--one-slide .pswp__button--arrow--right, .lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--one-slide .pswp__counter{display:none}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__element--disabled{display:none!important}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--minimal--dark .pswp__top-bar{background:none}@media (-webkit-min-device-pixel-ratio: 1.1),(min-resolution: 105dpi),(min-resolution: 1.1dppx){.lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--left:before, .lightbox-wrapper[_ngcontent-%COMP%] .pswp--svg .pswp__button--arrow--right:before{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjY0IiBoZWlnaHQ9Ijg4IiB2aWV3Qm94PSIwIDAgMjY0IDg4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjx0aXRsZT5kZWZhdWx0LXNraW4gMjwvdGl0bGU+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Zz48cGF0aCBkPSJNNjcuMDAyIDU5LjV2My43NjhjLTYuMzA3Ljg0LTkuMTg0IDUuNzUtMTAuMDAyIDkuNzMyIDIuMjItMi44MyA1LjU2NC01LjA5OCAxMC4wMDItNS4wOThWNzEuNUw3MyA2NS41ODUgNjcuMDAyIDU5LjV6IiBpZD0iU2hhcGUiIGZpbGw9IiNmZmYiLz48ZyBmaWxsPSIjZmZmIj48cGF0aCBkPSJNMTMgMjl2LTVoMnYzaDN2MmgtNXpNMTMgMTVoNXYyaC0zdjNoLTJ2LTV6TTMxIDE1djVoLTJ2LTNoLTN2LTJoNXpNMzEgMjloLTV2LTJoM3YtM2gydjV6IiBpZD0iU2hhcGUiLz48L2c+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTYyIDI0djVoLTJ2LTNoLTN2LTJoNXpNNjIgMjBoLTV2LTJoM3YtM2gydjV6TTcwIDIwdi01aDJ2M2gzdjJoLTV6TTcwIDI0aDV2MmgtM3YzaC0ydi01eiIvPjwvZz48cGF0aCBkPSJNMjAuNTg2IDY2bC01LjY1Ni01LjY1NiAxLjQxNC0xLjQxNEwyMiA2NC41ODZsNS42NTYtNS42NTYgMS40MTQgMS40MTRMMjMuNDE0IDY2bDUuNjU2IDUuNjU2LTEuNDE0IDEuNDE0TDIyIDY3LjQxNGwtNS42NTYgNS42NTYtMS40MTQtMS40MTRMMjAuNTg2IDY2eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMTEuNzg1IDY1LjAzTDExMCA2My41bDMtMy41aC0xMHYtMmgxMGwtMy0zLjUgMS43ODUtMS40NjhMMTE3IDU5bC01LjIxNSA2LjAzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xNTIuMjE1IDY1LjAzTDE1NCA2My41bC0zLTMuNWgxMHYtMmgtMTBsMy0zLjUtMS43ODUtMS40NjhMMTQ3IDU5bDUuMjE1IDYuMDN6IiBmaWxsPSIjZmZmIi8+PGc+PHBhdGggaWQ9IlJlY3RhbmdsZS0xMSIgZmlsbD0iI2ZmZiIgZD0iTTE2MC45NTcgMjguNTQzbC0zLjI1LTMuMjUtMS40MTMgMS40MTQgMy4yNSAzLjI1eiIvPjxwYXRoIGQ9Ik0xNTIuNSAyN2MzLjAzOCAwIDUuNS0yLjQ2MiA1LjUtNS41cy0yLjQ2Mi01LjUtNS41LTUuNS01LjUgMi40NjItNS41IDUuNSAyLjQ2MiA1LjUgNS41IDUuNXoiIGlkPSJPdmFsLTEiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTUwIDIxaDV2MWgtNXoiLz48L2c+PGc+PHBhdGggZD0iTTExNi45NTcgMjguNTQzbC0xLjQxNCAxLjQxNC0zLjI1LTMuMjUgMS40MTQtMS40MTQgMy4yNSAzLjI1eiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0xMDguNSAyN2MzLjAzOCAwIDUuNS0yLjQ2MiA1LjUtNS41cy0yLjQ2Mi01LjUtNS41LTUuNS01LjUgMi40NjItNS41IDUuNSAyLjQ2MiA1LjUgNS41IDUuNXoiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTA2IDIxaDV2MWgtNXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTA5LjA0MyAxOS4wMDhsLS4wODUgNS0xLS4wMTcuMDg1LTV6Ii8+PC9nPjwvZz48L2c+PC9zdmc+)}}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__img--placeholder{align-items:center;display:flex;justify-content:center}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__img--placeholder:after{background:url(data:image/gif;base64,R0lGODlhQABAAPcbADc3N5CQkJOTk5GRkVxcXIODgz8/P5eXl2xsbHx8fENDQ5aWlnp6epSUlIeHhzo6OldXV3FxcURERIWFhZKSkkFBQWpqamNjY2BgYISEhGVlZTQ0NIaGhkhISJubm35+fpycnImJiX9/f5qamnJyclVVVZ+fn52dnYKCgkJCQp6enkVFRWlpaT4+PkZGRl1dXW1tbYCAgF9fX4+Pj1ZWVo6OjkdHR3Z2dkBAQElJSTg4OGRkZIiIiGFhYXt7e1RUVGZmZoqKiltbW1NTU3h4eFJSUjY2NouLi25ubpWVlZmZmUtLS2dnZ29vb2trazU1NV5eXnNzc3BwcGhoaKOjo6GhoaSkpHR0dKCgoFpaWoGBgaenpzk5OU9PTzs7O0pKSpiYmI2NjXl5eUxMTHd3d1lZWXV1dT09PVBQUFFRUX19fVhYWE1NTU5OTjw8PGJiYoyMjKKioqurq6ioqKysrLKysq+vr7GxsbCwsK2tra6urqmpqaqqqrW1tbS0tLOzs6Wlpaampre3t7m5uba2tru7u7i4uL29vb+/v8HBwcPDw7y8vLq6us7OzsfHx8zMzMjIyM3NzcvLy8TExMXFxdHR0c/Pz8bGxsDAwNPT09TU1NLS0sLCwtfX18rKyt7e3tzc3Nra2tnZ2djY2Nvb2+Hh4dbW1uPj4+Li4t3d3efn5+vr6+zs7L6+vsnJyeTk5N/f39XV1eDg4Orq6ubm5u/v7+3t7dDQ0Ojo6PDw8Pn5+fb29vX19e7u7vLy8v39/fT09Pf39/j4+OXl5QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hlPcHRpbWl6ZWQgdXNpbmcgZXpnaWYuY29tACH5BAkFABsALAAAAABAAEAAAAf/gBuCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqpZcFTmvD6uLrTcNSre4DTcQXLKEXhZJC2K8BkYAxhC1urGyGrYyOoo6MgICF6oADAdIvY1cSNxGp14O15Jd1l6lTxNHOJQKRwXro2QDBqznohAHOZhj/IXS5kSTkxoAQDHBp0nHvk7tsG2q9indE04RPUVgoLGJpwEEOkIE88XTEpIYUY7Mt0mCypT/Wr6UGbPhTJsscZoE8xGDyE4IEvzkZBHmTihEk1zUOGNcpowVH17aQcFbVBsAF4QUlSBAzknyOI7S9i7egAxOx2aQ6igdPXK1QZrUYwRMnCoaYa5JS0TNGg1fRp4No2HDGLLCYhYw8zWIlq1ct3ZZZVyoFazJlDNr3sy5s+fPoEOLHk26tOnTmgMBACH5BAkFAAIALA4ADgAkACQAAAj/AAUIHEhQoAIoRGI4qMFQCxkoCgpKnEgQQoYBDpgQ+MGmDUcmCwtAoEhSQAeHGku6mDJDRIeSBS+0dANzYAsYLWsCIANnSU2JS+CYAUByp4MUPycqWEh0Is8zSSm2CEHG6dGoRWswKXgyDVaVA7wO/EDiK8woH2yGNQsWjUAxMNieJWJyRg65bYfAwZs3Ct+SN4BE2fFTwZ9Vq/5ErCnYx4ufd1gdOcJKz0+EE2j8pLVAoJJTlwtwGFlTVWcBC16FVoMB8iqqlVdPKXx4Fp7Fc534+EvS8Rq7vIEGQPNgeHCuwOEeJ0jW4NrlNp4LUL48YUXpf39gnw48+/a3PFrwNZ1aVelTuS6CxGhqvqdZ7WTY956ZFAfarfVRdpTKhiWKL+9d1BIMEKWBWQAYrTHegQ3pV1NAACH5BAkFAAEALA0ADQAmACYAAAj/AAMIHEiQIIAcXUoIWZgQQMGHECF6KUPCQYgCashoxIhxjZeIIAcagcIhg5MuRh4etJCBw4uUIVWiQFHmScwATyjSvClww5sQL3mKfMHjzU0uNyZ8ESrTjA6QPpUylRjDDEgnHHBMjeilKsQsQLdCDWuwqNiQTHicIThF6lmuE5iINPv2Koq1AUhuqDsWikCNfNHewEn26BqPQrvczeEAZswbuXRJjsDTSOMiDHhKWSXChQsRnDWnEWL1ZuiBE2aJ/nlUVxe2rm+SOCyl9euBRWI/fkOaZ6gJBFOv1qKZswQboDUMf1Bccq7BR4N8Qbq08mGHPIdo4ZKXRWC7Ail+YI9osifd8bjd5lWL3jyBsi/ar7fJtjF65NULyihg4DuOuHa5wFcKXvW1xFttlPRUSFGx8BFTEwG1V3YXEUAfg0SdlxgLFaKkUkItOYigThd9sBFHLCDmH0IKMdQBdjcFBAAh+QQJBQABACwMAAwAKAAoAAAI/wADCBxIsGCAJ1wMrFiY0KDDhw8TSilAsWLFH1wgaixogIWWKARs4PCCkKQNAlE+GtgI0csOlSzPvIHJkmDHJjhqDsTRREQHnQFoaIEAlOPQmj3U2Chq0MYHDBshHGXa9GnLqVSr/tSa9SqAql01wpBhdGXYiFiD7jgr1snOAgrYehX4Q03RLl2KJhhC90JNHQtO7Rq8QAdSFgIZ8N3oRRQqGBUUwHhl6kFMIom/sNzySoJNWlQuH4TLmFcUg2Z4fdWYAq5kw2J3mf1MlLVr0rFn75TN+LZuhwBMo1bdmyRujZw97wYkOoDimKReQZZM2QtLqZn/Gho82NDvhzJg9GwF2hHCd4g+fvSNKxe4z4FO/LZH7fZtzvn2C3rED38KV/wloHCeUEu159RiDjHxQQtyHRiVUmc5eFkaXdVFoU4HnncVhHe91MMZE15gWVY3NRHSSCWNhFIMHzbYQXgJWBSjDB08wd9OKURGVUAAIfkECQUAAQAsDAAMACgAKAAACP8AAwgcSJCgAgJOmlwRw1ChEAUFI0qUyKVMFIY00iyxwVEjEwZRynCZSHIgAIs7cjwpaSTHDiIiS1IkAwOiTIMOb5pkUETnzJ4318D0OdHG0JIQzBggSrKC0qY8mbIUk+OnVJlJbUSMUOZqUCkFuzDwqpNEl500yH5dGqCNGbVl2wgEUsLnSS0iIBjxmbFt1KCffJEaTEqGTqdP2Ly9GaSXiiVzVdRyEFcIWKy1DIfNvFZzU1IeJkoeibSHhTWYIVP0xQIrC64y1dAqOUuMa9glo8yiHeE2hNRFfakmieE0lJuUQks8cakz4BdbbZ11LbZsbYIiVlFm3EWHjxTWaVl1Gv/pwuHvARCgPiw0797zc2+8h1v0r5Gj9CVmRZs/IgD8AWwQUn9hRbFBgUAR+ANVVvUnwV/6PUVfCgyWNpxXY0BoIVlZACjTfxWyZ4aBUrUUQUguHEiRC5ZxtRKHpsGUkUpDvPARTVo5yEaLNMGQ0hcq3hQQACH5BAkFAAEALAsACwAqACoAAAj/AAMIHEiwoEAdODqkQaNQh8GHECNyWSIESRSLQDJiFLKES8SPBR8QuNKkTAqIKco0IekRJMohJFjYcHmQRYQhG2gSdOMEwRKdO2FY8AJ0xU0jQEO+keKCZhGSSVFeSQPyy9SoEq9KpYH1Iw0SOB6KLNO1KlODGJoAKPsRwNmgVNl61SowrVyXPQdyoevyyRCcRbV2uaKzySVVrBJPQlK4TV0IfQmx2qLWBostk0+aDWAkSlOQhkzJQNtJUF/PKfg+VLOK7EMIq8SYrWDZZaQTH088wmuDDYyqrb2uegASyRiYwIlLZAVlNpDkbYPP9Q09Io3hxY+zsL0l9ybeFVQbk4zBHGXs2U882xZNutDpJwEuQK4qmbICy5gJaZ5bs/DhxKos1lhNKxT1F3w6GUXUY3dtppd4DRJYkF0RoiWEQW7NV+Fja2EIIVuDhbVVhW2oN12DEHyo1AVIddUZWIGZmNQYFhkQVYZD5VSVZaiV5QUUJPXQRUI7JdRDTC8sKNePGgXJhBROArYhZyvkwMaBByYVEAAh+QQJBQABACwKAAoALAAsAAAI/wADCBxIsGBBAAZaADDIsKHDg0uyXHACo6JFiW4eajRoZAgTBDKWuPGiY4NJkhErDnmyUWPKNjo26mgzZcqSlgy9TISJU+ATmhce9PRpgYCRoQQ7ssDRswNIpAwhOOnQ0gaMLlAbdrnqcmpWh2m8amUh5OtDAjbHCjXb8GeWnAgysnX4JK7BHRDmdj24tGmaNAtxAhArEG/VGo9eqVr8qMaGqhcGPujrclGqIxoqKNhxJBWjFDK5BogY86GBWH9WcBSkyUBXqqPfauwz6KEgPRuFlA3ApI1GM6AkmP5kZm/s2Vg2YsG014jotqeK/0b1WqeX4WNyq8pO9yqX51G3a6znrpY3F43EtRvvjVz5ovU3z8ZyTTeWE/jube/JvYaozEyCqHYQa/SdZQNR57lkBSiYJaRBZ1a0ENpRhR3YkgZ7dMLYHpFBllRafg3lVkET6WUgR4SZ+CFsJGLwmIqS9SBDeTBWeB2NKoZVwWs5ptidECyZVZePptW0FlSTgejXVEdCxmRWJhUVUpBtWWVkaV+5pdIYK5xhxJddSrQljCOJeZGUItWYU0KBQRUQACH5BAkFAAEALAoACgAsACwAAAj/AAMIHEiwoMAzEpaggcBQ4RmDECNK9KJwB5MLBGh02ZjxYkOJIAvqGAKkxxAXACKOJFkyZUiVHFe8JOjijcUnM2lCuZlTp4YeOnoawOilJ0SKF1rMXNHSKEwNMkG2gOo05MKoRzFWffnRININW61mxCkSaFiuGctiPSuV6kCSbHPufItBgdwCeypZ2lOgRE4FQLgc1LAU0KgtQRDACDLnsISZNgW+KPKSRChGNrwaCkUCbQAATFJYBUUB7FEKoLqINcI0pJFEhl4uSmT6qcLRmUf3EJsGQkg4gHICOsJ7SEhCWnLiLS71ExS5pIy0TTuxlAzoLm0fF6F8EnOQw4XHpfkucYKo3M25g3yTYyhZlZ9iH7dkoG39HXbXfyqtErXftoNVBoohHWgGyhWefUYZV5gclthiW5CCSRuQ5WZEUksd8UkpHHIYhHRoKRVgXAkOJiKJ/Z1oIopZfQGREDK8x6KCQjw1I13ZeaXVjFeRd9YGGj0WYo5VgYbhX0RtBWSSRi0ZWpMYMFkkSxtN11RcSFnUEBtt9EZlEUXdCEB7MW1ZX08BAQAh+QQJBQABACwJAAkALgAuAAAI/wADCBxIsKBBI24SGlzIsOFCHS6GlIEio6JFiQAcamSIEMJFA142PAkZ0qNHIxs3dsSoMgXGJyk5CvEYk6CBNTNr2oSyQudBiTpX+nyYZQjKlkaHysyS0eEGmkodpoGwQSrVqE6hEm2BVSOAnD8rdFUp44HBIjTGpiyRpuBNLymNMHHAR5IkPg52VCULd2CXLmsFjWK0oEmTBYxE+bGxFrBfNxujZNLT4azgKGQfR7YEwyGSW5g1EjAg8K/oTEw2M7ZamrRDPypibqnjVUiAr2YbpvEEOfCQrA+8lPW8QGdh4C1kaFShxTgI5C80UkKiMwIn0ThwRH99o/ol7G6GN7bcUrwmYui5GdLVKWgC8ADiGU4J1XYt/aytnR4yJBtRU5n5SeVJaJ7FsgR2ARbYg4K1uQafgw19NsB/fgkAWmZ+lRGYIiAUwAQLBYAwCWWN7dSXSlGo4MolHR7YGA5uaagWgjGmN2NYW92Io0x76XibVvLV5yNafA15FV89YoUbhRtNdaJSwqEB5UlDCRVVlDYwqZsNLyQ1VpRlVUBSayWZ9KRaEKGFwZpstrkChD6SSRIAMA0VEAAh+QQJBQACACwJAAkALgAuAAAI/wAFCBxIsKBALyu+tBkCoeHChAYjSpwowMASGmvSXFTQoqMNjhkvUhxZEAfDjxsoPvHYkKTKky5LYjQQU6aLmgY3uMiIUycENzglummYkqSRIiW4BJ3IJakRkkgBLH1ZZCQbGlKnUmXKU6vVljnLVPDqUkFXgmPakI250CZbIjOsxAlghg3bpGi/lCWkqVIfCg36bNJESEjZIQM9Wk1CWGxevhSycgVaUa3KP40YMGXQ6I9koXorj/RgaQxUSx6+Vn5CUYMmMzGJvKYKkWkiOTjtLGI9eUnr2TV7aFpDm2KITF56Agf90bigpYNMFJ9oh8LSwNMlVr+eJ3tEE92D6qdR4d3gceiae/+2oZy4etvWazLmLZHoz98a2C6POHTsfeOlnZbaSwexd1kkV/zmmWqrLdZYTnIQltxWBRrF2WCBwBEXIpkgQsRTDBao1F50lFiiYYfRlNdah4U1FouT8XcWjHkZGBFRNNb41WcsAiBEGkZpRFmPOKZIpGk9iTXiUg8oOVWTISU5E1kWhcSjTWldqVV/SDnFEkhddDBkjquF6dBDDwQVEAAh+QQJBQABACwIAAgAMAAwAAAI/wADCBxIsCDBDQAenKnAUKHBhxAjGtTRcIjFMRgvLmEosWNEBV3SbPQCYCLJkBY9qgyAA+VKgSdxvHxoJOPMgy5vssypE+aXkDdbFFHQkybQlSANFIXoRqTKljqWfnQqkSJRqVOvGpWJtSoarhPHdPVoY4nRl09+Elk75MnLowThVvUAqdGjP4Qe2fXg4mmbgi1V3qgbpQQXn1Hy3vAbFyzTOZImlKQ54W6Fji7YDEzr9mOdQUXIFqoTNavPjnS7II2MWfNOiTQaqZkZoxENr6cjbrGzYeaTz7jTVo2EQCeCSG5gIwwd8bjYoLaVN5XoAFJv6BOk/9W9pSifGdqpd7/vOedI+OaOikLKnnU7xDfRsWdN2JmmpB3GJT2Y31Z8UDkH4PbaR7LRhpyAIKEGiWpPNRKgcj5N9hhoopHmlYRfYfaZZEwVYFlrOJFF1x+uxYXXAYeB2FiDklAyCIAvRjYEY4Bl+JQZMeSYo2MXnjWWgGEp9eNWpg2Jk1ZMDWVkhH55YaQXSiLVhpNjQSnkWzZKVdZ+Ogk1ZVEGhCRhT1C14BtPWIXJRg4pkBQkm18uyUWbQ2mEkkZLblXRGUUFBAAh+QQJBQACACwIAAgAMAAwAAAI/wAFCBxIsODABwYUrOiwpOHChAYjSpwo8EmLhw4VJtyY8QnFjwU34Mj4UaTGkSBLakwZ8ktDlgYtuoQZ8SJDmgJ0uNyAs6aLlyy9AO0pcSVIoQaIqvSyVOnRoTFnOn2a1OfUoDZ8AoA5BMiNK0yGcIWak2xRDowcMZrDJy0jDlmpEqxgNuSCSTVkMD0oIwDeFk0r1p1bx0rcolbuVAgMkaICQgu2lrx7RqVgx4FOcFVBhefEi2VxUPxg5wFNHXfUOOaIuURPCJxSrEY5MYYHpSA+zBY9kZAFpTDs7P7sqOrr4p8f1j40FRGT5IMFNOnTHAb0vREtGKp+fSKBS9yL2qIYT3yM0jbIxQulaOcGcD3DlxPdUIdD/KKXLhz/spu3xCCKnZbaapd9xsdtQWXGBYFllQSZZJj94Z94gmGHWGcRVsZggY7dgEheFp7Rl3Oebchhh26x5RZcKa3XEk1dfRXWWBq+eNVT6t04G3Q6ahVYjxW2uBCQodHo0VVGMAQhVtEJ+dN5yuGEEZIj7bTURkDq1JGWCJ1x0kglEhkaRh31FBAAIfkECQUAAQAsBwAHADIAMgAACP8AAwgcSLBgwQ1PdHBxw1ChwYcQI0IEcEaBjYsSUlh0gbGixI8SE3LcqMOgSJJPQKoMgHDkhpUtOa4MucLizIMbbxKMqfPhg4w9Rfak+RIm0KFEjSIFmVClzaVMU3w0oACq0ok1Z57UqPUpTi9OkYw4hKhs2RFIzjjlipNpBEEmGJTJsbADAQYmCEUwEvXrVCp2ekjsUYdKi6kyB1L96wEsYxuIdzqeaIdHUbd23NBU7CIk4MthTfCNyBbh4MxB60TZzFIzZQtInfgBQLpz64hMDI1OzaR26wcRB8CBWmNG7ZYSA0MlTNS1T0QSoLpA1Jw0dOnXsXqp+hp7dO0rrLe58X68Q3Inyw81TwlRhfGlwo/fhoiEENRBCHzPfw57qOzJJoW330NRzMYbaxR5NgNoTCkhGmsDmoSIZTAFkYdzPnEh2WMNogahQImRBlgUtPlUoGGRtTXVW3ARQNdPUMBhiCAW7KafiiuikJdZcWmgVl8Z2hiVeTcZEWKAVg2ZYpJYKclkgAxq96Rk7BlVpVVCBZUVlgwtdRJSdZUoXU1XDlkmlx39SCWZGE75W5ooZSSVm+VR1CVSAQEAIfkECQUAAQAsBwAHADIAMgAACP8AAwgcSLAgQQAPvJwxgKPhwoQGI0qceDBhBYcWM17ESLFjxCcPIXY0otGjSYULTRYE2VClRJYAXH5kaETmQIQ6bE5EaZNLS507f55kCHQkUY8+NxRFejQol6VDY74UCtXo05lDfYwI1OdPICU+hjwZinUkEDqDTJABUoYAEDIm0gJRatQgwpFU6oR9qTWQgroV8YZ5gDSMHBeABVK1aweFzAl2JFBUeNPA5Dk8dDoARDioYssTN4/tCYLD5DOfdxL6C1SBoDGe74YWATUGiNM+VbMuKmFQF6eoJZqJUzUAFRLAZxffnFziCTPLTTSPqGdH8bPTDVa/nid7wThNuHusJ5iEgfiplIUfiD7+5mqoKXy3H6i2dpXTAVJKLPO+9WvP+YHmHAijyfTECSfgF+BlDmimh2QALqiaYy5BBuFUgTHY2WSG7YZhZTiMBNleH/UllVMrncigWlO09VZccCDGVFlMJZCEHXQ0EJaKCtq1WHEz1YQekCjiRqSPV1l1JIgu4bSkDiI1+SNv+vVEE3wWYdkCj1ElWVVJVuW2ZIAhabQBSg+NGWSZVdoUEAAh+QQJBQAGACwGAAYANAA0AAAH/4AGgoOEhYaDG0+Kh4yNjo1GOg9elG6WlZKPmpoAk5hPhIqdmJuliKSbopSmj5GrrKevsKGys7G2BoleuIyVsKK8kLWtlsGOkqCcw8aGo8rMz8e7qS9EWjVhKGYv09GHnZtdAnd6AxnWBRR6dwJdqQC9Rq0FdQ5r8oUAZUH1Z8Tf3XpRAdOG0wgqBaU1w8fojR0HDOfZwaCQ1jE8RGYRwYOmoqBliOTwwHVkTgVhoRwJWBCRFYCDx041GnKnQ7AOddzF+wiPkYMjzLChzNUyJAFmL/IU5cn0UBaOzB7U3Em04QloBkyQ2JmokZoZWM0JW+RTS1gOYwMW+nk2rVeg0K/gMEDp5m1bgE0NPV36K89RvFW/0fl7UylVkIKCCAjqYGgxqzpxlck5NJfKKmpNSc1QObBgMCQRdu4686HGiR7zOn3Itxk/ip1Vf054UQ+NfwtbUal3r9k+3rgXbtBEoAE5c2bQrWunrCdgTtWuZduWmWpsrPla38Iu/J1z7kSTeQcf/lck7r5seYqKWHP6Wa6+3/T0oJSq6sboc3EWa/8l8v2dIeCABKICYDztmRIIACH5BAkFAAQALAYABgA0ADQAAAf/gASCg4SFhhuIRoqLG4aOj5CQTwA6lJaTl5SRm5yYmpuejZyjgomipKWWqJGqq4WJrq+fsYezq5O0oE+utrmPvawAvqPAjorDpMWEuMkQGE4WzzioygSwoDthfFsnARQec3xhF1ydwr9G2FQDTjmHTt9SXrrG55Bo60OcJUlUbfSyImHYI2YVmTkYggW8RzAWAjlQFA6qxqYhLSn+JNmzJsmEg2EOVKT7NXFjIYzlfHGJ00Rjql9bdiAjoGGLAXSlIEVRckrlgQiSrJkk9HOmIBYLNDI7BNGoNTk36y09yafnsCdNpY40dMGEU0FV3qA75giGgK8EGjgZu5UqWgFrsLU+enPibUK5ZbfYZQvzi9MvUPk+Uuu0yVmcVglF8JA4lpETLUkKhXl32I45UWu9nMv4qkeXOWGKAGklpbFluzjHdbhHH86SArNSuyK73kLOR9wk01bZ9m3cPfZ1+yfxt7EE4tq9i3cGoG9WDE7I4eYN3HQG7joV3+cMmgwI05I1Ro32tfPym4kN/bs+mA60mGgtYp9afqtcl2Z6chz/ayZz95UXyoCZkIVeLQS2N0ogACH5BAkFAAsALAYABgA0ADQAAAf/gAuCg4SFhoIbiYobh42Oj4aLjZKQlY6KloOYmZaJnIeen5Oik4ykmqanoKmfoaqloq6vsJmss4+2tJ0pY0u8nLKrlRttZkpWyMlKZm25kc6I0IQ/Ah4JMr5GADhLMh8gAT/D0E+QKTwnQNILT0AqHDjmwo5FJyhutQUgXbi56xdUrpAy1gNXoX8BVVmgIu4SoXKTTBCZ5cPECoeaHM2AN2tDiBkY2dGrYuAWuzhlMEKMdCCKSUHGyCFqRIPhy5MNI7GDZuymIAoCQYls1NLnAiQCLpGz6bOmEV0Hmd5cIvXgUgVGDVR9eDXrVlQRsTb9Gk1jzpdfqCg1a3RhyEMInI4YPYJg1FBDbcie8qLX1EpDA+q+RKpyZAuTOFCGlHaEx19STz6+fTztHUUVYoWCpYmFiUK9Ow8+AmhmIOi7mzkXqJBvXz/NI6uIyTFaTJwDLuTZvQSQyrI3Xby46XKhou8LAHRDnVTsmDJkzNaVNQiM15dftaTPNAp7HPfU2b8/0T5vKvnlr4LtPY++o/p+7CFvejb+u9JFlE4FAgAh+QQJBQAUACwPAA8AIgAiAAAH/4AUgoOEFF5CPjwBSmCLPkIPhZKTgh0iHgdmPWVoaZxmB5gdlJQKCSAiJUalNJePpIRsmTawsR8HXbVlHk21k0i8pBCovpRMIGuTXyNOxcIjo7YJzrCJBoRqSVzUpA9J05XI3LAy4hQkR+OwG0ckhg0E6uQN717y3chSE/fVTQUR/J4FYRLQWIgBMgr+mlEjocJC5SZMeQjxH0CKg6IwOIYxY5Nl9jB6EYero5AFghA46Oig17sfFFuFPHdkg0J2zQYBCBNFoUYAhRSU5DcFzMxY5tSpTGZQDVBqO5MwFQjB2TB4xQ6JOsNK0UduEi6hIgHkRwkgN46AEHXv0KlFjQUUQaoVCAAh+QQJBQAIACwOAA4AJAAkAAAI/wARCBxIkGCOImUwKERYsKFDhwhrNJjhQIRFiWGkpHnIcaANFAJ8CPHS0IsQHyFddCzZJOVKBBWuuHwZs4CNlwYLOFDZscxMnB7JCFjDkU1IoA+ZDIVIESnHKDMqNAS5wenDJxYLQqBgwGrRpQIBHLHgtWcQIwJLRC37tYTAK03YmoUZYIjcr09WCNBx9+rQNDyQHkQaAg2BGEAP/Pq1IPFJIjjb/NLA5NcSnAyATGEQefIOy5iZyCiQeHEDx1sF30TdgGTfkq0fgH2dkwsCIlFoF4SbdoBr3V4aEKXrRHfvJx73Ap/9dufrDR9QwCbxmsTa6Xc1MNf6U/X1r84Fayn53jF4jSlnaCoNH/lFiPMjWaLk8QKt1Q1DImCseHFihI3PIbQQQzgFBAAh+QQJBQABACwNAA0AJgAmAAAI/wADCBxIsOAGLjhsuKjwYEPBhxAjBqhA4AacMBgzXsxSQaLHgRXIYGTBRoGbJ0ZOtmHBAWPHjw91MLn4BeYSMUeY6IAJEoWaMTx7okjBs0sQEk+CEjQSJUiXj2jCaFAKEYhUiQqOUo04UwJELxmubJVo8cHDJjEAjI0IQEQTg1fXsg2jgKBIuR4t9kSDFytdgRCc9p0LQWCUt4O5RjBcJGgRBw4aFy0wEc7Lj0eEvaIl7MjkynWh7rJgONgPm3Rt/MUciWAjAahNrvZ4QJJrJbFDhs4rTAbg3rln8/bkCdiMyQ55lHHMI4Nk1IYXJ66KOLDD6UsFg8ae/TJa7gO/704F/+VIi4JdsT8oACRmDDHTNzBI+1V5Yif2CeOVL5w6GbVbydTfXOxRNURYXjk2EwNLoObTTgGKJFVJKqnE0oR9UWSRRhpxRF5CC5mlVEAAIfkECQUAAQAsDQANACYAJgAACP8AAwgcSHBgCyEkyKgpwJAICSFnCkqcWHDDkCgTzEChgebLko5QzGQcsoGiyQAA1nxIwPGkFwgJ1EAwcrLiwhdPahp8wRCHTpQoyET8SfCMQhs10WRwQpOozQxdTK4o0MOpyTcFXEw0CsPqSSdQJWIc6nWrwoJtwpaVOqGNQZlrk/rIGQChjrguUWQRKGIHXrlA3f5lawAm0RQgChUCkeLnBr07fPz8gQoUIECPUA35+RAGE51cPuUBMFBKKi46mSDQqDPIqQcFGwVJfQVuTUFyJO7pQ5ulTty6ecv1XdM1bIKVZg//nDSVHdJ8P6FePtmU5UCVMqHhvKOEZMdR7Nh0iVKSu5e2gwnXlZGeIlaBaURAb/92O18M9An2EFM0a/7ABYFFV3pPYCTRY07QJ6BZVQ2GlQFsMRWXEUcNR5ZTblQIGgMxrFEeaCoRcWFNFgU1k1QKhEhAU1a9FFRPJYzBRhmRNcTEdBPaIENCC9kI0YcmBQQAIfkECQUAAQAsDAAMACgAKAAACP8AAwgcSLAgABtj0tBYmBBAwYcQIwYw0oaJmotRkGgkgpGNEYkgB1JkkNFFRBcESn4MCXEIxxUsBdogQSRNTJFTat4kiEbMlJUhvZTcaZCml6A1NxB9+ASIzpMZl0rUEAVoQQtNlEqFuKEJi5Y3dGyVaORGkaIdxoLMIcbhQCFN1IbUiFOC3LUJjgbIEuXu3DIC6cZka8dOgbSDYUz8AJNlgVWJVGBJNEtE4pFuQfJ4dYHnKw4sAYhQsMRHaFsaHiJYhVizBJeOKUW81MDxELgsW3mIqGKRbdwhdfNu9TtC7Nlgip9mohq1csenOr/9HHO0DsbVIUumXODyXiE3ZxZpPhy+73e/rmWORg/WzVvj7K864ammS/z3Vve2vS8UfFEWWvnVVFZcmSGdXxaJBdUOAW7VFRl6kXUDgQ56FeFaOdm0VBpJEcXGhI2FZpSDJUQwVIEqybXBhxhltFFH+amIkEIMpZEZSwEBACH5BAkFAAIALAsACwAqACoAAAj/AAUIHEiwoMAnXM4oSNEiocGHECMKMFACCZGLGDGWaCGxY8ExCMggobEwIcKGNJqIHONR4hKLQlJ4TEHgBowSLT9qiAJBR86DEHg++anDgpMHP3Va8DlTpZGkBo0AaeKlIwCVUCPKQAJAIhMgWSNu+KqValixIx+uIFPhrEu2Bp3IcNsRgxOdXOi+zUEQyA+9dcEehDszAKNPnxjN6NqUMciWNiLVykRlS6bJNloeFfhmzUxZmIb0VQTLRVPOLDtiIgSxD6LTAqSY9irLAEQDqDy/nUg44gw9EvHAscrWQO/Ww31jIk7x+ENXMyTOcMWci3ODgO4Ep8I8dlvpuDKrnMUlgjhqq6YYtdZ0xrz3z39sD6xAqDTsLlIg16+jxEMdWPFp1sVg8nnEhAqmaKKCBjlV0JtcgO0233UR8sZGQTdVGNddSmk44YUbVqWhFzyVtUGFYwl2W1oRbsUYWkC8GNZVMIhInFkzOpVUFlf8MBRRFZWR41JPWQXSUnS9ZIYGRZS0wUkLTYVkhE9udYVIWMq1BFMezocDRWEFBAAh+QQJBQACACwLAAsAKgAqAAAI/wAFCBxIsKCAMza60JABZWFCgxAjQjTyRYgTBAwhDNloEcbFL0YkiiRYEeMXiRtcdDw5MmIKIEiGhGx5cEgTIClokpQCAYBOkhB46qQY8+fEmD5FAphi4YxRlx9F7mCh4ylKCxckWvRiVWlRgyoVdB2pQChQCGNbBi2IBknStFeHDFzaBa5atwLbDm1SI0+eGXhbbqCaNw3NMpWGUTJhghKtSmUOZ6XbUosqQjhIClKl5S5RtbiCRAyC64dnGWRvCRLZ59HbiBfSWBwJI1VOlLCc0N46MgCglnNm7G44sk8A4H/IxqzL2gNy5Qx7H3rulbhUVC1PwRiOITUckTNucYrZ3WE7bc6jVaElbxb85Qqa0SsPGftw4sVxHEOWXHhvXzk1BHZXYSvYNd9cUxgY11kKQtTWE2whYECDDBp0gRNcUegFRlBNUZWCOiAVl1N2hXjBa0cJ2JUEIkrWk1UbXvSUSjfNJNgPNY41BkY4kQhWGwxhWGJJESznkAZMLWejgTqUxFFDP4D0U0AAIfkECQUAAgAsCgAKACwALAAACP8ABQgcSLDgQB0tVoxZolCHwYcQIwo8wxAIkx09XmjEaJGhxI8FFWTE0MYLRC9tMFggCVJiQhY0TLZ0QwPmmZYGx9jESRAlTJ4CNkBgkgJozos4AcjQ0MLow5cAPgq9wMXpyQtQPg6VafUp0qs4urr8eTSs2LFmCZZ5cRYkgTUhLTRti1Yt3Jk/GACykuDHBpxZsk6U27JHI1GFRowoFOoRBrxz2bAA2WJOKR5xUQG6qbWogLcg6VSCADFLJkBuhQQlG1HKKdWtXznROuVJC8IRvYRaELqTZ7ApLHxk8KoDZdlobTAZbgqnqBqddX7ci/NOlehvptOpPgI7c+dEvEuqLFP8OOncVL2wqCqREvTvdYlqLZVlPH2pFwSCnp6p/kPT3dE2GGV+XJbZHZyN55lSczGHGBiLNZYAZAPtl1oAdMgRgBlfAFZGT6zR5RVX+pUgYmsmxrXEiWWNyOJBuP0nH4vq0ZDciTVSBgVVdIkERVSFhejUEkKmRkCCPFFUJF47MRnTWUSuFJMbT/REU01S4pgGVip1qYGUSxnw4kEKuJADGwuZSWJLAQEAIfkECQUAAQAsCgAKACwALAAACP8AAwgcSLBggDM5upTIgkEGQ4VnDEqcKHFDBxo9LggZwsZGhY8dN4qkSLKgghcNJQAgCeBkypIU3ax5g2YlzIEtX97ESdPLToMuV/xcovFnzKIwIRpl2bCkDRkPljqFetSA1KQ9rAKlehVrVJMQuu6E8AasT7FeCW4cioYMGY5seRq52YWQqLuJ8NKgy3Vm0gakJix5IhDhhFADuGA9SNMpJUtCJr5oxOls1ZMwPUBmaemA1xIlL3y6kHT01IUl8XjYqQLPaRuch4w1tYEpUsmhbJaWfZnkh1ZGE/mwrXViDETBPxAniQGUbscylsc0TfdTxN4kJalgDel1aOrMwUuNDlmbZKDN0wOdDpDVcaUmuCdZnpgCQ+Eufz/lgaGgMAw8+s03nn9fffeJfpYc+AgUfKUwEGo3PXHSBz5oRBhfarWH1noZbjjVVmh4OJ6DIIpoVlUmEsihhwZouKJYTwkonVQPkHUdWzqNBZGMO9XYU1IKQcDjUi1aeJECBiTpwkUODZbifRg9JOWNNwUEACH5BAkFAAEALAkACQAuAC4AAAj/AAMIHEiw4EA3FVws6cJQoRuDECNKFPgkYRYCGMsM+aExY5uEE0MWBLBQiEkDXEZyQcnxpMiJRr60NPIyQMyONGsS9IJT58EOLn3e9OLTIEmNOleYLCqx4UulNpjCXDp1idSQTiPquHhV5EcAETl2ffoDYgohZ8Z6pfozR1IIZMhASFlT5ki0L5Ew6sS37yEYZBUQ5IoVRKc+R4AokADkiKBOB+hO/en1ECULYR0dsoq1BcUuhT1INgtic2eBSCeGMR369M2JPzKhqaupRFWEIU3Q8UnHRGeQExWh8Jkh0e8sMDsJ8fmi08OmUGFnyknbNnSF0qk/lX07O3Pn3YMjm/l+PDce3nuOg22qvCaS9tc3sI1IwRNRrJ4o/LYJOrlomKXpcBp/lbFmlGWcXfeZYFgVl4F2RjionVZzUbZdX36lUVeC/LnwFgNyebjhem2ptZ9BFZoIHYUcqngQXiy6uBOMCspoA0aBqSjffOV1dRSDb2m0AVMrBVVUkRhNeJ2QY+14EkoqsdSTjhZ59FFHHj0hY5QlOaRlUQEBACH5BAkFAAEALAgACAAwADAAAAj/AAMIHEiwoMEATx5wMXKwocOHBReySVOChsWLE7lA3NhQR4chFVfgcONxg8mSIXVw5Ajgyw8XbliSfAlgZUcbNG0ObBlS506QD3xG7DLEi88zFYXedGHTQFKlS1k+hRr1IU+qEJcUdbgBTResG4luqAqW61aDV5sC1dmihNGhLKVUcVSpbl0TUp7EHfr2YZZFlvBiSKEAQ4w4lgplCUsj6M+NMBJDaAgBUyMnjAmedQik0oG+BwEcqAQk65CfoNE+OrBy9Fezb2FC5JMoNVdMfEwLHCPBL2mdfxeb3W3g4YE+QvuwHo6weEdIAoQ2gKT3JnHOv32SqGRj+JPG2LsfxeUOO21o8uPFh3bb1fF5FkK3q0f7mvdDRTykK7J6OgBF44Ak1wB/14UH3GUE+lfEbIW4l9UigejWnErYRcdREo+0wV9fm1Hm2RlWgZEdcwX6ppiHCEpInIMoFiIAAoXBIEAhdQm3IVyMmUCXXZfgVZ2KqLG1lloM0WdbWQSZRx+SZK23BJNGmjYfkzCx2GRZWjknVRdFYmWEWEd5lYKXQ4aZk5lMkZlSWCJZVBOSXoTkJkknzUSRm1DuxJNLGE20Zp7lYRUQACH5BAkFAAIALAgACAAwADAAAAj/AAUIHEiwIMEHZ3AosMFQ4QODECNKPGig4ZgcLlIs7HCxy0UAE0NCRJgR40QvEth4NCBSJACLLQdygRnTIEmWNSl6zClzJ0+bPmM+wTjmZ8SbLTcsWWpUItKQRJueZDoVpNSqToNezdphpNatXIEKtQDijqKzICx4Eeqi4EwdLnlQuhMkioweJOD8mcQDh0utJqEqCtTl6B5Fb/72tCqRiKsPImM8hupTYcgpnm7EJJOZskCOJx0FyNnAEZupApQ+lFiWcVJBI6oiDD3lJxBPFWRPvMGo6aErulnDaTojUPCIiJtGUHQcYmejFzw1N5gcuqPpBQ3BUC4IO0EVw43qp/U+kLdv4Fy/HnyeU0Z1rygDR9xjnGceKlhBOxVN2jTq1Etcxp6Atf33kmsQRTEZgZQxpt9ugyGx2kFN2PFeVjq5ZIInEd4VBQWTeGKCBH8hmFFMa3ywIVomfLDGWEDhBBZWMc4Y1lEq2VgjjToCGGCDCG51YJDwEWlUfBvkVJGMUjl05EpNOVRUlDSxxZGRSlq0VnoPznjgSkRt1JFGWHqZkJgNGRUQACH5BAkFAAEALAcABwAyADIAAAj/AAMIHEiwoEGBT3QAeHKwocOHB7kYkGAjx5eLGClygcjxoRcFFjVKRKiwhUgvHVMGkBhyA8cNH0OqdLiw4saZK0uixDnwjEyePUsCTWERaMSiMzUabSg0JUgjS2lW7FihA4CoHq1CdIMUq1Q3X72+7FoQpMo2YkIcWEukzdkUR69uJVMnESABMVAMAGSXzM6sOgyahWjhUJ03gcu+6XPIwtiyZA/6UDThJlMHlLeSdQH3IV0hTgfd0Bz070Ehg4YkRUQAMEmPonnCGGT56GuHMfK4xLlBjoistyMWemN0R6HdtnM6RJ2Ypw7WUoMb5HEgqgcU0WGaLoglRtTcX2s6z7z73Ur4i+PVlD8vV7L3peCZoo9M0IH5peTlSy/IRBHYoYpoEJ1yTA1XHCLNCWbDfgVNUAdyZ/WB3YAj0RQbTrNBqGBQD70AXYStDchgg4aoRtWFIubEkGcfssiAZv9xSJhhL2i4koeNPQZZezTZl4gDPgQZpCCHbRdXclQREQRbbb0lH49iHflklFO6RiVBPq0I45UkKeAURlc+oFVSY3plUgVD/WRUTQ8slZBNzhFFkZk2cTVWS2Fa1VKFLJ2pZp44rJDRoHty+SSbUM4UEAAh+QQJBQABACwHAAcAMgAyAAAI/wADCBxIsGBBAFy8nDHAUCEAgxAjSjSIsIIEBSkaummhEeOZiSAlPvB4ceLIhRhDqgygI6PClQJbooQZkaMbmgc3tsA58InFhzxzZsRZMWhNHClXnjRq8mXIpUybAhWZNKrUplZVjqQKs+WOKBF2lNDR1QDEoiHfNDBUSA+WKnraNnijtWrPmyCLxCk04w0XglzUti3y1CzgjSBhMJogwaQDRizy2k2YuE+WlUL8fJB818hEKZZpdghN1TNLBZ8LCeGZuQzW0yLvoDCaQY/ps6hlSnTyx4tRN6Rxw5aoQkRULSqk+q65iDDTEoWW43YKMcuiDVGfqC5tuPqfrHoscL/f7QE8g/ER1YAxj947+6Nb0zOaGhQA5PYUm0ctox9+d4hU+HBcHFiVJN8Dv90xBVfDnbWHGrTtgd1RMX1EXgms3cegEQYeKFofs5V2V2WuqfQGiJx1Vhljjmko4mFp7dXEfzE1cQcWzr122BMmrvXWCED+SFdht8GoFARfhTVWWfBlReSLTk4nGY9RClUXfVHaVBaWViHF5ZQ01vfTbx6RyVGXDk14ZJlOeumQckNVWSFKLnVkk0VyTvdTR0YFBAAh+QQJBQADACwHAAYAMgA0AAAH/4ADgoOEhYaCG0+Kh4yNjocAD25nLQaWl5MAj5uOOpmVkpFGol6gk5yoA6Sem4qfqY2rsIillLOEtU+3uJm7tbuQlrO9wMFuqZQbxbG2rWfLzo+/0J2VzLqoom0p2JyuxpxdZCd95eYnZF3e1oWlm10C5ekunhI/430UbNG8yrFi+YY4KoElnz9Gonh1OvDHgjcEfxZ4qTboVCyG+5BtOXDQ0LRIjjjYcXGrAx4UFFVNZAShITAhfiAwUxkywzKR11ix/CNhmQuXCJM14kCBmgA1ORvtcQjNwpykkPqkoZZGalB2Hq1CU6DVo9BDMKmpinm1I6EsdcQOIGvMyM5jW6W7trv0lqrcfm4ZLbUL9RCKAEZF9DWElmSxnzLLXoRz84RZhSB3ynh5VyHNoZXf3SGTMjLmjNlOSst7GeECOgLD2eGYktZjXD4iggYbpOHrilhpaZbXhd6keyYC8uvXqgmYP+fONSF9Ddy6IdCjZ2sWTG3z1tYtj9aR3TWy3NRGrfyuSWzCknQPp/flqht56uqTuS9rUa14U/Jz4e+OF/9+/qNREwgAIfkECQUAAgAsBgAGADQANAAACP8ABQgcSLCgQQEbnig0svCgw4cQDS58QJGLjosWKz6JyBHiRAAbHSYEWbGjSYEYN5xESHKlR4ouC7aMOXAkTYkYaTa8eZBhyJM+eT7c2ZGhUI4WiyY9+lJlxKVMXz4FUPRFjAYm+MzJKuLFA6VDdSBlQIfPESRvSkBIe0QOHQZfp4p0+vBCIBUyqPaUoQKQDLk4PWaQw+LnUAuE9c6VyUUwlTYru1g5IDYsQZAQ21aIWYHKEak1M/NZcnMMYY81G4vUg0QoEj02LLN8yMGEEaFGqkyQDVVmnjJMCeSJGxhhXSpRBfgdOtshBy3JC+wW2fygCQvRVYRV+PB0VL7bK0vYhJ08DfmeGVfH/m7F8OWgDudkKb8lfHcg9O07FxG9AHPjDvFFF0+5QfFfQuoFRwdxjKGkmEFa2IYbFvwtVt14f/H02hmyXVjQDlusUNoWF3ToYUETVLGZS535ZyJ3Qx3wWGST3WbiiZcFkBhSfNUgHno4DSjRB3ng9eBlfOXhg5ANAvlUAWWddYFabLkFF1JHvlcUWXp06eUBFhgAFmjJWdhUmegxmSaa79molHtREWWSTWXOxNlFTMmpU293unmUnUDxGWdOU8HIZmoZpaQoZoeG55OaJwUEACH5BAkFADIALAYABgA0ADQAAAf/gDKCg4SFhhtPiUaLioaOj5CPiIyUk5UbkZmZiomalk+aoYONooWMpZGLqJJGq6aqro6TsZ2xqaCosLa3pbq7vJ64v8GbraEPGFEMHBPLGA+ivq+hWRlWJ8zKyypWBSXHkNKGLwtVTh2SCFUNBMWyxulbIi2eIvIAwITwsgLmqBDr6IWbJmnAAgWuJBwIgI+VvkgFTFSwlcJEgVQPH0GxsuTXEo4DBdU6hG2YjAgjGh7Cta8QEokmH2BxErIloZImT54YiEjSlo45PwJd2dMRDA85B3mg+W6kyxhJBWmB2hSToyNRoupkJU4qE60sBHC1akjA16hMKIx95IAEWAdroa8meBt3nNioZuua+pm0At93Mmwq1ZA0bU1IFpQI22VEyZSai/UtNekEhEqCRY1SGUoLpKSMbE8YoGgR4yjBIvshXKWQYbiWkff6K1UioGl9qFPfq7f7tSxNBDzMRMdvXbt8h3hvweZs2/Imlz/7PnZBWzNlK6KRdagVMLHuuHvF7jseOXlaudF7LJ8rvXj2xeAnJBWsK/lLnCiBv5V/u6tAACH5BAUFAEYALA8ADwAiACIAAAf8gEaCg4SDLj8EQBoviIWOj4UrCBQmHpQ8lzMIEpCdRhVEIAyMjgaJojiehRYnIhWqnyKtsDoxHkOwhD+Wr5C1DSu5kQ01Oo8bGQItwqXEG443t8zHHhHDuNOPQybYgiEM2Z0+PIbc4b7SRh8O5+IJ3jvtkEAjn+byzRIv9fiPIKf9/F2AkSEgNAusDBbigHCGQkKaIBx4OGgEDQX3FOIwJwADxQsD4FEsYM2eAo0qbAxKIEJhjHfluskTklEQqw74csyCBqxdiwUx0PXMtsJZp180ppWo9EAVMqawtpmI8CxXkRqVLJyMxArqtA0ybFXKZIlXVZ+IFCVykSsQADs=) center/64px no-repeat;color:#fff;content:\\\"\\\";font-size:128px;height:128px;left:50%;pointer-events:none;position:absolute;top:50%;transform:translate3d(-50%,-50%,0);width:128px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__ui--hidden .pswp__top-bar{transform:translateY(-15px)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__top-bar{background:none;height:30px;margin-top:env(safe-area-inset-top);padding:16px;transition:transform .3s ease-out,opacity .3s cubic-bezier(.4,0,.22,1)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__counter{background-color:#00000080;border-radius:15px;font-family:Hiragino Kaku Gothic ProN,\\\\30e1\\\\30a4\\\\30ea\\\\30aa,sans-serif;font-size:12px;font-weight:700;height:30px;left:16px;line-height:30px;padding:0 16px;top:16px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button{background-size:180px 60px;height:30px;margin-left:8px;opacity:1;width:30px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button:before{background-color:#00000080;border-radius:50%;content:\\\"\\\";inset:0;position:absolute;z-index:-1}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--close{background-position:0 -30px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--share{background-position:-30px -30px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--zoom{background-position:-60px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--zoomed-in .pswp__button--zoom{background-position:-90px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp--fs .pswp__button--fs{background-position:-30px 0}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--left:before{background-position:-94px -30px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__button--arrow--right:before{background-position:-64px -30px}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption{padding-bottom:env(safe-area-inset-top)}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption--empty{display:block;opacity:0;pointer-events:none;transition:opacity .2s ease-out}.lightbox-wrapper[_ngcontent-%COMP%] .pswp__caption__center{min-height:40px}\"],\n changeDetection: 0\n });\n }\n }\n return LightboxComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nclass PhotoGalleryConfig {}\nlet LightboxService = /*#__PURE__*/(() => {\n class LightboxService {\n constructor(applicationRef) {\n this.applicationRef = applicationRef;\n this.lightbox = null;\n this.lightboxElement = null;\n }\n create() {\n this.lightbox = createComponent(LightboxComponent, {\n environmentInjector: this.applicationRef.injector\n });\n document.body.appendChild(this.lightbox.location.nativeElement);\n this.applicationRef.attachView(this.lightbox.hostView);\n this.lightboxElement = this.lightbox.instance.element;\n }\n destroy() {\n if (this.lightbox) {\n this.applicationRef.detachView(this.lightbox.hostView);\n this.lightbox = null;\n this.lightboxElement = null;\n }\n }\n /**\n * Returns the lightbox element.\n * This method is called after the lightbox is created.\n * @returns HTMLElement\n */\n getLightboxElement() {\n return this.lightboxElement.nativeElement;\n }\n static {\n this.ɵfac = function LightboxService_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || LightboxService)(i0.ɵɵinject(i0.ApplicationRef));\n };\n }\n static {\n this.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: LightboxService,\n factory: LightboxService.ɵfac,\n providedIn: 'root'\n });\n }\n }\n return LightboxService;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nconst DEFAULT_OPTIONS = {\n history: false,\n closeEl: true,\n captionEl: false,\n fullscreenEl: false,\n zoomEl: true,\n shareEl: false,\n counterEl: true,\n arrowEl: false,\n preloaderEl: true\n};\nlet PhotoGalleryGroupDirective = /*#__PURE__*/(() => {\n class PhotoGalleryGroupDirective {\n constructor(photoGalleryConfig, lightboxService) {\n this.photoGalleryConfig = photoGalleryConfig;\n this.lightboxService = lightboxService;\n this.onPhotoGalleryInit = new EventEmitter();\n this.onPhotoGalleryDestroy = new EventEmitter();\n this.galleryItems = {};\n this.galleryItemIds = new Set();\n this.galleryImages = [];\n this.defaultOptions = {\n ...DEFAULT_OPTIONS,\n ...this.photoGalleryConfig?.defaultOptions\n };\n }\n registerGalleryItem(item) {\n const image = {\n id: item.id,\n src: item.imageUrl,\n ...(item.caption ? {\n title: item.caption\n } : {}),\n w: 0,\n h: 0,\n doGetSlideDimensions: true\n };\n this.galleryItems[item.id] = {\n id: item.id,\n element: item.element,\n image\n };\n this.galleryItemIds.add(item.id);\n }\n unregisterGalleryItem(id) {\n this.galleryItemIds.delete(id);\n }\n async openPhotoSwipe(id) {\n this.lightboxService.create();\n if (this.galleryItems[id].image.doGetSlideDimensions) {\n const targetImage = await loadImage(this.galleryItems[id].image.src);\n this.galleryItems[id].image.w = targetImage.naturalWidth;\n this.galleryItems[id].image.h = targetImage.naturalHeight;\n delete this.galleryItems[id].image.doGetSlideDimensions;\n }\n this.galleryImages = [...this.galleryItemIds].map(key => this.galleryItems[key].image);\n const idx = this.galleryImages.findIndex(image => image.id === id);\n const options = {\n ...this.defaultOptions,\n ...this.options\n };\n options.index = idx;\n options.getThumbBoundsFn = imageIndex => {\n const key = this.galleryImages[imageIndex].id;\n const thumbnail = this.galleryItems[key].element;\n const origin = this.galleryItems[key].image;\n const pageYScroll = window.pageYOffset || document.documentElement.scrollTop;\n const rect = thumbnail.getBoundingClientRect();\n const thumbnailRate = rect.height / rect.width;\n const originRate = origin.h / origin.w;\n let x, y, w;\n if (thumbnailRate > originRate) {\n // portrait\n y = rect.top + pageYScroll;\n w = origin.w * rect.height / origin.h;\n x = rect.left - (w - rect.width) / 2;\n } else {\n // landscape\n const imageHeight = origin.h * rect.width / origin.w;\n x = rect.left;\n w = rect.width;\n y = rect.top + pageYScroll - (imageHeight - rect.height) / 2;\n }\n return {\n x,\n y,\n w\n };\n };\n const photoSwipe = this.lightboxService.getLightboxElement();\n this.gallery = new PhotoSwipe(photoSwipe, PhotoSwipeUI_Default, this.galleryImages, options);\n this.gallery.listen('gettingData', (_, slide) => {\n if (slide.doGetSlideDimensions) {\n setTimeout(async () => {\n await this.getSlideDimensions(slide);\n }, 300);\n }\n });\n this.gallery.listen('imageLoadComplete', async (_, slide) => {\n if (slide.doGetSlideDimensions) {\n await this.getSlideDimensions(slide);\n }\n });\n this.gallery.listen('destroy', () => {\n this.lightboxService.destroy();\n this.onPhotoGalleryDestroy.emit();\n });\n this.onPhotoGalleryInit.emit();\n this.gallery.init();\n }\n async getSlideDimensions(slide) {\n if (!slide.doGetSlideDimensions) {\n return;\n }\n const image = await loadImage(slide.src).catch(() => null);\n slide.doGetSlideDimensions = false;\n slide.w = image.naturalWidth;\n slide.h = image.naturalHeight;\n this.gallery.invalidateCurrItems();\n this.gallery.updateSize(true);\n }\n static {\n this.ɵfac = function PhotoGalleryGroupDirective_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PhotoGalleryGroupDirective)(i0.ɵɵdirectiveInject(PhotoGalleryConfig, 8), i0.ɵɵdirectiveInject(LightboxService));\n };\n }\n static {\n this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: PhotoGalleryGroupDirective,\n selectors: [[\"\", \"photoGalleryGroup\", \"\"]],\n inputs: {\n options: [0, \"photoGalleryGroup\", \"options\"]\n },\n outputs: {\n onPhotoGalleryInit: \"onPhotoGalleryInit\",\n onPhotoGalleryDestroy: \"onPhotoGalleryDestroy\"\n }\n });\n }\n }\n return PhotoGalleryGroupDirective;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction loadImage(path) {\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.onload = () => resolve(image);\n image.onerror = e => reject(e);\n image.src = path;\n });\n}\nlet PhotoGalleryDirective = /*#__PURE__*/(() => {\n class PhotoGalleryDirective {\n constructor(el, photoGalleryGroup) {\n this.el = el;\n this.photoGalleryGroup = photoGalleryGroup;\n }\n async openPhotoSwipe() {\n await this.photoGalleryGroup.openPhotoSwipe(this.id);\n }\n ngAfterContentInit() {\n this.id = this.photoGalleryTrackBy || this.imageUrl;\n this.photoGalleryGroup.registerGalleryItem({\n id: this.id,\n element: this.el.nativeElement,\n imageUrl: this.imageUrl,\n caption: this.photoGalleryCaption\n });\n }\n ngOnDestroy() {\n this.photoGalleryGroup.unregisterGalleryItem(this.id);\n }\n static {\n this.ɵfac = function PhotoGalleryDirective_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PhotoGalleryDirective)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(PhotoGalleryGroupDirective));\n };\n }\n static {\n this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: PhotoGalleryDirective,\n selectors: [[\"\", \"photoGallery\", \"\"]],\n hostBindings: function PhotoGalleryDirective_HostBindings(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵlistener(\"click\", function PhotoGalleryDirective_click_HostBindingHandler() {\n return ctx.openPhotoSwipe();\n });\n }\n },\n inputs: {\n imageUrl: [0, \"photoGallery\", \"imageUrl\"],\n photoGalleryTrackBy: \"photoGalleryTrackBy\",\n photoGalleryCaption: \"photoGalleryCaption\"\n }\n });\n }\n }\n return PhotoGalleryDirective;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet PhotoGalleryModule = /*#__PURE__*/(() => {\n class PhotoGalleryModule {\n static forRoot(config) {\n return {\n ngModule: PhotoGalleryModule,\n providers: [{\n provide: PhotoGalleryConfig,\n useValue: config\n }]\n };\n }\n static {\n this.ɵfac = function PhotoGalleryModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PhotoGalleryModule)();\n };\n }\n static {\n this.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: PhotoGalleryModule\n });\n }\n static {\n this.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({});\n }\n }\n return PhotoGalleryModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/*\n * Public API Surface of core\n */\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { DEFAULT_OPTIONS, PhotoGalleryDirective, PhotoGalleryGroupDirective, PhotoGalleryModule };\n","import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from 'src/app/services/right-sidebar.service';\nimport { BaseService } from \"../../../../../services/base.service\";\nimport { TicketService } from \"../../../../../services/ticket.service\";\nimport { DatePipe } from '@angular/common';\nimport { FormControl, FormGroup, Validators } from \"@angular/forms\";\nimport { TicketMessage } from \"./ticket.interface\";\nimport { FileUploadComponent } from \"../../../../../modals/file-upload/file-upload.component\";\nimport { FileSystemService } from \"../../../../../services/file-system.service\";\nimport { MatDialog } from \"@angular/material/dialog\";\nimport { ModalData } from \"../../../../../model/modal-data.model\";\nimport { ModalComponent } from \"../../../../modal/modal.component\";\nimport { environment } from '../../../../../../environments/environment';\n\n@Component({\n selector: 'app-ticket',\n templateUrl: './ticket.component.html',\n styleUrls: ['./ticket.component.scss'],\n})\nexport class TicketComponent implements OnInit{\n\n public ticketForm!: FormGroup;\n\n public ticket!: any;\n public messages: any[] = [];\n public title!: string;\n public isLoader: boolean = false;\n public ticketFiles: any[] = [];\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n readonly baseService: BaseService,\n public readonly ticketService: TicketService,\n private datePipe: DatePipe,\n public fileSystemService: FileSystemService,\n private dialog: MatDialog,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.ticket = options.data?.ticket;\n this.title = this.ticket.name;\n }\n\n openModal(data: ModalData) {\n\n const modalRef = this.dialog\n .open(ModalComponent, {\n data,\n panelClass: data.containerClass,\n enterAnimationDuration: data?.enterAnimationDuration || '0ms',\n exitAnimationDuration: data?.exitAnimationDuration || '0ms',\n autoFocus: data?.autoFocus || true,\n width: this.baseService.isMobile ? '100%' : data?.width,\n maxWidth: this.baseService.isMobile ? '100vw' : '80vw',\n height: data?.height,\n });\n\n return modalRef.afterClosed();\n }\n\n ngOnInit() {\n this.initForm();\n this.initMessages();\n }\n\n public initForm() {\n this.ticketForm = new FormGroup({\n text: new FormControl(null, [Validators.required])\n })\n }\n\n public initMessages() {\n this.messages.splice(0, this.messages.length)\n this.isLoader = true;\n this.ticketService.getTicket(this.ticket.id)\n .subscribe((res: any) => {\n this.ticket = res;\n });\n\n const params = {\n take: 25,\n page: 1,\n search: '',\n id: this.ticket.id,\n }\n\n this.ticketService.getMessageTicket(params)\n .subscribe((res: any) => {\n if (res.items) {\n this.messages.push(...res.items);\n }\n this.isLoader = false;\n });\n }\n\n closeBar() {\n this.rightSidebarService.close();\n }\n\n formatDate(date: string): string {\n return this.datePipe.transform(date, 'dd.MM.yyyy HH:mm') ?? '';\n }\n\n sendMessage(){\n if(this.ticketForm.valid){\n const payloadForm = this.ticketForm.getRawValue();\n\n let attachment = null;\n\n if (this.ticketFiles.length) {\n attachment = this.ticketFiles;\n }\n\n const payload = {\n text: payloadForm.text,\n ticket_id: this.ticket.id,\n attachment: attachment,\n };\n\n this.ticketService.sendMessage(payload)\n .subscribe({\n next: () => {\n this.initMessages();\n this.ticketForm.reset();\n this.ticketFiles.splice(0, this.ticketFiles.length);\n },\n });\n }\n }\n\n public pressCmdEnter(e: Event): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'cmd_enter' && !this.baseService.isMobile) {\n this.sendMessage();\n }\n }\n\n public pressShiftEnter(e: Event): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'shift_enter' && !this.baseService.isMobile) {\n this.sendMessage();\n }\n }\n\n public pressEnter(e: Event): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'enter' && !this.baseService.isMobile) {\n e.preventDefault();\n this.sendMessage();\n }\n }\n\n openFileUpload() {\n const dialogData = {\n title: 'choose_files',\n translate: true,\n component: FileUploadComponent,\n width: '70vw',\n };\n this.openModal(dialogData)\n .subscribe((res: string[]) => {\n res.forEach((fileId: string ) => {\n this.ticketFiles.push({url: environment.baseURL + '/assets/ticket/' + fileId});\n })\n });\n }\n\n deletePhoto(index: number) {\n this.ticketFiles.splice(index, 1);\n }\n}\n","
\n
\n

{{ 'ticket_system.ticket' | translate }}: {{ title }}

\n\n \n close\n \n
\n\n \n\n \n
\n \n
\n
\n\n \n
\n \n \n \n #{{messages.length - i}} {{ 'ticket_system.message' | translate }}: {{ formatDate(message.created_at) }}\n \n
Адміністратор
\n
\n\n \n

{{ message.text }}

\n\n
\n
\n
\n \"{{i}}\"\n
\n
\n
\n
\n
\n
\n
\n\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n\n \n
\n\n \n send\n \n
\n
\n \n {{ 'ticket_system.files' | translate }}\n \n
\n
\n \n delete\n \n \"{{i}}\"\n
\n
\n
\n
\n
\n
\n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { BaseService } from '../../../../../services/base.service';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { Subject, takeUntil } from 'rxjs';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { InviteForm } from './invite-manager.interface';\nimport { IError, IGroups } from '../../../../../interfaces';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { ERROR_MESSAGES } from '../../../../../enum/constants';\n\n\n@Component({\n selector: 'app-invite-manager',\n templateUrl: './invite-manager.component.html',\n styleUrl: './invite-manager.component.scss'\n})\nexport class InviteManagerComponent implements OnInit, OnDestroy {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Form\n public inviteForm!: FormGroup;\n\n// Var`s\n public groups: IGroups[] = [];\n public isLoader: boolean = false;\n\n constructor(\n public crmService: CrmService,\n public baseService: BaseService,\n public notificationService: NotificationService,\n public rightSidebarService: RightSidebarService,\n ) {\n }\n\n public ngOnInit(): void {\n this.isLoader = true;\n this.initForm();\n this.getAllGroups();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n private initForm(): void {\n this.inviteForm = new FormGroup({\n email: new FormControl('', [Validators.required, Validators.email]),\n role: new FormControl(null, Validators.required),\n });\n }\n\n public onSendInvite(): void {\n this.inviteForm.markAllAsTouched();\n\n if (this.inviteForm.valid) {\n this.isLoader = true;\n const { email, role } = this.inviteForm.getRawValue();\n\n this.crmService.sendInvite({\n email: email,\n role: role.name,\n group_id: role.id,\n }).subscribe({\n next: () => {\n this.isLoader = false;\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n })\n }\n }\n\n private getAllGroups(): void {\n this.crmService.getAllGroups({ page: 1, take: 25 })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v) => {\n this.groups = v.items;\n this.isLoader = false;\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n });\n }\n}\n","
\n
\n

{{ 'invite_new_manager' | translate }}

\n \n close\n \n
\n\n \n\n \n
\n \n
\n
\n\n
\n
\n
\n \n \n {{'email_field_cannot_be_empty' | translate}}\n \n
\n\n
\n \n \n {{'field_cannot_be_empty' | translate}}.\n \n
\n
\n\n
\n {{'cancel' | translate}}\n
\n {{ 'invite' | translate }}\n
\n
\n
\n","import { FormControl } from '@angular/forms';\nimport { languages } from '../../../../../enum/constants';\n\n\nexport interface GroupForm {\n name: FormControl;\n x_data: any;\n}\n\nexport interface Permission {\n id: string,\n name: string,\n key: string,\n type: string\n}\n\nexport interface GroupPermission {\n id: string,\n group_id: string,\n permission_id: string,\n}\n\nexport const PERMISSIONS_KEYS = {\n ACCESS_CHANNELS: 'access_channels',\n ACCESS_CHATS: 'access_chats',\n}\n\nexport const PERMISSIONS: any ={\n 'access_team': languages[localStorage.getItem('language') || 'ua'].permissions.access_team,\n 'action_team': languages[localStorage.getItem('language') || 'ua'].permissions.action_team,\n 'access_apps': languages[localStorage.getItem('language') || 'ua'].permissions.access_apps,\n 'action_apps': languages[localStorage.getItem('language') || 'ua'].permissions.action_apps,\n 'access_integration': languages[localStorage.getItem('language') || 'ua'].permissions.access_integration,\n 'action_integration': languages[localStorage.getItem('language') || 'ua'].permissions.action_integration,\n 'access_crm': languages[localStorage.getItem('language') || 'ua'].permissions.access_crm,\n 'action_crm': languages[localStorage.getItem('language') || 'ua'].permissions.action_crm,\n 'access_contacts': languages[localStorage.getItem('language') || 'ua'].permissions.access_contacts,\n 'action_contacts': languages[localStorage.getItem('language') || 'ua'].permissions.action_contacts,\n 'access_logistics': languages[localStorage.getItem('language') || 'ua'].permissions.access_logistics,\n 'action_logistics': languages[localStorage.getItem('language') || 'ua'].permissions.action_logistics,\n 'access_company': languages[localStorage.getItem('language') || 'ua'].permissions.access_company,\n 'action_company': languages[localStorage.getItem('language') || 'ua'].permissions.action_company,\n 'access_payment': languages[localStorage.getItem('language') || 'ua'].permissions.access_payment,\n 'action_payment': languages[localStorage.getItem('language') || 'ua'].permissions.action_payment,\n 'access_channels': languages[localStorage.getItem('language') || 'ua'].permissions.access_channels,\n 'action_channels': languages[localStorage.getItem('language') || 'ua'].permissions.action_channels,\n 'access_p_channels': languages[localStorage.getItem('language') || 'ua'].permissions.access_p_channels,\n 'action_p_channels': languages[localStorage.getItem('language') || 'ua'].permissions.action_p_channels,\n 'access_settings': languages[localStorage.getItem('language') || 'ua'].permissions.access_settings,\n 'action_settings': languages[localStorage.getItem('language') || 'ua'].permissions.action_settings,\n 'access_files': languages[localStorage.getItem('language') || 'ua'].permissions.access_files,\n 'action_files': languages[localStorage.getItem('language') || 'ua'].permissions.action_files,\n 'access_chats': languages[localStorage.getItem('language') || 'ua'].permissions.access_chats,\n 'action_chats': languages[localStorage.getItem('language') || 'ua'].permissions.action_chats,\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { GroupForm, GroupPermission, Permission, PERMISSIONS, PERMISSIONS_KEYS } from './create-group.interface';\nimport { Subject, takeUntil } from 'rxjs';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { IError } from '../../../../../interfaces';\nimport { ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport * as _ from 'lodash';\nimport { trimValidator } from '../../../../../helpers';\nimport { CHIP_TYPES } from \"../../../../chip/chip.interface\";\nimport { ChannelService } from '../../../../../services/channel.service';\n\n\n@Component({\n selector: 'app-create-group',\n templateUrl: './create-group.component.html',\n styleUrl: './create-group.component.scss'\n})\nexport class CreateGroupComponent implements OnInit, OnDestroy {\n// Imports\n protected readonly PERMISSIONS = PERMISSIONS;\n protected readonly CHIP_TYPES = CHIP_TYPES;\n\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Form\n public groupForm!: FormGroup;\n\n// Var`s\n private priority = ['CRM', 'COMPANY', 'PAYMENTS', 'TEAM', 'INTEGRATION', 'CHANNELS', 'CHATS', 'APPS', 'SETTINGS', 'CONTACTS', 'LOGISTICS', 'FILES'];\n\n public initialValue: any;\n public group: any;\n public group_id: string;\n\n public permissions: Permission[] = [];\n public groupPermissions: string[] = [];\n public groupedPermissions: { type: string, permissions: Permission[] }[] = [];\n public startGroupPermissions: string[] = [];\n public channels: any[] = [];\n\n public isLoaderPermissions: boolean = false;\n public isHasChange: boolean = false;\n public isLoader: boolean = false;\n\n constructor(\n private readonly crmService: CrmService,\n public readonly baseService: BaseService,\n public readonly dialogService: DialogService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly channelService: ChannelService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.group_id = options.data?.group_id;\n }\n\n public ngOnInit(): void {\n this.isLoader = true;\n this.isLoaderPermissions = true;\n this.onGetAllPermissions();\n this.getChannels();\n if (this.group_id) {\n this.onGetGroupById();\n this.onGetAllPermissionsForGroup();\n } else {\n this.initForm();\n }\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm(): void {\n if (this.group_id) {\n this.groupForm = new FormGroup({\n name: new FormControl(this.group?.name, [Validators.required, trimValidator]),\n x_data: new FormGroup({\n channels: new FormControl(this.group.x_data?.channels),\n }),\n });\n this.isLoader = false;\n this.onCreateGroupFormValueChange();\n } else {\n this.groupForm = new FormGroup({\n name: new FormControl('', [Validators.required, trimValidator]),\n x_data: new FormGroup({\n channels: new FormControl(),\n }),\n });\n this.isLoader = false;\n }\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n public onCreateGroup(): void {\n this.groupForm.markAllAsTouched();\n\n if (this.groupForm.valid) {\n const { name } = this.groupForm.getRawValue();\n this.crmService.createGroup({\n name,\n permissions: this.groupPermissions\n })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n }\n\n public onUpdateGroup(): void {\n if (this.groupForm.valid) {\n const { name, x_data } = this.groupForm.getRawValue();\n this.crmService.updateGroup({\n name,\n permissions: this.groupPermissions,\n x_data: x_data,\n }, this.group_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n }\n\n public onGetAllPermissions(): void {\n this.crmService.getAllPermissions()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v: any) => {\n this.permissions = [...v];\n this.groupPermissionsByType();\n if (!this.group_id) {\n this.groupPermissions = [...this.permissions.map((v: Permission) => v.id)];\n this.isLoaderPermissions = false;\n }\n },\n error: (e: any) => {\n this.isLoaderPermissions = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n\n public onGetAllPermissionsForGroup(): void {\n this.crmService.getAllPermissionsByGroupId(this.group_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v: any) => {\n this.groupPermissions = [...v.map((v: GroupPermission) => v.permission_id)];\n this.startGroupPermissions = [...this.groupPermissions];\n this.isLoaderPermissions = false;\n },\n error: (e: any) => {\n this.isLoaderPermissions = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n\n public onSelectRadio(e: any, permission: Permission): void {\n if (e && !this.groupPermissions.includes(permission.id)) this.groupPermissions.push(permission.id);\n if (!e && this.groupPermissions.includes(permission.id)) this.groupPermissions = this.groupPermissions.filter((v: string) => v !== permission.id)\n\n this.checkIsHasChange();\n }\n\n public onSelectRadioAll(e: any): void {\n if (e.value === 1) this.groupPermissions = [...this.permissions].map(v => v.id);\n if (e.value === 0) this.groupPermissions = [];\n\n this.checkIsHasChange();\n }\n\n public onGetGroupById(): void {\n this.crmService.getGroupById(this.group_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v: any) => {\n this.group = v;\n this.initForm();\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n\n public onDeleteGroup(): void {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.create_group`,\n });\n\n dialogRef\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n if (res) {\n this.crmService.deleteGroup(this.group_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted, '')\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n\n private onCreateGroupFormValueChange(): void {\n const initialValue = { ...this.groupForm.getRawValue() };\n this.initialValue = this.groupForm.getRawValue().name;\n\n this.groupForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.isHasChange = !_.isEqual(value, initialValue);\n });\n }\n\n private checkIsHasChange(): void {\n this.isHasChange = !_.isEqual(this.groupPermissions, this.startGroupPermissions) ||\n this.initialValue !== this.groupForm.controls.name.value;\n }\n\n private groupPermissionsByType(): void {\n const grouped = this.permissions.reduce((acc, permission) => {\n const type = permission.type;\n if (!acc[type]) {\n acc[type] = [];\n }\n acc[type].push(permission);\n return acc;\n }, {} as { [key: string]: Permission[] });\n\n const groupedPermissions = Object.keys(grouped).map(type => ({\n type,\n permissions: grouped[type]\n }));\n\n this.groupedPermissions = groupedPermissions.sort((a, b) => {\n return this.priority.indexOf(a.type) - this.priority.indexOf(b.type);\n });\n }\n\n private getChannels() {\n this.channelService.getChannels()\n .subscribe({\n next: (res) => {\n this.channels = res;\n },\n error: () => {\n\n }\n });\n }\n\n public onSelectChannels(items: any) {\n this.isHasChange = true;\n }\n\n protected readonly PERMISSIONS_KEYS = PERMISSIONS_KEYS;\n}\n","
\n
\n

{{ !group_id ? ('create_new_role' | translate) : ('update_your_role' | translate) + (group?.name ? ' ' + group.name : '') }}

\n \n close\n \n
\n\n \n\n
\n \n
\n\n
\n
\n
\n \n \n {{'field_cannot_be_empty' | translate}}.\n \n
\n\n
\n \n
\n\n
\n

{{'rights_for_roles' | translate}}:

\n
1\" [ngStyle]=\"{'grid-template-columns': baseService.isMobile ? '1fr' : '1fr 1fr'}\">\n \n \n \n {{'allowed' | translate}}\n \n \n {{'denied' | translate}}\n \n \n
\n

\n ({{'you_can_choose_from_several_options'| translate}})
\n {{'the_user_is_entitled_to' | translate}}:\n

\n\n
\n \n \n {{ ('permissions_types.' + group.type.toLowerCase()) | translate }}: \n \n \n
\n
\n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n\n\n
\n {{'cancel' | translate}}\n {{ 'save' | translate }}\n \n\n
\n\n {{ 'save' | translate }}\n \n \n delete\n \n
\n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { IError, IGroups } from '../../../../../interfaces';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { UpdateManagerForm } from './update-manager.interface';\nimport * as _ from 'lodash';\nimport { DialogService } from '../../../../../services/dialog.service';\n\n\n@Component({\n selector: 'app-update-manager',\n templateUrl: './update-manager.component.html',\n styleUrl: './update-manager.component.scss'\n})\nexport class UpdateManagerComponent implements OnInit, OnDestroy {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Form\n public updateManagerForm!: FormGroup;\n\n// Var`s\n public groups: IGroups[] = [];\n\n public manager: any;\n public manager_id: string;\n public isHasChange: boolean = false;\n\n public managers: string[] = [];\n\n constructor(\n public crmService: CrmService,\n public baseService: BaseService,\n public dialogService: DialogService,\n public notificationService: NotificationService,\n public rightSidebarService: RightSidebarService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.manager_id = options.data?.manager_id;\n this.managers = options.data?.managers;\n }\n\n public ngOnInit(): void {\n this.onGetAllGroups();\n if (this.manager_id) this.onGetManagerById();\n else this.initForm();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n private initForm(): void {\n if (this.manager_id) {\n this.updateManagerForm = new FormGroup({\n group_id: new FormControl(this.manager?.group_id, Validators.required),\n });\n this.onCreateGroupFormValueChange();\n } else {\n this.updateManagerForm = new FormGroup({\n group_id: new FormControl('', Validators.required),\n });\n }\n }\n\n private onCreateGroupFormValueChange() {\n const initialValue: any = { ...this.updateManagerForm.getRawValue() };\n this.updateManagerForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.isHasChange = !_.isEqual(value, initialValue);\n });\n }\n\n public onUpdateManager(): void {\n if (this.updateManagerForm.valid) {\n const { group_id } = this.updateManagerForm.getRawValue();\n const role = this.groups.find(v => v.id === group_id)\n\n if (this.manager?.id) {\n this.crmService.updateManager({ role: role?.name, group_id: role?.id, }, this.manager?.id)\n .subscribe({\n next: () => {\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n })\n } else if (this.managers?.length) {\n this.crmService.bulkUpdateManagers({ managers: this.managers, role: role?.name, group_id: role?.id, })\n .subscribe({\n next: () => {\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n })\n }\n }\n }\n\n private onGetAllGroups(): void {\n this.crmService.getAllGroups({ page: 1, take: 25 })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v) => {\n this.groups = v.items;\n }\n });\n }\n\n private onGetManagerById(): void {\n this.crmService.getManagerById(this.manager_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v) => {\n this.manager = v;\n this.initForm();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n });\n }\n\n public onDeleteManager() {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.update-manager`,\n });\n\n dialogRef.subscribe({\n next: (res: any) => {\n if (res) {\n this.crmService.deleteManager(this.manager.id)\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.user_has_been_successfully_removed_from_the_team)\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n}\n","
\n
\n

{{'updating' | translate}} {{manager?.first_name}} {{manager?.last_name}}

\n \n close\n \n
\n\n \n\n
\n
\n \n
\n\n
\n {{'cancel' | translate}}\n {{ 'save' | translate }}\n \n\n
\n\n \n delete\n \n
\n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, takeUntil } from 'rxjs';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { BaseService } from '../../../../../services/base.service';\nimport { ChannelForm } from './create-channel.interface';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { trimValidator } from '../../../../../helpers';\nimport { IError, IFunnel } from '../../../../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, FUNNEL_TYPE, languages, SUCCESS_RESPONSE } from '../../../../../enum/constants';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { IntegrationService } from '../../../../../services/integration.service';\nimport * as _ from 'lodash';\nimport { ChannelType } from '../../../../../modules/crm/channel/components/channel/channel.interface';\nimport { ChannelService } from '../../../../../services/channel.service';\n\n@Component({\n selector: 'app-create-channel',\n templateUrl: './create-channel.component.html',\n styleUrl: './create-channel.component.scss'\n})\nexport class CreateChannelComponent implements OnInit, OnDestroy {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Form\n public channelForm!: FormGroup;\n\n// Var`s\n public isLoader: boolean = false;\n public isDisabled: boolean = false;\n public isHasChange: boolean = false;\n\n public channel_id: string = '';\n public channel_type: string = '';\n public channel!: any;\n public allowedFunnels!: IFunnel[];\n public allowedColumns!: any[];\n\n public managersList: any[] = [];\n public managersListInfo: any = {};\n public searchedManager = '';\n public managerPage = 1;\n protected readonly channelTypes: ChannelType[] = this.channelService.channelTypes;\n\n constructor(\n readonly baseService: BaseService,\n readonly crmService: CrmService,\n readonly rightSidebarService: RightSidebarService,\n readonly notificationService: NotificationService,\n readonly integrationService: IntegrationService,\n private readonly channelService: ChannelService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channel_id = options.data?.channel_id;\n this.channel_type = options.data?.channel_type;\n }\n\n ngOnInit(): void {\n this.isLoader = true;\n if (this.channel_id) this.onGetChannel();\n else this.onInitForm();\n this.onGetAllFunnels();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private onInitForm(): void {\n if (this.channel_id) {\n this.channelForm = new FormGroup({\n name: new FormControl(this.channel.name, [Validators.required, trimValidator]),\n type: new FormControl(this.channel.type, [Validators.required]),\n funnel_id: new FormControl(this.channel?.funnel_id),\n column_id: new FormControl(this.channel?.column_id),\n is_create_deal: new FormControl(this.channel?.is_create_deal),\n is_read_message: new FormControl(this.channel?.is_read_message),\n });\n if (this.channelForm.controls.funnel_id.value) {\n this.getColumnsByFunnel({ id: this.channelForm.controls.funnel_id.value});\n }\n this.onCreateGroupFormValueChange();\n } else {\n this.channelForm = new FormGroup({\n name: new FormControl('', [Validators.required, trimValidator]),\n type: new FormControl('', [Validators.required]),\n funnel_id: new FormControl(''),\n column_id: new FormControl(''),\n is_create_deal: new FormControl(true),\n is_read_message: new FormControl(true),\n });\n\n this.isLoader = false;\n }\n }\n\n private onCreateGroupFormValueChange(): void {\n const initialValue = { ...this.channelForm.getRawValue() };\n\n this.channelForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.isHasChange = !_.isEqual(value, initialValue);\n });\n }\n\n private onGetChannel(): void {\n this.integrationService.getChannel(this.channel_id)\n .subscribe({\n next: (v) => {\n this.isLoader = false;\n\n this.channel = v;\n this.onInitForm();\n },\n error: (e) => {\n this.isLoader = false;\n\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n\n this.onCloseSidebar();\n }\n })\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n public onSave(): void {\n this.channelForm.markAllAsTouched();\n if (this.channelForm.valid) {\n const payload = this.channelForm.getRawValue();\n this.integrationService.createChannel(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_created)\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n }\n\n public onSaveChange(): void {\n this.channelForm.markAllAsTouched();\n\n if (this.channelForm.valid) {\n const payload = this.channelForm.getRawValue();\n this.integrationService.updateChannel(this.channel.id, payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated)\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n }\n\n onGetAllFunnels() {\n this.crmService.getAllFunnels({ type: FUNNEL_TYPE.DEAL })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (funnels: IFunnel[]) => {\n this.allowedFunnels = [...funnels];\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n getColumnsByFunnel(event: any) {\n this.crmService.getAllColumns({\n funnel_id: event.id,\n page: 1,\n take: 50,\n })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (resColumn: any) => {\n this.allowedColumns = [...resColumn.items];\n if (this.allowedColumns.length){\n this.channelForm.controls.column_id.setValue(this.allowedColumns[0].id);\n }\n },\n });\n }\n\n selectDisable(event: any) {\n if (event.value === \"WIDGET\") {\n this.channelForm.controls.is_create_deal.setValue(true);\n this.channelForm.controls.is_read_message.setValue(true);\n this.isDisabled = true;\n } else {\n this.isDisabled = false;\n }\n }\n}\n","
\n
\n {{ channel_id ? ('update_your_channel' | translate) + \" \" + channel?.name : 'create_a_new_channel' | translate }}\n \n close\n \n
\n\n \n\n
\n \n
\n\n
\n
\n
{{ 'name_channel' | translate }}*
\n \n \n {{ 'field_cannot_be_empty' | translate }}.\n \n\n
{{ 'type_channel' | translate }}*
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}.\n \n\n
{{ 'name_funnel' | translate }}
\n \n\n
{{ 'name_column' | translate }}
\n \n\n \n \n {{ 'is_create_deal' | translate }}\n \n \n {{ 'is_read_messages' | translate }}\n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n\n
\n {{ 'cancel' | translate }}\n \n
\n {{ 'save' | translate }}\n {{ 'change' | translate }}\n \n
\n
\n
\n","import { Injectable } from '@angular/core';\nimport { map } from 'rxjs';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\n\n\n@Injectable({\n providedIn: 'root',\n})\nexport class IntegrationService {\n\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n private channelUrl = environment.baseURL + '/channel/';\n private integrationsUrl = environment.baseURL + '/integrations/';\n private olxIntegrationUrl = environment.baseURL + '/olx/integration/';\n private tgIntegrationUrl = environment.baseURL + '/tg/integration';\n private tgConnectUrl = environment.baseURL + '/tg/connect';\n\n public getChannels() {\n return this.http.get(this.channelUrl)\n .pipe(map(res => res.data));\n }\n\n public getArchivedChannels(page: number) {\n const params = {\n page: page,\n take: 20,\n };\n return this.http.get(environment.baseURL + '/olx/channels/archived', { params: params })\n .pipe(map(res => res.data));\n }\n\n public getChannel(channel_id: string) {\n return this.http.get(this.channelUrl + channel_id)\n .pipe(map(res => res.data));\n }\n\n public createChannel(payload: any) {\n return this.http.post(this.channelUrl, payload)\n .pipe(map(res => res.data));\n }\n\n public getIntegrations() {\n return this.http.get(this.integrationsUrl)\n .pipe(map(res => res.data));\n }\n\n public getOlxIntegration(channel_id: string) {\n return this.http.get(this.olxIntegrationUrl + channel_id)\n .pipe(map(res => res.data));\n }\n\n public updateChannel(channel_id: string, payload: {\n name?: string,\n assign_user_id?: string,\n is_first_reply?: boolean,\n first_reply?: string\n }) {\n return this.http.put(this.channelUrl + channel_id, payload)\n .pipe(map(res => res.data));\n }\n\n public disconnect(channel_id: string) {\n return this.http.post(this.olxIntegrationUrl + 'disconnect', { channel_id })\n .pipe(map(res => res.data));\n }\n\n public refresh(channel_id: string) {\n return this.http.post(this.olxIntegrationUrl + 'refresh', { channel_id })\n .pipe(map(res => res.data));\n }\n\n public archiveChannel(channel_id: string) {\n return this.http.post(this.channelUrl + `archive/${channel_id}`, {})\n .pipe(map(res => res.data));\n }\n\n public unarchiveChannel(channel_id: string) {\n return this.http.post(this.channelUrl + `unarchive/${channel_id}`, {})\n .pipe(map(res => res.data));\n }\n}\n","import { Injectable } from '@angular/core';\nimport { HttpClient } from \"@angular/common/http\";\nimport { Router } from \"@angular/router\";\nimport { BehaviorSubject, map, tap } from \"rxjs\";\nimport { IParams } from \"../model/http\";\nimport { environment } from \"../../environments/environment\";\nimport { initItemsGroParams } from \"./crm.service\";\n\nexport const initItemsRingParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class RingostatService {\n\n private readonly itemsSubject$ = new BehaviorSubject({});\n public readonly items$ = this.itemsSubject$.asObservable();\n public readonly isLoader$ = new BehaviorSubject(false);\n public queryRingostatParams = { ...initItemsRingParams };\n\n\n\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n ) {\n }\n\n loadListItems() {\n this.isLoader$.next(true);\n return this.getItems(this.queryRingostatParams)\n .pipe(tap((v) => {\n this.itemsSubject$.next(v);\n this.isLoader$.next(false);\n }));\n }\n\n public setRingostatParams(params: IParams) {\n const {\n sort,\n order,\n search,\n take,\n page\n } = params;\n\n this.queryRingostatParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryRingostatParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryRingostatParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryRingostatParams.search,\n take: take || this.queryRingostatParams.take,\n page: page || this.queryRingostatParams.page,\n };\n\n return this.loadListItems();\n }\n\n private getItems(params: any) {\n return this.http.get(environment.baseURL + `/ringostat/history`, { params })\n .pipe(map(v => v.data));\n }\n\n getKeys() {\n return this.http.get(environment.baseURL + `/ringostat/keys`)\n .pipe(map(v => v.data));\n }\n\n addApiRingostat(payload: any ) {\n const url = environment.baseURL + `/ringostat/integrate`;\n return this.http.post(url, payload)\n }\n\n putApiRingostat(payload: any, id: string ) {\n const url = environment.baseURL + `/ringostat/${id}`;\n return this.http.put(url, payload)\n }\n\n deleteApiRingostat(id: any ) {\n return this.http.delete(environment.baseURL + `/ringostat/${id}`)\n .pipe(map(v => v.data));\n\n }\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from \"@angular/forms\";\nimport { pipe, Subject, take, takeUntil } from \"rxjs\";\nimport { RightSidebarService } from \"../../../../../services/right-sidebar.service\";\nimport { RingostatService } from \"../../../../../services/ringostat.service\";\nimport { NotificationService } from \"../../../../../services/notifications.service\";\nimport { IError } from \"../../../../../interfaces\";\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { DialogService } from \"../../../../../services/dialog.service\";\n\n\ninterface IRingostatControlForm {\n api_key: FormControl,\n}\n\n@Component({\n selector: 'app-integration-ringostat',\n templateUrl: './integration-ringostat.component.html',\n styleUrls: ['./integration-ringostat.component.scss']\n})\nexport class IntegrationRingostatComponent implements OnInit, OnDestroy{\n public form!: FormGroup;\n private readonly destroy$: Subject = new Subject();\n public isFormSubmitted: boolean = false;\n public keys: any[] = [];\n\n\n constructor(\n public rightSidebarService: RightSidebarService,\n private readonly ringostatService: RingostatService,\n private readonly notificationService: NotificationService,\n private readonly dialogService: DialogService,\n ) {\n }\n\n ngOnInit() {\n this.getKeys();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n initForm() {\n if (this.keys.length){\n this.form = new FormGroup({\n api_key: new FormControl( this.keys[0]?.api_key, Validators.required),\n })\n } else {\n this.form = new FormGroup({\n api_key: new FormControl( '', Validators.required),\n })\n }\n }\n\n onSubmit() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n const payload = this.form.getRawValue();\n\n if (this.keys.length) {\n this.ringostatService.putApiRingostat(payload, this.keys[0].id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n this.rightSidebarService.close();\n },\n error: e => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n } else {\n this.ringostatService.addApiRingostat(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_added);\n this.rightSidebarService.close();\n },\n error: (e) => {\n // this.notificationService.showError('Помилка', 'Даний API ключ вже підключений', 5000)\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n }\n }\n\n deleteApi() {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.integration-ringostat`,\n });\n\n dialogRef.subscribe( {\n next: (res: boolean) => {\n if (res) {\n this.ringostatService.deleteApiRingostat(this.keys[0].id)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n this.rightSidebarService.close();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }},\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n getKeys() {\n this.ringostatService.getKeys()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe( (v) => {\n this.keys = v;\n this.initForm();\n })\n }\n\n}\n","
\n \n \n \n
\n \"Ringostat\n

\n {{ 'integration' | translate }}:\n {{ keys.length ? 'встановлена ' : 'не встановлена' }}\n

\n
\n\n
\n

API-ключ

\n
\n \n \n
\n {{'field_cannot_be_empty' | translate}}.\n
\n
\n delete\n \n {{'add' | translate}}\n {{'update' | translate}}\n \n
\n
\n
\n\n\n\n
\n\n \n {{'integration_setup_is_very_simple_and_fast' | translate }}.\n {{'to_connect_your_integration,_you_need_to_enter_the_API_key_in_the_form' | translate}}.\n \n
\n
\n","import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Router } from '@angular/router';\nimport { environment } from '../../environments/environment';\nimport { map } from 'rxjs';\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class IntegrationTelegramBotService {\n\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n ) {\n }\n\n addTelegramBotApi(payload: any) {\n return this.http.post(environment.baseURL + `/tg-bots/`, payload )\n .pipe(map(v => v.data));\n }\n\n updateTelegramBotApi(payload: any, id: any) {\n return this.http.put(environment.baseURL + `/tg-bots/update-token/${id}`, payload )\n .pipe(map(v => v.data));\n }\n\n getToken(channel_id: string) {\n return this.http.get(environment.baseURL + `/tg-bots/token/${channel_id}`)\n .pipe(map(v => v.data));\n }\n\n deleteToken(id: any) {\n return this.http.delete( environment.baseURL + `/tg-bots/token/${id}`)\n .pipe(map(v => v.data));\n }\n\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from \"@angular/forms\";\nimport { pipe, Subject, takeUntil } from \"rxjs\";\nimport { RightSidebarService } from \"../../../../../services/right-sidebar.service\";\nimport { NotificationService } from \"../../../../../services/notifications.service\";\nimport { IError } from \"../../../../../interfaces\";\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { DialogService } from \"../../../../../services/dialog.service\";\nimport { IntegrationTelegramBotService } from '../../../../../services/integration-telegram-bot.service';\n\n\ninterface ITelegramBotControlForm {\n token: FormControl,\n}\n\n@Component({\n selector: 'app-integration-telegram-bot',\n templateUrl: './integration-telegram-bot.component.html',\n styleUrls: ['./integration-telegram-bot.component.scss']\n})\nexport class IntegrationTelegramBotComponent implements OnInit, OnDestroy{\n public form!: FormGroup;\n private readonly destroy$: Subject = new Subject();\n public isFormSubmitted: boolean = false;\n public token: any;\n private channel_id!: string;\n\n\n constructor(\n public rightSidebarService: RightSidebarService,\n private readonly integrationTelegramBotService: IntegrationTelegramBotService,\n private readonly notificationService: NotificationService,\n private readonly dialogService: DialogService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channel_id = options.data.channel.id;\n }\n\n ngOnInit() {\n this.getToken();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n initForm() {\n if (this.token){\n this.form = new FormGroup({\n token: new FormControl( this.token.token, Validators.required),\n })\n } else {\n this.form = new FormGroup({\n token: new FormControl( '', Validators.required),\n })\n }\n }\n\n onSubmit() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n const payload = this.form.getRawValue();\n\n if (!this.token) {\n this.integrationTelegramBotService.addTelegramBotApi({...payload, channel_id: this.channel_id})\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n this.rightSidebarService.close('success');\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n } else {\n this.integrationTelegramBotService.updateTelegramBotApi(payload, this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_added);\n this.rightSidebarService.close('success');\n },\n error: (e) => {\n // this.notificationService.showError('Помилка', 'Даний API ключ вже підключений', 5000)\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n })\n }\n }\n }\n\n deleteApi() {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.integration-telegram-bot`,\n });\n\n dialogRef.subscribe( {\n next: (res: boolean) => {\n if (res) {\n this.integrationTelegramBotService.deleteToken(this.token.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n this.rightSidebarService.close('success');\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }},\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n getToken() {\n this.integrationTelegramBotService.getToken(this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe( (v) => {\n this.token = v;\n this.initForm();\n })\n }\n\n}\n","
\n \n \n \n
\n \"Telegram\n

\n {{ 'integration' | translate }}:\n {{ token ? 'встановлена ' : 'не встановлена' }}\n

\n
\n\n
\n

Токен

\n
\n \n \n
\n {{'field_cannot_be_empty' | translate}}.\n
\n
\n delete\n \n {{'add' | translate}}\n {{'update' | translate}}\n \n
\n
\n
\n\n\n\n
\n\n \n {{'integration_setup_is_very_simple_and_fast' | translate }}.\n {{'to_connect_your_integration,_you_need_to_enter_the_API_key_in_the_form' | translate}}.\n \n
\n
\n","import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';\nimport { FormControl, Validators } from '@angular/forms';\nimport { Subject, Subscription, takeUntil } from 'rxjs';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { IntegrationTelegramService } from '../../../../../services/integration-telegram.service';\nimport {\n SendCodeRequest,\n SendCodeResponse,\n SignInWithPasswordRequest,\n VerifyCodeRequest,\n} from './integration-telegram-user.interface';\nimport { IError } from '../../../../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { WebSocketService } from '../../../../../services/gateway.service';\nimport QRCodeStyling from 'qr-code-styling';\nimport { trimValidator } from '../../../../../helpers';\n\n@Component({\n selector: 'app-integration-telegram-user',\n templateUrl: './integration-telegram-user.component.html',\n styleUrls: ['./integration-telegram-user.component.scss'],\n})\nexport class IntegrationTelegramUserComponent implements OnInit, OnDestroy {\n @ViewChild('qrcode') qrCodeCanvas!: ElementRef;\n private readonly destroy$ = new Subject();\n private readonly channelId!: string;\n private sendCodeResponse!: SendCodeResponse | null;\n private qrCodeRequest!: Subscription;\n protected readonly channelName!: string;\n protected qrCode!: string | null;\n protected phoneNumber!: FormControl;\n protected phoneCode!: FormControl | null;\n protected password!: FormControl | null;\n protected loading: boolean = false;\n protected alreadyAuth: boolean = false;\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly integrationTelegramService: IntegrationTelegramService,\n private readonly notificationService: NotificationService,\n private readonly webSocketService: WebSocketService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channelId = options.data.channel.id;\n this.channelName = options.data.channel.name;\n }\n\n ngOnInit() {\n this.phoneNumber = new FormControl('', [Validators.required, trimValidator]);\n this.checkAuthorization();\n this.handleWebSocket();\n }\n\n ngOnDestroy() {\n if (this.qrCodeRequest) this.qrCodeRequest.unsubscribe();\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private checkAuthorization() {\n this.loading = true;\n this.integrationTelegramService.checkAuthorization(this.channelId)\n .subscribe({\n next: data => {\n if (data && data.phone_number) this.phoneNumber.setValue(data.phone_number);\n if (data && data.is_auth) this.alreadyAuth = true;\n this.loading = false;\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n this.loading = false;\n }\n });\n }\n\n protected onLogOut() {\n this.loading = true;\n this.integrationTelegramService.logOut(this.channelId)\n .subscribe({\n next: () => {\n this.alreadyAuth = false;\n this.loading = false;\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n this.loading = false;\n }\n });\n }\n\n protected handleWebSocket() {\n this.webSocketService.getQRCodeRead()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: res => {\n if (res?.is_password) {\n if (this.qrCodeCanvas?.nativeElement) this.qrCodeCanvas.nativeElement.innerHTML = '';\n this.password = new FormControl('', [Validators.required, trimValidator]);\n }\n else {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved);\n this.rightSidebarService.close('success');\n }\n this.loading = false;\n },\n });\n }\n\n private initQRCode() {\n if (!this.qrCodeCanvas?.nativeElement || !this.qrCode) return;\n\n const qrCode = new QRCodeStyling({\n width: 280,\n height: 280,\n margin: 0,\n data: this.qrCode,\n image: '/assets/svgs/integrations/qrcode_canvas.png',\n imageOptions: {\n hideBackgroundDots: true,\n imageSize: 0.4,\n margin: 5\n },\n backgroundOptions: {\n round: 0,\n color: '#ffffff',\n },\n dotsOptions: {\n type: 'rounded',\n color: '#000000',\n roundSize: true,\n },\n cornersSquareOptions: {\n type: 'extra-rounded',\n color: '#000000',\n },\n cornersDotOptions: {\n color: '#000000',\n },\n qrOptions: {\n errorCorrectionLevel: 'M'\n }\n });\n\n this.qrCodeCanvas.nativeElement.innerHTML = '';\n qrCode.append(this.qrCodeCanvas.nativeElement);\n }\n\n protected onSendCode() {\n if (this.phoneNumber.invalid) {\n this.phoneNumber.markAsTouched();\n return;\n }\n this.loading = true;\n\n const payload: SendCodeRequest = {\n phone_number: this.phoneNumber.getRawValue(),\n channel_id: this.channelId,\n };\n this.integrationTelegramService.sendCode(payload)\n .subscribe({\n next: (res: SendCodeResponse) => {\n this.phoneCode = new FormControl('', [Validators.required, trimValidator]);\n this.sendCodeResponse = res;\n this.loading = false;\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n this.loading = false;\n },\n });\n }\n\n protected onCancelCode() {\n this.phoneCode = null;\n this.sendCodeResponse = null;\n }\n\n protected onVerifyCode() {\n if (!this.sendCodeResponse || !this.phoneCode || this.phoneCode?.invalid) {\n this.phoneCode?.markAsTouched();\n return;\n }\n this.loading = true;\n\n const payload: VerifyCodeRequest = {\n phone_code: this.phoneCode.getRawValue(),\n phone_number: this.phoneNumber.getRawValue(),\n channel_id: this.channelId,\n phone_code_hash: this.sendCodeResponse.phone_code_hash,\n };\n this.integrationTelegramService.verifyCode(payload)\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved);\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'SESSION_PASSWORD_NEEDED') {\n this.password = new FormControl('', [Validators.required, trimValidator]);\n return;\n }\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n this.phoneCode?.reset();\n });\n this.loading = false;\n },\n });\n }\n\n protected onSignInWithPassword() {\n if (!this.password || this.password?.invalid) {\n this.password?.markAsTouched();\n return;\n }\n this.loading = true;\n\n const payload: SignInWithPasswordRequest = {\n password: this.password.getRawValue(),\n channel_id: this.channelId,\n };\n this.integrationTelegramService.signInWithPassword(payload)\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved);\n this.rightSidebarService.close('success');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n this.password?.reset();\n });\n this.loading = false;\n },\n });\n }\n\n protected openQrLogin() {\n this.loading = true;\n\n this.qrCodeRequest = this.integrationTelegramService.signInWithQRCode(this.channelId).subscribe({\n next: (qrLink) => {\n this.loading = false;\n this.qrCode = qrLink;\n setTimeout(() => this.initQRCode(), 0);\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n this.loading = false;\n }\n });\n }\n\n protected openPhoneLogin() {\n this.loading = true;\n if (this.qrCodeRequest) this.qrCodeRequest.unsubscribe();\n this.qrCode = null;\n this.loading = false;\n }\n}\n","
\n \n \n \n
\n
\n \"Telegram\n
\n
\n {{ 'log_in_with_your_telegram_account' | translate }}\n {{'channel' | translate}}: {{ channelName }}\n \n {{ 'integration' | translate }}:\n {{ alreadyAuth ? 'встановлена ' : 'не встановлена' }}\n \n \n {{ 'exit' | translate }}\n \n
\n
\n\n \n\n \n
\n
\n
\n\n\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n\n \n {{ 'next' | translate }}\n \n
\n
\n\n\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n\n
\n \n {{ 'next' | translate }}\n \n \n {{ 'cancel' | translate }}\n \n
\n
\n
\n\n\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'next' | translate }}\n \n
\n
\n","import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\nimport { map } from 'rxjs';\nimport {\n SendCodeRequest,\n SignInWithPasswordRequest,\n VerifyCodeRequest,\n} from '../components/layout/right-sidebar/components/integration-telegram-user/integration-telegram-user.interface';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class IntegrationTelegramService {\n\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n sendCode(payload: SendCodeRequest) {\n return this.http.post(environment.baseURL + `/tg-user/send-code`, payload)\n .pipe(map(v => v.data));\n }\n\n verifyCode(payload: VerifyCodeRequest) {\n return this.http.post(environment.baseURL + `/tg-user/verify-code`, payload);\n }\n\n signInWithPassword(payload: SignInWithPasswordRequest) {\n return this.http.post(environment.baseURL + `/tg-user/verify-pass`, payload);\n }\n\n signInWithQRCode(channel_id: string) {\n return this.http.post(environment.baseURL + `/tg-user/qr-code`, { channel_id })\n .pipe(map(v => v.data));\n }\n\n checkAuthorization(channel_id: string) {\n return this.http.get(environment.baseURL + `/tg-user/${channel_id}`)\n .pipe(map(v => v.data));\n }\n\n logOut(channel_id: string) {\n return this.http.post(environment.baseURL + `/tg-user/log-out`, { channel_id });\n }\n}\n","import { map } from 'rxjs';\nimport { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Router } from '@angular/router';\nimport { environment } from '../../environments/environment';\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class AiService {\n\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n\n ) { }\n\n public saveAiSettings(payload: any) {\n return this.http.post(environment.baseURL + `/ai/save-settings`, payload)\n .pipe(map(res => res.data));\n }\n\n public getAiSettings() {\n return this.http.get(environment.baseURL + `/ai/get-settings`)\n .pipe(map(res => res.data));\n }\n}\n\n\n","import { Injectable } from '@angular/core';\nimport { map, Observable, Subject } from 'rxjs';\nimport { HttpClient } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class PaymentService {\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly http: HttpClient\n ) { }\n\n create(payload: any): Observable {\n return this.http.post(`${environment.baseURL}/payment/create`, payload)\n .pipe(map(res => res.data));\n }\n\n liqpay(url: string, data: any) {\n const headers = {\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n return this.http.post(url, data, { headers })\n .pipe(map(res => res.data));\n }\n\n activateTariff(payload: { tariff: string, promo_code: string }) {\n return this.http.post(`${environment.baseURL}/payment/activate`, payload)\n .pipe(map(res => res.data));\n }\n}\n","export interface Table {\n}\n\nexport interface AppTableSource {\n rows: AppTableRow[];\n headers: AppTableHeader[];\n length: number;\n pageIndex: number;\n pageSize: number;\n moduleName?:string;\n}\n\n\nexport interface AppTableHeader {\n id?:any;\n name?: string;\n value: string;\n type?: TableColumnTypes;\n checked?: boolean,\n actions?: any[];\n sort?: ColumnSortType;\n order?:number;\n align?: string,\n className?:string;\n resizeColumn?:boolean;\n width?: string;\n disabled?: boolean;\n translate?: boolean;\n}\n\nexport interface AppTableRow {\n position: number;\n data: any;\n}\n\nexport enum TableColumnTypes {\n TITLE = 'TITLE',\n TEXT = 'TEXT',\n TEXT_TRANSLATE = 'TEXT_TRANSLATE',\n DATE = 'DATE',\n IMAGE = 'IMAGE',\n DATETIME = 'DATETIME',\n CHECKBOX = 'CHECKBOX',\n BOOLEAN = 'BOOLEAN',\n ACTION = 'ACTION',\n OLX_STATUS = 'OLX_STATUS',\n NOVA_POSHTA_STATUS = 'NOVA_POSHTA_STATUS',\n UKR_POSHTA_STATUS = 'UKR_POSHTA_STATUS',\n PHONE = 'PHONE',\n ARRAY_PHONE = 'ARRAY_PHONE',\n ARRAY = 'ARRAY',\n TOGGLE = 'TOGGLE',\n TEXT_COPY = 'TEXT_COPY',\n OLX_ADVERT_STATUS = 'OLX_ADVERT_STATUS',\n LINK = 'LINK',\n OLX_IMAGE = 'OLX_IMAGE',\n PLANNER_STATUS = 'PLANNER_STATUS',\n PLANNER_TYPE = 'PLANNER_TYPE',\n AUDIO = 'AUDIO',\n RINGOSTAT_STATUS = 'RINGOSTAT_STATUS',\n CALL = 'CALL',\n TIME = 'TIME',\n CHANNEL_TYPE = 'CHANNEL_TYPE',\n CHANNEL_STATUS = 'CHANNEL_STATUS',\n}\n\nexport enum ColumnSortType {\n ASC = 'asc',\n DESC = 'desc',\n NONE = 'none'\n}\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ViewChild\n} from '@angular/core';\nimport { MatSort, Sort } from '@angular/material/sort';\nimport { AppTableRow, AppTableSource, TableColumnTypes } from '../../model/table.model';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport {\n ADVERT_PLANNER_TYPE, CHANNEL_STATUS,\n languages,\n status_table_nova_poshta,\n status_table_olx_adverts,\n status_table_planner,\n} from '../../enum/constants';\nimport { BaseService } from \"../../services/base.service\";\nimport { Clipboard } from '@angular/cdk/clipboard';\nimport { NotificationService } from '../../services/notifications.service';\nimport { AudioService } from \"../../services/audio.service\";\nimport * as _ from \"lodash\";\nimport { Subject, takeUntil } from \"rxjs\";\n\n@Component({\n selector: 'app-table',\n templateUrl: './table.component.html',\n styleUrls: ['./table.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class TableComponent implements OnInit, AfterViewInit {\n // Destroy\n private readonly destroy$: Subject = new Subject();\n\n // Input's\n @Input() dataSource!: any;\n @Input() visibleColumnKeys!: string[];\n @Input() showPagination: boolean = true;\n @Input() showUpPagination: boolean = false;\n @Input() isLoader: boolean = false;\n @Input() classImage: string = '';\n\n // Output's\n @Output() onSortChange = new EventEmitter();\n @Output() onTableRowClick = new EventEmitter();\n @Output() onTableRowCheck = new EventEmitter();\n @Output() onPaginationChange = new EventEmitter();\n @Output() isAllRowsSelected = new EventEmitter();\n @Output() menuSelected = new EventEmitter();\n\n /** Checkbox events */\n @Input() selection = new SelectionModel(true, []);\n\n // Constants\n manualSortEnabled: boolean = false;\n playId: string = '';\n pageCount!: number;\n\n actions: string[] = [];\n\n constructor(\n private readonly changeDetectorRef: ChangeDetectorRef,\n public readonly baseService: BaseService,\n public readonly clipboard: Clipboard,\n public readonly notificationService: NotificationService,\n public readonly audioService: AudioService,\n ) {\n }\n\n @ViewChild(MatSort) sort!: MatSort;\n\n ngOnInit() {\n if (this.dataSource?.headers) {\n for (let header of this.dataSource.headers) {\n /** Action */\n if (header.type === TableColumnTypes.ACTION) {\n if (header.actions) {\n for (let action of header.actions) {\n this.actions.push(action);\n }\n }\n }\n\n if (header.type === TableColumnTypes.AUDIO) {\n this.audioService.currentAudio$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.changeDetectorRef.detectChanges();\n });\n\n this.audioService.playAudio$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.changeDetectorRef.detectChanges();\n });\n }\n }\n }\n }\n\n ngAfterViewInit() {\n let s: any = this.dataSource?.headers.find((e: any) => {\n if (e.sort != 'none') return e.value;\n })\n this.manualSortEnabled = true;\n if (s) {\n this.sort?.sort({\n id: s.value,\n start: s.sort,\n disableClear: false,\n });\n }\n this.manualSortEnabled = false;\n this.changeDetectorRef.detectChanges();\n // this.dataSource.headers.s = this.sort;\n }\n\n /** sorting */\n announceSortChange(sortState: Sort) {\n if (!this.manualSortEnabled) this.onSortChange.emit(sortState);\n }\n\n onRowClick(row: any) {\n this.onTableRowClick.emit(row);\n }\n\n cellClicked(row: any, column: string) {\n\n const columns = ['stage', 'priority', 'image_url', 'audio_url'];\n if (column && !columns.includes(column))\n this.onRowClick(row);\n else {\n if (column === 'audio_url') {\n if (!row.audio_url) return;\n const audio_urls = this.dataSource.rows\n .filter((v: any) => !!v.audio_url)\n .map((v: any) => v.audio_url);\n if (!_.isEqual(audio_urls, this.audioService.audioSource)) this.audioService.audioSource = audio_urls;\n\n this.audioService.setIsPlay(this.audioService.currentAudio !== row.audio_url || !this.audioService.isPlay);\n if (this.audioService.currentAudio !== row.audio_url) this.audioService.setCurrentAudio(row.audio_url);\n }\n }\n // }\n\n }\n\n onCheckRow(event: any, row: any) {\n event.stopPropagation();\n this.onTableRowCheck.emit(row);\n }\n\n onPagination(paginationDetails: any) {\n if (this.dataSource?.pageSize) {\n this.pageCount = Math.round(this.dataSource.length / paginationDetails.pageSize)\n }\n this.onPaginationChange.emit(paginationDetails)\n }\n\n isEllipsisActive(e: HTMLElement): boolean {\n return e ? (e.clientWidth < e.scrollWidth - 20) : false;\n }\n\n toggleAllRows() {\n if (this.isAllSelected()) {\n this.selection.clear();\n this.isAllRowsSelected.emit(false)\n return;\n }\n this.isAllRowsSelected.emit(true)\n this.selection.select(...this.dataSource.rows);\n }\n\n isAllSelected() {\n const numSelected = this.selection.selected.length;\n const numRows = this.dataSource.rows.length;\n return numSelected === numRows;\n }\n\n getActionList(actions: any, rows: any): any {\n return actions;\n }\n\n /** Action Menu Value Changed */\n menuOnSelect(selectedMenu: any, event: any) {\n let eventValue: any = { label: event.value }\n const objectWithEvent = Object.assign(selectedMenu, eventValue)\n this.menuSelected.emit(objectWithEvent);\n }\n\n\n protected readonly TableColumnTypes = TableColumnTypes;\n protected readonly status_table_nova_poshta = status_table_nova_poshta;\n\n formatPhone(rowElement: any) {\n let phoneNumber: string = rowElement.toString();\n\n if (phoneNumber.charAt(0) !== '+') {\n phoneNumber = '+' + phoneNumber;\n }\n return phoneNumber;\n }\n\n onCopy(value: any) {\n if (this.clipboard.copy(value)) {\n this.notificationService.showInfo('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.copied)\n }\n }\n\n arrayFormat(value: any[]) {\n return this._filter(value).map(this.formatPhone).join(', ');\n }\n\n _filter(value: any[]) {\n return (value || []).filter(v => v)\n }\n\n millisecondsToTime(milliseconds: any): string {\n let totalSeconds = Math.floor(milliseconds / 1000);\n let hours = Math.floor(totalSeconds / 3600);\n let minutes = Math.floor((totalSeconds % 3600) / 60);\n let seconds = totalSeconds % 60;\n\n let formattedTime = '';\n\n if (hours > 0) {\n formattedTime += hours + ':';\n }\n\n formattedTime += (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;\n\n return formattedTime;\n }\n\n protected readonly status_table_planner = status_table_planner;\n protected readonly ADVERT_PLANNER_TYPE = ADVERT_PLANNER_TYPE;\n protected readonly status_table_olx_adverts = status_table_olx_adverts;\n protected readonly CHANNEL_STATUS = CHANNEL_STATUS;\n}\n","\n
\n \n
\n
\n\n \n \n \n \n \n
\n
\n \n\n \n \n \n \n {{ (header.translate && header.name) ? (header.name | translate) : header.name }}\n \n\n\n \n \n \n {{ row[header.value] ? row[header.value] : '-' }}\n \n \n\n \n \n \n {{ row[header.value] ? (row[header.value] | translate) : '-' }}\n \n \n\n \n \n \n {{ row[header.value] ? row[header.value] : '-' }}\n \n \n\n \n \n \n {{ row[header.value] ? row[header.value] : '-' }}\n \n \n\n \n \n \n {{ row[header.value] ? 'yes' : 'no' | translate}}\n \n \n\n \n \n \n \n \n \n\n\n \n \n \n \n \n \n \n\n \n \n \n {{ row[header.value] ? (row[header.value] | date:\"dd/MM/yyyy\") : '-' }}\n \n \n\n \n \n \n {{ row[header.value] ? (row[header.value] | date:\"dd/MM/yyyy HH:mm:ss\") : '-' }}\n \n \n\n \n \n \n cancel\n warning\n check_circle\n hourglass_empty\n pause_circle\n \n \n\n \n \n \n pending\n check_circle\n warning\n hourglass_empty\n pause_circle\n cancel\n \n \n\n \n \n \n \"olx\"\n \"ria\"\n \"telegram\n \"telegram\"\n \"widget\"\n \n \n\n \n \n \n \n \n \n\n \n \n \n
\n \n {{ status_table_olx_adverts(row[header.value]).icon }}\n \n
\n \n
\n\n \n \n \n
\n \n {{ status_table_nova_poshta(row[header.value]).icon }}\n \n
\n \n
\n\n \n \n \n
\n \n {{ status_table_nova_poshta(row[header.value]).icon }}\n \n
\n \n
\n \n \n \n \n \n\n \n \n \n
\n \n {{ status_table_planner(row[header.value]).icon }}\n \n
\n \n
\n\n \n \n \n {{ ADVERT_PLANNER_TYPE[row[header.value]] }}\n \n \n\n \n \n \n \n \n pause\n play_arrow\n \n \n {{'no_conversation' | translate}}\n \n \n\n \n \n phone_forwarded\n phone_callback\n \n \n\n\n \n \n \n {{ row[header.value] ? formatPhone(row[header.value]) : '-' }}\n \n \n\n \n \n \n \n \n \n\n \n \n \n {{ row[header.value].length ? arrayFormat(row[header.value]) : '-' }}\n \n \n\n \n \n \n {{ row[header.value].length ? _filter(row[header.value]).join(', ') : '-' }}\n \n \n\n \n \n \n
\n content_copy\n
\n {{ row[header.value] ? row[header.value] : '-' }}\n \n
\n\n \n \n \n \n more_vert\n \n \n \n \n\n \n \n\n\n\n \n \n {{ millisecondsToTime(row[header.value]) }}\n \n \n\n\n \n \n \n \n \n \n\n \n
\n \n
\n
\n
\n\n \n \n {{ 'nothing_found' | translate }}\n \n \n \n \n \n\n \n
\n \n\n
\n\n
\n \n \n \n \n
\n
\n\n","import {\n Component,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ViewChild\n} from '@angular/core';\nimport { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';\nimport { MatPaginator, PageEvent } from '@angular/material/paginator';\nimport { DropdownPosition } from \"@ng-select/ng-select\";\n\n@Component({\n selector: 'table-pagination',\n templateUrl: './pagination.component.html',\n styleUrls: ['./pagination.component.scss'],\n providers: [\n { provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } }\n ],\n})\nexport class PaginationComponent implements OnInit {\n\n pageSize!: number;\n pageIndex!: number;\n length!: number;\n currentPage!: number;\n pageNumbers!: number[];\n takes: number[] = [25, 50, 100, 250, 500]\n @Input() disabled = false;\n @Input() hidePageSize = false;\n @Input() pageSizeOptions!: number[];\n @Input() showFirstLastButtons = true;\n @Input() searchable: boolean = false;\n @Input() dropdownPosition: DropdownPosition = 'top';\n @Output() page = new EventEmitter();\n\n @Input('pageIndex') set pageIndexChanged(pageIndex: number) {\n this.pageIndex = pageIndex;\n this.updateCurrentPage();\n }\n @Input('length') set lengthChanged(length: number) {\n this.length = length;\n this.updateCurrentPage();\n }\n @Input('pageSize') set pageSizeChanged(pageSize: number) {\n this.pageSize = pageSize;\n this.updateCurrentPage();\n }\n\n constructor() {}\n\n ngOnInit() {\n this.updateCurrentPage();\n }\n\n updateCurrentPage() {\n this.currentPage = (this.pageIndex || 0) + 1;\n this.pageNumbers = [];\n for (let i = 1; i <= Math.ceil(this.length / this.pageSize); i++) {\n this.pageNumbers.push(i);\n }\n }\n\n paginationChange(pageEvt: PageEvent) {\n this.length = pageEvt.length;\n this.pageIndex = pageEvt.pageIndex;\n this.pageSize = pageEvt.pageSize;\n this.updateCurrentPage();\n this.emitPageEvent(pageEvt);\n }\n\n currentPageChange() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: this.currentPage - 1,\n pageSize: this.pageSize,\n };\n this.emitPageEvent(event);\n }\n\n emitPageEvent(pageEvent: PageEvent) {\n this.page.next(pageEvent);\n }\n\n currentTakeChange() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: this.currentPage - 1,\n pageSize: this.pageSize,\n };\n this.emitPageEvent(event);\n }\n\n toFirstPage() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: 0,\n pageSize: this.pageSize,\n };\n this.page.next(event);\n }\n\n toLastPage() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: (this.pageNumbers.at(-1) || 1) - 1,\n pageSize: this.pageSize,\n };\n this.page.next(event);\n }\n\n toNextPage() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: this.pageIndex + 1,\n pageSize: this.pageSize,\n };\n this.page.next(event);\n }\n\n toPrevPage() {\n const event: PageEvent = {\n length: this.length,\n pageIndex: this.pageIndex - 1,\n pageSize: this.pageSize,\n };\n this.page.next(event);\n }\n\n disabledBeforeBtn() {\n if (!this.currentPage) return false;\n return this.currentPage === 1;\n }\n\n disabledAfterBtn() {\n if (!this.pageNumbers?.length) return false;\n return this.pageNumbers.length === 1 || this.pageNumbers.at(-1) === this.currentPage;\n }\n}\n","
\n\n
{{currentPage}} {{ 'page' | translate }}
\n
\n first_page\n navigate_before\n \n navigate_next\n last_page\n
\n \n
\n
\n\n\n\n \n \n {{item}}\n \n\n \n\n\n\n \n \n {{item}}\n \n\n \n\n","import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n@Component({\n selector: 'app-toggle',\n templateUrl: './toggle.component.html',\n styleUrls: ['./toggle.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => ToggleComponent),\n },\n ],\n})\nexport class ToggleComponent implements OnInit, ControlValueAccessor {\n @Input() checked: boolean = false;\n @Input() name?: string;\n @Input() disabled: boolean = false;\n @Input() customClass: string = '';\n @Input() readOnly: boolean = false;\n\n @Output() inputChange = new EventEmitter();\n @Output() change = new EventEmitter();\n\n onChange = (value: any) => {};\n onTouch = () => {};\n\n ngOnInit(): void {}\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: any): void {\n this.checked = value;\n }\n\n setDisabledState?(isDisabled: boolean): void {\n this.disabled = isDisabled;\n }\n\n onchange(event: any): void {\n this.checked = event;\n\n this.onChange(this.checked);\n this.onTouch();\n\n this.inputChange.emit(this.checked);\n this.change.emit(this.checked);\n }\n}\n","\n\n","import { Component, OnInit } from '@angular/core';\nimport { ChannelService } from '../../../../../services/channel.service';\nimport { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';\nimport { IFormAiConfig } from './integration-ai.interface';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { AiService } from '../../../../../services/ai.service';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { paymentBody } from '../../../../../modals/props/props.interface';\nimport { liqPayData } from '../../../../../helpers';\nimport { environment } from '../../../../../../environments/environment';\nimport { PaymentService } from '../../../../../services/payment.service';\nimport { IError } from '../../../../../interfaces';\nimport { CrmService } from '../../../../../services/crm.service';\n\n\n@Component({\n selector: 'app-integration-ai',\n templateUrl: './integration-ai.component.html',\n styleUrl: './integration-ai.component.scss',\n})\nexport class IntegrationAiComponent implements OnInit {\n\n channels = [];\n currentChannels: any[] = [];\n controllers: any = [];\n controlChannel: any = [];\n public settingsAi: any[] = [];\n public crmConfig: any;\n public aiPrice: any;\n public tokensControl!: FormControl;\n public prices = [\n {name: '1 000 000', value: 1_000_000},\n {name: '5 000 000', value: 5_000_000},\n {name: '10 000 000', value: 10_000_000},\n {name: '50 000 000', value: 50_000_000},\n {name: '100 000 000', value: 100_000_000},\n ];\n public totalPrice: number = 0;\n\n formAiConfig!: FormGroup\n\n constructor(\n private readonly channelService: ChannelService,\n private readonly notificationService: NotificationService,\n public readonly baseService: BaseService,\n private readonly aiService: AiService,\n private readonly paymentService: PaymentService,\n private readonly crmService: CrmService,\n ) {\n this.tokensControl = new FormControl(this.prices[0].value, [Validators.required]);\n }\n\n ngOnInit() {\n this.channelService.getChannels()\n .subscribe((res) => {\n this.channels = res;\n this.currentChannels = res;\n this.getAiSettings();\n });\n this.getConfig();\n }\n\n onChangeChannel(event?: any) {\n // const channelIds: any[] = [];\n // this.controlChannel = this.formAiConfig.getRawValue();\n // (this.controlChannel.integrations as any).forEach((v: any) => {\n // channelIds.push(...v.channels.map((item: any) => item.id));\n // })\n // this.controllers = channelIds;\n // this.currentChannels = this.channels.map((v: any) => {\n // v.isdisabled = this.controllers.includes(v.id);\n // return v;\n // });\n\n }\n onOpen(event: any) {\n this.controlChannel = this.formAiConfig.getRawValue();\n\n const channelIds = (this.controlChannel.integrations as any)\n .flatMap((v: any) => v.channels.map((item: any) => item.id));\n const currentChannels = (event.controls.channels.value || []).map((v: any) => v.id);\n this.currentChannels = this.channels.map((v: any) => {\n v.disabled = channelIds.includes(v.id) && !currentChannels.includes(v.id);\n return v;\n });\n }\n\n public initForm() {\n if (this.settingsAi.length) {\n this.formAiConfig = new FormGroup({\n integrations: new FormArray([]),\n });\n\n const integrationsArray = this.formAiConfig.get('integrations') as FormArray;\n\n this.settingsAi.forEach((v: any) => {\n const channels = v.channels.map((channel_id: string) => this.channels.find((channel: any) => channel.id === channel_id));\n const integrationGroup = new FormGroup({\n text: new FormControl(v.prompt),\n token_output: new FormControl(v.token_output),\n channels: new FormControl(channels),\n });\n integrationsArray.push(integrationGroup);\n });\n\n } else {\n this.formAiConfig = new FormGroup({\n integrations: new FormArray([new FormGroup({\n text: new FormControl(),\n token_output: new FormControl(),\n channels: new FormControl(),\n })])\n })\n }\n this.onChangeChannel();\n // const selectedChannelIds = this.settingsAi.flatMap((value: any) => value.channels || []);\n //\n // this.currentChannels = this.channels.filter((channel: any) => !selectedChannelIds.includes(channel.id));\n\n }\n\n getConfig() {\n this.crmService.getConfig()\n .subscribe({\n next: (config) => {\n this.crmConfig = config;\n this.aiPrice = config.ai_token_price;\n this.changeTotalPrice();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n\n getIntegrations() {\n return ((this.formAiConfig.get('integrations') as FormArray));\n }\n\n addIntegration() {\n const integrations = this.formAiConfig.controls.integrations as FormArray;\n integrations.push(\n new FormGroup({\n text: new FormControl(''),\n token_output: new FormControl(),\n channels: new FormControl([]),\n })\n );\n }\n\n save() {\n const items = (this.formAiConfig.getRawValue().integrations as any || []).map((v: any) => {\n return {\n text: v.text,\n token_output: v.token_output,\n channels: v.channels.map((channel: any) => channel.id),\n }\n })\n this.aiService.saveAiSettings(items)\n .subscribe(() => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved);\n });\n }\n\n\n getAiSettings() {\n this.aiService.getAiSettings()\n .subscribe((v) => {\n this.settingsAi = v;\n this.initForm();\n });\n }\n\n deleteField(index: any) {\n this.getIntegrations().removeAt(index);\n this.onChangeChannel();\n }\n\n changeTotalPrice() {\n this.totalPrice = this.aiPrice * (this.tokensControl.value / 1_000_000);\n }\n\n getPayment() {\n if (!this.tokensControl.valid) {\n this.tokensControl.markAsTouched();\n return;\n }\n\n const tokens = this.tokensControl.getRawValue();\n\n const body: paymentBody = {\n payment_code: '',\n tariff: '',\n payment_type: \"token\",\n payment_method: \"card\",\n price: this.totalPrice,\n promo_code: '',\n x_data: {\n type: \"token\",\n count: tokens,\n }\n }\n const desc_text = `Payment on the \"${tokens}\" tokens.`;\n\n this.paymentService.create(body)\n .subscribe({\n next: (res) => {\n // this.onNotify();\n const data: any = liqPayData(\n this.crmConfig.liqpay_private_key,\n {\n action: 'pay',\n amount: this.totalPrice,\n public_key: this.crmConfig.liqpay_public_key,\n description: desc_text,\n currency: 'USD',\n order_id: res.id,\n server_url: environment.liqpay.webhook_url,\n version: environment.liqpay.version,\n },\n );\n\n const form = document.createElement('form');\n form.setAttribute('method', 'post');\n form.setAttribute('action', environment.liqpay.action_url);\n form.setAttribute('target', '_blank');\n for (const i in data) {\n if (data.hasOwnProperty(i)) {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = i;\n input.value = data[i];\n form.appendChild(input);\n }\n }\n document.body.appendChild(form);\n form.submit();\n document.body.removeChild(form);\n\n window.open(environment.liqpay.action_url, '_blank');\n }\n });\n }\n\n protected readonly event = event;\n}\n","
\n \n \n \n
\n \"Gen\n
\n
\n
\n {{ 'ai_integration.number_of_tokens' | translate }}\n : {{ baseService.currentCompanyInfo.settings.ai_token }}\n \n
\n \n \n {{ 'buy_tokens' | translate }}\n {{ 'price' | translate }}: {{ totalPrice }} USD\n
\n
\n \n \n \n \n \n
\n
\n
\n
\n {{ 'ai_integration.text_1' | translate }}
\n {{ 'ai_integration.text_2' | translate }}\n
\n
\n \n
\n

{{ 'ai_integration.h4_1' | translate }}

\n
\n \n
\n
\n \n
\n
\n
\n \n
\n
\n
\n \n
\n
\n
\n \n \n
\n
\n
\n
\n \n delete\n \n
\n
\n
\n
\n
\n \n {{ 'save' | translate }}\n \n \n add\n \n
\n
\n
\n \n

{{ 'ai_integration.h4_2' | translate }}

\n \n
\n \n
\n
\n
\n
\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport {BaseService} from \"../../services/base.service\";\nimport { NotificationService } from \"../../services/notifications.service\";\nimport { languages } from '../../enum/constants';\n\n\n@Component({\n templateUrl: './channel.component.html',\n styleUrls: ['./channel.component.scss'],\n})\nexport class ChannelComponent {\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) { }\n\n save() {\n if (!this.data.channel.trim()) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.empty_channel);\n } else {\n this.dialogRef.close(this.data.channel);\n }\n }\n\n onNoClick(): void {\n this.dialogRef.close();\n }\n\n}\n","
\n\n \n\n
\n\n\n\n
\n\n \n \n {{'cancel' | translate}}\n \n \n\n \n {{ 'save' | translate }}\n \n\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { AuthService } from '../../../../../services/auth.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { IntegrationService } from '../../../../../services/integration.service';\nimport { Router } from '@angular/router';\nimport { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';\nimport { environment } from '../../../../../../environments/environment';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { ChannelComponent } from '../../../../../dialogs/channel/channel.component';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { WebSocketService } from '../../../../../services/gateway.service';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { ChannelService } from '../../../../../services/channel.service';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages, NAME_PROJECT } from '../../../../../enum/constants';\nimport { IError } from '../../../../../interfaces';\nimport { Subject, takeUntil } from 'rxjs';\nimport { CrmService } from '../../../../../services/crm.service';\n\ninterface IFormReply {\n replies: any;\n}\n\n@Component({\n selector: 'app-integration-olx',\n templateUrl: './integration-olx.component.html',\n styleUrls: ['./integration-olx.component.scss'],\n})\nexport class IntegrationOlxComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n public readonly authService: AuthService,\n public readonly baseService: BaseService,\n public readonly integrationService: IntegrationService,\n public readonly channelService: ChannelService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly webSocketService: WebSocketService,\n private readonly dialogService: DialogService,\n private readonly router: Router,\n private readonly dialog: MatDialog,\n private readonly modalService: ModalService,\n public readonly crmService: CrmService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channel_id = options.data.channel.id;\n this.channel_name = options.data.channel.name;\n }\n\n protected readonly NAME_PROJECT = NAME_PROJECT;\n protected readonly environment = environment;\n\n channel_id!: string;\n channel_name!: string;\n account: any = {};\n integration!: any;\n is_empty: boolean = false;\n is_expired: boolean = false;\n is_pause: boolean = false;\n status!: string;\n\n error!: string;\n\n form!: FormGroup;\n formReply!: FormGroup;\n channels: any[] = [];\n channel: any;\n\n public isLoader: boolean = false;\n public managersList: any[] = [];\n public managersListInfo: any = {};\n public searchedManager = '';\n public managerPage = 1;\n\n public statuses: any = {\n 'ACTIVE': { title: 'active_status', color: 'green', translate: true },\n 'EXPIRED': { title: 'expired_status', color: 'orange', translate: true },\n 'CLOSED': { title: 'closed_status', color: 'gray', translate: true },\n 'PAUSE': { title: 'pause_status', color: 'orange', translate: true },\n };\n\n ngOnInit() {\n this.webSocketService.onSuccessOlx(this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_reconnected);\n this.isLoader = false;\n this.init();\n });\n\n this.webSocketService.onErrorOlx(this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe(res => {\n this.isLoader = false;\n if (res === 'MAX_INTEGRATION') {\n this.modalService.openTariffUpdate();\n this.notificationService.showError('', ERROR_MESSAGES[res].replace('{0}', 'OLX'));\n } else if (ERROR_MESSAGES[res]) {\n this.notificationService.showError('', ERROR_MESSAGES[res]);\n } else {\n this.notificationService.showError('', res);\n }\n });\n\n this.init();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n this.webSocketService.offErrorOlx(this.channel_id);\n }\n\n init() {\n this.isLoader = true;\n this.is_expired = false;\n this.integrationService.getOlxIntegration(this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.is_empty = false;\n this.account = res.account || {};\n this.isLoader = false;\n },\n error: (res) => {\n this.isLoader = false;\n (res?.error?.errors || []).map((err: IError) => {\n if (err.code === 'INTEGRATION_NOT_FOUND') {\n this.is_empty = true;\n // this.webSocketService.onSuccessOlx(this.channel_id)\n // .pipe(takeUntil(this.destroy$))\n // .subscribe(() => {\n // this.isLoader = false;\n // this.init();\n // });\n // this.webSocketService.onErrorOlx(this.channel_id)\n // .pipe(takeUntil(this.destroy$))\n // .subscribe(res => {\n // this.isLoader = false;\n // if (res === 'MAX_INTEGRATION') {\n // this.modalService.openTariffUpdate();\n // this.notificationService.showError('', ERROR_MESSAGES[res].replace('{0}', 'OLX'));\n // } else if (ERROR_MESSAGES[res]) {\n // this.notificationService.showError('', ERROR_MESSAGES[res]);\n // } else {\n // this.notificationService.showError('', res);\n // }\n // });\n }\n });\n\n },\n });\n\n this.channelService.getChannelById(this.channel_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (channel) => {\n this.channel = channel;\n this.status = channel.status;\n this.is_expired = channel.status === 'EXPIRED';\n this.is_pause = channel.status === 'PAUSED';\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n console.log(ERROR_MESSAGES[err.code]);\n });\n },\n });\n\n this.integrationService.getChannels()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (data) => {\n this.channels = data;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n console.log(ERROR_MESSAGES[err.code]);\n });\n },\n });\n\n this.onGetAllManagers();\n }\n\n onChangeChannel(event: any) {\n this.webSocketService.offErrorOlx(this.channel_id);\n this.webSocketService.offSuccessOlx(this.channel_id);\n this.channel_id = event.id;\n this.account = {};\n this.init();\n }\n\n openDialogUpdateChannel(): void {\n const dialogRef = this.dialog.open(ChannelComponent, {\n data: { channel: this.channels.find(v => v.id === this.channel_id).name },\n });\n\n dialogRef.afterClosed().subscribe(result => {\n if (result) {\n this.integrationService.updateChannel(this.channel_id, result).subscribe({\n next: () => {\n this.init();\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n console.log(ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n });\n }\n\n disconnectAccount() {\n const dialogRef = this.dialogService.openConfirm({\n text: 'dialogs.integration-olx_1',\n });\n dialogRef.subscribe(result => {\n if (result) {\n this.integrationService.disconnect(this.channel_id)\n .subscribe({\n next: () => {\n this.init();\n },\n error: (e) => {\n this.error = e?.error?.errors[0].message;\n (e.error?.errors || []).forEach((err: IError) => {\n console.log(ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n });\n }\n\n refreshAccount() {\n const dialogRef = this.dialogService.openConfirm({\n text: 'check_connect_correct_account',\n });\n\n dialogRef.subscribe(result => {\n if (result) {\n this.error = '';\n const url = new URL(environment.olx.url_ua + '/oauth/authorize');\n const company = this.baseService.getCompanyObj();\n url.searchParams.set('client_id', environment.olx.client_id);\n url.searchParams.set('scope', 'v2 read write');\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('redirect_uri', environment.olx.callback_refresh);\n url.searchParams.set('state', [`user_id:${this.baseService.currentUserInfo.id}`, `channel_id:${this.channel_id}`, `company_id:${company.id}`].join('|'));\n const windowFeatures = 'left=100,top=100,width=400,height=600';\n const newWindow = window.open(\n url,\n '_blank',\n windowFeatures,\n );\n if (newWindow) newWindow.focus();\n }\n });\n }\n\n connectAccount() {\n this.isLoader = true;\n const url = new URL(environment.olx.url_ua + '/oauth/authorize');\n const company = this.baseService.getCompanyObj();\n url.searchParams.set('client_id', environment.olx.client_id);\n url.searchParams.set('scope', 'v2 read write');\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('redirect_uri', environment.olx.callback_auth);\n url.searchParams.set('state', [`user_id:${this.baseService.currentUserInfo.id}`, `channel_id:${this.channel_id}`, `company_id:${company.id}`].join('|'));\n const windowFeatures = 'left=100,top=100,width=400,height=600';\n const newWindow = window.open(\n url,\n '_blank',\n 'left=0,top=0,width=0,height=0',\n );\n // if (newWindow) newWindow.focus();\n }\n\n deleteChannel() {\n const dialogRef = this.dialogService.openConfirm({\n text: 'dialogs.integration-olx_2',\n });\n dialogRef.subscribe(result => {\n if (result) {\n this.channelService.delete(this.channel_id)\n .subscribe({\n next: () => {\n this.init();\n this.rightSidebarService.close('success');\n },\n error: (e) => {\n (e?.error?.errors || []).map(({ message }: any) => {\n this.notificationService.showError('', message);\n });\n },\n });\n }\n });\n }\n\n public onGetAllManagers(isInit: boolean = true): void {\n const params = { search: this.searchedManager, take: 25, page: this.managerPage };\n this.crmService.getAllManagers(params)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v) => {\n this.managersListInfo = {\n page: v.page,\n size: v.size,\n total: v.total,\n };\n if (isInit || !!this.searchedManager) this.managersList = this.onParseFullname(v.items);\n else this.managersList = [...this.managersList, ...this.onParseFullname(v.items)];\n },\n error: (e) => {\n console.log(e);\n },\n });\n }\n\n private onParseFullname(arr: any[]): any[] {\n return arr.map((s: any) => {\n return { ...s, name: [s.first_name, s.last_name].join(' ') };\n });\n }\n\n public onUpdateChannel(e: any) {\n this.integrationService.updateChannel(this.channel.id, { name: this.channel.name, assign_user_id: e?.id || null })\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (data) => {\n this.channels = data;\n },\n error: (e): void => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n public onScrollToEndManagers() {\n if (this.managersListInfo.page * this.managersListInfo.size < this.managersListInfo.total) {\n ++this.managerPage;\n this.onGetAllManagers(false);\n }\n }\n\n public onSearchManager(e: any): void {\n this.searchedManager = e?.term || '';\n this.onGetAllManagers(false);\n }\n\n public onCloseDropdown() {\n this.searchedManager = '';\n this.managerPage = 1;\n this.onGetAllManagers();\n }\n}\n","
\n \n \n \n
\n
\n
\"Olx
\n
\n
{{'log_in_with_your_OLX_account' | translate}}
\n

{{'channel' | translate}}: {{ channel_name }}

\n \n

{{'status'| translate}}:\n \n {{ statuses[status]?.translate && statuses[status]?.title ? (statuses[status]?.title | translate) : statuses[status]?.title }}\n \n

\n
\n \n \n {{'disable' | translate}}\n \n \n {{'reconnect' | translate}}\n {{'disable' | translate}}\n \n \n {{'reconnect' | translate}}\n \n \n {{'connect' | translate}}\n {{'delete'| translate}}\n \n \n\n\n \n \n
\n
\n
\n
\n
\n \n
{{'instructions_for_obtaining_account_data' | translate}}:
\n
    \n
  1. {{'pen_the_browser_in_which_you_log_in_to_the_OLX_account_you_need' | translate}}.
  2. \n
  3. {{'Log_in_to_your_OLX_account_in_this_browser' | translate}}.
  4. \n
  5. {{'in_a_new_tab,_go_to' | translate}} {{ NAME_PROJECT }} ({{ environment.web_url }}) {{'using_your_credentials'| translate}}.\n
  6. \n
  7. {{'in_the_account_connection_tab,_click_the_button' | translate}} \"{{'connect' | translate}}\".
  8. \n
  9. {{'a_new_window_will_appear_in_which' | translate}} {{ NAME_PROJECT }} {{'you_need_to_grant_access_to_your_OLX_account.' | translate}}. {{'click_the_button'| translate}}\n \"{{'allow' | translate}}\".\n
  10. \n
\n
\n {{'account_connected_messenger' | translate}} {{ NAME_PROJECT }} {{'on_any_device' | translate}}.\n
\n
\n \n
{{'information'| translate}}
\n \n \n
\n \n
\n
\n \n
\n

ID {{'user'| translate}}: {{ account.id || channel.x_data?.user_id }}

\n

Email {{'user'| translate}}: {{ account.email || channel.x_data?.user_email }}

\n

Phone Login {{'user'| translate}}: {{ account.phone_login || channel.x_data?.user_phone_login }}

\n

Phone {{'user'| translate}}: {{ account.phone || channel.x_data?.user_phone }}

\n

{{'business_account' | translate }}?: {{ (account.is_business || channel.x_data?.user_is_business) ? 'yes' : 'no' | translate}}

\n

{{'domain' | translate}}: https://olx.ua\n

\n

{{'token' | translate }}: {{ channel.x_data?.access_token }}

\n
\n
\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n
{{'channel_settings' | translate}}
\n \n
\n
\n \n \n {{'customize' | translate}}\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n","import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Subject, takeUntil } from 'rxjs';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { ContactService } from '../../../../../services/contact.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { IError } from '../../../../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { BaseService } from '../../../../../services/base.service';\nimport * as _ from 'lodash';\nimport { trimValidator } from '../../../../../helpers';\n\n\ninterface IContactForm {\n name: FormControl;\n phone: FormControl;\n email: FormControl;\n address: FormControl;\n comment: FormControl;\n}\n\n@Component({\n selector: 'app-contact-detail',\n templateUrl: './contacts.component.html',\n styleUrls: ['./contacts.component.scss']\n})\nexport class ContactsComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n public contactForm!: FormGroup;\n public contact!: any;\n public contact_id!: string;\n\n public isValidationInvalid: boolean = false;\n public hasChange: boolean = false;\n public isLoader: boolean = false;\n\n\n constructor(\n private rightSidebarService: RightSidebarService,\n private contactService: ContactService,\n private notificationService: NotificationService,\n private dialogService: DialogService,\n public baseService: BaseService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.contact_id = options.data?.contact_id;\n }\n\n public ngOnInit(): void {\n this.isLoader = true;\n this.initContact();\n }\n\n onCreateGroupFormValueChange() {\n const initialValue: any = { ...this.contactForm.getRawValue() };\n this.contactForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.hasChange = !_.isEqual(value, initialValue);\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm(): void {\n if (!this.contact) {\n this.contactForm = new FormGroup({\n name: new FormControl(null, [Validators.required, trimValidator]),\n phone: new FormControl(null),\n email: new FormControl(null, [Validators.email]),\n address: new FormControl(null),\n comment: new FormControl(null, [Validators.maxLength(5000)])\n });\n } else {\n this.contactForm = new FormGroup({\n name: new FormControl(this.contact.name),\n phone: new FormControl(this.contact?.phones && this.contact?.phones[0]),\n email: new FormControl(this.contact?.emails && this.contact?.emails[0], Validators.email),\n address: new FormControl(this.contact?.addresses && this.contact?.addresses[0]),\n comment: new FormControl(this.contact?.comment, [Validators.maxLength(5000), trimValidator])\n });\n this.onCreateGroupFormValueChange();\n }\n }\n\n public onSaveContact(): void {\n const { name, phone, email, address, comment } = this.contactForm.getRawValue();\n\n this.isValidationInvalid = false;\n\n if (this.contactForm.controls.name.invalid) {\n this.contactForm.controls.name.markAsTouched();\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.name_field_required);\n }\n\n if (this.contactForm.controls.phone.invalid) {\n this.contactForm.controls.phone.markAsTouched();\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.phone_field_required);\n }\n\n if (this.contactForm.controls.email.invalid) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.email_not_expected);\n this.isValidationInvalid = true;\n }\n\n if (!!phone && phone.length < 12) {\n this.contactForm.controls['phone'].setErrors({\n 'incorrect': true\n });\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.number_invalid);\n this.isValidationInvalid = true;\n }\n\n if (this.contactForm.invalid || this.isValidationInvalid) return;\n\n\n const addressValidation = (add: any) => {\n if (Array.isArray(add)) return add.filter(Boolean).map(v => v.trim());\n else if (add) return [{ address: add.address }];\n else return [];\n };\n\n const contactValidation = (el: any) => {\n if (Array.isArray(el)) return el.filter(Boolean);\n else if (el) return [el];\n else return [];\n };\n\n\n if (!this.contact) {\n this.isLoader = true;\n this.contactService.create({\n name: name.trim(),\n phone: contactValidation(phone),\n email: contactValidation(email),\n address: addressValidation(address),\n comment: comment,\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close('close');\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_created);\n this.isLoader = false;\n },\n error: (error) => {\n this.isLoader = false;\n (error.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.contact_creation_error);\n },\n })\n } else {\n this.isLoader = true;\n this.contactService.update(this.contact.id, {\n name: name.trim(),\n phone: contactValidation(phone),\n email: contactValidation(email),\n address: addressValidation(address),\n comment: comment.trim(),\n }).pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.isLoader = false;\n this.rightSidebarService.close('close');\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (e) => {\n this.isLoader = false;\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.contact_updating_error);\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n }\n\n public onDeleteContact(): void {\n const dialogRef = this.dialogService.openConfirm({\n text: 'dialogs.contacts',\n });\n\n dialogRef.subscribe({\n next: (result: any) => {\n if (result) {\n this.contactService.delete(this.contact.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close('close');\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n },\n error: (e) => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.contact_deleting_error);\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_1)\n }\n });\n }\n\n public onCloseSidebar() {\n this.rightSidebarService.close('close');\n }\n\n private initContact() {\n if (!this.contact_id) {\n this.initForm();\n this.isLoader = false;\n return;\n }\n this.contactService.getById(this.contact_id)\n .subscribe({\n next: (res) => {\n this.contact = res;\n this.initForm();\n this.isLoader = false;\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n })\n }\n\n public onNoClick(): void {\n this.rightSidebarService.close('close');\n }\n}\n","
\n
\n

{{contact ? ('contact' | translate) + ' ' + contact.name : ('create_new_contact' | translate)}}

\n \n close\n \n
\n\n \n \n
\n \n
\n
\n
\n
\n
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n\n \n\n
\n \n \n \n {{ 'email_format_is_incorrect' | translate }}\n \n \n
\n\n \n\n
\n \n \n
\n
\n
\n {{'max_characters' | translate}} 5000.\n
\n
\n
\n {{contactForm.controls.comment.value?.length || 0}}/5000\n
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n {{ 'cancel' | translate }}\n
\n {{ 'save' | translate }}\n {{'delete' | translate}}\n \n
\n
\n
\n","import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-reminders',\n templateUrl: './reminders.component.html',\n styleUrls: ['./reminders.component.scss']\n})\nexport class RemindersComponent {\n fakeLikes = [\n { avatar: '/assets/images/bg/avatar1.jpeg', name: 'John Doe', reaction: 'reacted to your post', text: 'Lorem ipsum dolor sit amet consectetur,adipisicing elit. Unde,dolorem.' },\n { avatar: '/assets/images/bg/avatar2.jpeg', name: 'Richard Miles', reaction: 'liked your post', text: 'Lorem ipsum dolor sit amet consectetur,adipisicing elit. Unde,dolorem.'},\n { avatar: '/assets/images/bg/avatar3.jpeg', name: 'Brian Cumin', reaction: 'reacted to your post', text: 'Lorem ipsum dolor sit amet consectetur,adipisicing elit. Unde,dolorem.' },\n];\n}\n","
\n
\n
\n Notifications\n notifications\n
\n
\n
\n
\n
\n \"Avatar\"\n
\n
\n
\n
{{ like.name }}
\n
{{ like.reaction }}
\n
\n\n
{{ like.text }}
\n
\n\n
\n
\n
\n","import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-handbook',\n templateUrl: './handbook.component.html',\n styleUrls: ['./handbook.component.scss']\n})\nexport class HandbookComponent {\n\n}\n","

handbook works!

\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { BehaviorSubject, map, tap } from 'rxjs';\nimport { environment } from 'src/environments/environment';\nimport { IParams } from '../model/http';\n\nexport const initCategoriesListsParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport const initProductsListsParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\n@Injectable({\n providedIn: 'root',\n})\nexport class WarehouseService {\n private readonly itemsCategoriesSubject$ = new BehaviorSubject({});\n public readonly itemsCategories$ =\n this.itemsCategoriesSubject$.asObservable();\n private readonly itemsProductsSubject$ = new BehaviorSubject({});\n public readonly itemsProducts$ = this.itemsProductsSubject$.asObservable();\n\n public queryCategoriesListsParams = { ...initCategoriesListsParams };\n public queryProductsListsParams = { ...initProductsListsParams };\n\n private pageSize!: number;\n private itemsCategories: any;\n private itemsProducts: any;\n\n constructor(private readonly http: HttpClient) {}\n\n private getAllCategories(params: any) {\n return this.http\n .post(environment.baseURL + '/composition/categories', params)\n .pipe(map((v) => v.data));\n }\n\n public getCategory(uuid: string) {\n return this.http\n .get(environment.baseURL + `/composition/category/${uuid}`)\n .pipe(map((v) => v.data));\n }\n\n loadCategoriesLists() {\n return this.getAllCategories(this.queryCategoriesListsParams).pipe(\n tap((data) => {\n this.itemsCategories = data;\n this.itemsCategoriesSubject$.next(data);\n }),\n );\n }\n\n private getAllProducts(params: any) {\n return this.http\n .post(environment.baseURL + '/composition/products', params)\n .pipe(map((v) => v.data));\n }\n\n loadProductsLists() {\n return this.getAllProducts(this.queryProductsListsParams).pipe(\n tap((data) => {\n this.itemsProducts = data;\n this.itemsProductsSubject$.next(data);\n }),\n );\n }\n\n deleteCategory(uuid: string) {\n return this.http.delete(\n environment.baseURL + `/composition/category/${uuid}`,\n );\n }\n\n deleteProduct(uuid: string) {\n return this.http.delete(\n environment.baseURL + `/composition/product/${uuid}`,\n );\n }\n\n setQueryItemsParams(params: IParams) {\n const { sort, order, search, take, page, filters } = params;\n this.queryCategoriesListsParams = {\n sort:\n sort ||\n (typeof sort === 'string'\n ? sort\n : this.queryCategoriesListsParams.sort),\n order:\n order ||\n (typeof order === 'string'\n ? order\n : this.queryCategoriesListsParams.order),\n search:\n search ||\n (typeof search === 'string'\n ? search\n : this.queryCategoriesListsParams.search),\n take: take || this.queryCategoriesListsParams.take,\n page: page || this.queryCategoriesListsParams.page,\n filters: filters || this.queryCategoriesListsParams.filters,\n };\n\n return this.loadCategoriesLists();\n }\n\n setQueryProductsParams(params: IParams) {\n const { sort, order, search, take, page, filters } = params;\n this.queryProductsListsParams = {\n sort:\n sort ||\n (typeof sort === 'string' ? sort : this.queryProductsListsParams.sort),\n order:\n order ||\n (typeof order === 'string'\n ? order\n : this.queryProductsListsParams.order),\n search:\n search ||\n (typeof search === 'string'\n ? search\n : this.queryProductsListsParams.search),\n take: take || this.queryProductsListsParams.take,\n page: page || this.queryProductsListsParams.page,\n filters: filters || this.queryProductsListsParams.filters,\n };\n\n return this.loadProductsLists();\n }\n\n addCategory(body: any) {\n return this.http.post(environment.baseURL + '/composition/category', body);\n }\n\n addProduct(body: any) {\n return this.http.post(environment.baseURL + '/composition/product', body);\n }\n\n updateCategory(id: string, payload: any) {\n return this.http.put(\n environment.baseURL + `/composition/category/${id}`,\n payload,\n );\n }\n\n updateProduct(id: string, payload: any) {\n return this.http.put(\n environment.baseURL + `/composition/product/${id}`,\n payload,\n );\n }\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Subject, takeUntil } from 'rxjs';\nimport { WarehouseService } from 'src/app/services/warehouse.service';\nimport { RightSidebarService } from 'src/app/services/right-sidebar.service';\nimport { SUCCESS_RESPONSE } from '../../../../../enum/constants';\nimport { BaseService } from 'src/app/services/base.service';\n\ninterface ICategoryForm {\n name: FormControl;\n description: FormControl;\n}\n\n@Component({\n selector: 'app-create-category',\n templateUrl: './create-category.component.html',\n styleUrls: ['./create-category.component.scss']\n})\nexport class CreateCategoryComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n public form!: FormGroup;\n public isFormSubmitted = false;\n public isValidSubmitted = false;\n public isUpdate = false;\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly warehouseService: WarehouseService,\n readonly baseService: BaseService\n ) {\n const options = this.rightSidebarService.getOptions();\n this.isUpdate = options.data?.is_update;\n }\n\n ngOnInit() {\n this.initForm();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n if (this.isUpdate) {\n const options = this.rightSidebarService.getOptions();\n this.form = new FormGroup({\n name: new FormControl(options.data?.name, Validators.required),\n description: new FormControl(options.data?.description, Validators.maxLength(5000)),\n });\n } else {\n this.form = new FormGroup({\n name: new FormControl('', Validators.required),\n description: new FormControl('', Validators.maxLength(5000)),\n });\n }\n }\n\n public onNoClick(): void {\n this.rightSidebarService.close('close');\n }\n\n public onSubmit() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n this.warehouseService.addCategory(this.form.getRawValue())\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n },\n error: (res) => {\n console.error('Error during category creation:', res);\n },\n });\n }\n }\n\n public onUpdate() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n const options = this.rightSidebarService.getOptions();\n this.warehouseService.updateCategory(options.data?.id, this.form.getRawValue())\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n },\n error: (res) => {\n console.error('Update failed:', res);\n },\n });\n } \n }\n}\n","
\n
\n

\n {{ isUpdate ? ('dialogs.update_category' | translate) : 'dialogs.add_category' | translate }}\n

\n \n close\n \n
\n\n \n\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}.\n \n\n
\n \n \n
\n
\n
\n {{ 'max_characters' | translate }} 5000.\n
\n
\n
\n {{ form.controls.description.value.length }}/5000\n
\n
\n
\n
\n\n \n\n
\n {{ 'cancel' | translate }}\n
\n {{ 'save' | translate }}\n {{ 'change' | translate }}\n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Subject, takeUntil } from 'rxjs';\nimport { WarehouseService } from 'src/app/services/warehouse.service';\nimport { RightSidebarService } from 'src/app/services/right-sidebar.service';\nimport { BaseService } from 'src/app/services/base.service';\nimport { SUCCESS_RESPONSE } from '../../../../../enum/constants';\n\ninterface IProductForm {\n name: FormControl;\n category: FormControl;\n description: FormControl;\n}\n\n@Component({\n selector: 'app-create-product',\n templateUrl: './create-product.component.html',\n styleUrls: ['./create-product.component.scss'],\n})\nexport class CreateProductComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n public form!: FormGroup;\n public isFormSubmitted = false;\n public isValidSubmitted = false;\n public isUpdate = false; \n public categoriesManager: any[] = [];\n\n constructor(\n private readonly warehouseService: WarehouseService,\n private readonly rightSidebarService: RightSidebarService,\n readonly baseService: BaseService\n ) {\n const options = this.rightSidebarService.getOptions();\n this.isUpdate = options.data?.is_update;\n }\n\n ngOnInit() {\n this.initForm();\n this.onGetAllManagers();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n const options = this.rightSidebarService.getOptions();\n \n this.form = new FormGroup({\n name: new FormControl(this.isUpdate ? options.data?.name : '', Validators.required),\n category: new FormControl(this.isUpdate ? options.data?.category_name : '', Validators.required),\n description: new FormControl(this.isUpdate ? options.data?.description : '', Validators.maxLength(5000)),\n });\n }\n\n public onNoClick(): void {\n this.rightSidebarService.close('close');\n }\n\n public onSubmit() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n this.warehouseService.addProduct(this.form.getRawValue())\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n },\n error: (res) => {\n console.error('Error during product creation:', res);\n },\n });\n }\n }\n\n public onUpdate() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n const options = this.rightSidebarService.getOptions();\n this.warehouseService.updateProduct(options.data?.id, this.form.getRawValue())\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.rightSidebarService.close(SUCCESS_RESPONSE);\n },\n error: (res) => {\n console.error('Update failed:', res);\n },\n });\n }\n }\n\n public onGetAllManagers(): void {\n this.warehouseService.loadCategoriesLists()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v) => {\n this.categoriesManager = v.items;\n },\n error: (err) => {\n console.error('Error loading categories:', err);\n },\n });\n }\n}\n","
\n
\n

{{ isUpdate ? ('dialogs.update_product' | translate) : ('dialogs.add_product' | translate) }}

\n \n close\n \n
\n\n \n\n
\n \n \n {{'field_cannot_be_empty' | translate}}.\n \n\n \n\n
\n \n \n
\n
\n
\n {{ 'max_characters' | translate }} 5000.\n
\n
\n
\n {{ form.controls.description.value.length }}/5000\n
\n
\n
\n
\n\n \n\n
\n {{ 'cancel' | translate }}\n
\n {{ 'save' | translate }}\n {{ 'change' | translate }}\n
\n
\n","import { Component, Inject, OnDestroy, OnInit } from '@angular/core';\nimport { NotificationService } from '../../services/notifications.service';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { ModalService } from '../../services/modal.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Subject } from 'rxjs';\nimport { BaseService } from '../../services/base.service';\nimport moment from 'moment';\nimport { MatDatepickerInputEvent } from '@angular/material/datepicker';\nimport { OlxAdvertService } from '../../services/olx-advert.service';\nimport { ADVERT_PLANNER_TYPE } from '../../enum/constants';\n\ninterface FormDateTime {\n name: FormControl,\n x_data: any\n}\n\n@Component({\n selector: 'app-advert-plain',\n templateUrl: './planner.component.html',\n styleUrls: ['./planner.component.scss'],\n})\nexport class PlannerComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n public isLoader: boolean = false;\n public selected = null;\n public today = new Date();\n public advert_id!: string;\n public channel_id!: string;\n public adverts!: string[];\n public planner_id!: string;\n public planners: string[] = [];\n\n public advert: any = {};\n public type: any;\n public modalType = '';\n public info: any;\n\n formPlanner!: FormGroup;\n\n eachTimes = [\n { name: '0m', value: '0' },\n { name: '5m', value: '5' },\n { name: '10m', value: '10' },\n { name: '15m', value: '15' },\n { name: '30m', value: '30' },\n { name: '45m', value: '45' },\n { name: '1h', value: '60' },\n { name: '2h', value: '120' },\n { name: '3h', value: '180' },\n ];\n\n typePlanners = [\n { name: ADVERT_PLANNER_TYPE.ACTIVATION, value: 'ACTIVATION' },\n { name: ADVERT_PLANNER_TYPE.DEACTIVATION, value: 'DEACTIVATION' },\n { name: ADVERT_PLANNER_TYPE.PUSH_UP, value: 'PUSH_UP' },\n { name: ADVERT_PLANNER_TYPE.CREATE_ADVERT, value: 'CREATE_ADVERT' },\n { name: ADVERT_PLANNER_TYPE.ADS, value: 'ADS' },\n { name: ADVERT_PLANNER_TYPE.PACKET, value: 'PACKET' },\n ];\n\n statusPlanners = [\n { name: 'Чорновик', value: 'DRAFT' },\n { name: 'Активний', value: 'WAITING' },\n ];\n\n constructor(\n public modalService: ModalService,\n private notificationService: NotificationService,\n private dialogRef: MatDialogRef,\n public readonly baseService: BaseService,\n private readonly olxAdvertService: OlxAdvertService,\n @Inject(MAT_DIALOG_DATA) public data: any,\n ) {\n this.modalType = this.data.data.modalType;\n this.type = this.data.data.type;\n this.advert_id = data?.data?.advert_id;\n this.adverts = data?.data?.adverts;\n this.planners = data?.data?.planners || [];\n this.channel_id = data?.data?.channel_id;\n this.planner_id = data?.data?.planner_id;\n this.advert = data?.data?.advert;\n this.info = data?.data?.info;\n }\n\n ngOnInit(): void {\n this.onInitForm();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private onInitForm(planner?: any): void {\n this.formPlanner = new FormGroup({\n name: new FormControl(this.info?.name || planner?.name || null, [Validators.required]),\n x_data: new FormGroup({\n type: new FormControl(planner?.type || this.info?.type_planner || null, !this.planners?.length && this.modalType === 'ADVERT' ? [Validators.required] : []),\n status: new FormControl(planner?.status || this.info?.status || 'WAITING', !this.planners?.length ? [Validators.required] : []),\n date: new FormControl(planner?.planned_at || this.info?.planned_at || '', [Validators.required]),\n time: new FormControl(moment(planner?.planned_at).format('HH:mm') || moment(this.info?.type).format('HH:mm') || '', [Validators.required, Validators.minLength(5)]),\n step: new FormControl('0', []),\n }),\n });\n }\n\n onSave() {\n this.dialogRef.close(this.formPlanner.getRawValue());\n }\n\n addDate(type: string, event: MatDatepickerInputEvent) {\n this.formPlanner.controls.x_data.date.patchValue(event.value);\n }\n\n onClose() {\n this.dialogRef.close();\n }\n}\n","
\n \n {{'name' | translate}}\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n\n\n
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n\n \n\n\n \n Оберіть дату\n \n DD/MM/YYYY\n \n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n\n \n Оберіть час\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n \n {{ 'planner_min_min_length' | translate }}\n \n \n \n\n \n \n\n \n \n
\n {{ 'cancel' | translate }}\n {{ 'save' | translate }}\n
\n\n
\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, Observable, take, tap } from 'rxjs';\nimport { IParams } from '../model/http';\n\nexport const initItemsParams: any = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n filters: {},\n};\n\nexport const initPlannersParams: any = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport const initBackupsParams: any = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport const initDeliveriesParams: any = {\n filters: '',\n take: 10,\n page: 0,\n};\n\nexport const initPacketsParams: any = {\n filters: '',\n take: 10,\n page: 0,\n};\n\n@Injectable({\n providedIn: 'root'\n})\nexport class OlxAdvertService {\n private readonly itemsSubject$ = new BehaviorSubject({});\n public readonly items$ = this.itemsSubject$.asObservable();\n\n private readonly itemsPlannersSubject$ = new BehaviorSubject({});\n public readonly itemsPlanners$ = this.itemsPlannersSubject$.asObservable();\n\n private readonly itemsDeliveriesSubject$ = new BehaviorSubject({});\n public readonly itemsDeliveries$ = this.itemsDeliveriesSubject$.asObservable();\n\n private readonly itemsPacketsSubject$ = new BehaviorSubject({});\n public readonly itemsPackets$ = this.itemsPacketsSubject$.asObservable();\n\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryItemsParams = { ...initItemsParams };\n public queryPlannersParams = { ...initPlannersParams };\n public queryBackupsParams = { ...initBackupsParams };\n public queryDeliveriesParams = { ...initDeliveriesParams };\n public queryPacketsParams = { ...initPacketsParams };\n\n\n constructor(\n private readonly http: HttpClient,\n ) { }\n\n\n private getAll(params: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/adverts`, params)\n .pipe(map(v => v.data));\n }\n\n private getAllBackups(params: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/backup-adverts`, params)\n .pipe(map(v => v.data));\n }\n\n private getAllDeliveries(params: any) {\n return this.http.get(`${environment.baseURL}/olx-tools/deliveries`, { params } )\n .pipe(map(v => v.data));\n }\n\n public getAllPackets(params: any) {\n return this.http.get(`${environment.baseURL}/olx-tools/packets`, { params } )\n .pipe(map(v => v.data));\n }\n\n getAllAdvertising (channel_id: string, ad_ids: string[]) {\n return this.http.get(`${environment.baseURL}/olx-tools/advertising`, { params: { channel_id, ad_ids }} )\n .pipe(map(v => v.data));\n }\n\n getOneAdvertising (channel_id: string, ad_ids: string[]) {\n return this.http.get(`${environment.baseURL}/olx-tools/advertising/one`, { params: { channel_id, ad_ids }} )\n .pipe(map(v => v.data));\n }\n\n public loadItems(channel_id?: string) {\n this.isLoader$.next(true);\n return this.getAll({ channel_id, ...this.queryItemsParams })\n .pipe(tap((items) => {\n this.itemsSubject$.next(items);\n this.isLoader$.next(false);\n }));\n }\n\n public loadPlanners(channel_id: string) {\n this.isLoader$.next(true);\n return this.getAllPlanners({channel_id, ...this.queryPlannersParams})\n .pipe(tap((v) => {\n this.itemsPlannersSubject$.next(v);\n this.isLoader$.next(false);\n }));\n }\n\n public loadBackups(channel_id?: string) {\n this.isLoader$.next(true);\n return this.getAllBackups({ channel_id, ...this.queryBackupsParams })\n .pipe(tap((items) => {\n this.itemsSubject$.next(items);\n this.isLoader$.next(false);\n }));\n }\n\n public loadDeliveries(channel_id?: string) {\n return this.getAllDeliveries({ channel_id, ...this.queryDeliveriesParams })\n .pipe(tap((items) => {\n this.itemsDeliveriesSubject$.next(items);\n }));\n }\n\n public loadPackets(channel_id?: string) {\n return this.getAllPackets({ channel_id, ...this.queryPacketsParams })\n .pipe(tap((items) => {\n this.itemsPacketsSubject$.next(items);\n }));\n }\n\n setQueryItemsParams(channel_id: string, params: IParams) {\n const { sort, order, search, take, page, filters } = params;\n this.queryItemsParams = {\n channel_id: channel_id,\n sort: sort || typeof (sort) === 'string' ? sort : this.queryItemsParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryItemsParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryItemsParams.search,\n take: take || this.queryItemsParams.take,\n page: page || this.queryItemsParams.page,\n filters: filters || this.queryItemsParams.filters,\n };\n\n return this.loadItems();\n }\n\n\n setQueryPlannersParams(channel_id: string, params: IParams) {\n const { sort, order, search, take, page, filters } = params;\n this.queryPlannersParams = {\n channel_id: channel_id,\n sort: sort || typeof (sort) === 'string' ? sort : this.queryPlannersParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryPlannersParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryPlannersParams.search,\n take: take || this.queryPlannersParams.take,\n page: page || this.queryPlannersParams.page,\n filters: filters || this.queryItemsParams.filters,\n };\n\n return this.loadPlanners(channel_id);\n }\n\n setQueryBackupsParams(channel_id: string, params: IParams) {\n const { sort, order, search, take, page, filters } = params;\n this.queryBackupsParams = {\n channel_id: channel_id,\n sort: sort || typeof (sort) === 'string' ? sort : this.queryBackupsParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryBackupsParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryBackupsParams.search,\n take: take || this.queryBackupsParams.take,\n page: page || this.queryBackupsParams.page,\n filters: filters || this.queryBackupsParams.filters,\n };\n\n return this.loadBackups();\n }\n\n setQueryDeliveryParams(channel_id: string, params: IParams) {\n const { page, filters } = params;\n this.queryDeliveriesParams = {\n channel_id: channel_id,\n page: page ?? this.queryDeliveriesParams.page,\n filters: filters ?? this.queryDeliveriesParams.filters,\n };\n\n return this.loadDeliveries();\n }\n\n setQueryPacketsParams(channel_id: string, params: IParams) {\n const { page, filters } = params;\n this.queryPacketsParams = {\n channel_id: channel_id,\n page: page ?? this.queryPacketsParams.page,\n filters: filters ?? this.queryPacketsParams.filters,\n };\n\n return this.loadPackets();\n }\n\n updateStatus(channel_id: string, advert_id: string, command: string) {\n const url = `${environment.baseURL}/olx-tools/adverts/${advert_id}/command`;\n return this.http.put(url, { channel_id, command })\n .pipe(map(v => v.data));\n }\n\n updatePrice(channel_id: string, advert_id: string, value: any) {\n const url = `${environment.baseURL}/olx-tools/advert/${advert_id}/`;\n return this.http.put(url, { channel_id, olx: { price: value } })\n .pipe(map(v => v.data));\n }\n\n updateAdvert(channel_id: string, advert_id: string, olx: any) {\n const url = `${environment.baseURL}/olx-tools/advert/${advert_id}/`;\n return this.http.put(url, { channel_id, olx: olx })\n .pipe(map(v => v.data));\n }\n\n createAdvert(channel_id: string, advert: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/advert`, { channel_id, advert })\n .pipe(map(v => v.data));\n }\n\n duplicationAdvert(channel_id: string, advert_id: string) {\n const url = `${environment.baseURL}/olx-tools/advert/${advert_id}/duplication`;\n return this.http.put(url, { channel_id })\n .pipe(map(v => v.data));\n }\n\n deleteAdvert(channel_id: string, advert_id: string) {\n const url = `${environment.baseURL}/olx-tools/advert/${advert_id}/delete`;\n return this.http.put(url, { channel_id })\n .pipe(map(v => v.data));\n }\n\n\n updateBulkStatus(channel_id: string, adverts: string[], command: string) {\n const url = `${environment.baseURL}/olx-tools/adverts/command`;\n return this.http.put(url, { channel_id, adverts, command })\n .pipe(map(v => v.data));\n }\n\n getAllPlanners(params: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/planners`, params)\n .pipe(map(v => v.data));\n }\n\n createPlanner(payload: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/adverts/planner`, payload)\n .pipe(map(v => v.data));\n }\n\n bulkPlanners(payload: any) {\n return this.http.post(`${environment.baseURL}/olx-tools/adverts/planner/bulk`, payload)\n .pipe(map(v => v.data));\n }\n\n cancelBulkPlanners(ids: string[]) {\n return this.http.put(`${environment.baseURL}/olx-tools/adverts/planner/bulk/cancel`, {ids})\n .pipe(map(v => v.data));\n }\n\n bulkPlannersUpdate(planners: string[], data: any = {}) {\n return this.http.put(`${environment.baseURL}/olx-tools/adverts/planner/bulk`, {planners, ...data})\n .pipe(map(v => v.data));\n }\n\n updatePlanner(planner_id: string, planned_at: string) {\n return this.http.put(`${environment.baseURL}/olx-tools/adverts/planner`, {planner_id, planned_at})\n .pipe(map(v => v.data));\n }\n\n cancelPlanner(planner_id: string) {\n return this.http.put(`${environment.baseURL}/olx-tools/adverts/planner/cancel`, { planner_id })\n .pipe(map(v => v.data));\n }\n\n destroy() {\n this.queryItemsParams = {...initItemsParams};\n this.queryPlannersParams = {...initPlannersParams};\n this.queryBackupsParams = {...initBackupsParams};\n this.queryDeliveriesParams = {...initDeliveriesParams};\n }\n\n getBalance(channel_id: string) {\n return this.http.get(`${environment.baseURL}/olx-tools/balance`, { params: { channel_id }})\n .pipe(map(v => v.data));\n }\n\n pushUpAdvert(channel_id: string, advert_id: string) {\n return this.http.post(`${environment.baseURL}/olx-tools/advert/${advert_id}/push-up`, { advert_id, channel_id })\n .pipe(map(v => v.data));\n }\n\n bulkPushUpAdvert(channel_id: string, adverts: string[]) {\n return this.http.post(`${environment.baseURL}/olx-tools/adverts/push-up/bulk`, { adverts, channel_id })\n .pipe(map(v => v.data));\n }\n\n backupRestore(channel_id: string, id: string) {\n return this.http.post(`${environment.baseURL}/olx-tools/backup-adverts/restore`, { id, channel_id })\n .pipe(map(v => v.data));\n }\n\n getLastSync(advert_id: string) {\n return this.http.post(`${environment.baseURL}/olx-tools/last-sync`, { advert_id })\n .pipe(map(v => v.data));\n }\n\n buyPacket(payload: any, channel_id: string) {\n return this.http.post( `${environment.baseURL}/olx-tools/packet`, payload, { params: {\n channel_id,\n }})\n .pipe(map(v => v.data));\n }\n updateStatusPacket(payload: any) {\n return this.http.put(`${environment.baseURL}/olx-tools/status-planner`, payload)\n .pipe(map(v => v.data));\n }\n\n updatePlanner2(payload: any, event: any) {\n return this.http.put(`${environment.baseURL}/olx-tools/update-planner`, payload, { params: event })\n .pipe(map(v => v.data));\n }\n\n selectDraftPacket(payload: any, channel_id: string) {\n return this.http.post( `${environment.baseURL}/olx-tools/draft-planner`, payload, { params: {\n channel_id,\n }})\n .pipe(map(v => v.data));\n }\n\n\n getZones(category_id: any, channel_id: string) {\n return this.http.get( `${environment.baseURL}/olx-tools/zones`, { params: {\n category_id,\n channel_id,\n }})\n .pipe(map(v => v.data));\n }\n\n syncAdvert(advert_id: string, channel_id: string) {\n return this.http.put(`${environment.baseURL}/olx-tools/sync-advert`, { advert_id, channel_id})\n .pipe(map(v => v.data));\n }\n\n getCategoryAttributes(category_id: string, channel_id: string){\n return this.http.get(`${environment.baseURL}/olx-tools/category-attributes`, { params: {category_id, channel_id}})\n .pipe(map(v => v.data));\n }\n\n getCategories(parent_id: number) {\n return this.http.get(`${environment.baseURL}/olx-tools/categories`, { params: { parent_id } })\n .pipe(map(v => v.data));\n }\n\n getCategoryById(category_id: number) {\n return this.http.get(`${environment.baseURL}/olx-tools/categories/${category_id}`)\n .pipe(map(v => v.data));\n }\n\n getCategoryPhoto(category_id: number) {\n return this.http.get(`${environment.baseURL}/olx-tools/category-photo`, { params: { category_id } })\n .pipe(map(v => v.data));\n }\n\n getPackets(current_category_id: number, channel_id: string, zone_id?: any) {\n return this.http.get(`${environment.baseURL}/olx-tools/categories/${current_category_id}/packets`, { params: { channel_id, zone_id } })\n .pipe(map(v => v.data));\n }\n\n payAds(payload: any, channel_id: string) {\n return this.http.post(`${environment.baseURL}/olx-tools/advertising`, {payload, channel_id })\n .pipe(map(v => v.data));\n }\n\n getCity(params: any) {\n return this.http.get(`${environment.baseURL}/olx-tools/city`, { params })\n .pipe(map(v => v.data));\n }\n\n getDistrics(params: any) {\n return this.http.get(`${environment.baseURL}/olx-tools/district`, { params })\n .pipe(map(v => v.data));\n }\n}\n","import { Component, OnInit } from '@angular/core';\nimport { RightSidebarService } from 'src/app/services/right-sidebar.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { TableColumnTypes } from '../../../../../model/table.model';\nimport { MatDatepickerInputEvent } from '@angular/material/datepicker';\nimport { FormControl, FormGroup } from '@angular/forms';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { PageEvent } from '@angular/material/paginator';\nimport { PlannerComponent } from '../../../../../modals/planner/planner.component';\nimport {\n ADS_TYPES,\n BUNDLE_TYPES,\n DEFAULT_ERROR,\n ERROR_MESSAGES,\n languages,\n MODAL_TYPES,\n} from '../../../../../enum/constants';\nimport moment from 'moment';\nimport { OlxAdvertService } from '../../../../../services/olx-advert.service';\nimport { MAT_DIALOG_DATA } from '@angular/material/dialog';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { map } from 'rxjs';\nimport {DialogService} from \"../../../../../services/dialog.service\";\nimport { IAdsData, IOlxAdvert, IPayloadBundle } from './create-ads.interface';\nimport { IBundle } from './create-ads.interface';\nimport { TranslateService } from '@ngx-translate/core';\n\ninterface FormDateTime {\n type: FormControl\n date: FormControl,\n time: FormControl,\n step: FormControl,\n}\n\n@Component({\n selector: 'app-create-ads',\n templateUrl: './create-ads.component.html',\n styleUrls: ['./create-ads.component.scss'],\n})\nexport class CreateAdsComponent implements OnInit {\n public advertList: IOlxAdvert[] = [];\n public adsPrice: any = {};\n public channel_id!: string;\n public date: any;\n public isLoader: boolean = false;\n public today = new Date();\n public totalAmount: number = 0;\n currentIndex = 0;\n formPlanner!: FormGroup;\n public items: number[] = [];\n selectedBundles: any[] = [];\n selectedBundle: number | null = null;\n modalRes: any;\n public advertsWithExpTime: number[] = [];\n\n protected readonly TableColumnTypes = TableColumnTypes;\n\n displayedColumns: string[] = ['index', 'ad_id', 'title', 'vas_list', 'bundle_basic', 'bundle_optimum', 'bundle_premium'];\n\n// 'up_list',\n constructor(\n public readonly rightSidebarService: RightSidebarService,\n public readonly baseService: BaseService,\n public readonly modalService: ModalService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly notificationService: NotificationService,\n private readonly dialogService: DialogService,\n private readonly translate: TranslateService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.advertList = options.data.selectionTable;\n this.channel_id = options.data.channel_id;\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n ngOnInit() {\n this.isLoader = true;\n const ad_ids = this.advertList.map((res: IOlxAdvert) => res.id);\n const ad_ids_str = ad_ids.map((i: number) => i.toString());\n\n if (ad_ids.length > 1) {\n ad_ids.join(',');\n this.olxAdvertService.getAllAdvertising(this.channel_id, ad_ids_str)\n .subscribe((res) => {\n this.adsPrice = res;\n this.isLoader = false;\n });\n } else {\n this.olxAdvertService.getOneAdvertising(this.channel_id, ad_ids_str)\n .subscribe((res) => {\n this.adsPrice = res;\n if (this.adsPrice && this.advertList) {\n this.advertList.map((l: any) => {\n this.adsPrice.map((p: any) => {\n if (l.id === +p.ad_id) {\n return p.ad_title = l.title;\n }\n })\n });\n }\n this.isLoader = false;\n });\n }\n }\n\n public setAllUpList(): void {\n\n }\n\n setDate(): void {\n this.modalService.openModal({\n title: 'ads_scheduler',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADS,\n },\n }).subscribe((res) => {\n if (res) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n this.date = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n this.modalRes = res;\n }\n });\n }\n\n setCurrentIndex(event: any) {\n this.currentIndex = parseInt(event, 10) || 0;\n }\n\n onPageChange(event: PageEvent): void {\n this.currentIndex = event.pageIndex;\n this.setCurrentIndex(this.currentIndex);\n }\n\n nextSlide(): void {\n if (this.currentIndex < this.advertList.length - 1) {\n this.currentIndex++;\n }\n }\n\n prevSlide(): void {\n if (this.currentIndex > 0) {\n this.currentIndex--;\n }\n }\n\n getSliderTransform(): string {\n return `translateX(-${this.currentIndex * 100}%)`;\n }\n\n addPrice(event: any, elem: any, key: string, value: string): void {\n elem[key] = event ? value : null;\n this.getPrice();\n this.updateSelectedBundles();\n this.filterAppliedAds();\n }\n\n updateSelectedBundles(): void {\n this.selectedBundles = this.adsPrice.filter((v: any) => v.bundle || v.vas);\n }\n\n getPrice() {\n let price = 0;\n this.adsPrice.forEach((v: any) => {\n const bundle = v.bundles.find((a: any) => a.product_id === v.bundle);\n const vas = v.vas_list.find((a: any) => a.product_id === v.vas);\n if (bundle) {\n price += bundle.pricing.regular.raw;\n }\n if (vas) {\n price += vas.pricing.regular.raw;\n }\n });\n this.totalAmount = price ? price / 100 : price;\n }\n\n // This function filtered already applied ads\n public filterAppliedAds() {\n this.advertsWithExpTime = this.selectedBundles\n .filter((adv: IAdsData) => adv.bundles\n .some((bundle: IBundle) => !bundle.enabled && adv.bundle === bundle.product_id))\n .map((res: IAdsData) => res.ad_id);\n }\n\n public onPay(): void {\n this.filterAppliedAds();\n\n const text = this.advertsWithExpTime.length > 0 ? this.translate.instant(\"plan_applied_ads\", {advertsWithExpTime: this.advertsWithExpTime}) : \"dialogs.pay_ad\";\n\n const dialogRef = this.dialogService.openConfirm({\n isTranslate: this.advertsWithExpTime.length > 0,\n text: text,\n price: this.totalAmount,\n });\n\n dialogRef.subscribe(result => {\n if (result) {\n const bundles: IPayloadBundle[] = [];\n this.adsPrice.forEach((v: any) => {\n const advert = this.advertList.find((a: IOlxAdvert) => a.id === v.ad_id);\n if (v.bundle) {\n bundles.push({\n advert_id: v.ad_id,\n title: v.ad_title,\n code: v.bundle,\n advert: advert || this.advertList[0],\n });\n }\n\n if (v.vas) {\n bundles.push({\n advert_id: v.ad_id,\n title: v.ad_title,\n code: v.vas,\n advert: advert || this.advertList[0],\n });\n }\n });\n\n if (this.date) {\n const payload = {\n selectedBundle: bundles,\n date: this.date,\n name: this.modalRes.name,\n status: this.modalRes.x_data.status,\n };\n this.olxAdvertService.payAds(payload, this.channel_id)\n .subscribe((res) => {\n if (res) {\n this.rightSidebarService.close();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_planned);\n } else {\n this.rightSidebarService.close();\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_planner);\n }\n });\n } else {\n const payload = {\n selectedBundle: bundles,\n };\n this.olxAdvertService.payAds(payload, this.channel_id)\n .subscribe((res) => {\n this.rightSidebarService.close();\n });\n }\n }\n });\n }\n\n cancelPlanner() {\n this.date = null;\n }\n\n isAnyBundleSelected(): boolean {\n return !this.adsPrice.some((element: any) => !!element.bundle || !!element.vas);\n }\n\n protected readonly ADS_TYPES = ADS_TYPES;\n protected readonly BUNDLE_TYPES = BUNDLE_TYPES;\n}\n","
\n
\n \n

{{ ('ads' | translate) }}

\n
\n\n \n\n\n \n
\n \n
\n
\n\n \n
\n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n
ID оголошенняІм'яРазове підняття вгоруЛегкий стартШвидкий продажТурбо продаж
{{ i + 1 }}{{ ads.ad_id }}{{ ads.ad_title }}\n \n {{ ads.vas_list[0]?.pricing.regular.formatted }}\n \n \n {{ ads.vas_list[0]?.owned_product_expiration_timestamp }}\n \n \n
\n \n {{ ads.bundles[0]?.pricing.regular.formatted }}\n \n \n {{ ads.bundles[0]?.owned_product_expiration_timestamp }}\n \n
\n
\n
\n \n {{ ads.bundles[1]?.pricing.regular.formatted }}\n \n \n {{ ads.bundles[1]?.owned_product_expiration_timestamp }}\n \n
\n
\n
\n \n {{ ads.bundles[2]?.pricing.regular.formatted }}\n \n \n {{ ads.bundles[2]?.owned_product_expiration_timestamp }}\n \n
\n
\n\n
\n
Всього: {{ totalAmount }} грн.
\n
\n
\n \n event_busy\n \n \n calendar_month\n \n
\n {{ 'cancel' | translate }}\n \n {{ 'pay' | translate }}\n \n \n {{ 'plan_packet' | translate }}\n \n
\n\n
\n
\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import { Injectable } from '@angular/core';\nimport { Site, SiteRequest, SiteUpdateRequest } from '../modules/crm/site/components/site-list/site-list.interface';\nimport { map } from 'rxjs';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class SiteService {\n private readonly urlBase = environment.baseURL;\n private readonly urlSite = `${this.urlBase}/site`;\n private readonly urlSiteTemplate = `${this.urlSite}/templates`;\n private readonly urlSiteSettings = `${this.urlSite}/settings`;\n\n constructor(private readonly http: HttpClient) {}\n\n public getSites() {\n return this.http.get(this.urlSite)\n .pipe(map(v => v.data));\n }\n\n public getSite(id: string) {\n return this.http.get(`${this.urlSite}/${id}`)\n .pipe(map(v => v.data));\n }\n\n public createSite(site: SiteRequest) {\n return this.http.post(this.urlSite, site)\n .pipe(map(v => v.data));\n }\n\n public updateSite(id: string, site: SiteUpdateRequest) {\n return this.http.put(`${this.urlSite}/${id}`, site)\n .pipe(map(v => v.data));\n }\n\n public deleteSite(id: string) {\n return this.http.delete(`${this.urlSite}/${id}`)\n .pipe(map(v => v.data));\n }\n\n public loadSettings(id: string) {\n return this.http.get(`${this.urlSiteSettings}/${id}`)\n .pipe(map(v => v.data));\n }\n\n public saveSettings(id: string, settings: any) {\n return this.http.put(`${this.urlSiteSettings}/${id}`, settings)\n .pipe(map(v => v.data));\n }\n\n public uploadFiles(files: File[]) {\n const formData: FormData = new FormData();\n files.forEach(file => {\n formData.append('files', file);\n });\n return this.http.post(`${this.urlSite}/assets`, formData);\n }\n\n public uploadSite(id: string, file: File) {\n const formData: FormData = new FormData();\n formData.append('file', file);\n return this.http.post(`${this.urlSite}/upload-site/${id}`, formData)\n .pipe(map(v => v.data));\n }\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { RightSidebarOptions, SiteForm, SiteSettingsOptions } from './create-site.inteface';\nimport { BaseService } from '../../../../../services/base.service';\nimport { forkJoin, Subject, takeUntil } from 'rxjs';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages, SUCCESS_RESPONSE } from '../../../../../enum/constants';\nimport { SiteService } from '../../../../../services/site.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { IError } from '../../../../../interfaces';\n\n@Component({\n selector: 'app-create-site',\n templateUrl: './create-site.component.html',\n styleUrls: ['./create-site.component.scss'],\n})\nexport class CreateSiteComponent implements OnInit, OnDestroy {\n\n private readonly destroy$: Subject = new Subject();\n public siteForm!: FormGroup;\n public isUpdate = false;\n private data!: RightSidebarOptions;\n private settings!: SiteSettingsOptions;\n private site!: any;\n public isLoader: boolean = false;\n\n constructor(\n public readonly rightSidebarService: RightSidebarService,\n private readonly siteService: SiteService,\n public readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) {\n this.data = this.rightSidebarService.getOptions().data;\n this.isUpdate = this.data?.is_update || false;\n }\n\n public ngOnInit() {\n if(this.isUpdate){\n this.isLoader = true;\n this.initSiteSettings();\n } else {\n this.initForm();\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n this.siteForm = new FormGroup({\n name: new FormControl(this.isUpdate ? this.site?.name : '', Validators.required),\n subdomain: new FormControl(this.isUpdate ? this.settings?.subdomain : '', [Validators.required, Validators.minLength(3)]),\n product: new FormControl(this.isUpdate ? this.site?.x_data.product : ''),\n price: new FormControl(this.isUpdate ? this.site?.x_data.price : ''),\n is_product: new FormControl(this.isUpdate ? this.site?.x_data.is_product : false),\n });\n }\n\n private initSiteSettings() {\n this.siteService.loadSettings(this.data.id).subscribe((settings : SiteSettingsOptions) => {\n this.settings = settings;\n this.initSite();\n })\n }\n\n private initSite() {\n this.siteService.getSite(this.data.id).subscribe((site: any) => {\n this.site = site;\n this.initForm();\n this.isLoader = false;\n });\n }\n\n public onSubmit(): void {\n if (this.siteForm.invalid) {\n this.siteForm.markAllAsTouched();\n return;\n }\n\n this.isUpdate ? this.onUpdate() : this.onCreate();\n }\n\n public onCreate(): void {\n const form = this.siteForm.getRawValue();\n const payload = {\n ...form,\n x_data: {\n product: form.product,\n price: form.price,\n is_product: form.is_product,\n }\n };\n this.siteService.createSite(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_created);\n this.rightSidebarService.close('close');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n this.rightSidebarService.close('close');\n });\n },\n });\n }\n\n public onUpdate(): void {\n const site = {\n name: this.siteForm.controls.name.getRawValue(),\n x_data: {\n product: this.siteForm.controls.product.getRawValue(),\n price: this.siteForm.controls.price.getRawValue(),\n is_product: this.siteForm.controls.is_product.getRawValue(),\n },\n };\n const siteSettings = {\n subdomain: this.siteForm.controls.subdomain.getRawValue(),\n };\n forkJoin([\n this.siteService.updateSite(this.data.id, site),\n this.siteService.saveSettings(this.data.id, siteSettings),\n ])\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(\n languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success,\n languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated,\n );\n this.rightSidebarService.close('close');\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(\n languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error,\n ERROR_MESSAGES[err.code] || DEFAULT_ERROR,\n );\n });\n this.rightSidebarService.close('close');\n },\n });\n }\n\n onCheckboxChange(isChecked: boolean): void {\n if(isChecked) {\n this.siteForm.controls.is_product.setValue(true);\n this.siteForm.controls.product.setValidators([Validators.required]);\n this.siteForm.controls.price.setValidators([Validators.required]);\n } else {\n this.siteForm.controls.is_product.setValue(false);\n this.siteForm.controls.product.clearValidators();\n this.siteForm.controls.price.clearValidators();\n }\n this.siteForm.updateValueAndValidity();\n }\n\n public onNoClick(): void {\n this.rightSidebarService.close('close');\n }\n}\n","
\n
\n {{ isUpdate ? ('dialogs.update_site' | translate) : ('dialogs.add_site' | translate) }}\n \n close\n \n
\n\n \n\n \n
\n \n
\n
\n\n \n
\n \n \n {{ 'name_site_cannot_be_empty' | translate }}.\n \n\n \n \n \n {{ 'subdomain_site_cannot_be_empty' | translate }}.\n \n \n {{ 'subdomain_min_length' | translate }}\n \n \n\n {{ 'sell_product' | translate }}\n\n \n \n \n {{ 'field_cannot_be_empty' | translate }}.\n \n \n\n \n \n \n {{ 'field_cannot_be_empty' | translate }}.\n \n \n\n \n\n
\n {{ 'cancel' | translate }}\n \n
\n {{ 'save' | translate }}\n {{ 'change' | translate }}\n
\n
\n
\n
\n","import { Injectable } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\nimport { HttpClient } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\nimport {paymentBody} from \"../modals/props/props.interface\";\n\n@Injectable({\n providedIn: 'root'\n})\nexport class PropsService {\n// URLs\n private propsUrl = environment.baseURL + '/payment/notification'\n private propsMarketUrl = environment.baseURL + '/application/notification'\n private propsOrderSiteUrl = environment.baseURL + '/site/notification'\n\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly http: HttpClient\n ) { }\n\n notifyAdmin(body: paymentBody): Observable {\n return this.http.post(this.propsUrl, {...body});\n }\n\n notifyOrderSite(body: any): Observable {\n return this.http.post(this.propsOrderSiteUrl, {...body});\n }\n\n notifyMarketAdmin(body: any): Observable {\n return this.http.post(this.propsMarketUrl, {...body});\n }\n\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { SiteForm } from './create-order-site.interface';\nimport { BaseService } from '../../../../../services/base.service';\nimport { Subject } from 'rxjs';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { IError } from '../../../../../interfaces';\nimport { languages } from '../../../../../enum/constants';\nimport { PropsService } from '../../../../../services/props.service';\n\n@Component({\n selector: 'app-create-order-site',\n templateUrl: './create-order-site.component.html',\n styleUrls: ['./create-order-site.component.scss'],\n})\nexport class CreateOrderSiteComponent implements OnInit, OnDestroy {\n\n private readonly destroy$: Subject = new Subject();\n public siteForm!: FormGroup;\n\n constructor(\n public readonly rightSidebarService: RightSidebarService,\n public readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n private propsService: PropsService,\n ) {\n }\n\n public ngOnInit() {\n this.initForm();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n this.siteForm = new FormGroup({\n name: new FormControl( '', Validators.required),\n phone: new FormControl('', Validators.required),\n description: new FormControl('', Validators.required),\n });\n }\n\n public onSubmit(): void {\n this.siteForm.markAllAsTouched();\n if (this.siteForm.valid) {\n const payload: SiteForm = this.siteForm.getRawValue();\n\n this.propsService.notifyOrderSite(payload)\n .subscribe(\n () => {\n this.notificationService.showInfo(languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.thanks_for_order, '');\n this.rightSidebarService.close('close');\n },\n (error) => {\n console.error('Ошибка при отправке запроса:', error);\n }\n );\n }\n }\n\n public onNoClick(): void {\n this.rightSidebarService.close('close');\n }\n}\n","
\n
\n Замовити сайт\n \n close\n \n
\n\n \n\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n\n
\n

{{ 'description' | translate }}*

\n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n {{ siteForm.controls.description.value?.length || 0 }}/9000\n
\n
\n\n \n\n
\n {{ 'cancel' | translate }}\n \n
\n {{ 'order' | translate }}\n
\n
\n
\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, tap } from 'rxjs';\nimport { IParams } from '../model/http';\n\nexport const initUsersParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport interface IUsersApiResponse {\n items?: any[];\n total?: number;\n size?: number;\n page?: number;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class TelegramService {\n constructor(\n private readonly http: HttpClient,\n ) { }\n private readonly usersSubject$ = new BehaviorSubject({});\n public readonly users$ = this.usersSubject$.asObservable();\n\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryUsersParams = { ...initUsersParams };\n\n\n private getAll(params: any) {\n return this.http.get(environment.baseURL + '/tg/users/', { params })\n .pipe(map(v => v.data))\n }\n\n loadUsers() {\n this.isLoader$.next(true);\n return this.getAll(this.queryUsersParams)\n .pipe(tap((users) => {\n this.usersSubject$.next(users);\n this.isLoader$.next(false);\n }));\n }\n\n setQueryUsersParams(params: IParams) {\n const { sort, order, search, take, page } = params;\n this.queryUsersParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryUsersParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryUsersParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryUsersParams.search,\n take: take || this.queryUsersParams.take,\n page: page || this.queryUsersParams.page,\n };\n return this.loadUsers();\n }\n\n\n addPhone(phone: string) {\n return this.http.post(environment.baseURL + '/tg/user/', { phone });\n }\n\n removePhone(id: string) {\n return this.http.delete(environment.baseURL + `/tg/user/${id}`);\n }\n}\n","import { Component } from '@angular/core';\nimport { AppTableSource, ColumnSortType, TableColumnTypes } from '../../../../../model/table.model';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { initUsersParams, TelegramService } from '../../../../../services/telegram.service';\nimport { IParams } from '../../../../../model/http';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { environment } from '../../../../../../environments/environment';\nimport { IError } from '../../../../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES } from '../../../../../enum/constants';\nimport { DialogService } from \"../../../../../services/dialog.service\";\n\nexport const tableHeaders = [\n // {\n // value: 'primary',\n // sort: ColumnSortType.NONE,\n // name: '',\n // type: TableColumnTypes.CHECKBOX,\n // className: 'table-checkbox'\n // },\n {\n value: 'phone_number',\n sort: ColumnSortType.NONE,\n name: 'phone',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'tg_id',\n sort: ColumnSortType.NONE,\n name: 'approved',\n type: TableColumnTypes.BOOLEAN,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'fullname',\n sort: ColumnSortType.NONE,\n name: 'responsible',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n // {\n // value: 'notify_payment',\n // sort: ColumnSortType.NONE,\n // name: 'Оплата',\n // type: TableColumnTypes.TOGGLE,\n // className: 'table-header',\n // },\n // {\n // value: 'notify_olx',\n // sort: ColumnSortType.NONE,\n // name: 'Olx',\n // type: TableColumnTypes.BOOLEAN,\n // className: 'table-header',\n // },\n // {\n // value: 'notify_logistic',\n // sort: ColumnSortType.NONE,\n // name: 'Логістика',\n // type: TableColumnTypes.TOGGLE,\n // className: 'table-header',\n // },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n\n@Component({\n selector: 'app-integration-telegram',\n templateUrl: './integration-telegram.component.html',\n styleUrls: ['./integration-telegram.component.scss']\n})\nexport class IntegrationTelegramComponent {\n private readonly destroy$: Subject = new Subject();\n\n dataSource$: Observable = this.telegramService.users$\n .pipe(map((data) => {\n return {\n headers: tableHeaders,\n rows: (data.items || []).map((v: any) => ({ ...v, fullname: [v.first_name, v.last_name].join(' ') })) as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n }\n }))\n visibleColumns = tableHeaders.map(v => v.value);\n\n private usersRequest: any;\n public phoneInput!: string;\n\n\n constructor(\n private readonly telegramService: TelegramService,\n private readonly notificationService: NotificationService,\n private readonly dialogService: DialogService,\n ) {\n }\n\n getUsersWithUpdatedParams(params: IParams) {\n if (this.usersRequest) {\n this.usersRequest.unsubscribe();\n }\n this.usersRequest = this.telegramService\n .setQueryUsersParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe();\n }\n\n ngOnInit() {\n this.telegramService.loadUsers()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n\n onSortChange(e: any) {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n sort: e.active,\n order: e.direction,\n });\n }\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getUsersWithUpdatedParams({ page, take, });\n }\n onActionChange(event: any) {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.integration-telegram`,\n });\n dialogRef.subscribe(result => {\n if (result) {\n this.telegramService.removePhone(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n });\n },\n })\n }\n })\n }\n\n addPhone() {\n this.telegramService.addPhone(this.phoneInput)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n });\n this.phoneInput = '';\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'MAX_INTEGRATION') {\n this.notificationService.showError('', ERROR_MESSAGES[err.code].replace('{0}', 'Telegram'));\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n }\n })\n }\n });\n }\n\n protected readonly environment = environment;\n}\n","
\n \n \n \n
\n
\n
\"Telegram
\n
\n
{{'add_a_phone_number'| translate}}
\n

{{'you_can_delete_at_any_time' | translate}}

\n
\n \n {{'add' | translate}}\n
\n
\n
\n
\n
\n \n
{{'connection_instructions' | translate}}:
\n
\n
1. {{'add_users_who_will_have_access_to_Telegram' | translate}}.
\n
2. {{'go_to_the_Telegram_bot_and_register'| translate}}.
\n
{{'the_bot_is_available_at_this_link' | translate}}: @{{environment.telegram.name}}
\n
\n
\n \n
{{'account_settings' | translate}}
\n \n
\n
\n \n
\n
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, Observable, tap } from 'rxjs';\nimport { IParams } from '../model/http';\nimport { initItemsParams } from './channel.service';\n\n\nexport let invoiceTake = localStorage?.getItem('invoiceTake');\n\nexport const initListsParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport const initItemsInvoiceParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport interface IListsApiResponse {\n items?: any[];\n total?: number;\n size?: number;\n page?: number;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class NovaPoshtaService {\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n private readonly listsSubject$ = new BehaviorSubject({});\n public readonly lists$ = this.listsSubject$.asObservable();\n\n private readonly itemsInvoicesSubject$ = new BehaviorSubject({});\n public readonly itemsInvoices$ = this.itemsInvoicesSubject$.asObservable();\n\n private readonly itemsMyInvoicesSubject$ = new BehaviorSubject({});\n public readonly itemsMyInvoices$ = this.itemsMyInvoicesSubject$.asObservable();\n\n private itemsMyInvoices: any;\n private pageSize!: number;\n\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryListsParams = {...initListsParams};\n public queryListInvoicesParams = {...initItemsInvoiceParams};\n\n\n // List Integration Nova Poshta\n private getAll(params: any) {\n return this.http.get(environment.baseURL + '/np/all', {params})\n .pipe(map(v => v.data));\n }\n\n loadLists() {\n this.isLoader$.next(true);\n return this.getAll(this.queryListsParams)\n .pipe(tap((data) => {\n this.listsSubject$.next(data);\n this.isLoader$.next(false);\n }));\n }\n\n setQueryListsParams(params: IParams) {\n const {sort, order, search, take, page} = params;\n this.queryListsParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryListsParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryListsParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryListsParams.search,\n take: take || this.queryListsParams.take,\n page: page || this.queryListsParams.page,\n };\n return this.loadLists();\n }\n\n // List Invoices Nova Poshta\n\n private getInvoicesAll(params: any) {\n return this.http.get(environment.baseURL + '/np/invoices/all', {params})\n .pipe(map(v => v.data));\n }\n\n private getAllMyInvoices(params: any) {\n return this.http.post(environment.nova_poshta, {...params})\n .pipe(map(v => v));\n }\n\n loadListInvoices() {\n this.isLoader$.next(true);\n return this.getInvoicesAll(this.queryListInvoicesParams)\n .pipe(tap((data) => {\n this.itemsInvoicesSubject$.next(data);\n this.isLoader$.next(false);\n }));\n }\n\n loadListMyInvoices(api_key: string) {\n this.isLoader$.next(true);\n return this.getAllMyInvoices({\n apiKey: api_key,\n modelName: 'InternetDocument',\n calledMethod: 'getDocumentList',\n methodProperties: {\n DateTimeFrom: '01.01.2000',\n DateTimeTo: '01.01.2024',\n Page: '1',\n GetFullList: '1',\n },\n })\n .pipe(tap((v) => {\n this.itemsMyInvoices = {...v};\n this.itemsMyInvoicesSubject$.next({\n ...v,\n data: v.data.slice(0, initItemsInvoiceParams.take || 25),\n totalCount: v.data.length,\n pageSize: initItemsInvoiceParams.take || 0,\n pageIndex: initItemsInvoiceParams.page || 0,\n });\n this.isLoader$.next(false);\n }));\n }\n\n changeMyInvoicesSort(sortState: {\n active: string;\n direction: string;\n }) {\n const {active, direction} = sortState;\n\n const sortFunction = (a: any, b: any) => {\n const [valueA, valueB] = [a[active], b[active]];\n return valueA.localeCompare(valueB) * (direction === 'asc' ? 1 : -1);\n };\n\n const sortedData = [...this.itemsMyInvoices.data].sort(sortFunction);\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.pageSize ? sortedData.slice(0, this.pageSize) : sortedData.slice(0, 25),\n totalCount: sortedData.length,\n });\n }\n\n changeMyInvoicesPagination(paginationDetails: {\n previousPageIndex: number;\n pageIndex: number;\n pageSize: number;\n length: number;\n }) {\n this.pageSize = paginationDetails.pageSize;\n\n const start = Math.max(paginationDetails.pageIndex, 0) * paginationDetails.pageSize;\n const end = start + paginationDetails.pageSize;\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.itemsMyInvoices.data.slice(start, end - 1),\n totalCount: this.itemsMyInvoices.data.length,\n });\n }\n\n searchMyInvoices(searchedValue: string) {\n const filteredData = this.itemsMyInvoices.data.filter((item: any) => {\n return [item.Description, item.IntDocNumber, item.StateName, item.RecipientContactPerson, item.RecipientsPhone, item.AdditionalInformation].some(v => v.includes(searchedValue));\n });\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.pageSize ? filteredData.slice(0, this.pageSize) : filteredData.slice(0, 25),\n totalCount: filteredData.length,\n });\n }\n\n setQueryListInvoicesParams(params: IParams) {\n const {sort, order, search, take, page} = params;\n this.queryListInvoicesParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryListInvoicesParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryListInvoicesParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryListInvoicesParams.search,\n take: take || this.queryListInvoicesParams.take,\n page: page || this.queryListInvoicesParams.page,\n };\n return this.loadListInvoices();\n }\n\n\n addApiKey(payload: { title: string, api_key: string }) {\n return this.http.post(environment.baseURL + '/np/', payload);\n }\n\n removeApiKey(uuid: string) {\n return this.http.delete(environment.baseURL + `/np/${uuid}`);\n }\n\n checkApiKey(api_key: string) {\n return this.http.post(environment.nova_poshta, {\n apiKey: api_key,\n calledMethod: 'getCounterparties',\n methodProperties: {CounterpartyProperty: 'Sender'},\n modelName: 'Counterparty',\n });\n }\n\n getCabinets(search?: string) {\n const params: { search?: string } = {};\n if (search) {\n params.search = search.trim();\n }\n return this.http.get(environment.baseURL + '/np/cabinets', {params})\n .pipe(map(v => v.data));\n }\n\n updateInvoiceTake() {\n invoiceTake = localStorage?.getItem('invoiceTake');\n initItemsParams.take = (invoiceTake && +invoiceTake) || 25;\n }\n\n createInvoice(payload: any) {\n return this.http.post(environment.baseURL + '/np/invoices', payload);\n }\n\n updateInvoice(id: string, payload: any) {\n return this.http.put(environment.baseURL + `/np/invoices/${id}`, payload);\n }\n\n deleteInvoice(uuid: string) {\n return this.http.delete(environment.baseURL + `/np/invoices/${uuid}`);\n }\n\n deleteBulk(ids: string[]): Observable {\n const payload = {\n ids,\n }\n return this.http.patch<{ data: string[] }>(environment.baseURL + `/np/invoices/bulk/delete`, payload).pipe(\n map((res) => res.data),\n );\n }\n\n postCall(params: any) {\n return this.http.post(environment.nova_poshta, {...params})\n .pipe(map(v => v));\n }\n\n getTTNDetails(payload: any) {\n return this.http.post(environment.baseURL + '/np/ttn', payload);\n }\n}\n","import { Component } from '@angular/core';\nimport { AppTableSource, ColumnSortType, TableColumnTypes } from '../../../../../model/table.model';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { initUsersParams } from '../../../../../services/telegram.service';\nimport { IParams } from '../../../../../model/http';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { environment } from '../../../../../../environments/environment';\nimport { IError } from '../../../../../interfaces';\nimport { ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { NovaPoshtaService } from '../../../../../services/nova-poshta.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Router } from '@angular/router';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\n\nexport const tableHeaders = [\n // {\n // value: 'primary',\n // sort: ColumnSortType.NONE,\n // name: '',\n // type: TableColumnTypes.CHECKBOX,\n // className: 'table-checkbox'\n // },\n {\n value: 'title',\n sort: ColumnSortType.NONE,\n name: 'Назва',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'api_key',\n sort: ColumnSortType.NONE,\n name: 'API KEY',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n\ninterface IFormNovaPoshta {\n title: FormControl,\n api_key: FormControl,\n}\n\n@Component({\n selector: 'app-integration-novaposhta',\n templateUrl: './integration-novaposhta.component.html',\n styleUrls: ['./integration-novaposhta.component.scss']\n})\nexport class IntegrationNovaposhtaComponent {\n private readonly destroy$: Subject = new Subject();\n\n dataSource$: Observable = this.newDeliveryService.lists$\n .pipe(map((data) => {\n this.integrationTotal = data.total || 0;\n return {\n headers: tableHeaders,\n rows: data.items as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n }\n }))\n visibleColumns = tableHeaders.map(v => v.value);\n\n integrationTotal!: number;\n\n private usersRequest: any;\n public phoneInput!: string;\n\n public form!: FormGroup\n\n\n constructor(\n private readonly newDeliveryService: NovaPoshtaService,\n private readonly notificationService: NotificationService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly router: Router,\n ) {\n }\n\n getUsersWithUpdatedParams(params: IParams) {\n if (this.usersRequest) {\n this.usersRequest.unsubscribe();\n }\n this.usersRequest = this.newDeliveryService\n .setQueryListsParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe();\n }\n\n ngOnInit() {\n this.initForm();\n this.newDeliveryService.loadLists()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n\n onSortChange(e: any) {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n sort: e.active,\n order: e.direction,\n });\n }\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getUsersWithUpdatedParams({ page, take, });\n }\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n this.newDeliveryService.removeApiKey(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n });\n },\n })\n }\n }\n\n onRowClick(event: any) {\n this.rightSidebarService.close();\n this.router.navigate(['/crm/nova-poshta'], { queryParams: { cabinet: event.id } });\n }\n\n submit() {\n if (this.form.valid) {\n const payload = this.form.getRawValue();\n this.newDeliveryService.checkApiKey(payload.api_key)\n .subscribe({\n next: () => {\n this.newDeliveryService.addApiKey(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getUsersWithUpdatedParams({\n ...initUsersParams,\n });\n this.phoneInput = '';\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'MAX_INTEGRATION') {\n this.notificationService.showError('', ERROR_MESSAGES[err.code].replace('{0}', languages[localStorage.getItem('language') || 'ua'].nova_poshta));\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n }\n })\n }\n });\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.API_key_invalid)\n },\n })\n\n }\n }\n\n private initForm() {\n this.form = new FormGroup({\n title: new FormControl(`Кабінет №1`, Validators.required),\n api_key: new FormControl('', Validators.required),\n })\n }\n\n protected readonly environment = environment;\n}\n","
\n \n \n \n
\n
\n
\"Nova
\n
\n
{{'add_API_Key' | translate}}
\n

{{'you_can_delete_at_any_time' | translate}}

\n
\n \n \n {{'add' | translate}}\n
\n
\n
\n
\n
\n \n
{{'connection_instructions_a_api_key'}}:
\n
\n
1. {{'go_to_the_settings_and_select_the_section' | translate}} \"{{'security' | translate}}\".
\n
2. {{'click_the_button'| translate}} \"{{ 'create_key' | translate }}\".
\n
3. {{'you_will_be_provided_with_a_new_API_key' | translate}}. {{'enter_it_in_the_field_above_and_click_add' | translate }}.
\n
{{'the_site_is_available_at_this_link' | translate}}: {{'nova_poshta'| translate}}
\n
\n
\n \n
{{'API_key_settings' | translate}}
\n \n
\n
\n \n
\n
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-integration-checkbox',\n templateUrl: './integration-checkbox.component.html',\n styleUrls: ['./integration-checkbox.component.scss']\n})\nexport class IntegrationCheckboxComponent {\n\n}\n","
\n \n \n \n
\n \"Checkbox\n

\n {{ 'integration' | translate }}:\n встановлена\n ({{'exit'| translate}})\n

\n
\n\n

1) Логін касира:

\n
\n \n

Пароль касира:

\n \n

або
Пін-код касира

\n \n
\n\n

2) Ключ ліцензії каси:

\n
\n \n {{ 'save' | translate }}\n
\n\n
\n \n {{'integration_setup_is_very_simple_and_fast' | translate }}.\n {{'to_connect_your_cash_register' | translate}}.\n \n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { AuthService } from '../../../../../services/auth.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { IntegrationService } from '../../../../../services/integration.service';\nimport { Router } from '@angular/router';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { environment } from '../../../../../../environments/environment';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { WebSocketService } from '../../../../../services/gateway.service';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { ChannelService } from '../../../../../services/channel.service';\nimport { NAME_PROJECT } from '../../../../../enum/constants';\nimport { Subject } from 'rxjs';\nimport { CrmService } from '../../../../../services/crm.service';\n\ninterface IFormRia {\n api_key: FormControl;\n user_id: FormControl;\n}\n\n@Component({\n selector: 'app-integration-ria',\n templateUrl: './integration-ria.component.html',\n styleUrls: ['./integration-ria.component.scss'],\n})\nexport class IntegrationRiaComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n protected readonly webhookUrl = `${environment.baseURL}/webhooks/ria`;\n constructor(\n public readonly authService: AuthService,\n public readonly baseService: BaseService,\n public readonly integrationService: IntegrationService,\n public readonly channelService: ChannelService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly webSocketService: WebSocketService,\n private readonly dialogService: DialogService,\n private readonly router: Router,\n private readonly dialog: MatDialog,\n private readonly modalService: ModalService,\n public readonly crmService: CrmService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channel = options.data.channel;\n }\n\n protected readonly NAME_PROJECT = NAME_PROJECT;\n protected readonly environment = environment;\n\n channel_id!: string;\n account!: any;\n integration!: any;\n status!: string;\n\n error!: string;\n\n form!: FormGroup;\n channels: any[] = [];\n channel: any;\n\n public isLoader: boolean = false;\n\n public statuses: any = {\n 'ACTIVE': { title: 'active_status', color: 'green', translate: true },\n 'EXPIRED': { title: 'expired_status', color: 'orange', translate: true },\n 'CLOSED': { title: 'closed_status', color: 'gray', translate: true },\n 'PAUSE': { title: 'pause_status', color: 'orange', translate: true },\n };\n\n\n ngOnInit() {\n this.onInitForm();\n }\n\n ngOnDestroy(): void {\n\n }\n\n onInitForm() {\n this.form = new FormGroup({\n api_key: new FormControl(this.channel.x_data?.api_key || null, Validators.required),\n user_id: new FormControl(this.channel.x_data?.user_id || null, Validators.required),\n });\n }\n\n save() {\n if (this.form.dirty && this.form.valid) {\n const riaConfig = this.form.getRawValue();\n\n this.channelService.update(this.channel.id, { x_data: riaConfig })\n .subscribe((res) => {\n this.cancel('success');\n });\n } else {\n this.cancel();\n }\n }\n\n cancel(event?: string) {\n this.rightSidebarService.close(event);\n }\n}\n","
\n \n \n \n
\n
\n
\"RIA
\n
\n
{{ 'log_in_with_your_RIA_account' | translate }}
\n

{{ 'channel' | translate }}: {{ channel.name }}

\n

{{ 'status'| translate }}:\n \n {{ statuses[channel.status]?.translate && statuses[channel.status]?.title ? (statuses[channel.status]?.title | translate) : statuses[channel.status]?.title }}\n \n

\n
\n
\n
\n
\n \n
{{ 'information'| translate }}
\n \n
\n {{ 'api_key' | translate }}\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n
\n {{ 'user_id' | translate }}\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n
\n {{ 'pass_this_indicator_related' | translate }}\n \n
\n
\n {{ 'pass_this_indicator_webhook' | translate }}\n \n
\n
\n
\n {{ 'cancel' | translate }}\n \n {{ 'save' | translate }}\n
\n
\n
\n
\n","import { Component, HostListener, Inject, OnDestroy, OnInit } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { BaseService } from '../../services/base.service';\nimport { Subject, Subscription } from 'rxjs';\nimport { FileSystemService } from '../../services/file-system.service';\nimport { CHANNEL_TYPE, languages, UPLOAD_FILE_ICONS } from '../../enum/constants';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ChatService } from '../../services/chat.service';\n\n@Component({\n selector: 'app-file-upload-new',\n templateUrl: './file-upload-new.component.html',\n styleUrl: './file-upload-new.component.scss',\n})\nexport class FileUploadNewComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n uploadingFiles: { subscription: Subscription, data: any, response: any }[] = [];\n uploadedFiles: any[] = [];\n pastedFiles!: File[];\n selectedFileIndex: number | null = null;\n isFileView: boolean = false;\n public isLoader: boolean = false;\n file: any;\n chatFilesLength: any;\n accept: any;\n maxUploadFiles!: number;\n allowedTypes!: string;\n channelType: CHANNEL_TYPE;\n\n constructor(\n @Inject(MAT_DIALOG_DATA) public data: any,\n private dialogRef: MatDialogRef,\n protected readonly fileSystemService: FileSystemService,\n public readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n public chatService: ChatService,\n ) {\n this.pastedFiles = data?.data?.files;\n this.chatFilesLength = data?.data?.length;\n this.maxUploadFiles = data?.data?.max_upload_files;\n this.allowedTypes = data?.data?.allowed_types;\n this.channelType = data?.data?.channel_type;\n }\n\n ngOnInit(): void {\n if (this.pastedFiles?.length) {\n this.uploadFiles(this.pastedFiles);\n }\n }\n\n public ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onChooseFiles(event: Event) {\n const input = event.target as HTMLInputElement;\n const files = input.files;\n if (!files?.length)\n return;\n this.uploadFiles(files);\n }\n\n onDroppedFiles(event: Event) {\n this.uploadFiles(event);\n }\n\n uploadFiles(files: any) {\n let filesArray: File[] = Array.from(files);\n if (filesArray.length + this.uploadedFiles.length + this.chatFilesLength > this.maxUploadFiles) {\n filesArray.splice(this.maxUploadFiles - (filesArray.length + this.uploadedFiles.length + this.chatFilesLength));\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.no_more_upload + this.maxUploadFiles + languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.files);\n }\n\n if (filesArray) {\n filesArray = filesArray.filter((filesArray) => !this.allowedTypes || filesArray.type.match(this.allowedTypes));\n\n if (!filesArray.length) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.invalid_files_type);\n return;\n }\n }\n\n if (!filesArray.length) return;\n\n for (let i = 0; i < filesArray.length; i++) {\n const file: any = filesArray[i];\n\n const channelType = this.channelType ? this.channelType : CHANNEL_TYPE.UNKNOWN;\n\n const req = this.fileSystemService.uploadTempFile(file, channelType).subscribe({\n next: (res: any) => {\n const uploadingFile = this.uploadingFiles.find((item) => item.data === file);\n if (uploadingFile) {\n uploadingFile.response = res;\n if (res.state) {\n this.uploadedFiles.push(res.body.data);\n this.uploadingFiles = this.uploadingFiles.filter((item) => item.data !== file);\n }\n }\n },\n error: (e: any) => {\n console.error('Error during file upload:', e);\n },\n });\n this.uploadingFiles.push({ subscription: req, data: file, response: {} });\n }\n }\n\n deleteUploadingFile(i: number) {\n // if (this.selectedFileIndex === i) {\n // this.selectedFileIndex = null;\n // this.file = null;\n // this.isFileView = false;\n // }\n if (this.uploadingFiles[i]) {\n this.uploadingFiles[i].subscription.unsubscribe();\n this.uploadingFiles.splice(i, 1);\n }\n }\n\n deleteUploadedFile(i: number) {\n if (this.selectedFileIndex === i) {\n this.selectedFileIndex = null;\n this.file = null;\n this.isFileView = false;\n }\n this.fileSystemService.deleteTempFile(this.uploadedFiles[i].key).subscribe({\n next(res) {\n\n },\n });\n this.uploadedFiles.splice(i, 1);\n }\n\n formatFileSize(size: number): string {\n if (size < 1024) return size + ' B';\n else if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KB';\n else return (size / (1024 * 1024)).toFixed(1) + ' MB';\n }\n\n onCancel() {\n this.dialogRef.close();\n }\n\n onSend() {\n this.dialogRef.close();\n }\n\n onViewFile(file: any, index: number) {\n if (index === this.selectedFileIndex) {\n this.isFileView = false;\n this.isLoader = false;\n this.selectedFileIndex = null;\n this.file = null;\n } else {\n this.selectedFileIndex = index;\n this.file = file;\n this.isLoader = true;\n this.isFileView = true;\n }\n }\n\n fileLoaded() {\n this.isLoader = false;\n }\n\n onCloseModal() {\n this.dialogRef.close(this.uploadedFiles);\n }\n\n onCancelModal() {\n if (this.uploadingFiles.length) {\n this.uploadingFiles.forEach((item) => {\n item.subscription.unsubscribe();\n });\n this.uploadingFiles.splice(0, this.uploadingFiles.length);\n } else if (this.uploadedFiles.length) {\n this.uploadedFiles.forEach((item) => {\n this.fileSystemService.deleteTempFile(item.key).subscribe({\n next: (res) => {\n\n },\n });\n });\n this.uploadedFiles.splice(0, this.uploadedFiles.length);\n }\n this.dialogRef.close();\n }\n\n resetInput(event: any) {\n event.target.value = null;\n }\n\n @HostListener('window:paste', ['$event'])\n eventPaste(e: ClipboardEvent) {\n if (!e.clipboardData) return;\n\n const files = Array\n .from(e.clipboardData.items)\n .map((v: DataTransferItem) => v.getAsFile())\n .filter((v): v is File => !!v);\n\n if (files?.length) {\n this.uploadFiles(files);\n e.preventDefault();\n }\n }\n\n protected readonly UPLOAD_FILE_ICONS = UPLOAD_FILE_ICONS;\n}\n\n","
\n
\n
\n \n\n\n\n\n\n\n\n
\n \n {{ 'uploaded_files' | translate }}:\n
\n
\n
\n \n \n {{ file.name }}\n
\n \n delete\n \n
\n
\n
\n
\n
\n
\n \n
\n \n \n
\n
\n \n
\n {{ 'cancel' | translate }}\n 0\" (onChangeClick)=\"onCloseModal()\">{{ 'save' | translate }}\n
\n
\n\n\n\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, Subscription, takeUntil } from 'rxjs';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { CrmService } from '../../../../../services/crm.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { AttributesForm, UpdateForm } from './create-update-olx-advert.interface';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { OlxAdvertService } from '../../../../../services/olx-advert.service';\nimport {\n CHANNEL_TYPE,\n CURRENCY,\n DEFAULT_ERROR,\n ERROR_MESSAGES,\n languages,\n MODAL_TYPES,\n PRICE_TYPES,\n UPLOAD_FILE_TYPES,\n} from '../../../../../enum/constants';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { PageEvent } from '@angular/material/paginator';\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { IError } from '../../../../../interfaces';\nimport { PlannerComponent } from '../../../../../modals/planner/planner.component';\nimport { FileUploadNewComponent } from '../../../../../modals/file-upload-new/file-upload-new.component';\nimport moment from 'moment';\n\n@Component({\n selector: 'app-create-update-olx-advert',\n templateUrl: './create-update-olx-advert.component.html',\n styleUrl: './create-update-olx-advert.component.scss',\n})\nexport class CreateUpdateOlxAdvertComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n public updateForm!: FormGroup;\n public attributeForm!: FormGroup;\n public isFormSubmitted = false;\n public priceTypeControl: FormControl = new FormControl('', [Validators.required]);\n public isLoader: boolean = false;\n public isDistrict: boolean = false;\n public isChangeCity: boolean = false;\n public currentIndex = 0;\n public current_category_id: any = 0;\n public advert: any = {};\n public dataAttributes: any = [];\n public channel_id: string;\n public images: { url: string }[] = [];\n public categories: any[] = [];\n public cities: any[] = [];\n public cityPage: number = 1;\n public districtPage: number = 1;\n public cityTotal: number = 0;\n public districtTotal: number = 0;\n private citySubscription: Subscription;\n private districtSubscription: Subscription;\n public districts: any[] = [];\n public current_city: any;\n public current_district: any;\n public blocks: any;\n public searchedCity: string = '';\n public type_action: string;\n public photos_limit: number = 0;\n public calendar: any;\n public planned_at: any;\n public is_planner: boolean = false;\n public isDraft: any;\n public current_category: string = '';\n public choosed_city: any = {};\n\n constructor(\n public crmService: CrmService,\n public baseService: BaseService,\n public dialogService: DialogService,\n public notificationService: NotificationService,\n public rightSidebarService: RightSidebarService,\n public olxAdvertService: OlxAdvertService,\n public readonly modalService: ModalService,\n ) {\n const options = this.rightSidebarService.getData();\n this.advert = options.advert;\n this.channel_id = options.channel_id;\n this.type_action = options.type_action;\n this.is_planner = options.is_planner;\n this.images = this.advert?.images;\n this.citySubscription = Subscription.EMPTY;\n this.districtSubscription = Subscription.EMPTY;\n }\n\n public ngOnInit(): void {\n if (this.advert) {\n this.getCategoryAttributes();\n this.isLoader = true;\n this.getCategoryById(this.advert.category_id);\n const price = this.advert.price;\n if (price?.value) {\n this.priceTypeControl.setValue('price');\n } else if (price?.value === 0) {\n this.priceTypeControl.setValue('free');\n } else if (price?.trade) {\n this.priceTypeControl.setValue('trade');\n }\n this.getCategory();\n this.getCategoryAdvert();\n this.getCity();\n this.initForm();\n } else {\n this.images = [];\n this.getCategoryAdvert();\n this.getCity();\n this.initForm();\n }\n }\n\n getCategoryById(category_id: number) {\n this.olxAdvertService.getCategoryById(category_id)\n .subscribe((res) => {\n if (res) {\n this.current_category = res.name;\n }\n });\n }\n\n getCategory() {\n this.olxAdvertService.getCategoryPhoto(this.advert.category_id)\n .subscribe((res) => {\n if (res) {\n this.photos_limit = res.photos_limit;\n this.setEmptyBlocks();\n }\n });\n }\n\n getCategoryAdvert() {\n this.olxAdvertService.getCategories(this.current_category_id)\n .subscribe({\n next: (res) => {\n if (res) this.categories.push(res);\n },\n });\n }\n\n selectCategory(category: any, i: any) {\n\n if (i !== this.categories.length - 1) {\n this.categories.splice(i + 1);\n this.current_category_id = category.category_id;\n }\n\n this.current_category_id = category.category_id;\n\n if (!category.is_leaf) {\n this.updateForm.controls.category_id.setValue(null);\n this.getCategoryAdvert();\n } else {\n this.olxAdvertService.getCategoryPhoto(this.current_category_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe((res) => {\n if (res) {\n this.photos_limit = res.photos_limit;\n this.setEmptyBlocks();\n this.getCategoryAttributesAtCreate();\n }\n });\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public initForm() {\n this.getCity();\n\n const attributes: any = {};\n if (this.dataAttributes) {\n this.dataAttributes.forEach((att: any) => {\n const currentValue: any = this.advert.attributes.find((v: any) => v.code === att.code);\n const validates: any[] = [];\n if (att.validation.required) validates.push(Validators.required);\n if (att.validation.min !== 0) validates.push(Validators.min(att.validation.min));\n if (att.validation.max) validates.push(Validators.max(att.validation.max));\n if (att.values.length && att.validation.allow_multiple_values) {\n attributes[att.code] = new FormControl(currentValue?.values || [], validates);\n } else {\n attributes[att.code] = new FormControl(currentValue?.value || currentValue?.values, validates);\n }\n });\n }\n this.updateForm = new FormGroup({\n title: new FormControl(this.advert?.title ?? null, [Validators.minLength(16), Validators.maxLength(70), Validators.required]),\n description: new FormControl(this.advert?.description ?? null, [Validators.minLength(40), Validators.maxLength(9000), Validators.required]),\n category_id: new FormControl(this.advert ? this.advert.category_id : null, [Validators.required]),\n price: new FormGroup({\n value: new FormControl(this.advert?.price.value ?? null),\n currency: new FormControl(this.advert?.price.currency ?? 'UAH'),\n trade: new FormControl(this.advert?.price.trade ?? null),\n negotiable: new FormControl(this.advert?.price.negotiable ?? null),\n }),\n advertiser_type: new FormControl(this.advert?.type ?? 'private'),\n contact: new FormGroup({\n name: new FormControl(this.advert ? this.advert.contact?.name : null),\n phone: new FormControl(this.advert ? this.advert.contact?.phone : null),\n }),\n location: new FormGroup({\n city_id: new FormControl(this.advert ? (this.advert.location.city_id ?? null) : (this.current_city ?? null), [Validators.required]),\n district_id: new FormControl(this.advert ? (this.advert.location.district_id ?? null) : (this.current_district ?? null)),\n latitude: new FormControl(null),\n longitude: new FormControl(null),\n }),\n });\n\n if (this.advert) {\n this.attributeForm = new FormGroup({\n ...attributes,\n });\n }\n }\n\n\n initializeAttributes(): void {\n const attributes: any = {};\n if (this.dataAttributes) {\n this.dataAttributes.forEach((att: any) => {\n const validates: any[] = [];\n if (att.validation.required) validates.push(Validators.required);\n if (att.validation.min !== 0) validates.push(Validators.min(att.validation.min));\n if (att.validation.max) validates.push(Validators.max(att.validation.max));\n if (att.values.length && att.validation.allow_multiple_values) {\n attributes[att.code] = new FormControl([], validates);\n } else {\n attributes[att.code] = new FormControl(null, validates);\n }\n });\n }\n\n\n this.attributeForm = new FormGroup({ ...attributes });\n\n\n // const attributesGroup = this.attributeForm.get('attributes') as FormGroup;\n // this.dataAttributes.forEach((attribute: any) => {\n // attributesGroup.addControl(attribute.code, new FormControl(null));\n // });\n }\n\n public onCloseSidebar(): void {\n this.rightSidebarService.close('close');\n }\n\n protected save(publishNow: boolean = false) {\n this.isFormSubmitted = true;\n if (this.updateForm.valid) {\n const formValue = this.updateForm.getRawValue();\n\n switch (this.priceTypeControl.value) {\n case 'free':\n formValue.price.value = 0;\n formValue.price.trade = false;\n formValue.price.negotiable = false;\n break;\n case 'trade':\n formValue.price.value = null;\n formValue.price.trade = true;\n formValue.price.negotiable = false;\n break;\n case 'price':\n formValue.price.trade = false;\n break;\n }\n\n const attributeValues = this.attributeForm.getRawValue();\n const attributesArray = Object.keys(attributeValues).map((key) => {\n const attribute = attributeValues[key];\n const data: any = {\n code: key,\n };\n\n if (Array.isArray(attribute)) {\n data.values = attribute;\n } else {\n data.value = attribute;\n }\n\n return data;\n });\n\n if (publishNow) {\n this.current_category_id = this.advert.category_id;\n this.createAdvert(formValue, attributesArray);\n return;\n }\n\n if (this.type_action === 'CREATE') {\n if (this.planned_at) this.createAdvertPlanner(formValue, attributesArray);\n else this.createAdvert(formValue, attributesArray);\n return;\n }\n\n if (this.type_action === 'UPDATE') {\n if (this.planned_at) this.updatePlannerAdvert(formValue, attributesArray);\n else this.updateAdvert(formValue, attributesArray);\n return;\n }\n\n } else {\n this.updateForm.markAllAsTouched();\n this.priceTypeControl.markAsTouched();\n }\n }\n\n createAdvert(formValue: any, attributesArray: any) {\n const payloadCreate = {\n images: this.images,\n ...formValue,\n location: {\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n attributes: attributesArray,\n category_id: this.current_category_id,\n };\n this.olxAdvertService.createAdvert(this.channel_id, payloadCreate)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n if (this.advert?.planned_at) {\n const payload = {\n id: this.advert?.id,\n status: 'CREATED',\n };\n this.olxAdvertService.updateStatusPacket(payload)\n .subscribe();\n }\n this.onCloseSidebar();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_created);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (this.advert?.planned_at) {\n const payload = {\n id: this.advert?.id,\n status: 'CANCELED',\n };\n this.olxAdvertService.updateStatusPacket(payload)\n .subscribe();\n }\n\n if (err.code === 'MAX_PRICE') {\n this.updateForm.controls.price.controls.value.setErrors({ max_price: true });\n return;\n }\n\n if (err.code === 'AD_CPESIFIED_CURRENCY') {\n this.updateForm.controls.price.controls.currency.setErrors({ ad_cpesified_currency: true });\n return;\n }\n\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n updateAdvert(formValue: any, attributesArray: any) {\n console.log(this.images);\n const payloadUpdate = {\n images: this.images,\n ...formValue,\n location: {\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n attributes: attributesArray,\n };\n this.olxAdvertService.updateAdvert(this.channel_id, this.advert.id, payloadUpdate)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.onCloseSidebar();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'MAX_PRICE') {\n this.updateForm.controls.price.controls.value.setErrors({ max_price: true });\n return;\n }\n if (err.code === 'AD_CPESIFIED_CURRENCY') {\n this.updateForm.controls.price.controls.currency.setErrors({ ad_cpesified_currency: true });\n return;\n }\n\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n updatePlannerAdvert(formValue: any, attributesArray: any) {\n const event = {\n id: this.advert.id,\n };\n const advert = {\n images: this.images,\n ...formValue,\n city: {\n ...this.choosed_city,\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n location: {\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n attributes: attributesArray,\n };\n const payload = {\n x_data: {\n channel_id: this.channel_id,\n advert: advert,\n },\n };\n\n this.olxAdvertService.updatePlanner2(payload, event)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.onCloseSidebar();\n },\n error: () => {\n this.onCloseSidebar();\n\n },\n });\n }\n\n createAdvertPlanner(formValue: any, attributesArray: any) {\n const advert = {\n images: this.images,\n ...formValue,\n attributes: attributesArray,\n category_id: this.current_category_id,\n city: {\n ...this.choosed_city,\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n location: {\n city_id: formValue.location.city_id,\n district_id: formValue.location.district_id?.district_id,\n longitude: formValue.location.city_id.longitude,\n latitude: formValue.location.city_id.latitude,\n },\n };\n const payloadPlanner = {\n type: 'CREATE_ADVERT',\n planned_at: this.planned_at,\n name: this.calendar.name,\n status: this.isDraft,\n x_data: {\n channel_id: this.channel_id,\n advert: advert,\n },\n };\n this.olxAdvertService.createPlanner(payloadPlanner)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.onCloseSidebar();\n },\n error: () => {\n this.onCloseSidebar();\n\n },\n });\n }\n\n cancel() {\n this.onCloseSidebar();\n }\n\n getCategoryAttributes() {\n this.olxAdvertService.getCategoryAttributes(this.advert.category_id, this.channel_id)\n .subscribe((res) => {\n this.dataAttributes = res;\n this.isLoader = false;\n this.initForm();\n });\n }\n\n getCategoryAttributesAtCreate() {\n this.olxAdvertService.getCategoryAttributes(this.current_category_id, this.channel_id)\n .subscribe((res) => {\n this.dataAttributes = res;\n this.initializeAttributes();\n this.isLoader = false;\n });\n }\n\n checkItem(attributeCode: string, valueCode: string) {\n const currentValues = this.attributeForm.controls[attributeCode].value || [];\n\n if (currentValues.includes(valueCode)) {\n this.attributeForm.controls[attributeCode].setValue(\n currentValues.filter((code: string) => code !== valueCode),\n );\n } else {\n this.attributeForm.controls[attributeCode].setValue([\n ...currentValues,\n valueCode,\n ]);\n }\n }\n\n // openFileUpload(directoryId?: string) {\n // const dialogData = {\n // title: 'choose_files',\n // translate: true,\n // component: FileUploadComponent,\n // width: '70vw',\n // directoryId: directoryId || null,\n // };\n // this.openModal(dialogData)\n // .subscribe((res) => {\n // res.map((id: any) => {\n // this.fileSystemService.getSignedUrl(id)\n // .subscribe((res) => {\n // if (!this.images) {\n // this.images = [];\n // }\n // this.images.push({ url: res.url });\n // this.blocks = this.blocks.slice(res.length);\n // this.setEmptyBlocks();\n // this.initForm();\n // });\n // });\n // });\n // }\n\n setCurrentIndex(event: any) {\n this.currentIndex = parseInt(event, 10) || 0;\n }\n\n onPageChange(event: PageEvent): void {\n this.currentIndex = event.pageIndex;\n this.setCurrentIndex(this.currentIndex);\n }\n\n setEmptyBlocks() {\n if (this.images) {\n const emptyCount = this.photos_limit - this.images.length;\n this.blocks = new Array(emptyCount > 0 ? emptyCount : 0);\n } else {\n this.blocks = new Array(this.photos_limit);\n }\n\n }\n\n\n drop(event: CdkDragDrop) {\n moveItemInArray(this.images, event.previousIndex, event.currentIndex);\n }\n\n deletePhoto(index: any) {\n this.images.splice(index, 1);\n console.log(this.images);\n this.setEmptyBlocks();\n }\n\n\n getDistrict(city_id: string, searchTerm: string = '') {\n const payload = {\n city_id: city_id,\n page: this.districtPage,\n take: 25,\n search: searchTerm,\n };\n if (this.districtSubscription) {\n this.districtSubscription.unsubscribe();\n }\n this.districtSubscription = this.olxAdvertService.getDistrics(payload)\n .subscribe({\n next: (res: any) => {\n if (res.items.length) {\n this.districts = [...this.districts, ...res.items];\n this.districtTotal = res.total;\n this.isDistrict = true;\n }\n // if (this.currentContact && !this.contacts.some(obj => _.isEqual(obj, this.currentContact)))\n // this.contacts.unshift(this.currentContact);\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n this.isDistrict = false;\n });\n },\n });\n }\n\n getCity(searchTerm: string = '') {\n const payload = {\n page: this.cityPage,\n take: 25,\n search: searchTerm,\n };\n if (this.citySubscription) {\n this.citySubscription.unsubscribe();\n }\n this.citySubscription = this.olxAdvertService.getCity(payload)\n .subscribe({\n next: (res: any) => {\n const items = [...res.items].map(v => {\n const fullname = [];\n if (v.name) fullname.push(v.name);\n if (v.municipality) fullname.push(v.municipality);\n return {\n ...v,\n fullname: fullname.join(', '),\n };\n });\n this.cities = [...this.cities, ...items];\n this.cityTotal = res.total;\n // if (this.currentContact && !this.contacts.some(obj => _.isEqual(obj, this.currentContact)))\n // this.contacts.unshift(this.currentContact);\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n scrollToEndCity() {\n if (this.cities.length !== this.cityTotal) {\n ++this.cityPage;\n this.getCity(this.searchedCity);\n }\n }\n\n searchCity(e: any) {\n this.resetCities();\n this.searchedCity = e.term;\n this.getCity(this.searchedCity);\n }\n\n searchDistrict(e: any) {\n this.resetDistricts();\n this.searchedCity = e.term;\n this.getDistrict(this.current_city, this.searchedCity);\n }\n\n private resetCities() {\n this.cityPage = 1;\n this.cityTotal = 0;\n this.cities = [];\n this.resetDistricts();\n }\n\n private resetDistricts() {\n this.districtPage = 1;\n this.districtTotal = 0;\n this.districts = [];\n }\n\n selectDistrict(e: any) {\n this.current_district = e.district_id;\n this.updateForm.controls.location.controls.latitude.setValue(e.latitude);\n this.updateForm.controls.location.controls.longitude.setValue(e.longitude);\n }\n\n selectCity(e: any) {\n if (this.type_action === 'UPDATE' && !this.isChangeCity) {\n this.getDistrict(e);\n } else {\n this.choosed_city = {\n name: e.name,\n municipality: e.municipality,\n };\n this.updateForm.controls.location.controls.district_id.reset();\n this.current_city = e.city_id;\n this.isDistrict = false;\n this.getDistrict(e.city_id);\n }\n\n if (this.isDistrict) {\n this.updateForm.controls.location.controls.district_id.setValidators(Validators.required);\n } else {\n this.updateForm.controls.location.controls.district_id.clearValidators();\n }\n this.updateForm.controls.location.controls.district_id.updateValueAndValidity();\n }\n\n scrollToEndDistrict() {\n if (this.districts.length !== this.districtTotal) {\n ++this.districtPage;\n this.getDistrict(this.current_city, this.searchedCity);\n }\n }\n\n openPlanner() {\n this.modalService.openModal({\n title: 'planner_create',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.CREATE_ADVERT,\n },\n }).subscribe((res) => {\n if (res) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n this.calendar = res;\n this.planned_at = dateTime;\n this.isDraft = res.x_data.status;\n }\n });\n }\n\n public cancelPlanner() {\n this.planned_at = null;\n }\n\n setChoice(event: any) {\n if (event.value === 'price') {\n this.updateForm.controls.price.get('value')?.setValidators(Validators.required);\n this.updateForm.controls.price.get('currency')?.setValidators(Validators.required);\n } else {\n const priceControl = this.updateForm.controls.price.get('value');\n priceControl?.setValue(priceControl?.value ?? 0);\n this.updateForm.controls.price.get('value')?.clearValidators();\n this.updateForm.controls.price.get('currency')?.clearValidators();\n }\n }\n\n protected openFileUpload() {\n if (this.images.length >= this.photos_limit) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.no_more_upload + this.photos_limit + languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.files);\n return;\n } else {\n const allowedTypes = UPLOAD_FILE_TYPES[CHANNEL_TYPE.OLX]?.allowed_types;\n\n this.modalService.openModal({\n translate: true,\n component: FileUploadNewComponent,\n title: 'upload_file',\n height: 'auto',\n data: {\n length: this.images.length,\n max_upload_files: this.photos_limit,\n channel_type: CHANNEL_TYPE.OLX,\n allowed_types: allowedTypes,\n },\n }).subscribe((res) => {\n if (res) {\n if (!this.images) {\n this.images = [];\n }\n res.forEach((img: any) => {\n this.images.push({ url: img.link });\n });\n this.blocks = this.blocks.slice(res.length);\n this.setEmptyBlocks();\n }\n });\n }\n }\n\n protected readonly PRICE_TYPES = PRICE_TYPES;\n protected readonly CURRENCY = CURRENCY;\n protected readonly FormControl = FormControl;\n}\n","
\n
\n

\n {{ 'create_advert' | translate }}\n

\n

\n {{ 'change_advert' | translate }}\n

\n \n close\n \n
\n\n \n\n \n
\n \n
\n
\n\n \n
\n
\n
{{ 'name' | translate }}*
\n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'max_value' | translate }} {{ updateForm.controls.title.errors?.['maxlength'].requiredLength }}\n \n \n {{ 'min_value' | translate }} {{ updateForm.controls.title.errors?.['minlength'].requiredLength }}\n \n \n
\n {{ updateForm.controls.title.value?.length || 0 }}/70\n
\n
\n\n \n
\n
\n
{{ 'select_category' | translate }}*
\n
{{ 'selected_category' | translate }}
\n
\n
\n
\n \n
\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n
\n
\n {{this.current_category}}\n
\n
\n\n
\n
\n
{{ 'select_city' | translate }}*
\n
{{ 'selected_city' | translate }}
\n
\n
\n \n\n \n \n \n\n \n {{this.advert.city.name}} {{this.advert.city.municipality}}\n Обрати інше місто\n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n
\n\n
\n
Додайте в оголошення справжні фото товару...
\n
\n
\n
\n \n delete\n \n \"{{i}}\"\n
\n
Головне
\n
\n
\n
\n \n
\n
\n
\n
\n Додати фото\n
\n
\n
\n
\n
\n
\n\n
\n\n
\n
{{ 'description' | translate }}*
\n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'min_value' | translate }} {{ updateForm.controls.description.errors?.['minlength'].requiredLength }}\n \n \n {{ 'max_value' | translate }} {{ updateForm.controls.description.errors?.['maxlength'].requiredLength }}\n \n \n
\n {{ updateForm.controls.description.value?.length || 0 }}/9000\n
\n
\n\n
\n
{{ 'price' | translate }}*
\n
\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n\n
\n
\n \n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'errors.max_price' | translate }}\n \n \n {{ 'errors.ad_cpesified_currency' | translate }}\n \n \n \n {{ 'negotiable' | translate }}\n \n
\n
\n
\n\n
\n
\n
{{ 'contact_name' | translate }}*
\n
\n
\n \n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n
\n\n
\n
\n
{{ 'phone' | translate }}
\n
\n
\n \n
\n
\n
\n\n \n\n
\n

\n {{ 'additional_information' | translate }}\n

\n
\n\n
\n
{{ 'private_or_business' | translate }}*
\n \n \n {{ 'private_person' | translate }}\n \n \n {{ 'business' | translate }}\n \n \n\n
\n
\n {{ attribute.label }}*\n
\n 2 && !attribute.validation.allow_multiple_values\"\n bindValue=\"code\"\n bindLabel=\"label\"\n bindLabelOption=\"label\"\n [closeOnSelect]=\"true\"\n [items]=\"attribute.values\"\n [formControl]=\"attributeForm.controls[attribute.code]\"\n (focusout)=\"attributeForm.controls[attribute.code].markAsTouched()\"\n >\n \n \n {{ value.label }}\n \n \n \n \n \n \n \n
\n \n {{ value.label }}\n \n
\n
\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'max_value' | translate }} {{ attribute.validation.max }}\n \n \n {{ 'min_value' | translate }} {{ attribute.validation.min }}\n \n \n
\n
\n
\n\n
\n \n {{ 'cancel' | translate }}\n \n
\n
\n \n event_busy\n \n \n calendar_month\n \n
\n {{ 'publish_now' | translate }}\n {{ 'save' | translate }}\n {{ 'to_plan' | translate }}\n
\n
\n\n","import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { provideNgxMask } from 'ngx-mask';\n\n\n@Component({\n selector: 'app-price-field',\n templateUrl: './price-field.component.html',\n styleUrls: ['./price-field.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => PriceFieldComponent)\n },\n ]\n})\nexport class PriceFieldComponent implements OnInit, ControlValueAccessor {\n\n @Input() defaultActive = false;\n @Input() isEdit = false;\n @Input() actionBtn = true;\n @Input() defaultValue: string = '';\n @Input() label: string = '';\n @Input() customClass: string = '';\n @Input() placeholder: string = '';\n @Input() mask: string = '';\n @Input() maxLength!: number;\n @Input() disabled: boolean = false;\n @Input() border: boolean = false;\n @Input() leadZero: boolean = false;\n @Input() size: 'small' | 'default' | 'big' = 'default';\n @Input() type: 'hidden' | 'default' = 'default';\n @Input() required: boolean = false;\n @Input() Error: boolean = false;\n\n @Output() onChangeClick = new EventEmitter();\n @Output() onChangeSave = new EventEmitter();\n @Output() onChangeCancel = new EventEmitter();\n @Output() onValueChange = new EventEmitter();\n\n @ViewChild('inputRef', { static: false }) inputRef!: ElementRef;\n\n @Input() input:string = '';\n copyInput: string = '';\n\n ngOnInit(): void {\n }\n\n onChange = (data:any) => {}\n onTouch = (_:any) => {}\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: string) {\n this.input = value;\n this.onChange(value);\n }\n\n /**\n *Func triggered on input change\n */\n onchange(event:any){\n this.input=event;\n this.onChange(event);\n this.onValueChange.emit(event);\n }\n\n click() {\n if (!this.isEdit) {\n this.isEdit = true;\n this.copyInput = this.input;\n this.onChangeClick.emit();\n setTimeout(() => {\n this.inputRef.nativeElement.focus();\n }, 100);\n }\n }\n\n save() {\n this.isEdit = false;\n this.defaultValue = this.input;\n this.onChangeSave.emit(this.input);\n }\n\n cancel() {\n this.isEdit = false;\n this.input = this.copyInput;\n this.onChangeCancel.emit();\n }\n}\n","\n \n \n \n \n \n
\n \n
{{input}}
\n
\n \n \n
\n \n \n
\n
\n
\n
\n
\n\n\n\n
\n \n \n
\n
\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, take, takeUntil } from 'rxjs';\nimport { RightSidebarService } from 'src/app/services/right-sidebar.service';\nimport { BaseService } from 'src/app/services/base.service';\nimport { OlxAdvertService } from '../../../../../services/olx-advert.service';\nimport { FormControl, FormGroup } from '@angular/forms';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { PlannerComponent } from '../../../../../modals/planner/planner.component';\nimport { ERROR_MESSAGES, languages, MODAL_TYPES } from '../../../../../enum/constants';\nimport moment from 'moment/moment';\nimport { IError } from '../../../../../interfaces';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { DialogService } from '../../../../../services/dialog.service';\n\n\ninterface IFormGroup {\n currentPacket: FormControl;\n countAdverts: FormControl;\n zone: FormControl;\n currentZone: FormControl;\n}\n\ninterface SelectedTariff {\n category_id: number,\n is_premium: boolean,\n name: string,\n package_id: string,\n price: number,\n size: number,\n type: string,\n variant_id: string,\n zone_id: string\n}\n\n@Component({\n selector: 'app-buy-olx-package',\n templateUrl: './buy-olx-package.component.html',\n styleUrls: ['./buy-olx-package.component.scss'],\n})\nexport class BuyOlxPackageComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n formPacket!: FormGroup;\n public packet: [] = [];\n\n public categories: any[] = [];\n public current_category_id: number = 0;\n public packetsAll = [];\n public packets: any = {\n base: {},\n premium: {},\n mega: {},\n };\n private channel_id: any;\n selectedBase: any;\n selectedPremium: any;\n selectedMega: any;\n zones: any[] = [];\n currentZone: any;\n isMega: boolean = false;\n isPremium: boolean = false;\n isBase: boolean = false;\n isPacket: boolean = false;\n isCurrentZone: boolean = true;\n selectedTariff!: SelectedTariff;\n balance?: any;\n isLoader: boolean = false;\n calendar: any;\n planned_at_premium: any;\n planned_at_mega: any;\n planned_at_base: any;\n planned_at: any;\n isDraft?: string;\n notFound: boolean = false;\n\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n readonly baseService: BaseService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly modalService: ModalService,\n private readonly notificationService: NotificationService,\n private readonly dialogService: DialogService,\n ) {\n const options = this.rightSidebarService.getOptions();\n this.channel_id = options.data.channel_id;\n\n }\n\n ngOnInit() {\n this.initOlxCategories();\n this.initForm();\n this.initBalance();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initOlxCategories() {\n this.olxAdvertService.getCategories(this.current_category_id)\n .subscribe({\n next: (res) => {\n this.categories.push(res);\n },\n error: (err) => {\n\n },\n });\n }\n\n public initBalance() {\n this.olxAdvertService.getBalance(this.channel_id)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (balance) => {\n this.balance = balance;\n },\n error: () => {\n },\n });\n }\n\n public selectCategory(category: any, index: any) {\n if (index !== this.categories.length - 1) {\n this.categories.splice(index + 1);\n this.packets.premium.packages = [];\n this.packets.base.packages = [];\n this.packets.mega.packages = [];\n this.isPacket = false;\n this.current_category_id = category.category_id;\n this.notFound = false;\n } else {\n this.packets.premium.packages = [];\n this.packets.base.packages = [];\n this.packets.mega.packages = [];\n }\n this.current_category_id = category.category_id;\n\n if (category.is_leaf) {\n this.selectedBase = null;\n this.selectedPremium = null;\n this.selectedMega = null;\n this.planned_at_base = null;\n this.planned_at_premium = null;\n this.planned_at_mega = null;\n this.formPacket.controls.countAdverts.setValue(null);\n this.notFound = false;\n this.getZones();\n } else {\n this.formPacket.controls.countAdverts.setValue(null);\n this.selectedBase = null;\n this.selectedPremium = null;\n this.selectedMega = null;\n this.planned_at_base = null;\n this.planned_at_premium = null;\n this.planned_at_mega = null;\n\n this.initOlxCategories();\n this.zones = [];\n this.currentZone = null;\n }\n\n this.formPacket.controls['currentPacket'].setValue(null);\n }\n\n\n private initPackets(zone_id?: string) {\n this.isLoader = true;\n this.olxAdvertService.getPackets(this.current_category_id, this.channel_id, zone_id || '')\n .subscribe({\n next: (res) => {\n if (!res.error) {\n this.notFound = false;\n this.packetsAll = res;\n this.isPacket = true;\n res.forEach((v: any) => {\n if (v.is_premium && v.type === 'base') {\n if (this.packets.premium?.packages?.length) {\n this.packets.premium.packages.push(v);\n } else {\n this.packets.premium.packages = [v];\n }\n\n }\n if (!v.is_premium && v.type === 'base') {\n if (this.packets.base?.packages?.length) {\n this.packets.base.packages.push(v);\n } else {\n this.packets.base.packages = [v];\n }\n }\n\n if (v.is_premium && v.type === 'mega') {\n if (this.packets.mega?.packages?.length) {\n this.packets.mega.packages.push(v);\n } else {\n this.packets.mega.packages = [v];\n }\n }\n });\n const p = Object.keys(this.packets.mega).length;\n if (p) {\n this.isPremium = true;\n const a = this.packets.premium.packages.sort((a: any, b: any) => a.size - b.size);\n this.packets.premium.packages = [...a];\n }\n\n const b = Object.keys(this.packets.mega).length;\n if (b) {\n this.isBase = true;\n const a = this.packets.base.packages.sort((a: any, b: any) => a.size - b.size);\n this.packets.base.packages = [...a];\n }\n\n const m = Object.keys(this.packets.mega).length;\n if (m) {\n this.isMega = true;\n const a = this.packets.mega.packages.sort((a: any, b: any) => a.size - b.size);\n this.packets.mega.packages = [...a];\n }\n this.isLoader = false;\n } else {\n this.notFound = true;\n this.isLoader = false;\n }\n\n },\n error: (err) => {\n\n },\n });\n }\n\n public initForm() {\n this.formPacket = new FormGroup({\n currentPacket: new FormControl(),\n countAdverts: new FormControl(),\n zone: new FormControl(),\n currentZone: new FormControl(),\n });\n }\n\n packetBase(selectedPackage: any) {\n this.selectedBase = selectedPackage;\n }\n\n packetPremium(selectedPackage: any) {\n this.selectedPremium = selectedPackage;\n }\n\n packetMega(selectedPackage: any) {\n this.selectedMega = selectedPackage;\n }\n\n closeBar() {\n this.rightSidebarService.close();\n }\n\n choosePacket(e: any, packet: any) {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.buy_package`,\n });\n dialogRef.subscribe(result => {\n if (result) {\n const v = this.formPacket.getRawValue();\n if (!this.planned_at_premium && !this.planned_at_mega && !this.planned_at_base) {\n const value = {\n payment_method: 'account',\n category_id: v.countAdverts.category_id,\n size: v.countAdverts.size,\n type: v.countAdverts.type,\n zone_id: v.countAdverts.zone_id,\n is_premium: v.countAdverts.is_premium,\n };\n this.olxAdvertService.buyPacket(value, this.channel_id)\n .subscribe({\n next: (res) => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_bought);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n this.rightSidebarService.close();\n });\n },\n });\n } else {\n if (this.planned_at_base && packet.type === 'base' && packet.is_premium === false) {\n this.planned_at = this.planned_at_base;\n } else if (this.planned_at_premium && packet.type === 'base' && packet.is_premium === true) {\n this.planned_at = this.planned_at_premium;\n } else if (this.planned_at_mega && packet.type === 'mega') {\n this.planned_at = this.planned_at_mega;\n }\n const value = {\n name: this.calendar?.name || '',\n planned_at: this.planned_at,\n type: 'PACKET',\n status: this.isDraft,\n x_data: {\n channel_id: this.channel_id,\n payment_method: 'account',\n category_id: v.countAdverts.category_id,\n size: v.countAdverts.size,\n type: v.countAdverts.type,\n zone_id: v.countAdverts.zone_id,\n is_premium: v.countAdverts.is_premium,\n },\n };\n this.olxAdvertService.createPlanner(value)\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_planned);\n this.rightSidebarService.close();\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_planner);\n this.rightSidebarService.close();\n },\n });\n }\n }\n });\n }\n\n getZones() {\n this.olxAdvertService.getZones(this.current_category_id, this.channel_id)\n .subscribe((res) => {\n this.zones = res.map((v: any) => {\n return { ...v, label_ua: v.labels.uk };\n });\n if (!this.zones.length) this.initPackets();\n });\n }\n\n selectZone(e: any) {\n this.isCurrentZone = true;\n const v = this.formPacket.get('zone')?.value;\n this.currentZone = v;\n if (this.currentZone.sub_zones.length === 0) {\n this.isCurrentZone = false;\n this.initPackets(v.id);\n } else {\n this.currentZone = this.currentZone.sub_zones.map((v: any) => {\n return { ...v, label_ua: v.labels.uk };\n });\n }\n }\n\n selectCurrentZone(e: any) {\n const v = this.formPacket.get('currentZone')?.value;\n this.initPackets(v.id);\n }\n\n openPlanner(packet: any) {\n this.modalService.openModal({\n title: 'planner_paid',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.PACKET,\n },\n }).subscribe((res) => {\n if (res) {\n if (packet.type === 'base' && packet.is_premium === false) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n this.calendar = res;\n this.planned_at_base = dateTime;\n this.isDraft = res.x_data.status;\n } else if (packet.type === 'base' && packet.is_premium) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n this.calendar = res;\n this.planned_at_premium = dateTime;\n this.isDraft = res.x_data.status;\n } else if (packet.type === 'mega') {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n this.calendar = res;\n this.planned_at_mega = dateTime;\n this.isDraft = res.x_data.status;\n }\n }\n });\n }\n\n cancelPlanner(packet?: any) {\n if (packet.type === 'base' && packet.is_premium === false) {\n this.planned_at_base = null;\n } else if (packet.type === 'base' && packet.is_premium === true) {\n this.planned_at_premium = null;\n } else if (packet.type === 'mega') {\n this.planned_at_mega = null;\n }\n }\n}\n","
\n
\n

{{ 'buy_package' | translate }}

\n
\n
{{ 'your_account_information' | translate }}: {{ balance?.sum }} {{ 'UAH' | translate }}.
\n
{{ 'available_balance'| translate }}: {{ balance?.bonus }} {{ 'bonuses' | translate }}
\n
\n \n close\n \n
\n\n \n\n \n \n \n \n \n \n \n \n \n
\n
\n \n
\n \n \n
\n\n
\n \n [disabled]=\"isLoader\"\n \n
\n
\n
\n\n\n \n
\n {{'not_found_packet' | translate}}\n
\n
\n\n \n
\n \n
\n
\n \n
\n
\n
\n
\n
\n
Старт
\n
\n
\n \n
\n
\n\n
\n
\n

{{ selectedBase?.price || 0 }}грн

\n
\n
\n
\n {{ \"buy\" | translate }}\n \n {{ \"plan_packet\" | translate }}\n \n
\n
\n \n event_busy\n \n \n calendar_month\n \n
\n
\n
\n\n
\n
\n

Що включено

\n
\n check\n Бізнес-сторінка\n
\n
\n check\n Пакет дійсний 30 днів\n
\n

\n

\n

\n
\n
\n
\n
\n\n
\n
\n
\n
\n
Преміум
\n
\n
\n \n
\n
\n\n
\n
\n

{{ selectedPremium?.price || 0 }}грн

\n
\n
\n {{ \"buy\" | translate }}\n \n {{ \"plan_packet\" | translate }}\n \n
\n \n event_busy\n \n \n calendar_month\n \n
\n
\n
\n\n
\n
\n

Що включено

\n
\n check\n 3 х Підняття кожного оголошення\n
\n
\n check\n Персональний дизайн сторінок оголошень\n
\n
\n check\n Оновлена бізнес-сторінка\n
\n
\n check\n Пакет дійсний 30 днів\n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n
Мега
\n
\n
\n \n
\n
\n\n
\n
\n

{{ selectedMega?.price || 0 }}грн

\n
\n
\n
\n {{ \"buy\" | translate }}\n \n {{ \"plan_packet\" | translate }}\n \n
\n
\n \n event_busy\n \n \n calendar_month\n \n
\n
\n
\n\n
\n
\n

Що включено

\n
\n check\n 3 х Підняття кожного оголошення\n
\n
\n check\n Персональний дизайн сторінок оголошень\n
\n
\n check\n Оновлена бізнес-сторінка\n
\n
\n check\n Пакет дійсний 30 днів\n
\n
\n
\n
\n
\n
\n
\n\n
\n","import { Component, TemplateRef, ViewChild } from '@angular/core';\nimport { RightSidebarService } from '../../../services/right-sidebar.service';\nimport { CdkPortal } from '@angular/cdk/portal';\n\n\n@Component({\n selector: 'app-right-sidebar',\n templateUrl: './right-sidebar.component.html',\n styleUrls: ['./right-sidebar.component.scss']\n})\nexport class RightSidebarComponent {\n @ViewChild('drawerPortal') drawerPortal?: CdkPortal;\n @ViewChild('drawerContent') drawerContent?: TemplateRef;\n\n constructor(\n public readonly rightSidebarService: RightSidebarService,\n ) { }\n\n}\n","\n \n \n \n \n \n \n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n","import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\n\n\n@Injectable({\n providedIn: 'root'\n})\nexport class SidebarService {\n public opened = !(window.innerWidth <= 700);\n private openedStateChanged$ = new BehaviorSubject(this.opened);\n public opened$ = this.openedStateChanged$.asObservable();\n\n constructor() { }\n\n public toggle() {\n this.opened = !this.opened;\n this.openedStateChanged$.next(this.opened);\n }\n}\n","import { Component, OnInit } from '@angular/core';\nimport { SidebarService } from '../../../services/sidebar.service';\nimport { BaseService } from '../../../services/base.service';\nimport { Router } from '@angular/router';\nimport { RightSidebarService } from '../../../services/right-sidebar.service';\nimport { ChatService } from '../../../services/chat.service';\nimport { environment } from '../../../../environments/environment';\nimport { PERMISSIONS } from \"../../../enum/constants\";\nimport { TranslateService } from '@ngx-translate/core';\n\n\n@Component({\n selector: 'app-left-menu',\n templateUrl: './left-menu.component.html',\n styleUrls: ['./left-menu.component.scss'],\n})\nexport class LeftMenuComponent implements OnInit {\n opened!: boolean;\n show: boolean = false;\n public version = environment.version;\n public subCrm = [\n // { icon: 'person_search', title: 'Ліди', url: '/leads' },\n { icon: 'price_check', tkey: 'deals', url: '/deals', key: PERMISSIONS.CRM_DEAL },\n { icon: 'group', tkey: 'contacts', url: '/contact', key: PERMISSIONS.CONTACTS_ACCESS },\n // { icon: 'shop', title: 'Продукти', url: '/products', disabled: true },\n ];\n\n public subLogistic = [\n { svg: 'assets/svgs/integrations/nova_poshta_white.svg', icon: 'mark_as_unread', tkey: 'nova_poshta', url: '/logistic/nova-poshta' },\n { svg: 'assets/svgs/integrations/ukr_poshta_icon_white.svg', icon: 'local_post_office', tkey: 'ukr_poshta', url: '/logistic/ukr-poshta' },\n { icon: 'settings', tkey: 'settings', url: '/logistic/settings' },\n // { icon: 'markunread_mailbox', title: 'Meest', url: '/meest-poshta', disabled: true },\n ];\n\n public subSetting = [\n { icon: 'sync_alt', tkey: 'general', url: '/settings/general' },\n { icon: 'manage_accounts', tkey: 'User_profile', url: '/settings/profile' },\n { icon: 'manage_accounts', tkey: 'CompanySettings', url: '/settings/company' },\n ];\n\n public subSpace = [\n { icon: 'cloud_upload', tkey: 'disk', url: '/file-system' },\n ];\n\n public subPeople = [\n { icon: 'people', tkey: 'people', url: '/team' },\n ];\n\n public subWareHouse = [\n { icon: 'inbox', tkey: 'products', url: '/warehouse/products' },\n { icon: 'category', tkey: 'categories', url: '/warehouse/categories' },\n ];\n\n public subMarket = [\n { icon: 'storefront', tkey: 'store', url: '/marketplace' },\n {\n svg: 'assets/svgs/integrations/olx.svg',\n icon: 'storefront',\n tkey: 'PowerOLX',\n url: '/application/olx',\n key: 'OLX_TOOL',\n hidden: !this.baseService.currentCompanyInfo.application_keys.includes('OLX_TOOL'),\n },\n {\n svg: 'assets/svgs/integrations/ringostat-logo.svg',\n tkey: 'ringostat',\n url: '/application/ringostat',\n key: 'RINGOSTAT',\n hidden: !this.baseService.currentCompanyInfo.application_keys.includes('RINGOSTAT'),\n },\n {\n icon: 'construction',\n tkey: 'sites',\n url: '/application/sites',\n hidden: !this.baseService.currentCompanyInfo.settings.is_sites,\n }\n ];\n\n public menuList: any[] = [\n { icon: 'leaderboard', tkey: 'home', url: '/dashboard' },\n { icon: 'apartment', tkey: 'structure', sub: this.subPeople, isBeta: true, key: PERMISSIONS.TEAM_ACCESS },\n { icon: 'dashboard', tkey: 'CRM', sub: this.subCrm, isBeta: true, key: PERMISSIONS.CRM_ACCESS },\n { icon: 'multiple_stop', tkey: 'Integrations', url: '/integration', key: PERMISSIONS.INTEGRATION_ACCESS },\n { icon: 'hub', tkey: 'channels', url: '/channel', isWarning: this.baseService.isExpired, key: PERMISSIONS.CHANNELS_ACCESS },\n { icon: 'local_shipping', tkey: 'logistics', sub: this.subLogistic, key: PERMISSIONS.LOGISTICS_ACCESS },\n { icon: 'warehouse', tkey: 'warehouse', sub: this.subWareHouse, hidden: !this.baseService.currentCompanyInfo.settings.is_warehouse },\n // { icon: 'group_work', title: 'OLX', sub: this.subOlx },\n // { icon: 'folder', title: 'Файли', url: '/cloud' },\n { icon: 'chat', tkey: 'chats', isChat: true, key: PERMISSIONS.CHATS_ACCESS },\n { icon: 'rocket_launch', tkey: 'JE_Space', sub: this.subSpace, hidden: !this.baseService.currentCompanyInfo.settings.is_jespace, key: PERMISSIONS.FILES_ACCESS },\n { icon: 'apps', tkey: 'applications', sub: this.subMarket, key: PERMISSIONS.APPS_ACCESS },\n // { icon: 'list_alt', title: 'Мої Задачі', sub: this.subFilters },\n { icon: 'settings', tkey: 'settings', sub: this.subSetting, key: PERMISSIONS.SETTINGS_ACCESS },\n { icon: 'precision_manufacturing', url: '/automation', tkey: 'automation', hidden: !this.baseService.currentCompanyInfo.settings.is_automation, },\n { icon: 'payment', tkey: 'Payment', url: '/payment', key: PERMISSIONS.PAYMENT_ACCESS },\n { icon: 'support_agent', tkey: 'support', url: '/support' },\n ];\n\n lastOpenedItem: any = null;\n urlHistoryVersion = '/history-version';\n\n constructor(\n public readonly sidebarService: SidebarService,\n public readonly baseService: BaseService,\n public readonly chatService: ChatService,\n public readonly rightSidebarService: RightSidebarService,\n private readonly router: Router,\n private translate: TranslateService,\n ) {\n }\n\n ngOnInit() {\n const per = this.baseService.currentUserInfo.permissions;\n this.menuList = [...this.menuList.filter(v => !v.key || per.includes(v.key))];\n\n this.sidebarService.opened$\n .subscribe(v => {\n this.opened = v;\n if (!this.opened)\n this.menuList[1].active = false;\n });\n\n this.baseService.currentCompanyInfo$.subscribe(data => {\n const applications = this.subMarket.filter((v: any) => v.key);\n applications.forEach((v: any) => {\n v.hidden = !this.baseService.currentCompanyInfo.application_keys.includes(v.key);\n })\n // this.subMarket[1].hidden = !this.baseService.currentCompanyInfo.application_keys.includes('OLX_TOOL');\n this.menuList.forEach(v => {\n if (v.key === 'Логістика') {\n v.hidden = !this.baseService.currentCompanyInfo.application_keys.includes('LOGISTIC');\n }\n })\n });\n }\n\n openMenu() {\n if (!this.baseService.isMobile) this.show = true;\n }\n\n closeMenu() {\n if (!this.baseService.isMobile) this.show = false;\n }\n\n openSubMenu(item: any) {\n if (this.lastOpenedItem && this.lastOpenedItem !== item) {\n this.lastOpenedItem.active = false;\n }\n\n this.lastOpenedItem = item;\n item.active = !item.active;\n\n if (!this.opened && item.active) {\n this.sidebarService.toggle();\n }\n }\n\n closeSidebar() {\n if (this.baseService.isMobile && this.opened)\n this.sidebarService.toggle();\n }\n\n public sidebarRowOpened = false;\n\n toggleSidebar() {\n this.sidebarService.toggle();\n if (this.opened) {\n this.menuList.forEach(item => {\n if (item.active) {\n this.lastOpenedItem = item;\n }\n });\n }\n this.sidebarRowOpened = !this.sidebarRowOpened;\n }\n\n public openChat() {\n this.rightSidebarService.openChats()\n .subscribe(() => {\n // this.chatService.loadCount();\n });\n }\n\n getCountSub(item: any) {\n const copy = { ...item };\n const sub = copy.sub.filter((v: any) => !v.hidden);\n return sub.length;\n }\n}\n\n","
\n
\n
\n \n
\n \"logo\"\n
\n
\n \n \n
\n
\n
\n {{ item.icon }}\n {{ item.tkey | translate }}\n {{ 'beta_test' | translate }}\n
\n keyboard_arrow_right\n
\n
\n \n \n
\n
\n {{ subitem.icon }}\n {{ subitem.tkey | translate }}\n {{ 'coming_soon' | translate }}\n
\n
\n
\n
\n \"\"\n {{ subitem.icon }}\n {{ subitem.tkey | translate }}\n
\n
\n
\n
\n
\n
\n \n
\n
\n {{ item.icon }}\n {{ item.tkey | translate }}\n
\n
\n
\n
\n {{ item.icon }}\n {{ item.tkey | translate }}\n
\n
\n
\n
\n
\n
\n
\n
{{ version }}\n
\n
\n
\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { BaseService } from '../../services/base.service';\nimport { NotificationService } from \"../../services/notifications.service\";\nimport { combineLatest } from 'rxjs';\n\n\n@Component({\n templateUrl: './notification-popup.component.html',\n styleUrls: ['./notification-popup.component.scss'],\n})\nexport class NotificationPopupComponent {\n\n notification!: any;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) {\n this.notification = data?.data?.notification;\n }\n\n save() {\n this.dialogRef.close(true);\n }\n\n onNoClick(): void {\n this.dialogRef.close(false);\n }\n\n readNot() {\n this.notificationService.readNotification(this.notification.id)\n .subscribe(() => {\n this.save();\n combineLatest([\n this.notificationService.getCountNotification(),\n this.notificationService.getLastNotification(),\n ]).subscribe();\n })\n }\n\n}\n","
\n
{{ notification.title }}
\n
{{ notification.message }}
\n
\n {{ 'read' | translate }}\n
\n
\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { map, tap } from 'rxjs';\nimport { Router } from '@angular/router';\nimport { KeycloakService } from 'keycloak-angular';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class CompanyService {\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n private readonly keycloakService: KeycloakService,\n ) { }\n\n public updateCompany(payload: any) {\n return this.http.put(environment.baseURL + '/companies/update', payload)\n .pipe(map(res => res.data));\n }\n\n public getZone() {\n return this.http.get(environment.baseURL + '/companies/zone')\n .pipe(map(res => res.data));\n }\n\n public getSettings() {\n return this.http.get(environment.baseURL + '/companies/settings')\n .pipe(map(res => res.data));\n }\n\n public updateSettings(id: string, payload: any) {\n return this.http.put(environment.baseURL + `/companies/settings/${id}`, payload)\n .pipe(map(res => res.data));\n }\n}\n","import { Injectable } from '@angular/core';\nimport { BehaviorSubject } from 'rxjs';\nimport { BaseService } from './base.service';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class RightMenuService {\n public opened = !(window.innerWidth <= 700);\n private openedStateChanged$ = new BehaviorSubject(this.opened);\n constructor(private baseService: BaseService) { }\n\n public toggle() {\n this.opened = !this.opened;\n this.openedStateChanged$.next(this.opened);\n }\n\n}\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { map, tap } from 'rxjs';\nimport { Router } from '@angular/router';\nimport { KeycloakService } from 'keycloak-angular';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class TariffService {\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n private readonly keycloakService: KeycloakService,\n ) { }\n\n public getTariffs() {\n return this.http.get(environment.baseURL + '/tariff')\n .pipe(map(res => res.data));\n }\n\n public getMyTariff() {\n return this.http.get(environment.baseURL + '/tariff/my')\n .pipe(map(res => res.data));\n }\n\n public getPaymentNotification() {\n return this.http.get(environment.baseURL + '/payment/all')\n .pipe(map(res => res.data));\n }\n\n usePromoCode(payload: { promo_code: string }) {\n return this.http.post(environment.baseURL + '/tariff/check', payload)\n .pipe(map(res => res.data));\n }\n\n checkPromo(payload: { promo_code: string }) {\n return this.http.post(environment.baseURL + '/tariff', payload)\n .pipe(map(res => res.data));\n }\n\n public activeTrial(payload: any) {\n const url = environment.baseURL + '/payment/activate-demo';\n return this.http.put(url, payload)\n .pipe(map(res => res.data));\n }\n\n}\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { map, tap } from 'rxjs';\nimport { Router } from '@angular/router';\nimport { KeycloakService } from 'keycloak-angular';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class SettingsService {\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n private readonly keycloakService: KeycloakService,\n ) { }\n\n public updateSettings(payload: any) {\n return this.http.put(environment.baseURL + '/profile/settings', payload)\n .pipe(map(res => res.data));\n }\n\n}\n","import { Component, ElementRef, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';\nimport { AudioService } from \"../../services/audio.service\";\nimport { Subject, takeUntil } from \"rxjs\";\nimport { BaseService } from \"../../services/base.service\";\n\n@Component({\n selector: 'app-audio-player',\n templateUrl: './audio-player.component.html',\n styleUrl: './audio-player.component.scss'\n})\nexport class AudioPlayerComponent implements OnInit, OnDestroy {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// ViewChild`s\n @ViewChild('audio', { static: true }) audioRef!: ElementRef;\n @ViewChild('currentTime', { static: true }) currentTimeElRef!: ElementRef;\n @ViewChild('duration', { static: true }) durationElRef!: ElementRef;\n @ViewChild('progressBar', { static: true }) progressBarRef!: ElementRef;\n @ViewChild('volume', { static: true }) volume!: ElementRef;\n\n// Var`s\n private loadedMetadataListener: any;\n private timeUpdateListener: any;\n\n public currentIndex!: number;\n public currentVolume: number = 1;\n\n public isVolumeOff: boolean = false;\n public isSeeking: boolean = false;\n\n public progress: number = 0;\n\n constructor(\n public baseService: BaseService,\n public readonly audioService: AudioService,\n private renderer: Renderer2,\n ) {\n }\n\n ngOnInit() {\n this.initAudio();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n\n if (this.loadedMetadataListener) this.loadedMetadataListener();\n if (this.timeUpdateListener) this.timeUpdateListener();\n }\n\n initAudio(): void {\n const audio = this.audioRef.nativeElement;\n const currentTimeElem = this.currentTimeElRef.nativeElement;\n const durationElem = this.durationElRef.nativeElement;\n const progressBar = this.progressBarRef.nativeElement;\n\n this.loadedMetadataListener = this.renderer.listen(audio, 'loadedmetadata', () => {\n durationElem.textContent = this.formatTime(audio.duration);\n });\n\n this.timeUpdateListener = this.renderer.listen(audio, 'timeupdate', () => {\n if (!this.isSeeking) {\n currentTimeElem.textContent = this.formatTime(audio.currentTime);\n audio.addEventListener('timeupdate', this.syncProgress.bind(this));\n const progressPercent = (audio.currentTime / audio.duration) * 100;\n if (progressPercent === 100) this.audioService.setIsPlay(false);\n progressBar.style.width = `${progressPercent}%`;\n }\n });\n\n this.renderer.listen(progressBar, 'click', (event: MouseEvent) => {\n const rect = progressBar.getBoundingClientRect();\n const offsetX = event.clientX - rect.left;\n audio.currentTime = (offsetX / rect.width) * audio.duration;\n });\n\n this.currentIndex = this.audioService.audioSource.findIndex(v => v === this.audioService.currentAudio);\n this.audioRef.nativeElement.volume = this.currentVolume;\n\n this.audioService.playAudio$\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (v: any) => v ? audio.play() : audio.pause(),\n })\n }\n\n private formatTime = (seconds: number): string => {\n const hours = Math.floor(seconds / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n\n seconds = Math.floor(seconds % 60);\n return `${hours}:${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;\n }\n\n public onChangeAudio(isNext: boolean): void {\n const audio = this.audioRef.nativeElement;\n\n const changeAudio = () => {\n audio.pause();\n this.audioService.setIsPlay(false);\n\n audio.src = this.audioService.currentAudio;\n\n audio.play();\n this.audioService.setIsPlay(true);\n }\n\n if (isNext && this.currentIndex < this.audioService.audioSource.length - 1) {\n this.audioService.setCurrentAudio(this.audioService.audioSource[this.currentIndex + 1]);\n this.currentIndex = this.audioService.audioSource.findIndex(v => v === this.audioService.currentAudio);\n changeAudio();\n } else if (!isNext && this.currentIndex > 0) {\n this.audioService.setCurrentAudio(this.audioService.audioSource[this.currentIndex - 1]);\n this.currentIndex = this.audioService.audioSource.findIndex(v => v === this.audioService.currentAudio);\n changeAudio();\n }\n }\n\n public onChangePlay(): void {\n const audio = this.audioRef.nativeElement;\n\n if (audio.paused) {\n audio.play();\n this.audioService.setIsPlay(true);\n } else {\n audio.pause();\n this.audioService.setIsPlay(false);\n }\n }\n\n public onVolumeChange(event: Event): void {\n this.currentVolume = (event.target as HTMLInputElement).valueAsNumber;\n this.isVolumeOff = this.currentVolume === 0;\n\n this.audioRef.nativeElement.volume = this.currentVolume;\n }\n\n public onValueMute(): void {\n if (this.isVolumeOff) {\n if (this.currentVolume === 0) {\n this.audioRef.nativeElement.volume = 1;\n this.volume.nativeElement.value = 1;\n } else {\n this.audioRef.nativeElement.volume = this.currentVolume;\n this.volume.nativeElement.value = this.currentVolume;\n }\n } else {\n this.audioRef.nativeElement.volume = 0;\n this.volume.nativeElement.value = 0;\n }\n\n this.isVolumeOff = !this.isVolumeOff;\n }\n\n public checkAudioVolume(): string {\n if (this.isVolumeOff) return 'volume_off';\n\n const volume: number = this.audioRef?.nativeElement?.volume;\n if (volume > 0 && volume < 0.25) return 'volume_mute';\n if (volume >= 0.25 && volume < 0.75) return 'volume_down';\n\n return 'volume_up';\n }\n\n public updateProgress(event: Event): void {\n this.isSeeking = true;\n\n const audio = this.audioRef.nativeElement;\n const value = (event.target as HTMLInputElement).value;\n const currentTimeElem = this.currentTimeElRef.nativeElement;\n\n this.progress = Number(value);\n currentTimeElem.textContent = this.formatTime((audio.duration / 100) * Number(value));\n }\n\n public setProgress(event: Event): void {\n const audio = this.audioRef.nativeElement;\n const value = (event.target as HTMLInputElement).value;\n\n audio.currentTime = (audio.duration / 100) * Number(value);\n\n this.isSeeking = false;\n }\n\n public syncProgress(): void {\n if (!this.isSeeking) {\n const audio = this.audioRef.nativeElement;\n this.progress = (audio.currentTime / audio.duration) * 100;\n }\n }\n\n public onRewind(isForward: boolean): void {\n const audio = this.audioRef.nativeElement;\n audio.currentTime = isForward ? audio.currentTime + 5 : audio.currentTime - 5;\n }\n\n public onClearAudio() {\n this.audioService.audioSource = [];\n this.audioService.setCurrentAudio('');\n }\n}\n","
\n
\n \n close\n \n\n \n skip_previous\n \n\n \n {{\n audioService.isPlay ? 'pause' : 'play_arrow'\n }}\n \n\n \n skip_next\n \n
\n \n {{\n !baseService.isMobile ? 'keyboard_arrow_up' : 'keyboard_arrow_down'\n }}\n {{\n !baseService.isMobile ? 'keyboard_arrow_down' : 'keyboard_arrow_up'\n }}\n
\n\n\n\n\n\n \n \n replay_5\n \n\n \n skip_previous\n \n\n \n {{\n audioService.isPlay ? 'pause' : 'play_arrow'\n }}\n \n\n \n skip_next\n \n\n \n forward_5\n \n \n
\n
\n
\n 00:00:00\n
\n
\n
\n
\n
\n
\n
\n \n
\n --:--:--\n
\n
\n
\n {{ checkAudioVolume() }}\n \n
\n
\n
\n","import { Pipe, PipeTransform } from '@angular/core';\nimport moment from 'moment';\nimport { BaseService } from \"../services/base.service\";\nimport { languages } from \"../enum/constants\";\n\n\n@Pipe({\n name: 'dateAgo',\n pure: true\n})\nexport class DateAgoPipe implements PipeTransform {\n constructor(private readonly baseService: BaseService) {\n }\n\n transform(value: any, args?: any): any {\n if (value) {\n const seconds = Math.floor((+new Date() - +new Date(value)) / 1000);\n if (seconds < 29)\n return languages[this.baseService.currentUserInfo.settings.language]['just_now'];\n else {\n moment.locale(`${this.baseService.currentUserInfo.settings.language === 'ua' ? 'uk' : this.baseService.currentUserInfo.settings.language}`);\n return moment(value).fromNow();\n }\n }\n\n return value;\n }\n}\n","import {\n Component,\n HostListener,\n OnDestroy,\n OnInit,\n ViewChild,\n} from '@angular/core';\nimport { SidebarService } from '../../../services/sidebar.service';\nimport { AuthService } from '../../../services/auth.service';\nimport { BaseService } from '../../../services/base.service';\nimport { OnlineStatusType } from 'ngx-online-status';\nimport { map, share, Subject, switchMap, take, takeUntil, timer } from 'rxjs';\nimport { CompanyService } from '../../../services/company.service';\nimport { Company, CompanyForm } from './header.interface';\nimport { environment } from '../../../../environments/environment';\nimport { RightMenuService } from '../../../services/right-menu.service';\nimport { ChatService } from '../../../services/chat.service';\nimport { RightSidebarService } from '../../../services/right-sidebar.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { ModalService } from '../../../services/modal.service';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { NotificationService } from '../../../services/notifications.service';\nimport { IError } from '../../../interfaces';\nimport {\n DEFAULT_ERROR,\n ERROR_MESSAGES, languages,\n PERMISSIONS,\n quotes,\n} from '../../../enum/constants';\nimport { DialogService } from '../../../services/dialog.service';\nimport { TariffService } from '../../../services/tariff.service';\nimport moment from 'moment';\nimport { differenceDate } from '../../../helpers';\nimport { AudioService } from '../../../services/audio.service';\nimport { TranslateService } from '@ngx-translate/core';\nimport { SettingsForm } from 'src/app/modules/crm/settings/components/settings/settings.interface';\nimport { SettingsService } from 'src/app/services/settings.service';\nimport { OneFieldComponent } from '../../../dialogs/one-field/one-field.component';\nimport { NotificationPopupComponent } from '../../../dialogs/notification-popup/notification-popup.component';\n\n@Component({\n selector: 'app-header',\n templateUrl: './header.component.html',\n styleUrls: ['./header.component.scss'],\n})\nexport class HeaderComponent implements OnInit, OnDestroy {\n protected readonly OnlineStatusType = OnlineStatusType;\n\n public languages = [\n { name: 'UA', value: 'ua' },\n { name: 'EN', value: 'en' },\n ];\n\n public fullLanguages = [\n { name: 'Українська', value: 'ua' },\n { name: 'English', value: 'en' },\n ];\n\n private readonly destroy$: Subject = new Subject();\n\n private readonly chatIdQuery: string =\n this.activatedRoute.snapshot.queryParams['chat'];\n private readonly dealIdQuery: string =\n this.activatedRoute.snapshot.queryParams['deal'];\n\n public company!: Company;\n public companyName: string = '';\n public companySubdomain: string = '';\n\n public warn: boolean = false;\n public isHasChatPer: boolean = false;\n public isHasTeamPer: boolean = false;\n public isHasSettingsPer: boolean = false;\n\n public formSubmitted: boolean = false;\n public companyForm!: FormGroup;\n public settingsForm!: FormGroup;\n\n public rxDate = new Date();\n public trialDays!: number;\n\n public currentQuote = this.getRandomQuote();\n public notificationLoader: boolean = false;\n public notifications: any[] = [];\n\n constructor(\n public readonly authService: AuthService,\n public readonly baseService: BaseService,\n public readonly sidebarService: SidebarService,\n public readonly companyService: CompanyService,\n public readonly rightMenuService: RightMenuService,\n public readonly rightSidebarService: RightSidebarService,\n public readonly chatService: ChatService,\n public readonly modalService: ModalService,\n public readonly notificationService: NotificationService,\n public readonly activatedRoute: ActivatedRoute,\n public readonly router: Router,\n public readonly dialogService: DialogService,\n public readonly tariffService: TariffService,\n public readonly audioService: AudioService,\n public readonly settingsService: SettingsService,\n private translate: TranslateService,\n ) {}\n\n ngOnInit() {\n this.isHasChatPer = this.baseService.isHasPermission(\n PERMISSIONS.CHATS_ACCESS,\n );\n this.isHasTeamPer = this.baseService.isHasPermission(\n PERMISSIONS.TEAM_ACCESS,\n );\n this.isHasSettingsPer = this.baseService.isHasPermission(\n PERMISSIONS.SETTINGS_ACCESS,\n );\n\n this.company = this.baseService.getCompanyObj();\n this.companyName = this.company.name;\n this.companySubdomain = this.company.subdomain;\n this.initForm();\n this.initNotification();\n\n this.notificationService\n .getCountNotification()\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n\n timer(0, 1000)\n .pipe(takeUntil(this.destroy$))\n .pipe(\n map(() => new Date()),\n share(),\n )\n .subscribe((date) => {\n this.rxDate = date;\n });\n // combineLatest(this.baseService.getProfile(), this.baseService.getCompanyInfo())\n // .pipe(take(1))\n // .subscribe({\n // next: ([adverts, company]) => {\n //\n // },\n // });\n if (this.chatIdQuery || typeof this.chatIdQuery === 'string') {\n if (!this.baseService.isHasPermission(PERMISSIONS.CHATS_ACCESS))\n this.router.navigate(['/crm/support']);\n else this.openChat();\n }\n\n if (this.dealIdQuery || typeof this.dealIdQuery === 'string') {\n if (!this.baseService.isHasPermission(PERMISSIONS.CRM_ACCESS))\n this.router.navigate(['/crm/support']);\n this.openDeal();\n }\n this.trackerDemo();\n }\n\n private initForm() {\n this.companyForm = new FormGroup({\n name: new FormControl(this.company?.name, Validators.required),\n subdomain: new FormControl(this.company?.subdomain, Validators.required),\n });\n\n this.settingsForm = new FormGroup({\n is_notification: new FormControl(\n this.baseService.currentUserInfo.settings.is_notification,\n ),\n is_notification_sound: new FormControl(\n this.baseService.currentUserInfo.settings.is_notification_sound,\n ),\n is_message_read: new FormControl(\n this.baseService.currentUserInfo.settings.is_message_read,\n ),\n is_lazy_loading: new FormControl(\n this.baseService.currentUserInfo.settings.is_lazy_loading,\n ),\n language: new FormControl(\n this.baseService.currentUserInfo.settings.language,\n ),\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public logout() {\n this.authService.logout();\n }\n\n public companies() {\n window.location.href =\n window.location.origin.replace(\n this.baseService?.currentCompanyInfo?.subdomain,\n 'auth',\n ) + '/companies';\n }\n\n public getTitle() {\n return this.company?.name;\n }\n\n private validateSubdomain(input: string) {\n const isLengthValid = input.length >= 1 && input.length <= 10;\n\n // Перевірка на допустимі символи (букви, цифри та дефіс)\n const hasValidCharacters = /^[a-z0-9\\-]+$/.test(input);\n\n // Перевірка на закінчення дефісом\n const doesNotEndWithDash = !input.endsWith('-');\n\n // Перевірка на входження в спеціальні домени\n const isNotSpecialDomain = !environment.specialDomains.includes(input);\n\n // Повертаємо загальний результат перевірок\n return (\n isLengthValid &&\n hasValidCharacters &&\n doesNotEndWithDash &&\n isNotSpecialDomain\n );\n // return !(input.length < 1 || input.length > 10 || !/^[a-z\\-]+$/.test(input) || input.endsWith('-') || environment.specialDomains.includes(input));\n }\n\n openChat() {\n this.rightSidebarService\n .openChats()\n .pipe(take(2))\n .subscribe(() => {\n this.chatService.loadCount();\n });\n }\n\n openDeal() {\n this.rightSidebarService\n .open('deal', {\n max: true,\n data: {\n card_id: this.dealIdQuery,\n },\n })\n .pipe(take(2))\n .subscribe(() => {\n this.chatService.loadCount();\n });\n }\n\n public submitCompany() {\n this.formSubmitted = true;\n if (this.companyForm.valid) {\n const data = {\n name: this.companyForm.controls.name.value,\n };\n this.companyService\n .updateCompany(data)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n window.location.reload();\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, DEFAULT_ERROR);\n });\n },\n });\n }\n }\n\n public submit() {\n this.formSubmitted = true;\n if (this.companyForm.valid) {\n const data = {\n name: this.companyForm.controls.name.value,\n subdomain: this.companyForm.controls.subdomain.value,\n };\n if (!this.validateSubdomain(data.subdomain)) {\n this.companyForm.controls.subdomain.setErrors({});\n return;\n }\n this.companyService\n .updateCompany(data)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n const currentUrl = window.location.href;\n window.location.href = currentUrl.replace(\n this.companySubdomain,\n data.subdomain,\n );\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n }\n\n public isEditSubdomain = () => {\n const targetDate = new Date(this.company.created_at);\n const currentDate = new Date();\n const timeDifference = currentDate.getTime() - targetDate.getTime();\n const threeDaysInMilliseconds = 3 * 24 * 60 * 60 * 1000; // 3 дні у мілісекундах\n return !(timeDifference >= threeDaysInMilliseconds);\n };\n\n public themes = () => {\n this.modalService.openThemes();\n };\n\n public profile = () => {\n this.router.navigate(['/crm/settings/profile']);\n };\n\n public settings = () => {\n this.router.navigate(['/crm/settings/general']);\n };\n\n protected readonly environment = environment;\n protected readonly Date = Date;\n\n getRandomQuote() {\n const quote = quotes[Math.floor(Math.random() * quotes.length)];\n this.currentQuote = quote;\n return quote;\n }\n\n public initNotification() {\n this.notificationLoader = true;\n this.notificationService\n .getUnreadNotification()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (notifications: any[]) => {\n this.notificationLoader = false;\n this.notifications = notifications;\n\n notifications.forEach((not) => {\n if (not.is_popup) {\n const modalData = {\n title: 'change_price',\n component: NotificationPopupComponent,\n translate: true,\n hideHeader: true,\n disableClose: true,\n backdropClass: 'my-backdrop',\n // .cdk-overlay-dark-backdrop\n data: {\n notification: not,\n }\n };\n this.modalService.openModal(modalData)\n .subscribe()\n }\n });\n },\n error: (r) => {\n console.log(r)\n this.notificationLoader = false;\n },\n });\n }\n\n onReadAll() {\n this.notificationService\n .readAllNotification()\n .pipe(takeUntil(this.destroy$))\n .pipe(switchMap(() => this.notificationService.getCountNotification()))\n .subscribe({\n next: (data) => {\n this.initNotification();\n },\n error: () => {},\n });\n }\n\n read(item: any) {\n if (!item.is_read) {\n this.notificationService\n .readNotification(item.id)\n .pipe(takeUntil(this.destroy$))\n .pipe(switchMap(() => this.notificationService.getCountNotification()))\n .subscribe({\n next: (data) => {\n this.initNotification();\n },\n error: () => {},\n });\n }\n }\n\n onCopy() {}\n\n startDemo(event: any) {\n if (\n this.baseService.currentCompanyInfo?.tariff === 'free' &&\n !this.baseService.currentCompanyInfo.tariff_updated_at\n ) {\n // event.stopPropagation();\n event.preventDefault();\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.header`,\n });\n\n dialogRef.subscribe((res) => {\n if (res) {\n this.tariffService.activeTrial({}).subscribe();\n window.location.reload();\n }\n });\n }\n }\n\n trackerDemo() {\n if (\n differenceDate(\n this.baseService.currentCompanyInfo.tariff_updated_at,\n new Date(),\n ) > this.baseService.currentCompanyInfo.config.trial_days\n )\n return;\n this.trialDays =\n this.baseService.currentCompanyInfo.config.trial_days -\n Math.ceil(\n differenceDate(\n this.baseService.currentCompanyInfo.tariff_updated_at,\n new Date(),\n ),\n );\n }\n\n openTeam() {\n this.router.navigate(['/crm/team']);\n }\n\n goToPayment() {\n this.router.navigate(['/crm/payment']);\n }\n\n switchLanguage() {\n this.settingsService\n .updateSettings(this.settingsForm.getRawValue())\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe({\n next: (res) => {\n window.location.reload();\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', err.message);\n });\n },\n });\n }\n\n goToDashboard() {\n this.router.navigate(['/crm/dashboard']);\n }\n\n protected readonly String = String;\n}\n","\n \n menu\n \n\n
\n
\n\n \n tune\n \n\n \n\n \n {{ rxDate | date : 'HH:mm:ss' }}\n {{ currentQuote.text }}\n \n\n
\n \n
\n\n \n\n \n \n 0 ? tarifMenu : null\"\n [ngClass]=\"{ bgTrial: trialDays <= 2 }\"\n #menuTrigger=\"matMenuTrigger\"\n class=\"d-flex align-items-center justify-content-around click-css\"\n style=\"\n background-color: #ea5728;\n height: 36px;\n border-radius: 4px;\n width: 250px;\n \"\n >\n rocket\n {{ 'your_tariff' | translate }}: Demo\n {{ 'your_tariff' | translate }}: Demo\n 0\"\n >keyboard_arrow_up\n \n 0\"\n >keyboard_arrow_down\n \n \n\n \n rocket\n {{ 'try_demo' | translate }}\n \n\n \n rocket\n {{ 'buy_tariff' | translate }}\n \n \n \n\n \n 0\"\n class=\"d-flex align-items-center px-3 py-2 gap-2 overflow-hidden\"\n >\n До кінця демо режиму залишилось {{ trialDays }} дня(-ів)\n \n \n Це останній день демо режиму\n \n \n\n
\n \n
\n \n \n
\n {{\n baseService.currentUserInfo.first_name\n }}\n keyboard_arrow_up\n keyboard_arrow_down\n
\n\n \n
\n
\n \n \n
\n
\n \n {{ baseService.currentUserInfo.first_name }}\n {{ baseService.currentUserInfo.last_name }}\n \n
\n
\n\n \n\n \n \n \n \n \n \n
\n language\n {{ 'language' | translate }}\n
\n \n \n \n \n \n \n \n\n \n \n \n \n\n \n
\n

\n {{ 'here_you_can_enter/change_your_company_name' | translate }}.\n

\n \n
\n \n \n \n {{\n 'the_selected_subdomain_may_already_be_taken._Please_select_another_subdomain_up_to_10_characters_using_only_lowercase-Latin_letters_and_a_hyphen._The_hyphen_cannot_be_the_last_character_of_the_subdomain'\n | translate\n }}.\n \n
\n
\n \n {{\n \"you_can_change_the_company's_subdomain_only_for_the_first_3_days\"\n | translate\n }}.\n \n
\n
\n\n
\n {{ 'your_address' | translate }}:\n {{ companyForm.controls.subdomain.value }}.{{ environment.web_url }}\n
\n\n
\n warning\n \n \n {{\n 'the_selected_subdomain_may_already_be_taken._Please_select_another_subdomain_up_to_10_characters_using_only_lowercase-Latin_letters_and_a_hyphen._The_hyphen_cannot_be_the_last_character_of_the_subdomain'\n | translate\n }}.\n \n
\n
\n {{\n 'save' | translate\n }}\n \n {{ 'save' | translate }}\n \n
\n\n \n
\n\n
\n \n 99 ? '99+': String(notificationService.notificationsCount)\">\n notifications\n \n \n
\n\n
\n \n 99 ? '99+' : String(chatService.message_count)\">\n chat\n \n \n
\n\n
\n\n\n
\n
\n
\n {{ 'notifications' | translate }}\n ({{ notificationService.notificationsCount }})\n
\n
{{ 'all' | translate }}
\n
\n \n mark_chat_read\n
\n
\n \n\n
\n \n
\n manage_accounts\n
\n
\n
{{ item.title }}
\n
{{ item.message }}
\n
{{ item.created_at | dateAgo }}
\n
\n
\n
\n circle\n
\n
\n
\n {{ 'all_notification_read' | translate }}\n
\n \n \n
\n","import { Component } from '@angular/core';\nimport { SidebarService } from '../../../services/sidebar.service';\nimport { AuthService } from '../../../services/auth.service';\nimport { BaseService } from '../../../services/base.service';\nimport { OnlineStatusType } from 'ngx-online-status';\nimport { Router } from '@angular/router';\nimport { AudioService } from \"../../../services/audio.service\";\n\n@Component({\n selector: 'app-footer',\n templateUrl: './footer.component.html',\n styleUrls: ['./footer.component.scss']\n})\nexport class FooterComponent {\n constructor(\n public readonly audioService: AudioService,\n public readonly baseService: BaseService,\n ) {\n }\n}\n","
\n \n
\n","export interface MenuItem {\n is_mat_icon: boolean;\n icon: string;\n key: string;\n entity_type?: string;\n hidden?: boolean;\n}\n\nexport enum LEFT_MENU {\n \"SETTINGS\" = \"SETTINGS\",\n}\n\nexport enum RIGHT_MENU {\n \"ABOUT\" = \"ABOUT\",\n}\n","import { Injectable } from '@angular/core';\nimport { catchError, from, Observable, of, switchMap } from 'rxjs';\nimport { NotificationService } from './notifications.service';\nimport { TranslateService } from '@ngx-translate/core';\nimport { MAX_CACHE_SIZE_MB } from '../enum/constants';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class CacheService {\n constructor(\n private readonly notificationService: NotificationService,\n private readonly translate: TranslateService,\n ) {\n }\n\n getCachedOrFetchBlob(url: string, fetch$: Observable, name: string): Observable {\n return from(caches.open(name)).pipe(\n switchMap((cache) =>\n from(cache.match(url)).pipe(\n switchMap((cachedResponse) => {\n if (cachedResponse) {\n return from(cachedResponse.blob());\n } else {\n return fetch$.pipe(\n switchMap((blob) => {\n const blobSizeMB = blob.size / (1024 * 1024);\n\n if (blobSizeMB >= MAX_CACHE_SIZE_MB) {\n return of(blob);\n }\n\n const responseToCache = new Response(blob);\n\n return from(cache.put(url, responseToCache)).pipe(\n switchMap(() => of(blob)),\n );\n }),\n );\n }\n }),\n ),\n ),\n catchError(() => of(null)),\n );\n }\n\n clearCache(name: string) {\n from(caches.delete(name)).subscribe({\n next: (success) => {\n if (success) {\n this.notificationService.showSuccess(\n this.translate.instant('notifications_texts.success.success'),\n this.translate.instant('notifications_texts.success.successfully_cleared'),\n );\n } else {\n this.notificationService.showWarning(\n this.translate.instant('notifications_texts.warn.warn'),\n this.translate.instant('notifications_texts.warn.no_cache_to_clear'),\n );\n }\n },\n error: () => {\n this.notificationService.showError(\n this.translate.instant('notifications_texts.error.error'),\n this.translate.instant('notifications_texts.error.something_went_wrong_1'),\n );\n },\n });\n }\n}\n","import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n@Component({\n selector: 'app-search',\n templateUrl: './search.component.html',\n styleUrls: ['./search.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => SearchComponent),\n },\n ],\n})\nexport class SearchComponent implements OnInit, ControlValueAccessor {\n @ViewChild('inputRef', { static: false }) inputRef!: ElementRef;\n @Input() searchType: 'default' | 'transparent' | 'cancel' | 'chats' = 'default';\n @Input() label: string = '';\n @Input() customClass: string = '';\n // @Input() placeholder: string = 'Пошук...';\n @Input() disabled: boolean = false;\n @Input() autoFocus: boolean = false;\n @Input() clearVisibility: boolean = false;\n @Input() loading: boolean = false;\n @Input() arrows: boolean = false;\n @Input() input: string = '';\n @Output() onChangeClick = new EventEmitter();\n @Output() onChangeSave = new EventEmitter();\n @Output() onChangeCancel = new EventEmitter();\n @Output() onValueChange = new EventEmitter();\n @Output() onClearChange = new EventEmitter();\n @Output() onArrowUpClick = new EventEmitter();\n @Output() onArrowDownClick = new EventEmitter();\n copyInput: string = '';\n\n ngOnInit(): void {\n if (this.autoFocus) {\n setTimeout(() => this.inputRef.nativeElement.focus());\n }\n }\n\n onChange = (data: any) => {};\n onTouch = (_: any) => {};\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: string) {\n this.input = value;\n this.onChange(value);\n }\n\n /**\n *Func triggered on input change\n */\n onchange(event: any) {\n if (event.trim() !== '' || event === '') {\n this.input = event;\n this.onChange(event);\n this.onValueChange.emit(event);\n }\n }\n\n clear() {\n this.onClearChange.emit();\n this.onchange('');\n }\n\n protected keepFocus(event: MouseEvent) {\n event.preventDefault();\n this.inputRef.nativeElement.focus();\n }\n\n protected arrowUpClick(event: MouseEvent) {\n event.stopPropagation();\n this.inputRef.nativeElement.blur();\n this.onArrowUpClick.emit();\n }\n\n protected arrowDownClick(event: MouseEvent) {\n event.stopPropagation();\n this.inputRef.nativeElement.blur();\n this.onArrowDownClick.emit();\n }\n}\n","\n \n \n \n\n \n \n \n\n \n \n \n\n \n
\n \n search\n \n \n \n
\n keyboard_arrow_up\n keyboard_arrow_down\n
\n close\n
\n
\n
\n\n\n\n","import { Component, Input, OnInit, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';\nimport { isCurrentWeek, isToday, isYesterday } from '../../../../../helpers';\nimport { ATTACHMENTS_TYPES, CHAT_TYPES, DEFAULT_PROFILE_ICON } from '../../../../../enum/constants';\nimport { IChat } from '../../../../../interfaces';\nimport { CHAT_TABS, IMenuChat } from '../chat-list/chat-list.interface';\nimport { ChatService } from '../../../../../services/chat.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { Overlay, OverlayRef } from '@angular/cdk/overlay';\n\n@Component({\n selector: 'app-chat-list-item',\n templateUrl: './chat-list-item.component.html',\n styleUrl: './chat-list-item.component.scss'\n})\nexport class ChatListItemComponent implements OnInit {\n @ViewChild('menu') menuTemplate!: TemplateRef;\n @Input() chat!: IChat;\n @Input() selected!: boolean;\n private overlayRef!: OverlayRef;\n protected readonly entityIcons: Record = {\n [CHAT_TYPES.OLX]: '/assets/svgs/integrations/olx_off_color.svg',\n [CHAT_TYPES.AUTO_RIA_WH]: '/assets/svgs/integrations/autoria_old_off_color.png',\n [CHAT_TYPES.DOM_RIA_WH]: '/assets/svgs/integrations/dimria_old_off_color.png',\n [CHAT_TYPES.AGRO_RIA_WH]: '/assets/svgs/integrations/agroria_old_off_color.png',\n [CHAT_TYPES.RIA_WH]: '/assets/svgs/integrations/ria_old_off_color.png',\n [CHAT_TYPES.AUTO_RIA]: '/assets/svgs/integrations/autoria_off_color.png',\n [CHAT_TYPES.DOM_RIA]: '/assets/svgs/integrations/dimria_off_color.png',\n [CHAT_TYPES.AGRO_RIA]: '/assets/svgs/integrations/agroria_off_color.png',\n [CHAT_TYPES.RIA]: '/assets/svgs/integrations/ria_off_color.png',\n [CHAT_TYPES.TELEGRAM_USER]: '/assets/svgs/integrations/telegram.svg',\n [CHAT_TYPES.TELEGRAM_BOT]: '/assets/svgs/integrations/telegram_bot.png',\n };\n protected chatMenus: Array = [];\n\n constructor(\n private readonly chatService: ChatService,\n private readonly overlay: Overlay,\n private readonly viewContainerRef: ViewContainerRef,\n protected readonly baseService: BaseService,\n ) {}\n\n ngOnInit(): void {\n this.initChatMenus();\n this.isAttachmentsType = Object.keys(ATTACHMENTS_TYPES).some(v => v === this.chat?.last_message);\n }\n\n private initChatMenus() {\n this.chatMenus = [];\n\n if (this.chat.status === CHAT_TABS.ACTIVE) {\n this.chatMenus.push({ title: 'close', icon: 'archive', action: () => this.chatService.onMoveChat(this.chat.id, CHAT_TABS.CLOSED) });\n } else {\n this.chatMenus.push({ title: 'open', icon: 'unarchive', action: () => this.chatService.onMoveChat(this.chat.id, CHAT_TABS.ACTIVE) });\n }\n\n if (this.chat.is_favorite) {\n this.chatMenus.push({ title: 'unpin', icon: 'unpin', is_svg: true, action: () => this.onChangePin(false) });\n } else {\n this.chatMenus.push({ title: 'pin', icon: 'pin', is_svg: true, action: () => this.onChangePin(true) })\n }\n\n if (this.chat.entity_type === CHAT_TYPES.OLX) {\n this.chatMenus.push({\n title: this.chat.is_blocked ? 'unblock' : 'block',\n icon: this.chat.is_blocked ? 'do_not_disturb_off' : 'do_not_disturb_on',\n action: () => this.onBlockUser()\n })\n }\n\n // TODO: Implements it next sprint\n // if (!this.chat.is_blocked) {\n // this.chatMenus.push({ title: 'mute', icon: 'notifications_off', action: () => {} })\n // } else {\n // this.chatMenus.push({ title: 'unmute', icon: 'notifications', action: () => {} })\n // }\n\n if (this.chat.is_mark_unread || this.chat.unread_count) {\n this.chatMenus.push({ title: 'read', icon: 'mark_chat_read', action: () => this.onReadChat() });\n } else {\n this.chatMenus.push({ title: 'mark_as_unread', icon: 'mark_chat_unread', action: () => this.markAsUnread() });\n }\n }\n\n private onReadChat() {\n this.chatService.readChat(this.chat)\n .subscribe();\n }\n\n private markAsUnread() {\n this.chatService.updateChat(this.chat.id, { is_mark_unread: true })\n .subscribe();\n }\n\n private onChangePin(pin: boolean) {\n this.chatService.updateChat(this.chat.id, { is_favorite: pin })\n .subscribe();\n }\n\n private onBlockUser() {\n if (this.chat.is_blocked) {\n this.chatService.unblockUser(this.chat.data.interlocutor_id);\n } else {\n this.chatService.blockUser(this.chat.channel_id, this.chat.data.interlocutor_id)\n .subscribe();\n }\n }\n\n // protected openMenu(event: TouchEvent): void {\n // event.stopPropagation();\n // console.log(\"start\");\n // if (!this.overlayRef) {\n // this.overlayRef = this.overlay.create({\n // positionStrategy: this.overlay\n // .position()\n // .flexibleConnectedTo({ x: event.touches[0].clientX, y: event.touches[0].clientY })\n // .withPositions([\n // { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'top' }\n // ]),\n // hasBackdrop: true,\n // backdropClass: 'cdk-overlay-transparent-backdrop'\n // });\n //\n //\n // this.overlayRef.backdropClick().subscribe(() => this.overlayRef.dispose());\n // document.addEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n //\n // }\n //\n // this.overlayRef.attach(new TemplatePortal(this.menuTemplate, this.viewContainerRef));\n // }\n // closeMenuOnOutsideClick(event: MouseEvent): void {\n // console.log(\"start\");\n //\n // const targetElement = event.target as HTMLElement;\n //\n // if (!this.overlayRef.overlayElement.contains(targetElement)) {\n // this.overlayRef.dispose();\n // this.overlayRef = null!;\n // document.removeEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n // }\n // }\n\n protected readonly isToday = isToday;\n protected readonly DEFAULT_PROFILE_ICON = DEFAULT_PROFILE_ICON;\n protected readonly isCurrentWeek = isCurrentWeek;\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly isYesterday = isYesterday;\n\n openMenu($event: MouseEvent) {\n\n }\n protected isAttachmentsType = false;\n\n protected readonly ATTACHMENTS_TYPES = ATTACHMENTS_TYPES;\n\n protected readonly Array = Array;\n protected readonly Object = Object;\n}\n","
\n \n
\n
\n \n \n block\n \n \"channel\"\n
\n
\n
\n
\n {{ chat.title }}\n
\n \n
\n
\n {{ chat.updated_at | date: 'HH:mm' }}\n {{ 'yesterday' | translate }}\n {{ chat.updated_at | date: 'EE' }}\n {{ chat.updated_at | date: 'dd.MM.YY' }}\n
\n
\n
\n {{ 'you' | translate }}:\n AI:\n \n {{ 'attachments.'+ chat.last_message.toLowerCase() | translate }}\n \n \n {{ chat.last_message }}\n \n
\n
\n \n \n \n
{{ chat.unread_count || '' }}
\n
\n
\n
\n \n more_vert\n \n
\n
\n\n
\n \n
\n
\n\n\n","import {\n AfterViewInit,\n Component,\n Input,\n OnChanges,\n OnDestroy,\n OnInit, Renderer2,\n SimpleChanges,\n} from '@angular/core';\nimport { ChatService } from '../../../../../services/chat.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { CHAT_SORT, CHAT_TABS, IMenuButton } from './chat-list.interface';\nimport { MatButtonToggleChange } from '@angular/material/button-toggle';\nimport { debounceTime, Subject } from 'rxjs';\nimport { LEFT_MENU, MenuItem } from '../../chat-sidebar.interface';\nimport { CHAT_MEDIA_CACHE_NAME } from '../../../../../enum/constants';\nimport { CacheService } from '../../../../../services/cache.service';\n\n@Component({\n selector: 'app-chat-list',\n templateUrl: './chat-list.component.html',\n styleUrl: './chat-list.component.scss',\n})\nexport class ChatListComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {\n @Input() selectedTabKey!: string;\n private readonly destroy$: Subject = new Subject();\n private searchTerms = new Subject();\n private leftMenu!: HTMLElement | null;\n protected readonly menus: Array = [\n { key: 'search', title: 'search', icon: 'search', action: () => this.toggleSearch() },\n { key: 'reset', title: 'update_chats', icon: 'refresh', action: () => this.chatService.resetAndInitChatList() },\n { key: 'sort', title: 'sort.sort', icon: 'sort' },\n { key: 'tabs', title: 'tabs.chats', icon: 'forum' },\n { key: 'settings', title: 'settings', icon: 'settings', action: () => this.openSettings()},\n { key: 'clear_media_cache', title: 'clear_media_cache', icon: 'cached', action: () => this.clearMediaCache()},\n ];\n protected readonly menuItems: MenuItem[] = this.chatService.menuItems;\n protected isSearch = false;\n\n constructor(\n private readonly renderer: Renderer2,\n private readonly cacheService: CacheService,\n protected readonly baseService: BaseService,\n protected readonly chatService: ChatService,\n ) {}\n\n ngOnInit() {\n this.initSearch();\n this.initMenu();\n this.chatService.initChats();\n }\n\n ngAfterViewInit() {\n this.leftMenu = document.getElementById('leftMenu');\n this.initChatListWidth();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes['selectedTabKey']) {\n this.chatService.resetAndInitChatList();\n }\n }\n\n ngOnDestroy() {\n this.chatService.chatsInfo.search_text = '';\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initChatListWidth(): void {\n const chatListWidth = localStorage.getItem('leftMenuWidth');\n if (chatListWidth && this.leftMenu) {\n this.leftMenu.style.setProperty('--left-menu-width', chatListWidth);\n }\n }\n\n private initSearch(): void {\n this.searchTerms.pipe(\n debounceTime(300)\n ).subscribe((text: string) => {\n this.chatService.resetChatsInfo();\n this.chatService.chatsInfo.search_text = text;\n this.chatService.initChats();\n });\n }\n\n private initMenu(): void {\n const selectedChatTab = localStorage.getItem('selectedChatTab');\n if (selectedChatTab) {\n this.chatService.selectedChatTab = selectedChatTab as CHAT_TABS;\n }\n const selectedChatSort = localStorage.getItem('selectedChatSort');\n if (selectedChatSort) {\n this.chatService.selectedChatSort = selectedChatSort as CHAT_SORT;\n }\n }\n\n protected isMenuButtonVisible(item: IMenuButton): boolean {\n let condition = true;\n if (item.title === 'search') condition = this.baseService.isMobile;\n if (item.title === 'clear_media_cache') condition = this.baseService.currentCompanyInfo.settings?.is_telegram_user;\n return condition;\n }\n\n private clearMediaCache() {\n this.cacheService.clearCache(CHAT_MEDIA_CACHE_NAME);\n }\n\n protected onSearch(text: string) {\n this.searchTerms.next(text);\n }\n\n protected scrollTo(event: any) {\n const chatsInfo = this.chatService.chatsInfo;\n if (this.chatService.isLoaderChatList.center || this.chatService.isLoaderChatList.down || this.chatService.isLoaderChatList.up) return;\n if (event.target.scrollHeight - event.target.scrollTop <= event.target.clientHeight + 700) {\n const candidatePage = (chatsInfo.loaded_pages[chatsInfo.loaded_pages.length - 1] || 0) + 1;\n if (candidatePage && chatsInfo.page_number < chatsInfo.page_count) {\n chatsInfo.page_number = candidatePage;\n this.chatService.initChats(true, { direction: 'down' }, true);\n }\n }\n if (event.target.scrollTop <= 350) {\n if (chatsInfo.loaded_pages.length) {\n const candidatePage = chatsInfo.loaded_pages[0] - 1;\n if (candidatePage >= 1) {\n chatsInfo.page_number = candidatePage;\n this.chatService.initChats(true, { direction: 'up', event_scroll: event }, true);\n }\n }\n }\n }\n\n protected onToggleChangeTab(event: MatButtonToggleChange) {\n this.chatService.selectedChatTab = event.value;\n this.chatService.resetAndInitChatList();\n localStorage.setItem('selectedChatTab', this.chatService.selectedChatTab)\n }\n\n protected onToggleChangeSort(event: MatButtonToggleChange) {\n this.chatService.selectedChatSort = event.value;\n this.chatService.resetAndInitChatList();\n localStorage.setItem('selectedChatSort', this.chatService.selectedChatSort)\n }\n\n protected onChangeTab(tabKey: string) {\n this.chatService.selectedTab = this.menuItems.find(menu => menu.key === tabKey) || this.menuItems[0];\n localStorage.setItem('selectedTabKey', this.chatService.selectedTab.key);\n }\n\n protected openSettings() {\n this.chatService.actionLeftMenu(LEFT_MENU.SETTINGS);\n }\n\n protected toggleSearch() {\n if (!this.chatService.chatsInfo.search_text) {\n this.isSearch = !this.isSearch;\n }\n }\n\n protected onMouseDown(event: MouseEvent) {\n event.preventDefault();\n const mouseMoveListener = this.renderer.listen('document', 'mousemove', (e) => this.onMouseMove(e));\n const mouseUpListener = this.renderer.listen('document', 'mouseup', () => {\n mouseMoveListener();\n mouseUpListener();\n });\n }\n\n protected onMouseMove(event: MouseEvent) {\n if (!this.leftMenu) return;\n const newWidth = event.clientX - this.leftMenu.getBoundingClientRect().left;\n if (newWidth < 0) return;\n this.leftMenu.style.setProperty('--left-menu-width', `${newWidth}px`);\n localStorage.setItem('leftMenuWidth', `${newWidth}px`);\n }\n\n protected readonly CHAT_TABS = CHAT_TABS;\n protected readonly CHAT_SORT = CHAT_SORT;\n}\n","
\n \n
\n \n menu\n \n \n \n
\n \n \n
\n \n
\n {{ item.icon }}\n \"chat\n
\n
\n
\n
\n \n
\n\n \n\n
\n
\n {{ 'no_dialog_found'| translate }}.\n
\n \n
\n\n \n\n
\n
\n\n\n
\n \n
\n
\n {{ item.icon }}\n {{ item.title | translate }}\n
\n\n
\n \n \n {{ 'sort.new' | translate }}\n \n \n {{ 'sort.unread' | translate }}\n \n \n
\n\n
\n \n \n {{ 'tabs.opened' | translate }}\n \n \n {{ 'tabs.closed' | translate }}\n \n \n
\n
\n
\n
\n
\n","import {\n Component,\n ElementRef,\n HostListener,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Renderer2,\n SimpleChanges,\n ViewChild,\n} from '@angular/core';\nimport {\n ATTACHMENTS_TYPES,\n CHAT_TYPES,\n DEFAULT_ERROR,\n DEFAULT_PROFILE_ICON,\n ERROR_MESSAGES,\n languages,\n MESSAGE_TYPES,\n UPLOAD_FILE_TYPES,\n} from '../../../../../enum/constants';\nimport { isCurrentWeek, isToday, isYesterday } from '../../../../../helpers';\nimport { ChatService } from '../../../../../services/chat.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { FormControl } from '@angular/forms';\nimport { debounceTime, Subject, takeUntil } from 'rxjs';\nimport { IMenuButton, ITempMessagePayload, MESSAGES_INFO_POSITION, UploadAttachments } from './chat.interface';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { FileUploadNewComponent } from '../../../../../modals/file-upload-new/file-upload-new.component';\nimport { RIGHT_MENU } from '../../chat-sidebar.interface';\nimport { IChat, IError } from '../../../../../interfaces';\nimport { Attachment, Message } from '../chat-message/chat-message.interface';\nimport { v4 } from 'uuid';\nimport { MessageTemplatesComponent } from '../../../../../bottom-sheet/message-templates/message-templates.component';\nimport { MatBottomSheet } from '@angular/material/bottom-sheet';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { Router } from '@angular/router';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\n\n@Component({\n selector: 'app-chat',\n templateUrl: './chat.component.html',\n styleUrl: './chat.component.scss',\n})\nexport class ChatComponent implements OnInit, OnChanges, OnDestroy {\n @ViewChild('chatInput') chatInput!: ElementRef;\n @ViewChild('messageContainer') messageContainer!: ElementRef;\n @Input() chat!: IChat;\n private readonly destroy$: Subject = new Subject();\n private searchTerms = new Subject();\n protected menuButtonsDesktop: IMenuButton[] = [\n { title: 'search', icon: 'search', action: () => this.toggleSearch() },\n { title: 'pinned_messages', icon: 'pinboard', action: () => this.togglePinned() },\n { title: 'about_chat', icon: 'info', action: () => this.openAbout() },\n ];\n protected menuButtons: IMenuButton[] = [\n { title: 'about_chat', icon: 'info', action: () => this.openAbout(), divider: true },\n { title: 'search', icon: 'search', action: () => this.toggleSearch() },\n { title: 'pinned_messages', icon: 'pinboard', action: () => this.togglePinned() },\n ];\n protected chatMenuButtons: IMenuButton[] = [\n { title: 'file', icon: 'description', action: () => this.openFileUpload(), key: 'upload' },\n { title: 'fast_reply', icon: 'reply', action: () => this.openTemplates(), key: 'fast_reply' },\n { title: 'auto_reply', icon: 'robot', is_svg: true, action: () => this.getAiAnswer(), key: 'auto_reply' },\n ];\n protected lastClickAction!: IMenuButton;\n protected textControl!: FormControl;\n protected editableMessage!: number | null;\n protected editableMessageText!: string | '';\n protected attachments: UploadAttachments[] = [];\n protected isResultContainer: boolean = false;\n protected isDragging = false;\n protected isFilesModalShown = false;\n\n constructor(\n private readonly renderer: Renderer2,\n private readonly notificationService: NotificationService,\n private readonly modalService: ModalService,\n private readonly matBottomSheet: MatBottomSheet,\n private readonly dialogService: DialogService,\n private readonly router: Router,\n private readonly rightSidebarService: RightSidebarService,\n protected readonly chatService: ChatService,\n protected readonly baseService: BaseService,\n ) {\n this.textControl = new FormControl('');\n }\n\n public ngOnInit() {\n const lastAction = this.chatMenuButtons.find(v => v.key === 'fast_reply');\n if (lastAction) this.lastClickAction = lastAction;\n if (localStorage.getItem('lastActionChat')) {\n const lastAction = this.chatMenuButtons.find(v => v.key === localStorage.getItem('lastActionChat'));\n if (lastAction) this.lastClickAction = lastAction;\n }\n this.initSearch();\n }\n\n public ngOnChanges(changes: SimpleChanges) {\n if (changes['chat']) {\n this.initChat();\n }\n }\n\n public ngOnDestroy() {\n this.chatService.selectedChatId = '';\n this.chatService.resetChat();\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initChat() {\n this.resetTextArea();\n this.onClearEdit();\n }\n\n private initSearch(): void {\n this.searchTerms.pipe(\n takeUntil(this.destroy$),\n debounceTime(300),\n ).subscribe((text: string) => {\n if (!text.trim()) return;\n this.chatService.resetChatSearchInfo();\n this.chatService.chatSearchInfo.search_text = text;\n this.chatService.searchChatMessages();\n });\n }\n\n protected onSearchScroll(event: any): void {\n const searchInfo = this.chatService.chatSearchInfo;\n if (searchInfo.loading) return;\n if (event.target.scrollHeight - event.target.scrollTop <= event.target.clientHeight + 400) {\n const candidatePage = searchInfo.page_number + 1;\n if (candidatePage <= searchInfo.page_count) {\n searchInfo.page_number = candidatePage;\n this.chatService.searchChatMessages(true);\n }\n }\n }\n\n protected toggleSearch() {\n const chatSearchInfo = this.chatService.chatSearchInfo;\n if (!chatSearchInfo.search_text) {\n chatSearchInfo.search_visible = !chatSearchInfo.search_visible;\n }\n }\n\n protected togglePinned() {\n const chatPinnedInfo = this.chatService.chatPinnedInfo;\n chatPinnedInfo.visible = !chatPinnedInfo.visible;\n }\n\n private openAbout() {\n this.chatService.actionRightMenu(RIGHT_MENU.ABOUT);\n }\n\n protected sendMessage(chat_id: string) {\n const text = (this.textControl.value).trim();\n if (!text && !this.attachments.length) return;\n this.sendMessageText(chat_id, text, { array: this.attachments as Attachment[] });\n this.resetTextArea();\n }\n\n private resetTextArea() {\n this.textControl.reset('');\n this.attachments = [];\n\n if (this.chatInput?.nativeElement) {\n this.chatInput.nativeElement.dispatchEvent(new Event('input'));\n }\n }\n\n private sendMessageText(chatId: string, text: string, attachments: { array: Attachment[] }) {\n const tempId: string = v4();\n const payload: ITempMessagePayload = {\n temp_id: tempId,\n chat_id: chatId,\n type_message: MESSAGE_TYPES.TEXT,\n text,\n attachments,\n };\n\n this.chatService.addTempMessage(payload);\n\n const body: Record = {\n text: text,\n };\n if (attachments.array.length) {\n body['attachments'] = attachments;\n }\n\n this.chatService.sendMessage(chatId, body)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: Message) => {\n this.chatService.replaceTempMessage(res, tempId);\n this.chatService.reloadChatList();\n },\n });\n }\n\n protected onMouseDown(event: MouseEvent) {\n event.preventDefault();\n\n const initialHeight = this.chatInput.nativeElement.offsetHeight;\n const initialY = event.clientY;\n\n const mouseMoveListener = this.renderer.listen('document', 'mousemove', (e) => this.onMouseMove(e, initialHeight, initialY));\n const mouseUpListener = this.renderer.listen('document', 'mouseup', () => {\n mouseMoveListener();\n mouseUpListener();\n });\n }\n\n protected onMouseMove(event: MouseEvent, initialHeight: number, initialY: number) {\n const deltaY = initialY - event.clientY;\n const newHeight = initialHeight + deltaY;\n\n if (newHeight > 500) return;\n\n this.renderer.setStyle(this.chatInput.nativeElement, 'height', 0);\n this.renderer.setStyle(this.chatInput.nativeElement, 'minHeight', `max(48px, min(${newHeight}px, 200px))`);\n }\n\n public pressCmdEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'cmd_enter' && !this.baseService.isMobile && !this.chat.is_blocked) {\n this.sendMessage(this.chat.id);\n }\n }\n\n public pressShiftEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'shift_enter' && !this.baseService.isMobile && !this.chat.is_blocked) {\n e.preventDefault();\n this.sendMessage(this.chat.id);\n }\n }\n\n public pressEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'enter' && !this.baseService.isMobile && !this.chat.is_blocked) {\n e.preventDefault();\n this.sendMessage(this.chat.id);\n }\n }\n\n protected openFileUpload(files?: File[]) {\n if (this.chatService.riaChatTypes.includes(this.chat.entity_type)) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.unable_to_transfer_files);\n return;\n }\n\n const maxUploadFiles = UPLOAD_FILE_TYPES[this.chat.entity_type]?.max_upload_files;\n const allowedTypes = UPLOAD_FILE_TYPES[this.chat.entity_type]?.allowed_types;\n\n if (maxUploadFiles && this.attachments.length >= maxUploadFiles) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.no_more_upload + UPLOAD_FILE_TYPES[this.chat.entity_type].max_upload_files + languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.files);\n return;\n }\n\n this.isFilesModalShown = true;\n\n this.modalService.openModal({\n translate: true,\n component: FileUploadNewComponent,\n title: 'upload_file',\n height: 'auto',\n data: {\n channel_type: this.chat.channel_type,\n files,\n length: this.attachments.length,\n max_upload_files: maxUploadFiles,\n allowed_types: allowedTypes,\n },\n }).subscribe((res: any[]) => {\n this.isFilesModalShown = false;\n\n if (res) {\n this.attachments.push(...res.map(v => ({\n url: v.link,\n mimeType: v.mimeType,\n type: ATTACHMENTS_TYPES.DOCUMENT,\n name: v.name,\n size: v.size,\n })));\n }\n });\n }\n\n protected onEditMessage(event: any) {\n this.textControl.setValue(event.text);\n this.editableMessage = event.id;\n this.editableMessageText = event.text;\n }\n\n protected onClearEdit() {\n this.textControl.reset('');\n this.editableMessage = null;\n this.editableMessageText = '';\n }\n\n protected scrollTo(event: any) {\n const scrollDistancePx = event.target.scrollHeight * 0.2;\n const scrollTop = Math.abs(event.target.scrollTop);\n if (scrollTop + event.target.clientHeight >= event.target.scrollHeight - scrollDistancePx) {\n this.loadDownChats(event);\n }\n if (scrollTop <= scrollDistancePx) {\n this.loadUpChats(event);\n }\n }\n\n protected scrollBottom(messageId?: string) {\n if (!messageId) return;\n\n this.chatService.findMessage(messageId, { behavior: 'smooth', is_highlighted: false });\n }\n\n protected isBtnBottomShow(): boolean {\n return Math.abs(this.messageContainer?.nativeElement.scrollTop) > 300 ||\n !this.chatService.hasMessagesInfoItem(MESSAGES_INFO_POSITION.FIRST)\n }\n\n protected loadUpChats(event?: any) {\n if (this.chatService.isLoaderChat.down || this.chatService.isLoaderChat.up) return;\n\n if (!this.chatService.hasMessagesInfoItem(MESSAGES_INFO_POSITION.FIRST)) {\n this.chatService.loadChatMessages(true, { direction: 'up', event }, true);\n }\n }\n\n protected loadDownChats(event?: any) {\n if (this.chatService.isLoaderChat.down || this.chatService.isLoaderChat.up) return;\n\n if (!this.chatService.hasMessagesInfoItem(MESSAGES_INFO_POSITION.LAST)) {\n this.chatService.loadChatMessages(true, { direction: 'down', event }, true);\n }\n }\n\n protected onResendMessage(event: { text: string, attachments: { array: Attachment[] } }) {\n return this.sendMessageText(this.chat.id, event.text, event.attachments);\n }\n\n protected onSearch(text: string) {\n if (!text.trim()) this.chatService.resetChatSearchInfo();\n this.searchTerms.next(text);\n }\n\n protected onSearchFocusIn() {\n this.isResultContainer = true;\n }\n\n protected onSearchFocusOut() {\n this.isResultContainer = false;\n }\n\n protected addEmoji(event: any) {\n const emoji = event.emoji.native;\n const inputElement = this.chatInput.nativeElement;\n if (!inputElement) {\n return;\n }\n const start = inputElement.selectionStart;\n const end = inputElement.selectionEnd;\n const text = this.textControl.value;\n\n const newText = text.substring(0, start) + emoji + text.substring(end);\n\n this.textControl.setValue(newText);\n\n setTimeout(() => {\n inputElement.setSelectionRange(start + emoji.length, start + emoji.length);\n }, 0);\n }\n\n protected selectSearchMessage(message_id: string, index: number) {\n this.chatService.chatSearchInfo.selected_number = index;\n const searchInput: HTMLInputElement | null = document.querySelector('.chat-header app-search input');\n if (searchInput) {\n searchInput.blur();\n }\n this.chatService.findMessage(message_id);\n }\n\n protected arrowSearch(direction: 'up' | 'down') {\n const chatSearchInfo = this.chatService.chatSearchInfo;\n if (!chatSearchInfo.items) return;\n\n let number = 0;\n if (chatSearchInfo.selected_number || chatSearchInfo.selected_number === 0) {\n if (direction === 'up' && chatSearchInfo.selected_number + 1 < chatSearchInfo.items.length) {\n chatSearchInfo.selected_number = chatSearchInfo.selected_number + 1;\n } else if (direction === 'down' && chatSearchInfo.selected_number - 1 >= 0) {\n chatSearchInfo.selected_number = chatSearchInfo.selected_number - 1;\n }\n number = chatSearchInfo.selected_number;\n } else chatSearchInfo.selected_number = number;\n\n const message_id = chatSearchInfo.items[number].id;\n this.chatService.findMessage(message_id);\n }\n\n protected deleteFile(index: number) {\n this.attachments.splice(index, 1);\n }\n\n protected isMenuButtonVisible(item: IMenuButton): boolean {\n let condition = true;\n if (item.title === 'pinned_messages') condition = !!this.chatService.chatPinnedInfo.items.length;\n if (item.title === 'search') condition = !this.chatService.oldRiaChatTypes.includes(this.chat.entity_type);\n return condition;\n }\n\n private openTemplates() {\n const bottomRef = this.matBottomSheet.open(MessageTemplatesComponent, { hasBackdrop: true });\n bottomRef.afterDismissed()\n .pipe(takeUntil(this.destroy$))\n .subscribe((res) => {\n if (res) {\n this.textControl.setValue(this.textControl.value ? this.textControl.value + `\\n${res.text}` : res.text);\n this.chatInput.nativeElement.dispatchEvent(new Event('input'));\n }\n });\n }\n\n private getAiAnswer() {\n const isAi = this.baseService.currentCompanyInfo.application_keys.includes('GEN_AI');\n\n if (!isAi) {\n const dialogRef = this.dialogService.openConfirm({\n text: 'buy_ai',\n });\n dialogRef.subscribe((res) => {\n if (res) {\n this.router.navigate(['/crm/marketplace']);\n this.rightSidebarService.closeChats();\n }\n });\n } else {\n this.chatService.getAiAnswer(this.chatService.chat.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n if (res) {\n this.animateTextInput(res, this.chatInput.nativeElement);\n this.baseService.getCompanyInfo()\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n }\n },\n error: e => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n }\n\n private animateTextInput(text: string, element: HTMLTextAreaElement) {\n this.resetTextArea();\n let index = 0;\n const interval = setInterval(() => {\n if (index < text.length) {\n element.value += text[index];\n element.dispatchEvent(new Event('input'));\n index++;\n } else {\n clearInterval(interval);\n this.textControl.setValue(text);\n }\n }, 15);\n }\n\n protected saveLastItem(item: IMenuButton) {\n this.lastClickAction = item;\n if (item.key) localStorage.setItem('lastActionChat', item.key);\n return item;\n }\n\n @HostListener('window:paste', ['$event'])\n eventPaste(e: ClipboardEvent) {\n if (this.isFilesModalShown || this.chatService.oldRiaChatTypes.includes(this.chat?.entity_type) || !e.clipboardData) return;\n\n const files = Array\n .from(e.clipboardData.items)\n .map((v: DataTransferItem) => v.getAsFile())\n .filter((v): v is File => !!v);\n\n if (files?.length) {\n this.openFileUpload(files);\n e.preventDefault();\n }\n }\n\n @HostListener('drop', ['$event'])\n onDrop(event: DragEvent) {\n event.preventDefault();\n event.stopPropagation();\n this.isDragging = false;\n\n if (event.dataTransfer?.files?.length) {\n const files = Array.from(event.dataTransfer.files);\n this.openFileUpload(files);\n }\n }\n\n @HostListener('dragover', ['$event'])\n onDragOver(event: DragEvent) {\n event.preventDefault();\n event.stopPropagation();\n this.isDragging = true;\n }\n\n @HostListener('dragleave', ['$event'])\n onDragLeave(event: DragEvent) {\n event.preventDefault();\n event.stopPropagation();\n this.isDragging = false;\n }\n\n protected readonly isToday = isToday;\n protected readonly isYesterday = isYesterday;\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly DEFAULT_PROFILE_ICON = DEFAULT_PROFILE_ICON;\n protected readonly isCurrentWeek = isCurrentWeek;\n protected readonly MESSAGES_INFO_POSITION = MESSAGES_INFO_POSITION;\n}\n","
\n
\n \n {{ 'select_chat' | translate }}\n \n \n
\n\n
\n
\n \n \n\n \n \n arrow_back\n \n\n \n \n\n \n \n
\n {{ chat.title }}\n {{ 'bot' | translate }}\n
\n \n {{ chatService.userInfo?.name || chatService.userInfo?.person || 'Client' }}\n \n \n {{ chatService.userInfo?.name }}\n \n
\n\n \n \n \n arrow_back\n \n {{ 'pinned_header' | translate: { count: chatService.chatPinnedInfo.items.length } }}\n \n\n
\n\n \n \n \n\n \n \n \n \n \n {{ item.icon }}\n \n \n \n \n more_vert\n \n \n
\n\n
\n
\n \n {{ 'no_messages' | translate }}\n \n\n \n
\n\n \n\n
\n
\n
\n\n \n arrow_downward\n \n\n
\n {{'upload_more'| translate}}...\n
\n\n
\n
{{ 'today' | translate }}
\n
{{ 'yesterday' | translate }}
\n
{{ group.date }}
\n \n \n \n
\n\n
\n {{'upload_more'| translate}}...\n
\n\n
\n \n
\n
\n\n \n
\n\n
\n
\n
{{ 'today' | translate }}
\n
{{ 'yesterday' | translate }}
\n
{{ group.date }}
\n\n \n \n \n
\n
\n\n
\n
\n
\n \n edit\n {{ this.editableMessageText }}\n
\n close\n
\n \n
\n\n
\n
\n
\n
\n \n
\n
\n \n \n delete\n \n
\n
\n \n
\n
\n \n mood\n \n \n \n \n \n \n \n \n \n {{ lastClickAction.icon }}\n \n \n add_2\n \n
\n
\n
\n
\n \n send\n \n
\n
\n
\n\n\n\n
\n \n
\n
\n {{ button.icon }}\n \n {{ button.title | translate }}\n
\n
\n
\n
\n
\n\n\n
\n \n
\n {{ item.icon }}\n {{ item.title | translate }}\n
\n
\n
\n
\n\n\n\n","import { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { map } from 'rxjs';\nimport { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class MessagesTemplatesService {\n\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n save(payload: { text: string }) {\n const url = environment.baseURL + '/mt/';\n return this.http.post(url, payload)\n .pipe(map(v => v.data));\n }\n\n update(id: string, payload: { text: string }) {\n const url = environment.baseURL + `/mt/${id}`;\n return this.http.put(url, payload)\n .pipe(map(v => v.data));\n }\n\n get() {\n const url = environment.baseURL + '/mt/';\n return this.http.get(url)\n .pipe(map(v => v.data));\n }\n\n delete(id: string) {\n const url = environment.baseURL + `/mt/${id}`;\n return this.http.delete(url)\n .pipe(map(v => v.data));\n }\n}\n","import { Component, forwardRef } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { MessagesTemplatesService } from '../../services/messages-templates.service';\nimport { MatBottomSheet, MatBottomSheetRef } from '@angular/material/bottom-sheet';\n\n\n@Component({\n selector: 'message-templates',\n templateUrl: './message-templates.component.html',\n styleUrls: ['./message-templates.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => MessageTemplatesComponent)\n }\n ]\n})\nexport class MessageTemplatesComponent {\n templates: any[] = [];\n\n\n constructor(\n private readonly messagesTemplatesService: MessagesTemplatesService,\n private readonly matBottomSheetRef: MatBottomSheetRef\n ) {}\n\n ngOnInit() {\n const element = document.getElementsByTagName('mat-bottom-sheet-container')[0];\n if (element) {\n element.classList.add('crm-scroll');\n }\n this.init();\n }\n\n private init() {\n this.messagesTemplatesService.get()\n .subscribe({\n next: (res) => {\n this.templates = res;\n\n }\n })\n }\n\n close(template: any) {\n this.matBottomSheetRef.dismiss(template)\n }\n}\n","
\n
\n
\n
{{'quick_replies' | translate}}
\n
\n
\n close\n
\n
\n
\n
\n \n
\n
\n check {{'save_an_already_sent_message_as_a_quick_reply_by_right-clicking_on_the_message' | translate}}\n
\n
\n check{{'create_a_new_one_in_chat_settings'| translate}}\n
\n\n
\n\n
\n
\n
{{template.text}}
\n \n
\n
\n
\n","import * as i0 from '@angular/core';\nimport { Injectable, EventEmitter, inject, NgZone, Component, ChangeDetectionStrategy, Input, Output, ViewChild, NgModule } from '@angular/core';\nimport { Subject, switchMap, fromEvent, EMPTY, takeUntil } from 'rxjs';\nimport * as i1 from '@angular/common';\nimport { CommonModule } from '@angular/common';\nconst _c0 = [\"button\"];\nconst _c1 = [\"*\", \"*\"];\nfunction EmojiComponent_ng_template_0_button_0_ng_template_3_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtext(0);\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext(3);\n i0.ɵɵtextInterpolate(ctx_r1.unified);\n }\n}\nfunction EmojiComponent_ng_template_0_button_0_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"button\", 4, 1);\n i0.ɵɵlistener(\"click\", function EmojiComponent_ng_template_0_button_0_Template_button_click_0_listener($event) {\n i0.ɵɵrestoreView(_r1);\n const ctx_r1 = i0.ɵɵnextContext(2);\n return i0.ɵɵresetView(ctx_r1.handleClick($event));\n });\n i0.ɵɵelementStart(2, \"span\", 5);\n i0.ɵɵtemplate(3, EmojiComponent_ng_template_0_button_0_ng_template_3_Template, 1, 1, \"ng-template\", 2);\n i0.ɵɵprojection(4);\n i0.ɵɵelementEnd()();\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext(2);\n i0.ɵɵclassProp(\"emoji-mart-emoji-native\", ctx_r1.isNative)(\"emoji-mart-emoji-custom\", ctx_r1.custom);\n i0.ɵɵattribute(\"title\", ctx_r1.title)(\"aria-label\", ctx_r1.label);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngStyle\", ctx_r1.style);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx_r1.isNative);\n }\n}\nfunction EmojiComponent_ng_template_0_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtemplate(0, EmojiComponent_ng_template_0_button_0_Template, 5, 8, \"button\", 3);\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext();\n const spanTpl_r3 = i0.ɵɵreference(2);\n i0.ɵɵproperty(\"ngIf\", ctx_r1.useButton)(\"ngIfElse\", spanTpl_r3);\n }\n}\nfunction EmojiComponent_ng_template_1_ng_template_3_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtext(0);\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext(2);\n i0.ɵɵtextInterpolate(ctx_r1.unified);\n }\n}\nfunction EmojiComponent_ng_template_1_Template(rf, ctx) {\n if (rf & 1) {\n const _r4 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"span\", 6, 1);\n i0.ɵɵlistener(\"click\", function EmojiComponent_ng_template_1_Template_span_click_0_listener($event) {\n i0.ɵɵrestoreView(_r4);\n const ctx_r1 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r1.handleClick($event));\n });\n i0.ɵɵelementStart(2, \"span\", 5);\n i0.ɵɵtemplate(3, EmojiComponent_ng_template_1_ng_template_3_Template, 1, 1, \"ng-template\", 2);\n i0.ɵɵprojection(4, 1);\n i0.ɵɵelementEnd()();\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext();\n i0.ɵɵclassProp(\"emoji-mart-emoji-native\", ctx_r1.isNative)(\"emoji-mart-emoji-custom\", ctx_r1.custom);\n i0.ɵɵattribute(\"title\", ctx_r1.title)(\"aria-label\", ctx_r1.label);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngStyle\", ctx_r1.style);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx_r1.isNative);\n }\n}\nconst categories = [{\n id: 'people',\n name: 'Smileys & People',\n emojis: ['1F600', '1F603', '1F604', '1F601', '1F606', '1F605', '1F923', '1F602', '1F642', '1F643', '1FAE0', '1F609', '1F60A', '1F607', '1F970', '1F60D', '1F929', '1F618', '1F617', '263A-FE0F', '1F61A', '1F619', '1F972', '1F60B', '1F61B', '1F61C', '1F92A', '1F61D', '1F911', '1F917', '1F92D', '1FAE2', '1FAE3', '1F92B', '1F914', '1FAE1', '1F910', '1F928', '1F610', '1F611', '1F636', '1FAE5', '1F636-200D-1F32B-FE0F', '1F60F', '1F612', '1F644', '1F62C', '1F62E-200D-1F4A8', '1F925', '1F60C', '1F614', '1F62A', '1F924', '1F634', '1F637', '1F912', '1F915', '1F922', '1F92E', '1F927', '1F975', '1F976', '1F974', '1F635', '1F635-200D-1F4AB', '1F92F', '1F920', '1F973', '1F978', '1F60E', '1F913', '1F9D0', '1F615', '1FAE4', '1F61F', '1F641', '2639-FE0F', '1F62E', '1F62F', '1F632', '1F633', '1F97A', '1F979', '1F626', '1F627', '1F628', '1F630', '1F625', '1F622', '1F62D', '1F631', '1F616', '1F623', '1F61E', '1F613', '1F629', '1F62B', '1F971', '1F624', '1F621', '1F620', '1F92C', '1F608', '1F47F', '1F480', '2620-FE0F', '1F4A9', '1F921', '1F479', '1F47A', '1F47B', '1F47D', '1F47E', '1F916', '1F44B', '1F91A', '1F590-FE0F', '270B', '1F596', '1FAF1', '1FAF2', '1FAF3', '1FAF4', '1F44C', '1F90C', '1F90F', '270C-FE0F', '1F91E', '1FAF0', '1F91F', '1F918', '1F919', '1F448', '1F449', '1F446', '1F595', '1F447', '261D-FE0F', '1FAF5', '1F44D', '1F44E', '270A', '1F44A', '1F91B', '1F91C', '1F44F', '1F64C', '1FAF6', '1F450', '1F932', '1F91D', '1F64F', '270D-FE0F', '1F485', '1F933', '1F4AA', '1F9BE', '1F9BF', '1F9B5', '1F9B6', '1F442', '1F9BB', '1F443', '1F9E0', '1FAC0', '1FAC1', '1F9B7', '1F9B4', '1F440', '1F441-FE0F', '1F445', '1F444', '1FAE6', '1F476', '1F9D2', '1F466', '1F467', '1F9D1', '1F471', '1F468', '1F9D4', '1F9D4-200D-2642-FE0F', '1F9D4-200D-2640-FE0F', '1F468-200D-1F9B0', '1F468-200D-1F9B1', '1F468-200D-1F9B3', '1F468-200D-1F9B2', '1F469', '1F469-200D-1F9B0', '1F9D1-200D-1F9B0', '1F469-200D-1F9B1', '1F9D1-200D-1F9B1', '1F469-200D-1F9B3', '1F9D1-200D-1F9B3', '1F469-200D-1F9B2', '1F9D1-200D-1F9B2', '1F471-200D-2640-FE0F', '1F471-200D-2642-FE0F', '1F9D3', '1F474', '1F475', '1F64D', '1F64D-200D-2642-FE0F', '1F64D-200D-2640-FE0F', '1F64E', '1F64E-200D-2642-FE0F', '1F64E-200D-2640-FE0F', '1F645', '1F645-200D-2642-FE0F', '1F645-200D-2640-FE0F', '1F646', '1F646-200D-2642-FE0F', '1F646-200D-2640-FE0F', '1F481', '1F481-200D-2642-FE0F', '1F481-200D-2640-FE0F', '1F64B', '1F64B-200D-2642-FE0F', '1F64B-200D-2640-FE0F', '1F9CF', '1F9CF-200D-2642-FE0F', '1F9CF-200D-2640-FE0F', '1F647', '1F647-200D-2642-FE0F', '1F647-200D-2640-FE0F', '1F926', '1F926-200D-2642-FE0F', '1F926-200D-2640-FE0F', '1F937', '1F937-200D-2642-FE0F', '1F937-200D-2640-FE0F', '1F9D1-200D-2695-FE0F', '1F468-200D-2695-FE0F', '1F469-200D-2695-FE0F', '1F9D1-200D-1F393', '1F468-200D-1F393', '1F469-200D-1F393', '1F9D1-200D-1F3EB', '1F468-200D-1F3EB', '1F469-200D-1F3EB', '1F9D1-200D-2696-FE0F', '1F468-200D-2696-FE0F', '1F469-200D-2696-FE0F', '1F9D1-200D-1F33E', '1F468-200D-1F33E', '1F469-200D-1F33E', '1F9D1-200D-1F373', '1F468-200D-1F373', '1F469-200D-1F373', '1F9D1-200D-1F527', '1F468-200D-1F527', '1F469-200D-1F527', '1F9D1-200D-1F3ED', '1F468-200D-1F3ED', '1F469-200D-1F3ED', '1F9D1-200D-1F4BC', '1F468-200D-1F4BC', '1F469-200D-1F4BC', '1F9D1-200D-1F52C', '1F468-200D-1F52C', '1F469-200D-1F52C', '1F9D1-200D-1F4BB', '1F468-200D-1F4BB', '1F469-200D-1F4BB', '1F9D1-200D-1F3A4', '1F468-200D-1F3A4', '1F469-200D-1F3A4', '1F9D1-200D-1F3A8', '1F468-200D-1F3A8', '1F469-200D-1F3A8', '1F9D1-200D-2708-FE0F', '1F468-200D-2708-FE0F', '1F469-200D-2708-FE0F', '1F9D1-200D-1F680', '1F468-200D-1F680', '1F469-200D-1F680', '1F9D1-200D-1F692', '1F468-200D-1F692', '1F469-200D-1F692', '1F46E', '1F46E-200D-2642-FE0F', '1F46E-200D-2640-FE0F', '1F575-FE0F', '1F575-FE0F-200D-2642-FE0F', '1F575-FE0F-200D-2640-FE0F', '1F482', '1F482-200D-2642-FE0F', '1F482-200D-2640-FE0F', '1F977', '1F477', '1F477-200D-2642-FE0F', '1F477-200D-2640-FE0F', '1FAC5', '1F934', '1F478', '1F473', '1F473-200D-2642-FE0F', '1F473-200D-2640-FE0F', '1F472', '1F9D5', '1F935', '1F935-200D-2642-FE0F', '1F935-200D-2640-FE0F', '1F470', '1F470-200D-2642-FE0F', '1F470-200D-2640-FE0F', '1F930', '1FAC3', '1FAC4', '1F931', '1F469-200D-1F37C', '1F468-200D-1F37C', '1F9D1-200D-1F37C', '1F47C', '1F385', '1F936', '1F9D1-200D-1F384', '1F9B8', '1F9B8-200D-2642-FE0F', '1F9B8-200D-2640-FE0F', '1F9B9', '1F9B9-200D-2642-FE0F', '1F9B9-200D-2640-FE0F', '1F9D9', '1F9D9-200D-2642-FE0F', '1F9D9-200D-2640-FE0F', '1F9DA', '1F9DA-200D-2642-FE0F', '1F9DA-200D-2640-FE0F', '1F9DB', '1F9DB-200D-2642-FE0F', '1F9DB-200D-2640-FE0F', '1F9DC', '1F9DC-200D-2642-FE0F', '1F9DC-200D-2640-FE0F', '1F9DD', '1F9DD-200D-2642-FE0F', '1F9DD-200D-2640-FE0F', '1F9DE', '1F9DE-200D-2642-FE0F', '1F9DE-200D-2640-FE0F', '1F9DF', '1F9DF-200D-2642-FE0F', '1F9DF-200D-2640-FE0F', '1F9CC', '1F486', '1F486-200D-2642-FE0F', '1F486-200D-2640-FE0F', '1F487', '1F487-200D-2642-FE0F', '1F487-200D-2640-FE0F', '1F6B6', '1F6B6-200D-2642-FE0F', '1F6B6-200D-2640-FE0F', '1F9CD', '1F9CD-200D-2642-FE0F', '1F9CD-200D-2640-FE0F', '1F9CE', '1F9CE-200D-2642-FE0F', '1F9CE-200D-2640-FE0F', '1F9D1-200D-1F9AF', '1F468-200D-1F9AF', '1F469-200D-1F9AF', '1F9D1-200D-1F9BC', '1F468-200D-1F9BC', '1F469-200D-1F9BC', '1F9D1-200D-1F9BD', '1F468-200D-1F9BD', '1F469-200D-1F9BD', '1F3C3', '1F3C3-200D-2642-FE0F', '1F3C3-200D-2640-FE0F', '1F483', '1F57A', '1F574-FE0F', '1F46F', '1F46F-200D-2642-FE0F', '1F46F-200D-2640-FE0F', '1F9D6', '1F9D6-200D-2642-FE0F', '1F9D6-200D-2640-FE0F', '1F9D7', '1F9D7-200D-2642-FE0F', '1F9D7-200D-2640-FE0F', '1F93A', '1F3C7', '26F7-FE0F', '1F3C2', '1F3CC-FE0F', '1F3CC-FE0F-200D-2642-FE0F', '1F3CC-FE0F-200D-2640-FE0F', '1F3C4', '1F3C4-200D-2642-FE0F', '1F3C4-200D-2640-FE0F', '1F6A3', '1F6A3-200D-2642-FE0F', '1F6A3-200D-2640-FE0F', '1F3CA', '1F3CA-200D-2642-FE0F', '1F3CA-200D-2640-FE0F', '26F9-FE0F', '26F9-FE0F-200D-2642-FE0F', '26F9-FE0F-200D-2640-FE0F', '1F3CB-FE0F', '1F3CB-FE0F-200D-2642-FE0F', '1F3CB-FE0F-200D-2640-FE0F', '1F6B4', '1F6B4-200D-2642-FE0F', '1F6B4-200D-2640-FE0F', '1F6B5', '1F6B5-200D-2642-FE0F', '1F6B5-200D-2640-FE0F', '1F938', '1F938-200D-2642-FE0F', '1F938-200D-2640-FE0F', '1F93C', '1F93C-200D-2642-FE0F', '1F93C-200D-2640-FE0F', '1F93D', '1F93D-200D-2642-FE0F', '1F93D-200D-2640-FE0F', '1F93E', '1F93E-200D-2642-FE0F', '1F93E-200D-2640-FE0F', '1F939', '1F939-200D-2642-FE0F', '1F939-200D-2640-FE0F', '1F9D8', '1F9D8-200D-2642-FE0F', '1F9D8-200D-2640-FE0F', '1F6C0', '1F6CC', '1F9D1-200D-1F91D-200D-1F9D1', '1F46D', '1F46B', '1F46C', '1F48F', '1F469-200D-2764-FE0F-200D-1F48B-200D-1F468', '1F468-200D-2764-FE0F-200D-1F48B-200D-1F468', '1F469-200D-2764-FE0F-200D-1F48B-200D-1F469', '1F491', '1F469-200D-2764-FE0F-200D-1F468', '1F468-200D-2764-FE0F-200D-1F468', '1F469-200D-2764-FE0F-200D-1F469', '1F46A', '1F468-200D-1F469-200D-1F466', '1F468-200D-1F469-200D-1F467', '1F468-200D-1F469-200D-1F467-200D-1F466', '1F468-200D-1F469-200D-1F466-200D-1F466', '1F468-200D-1F469-200D-1F467-200D-1F467', '1F468-200D-1F468-200D-1F466', '1F468-200D-1F468-200D-1F467', '1F468-200D-1F468-200D-1F467-200D-1F466', '1F468-200D-1F468-200D-1F466-200D-1F466', '1F468-200D-1F468-200D-1F467-200D-1F467', '1F469-200D-1F469-200D-1F466', '1F469-200D-1F469-200D-1F467', '1F469-200D-1F469-200D-1F467-200D-1F466', '1F469-200D-1F469-200D-1F466-200D-1F466', '1F469-200D-1F469-200D-1F467-200D-1F467', '1F468-200D-1F466', '1F468-200D-1F466-200D-1F466', '1F468-200D-1F467', '1F468-200D-1F467-200D-1F466', '1F468-200D-1F467-200D-1F467', '1F469-200D-1F466', '1F469-200D-1F466-200D-1F466', '1F469-200D-1F467', '1F469-200D-1F467-200D-1F466', '1F469-200D-1F467-200D-1F467', '1F5E3-FE0F', '1F464', '1F465', '1FAC2', '1F463', '1F63A', '1F638', '1F639', '1F63B', '1F63C', '1F63D', '1F640', '1F63F', '1F63E', '1F648', '1F649', '1F64A', '1F48B', '1F48C', '1F498', '1F49D', '1F496', '1F497', '1F493', '1F49E', '1F495', '1F49F', '2763-FE0F', '1F494', '2764-FE0F-200D-1F525', '2764-FE0F-200D-1FA79', '2764-FE0F', '1F9E1', '1F49B', '1F49A', '1F499', '1F49C', '1F90E', '1F5A4', '1F90D', '1F4AF', '1F4A2', '1F4A5', '1F4AB', '1F4A6', '1F4A8', '1F573-FE0F', '1F4A3', '1F4AC', '1F441-FE0F-200D-1F5E8-FE0F', '1F5E8-FE0F', '1F5EF-FE0F', '1F4AD', '1F4A4']\n}, {\n id: 'nature',\n name: 'Animals & Nature',\n emojis: ['1F435', '1F412', '1F98D', '1F9A7', '1F436', '1F415', '1F9AE', '1F415-200D-1F9BA', '1F429', '1F43A', '1F98A', '1F99D', '1F431', '1F408', '1F408-200D-2B1B', '1F981', '1F42F', '1F405', '1F406', '1F434', '1F40E', '1F984', '1F993', '1F98C', '1F9AC', '1F42E', '1F402', '1F403', '1F404', '1F437', '1F416', '1F417', '1F43D', '1F40F', '1F411', '1F410', '1F42A', '1F42B', '1F999', '1F992', '1F418', '1F9A3', '1F98F', '1F99B', '1F42D', '1F401', '1F400', '1F439', '1F430', '1F407', '1F43F-FE0F', '1F9AB', '1F994', '1F987', '1F43B', '1F43B-200D-2744-FE0F', '1F428', '1F43C', '1F9A5', '1F9A6', '1F9A8', '1F998', '1F9A1', '1F43E', '1F983', '1F414', '1F413', '1F423', '1F424', '1F425', '1F426', '1F427', '1F54A-FE0F', '1F985', '1F986', '1F9A2', '1F989', '1F9A4', '1FAB6', '1F9A9', '1F99A', '1F99C', '1F438', '1F40A', '1F422', '1F98E', '1F40D', '1F432', '1F409', '1F995', '1F996', '1F433', '1F40B', '1F42C', '1F9AD', '1F41F', '1F420', '1F421', '1F988', '1F419', '1F41A', '1FAB8', '1F40C', '1F98B', '1F41B', '1F41C', '1F41D', '1FAB2', '1F41E', '1F997', '1FAB3', '1F577-FE0F', '1F578-FE0F', '1F982', '1F99F', '1FAB0', '1FAB1', '1F9A0', '1F490', '1F338', '1F4AE', '1FAB7', '1F3F5-FE0F', '1F339', '1F940', '1F33A', '1F33B', '1F33C', '1F337', '1F331', '1FAB4', '1F332', '1F333', '1F334', '1F335', '1F33E', '1F33F', '2618-FE0F', '1F340', '1F341', '1F342', '1F343', '1FAB9', '1FABA']\n}, {\n id: 'foods',\n name: 'Food & Drink',\n emojis: ['1F347', '1F348', '1F349', '1F34A', '1F34B', '1F34C', '1F34D', '1F96D', '1F34E', '1F34F', '1F350', '1F351', '1F352', '1F353', '1FAD0', '1F95D', '1F345', '1FAD2', '1F965', '1F951', '1F346', '1F954', '1F955', '1F33D', '1F336-FE0F', '1FAD1', '1F952', '1F96C', '1F966', '1F9C4', '1F9C5', '1F344', '1F95C', '1FAD8', '1F330', '1F35E', '1F950', '1F956', '1FAD3', '1F968', '1F96F', '1F95E', '1F9C7', '1F9C0', '1F356', '1F357', '1F969', '1F953', '1F354', '1F35F', '1F355', '1F32D', '1F96A', '1F32E', '1F32F', '1FAD4', '1F959', '1F9C6', '1F95A', '1F373', '1F958', '1F372', '1FAD5', '1F963', '1F957', '1F37F', '1F9C8', '1F9C2', '1F96B', '1F371', '1F358', '1F359', '1F35A', '1F35B', '1F35C', '1F35D', '1F360', '1F362', '1F363', '1F364', '1F365', '1F96E', '1F361', '1F95F', '1F960', '1F961', '1F980', '1F99E', '1F990', '1F991', '1F9AA', '1F366', '1F367', '1F368', '1F369', '1F36A', '1F382', '1F370', '1F9C1', '1F967', '1F36B', '1F36C', '1F36D', '1F36E', '1F36F', '1F37C', '1F95B', '2615', '1FAD6', '1F375', '1F376', '1F37E', '1F377', '1F378', '1F379', '1F37A', '1F37B', '1F942', '1F943', '1FAD7', '1F964', '1F9CB', '1F9C3', '1F9C9', '1F9CA', '1F962', '1F37D-FE0F', '1F374', '1F944', '1F52A', '1FAD9', '1F3FA']\n}, {\n id: 'activity',\n name: 'Activities',\n emojis: ['1F383', '1F384', '1F386', '1F387', '1F9E8', '2728', '1F388', '1F389', '1F38A', '1F38B', '1F38D', '1F38E', '1F38F', '1F390', '1F391', '1F9E7', '1F380', '1F381', '1F397-FE0F', '1F39F-FE0F', '1F3AB', '1F396-FE0F', '1F3C6', '1F3C5', '1F947', '1F948', '1F949', '26BD', '26BE', '1F94E', '1F3C0', '1F3D0', '1F3C8', '1F3C9', '1F3BE', '1F94F', '1F3B3', '1F3CF', '1F3D1', '1F3D2', '1F94D', '1F3D3', '1F3F8', '1F94A', '1F94B', '1F945', '26F3', '26F8-FE0F', '1F3A3', '1F93F', '1F3BD', '1F3BF', '1F6F7', '1F94C', '1F3AF', '1FA80', '1FA81', '1F3B1', '1F52E', '1FA84', '1F9FF', '1FAAC', '1F3AE', '1F579-FE0F', '1F3B0', '1F3B2', '1F9E9', '1F9F8', '1FA85', '1FAA9', '1FA86', '2660-FE0F', '2665-FE0F', '2666-FE0F', '2663-FE0F', '265F-FE0F', '1F0CF', '1F004', '1F3B4', '1F3AD', '1F5BC-FE0F', '1F3A8', '1F9F5', '1FAA1', '1F9F6', '1FAA2']\n}, {\n id: 'places',\n name: 'Travel & Places',\n emojis: ['1F30D', '1F30E', '1F30F', '1F310', '1F5FA-FE0F', '1F5FE', '1F9ED', '1F3D4-FE0F', '26F0-FE0F', '1F30B', '1F5FB', '1F3D5-FE0F', '1F3D6-FE0F', '1F3DC-FE0F', '1F3DD-FE0F', '1F3DE-FE0F', '1F3DF-FE0F', '1F3DB-FE0F', '1F3D7-FE0F', '1F9F1', '1FAA8', '1FAB5', '1F6D6', '1F3D8-FE0F', '1F3DA-FE0F', '1F3E0', '1F3E1', '1F3E2', '1F3E3', '1F3E4', '1F3E5', '1F3E6', '1F3E8', '1F3E9', '1F3EA', '1F3EB', '1F3EC', '1F3ED', '1F3EF', '1F3F0', '1F492', '1F5FC', '1F5FD', '26EA', '1F54C', '1F6D5', '1F54D', '26E9-FE0F', '1F54B', '26F2', '26FA', '1F301', '1F303', '1F3D9-FE0F', '1F304', '1F305', '1F306', '1F307', '1F309', '2668-FE0F', '1F3A0', '1F6DD', '1F3A1', '1F3A2', '1F488', '1F3AA', '1F682', '1F683', '1F684', '1F685', '1F686', '1F687', '1F688', '1F689', '1F68A', '1F69D', '1F69E', '1F68B', '1F68C', '1F68D', '1F68E', '1F690', '1F691', '1F692', '1F693', '1F694', '1F695', '1F696', '1F697', '1F698', '1F699', '1F6FB', '1F69A', '1F69B', '1F69C', '1F3CE-FE0F', '1F3CD-FE0F', '1F6F5', '1F9BD', '1F9BC', '1F6FA', '1F6B2', '1F6F4', '1F6F9', '1F6FC', '1F68F', '1F6E3-FE0F', '1F6E4-FE0F', '1F6E2-FE0F', '26FD', '1F6DE', '1F6A8', '1F6A5', '1F6A6', '1F6D1', '1F6A7', '2693', '1F6DF', '26F5', '1F6F6', '1F6A4', '1F6F3-FE0F', '26F4-FE0F', '1F6E5-FE0F', '1F6A2', '2708-FE0F', '1F6E9-FE0F', '1F6EB', '1F6EC', '1FA82', '1F4BA', '1F681', '1F69F', '1F6A0', '1F6A1', '1F6F0-FE0F', '1F680', '1F6F8', '1F6CE-FE0F', '1F9F3', '231B', '23F3', '231A', '23F0', '23F1-FE0F', '23F2-FE0F', '1F570-FE0F', '1F55B', '1F567', '1F550', '1F55C', '1F551', '1F55D', '1F552', '1F55E', '1F553', '1F55F', '1F554', '1F560', '1F555', '1F561', '1F556', '1F562', '1F557', '1F563', '1F558', '1F564', '1F559', '1F565', '1F55A', '1F566', '1F311', '1F312', '1F313', '1F314', '1F315', '1F316', '1F317', '1F318', '1F319', '1F31A', '1F31B', '1F31C', '1F321-FE0F', '2600-FE0F', '1F31D', '1F31E', '1FA90', '2B50', '1F31F', '1F320', '1F30C', '2601-FE0F', '26C5', '26C8-FE0F', '1F324-FE0F', '1F325-FE0F', '1F326-FE0F', '1F327-FE0F', '1F328-FE0F', '1F329-FE0F', '1F32A-FE0F', '1F32B-FE0F', '1F32C-FE0F', '1F300', '1F308', '1F302', '2602-FE0F', '2614', '26F1-FE0F', '26A1', '2744-FE0F', '2603-FE0F', '26C4', '2604-FE0F', '1F525', '1F4A7', '1F30A']\n}, {\n id: 'objects',\n name: 'Objects',\n emojis: ['1F453', '1F576-FE0F', '1F97D', '1F97C', '1F9BA', '1F454', '1F455', '1F456', '1F9E3', '1F9E4', '1F9E5', '1F9E6', '1F457', '1F458', '1F97B', '1FA71', '1FA72', '1FA73', '1F459', '1F45A', '1F45B', '1F45C', '1F45D', '1F6CD-FE0F', '1F392', '1FA74', '1F45E', '1F45F', '1F97E', '1F97F', '1F460', '1F461', '1FA70', '1F462', '1F451', '1F452', '1F3A9', '1F393', '1F9E2', '1FA96', '26D1-FE0F', '1F4FF', '1F484', '1F48D', '1F48E', '1F507', '1F508', '1F509', '1F50A', '1F4E2', '1F4E3', '1F4EF', '1F514', '1F515', '1F3BC', '1F3B5', '1F3B6', '1F399-FE0F', '1F39A-FE0F', '1F39B-FE0F', '1F3A4', '1F3A7', '1F4FB', '1F3B7', '1FA97', '1F3B8', '1F3B9', '1F3BA', '1F3BB', '1FA95', '1F941', '1FA98', '1F4F1', '1F4F2', '260E-FE0F', '1F4DE', '1F4DF', '1F4E0', '1F50B', '1FAAB', '1F50C', '1F4BB', '1F5A5-FE0F', '1F5A8-FE0F', '2328-FE0F', '1F5B1-FE0F', '1F5B2-FE0F', '1F4BD', '1F4BE', '1F4BF', '1F4C0', '1F9EE', '1F3A5', '1F39E-FE0F', '1F4FD-FE0F', '1F3AC', '1F4FA', '1F4F7', '1F4F8', '1F4F9', '1F4FC', '1F50D', '1F50E', '1F56F-FE0F', '1F4A1', '1F526', '1F3EE', '1FA94', '1F4D4', '1F4D5', '1F4D6', '1F4D7', '1F4D8', '1F4D9', '1F4DA', '1F4D3', '1F4D2', '1F4C3', '1F4DC', '1F4C4', '1F4F0', '1F5DE-FE0F', '1F4D1', '1F516', '1F3F7-FE0F', '1F4B0', '1FA99', '1F4B4', '1F4B5', '1F4B6', '1F4B7', '1F4B8', '1F4B3', '1F9FE', '1F4B9', '2709-FE0F', '1F4E7', '1F4E8', '1F4E9', '1F4E4', '1F4E5', '1F4E6', '1F4EB', '1F4EA', '1F4EC', '1F4ED', '1F4EE', '1F5F3-FE0F', '270F-FE0F', '2712-FE0F', '1F58B-FE0F', '1F58A-FE0F', '1F58C-FE0F', '1F58D-FE0F', '1F4DD', '1F4BC', '1F4C1', '1F4C2', '1F5C2-FE0F', '1F4C5', '1F4C6', '1F5D2-FE0F', '1F5D3-FE0F', '1F4C7', '1F4C8', '1F4C9', '1F4CA', '1F4CB', '1F4CC', '1F4CD', '1F4CE', '1F587-FE0F', '1F4CF', '1F4D0', '2702-FE0F', '1F5C3-FE0F', '1F5C4-FE0F', '1F5D1-FE0F', '1F512', '1F513', '1F50F', '1F510', '1F511', '1F5DD-FE0F', '1F528', '1FA93', '26CF-FE0F', '2692-FE0F', '1F6E0-FE0F', '1F5E1-FE0F', '2694-FE0F', '1F52B', '1FA83', '1F3F9', '1F6E1-FE0F', '1FA9A', '1F527', '1FA9B', '1F529', '2699-FE0F', '1F5DC-FE0F', '2696-FE0F', '1F9AF', '1F517', '26D3-FE0F', '1FA9D', '1F9F0', '1F9F2', '1FA9C', '2697-FE0F', '1F9EA', '1F9EB', '1F9EC', '1F52C', '1F52D', '1F4E1', '1F489', '1FA78', '1F48A', '1FA79', '1FA7C', '1FA7A', '1FA7B', '1F6AA', '1F6D7', '1FA9E', '1FA9F', '1F6CF-FE0F', '1F6CB-FE0F', '1FA91', '1F6BD', '1FAA0', '1F6BF', '1F6C1', '1FAA4', '1FA92', '1F9F4', '1F9F7', '1F9F9', '1F9FA', '1F9FB', '1FAA3', '1F9FC', '1FAE7', '1FAA5', '1F9FD', '1F9EF', '1F6D2', '1F6AC', '26B0-FE0F', '1FAA6', '26B1-FE0F', '1F5FF', '1FAA7', '1FAAA']\n}, {\n id: 'symbols',\n name: 'Symbols',\n emojis: ['1F3E7', '1F6AE', '1F6B0', '267F', '1F6B9', '1F6BA', '1F6BB', '1F6BC', '1F6BE', '1F6C2', '1F6C3', '1F6C4', '1F6C5', '26A0-FE0F', '1F6B8', '26D4', '1F6AB', '1F6B3', '1F6AD', '1F6AF', '1F6B1', '1F6B7', '1F4F5', '1F51E', '2622-FE0F', '2623-FE0F', '2B06-FE0F', '2197-FE0F', '27A1-FE0F', '2198-FE0F', '2B07-FE0F', '2199-FE0F', '2B05-FE0F', '2196-FE0F', '2195-FE0F', '2194-FE0F', '21A9-FE0F', '21AA-FE0F', '2934-FE0F', '2935-FE0F', '1F503', '1F504', '1F519', '1F51A', '1F51B', '1F51C', '1F51D', '1F6D0', '269B-FE0F', '1F549-FE0F', '2721-FE0F', '2638-FE0F', '262F-FE0F', '271D-FE0F', '2626-FE0F', '262A-FE0F', '262E-FE0F', '1F54E', '1F52F', '2648', '2649', '264A', '264B', '264C', '264D', '264E', '264F', '2650', '2651', '2652', '2653', '26CE', '1F500', '1F501', '1F502', '25B6-FE0F', '23E9', '23ED-FE0F', '23EF-FE0F', '25C0-FE0F', '23EA', '23EE-FE0F', '1F53C', '23EB', '1F53D', '23EC', '23F8-FE0F', '23F9-FE0F', '23FA-FE0F', '23CF-FE0F', '1F3A6', '1F505', '1F506', '1F4F6', '1F4F3', '1F4F4', '2640-FE0F', '2642-FE0F', '26A7-FE0F', '2716-FE0F', '2795', '2796', '2797', '1F7F0', '267E-FE0F', '203C-FE0F', '2049-FE0F', '2753', '2754', '2755', '2757', '3030-FE0F', '1F4B1', '1F4B2', '2695-FE0F', '267B-FE0F', '269C-FE0F', '1F531', '1F4DB', '1F530', '2B55', '2705', '2611-FE0F', '2714-FE0F', '274C', '274E', '27B0', '27BF', '303D-FE0F', '2733-FE0F', '2734-FE0F', '2747-FE0F', '00A9-FE0F', '00AE-FE0F', '2122-FE0F', '0023-FE0F-20E3', '002A-FE0F-20E3', '0030-FE0F-20E3', '0031-FE0F-20E3', '0032-FE0F-20E3', '0033-FE0F-20E3', '0034-FE0F-20E3', '0035-FE0F-20E3', '0036-FE0F-20E3', '0037-FE0F-20E3', '0038-FE0F-20E3', '0039-FE0F-20E3', '1F51F', '1F520', '1F521', '1F522', '1F523', '1F524', '1F170-FE0F', '1F18E', '1F171-FE0F', '1F191', '1F192', '1F193', '2139-FE0F', '1F194', '24C2-FE0F', '1F195', '1F196', '1F17E-FE0F', '1F197', '1F17F-FE0F', '1F198', '1F199', '1F19A', '1F201', '1F202-FE0F', '1F237-FE0F', '1F236', '1F22F', '1F250', '1F239', '1F21A', '1F232', '1F251', '1F238', '1F234', '1F233', '3297-FE0F', '3299-FE0F', '1F23A', '1F235', '1F534', '1F7E0', '1F7E1', '1F7E2', '1F535', '1F7E3', '1F7E4', '26AB', '26AA', '1F7E5', '1F7E7', '1F7E8', '1F7E9', '1F7E6', '1F7EA', '1F7EB', '2B1B', '2B1C', '25FC-FE0F', '25FB-FE0F', '25FE', '25FD', '25AA-FE0F', '25AB-FE0F', '1F536', '1F537', '1F538', '1F539', '1F53A', '1F53B', '1F4A0', '1F518', '1F533', '1F532']\n}, {\n id: 'flags',\n name: 'Flags',\n emojis: ['1F1E6-1F1E8', '1F1E6-1F1E9', '1F1E6-1F1EA', '1F1E6-1F1EB', '1F1E6-1F1EC', '1F1E6-1F1EE', '1F1E6-1F1F1', '1F1E6-1F1F2', '1F1E6-1F1F4', '1F1E6-1F1F6', '1F1E6-1F1F7', '1F1E6-1F1F8', '1F1E6-1F1F9', '1F1E6-1F1FA', '1F1E6-1F1FC', '1F1E6-1F1FD', '1F1E6-1F1FF', '1F1E7-1F1E6', '1F1E7-1F1E7', '1F1E7-1F1E9', '1F1E7-1F1EA', '1F1E7-1F1EB', '1F1E7-1F1EC', '1F1E7-1F1ED', '1F1E7-1F1EE', '1F1E7-1F1EF', '1F1E7-1F1F1', '1F1E7-1F1F2', '1F1E7-1F1F3', '1F1E7-1F1F4', '1F1E7-1F1F6', '1F1E7-1F1F7', '1F1E7-1F1F8', '1F1E7-1F1F9', '1F1E7-1F1FB', '1F1E7-1F1FC', '1F1E7-1F1FE', '1F1E7-1F1FF', '1F1E8-1F1E6', '1F1E8-1F1E8', '1F1E8-1F1E9', '1F1E8-1F1EB', '1F1E8-1F1EC', '1F1E8-1F1ED', '1F1E8-1F1EE', '1F1E8-1F1F0', '1F1E8-1F1F1', '1F1E8-1F1F2', '1F1E8-1F1F3', '1F1E8-1F1F4', '1F1E8-1F1F5', '1F1E8-1F1F7', '1F1E8-1F1FA', '1F1E8-1F1FB', '1F1E8-1F1FC', '1F1E8-1F1FD', '1F1E8-1F1FE', '1F1E8-1F1FF', '1F1E9-1F1EA', '1F1E9-1F1EC', '1F1E9-1F1EF', '1F1E9-1F1F0', '1F1E9-1F1F2', '1F1E9-1F1F4', '1F1E9-1F1FF', '1F1EA-1F1E6', '1F1EA-1F1E8', '1F1EA-1F1EA', '1F1EA-1F1EC', '1F1EA-1F1ED', '1F1EA-1F1F7', '1F1EA-1F1F8', '1F1EA-1F1F9', '1F1EA-1F1FA', '1F1EB-1F1EE', '1F1EB-1F1EF', '1F1EB-1F1F0', '1F1EB-1F1F2', '1F1EB-1F1F4', '1F1EB-1F1F7', '1F1EC-1F1E6', '1F1EC-1F1E7', '1F1EC-1F1E9', '1F1EC-1F1EA', '1F1EC-1F1EB', '1F1EC-1F1EC', '1F1EC-1F1ED', '1F1EC-1F1EE', '1F1EC-1F1F1', '1F1EC-1F1F2', '1F1EC-1F1F3', '1F1EC-1F1F5', '1F1EC-1F1F6', '1F1EC-1F1F7', '1F1EC-1F1F8', '1F1EC-1F1F9', '1F1EC-1F1FA', '1F1EC-1F1FC', '1F1EC-1F1FE', '1F1ED-1F1F0', '1F1ED-1F1F2', '1F1ED-1F1F3', '1F1ED-1F1F7', '1F1ED-1F1F9', '1F1ED-1F1FA', '1F1EE-1F1E8', '1F1EE-1F1E9', '1F1EE-1F1EA', '1F1EE-1F1F1', '1F1EE-1F1F2', '1F1EE-1F1F3', '1F1EE-1F1F4', '1F1EE-1F1F6', '1F1EE-1F1F7', '1F1EE-1F1F8', '1F1EE-1F1F9', '1F1EF-1F1EA', '1F1EF-1F1F2', '1F1EF-1F1F4', '1F1EF-1F1F5', '1F1F0-1F1EA', '1F1F0-1F1EC', '1F1F0-1F1ED', '1F1F0-1F1EE', '1F1F0-1F1F2', '1F1F0-1F1F3', '1F1F0-1F1F5', '1F1F0-1F1F7', '1F1F0-1F1FC', '1F1F0-1F1FE', '1F1F0-1F1FF', '1F1F1-1F1E6', '1F1F1-1F1E7', '1F1F1-1F1E8', '1F1F1-1F1EE', '1F1F1-1F1F0', '1F1F1-1F1F7', '1F1F1-1F1F8', '1F1F1-1F1F9', '1F1F1-1F1FA', '1F1F1-1F1FB', '1F1F1-1F1FE', '1F1F2-1F1E6', '1F1F2-1F1E8', '1F1F2-1F1E9', '1F1F2-1F1EA', '1F1F2-1F1EB', '1F1F2-1F1EC', '1F1F2-1F1ED', '1F1F2-1F1F0', '1F1F2-1F1F1', '1F1F2-1F1F2', '1F1F2-1F1F3', '1F1F2-1F1F4', '1F1F2-1F1F5', '1F1F2-1F1F6', '1F1F2-1F1F7', '1F1F2-1F1F8', '1F1F2-1F1F9', '1F1F2-1F1FA', '1F1F2-1F1FB', '1F1F2-1F1FC', '1F1F2-1F1FD', '1F1F2-1F1FE', '1F1F2-1F1FF', '1F1F3-1F1E6', '1F1F3-1F1E8', '1F1F3-1F1EA', '1F1F3-1F1EB', '1F1F3-1F1EC', '1F1F3-1F1EE', '1F1F3-1F1F1', '1F1F3-1F1F4', '1F1F3-1F1F5', '1F1F3-1F1F7', '1F1F3-1F1FA', '1F1F3-1F1FF', '1F1F4-1F1F2', '1F1F5-1F1E6', '1F1F5-1F1EA', '1F1F5-1F1EB', '1F1F5-1F1EC', '1F1F5-1F1ED', '1F1F5-1F1F0', '1F1F5-1F1F1', '1F1F5-1F1F2', '1F1F5-1F1F3', '1F1F5-1F1F7', '1F1F5-1F1F8', '1F1F5-1F1F9', '1F1F5-1F1FC', '1F1F5-1F1FE', '1F1F6-1F1E6', '1F1F7-1F1EA', '1F1F7-1F1F4', '1F1F7-1F1F8', '1F1F7-1F1FA', '1F1F7-1F1FC', '1F1F8-1F1E6', '1F1F8-1F1E7', '1F1F8-1F1E8', '1F1F8-1F1E9', '1F1F8-1F1EA', '1F1F8-1F1EC', '1F1F8-1F1ED', '1F1F8-1F1EE', '1F1F8-1F1EF', '1F1F8-1F1F0', '1F1F8-1F1F1', '1F1F8-1F1F2', '1F1F8-1F1F3', '1F1F8-1F1F4', '1F1F8-1F1F7', '1F1F8-1F1F8', '1F1F8-1F1F9', '1F1F8-1F1FB', '1F1F8-1F1FD', '1F1F8-1F1FE', '1F1F8-1F1FF', '1F1F9-1F1E6', '1F1F9-1F1E8', '1F1F9-1F1E9', '1F1F9-1F1EB', '1F1F9-1F1EC', '1F1F9-1F1ED', '1F1F9-1F1EF', '1F1F9-1F1F0', '1F1F9-1F1F1', '1F1F9-1F1F2', '1F1F9-1F1F3', '1F1F9-1F1F4', '1F1F9-1F1F7', '1F1F9-1F1F9', '1F1F9-1F1FB', '1F1F9-1F1FC', '1F1F9-1F1FF', '1F1FA-1F1E6', '1F1FA-1F1EC', '1F1FA-1F1F2', '1F1FA-1F1F3', '1F1FA-1F1F8', '1F1FA-1F1FE', '1F1FA-1F1FF', '1F1FB-1F1E6', '1F1FB-1F1E8', '1F1FB-1F1EA', '1F1FB-1F1EC', '1F1FB-1F1EE', '1F1FB-1F1F3', '1F1FB-1F1FA', '1F1FC-1F1EB', '1F1FC-1F1F8', '1F1FD-1F1F0', '1F1FE-1F1EA', '1F1FE-1F1F9', '1F1FF-1F1E6', '1F1FF-1F1F2', '1F1FF-1F1FC', '1F38C', '1F3C1', '1F3F3-FE0F', '1F3F3-FE0F-200D-1F308', '1F3F3-FE0F-200D-26A7-FE0F', '1F3F4', '1F3F4-200D-2620-FE0F', '1F3F4-E0067-E0062-E0065-E006E-E0067-E007F', '1F3F4-E0067-E0062-E0073-E0063-E0074-E007F', '1F3F4-E0067-E0062-E0077-E006C-E0073-E007F', '1F6A9']\n}];\nconst emojis = [{\n name: 'Grinning Face',\n unified: '1F600',\n text: ':D',\n keywords: ['grinning_face', 'face', 'smile', 'happy', 'joy', ':D', 'grin'],\n sheet: [32, 20],\n shortName: 'grinning'\n}, {\n name: 'Smiling Face with Open Mouth',\n unified: '1F603',\n text: ':)',\n emoticons: ['=)', '=-)'],\n keywords: ['grinning_face_with_big_eyes', 'face', 'happy', 'joy', 'haha', ':D', ':)', 'smile', 'funny'],\n sheet: [32, 23],\n shortName: 'smiley'\n}, {\n name: 'Smiling Face with Open Mouth and Smiling Eyes',\n unified: '1F604',\n text: ':)',\n emoticons: ['C:', 'c:', ':D', ':-D'],\n keywords: ['grinning_face_with_smiling_eyes', 'face', 'happy', 'joy', 'funny', 'haha', 'laugh', 'like', ':D', ':)', 'smile'],\n sheet: [32, 24],\n shortName: 'smile'\n}, {\n name: 'Grinning Face with Smiling Eyes',\n unified: '1F601',\n keywords: ['beaming_face_with_smiling_eyes', 'face', 'happy', 'smile', 'joy', 'kawaii'],\n sheet: [32, 21],\n shortName: 'grin'\n}, {\n name: 'Smiling Face with Open Mouth and Tightly-Closed Eyes',\n unified: '1F606',\n emoticons: [':>', ':->'],\n keywords: ['grinning_squinting_face', 'happy', 'joy', 'lol', 'satisfied', 'haha', 'face', 'glad', 'XD', 'laugh'],\n sheet: [32, 26],\n shortNames: ['satisfied'],\n shortName: 'laughing'\n}, {\n name: 'Smiling Face with Open Mouth and Cold Sweat',\n unified: '1F605',\n keywords: ['grinning_face_with_sweat', 'face', 'hot', 'happy', 'laugh', 'sweat', 'smile', 'relief'],\n sheet: [32, 25],\n shortName: 'sweat_smile'\n}, {\n name: 'Rolling on the Floor Laughing',\n unified: '1F923',\n keywords: ['rolling_on_the_floor_laughing', 'face', 'rolling', 'floor', 'laughing', 'lol', 'haha', 'rofl'],\n sheet: [40, 15],\n shortName: 'rolling_on_the_floor_laughing'\n}, {\n name: 'Face with Tears of Joy',\n unified: '1F602',\n keywords: ['face_with_tears_of_joy', 'face', 'cry', 'tears', 'weep', 'happy', 'happytears', 'haha'],\n sheet: [32, 22],\n shortName: 'joy'\n}, {\n name: 'Slightly Smiling Face',\n unified: '1F642',\n emoticons: [':)', '(:', ':-)'],\n keywords: ['slightly_smiling_face', 'face', 'smile'],\n sheet: [33, 28],\n shortName: 'slightly_smiling_face'\n}, {\n name: 'Upside-Down Face',\n unified: '1F643',\n keywords: ['upside_down_face', 'face', 'flipped', 'silly', 'smile'],\n sheet: [33, 29],\n shortName: 'upside_down_face'\n}, {\n name: 'Melting Face',\n unified: '1FAE0',\n keywords: ['melting face', 'hot', 'heat'],\n sheet: [55, 12],\n hidden: ['facebook'],\n shortName: 'melting_face'\n}, {\n name: 'Winking Face',\n unified: '1F609',\n text: ';)',\n emoticons: [';)', ';-)'],\n keywords: ['winking_face', 'face', 'happy', 'mischievous', 'secret', ';)', 'smile', 'eye'],\n sheet: [32, 29],\n shortName: 'wink'\n}, {\n name: 'Smiling Face with Smiling Eyes',\n unified: '1F60A',\n text: ':)',\n keywords: ['smiling_face_with_smiling_eyes', 'face', 'smile', 'happy', 'flushed', 'crush', 'embarrassed', 'shy', 'joy'],\n sheet: [32, 30],\n shortName: 'blush'\n}, {\n name: 'Smiling Face with Halo',\n unified: '1F607',\n keywords: ['smiling_face_with_halo', 'face', 'angel', 'heaven', 'halo', 'innocent'],\n sheet: [32, 27],\n shortName: 'innocent'\n}, {\n name: 'Smiling Face with Smiling Eyes and Three Hearts',\n unified: '1F970',\n keywords: ['smiling_face_with_hearts', 'face', 'love', 'like', 'affection', 'valentines', 'infatuation', 'crush', 'hearts', 'adore'],\n sheet: [43, 58],\n shortName: 'smiling_face_with_3_hearts'\n}, {\n name: 'Smiling Face with Heart-Shaped Eyes',\n unified: '1F60D',\n keywords: ['smiling_face_with_heart_eyes', 'face', 'love', 'like', 'affection', 'valentines', 'infatuation', 'crush', 'heart'],\n sheet: [32, 33],\n shortName: 'heart_eyes'\n}, {\n name: 'Grinning Face with Star Eyes',\n unified: '1F929',\n keywords: ['star_struck', 'face', 'smile', 'starry', 'eyes', 'grinning'],\n sheet: [40, 38],\n shortNames: ['grinning_face_with_star_eyes'],\n shortName: 'star-struck'\n}, {\n name: 'Face Throwing a Kiss',\n unified: '1F618',\n emoticons: [':*', ':-*'],\n keywords: ['face_blowing_a_kiss', 'face', 'love', 'like', 'affection', 'valentines', 'infatuation', 'kiss'],\n sheet: [32, 44],\n shortName: 'kissing_heart'\n}, {\n name: 'Kissing Face',\n unified: '1F617',\n keywords: ['kissing_face', 'love', 'like', 'face', '3', 'valentines', 'infatuation', 'kiss'],\n sheet: [32, 43],\n shortName: 'kissing'\n}, {\n name: 'White Smiling Face',\n unified: '263A-FE0F',\n keywords: ['smiling_face', 'face', 'blush', 'massage', 'happiness'],\n sheet: [57, 4],\n shortName: 'relaxed'\n}, {\n name: 'Kissing Face with Closed Eyes',\n unified: '1F61A',\n keywords: ['kissing_face_with_closed_eyes', 'face', 'love', 'like', 'affection', 'valentines', 'infatuation', 'kiss'],\n sheet: [32, 46],\n shortName: 'kissing_closed_eyes'\n}, {\n name: 'Kissing Face with Smiling Eyes',\n unified: '1F619',\n keywords: ['kissing_face_with_smiling_eyes', 'face', 'affection', 'valentines', 'infatuation', 'kiss'],\n sheet: [32, 45],\n shortName: 'kissing_smiling_eyes'\n}, {\n name: 'Smiling Face with Tear',\n unified: '1F972',\n keywords: ['smiling face with tear', 'sad', 'cry', 'pretend'],\n sheet: [43, 60],\n shortName: 'smiling_face_with_tear'\n}, {\n name: 'Face Savouring Delicious Food',\n unified: '1F60B',\n keywords: ['face_savoring_food', 'happy', 'joy', 'tongue', 'smile', 'face', 'silly', 'yummy', 'nom', 'delicious', 'savouring'],\n sheet: [32, 31],\n shortName: 'yum'\n}, {\n name: 'Face with Stuck-out Tongue',\n unified: '1F61B',\n text: ':p',\n emoticons: [':p', ':-p', ':P', ':-P', ':b', ':-b'],\n keywords: ['face_with_tongue', 'face', 'prank', 'childish', 'playful', 'mischievous', 'smile', 'tongue'],\n sheet: [32, 47],\n shortName: 'stuck_out_tongue'\n}, {\n name: 'Face with Stuck-out Tongue and Winking Eye',\n unified: '1F61C',\n text: ';p',\n emoticons: [';p', ';-p', ';b', ';-b', ';P', ';-P'],\n keywords: ['winking_face_with_tongue', 'face', 'prank', 'childish', 'playful', 'mischievous', 'smile', 'wink', 'tongue'],\n sheet: [32, 48],\n shortName: 'stuck_out_tongue_winking_eye'\n}, {\n name: 'Grinning Face with One Large and One Small Eye',\n unified: '1F92A',\n keywords: ['zany_face', 'face', 'goofy', 'crazy'],\n sheet: [40, 39],\n shortNames: ['grinning_face_with_one_large_and_one_small_eye'],\n shortName: 'zany_face'\n}, {\n name: 'Face with Stuck-out Tongue and Tightly-Closed Eyes',\n unified: '1F61D',\n keywords: ['squinting_face_with_tongue', 'face', 'prank', 'playful', 'mischievous', 'smile', 'tongue'],\n sheet: [32, 49],\n shortName: 'stuck_out_tongue_closed_eyes'\n}, {\n name: 'Money-Mouth Face',\n unified: '1F911',\n keywords: ['money_mouth_face', 'face', 'rich', 'dollar', 'money'],\n sheet: [38, 59],\n shortName: 'money_mouth_face'\n}, {\n name: 'Hugging Face',\n unified: '1F917',\n keywords: ['hugging_face', 'face', 'smile', 'hug'],\n sheet: [39, 4],\n shortName: 'hugging_face'\n}, {\n name: 'Smiling Face with Smiling Eyes and Hand Covering Mouth',\n unified: '1F92D',\n keywords: ['face_with_hand_over_mouth', 'face', 'whoops', 'shock', 'surprise'],\n sheet: [40, 42],\n shortNames: ['smiling_face_with_smiling_eyes_and_hand_covering_mouth'],\n shortName: 'face_with_hand_over_mouth'\n}, {\n name: 'Face with Open Eyes and Hand over Mouth',\n unified: '1FAE2',\n keywords: ['face with open eyes and hand over mouth', 'silence', 'secret', 'shock', 'surprise'],\n sheet: [55, 14],\n hidden: ['facebook'],\n shortName: 'face_with_open_eyes_and_hand_over_mouth'\n}, {\n name: 'Face with Peeking Eye',\n unified: '1FAE3',\n keywords: ['face with peeking eye', 'scared', 'frightening', 'embarrassing', 'shy'],\n sheet: [55, 15],\n hidden: ['facebook'],\n shortName: 'face_with_peeking_eye'\n}, {\n name: 'Face with Finger Covering Closed Lips',\n unified: '1F92B',\n keywords: ['shushing_face', 'face', 'quiet', 'shhh'],\n sheet: [40, 40],\n shortNames: ['face_with_finger_covering_closed_lips'],\n shortName: 'shushing_face'\n}, {\n name: 'Thinking Face',\n unified: '1F914',\n keywords: ['thinking_face', 'face', 'hmmm', 'think', 'consider'],\n sheet: [39, 1],\n shortName: 'thinking_face'\n}, {\n name: 'Saluting Face',\n unified: '1FAE1',\n keywords: ['saluting face', 'respect', 'salute'],\n sheet: [55, 13],\n hidden: ['facebook'],\n shortName: 'saluting_face'\n}, {\n name: 'Zipper-Mouth Face',\n unified: '1F910',\n keywords: ['zipper_mouth_face', 'face', 'sealed', 'zipper', 'secret'],\n sheet: [38, 58],\n shortName: 'zipper_mouth_face'\n}, {\n name: 'Face with One Eyebrow Raised',\n unified: '1F928',\n keywords: ['face_with_raised_eyebrow', 'face', 'distrust', 'scepticism', 'disapproval', 'disbelief', 'surprise'],\n sheet: [40, 37],\n shortNames: ['face_with_one_eyebrow_raised'],\n shortName: 'face_with_raised_eyebrow'\n}, {\n name: 'Neutral Face',\n unified: '1F610',\n emoticons: [':|', ':-|'],\n keywords: ['neutral_face', 'indifference', 'meh', ':|', 'neutral'],\n sheet: [32, 36],\n shortName: 'neutral_face'\n}, {\n name: 'Expressionless Face',\n unified: '1F611',\n keywords: ['expressionless_face', 'face', 'indifferent', '-_-', 'meh', 'deadpan'],\n sheet: [32, 37],\n shortName: 'expressionless'\n}, {\n name: 'Face Without Mouth',\n unified: '1F636',\n keywords: ['face_without_mouth', 'face', 'hellokitty'],\n sheet: [33, 16],\n shortName: 'no_mouth'\n}, {\n name: 'Dotted Line Face',\n unified: '1FAE5',\n keywords: ['dotted line face', 'invisible', 'lonely', 'isolation', 'depression'],\n sheet: [55, 17],\n hidden: ['facebook'],\n shortName: 'dotted_line_face'\n}, {\n name: 'Face in Clouds',\n unified: '1F636-200D-1F32B-FE0F',\n keywords: ['face in clouds', 'shower', 'steam', 'dream'],\n sheet: [33, 15],\n hidden: ['facebook'],\n shortName: 'face_in_clouds'\n}, {\n name: 'Smirking Face',\n unified: '1F60F',\n keywords: ['smirking_face', 'face', 'smile', 'mean', 'prank', 'smug', 'sarcasm'],\n sheet: [32, 35],\n shortName: 'smirk'\n}, {\n name: 'Unamused Face',\n unified: '1F612',\n text: ':(',\n keywords: ['unamused_face', 'indifference', 'bored', 'straight face', 'serious', 'sarcasm', 'unimpressed', 'skeptical', 'dubious', 'side_eye'],\n sheet: [32, 38],\n shortName: 'unamused'\n}, {\n name: 'Face with Rolling Eyes',\n unified: '1F644',\n keywords: ['face_with_rolling_eyes', 'face', 'eyeroll', 'frustrated'],\n sheet: [33, 30],\n shortName: 'face_with_rolling_eyes'\n}, {\n name: 'Grimacing Face',\n unified: '1F62C',\n keywords: ['grimacing_face', 'face', 'grimace', 'teeth'],\n sheet: [33, 3],\n shortName: 'grimacing'\n}, {\n name: 'Face Exhaling',\n unified: '1F62E-200D-1F4A8',\n keywords: ['face exhaling', 'relieve', 'relief', 'tired', 'sigh'],\n sheet: [33, 5],\n hidden: ['facebook'],\n shortName: 'face_exhaling'\n}, {\n name: 'Lying Face',\n unified: '1F925',\n keywords: ['lying_face', 'face', 'lie', 'pinocchio'],\n sheet: [40, 17],\n shortName: 'lying_face'\n}, {\n name: 'Relieved Face',\n unified: '1F60C',\n keywords: ['relieved_face', 'face', 'relaxed', 'phew', 'massage', 'happiness'],\n sheet: [32, 32],\n shortName: 'relieved'\n}, {\n name: 'Pensive Face',\n unified: '1F614',\n keywords: ['pensive_face', 'face', 'sad', 'depressed', 'upset'],\n sheet: [32, 40],\n shortName: 'pensive'\n}, {\n name: 'Sleepy Face',\n unified: '1F62A',\n keywords: ['sleepy_face', 'face', 'tired', 'rest', 'nap'],\n sheet: [33, 1],\n shortName: 'sleepy'\n}, {\n name: 'Drooling Face',\n unified: '1F924',\n keywords: ['drooling_face', 'face'],\n sheet: [40, 16],\n shortName: 'drooling_face'\n}, {\n name: 'Sleeping Face',\n unified: '1F634',\n keywords: ['sleeping_face', 'face', 'tired', 'sleepy', 'night', 'zzz'],\n sheet: [33, 12],\n shortName: 'sleeping'\n}, {\n name: 'Face with Medical Mask',\n unified: '1F637',\n keywords: ['face_with_medical_mask', 'face', 'sick', 'ill', 'disease', 'covid'],\n sheet: [33, 17],\n shortName: 'mask'\n}, {\n name: 'Face with Thermometer',\n unified: '1F912',\n keywords: ['face_with_thermometer', 'sick', 'temperature', 'thermometer', 'cold', 'fever', 'covid'],\n sheet: [38, 60],\n shortName: 'face_with_thermometer'\n}, {\n name: 'Face with Head-Bandage',\n unified: '1F915',\n keywords: ['face_with_head_bandage', 'injured', 'clumsy', 'bandage', 'hurt'],\n sheet: [39, 2],\n shortName: 'face_with_head_bandage'\n}, {\n name: 'Nauseated Face',\n unified: '1F922',\n keywords: ['nauseated_face', 'face', 'vomit', 'gross', 'green', 'sick', 'throw up', 'ill'],\n sheet: [40, 14],\n shortName: 'nauseated_face'\n}, {\n name: 'Face with Open Mouth Vomiting',\n unified: '1F92E',\n keywords: ['face_vomiting', 'face', 'sick'],\n sheet: [40, 43],\n shortNames: ['face_with_open_mouth_vomiting'],\n shortName: 'face_vomiting'\n}, {\n name: 'Sneezing Face',\n unified: '1F927',\n keywords: ['sneezing_face', 'face', 'gesundheit', 'sneeze', 'sick', 'allergy'],\n sheet: [40, 36],\n shortName: 'sneezing_face'\n}, {\n name: 'Overheated Face',\n unified: '1F975',\n keywords: ['hot_face', 'face', 'feverish', 'heat', 'red', 'sweating'],\n sheet: [44, 2],\n shortName: 'hot_face'\n}, {\n name: 'Freezing Face',\n unified: '1F976',\n keywords: ['cold_face', 'face', 'blue', 'freezing', 'frozen', 'frostbite', 'icicles'],\n sheet: [44, 3],\n shortName: 'cold_face'\n}, {\n name: 'Face with Uneven Eyes and Wavy Mouth',\n unified: '1F974',\n keywords: ['woozy_face', 'face', 'dizzy', 'intoxicated', 'tipsy', 'wavy'],\n sheet: [44, 1],\n shortName: 'woozy_face'\n}, {\n name: 'Dizzy Face',\n unified: '1F635',\n keywords: ['dizzy_face', 'spent', 'unconscious', 'xox', 'dizzy'],\n sheet: [33, 14],\n shortName: 'dizzy_face'\n}, {\n name: 'Face with Spiral Eyes',\n unified: '1F635-200D-1F4AB',\n keywords: ['face with spiral eyes', 'sick', 'ill', 'confused', 'nauseous', 'nausea'],\n sheet: [33, 13],\n hidden: ['facebook'],\n shortName: 'face_with_spiral_eyes'\n}, {\n name: 'Shocked Face with Exploding Head',\n unified: '1F92F',\n keywords: ['exploding_head', 'face', 'shocked', 'mind', 'blown'],\n sheet: [40, 44],\n shortNames: ['shocked_face_with_exploding_head'],\n shortName: 'exploding_head'\n}, {\n name: 'Face with Cowboy Hat',\n unified: '1F920',\n keywords: ['cowboy_hat_face', 'face', 'cowgirl', 'hat'],\n sheet: [40, 12],\n shortName: 'face_with_cowboy_hat'\n}, {\n name: 'Face with Party Horn and Party Hat',\n unified: '1F973',\n keywords: ['partying_face', 'face', 'celebration', 'woohoo'],\n sheet: [44, 0],\n shortName: 'partying_face'\n}, {\n name: 'Disguised Face',\n unified: '1F978',\n keywords: ['disguised face', 'pretent', 'brows', 'glasses', 'moustache'],\n sheet: [44, 10],\n shortName: 'disguised_face'\n}, {\n name: 'Smiling Face with Sunglasses',\n unified: '1F60E',\n emoticons: ['8)'],\n keywords: ['smiling_face_with_sunglasses', 'face', 'cool', 'smile', 'summer', 'beach', 'sunglass'],\n sheet: [32, 34],\n shortName: 'sunglasses'\n}, {\n name: 'Nerd Face',\n unified: '1F913',\n keywords: ['nerd_face', 'face', 'nerdy', 'geek', 'dork'],\n sheet: [39, 0],\n shortName: 'nerd_face'\n}, {\n name: 'Face with Monocle',\n unified: '1F9D0',\n keywords: ['face_with_monocle', 'face', 'stuffy', 'wealthy'],\n sheet: [47, 11],\n shortName: 'face_with_monocle'\n}, {\n name: 'Confused Face',\n unified: '1F615',\n emoticons: [':\\\\\\\\', ':-\\\\\\\\', ':/', ':-/'],\n keywords: ['confused_face', 'face', 'indifference', 'huh', 'weird', 'hmmm', ':/'],\n sheet: [32, 41],\n shortName: 'confused'\n}, {\n name: 'Face with Diagonal Mouth',\n unified: '1FAE4',\n keywords: ['face with diagonal mouth', 'skeptic', 'confuse', 'frustrated', 'indifferent'],\n sheet: [55, 16],\n hidden: ['facebook'],\n shortName: 'face_with_diagonal_mouth'\n}, {\n name: 'Worried Face',\n unified: '1F61F',\n keywords: ['worried_face', 'face', 'concern', 'nervous', ':('],\n sheet: [32, 51],\n shortName: 'worried'\n}, {\n name: 'Slightly Frowning Face',\n unified: '1F641',\n keywords: ['slightly_frowning_face', 'face', 'frowning', 'disappointed', 'sad', 'upset'],\n sheet: [33, 27],\n shortName: 'slightly_frowning_face'\n}, {\n name: 'Frowning Face',\n unified: '2639-FE0F',\n keywords: ['frowning_face', 'face', 'sad', 'upset', 'frown'],\n sheet: [57, 3],\n shortName: 'white_frowning_face'\n}, {\n name: 'Face with Open Mouth',\n unified: '1F62E',\n emoticons: [':o', ':-o', ':O', ':-O'],\n keywords: ['face_with_open_mouth', 'face', 'surprise', 'impressed', 'wow', 'whoa', ':O'],\n sheet: [33, 6],\n shortName: 'open_mouth'\n}, {\n name: 'Hushed Face',\n unified: '1F62F',\n keywords: ['hushed_face', 'face', 'woo', 'shh'],\n sheet: [33, 7],\n shortName: 'hushed'\n}, {\n name: 'Astonished Face',\n unified: '1F632',\n keywords: ['astonished_face', 'face', 'xox', 'surprised', 'poisoned'],\n sheet: [33, 10],\n shortName: 'astonished'\n}, {\n name: 'Flushed Face',\n unified: '1F633',\n keywords: ['flushed_face', 'face', 'blush', 'shy', 'flattered'],\n sheet: [33, 11],\n shortName: 'flushed'\n}, {\n name: 'Face with Pleading Eyes',\n unified: '1F97A',\n keywords: ['pleading_face', 'face', 'begging', 'mercy', 'cry', 'tears', 'sad', 'grievance'],\n sheet: [44, 12],\n shortName: 'pleading_face'\n}, {\n name: 'Face Holding Back Tears',\n unified: '1F979',\n keywords: ['face holding back tears', 'touched', 'gratitude', 'cry'],\n sheet: [44, 11],\n hidden: ['facebook'],\n shortName: 'face_holding_back_tears'\n}, {\n name: 'Frowning Face with Open Mouth',\n unified: '1F626',\n keywords: ['frowning_face_with_open_mouth', 'face', 'aw', 'what'],\n sheet: [32, 58],\n shortName: 'frowning'\n}, {\n name: 'Anguished Face',\n unified: '1F627',\n emoticons: ['D:'],\n keywords: ['anguished_face', 'face', 'stunned', 'nervous'],\n sheet: [32, 59],\n shortName: 'anguished'\n}, {\n name: 'Fearful Face',\n unified: '1F628',\n keywords: ['fearful_face', 'face', 'scared', 'terrified', 'nervous'],\n sheet: [32, 60],\n shortName: 'fearful'\n}, {\n name: 'Face with Open Mouth and Cold Sweat',\n unified: '1F630',\n keywords: ['anxious_face_with_sweat', 'face', 'nervous', 'sweat'],\n sheet: [33, 8],\n shortName: 'cold_sweat'\n}, {\n name: 'Disappointed but Relieved Face',\n unified: '1F625',\n keywords: ['sad_but_relieved_face', 'face', 'phew', 'sweat', 'nervous'],\n sheet: [32, 57],\n shortName: 'disappointed_relieved'\n}, {\n name: 'Crying Face',\n unified: '1F622',\n text: ':\\'(',\n emoticons: [':\\'('],\n keywords: ['crying_face', 'face', 'tears', 'sad', 'depressed', 'upset', ':\\'('],\n sheet: [32, 54],\n shortName: 'cry'\n}, {\n name: 'Loudly Crying Face',\n unified: '1F62D',\n text: ':\\'(',\n keywords: ['loudly_crying_face', 'face', 'cry', 'tears', 'sad', 'upset', 'depressed'],\n sheet: [33, 4],\n shortName: 'sob'\n}, {\n name: 'Face Screaming in Fear',\n unified: '1F631',\n keywords: ['face_screaming_in_fear', 'face', 'munch', 'scared', 'omg'],\n sheet: [33, 9],\n shortName: 'scream'\n}, {\n name: 'Confounded Face',\n unified: '1F616',\n keywords: ['confounded_face', 'face', 'confused', 'sick', 'unwell', 'oops', ':S'],\n sheet: [32, 42],\n shortName: 'confounded'\n}, {\n name: 'Persevering Face',\n unified: '1F623',\n keywords: ['persevering_face', 'face', 'sick', 'no', 'upset', 'oops'],\n sheet: [32, 55],\n shortName: 'persevere'\n}, {\n name: 'Disappointed Face',\n unified: '1F61E',\n text: ':(',\n emoticons: ['):', ':(', ':-('],\n keywords: ['disappointed_face', 'face', 'sad', 'upset', 'depressed', ':('],\n sheet: [32, 50],\n shortName: 'disappointed'\n}, {\n name: 'Face with Cold Sweat',\n unified: '1F613',\n keywords: ['downcast_face_with_sweat', 'face', 'hot', 'sad', 'tired', 'exercise'],\n sheet: [32, 39],\n shortName: 'sweat'\n}, {\n name: 'Weary Face',\n unified: '1F629',\n keywords: ['weary_face', 'face', 'tired', 'sleepy', 'sad', 'frustrated', 'upset'],\n sheet: [33, 0],\n shortName: 'weary'\n}, {\n name: 'Tired Face',\n unified: '1F62B',\n keywords: ['tired_face', 'sick', 'whine', 'upset', 'frustrated'],\n sheet: [33, 2],\n shortName: 'tired_face'\n}, {\n name: 'Yawning Face',\n unified: '1F971',\n keywords: ['yawning_face', 'tired', 'sleepy'],\n sheet: [43, 59],\n shortName: 'yawning_face'\n}, {\n name: 'Face with Look of Triumph',\n unified: '1F624',\n keywords: ['face_with_steam_from_nose', 'face', 'gas', 'phew', 'proud', 'pride'],\n sheet: [32, 56],\n shortName: 'triumph'\n}, {\n name: 'Pouting Face',\n unified: '1F621',\n keywords: ['pouting_face', 'angry', 'mad', 'hate', 'despise'],\n sheet: [32, 53],\n shortName: 'rage'\n}, {\n name: 'Angry Face',\n unified: '1F620',\n emoticons: ['>:(', '>:-('],\n keywords: ['angry_face', 'mad', 'face', 'annoyed', 'frustrated'],\n sheet: [32, 52],\n shortName: 'angry'\n}, {\n name: 'Serious Face with Symbols Covering Mouth',\n unified: '1F92C',\n keywords: ['face_with_symbols_on_mouth', 'face', 'swearing', 'cursing', 'cussing', 'profanity', 'expletive'],\n sheet: [40, 41],\n shortNames: ['serious_face_with_symbols_covering_mouth'],\n shortName: 'face_with_symbols_on_mouth'\n}, {\n name: 'Smiling Face with Horns',\n unified: '1F608',\n keywords: ['smiling_face_with_horns', 'devil', 'horns'],\n sheet: [32, 28],\n shortName: 'smiling_imp'\n}, {\n name: 'Imp',\n unified: '1F47F',\n keywords: ['angry_face_with_horns', 'devil', 'angry', 'horns'],\n sheet: [25, 8],\n shortName: 'imp'\n}, {\n name: 'Skull',\n unified: '1F480',\n keywords: ['skull', 'dead', 'skeleton', 'creepy', 'death'],\n sheet: [25, 9],\n shortName: 'skull'\n}, {\n name: 'Skull and Crossbones',\n unified: '2620-FE0F',\n keywords: ['skull_and_crossbones', 'poison', 'danger', 'deadly', 'scary', 'death', 'pirate', 'evil'],\n sheet: [56, 56],\n shortName: 'skull_and_crossbones'\n}, {\n name: 'Pile of Poo',\n unified: '1F4A9',\n keywords: ['pile_of_poo', 'hankey', 'shitface', 'fail', 'turd', 'shit'],\n sheet: [27, 56],\n shortNames: ['poop', 'shit'],\n shortName: 'hankey'\n}, {\n name: 'Clown Face',\n unified: '1F921',\n keywords: ['clown_face', 'face'],\n sheet: [40, 13],\n shortName: 'clown_face'\n}, {\n name: 'Japanese Ogre',\n unified: '1F479',\n keywords: ['ogre', 'monster', 'red', 'mask', 'halloween', 'scary', 'creepy', 'devil', 'demon', 'japanese', 'ogre'],\n sheet: [24, 58],\n shortName: 'japanese_ogre'\n}, {\n name: 'Japanese Goblin',\n unified: '1F47A',\n keywords: ['goblin', 'red', 'evil', 'mask', 'monster', 'scary', 'creepy', 'japanese', 'goblin'],\n sheet: [24, 59],\n shortName: 'japanese_goblin'\n}, {\n name: 'Ghost',\n unified: '1F47B',\n keywords: ['ghost', 'halloween', 'spooky', 'scary'],\n sheet: [24, 60],\n shortName: 'ghost'\n}, {\n name: 'Extraterrestrial Alien',\n unified: '1F47D',\n keywords: ['alien', 'UFO', 'paul', 'weird', 'outer_space'],\n sheet: [25, 6],\n shortName: 'alien'\n}, {\n name: 'Alien Monster',\n unified: '1F47E',\n keywords: ['alien_monster', 'game', 'arcade', 'play'],\n sheet: [25, 7],\n shortName: 'space_invader'\n}, {\n name: 'Robot Face',\n unified: '1F916',\n keywords: ['robot', 'computer', 'machine', 'bot'],\n sheet: [39, 3],\n shortName: 'robot_face'\n}, {\n name: 'Smiling Cat Face with Open Mouth',\n unified: '1F63A',\n keywords: ['grinning_cat', 'animal', 'cats', 'happy', 'smile'],\n sheet: [33, 20],\n shortName: 'smiley_cat'\n}, {\n name: 'Grinning Cat Face with Smiling Eyes',\n unified: '1F638',\n keywords: ['grinning_cat_with_smiling_eyes', 'animal', 'cats', 'smile'],\n sheet: [33, 18],\n shortName: 'smile_cat'\n}, {\n name: 'Cat Face with Tears of Joy',\n unified: '1F639',\n keywords: ['cat_with_tears_of_joy', 'animal', 'cats', 'haha', 'happy', 'tears'],\n sheet: [33, 19],\n shortName: 'joy_cat'\n}, {\n name: 'Smiling Cat Face with Heart-Shaped Eyes',\n unified: '1F63B',\n keywords: ['smiling_cat_with_heart_eyes', 'animal', 'love', 'like', 'affection', 'cats', 'valentines', 'heart'],\n sheet: [33, 21],\n shortName: 'heart_eyes_cat'\n}, {\n name: 'Cat Face with Wry Smile',\n unified: '1F63C',\n keywords: ['cat_with_wry_smile', 'animal', 'cats', 'smirk'],\n sheet: [33, 22],\n shortName: 'smirk_cat'\n}, {\n name: 'Kissing Cat Face with Closed Eyes',\n unified: '1F63D',\n keywords: ['kissing_cat', 'animal', 'cats', 'kiss'],\n sheet: [33, 23],\n shortName: 'kissing_cat'\n}, {\n name: 'Weary Cat Face',\n unified: '1F640',\n keywords: ['weary_cat', 'animal', 'cats', 'munch', 'scared', 'scream'],\n sheet: [33, 26],\n shortName: 'scream_cat'\n}, {\n name: 'Crying Cat Face',\n unified: '1F63F',\n keywords: ['crying_cat', 'animal', 'tears', 'weep', 'sad', 'cats', 'upset', 'cry'],\n sheet: [33, 25],\n shortName: 'crying_cat_face'\n}, {\n name: 'Pouting Cat Face',\n unified: '1F63E',\n keywords: ['pouting_cat', 'animal', 'cats'],\n sheet: [33, 24],\n shortName: 'pouting_cat'\n}, {\n name: 'See-No-Evil Monkey',\n unified: '1F648',\n keywords: ['see_no_evil_monkey', 'monkey', 'animal', 'nature', 'haha'],\n sheet: [34, 24],\n shortName: 'see_no_evil'\n}, {\n name: 'Hear-No-Evil Monkey',\n unified: '1F649',\n keywords: ['hear_no_evil_monkey', 'animal', 'monkey', 'nature'],\n sheet: [34, 25],\n shortName: 'hear_no_evil'\n}, {\n name: 'Speak-No-Evil Monkey',\n unified: '1F64A',\n keywords: ['speak_no_evil_monkey', 'monkey', 'animal', 'nature', 'omg'],\n sheet: [34, 26],\n shortName: 'speak_no_evil'\n}, {\n name: 'Kiss Mark',\n unified: '1F48B',\n keywords: ['kiss_mark', 'face', 'lips', 'love', 'like', 'affection', 'valentines'],\n sheet: [26, 37],\n shortName: 'kiss'\n}, {\n name: 'Love Letter',\n unified: '1F48C',\n keywords: ['love_letter', 'email', 'like', 'affection', 'envelope', 'valentines'],\n sheet: [26, 38],\n shortName: 'love_letter'\n}, {\n name: 'Heart with Arrow',\n unified: '1F498',\n keywords: ['heart_with_arrow', 'love', 'like', 'heart', 'affection', 'valentines'],\n sheet: [27, 39],\n shortName: 'cupid'\n}, {\n name: 'Heart with Ribbon',\n unified: '1F49D',\n keywords: ['heart_with_ribbon', 'love', 'valentines'],\n sheet: [27, 44],\n shortName: 'gift_heart'\n}, {\n name: 'Sparkling Heart',\n unified: '1F496',\n keywords: ['sparkling_heart', 'love', 'like', 'affection', 'valentines'],\n sheet: [27, 37],\n shortName: 'sparkling_heart'\n}, {\n name: 'Growing Heart',\n unified: '1F497',\n keywords: ['growing_heart', 'like', 'love', 'affection', 'valentines', 'pink'],\n sheet: [27, 38],\n shortName: 'heartpulse'\n}, {\n name: 'Beating Heart',\n unified: '1F493',\n keywords: ['beating_heart', 'love', 'like', 'affection', 'valentines', 'pink', 'heart'],\n sheet: [27, 34],\n shortName: 'heartbeat'\n}, {\n name: 'Revolving Hearts',\n unified: '1F49E',\n keywords: ['revolving_hearts', 'love', 'like', 'affection', 'valentines'],\n sheet: [27, 45],\n shortName: 'revolving_hearts'\n}, {\n name: 'Two Hearts',\n unified: '1F495',\n keywords: ['two_hearts', 'love', 'like', 'affection', 'valentines', 'heart'],\n sheet: [27, 36],\n shortName: 'two_hearts'\n}, {\n name: 'Heart Decoration',\n unified: '1F49F',\n keywords: ['heart_decoration', 'purple-square', 'love', 'like'],\n sheet: [27, 46],\n shortName: 'heart_decoration'\n}, {\n name: 'Heart Exclamation',\n unified: '2763-FE0F',\n keywords: ['heart_exclamation', 'decoration', 'love'],\n sheet: [59, 7],\n shortName: 'heavy_heart_exclamation_mark_ornament'\n}, {\n name: 'Broken Heart',\n unified: '1F494',\n text: ' `https://unpkg.com/emoji-datasource-${set}@6.0.1/img/${set}/sheets-256/${sheetSize}.png`;\nlet EmojiService = /*#__PURE__*/(() => {\n class EmojiService {\n constructor() {\n this.uncompressed = false;\n this.names = {};\n this.emojis = [];\n if (!this.uncompressed) {\n this.uncompress(emojis);\n this.uncompressed = true;\n }\n }\n uncompress(list) {\n this.emojis = list.map(emoji => {\n const data = {\n ...emoji\n };\n if (!data.shortNames) {\n data.shortNames = [];\n }\n data.shortNames.unshift(data.shortName);\n data.id = data.shortName;\n data.native = this.unifiedToNative(data.unified);\n if (!data.skinVariations) {\n data.skinVariations = [];\n }\n if (!data.keywords) {\n data.keywords = [];\n }\n if (!data.emoticons) {\n data.emoticons = [];\n }\n if (!data.hidden) {\n data.hidden = [];\n }\n if (!data.text) {\n data.text = '';\n }\n if (data.obsoletes) {\n // get keywords from emoji that it obsoletes since that is shared\n const f = list.find(x => x.unified === data.obsoletes);\n if (f) {\n if (f.keywords) {\n data.keywords = [...data.keywords, ...f.keywords, f.shortName];\n } else {\n data.keywords = [...data.keywords, f.shortName];\n }\n }\n }\n this.names[data.unified] = data;\n for (const n of data.shortNames) {\n this.names[n] = data;\n }\n return data;\n });\n }\n getData(emoji, skin, set) {\n let emojiData;\n if (typeof emoji === 'string') {\n const matches = emoji.match(COLONS_REGEX);\n if (matches) {\n emoji = matches[1];\n if (matches[2]) {\n skin = parseInt(matches[2], 10);\n }\n }\n if (this.names.hasOwnProperty(emoji)) {\n emojiData = this.names[emoji];\n } else {\n return null;\n }\n } else if (emoji.id) {\n emojiData = this.names[emoji.id];\n } else if (emoji.unified) {\n emojiData = this.names[emoji.unified.toUpperCase()];\n }\n if (!emojiData) {\n emojiData = emoji;\n emojiData.custom = true;\n }\n const hasSkinVariations = emojiData.skinVariations && emojiData.skinVariations.length;\n if (hasSkinVariations && skin && skin > 1 && set) {\n emojiData = {\n ...emojiData\n };\n const skinKey = SKINS[skin - 1];\n const variationData = emojiData.skinVariations.find(n => n.unified.includes(skinKey));\n if (!variationData.hidden || !variationData.hidden.includes(set)) {\n emojiData.skinTone = skin;\n emojiData = {\n ...emojiData,\n ...variationData\n };\n }\n emojiData.native = this.unifiedToNative(emojiData.unified);\n }\n emojiData.set = set || '';\n return emojiData;\n }\n unifiedToNative(unified) {\n const codePoints = unified.split('-').map(u => parseInt(`0x${u}`, 16));\n return String.fromCodePoint(...codePoints);\n }\n emojiSpriteStyles(sheet, set = 'apple', size = 24, sheetSize = 64, sheetRows = 57, backgroundImageFn = DEFAULT_BACKGROUNDFN, sheetColumns = 58, url) {\n const hasImageUrl = !!url;\n url = url || backgroundImageFn(set, sheetSize);\n return {\n width: `${size}px`,\n height: `${size}px`,\n display: 'inline-block',\n 'background-image': `url(${url})`,\n 'background-size': hasImageUrl ? '100% 100%' : `${100 * sheetColumns}% ${100 * sheetRows}%`,\n 'background-position': hasImageUrl ? undefined : this.getSpritePosition(sheet, sheetColumns)\n };\n }\n getSpritePosition(sheet, sheetColumns) {\n const [sheetX, sheetY] = sheet;\n const multiply = 100 / (sheetColumns - 1);\n return `${multiply * sheetX}% ${multiply * sheetY}%`;\n }\n sanitize(emoji) {\n if (emoji === null) {\n return null;\n }\n const id = emoji.id || emoji.shortNames[0];\n let colons = `:${id}:`;\n if (emoji.skinTone) {\n colons += `:skin-tone-${emoji.skinTone}:`;\n }\n emoji.colons = colons;\n return {\n ...emoji\n };\n }\n getSanitizedData(emoji, skin, set) {\n return this.sanitize(this.getData(emoji, skin, set));\n }\n }\n EmojiService.ɵfac = function EmojiService_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || EmojiService)();\n };\n EmojiService.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: EmojiService,\n factory: EmojiService.ɵfac,\n providedIn: 'root'\n });\n return EmojiService;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet EmojiComponent = /*#__PURE__*/(() => {\n class EmojiComponent {\n set button(button) {\n // Note: `runOutsideAngular` is used to trigger `addEventListener` outside of the Angular zone\n // too. See `setupMouseEnterListener`. The `switchMap` will subscribe to `fromEvent` considering\n // the context where the factory is called in.\n this.ngZone.runOutsideAngular(() => this.button$.next(button?.nativeElement));\n }\n constructor() {\n this.skin = 1;\n this.set = 'apple';\n this.sheetSize = 64;\n /** Renders the native unicode emoji */\n this.isNative = false;\n this.forceSize = false;\n this.tooltip = false;\n this.size = 24;\n this.emoji = '';\n this.hideObsolete = false;\n this.emojiClick = new EventEmitter();\n /**\n * Note: `emojiOver` and `emojiOverOutsideAngular` are dispatched on the same event (`mouseenter`), but\n * for different purposes. The `emojiOverOutsideAngular` event is listened only in `emoji-category`\n * component and the category component doesn't care about zone context the callback is being called in.\n * The `emojiOver` is for backwards compatibility if anyone is listening to this event explicitly in their code.\n */\n this.emojiOver = new EventEmitter();\n this.emojiOverOutsideAngular = new EventEmitter();\n /** See comments above, this serves the same purpose. */\n this.emojiLeave = new EventEmitter();\n this.emojiLeaveOutsideAngular = new EventEmitter();\n this.title = undefined;\n this.label = '';\n this.custom = false;\n this.isVisible = true;\n // TODO: replace 4.0.3 w/ dynamic get verison from emoji-datasource in package.json\n this.backgroundImageFn = DEFAULT_BACKGROUNDFN;\n /**\n * The subject used to emit whenever view queries are run and `button` or `span` is set/removed.\n * We use subject to keep the reactive behavior so we don't have to add and remove event listeners manually.\n */\n this.button$ = new Subject();\n this.destroy$ = new Subject();\n this.ngZone = inject(NgZone);\n this.emojiService = inject(EmojiService);\n this.setupMouseListeners();\n }\n ngOnChanges() {\n if (!this.emoji) {\n return this.isVisible = false;\n }\n const data = this.getData();\n if (!data) {\n return this.isVisible = false;\n }\n // const children = this.children;\n this.unified = data.native || null;\n if (data.custom) {\n this.custom = data.custom;\n }\n if (!data.unified && !data.custom) {\n return this.isVisible = false;\n }\n if (this.tooltip) {\n this.title = data.shortNames[0];\n }\n if (data.obsoletedBy && this.hideObsolete) {\n return this.isVisible = false;\n }\n this.label = [data.native].concat(data.shortNames).filter(Boolean).join(', ');\n if (this.isNative && data.unified && data.native) {\n // hide older emoji before the split into gendered emoji\n this.style = {\n fontSize: `${this.size}px`\n };\n if (this.forceSize) {\n this.style.display = 'inline-block';\n this.style.width = `${this.size}px`;\n this.style.height = `${this.size}px`;\n this.style['word-break'] = 'keep-all';\n }\n } else if (data.custom) {\n this.style = {\n width: `${this.size}px`,\n height: `${this.size}px`,\n display: 'inline-block'\n };\n if (data.spriteUrl && this.sheetRows && this.sheetColumns) {\n this.style = {\n ...this.style,\n backgroundImage: `url(${data.spriteUrl})`,\n backgroundSize: `${100 * this.sheetColumns}% ${100 * this.sheetRows}%`,\n backgroundPosition: this.emojiService.getSpritePosition(data.sheet, this.sheetColumns)\n };\n } else {\n this.style = {\n ...this.style,\n backgroundImage: `url(${data.imageUrl})`,\n backgroundSize: 'contain'\n };\n }\n } else {\n if (data.hidden.length && data.hidden.includes(this.set)) {\n if (this.fallback) {\n this.style = {\n fontSize: `${this.size}px`\n };\n this.unified = this.fallback(data, this);\n } else {\n return this.isVisible = false;\n }\n } else {\n this.style = this.emojiService.emojiSpriteStyles(data.sheet, this.set, this.size, this.sheetSize, this.sheetRows, this.backgroundImageFn, this.sheetColumns, this.imageUrlFn?.(this.getData()));\n }\n }\n return this.isVisible = true;\n }\n ngOnDestroy() {\n this.destroy$.next();\n }\n getData() {\n return this.emojiService.getData(this.emoji, this.skin, this.set);\n }\n getSanitizedData() {\n return this.emojiService.getSanitizedData(this.emoji, this.skin, this.set);\n }\n handleClick($event) {\n const emoji = this.getSanitizedData();\n this.emojiClick.emit({\n emoji,\n $event\n });\n }\n setupMouseListeners() {\n const eventListener$ = eventName => this.button$.pipe(\n // Note: `EMPTY` is used to remove event listener once the DOM node is removed.\n switchMap(button => button ? fromEvent(button, eventName) : EMPTY), takeUntil(this.destroy$));\n eventListener$('mouseenter').subscribe($event => {\n const emoji = this.getSanitizedData();\n this.emojiOverOutsideAngular.emit({\n emoji,\n $event\n });\n // Note: this is done for backwards compatibility. We run change detection if developers\n // are listening to `emojiOver` in their code. For instance:\n // ``.\n if (this.emojiOver.observed) {\n this.ngZone.run(() => this.emojiOver.emit({\n emoji,\n $event\n }));\n }\n });\n eventListener$('mouseleave').subscribe($event => {\n const emoji = this.getSanitizedData();\n this.emojiLeaveOutsideAngular.emit({\n emoji,\n $event\n });\n // Note: this is done for backwards compatibility. We run change detection if developers\n // are listening to `emojiLeave` in their code. For instance:\n // ``.\n if (this.emojiLeave.observed) {\n this.ngZone.run(() => this.emojiLeave.emit({\n emoji,\n $event\n }));\n }\n });\n }\n }\n EmojiComponent.ɵfac = function EmojiComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || EmojiComponent)();\n };\n EmojiComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: EmojiComponent,\n selectors: [[\"ngx-emoji\"]],\n viewQuery: function EmojiComponent_Query(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵviewQuery(_c0, 5);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.button = _t.first);\n }\n },\n inputs: {\n skin: \"skin\",\n set: \"set\",\n sheetSize: \"sheetSize\",\n isNative: \"isNative\",\n forceSize: \"forceSize\",\n tooltip: \"tooltip\",\n size: \"size\",\n emoji: \"emoji\",\n fallback: \"fallback\",\n hideObsolete: \"hideObsolete\",\n sheetRows: \"sheetRows\",\n sheetColumns: \"sheetColumns\",\n useButton: \"useButton\",\n backgroundImageFn: \"backgroundImageFn\",\n imageUrlFn: \"imageUrlFn\"\n },\n outputs: {\n emojiClick: \"emojiClick\",\n emojiOver: \"emojiOver\",\n emojiOverOutsideAngular: \"emojiOverOutsideAngular\",\n emojiLeave: \"emojiLeave\",\n emojiLeaveOutsideAngular: \"emojiLeaveOutsideAngular\"\n },\n features: [i0.ɵɵNgOnChangesFeature],\n ngContentSelectors: _c1,\n decls: 3,\n vars: 1,\n consts: [[\"spanTpl\", \"\"], [\"button\", \"\"], [3, \"ngIf\"], [\"type\", \"button\", \"class\", \"emoji-mart-emoji\", 3, \"emoji-mart-emoji-native\", \"emoji-mart-emoji-custom\", \"click\", 4, \"ngIf\", \"ngIfElse\"], [\"type\", \"button\", 1, \"emoji-mart-emoji\", 3, \"click\"], [3, \"ngStyle\"], [1, \"emoji-mart-emoji\", 3, \"click\"]],\n template: function EmojiComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵprojectionDef(_c1);\n i0.ɵɵtemplate(0, EmojiComponent_ng_template_0_Template, 1, 2, \"ng-template\", 2)(1, EmojiComponent_ng_template_1_Template, 5, 8, \"ng-template\", null, 0, i0.ɵɵtemplateRefExtractor);\n }\n if (rf & 2) {\n i0.ɵɵproperty(\"ngIf\", ctx.isVisible);\n }\n },\n dependencies: [i1.NgIf, i1.NgStyle],\n encapsulation: 2,\n changeDetection: 0\n });\n return EmojiComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet EmojiModule = /*#__PURE__*/(() => {\n class EmojiModule {}\n EmojiModule.ɵfac = function EmojiModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || EmojiModule)();\n };\n EmojiModule.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: EmojiModule\n });\n EmojiModule.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n imports: [CommonModule]\n });\n return EmojiModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { DEFAULT_BACKGROUNDFN, EmojiComponent, EmojiModule, EmojiService, categories, emojis, skins };\n","import * as i0 from '@angular/core';\nimport { EventEmitter, Component, ChangeDetectionStrategy, Input, Output, PLATFORM_ID, Injectable, Inject, ViewChild, ViewChildren, NgModule } from '@angular/core';\nimport * as i2 from '@angular/common';\nimport { isPlatformBrowser, CommonModule } from '@angular/common';\nimport { Subject } from 'rxjs';\nimport * as i1 from '@ctrl/ngx-emoji-mart/ngx-emoji';\nimport { categories as categories$1, EmojiModule } from '@ctrl/ngx-emoji-mart/ngx-emoji';\nimport * as i2$1 from '@angular/forms';\nimport { FormsModule } from '@angular/forms';\nfunction AnchorsComponent_ng_template_1_span_0_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"span\", 3);\n i0.ɵɵlistener(\"click\", function AnchorsComponent_ng_template_1_span_0_Template_span_click_0_listener($event) {\n i0.ɵɵrestoreView(_r1);\n const idx_r2 = i0.ɵɵnextContext().index;\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleClick($event, idx_r2));\n });\n i0.ɵɵelementStart(1, \"div\");\n i0.ɵɵnamespaceSVG();\n i0.ɵɵelementStart(2, \"svg\", 4);\n i0.ɵɵelement(3, \"path\");\n i0.ɵɵelementEnd()();\n i0.ɵɵnamespaceHTML();\n i0.ɵɵelement(4, \"span\", 5);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const category_r4 = i0.ɵɵnextContext().$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵstyleProp(\"color\", category_r4.name === ctx_r2.selected ? ctx_r2.color : null);\n i0.ɵɵclassProp(\"emoji-mart-anchor-selected\", category_r4.name === ctx_r2.selected);\n i0.ɵɵattribute(\"title\", ctx_r2.i18n.categories[category_r4.id]);\n i0.ɵɵadvance(3);\n i0.ɵɵattribute(\"d\", ctx_r2.icons[category_r4.id]);\n i0.ɵɵadvance();\n i0.ɵɵstyleProp(\"background-color\", ctx_r2.color);\n }\n}\nfunction AnchorsComponent_ng_template_1_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtemplate(0, AnchorsComponent_ng_template_1_span_0_Template, 5, 8, \"span\", 2);\n }\n if (rf & 2) {\n const category_r4 = ctx.$implicit;\n i0.ɵɵproperty(\"ngIf\", category_r4.anchor !== false);\n }\n}\nconst _c0 = [\"container\"];\nconst _c1 = [\"label\"];\nfunction CategoryComponent_div_6_div_1_ngx_emoji_1_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"ngx-emoji\", 9);\n i0.ɵɵlistener(\"emojiOverOutsideAngular\", function CategoryComponent_div_6_div_1_ngx_emoji_1_Template_ngx_emoji_emojiOverOutsideAngular_0_listener($event) {\n i0.ɵɵrestoreView(_r1);\n const ctx_r1 = i0.ɵɵnextContext(3);\n return i0.ɵɵresetView(ctx_r1.emojiOverOutsideAngular.emit($event));\n })(\"emojiLeaveOutsideAngular\", function CategoryComponent_div_6_div_1_ngx_emoji_1_Template_ngx_emoji_emojiLeaveOutsideAngular_0_listener($event) {\n i0.ɵɵrestoreView(_r1);\n const ctx_r1 = i0.ɵɵnextContext(3);\n return i0.ɵɵresetView(ctx_r1.emojiLeaveOutsideAngular.emit($event));\n })(\"emojiClick\", function CategoryComponent_div_6_div_1_ngx_emoji_1_Template_ngx_emoji_emojiClick_0_listener($event) {\n i0.ɵɵrestoreView(_r1);\n const ctx_r1 = i0.ɵɵnextContext(3);\n return i0.ɵɵresetView(ctx_r1.emojiClick.emit($event));\n });\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const emoji_r3 = ctx.$implicit;\n const ctx_r1 = i0.ɵɵnextContext(3);\n i0.ɵɵproperty(\"emoji\", emoji_r3)(\"size\", ctx_r1.emojiSize)(\"skin\", ctx_r1.emojiSkin)(\"isNative\", ctx_r1.emojiIsNative)(\"set\", ctx_r1.emojiSet)(\"sheetSize\", ctx_r1.emojiSheetSize)(\"forceSize\", ctx_r1.emojiForceSize)(\"tooltip\", ctx_r1.emojiTooltip)(\"backgroundImageFn\", ctx_r1.emojiBackgroundImageFn)(\"imageUrlFn\", ctx_r1.emojiImageUrlFn)(\"hideObsolete\", ctx_r1.hideObsolete)(\"useButton\", ctx_r1.emojiUseButton);\n }\n}\nfunction CategoryComponent_div_6_div_1_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\");\n i0.ɵɵtemplate(1, CategoryComponent_div_6_div_1_ngx_emoji_1_Template, 1, 12, \"ngx-emoji\", 8);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const filteredEmojis_r4 = ctx.ngIf;\n const ctx_r1 = i0.ɵɵnextContext(2);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngForOf\", filteredEmojis_r4)(\"ngForTrackBy\", ctx_r1.trackById);\n }\n}\nfunction CategoryComponent_div_6_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\");\n i0.ɵɵtemplate(1, CategoryComponent_div_6_div_1_Template, 2, 2, \"div\", 7);\n i0.ɵɵpipe(2, \"async\");\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext();\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", i0.ɵɵpipeBind1(2, 1, ctx_r1.filteredEmojis$));\n }\n}\nfunction CategoryComponent_div_7_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\")(1, \"div\");\n i0.ɵɵelement(2, \"ngx-emoji\", 10);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(3, \"div\", 11);\n i0.ɵɵtext(4);\n i0.ɵɵelementEnd()();\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext();\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"emoji\", ctx_r1.notFoundEmoji)(\"size\", 38)(\"skin\", ctx_r1.emojiSkin)(\"isNative\", ctx_r1.emojiIsNative)(\"set\", ctx_r1.emojiSet)(\"sheetSize\", ctx_r1.emojiSheetSize)(\"forceSize\", ctx_r1.emojiForceSize)(\"tooltip\", ctx_r1.emojiTooltip)(\"backgroundImageFn\", ctx_r1.emojiBackgroundImageFn)(\"useButton\", ctx_r1.emojiUseButton);\n i0.ɵɵadvance(2);\n i0.ɵɵtextInterpolate1(\" \", ctx_r1.i18n.notfound, \" \");\n }\n}\nfunction CategoryComponent_ng_template_8_ngx_emoji_0_Template(rf, ctx) {\n if (rf & 1) {\n const _r5 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"ngx-emoji\", 9);\n i0.ɵɵlistener(\"emojiOverOutsideAngular\", function CategoryComponent_ng_template_8_ngx_emoji_0_Template_ngx_emoji_emojiOverOutsideAngular_0_listener($event) {\n i0.ɵɵrestoreView(_r5);\n const ctx_r1 = i0.ɵɵnextContext(2);\n return i0.ɵɵresetView(ctx_r1.emojiOverOutsideAngular.emit($event));\n })(\"emojiLeaveOutsideAngular\", function CategoryComponent_ng_template_8_ngx_emoji_0_Template_ngx_emoji_emojiLeaveOutsideAngular_0_listener($event) {\n i0.ɵɵrestoreView(_r5);\n const ctx_r1 = i0.ɵɵnextContext(2);\n return i0.ɵɵresetView(ctx_r1.emojiLeaveOutsideAngular.emit($event));\n })(\"emojiClick\", function CategoryComponent_ng_template_8_ngx_emoji_0_Template_ngx_emoji_emojiClick_0_listener($event) {\n i0.ɵɵrestoreView(_r5);\n const ctx_r1 = i0.ɵɵnextContext(2);\n return i0.ɵɵresetView(ctx_r1.emojiClick.emit($event));\n });\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const emoji_r6 = ctx.$implicit;\n const ctx_r1 = i0.ɵɵnextContext(2);\n i0.ɵɵproperty(\"emoji\", emoji_r6)(\"size\", ctx_r1.emojiSize)(\"skin\", ctx_r1.emojiSkin)(\"isNative\", ctx_r1.emojiIsNative)(\"set\", ctx_r1.emojiSet)(\"sheetSize\", ctx_r1.emojiSheetSize)(\"forceSize\", ctx_r1.emojiForceSize)(\"tooltip\", ctx_r1.emojiTooltip)(\"backgroundImageFn\", ctx_r1.emojiBackgroundImageFn)(\"imageUrlFn\", ctx_r1.emojiImageUrlFn)(\"hideObsolete\", ctx_r1.hideObsolete)(\"useButton\", ctx_r1.emojiUseButton);\n }\n}\nfunction CategoryComponent_ng_template_8_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtemplate(0, CategoryComponent_ng_template_8_ngx_emoji_0_Template, 1, 12, \"ngx-emoji\", 8);\n }\n if (rf & 2) {\n const ctx_r1 = i0.ɵɵnextContext();\n i0.ɵɵproperty(\"ngForOf\", ctx_r1.emojisToDisplay)(\"ngForTrackBy\", ctx_r1.trackById);\n }\n}\nfunction SkinComponent_span_1_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"span\", 2)(1, \"span\", 3);\n i0.ɵɵlistener(\"click\", function SkinComponent_span_1_Template_span_click_1_listener() {\n const skinTone_r2 = i0.ɵɵrestoreView(_r1).$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleClick(skinTone_r2));\n })(\"keyup.enter\", function SkinComponent_span_1_Template_span_keyup_enter_1_listener() {\n const skinTone_r2 = i0.ɵɵrestoreView(_r1).$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleClick(skinTone_r2));\n })(\"keyup.space\", function SkinComponent_span_1_Template_span_keyup_space_1_listener() {\n const skinTone_r2 = i0.ɵɵrestoreView(_r1).$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleClick(skinTone_r2));\n });\n i0.ɵɵelementEnd()();\n }\n if (rf & 2) {\n const skinTone_r2 = ctx.$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵclassProp(\"selected\", skinTone_r2 === ctx_r2.skin);\n i0.ɵɵadvance();\n i0.ɵɵclassMapInterpolate1(\"emoji-mart-skin emoji-mart-skin-tone-\", skinTone_r2, \"\");\n i0.ɵɵproperty(\"tabIndex\", ctx_r2.tabIndex(skinTone_r2));\n i0.ɵɵattribute(\"aria-hidden\", !ctx_r2.isVisible(skinTone_r2))(\"aria-pressed\", ctx_r2.pressed(skinTone_r2))(\"aria-haspopup\", !!ctx_r2.isSelected(skinTone_r2))(\"aria-expanded\", ctx_r2.expanded(skinTone_r2))(\"aria-label\", ctx_r2.i18n.skintones[skinTone_r2])(\"title\", ctx_r2.i18n.skintones[skinTone_r2]);\n }\n}\nfunction PreviewComponent_div_0_span_7_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"span\", 11);\n i0.ɵɵtext(1);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const short_name_r1 = ctx.$implicit;\n i0.ɵɵadvance();\n i0.ɵɵtextInterpolate1(\" :\", short_name_r1, \": \");\n }\n}\nfunction PreviewComponent_div_0_span_9_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"span\", 15);\n i0.ɵɵtext(1);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const emoticon_r2 = ctx.$implicit;\n i0.ɵɵadvance();\n i0.ɵɵtextInterpolate1(\" \", emoticon_r2, \" \");\n }\n}\nfunction PreviewComponent_div_0_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\", 8)(1, \"div\", 2);\n i0.ɵɵelement(2, \"ngx-emoji\", 9);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(3, \"div\", 4)(4, \"div\", 10);\n i0.ɵɵtext(5);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(6, \"div\", 11);\n i0.ɵɵtemplate(7, PreviewComponent_div_0_span_7_Template, 2, 1, \"span\", 12);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(8, \"div\", 13);\n i0.ɵɵtemplate(9, PreviewComponent_div_0_span_9_Template, 2, 1, \"span\", 14);\n i0.ɵɵelementEnd()()();\n }\n if (rf & 2) {\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"emoji\", ctx_r2.emoji)(\"size\", 38)(\"isNative\", ctx_r2.emojiIsNative)(\"skin\", ctx_r2.emojiSkin)(\"size\", ctx_r2.emojiSize)(\"set\", ctx_r2.emojiSet)(\"sheetSize\", ctx_r2.emojiSheetSize)(\"backgroundImageFn\", ctx_r2.emojiBackgroundImageFn)(\"imageUrlFn\", ctx_r2.emojiImageUrlFn);\n i0.ɵɵadvance(3);\n i0.ɵɵtextInterpolate(ctx_r2.emojiData.name);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngForOf\", ctx_r2.emojiData.shortNames);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngForOf\", ctx_r2.listedEmoticons);\n }\n}\nfunction PreviewComponent_ngx_emoji_3_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelement(0, \"ngx-emoji\", 16);\n }\n if (rf & 2) {\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵproperty(\"isNative\", ctx_r2.emojiIsNative)(\"skin\", ctx_r2.emojiSkin)(\"set\", ctx_r2.emojiSet)(\"emoji\", ctx_r2.idleEmoji)(\"backgroundImageFn\", ctx_r2.emojiBackgroundImageFn)(\"size\", 38)(\"imageUrlFn\", ctx_r2.emojiImageUrlFn);\n }\n}\nconst _c2 = [\"inputRef\"];\nconst _c3 = [\"scrollRef\"];\nfunction PickerComponent_emoji_search_3_Template(rf, ctx) {\n if (rf & 1) {\n const _r2 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"emoji-search\", 8);\n i0.ɵɵlistener(\"searchResults\", function PickerComponent_emoji_search_3_Template_emoji_search_searchResults_0_listener($event) {\n i0.ɵɵrestoreView(_r2);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleSearch($event));\n })(\"enterKey\", function PickerComponent_emoji_search_3_Template_emoji_search_enterKey_0_listener($event) {\n i0.ɵɵrestoreView(_r2);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleEnterKey($event));\n });\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵproperty(\"i18n\", ctx_r2.i18n)(\"include\", ctx_r2.include)(\"exclude\", ctx_r2.exclude)(\"custom\", ctx_r2.custom)(\"autoFocus\", ctx_r2.autoFocus)(\"icons\", ctx_r2.searchIcons)(\"emojisToShowFilter\", ctx_r2.emojisToShowFilter);\n }\n}\nfunction PickerComponent_emoji_category_6_Template(rf, ctx) {\n if (rf & 1) {\n const _r4 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"emoji-category\", 9);\n i0.ɵɵlistener(\"emojiOverOutsideAngular\", function PickerComponent_emoji_category_6_Template_emoji_category_emojiOverOutsideAngular_0_listener($event) {\n i0.ɵɵrestoreView(_r4);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleEmojiOver($event));\n })(\"emojiLeaveOutsideAngular\", function PickerComponent_emoji_category_6_Template_emoji_category_emojiLeaveOutsideAngular_0_listener() {\n i0.ɵɵrestoreView(_r4);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleEmojiLeave());\n })(\"emojiClick\", function PickerComponent_emoji_category_6_Template_emoji_category_emojiClick_0_listener($event) {\n i0.ɵɵrestoreView(_r4);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleEmojiClick($event));\n });\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n const category_r5 = ctx.$implicit;\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵproperty(\"id\", category_r5.id)(\"name\", category_r5.name)(\"emojis\", category_r5.emojis)(\"perLine\", ctx_r2.perLine)(\"totalFrequentLines\", ctx_r2.totalFrequentLines)(\"hasStickyPosition\", ctx_r2.isNative)(\"i18n\", ctx_r2.i18n)(\"hideObsolete\", ctx_r2.hideObsolete)(\"notFoundEmoji\", ctx_r2.notFoundEmoji)(\"custom\", category_r5.id === ctx_r2.RECENT_CATEGORY.id ? ctx_r2.CUSTOM_CATEGORY.emojis : undefined)(\"recent\", category_r5.id === ctx_r2.RECENT_CATEGORY.id ? ctx_r2.recent : undefined)(\"virtualize\", ctx_r2.virtualize)(\"virtualizeOffset\", ctx_r2.virtualizeOffset)(\"emojiIsNative\", ctx_r2.isNative)(\"emojiSkin\", ctx_r2.skin)(\"emojiSize\", ctx_r2.emojiSize)(\"emojiSet\", ctx_r2.set)(\"emojiSheetSize\", ctx_r2.sheetSize)(\"emojiForceSize\", ctx_r2.isNative)(\"emojiTooltip\", ctx_r2.emojiTooltip)(\"emojiBackgroundImageFn\", ctx_r2.backgroundImageFn)(\"emojiImageUrlFn\", ctx_r2.imageUrlFn)(\"emojiUseButton\", ctx_r2.useButton);\n }\n}\nfunction PickerComponent_div_7_Template(rf, ctx) {\n if (rf & 1) {\n const _r6 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"div\", 2)(1, \"emoji-preview\", 10);\n i0.ɵɵlistener(\"skinChange\", function PickerComponent_div_7_Template_emoji_preview_skinChange_1_listener($event) {\n i0.ɵɵrestoreView(_r6);\n const ctx_r2 = i0.ɵɵnextContext();\n return i0.ɵɵresetView(ctx_r2.handleSkinChange($event));\n });\n i0.ɵɵelementEnd()();\n }\n if (rf & 2) {\n const ctx_r2 = i0.ɵɵnextContext();\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"emoji\", ctx_r2.previewEmoji)(\"idleEmoji\", ctx_r2.emoji)(\"emojiIsNative\", ctx_r2.isNative)(\"emojiSize\", 38)(\"emojiSkin\", ctx_r2.skin)(\"emojiSet\", ctx_r2.set)(\"i18n\", ctx_r2.i18n)(\"emojiSheetSize\", ctx_r2.sheetSize)(\"emojiBackgroundImageFn\", ctx_r2.backgroundImageFn)(\"emojiImageUrlFn\", ctx_r2.imageUrlFn);\n i0.ɵɵattribute(\"title\", ctx_r2.title);\n }\n}\nlet AnchorsComponent = /*#__PURE__*/(() => {\n class AnchorsComponent {\n constructor() {\n this.categories = [];\n this.icons = {};\n this.anchorClick = new EventEmitter();\n }\n trackByFn(idx, cat) {\n return cat.id;\n }\n handleClick($event, index) {\n this.anchorClick.emit({\n category: this.categories[index],\n index\n });\n }\n }\n AnchorsComponent.ɵfac = function AnchorsComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || AnchorsComponent)();\n };\n AnchorsComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: AnchorsComponent,\n selectors: [[\"emoji-mart-anchors\"]],\n inputs: {\n categories: \"categories\",\n color: \"color\",\n selected: \"selected\",\n i18n: \"i18n\",\n icons: \"icons\"\n },\n outputs: {\n anchorClick: \"anchorClick\"\n },\n decls: 2,\n vars: 2,\n consts: [[1, \"emoji-mart-anchors\"], [\"ngFor\", \"\", 3, \"ngForOf\", \"ngForTrackBy\"], [\"class\", \"emoji-mart-anchor\", 3, \"emoji-mart-anchor-selected\", \"color\", \"click\", 4, \"ngIf\"], [1, \"emoji-mart-anchor\", 3, \"click\"], [\"xmlns\", \"http://www.w3.org/2000/svg\", \"viewBox\", \"0 0 24 24\", \"width\", \"24\", \"height\", \"24\"], [1, \"emoji-mart-anchor-bar\"]],\n template: function AnchorsComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"div\", 0);\n i0.ɵɵtemplate(1, AnchorsComponent_ng_template_1_Template, 1, 1, \"ng-template\", 1);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngForOf\", ctx.categories)(\"ngForTrackBy\", ctx.trackByFn);\n }\n },\n dependencies: [i2.NgForOf, i2.NgIf],\n encapsulation: 2,\n changeDetection: 0\n });\n return AnchorsComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet EmojiFrequentlyService = /*#__PURE__*/(() => {\n class EmojiFrequentlyService {\n constructor(platformId) {\n this.platformId = platformId;\n this.NAMESPACE = 'emoji-mart';\n this.frequently = null;\n this.defaults = {};\n this.initialized = false;\n this.DEFAULTS = ['+1', 'grinning', 'kissing_heart', 'heart_eyes', 'laughing', 'stuck_out_tongue_winking_eye', 'sweat_smile', 'joy', 'scream', 'disappointed', 'unamused', 'weary', 'sob', 'sunglasses', 'heart', 'poop'];\n }\n init() {\n this.frequently = JSON.parse(isPlatformBrowser(this.platformId) && localStorage.getItem(`${this.NAMESPACE}.frequently`) || 'null');\n this.initialized = true;\n }\n add(emoji) {\n if (!this.initialized) {\n this.init();\n }\n if (!this.frequently) {\n this.frequently = this.defaults;\n }\n if (!this.frequently[emoji.id]) {\n this.frequently[emoji.id] = 0;\n }\n this.frequently[emoji.id] += 1;\n if (isPlatformBrowser(this.platformId)) {\n localStorage.setItem(`${this.NAMESPACE}.last`, emoji.id);\n localStorage.setItem(`${this.NAMESPACE}.frequently`, JSON.stringify(this.frequently));\n }\n }\n get(perLine, totalLines) {\n if (!this.initialized) {\n this.init();\n }\n if (this.frequently === null) {\n this.defaults = {};\n const result = [];\n for (let i = 0; i < perLine; i++) {\n this.defaults[this.DEFAULTS[i]] = perLine - i;\n result.push(this.DEFAULTS[i]);\n }\n return result;\n }\n const quantity = perLine * totalLines;\n const frequentlyKeys = Object.keys(this.frequently);\n const sorted = frequentlyKeys.sort((a, b) => this.frequently[a] - this.frequently[b]).reverse();\n const sliced = sorted.slice(0, quantity);\n const last = isPlatformBrowser(this.platformId) && localStorage.getItem(`${this.NAMESPACE}.last`);\n if (last && !sliced.includes(last)) {\n sliced.pop();\n sliced.push(last);\n }\n return sliced;\n }\n }\n EmojiFrequentlyService.ɵfac = function EmojiFrequentlyService_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || EmojiFrequentlyService)(i0.ɵɵinject(PLATFORM_ID));\n };\n EmojiFrequentlyService.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: EmojiFrequentlyService,\n factory: EmojiFrequentlyService.ɵfac,\n providedIn: 'root'\n });\n return EmojiFrequentlyService;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet CategoryComponent = /*#__PURE__*/(() => {\n class CategoryComponent {\n constructor(ref, emojiService, frequently) {\n this.ref = ref;\n this.emojiService = emojiService;\n this.frequently = frequently;\n this.emojis = null;\n this.hasStickyPosition = true;\n this.name = '';\n this.perLine = 9;\n this.totalFrequentLines = 4;\n this.recent = [];\n this.custom = [];\n this.hideObsolete = true;\n this.virtualize = false;\n this.virtualizeOffset = 0;\n this.emojiClick = new EventEmitter();\n /**\n * Note: the suffix is added explicitly so we know the event is dispatched outside of the Angular zone.\n */\n this.emojiOverOutsideAngular = new EventEmitter();\n this.emojiLeaveOutsideAngular = new EventEmitter();\n this.containerStyles = {};\n this.emojisToDisplay = [];\n this.filteredEmojisSubject = new Subject();\n this.filteredEmojis$ = this.filteredEmojisSubject.asObservable();\n this.labelStyles = {};\n this.labelSpanStyles = {};\n this.margin = 0;\n this.minMargin = 0;\n this.maxMargin = 0;\n this.top = 0;\n this.rows = 0;\n }\n ngOnInit() {\n this.updateRecentEmojis();\n this.emojisToDisplay = this.filterEmojis();\n if (this.noEmojiToDisplay) {\n this.containerStyles = {\n display: 'none'\n };\n }\n if (!this.hasStickyPosition) {\n this.labelStyles = {\n height: 28\n };\n // this.labelSpanStyles = { position: 'absolute' };\n }\n }\n ngOnChanges(changes) {\n if (changes.emojis?.currentValue?.length !== changes.emojis?.previousValue?.length) {\n this.emojisToDisplay = this.filterEmojis();\n this.ngAfterViewInit();\n }\n }\n ngAfterViewInit() {\n if (!this.virtualize) {\n return;\n }\n const {\n width\n } = this.container.nativeElement.getBoundingClientRect();\n const perRow = Math.floor(width / (this.emojiSize + 12));\n this.rows = Math.ceil(this.emojisToDisplay.length / perRow);\n this.containerStyles = {\n ...this.containerStyles,\n minHeight: `${this.rows * (this.emojiSize + 12) + 28}px`\n };\n this.ref.detectChanges();\n this.handleScroll(this.container.nativeElement.parentNode.parentNode.scrollTop);\n }\n get noEmojiToDisplay() {\n return this.emojisToDisplay.length === 0;\n }\n memoizeSize() {\n const parent = this.container.nativeElement.parentNode.parentNode;\n const {\n top,\n height\n } = this.container.nativeElement.getBoundingClientRect();\n const parentTop = parent.getBoundingClientRect().top;\n const labelHeight = this.label.nativeElement.getBoundingClientRect().height;\n this.top = top - parentTop + parent.scrollTop;\n if (height === 0) {\n this.maxMargin = 0;\n } else {\n this.maxMargin = height - labelHeight;\n }\n }\n handleScroll(scrollTop) {\n let margin = scrollTop - this.top;\n margin = margin < this.minMargin ? this.minMargin : margin;\n margin = margin > this.maxMargin ? this.maxMargin : margin;\n if (this.virtualize) {\n const {\n top,\n height\n } = this.container.nativeElement.getBoundingClientRect();\n const parentHeight = this.container.nativeElement.parentNode.parentNode.clientHeight;\n if (parentHeight + (parentHeight + this.virtualizeOffset) >= top && -height - (parentHeight + this.virtualizeOffset) <= top) {\n this.filteredEmojisSubject.next(this.emojisToDisplay);\n } else {\n this.filteredEmojisSubject.next([]);\n }\n }\n if (margin === this.margin) {\n this.ref.detectChanges();\n return false;\n }\n if (!this.hasStickyPosition) {\n this.label.nativeElement.style.top = `${margin}px`;\n }\n this.margin = margin;\n this.ref.detectChanges();\n return true;\n }\n updateRecentEmojis() {\n if (this.name !== 'Recent') {\n return;\n }\n let frequentlyUsed = this.recent || this.frequently.get(this.perLine, this.totalFrequentLines);\n if (!frequentlyUsed || !frequentlyUsed.length) {\n frequentlyUsed = this.frequently.get(this.perLine, this.totalFrequentLines);\n }\n if (!frequentlyUsed.length) {\n return;\n }\n this.emojis = frequentlyUsed.map(id => {\n const emoji = this.custom.filter(e => e.id === id)[0];\n if (emoji) {\n return emoji;\n }\n return id;\n }).filter(id => !!this.emojiService.getData(id));\n }\n updateDisplay(display) {\n this.containerStyles.display = display;\n this.updateRecentEmojis();\n this.ref.detectChanges();\n }\n trackById(index, item) {\n return item;\n }\n filterEmojis() {\n const newEmojis = [];\n for (const emoji of this.emojis || []) {\n if (!emoji) {\n continue;\n }\n const data = this.emojiService.getData(emoji);\n if (!data || data.obsoletedBy && this.hideObsolete || !data.unified && !data.custom) {\n continue;\n }\n newEmojis.push(emoji);\n }\n return newEmojis;\n }\n }\n CategoryComponent.ɵfac = function CategoryComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || CategoryComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.EmojiService), i0.ɵɵdirectiveInject(EmojiFrequentlyService));\n };\n CategoryComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: CategoryComponent,\n selectors: [[\"emoji-category\"]],\n viewQuery: function CategoryComponent_Query(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵviewQuery(_c0, 7);\n i0.ɵɵviewQuery(_c1, 7);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.container = _t.first);\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.label = _t.first);\n }\n },\n inputs: {\n emojis: \"emojis\",\n hasStickyPosition: \"hasStickyPosition\",\n name: \"name\",\n perLine: \"perLine\",\n totalFrequentLines: \"totalFrequentLines\",\n recent: \"recent\",\n custom: \"custom\",\n i18n: \"i18n\",\n id: \"id\",\n hideObsolete: \"hideObsolete\",\n notFoundEmoji: \"notFoundEmoji\",\n virtualize: \"virtualize\",\n virtualizeOffset: \"virtualizeOffset\",\n emojiIsNative: \"emojiIsNative\",\n emojiSkin: \"emojiSkin\",\n emojiSize: \"emojiSize\",\n emojiSet: \"emojiSet\",\n emojiSheetSize: \"emojiSheetSize\",\n emojiForceSize: \"emojiForceSize\",\n emojiTooltip: \"emojiTooltip\",\n emojiBackgroundImageFn: \"emojiBackgroundImageFn\",\n emojiImageUrlFn: \"emojiImageUrlFn\",\n emojiUseButton: \"emojiUseButton\"\n },\n outputs: {\n emojiClick: \"emojiClick\",\n emojiOverOutsideAngular: \"emojiOverOutsideAngular\",\n emojiLeaveOutsideAngular: \"emojiLeaveOutsideAngular\"\n },\n features: [i0.ɵɵNgOnChangesFeature],\n decls: 10,\n vars: 11,\n consts: [[\"container\", \"\"], [\"label\", \"\"], [\"normalRenderTemplate\", \"\"], [1, \"emoji-mart-category\", 3, \"ngStyle\"], [1, \"emoji-mart-category-label\", 3, \"ngStyle\"], [\"aria-hidden\", \"true\", 3, \"ngStyle\"], [4, \"ngIf\", \"ngIfElse\"], [4, \"ngIf\"], [3, \"emoji\", \"size\", \"skin\", \"isNative\", \"set\", \"sheetSize\", \"forceSize\", \"tooltip\", \"backgroundImageFn\", \"imageUrlFn\", \"hideObsolete\", \"useButton\", \"emojiOverOutsideAngular\", \"emojiLeaveOutsideAngular\", \"emojiClick\", 4, \"ngFor\", \"ngForOf\", \"ngForTrackBy\"], [3, \"emojiOverOutsideAngular\", \"emojiLeaveOutsideAngular\", \"emojiClick\", \"emoji\", \"size\", \"skin\", \"isNative\", \"set\", \"sheetSize\", \"forceSize\", \"tooltip\", \"backgroundImageFn\", \"imageUrlFn\", \"hideObsolete\", \"useButton\"], [3, \"emoji\", \"size\", \"skin\", \"isNative\", \"set\", \"sheetSize\", \"forceSize\", \"tooltip\", \"backgroundImageFn\", \"useButton\"], [1, \"emoji-mart-no-results-label\"]],\n template: function CategoryComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"section\", 3, 0)(2, \"div\", 4)(3, \"span\", 5, 1);\n i0.ɵɵtext(5);\n i0.ɵɵelementEnd()();\n i0.ɵɵtemplate(6, CategoryComponent_div_6_Template, 3, 3, \"div\", 6)(7, CategoryComponent_div_7_Template, 5, 11, \"div\", 7);\n i0.ɵɵelementEnd();\n i0.ɵɵtemplate(8, CategoryComponent_ng_template_8_Template, 1, 2, \"ng-template\", null, 2, i0.ɵɵtemplateRefExtractor);\n }\n if (rf & 2) {\n const normalRenderTemplate_r7 = i0.ɵɵreference(9);\n i0.ɵɵclassProp(\"emoji-mart-no-results\", ctx.noEmojiToDisplay);\n i0.ɵɵproperty(\"ngStyle\", ctx.containerStyles);\n i0.ɵɵattribute(\"aria-label\", ctx.i18n.categories[ctx.id]);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngStyle\", ctx.labelStyles);\n i0.ɵɵattribute(\"data-name\", ctx.name);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngStyle\", ctx.labelSpanStyles);\n i0.ɵɵadvance(2);\n i0.ɵɵtextInterpolate1(\" \", ctx.i18n.categories[ctx.id], \" \");\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx.virtualize)(\"ngIfElse\", normalRenderTemplate_r7);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx.noEmojiToDisplay);\n }\n },\n dependencies: [i2.NgForOf, i2.NgIf, i2.NgStyle, i1.EmojiComponent, i2.AsyncPipe],\n encapsulation: 2,\n changeDetection: 0\n });\n return CategoryComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction uniq(arr) {\n return arr.reduce((acc, item) => {\n if (!acc.includes(item)) {\n acc.push(item);\n }\n return acc;\n }, []);\n}\nfunction intersect(a, b) {\n const uniqA = uniq(a);\n const uniqB = uniq(b);\n return uniqA.filter(item => uniqB.indexOf(item) >= 0);\n}\n// https://github.com/sonicdoe/measure-scrollbar\nfunction measureScrollbar() {\n if (typeof document === 'undefined') {\n return 0;\n }\n const div = document.createElement('div');\n div.style.width = '100px';\n div.style.height = '100px';\n div.style.overflow = 'scroll';\n div.style.position = 'absolute';\n div.style.top = '-9999px';\n document.body.appendChild(div);\n const scrollbarWidth = div.offsetWidth - div.clientWidth;\n document.body.removeChild(div);\n return scrollbarWidth;\n}\nlet EmojiSearch = /*#__PURE__*/(() => {\n class EmojiSearch {\n constructor(emojiService) {\n this.emojiService = emojiService;\n this.originalPool = {};\n this.index = {};\n this.emojisList = {};\n this.emoticonsList = {};\n this.emojiSearch = {};\n for (const emojiData of this.emojiService.emojis) {\n const {\n shortNames,\n emoticons\n } = emojiData;\n const id = shortNames[0];\n for (const emoticon of emoticons) {\n if (this.emoticonsList[emoticon]) {\n continue;\n }\n this.emoticonsList[emoticon] = id;\n }\n this.emojisList[id] = this.emojiService.getSanitizedData(id);\n this.originalPool[id] = emojiData;\n }\n }\n addCustomToPool(custom, pool) {\n for (const emoji of custom) {\n const emojiId = emoji.id || emoji.shortNames[0];\n if (emojiId && !pool[emojiId]) {\n pool[emojiId] = this.emojiService.getData(emoji);\n this.emojisList[emojiId] = this.emojiService.getSanitizedData(emoji);\n }\n }\n }\n search(value, emojisToShowFilter, maxResults = 75, include = [], exclude = [], custom = []) {\n this.addCustomToPool(custom, this.originalPool);\n let results;\n let pool = this.originalPool;\n if (value.length) {\n if (value === '-' || value === '-1') {\n return [this.emojisList['-1']];\n }\n if (value === '+' || value === '+1') {\n return [this.emojisList['+1']];\n }\n let values = value.toLowerCase().split(/[\\s|,|\\-|_]+/);\n let allResults = [];\n if (values.length > 2) {\n values = [values[0], values[1]];\n }\n if (include.length || exclude.length) {\n pool = {};\n for (const category of categories$1 || []) {\n const isIncluded = include && include.length ? include.indexOf(category.id) > -1 : true;\n const isExcluded = exclude && exclude.length ? exclude.indexOf(category.id) > -1 : false;\n if (!isIncluded || isExcluded) {\n continue;\n }\n for (const emojiId of category.emojis || []) {\n // Need to make sure that pool gets keyed\n // with the correct id, which is why we call emojiService.getData below\n const emoji = this.emojiService.getData(emojiId);\n pool[emoji?.id ?? ''] = emoji;\n }\n }\n if (custom.length) {\n const customIsIncluded = include && include.length ? include.indexOf('custom') > -1 : true;\n const customIsExcluded = exclude && exclude.length ? exclude.indexOf('custom') > -1 : false;\n if (customIsIncluded && !customIsExcluded) {\n this.addCustomToPool(custom, pool);\n }\n }\n }\n allResults = values.map(v => {\n let aPool = pool;\n let aIndex = this.index;\n let length = 0;\n // eslint-disable-next-line @typescript-eslint/prefer-for-of\n for (let charIndex = 0; charIndex < v.length; charIndex++) {\n const char = v[charIndex];\n length++;\n if (!aIndex[char]) {\n aIndex[char] = {};\n }\n aIndex = aIndex[char];\n if (!aIndex.results) {\n const scores = {};\n aIndex.results = [];\n aIndex.pool = {};\n for (const id of Object.keys(aPool)) {\n const emoji = aPool[id];\n if (!this.emojiSearch[id]) {\n this.emojiSearch[id] = this.buildSearch(emoji.short_names, emoji.name, emoji.id, emoji.keywords, emoji.emoticons);\n }\n const query = this.emojiSearch[id];\n const sub = v.substr(0, length);\n const subIndex = query.indexOf(sub);\n if (subIndex !== -1) {\n let score = subIndex + 1;\n if (sub === id) {\n score = 0;\n }\n aIndex.results.push(this.emojisList[id]);\n aIndex.pool[id] = emoji;\n scores[id] = score;\n }\n }\n aIndex.results.sort((a, b) => {\n const aScore = scores[a.id];\n const bScore = scores[b.id];\n return aScore - bScore;\n });\n }\n aPool = aIndex.pool;\n }\n return aIndex.results;\n }).filter(a => a);\n if (allResults.length > 1) {\n results = intersect.apply(null, allResults);\n } else if (allResults.length) {\n results = allResults[0];\n } else {\n results = [];\n }\n }\n if (results) {\n if (emojisToShowFilter) {\n results = results.filter(result => {\n if (result && result.id) {\n return emojisToShowFilter(this.emojiService.names[result.id]);\n }\n return false;\n });\n }\n if (results && results.length > maxResults) {\n results = results.slice(0, maxResults);\n }\n }\n return results || null;\n }\n buildSearch(shortNames, name, id, keywords, emoticons) {\n const search = [];\n const addToSearch = (strings, split) => {\n if (!strings) {\n return;\n }\n const arr = Array.isArray(strings) ? strings : [strings];\n for (const str of arr) {\n const substrings = split ? str.split(/[-|_|\\s]+/) : [str];\n for (let s of substrings) {\n s = s.toLowerCase();\n if (!search.includes(s)) {\n search.push(s);\n }\n }\n }\n };\n addToSearch(shortNames, true);\n addToSearch(name, true);\n addToSearch(id, true);\n addToSearch(keywords, true);\n addToSearch(emoticons, false);\n return search.join(',');\n }\n }\n EmojiSearch.ɵfac = function EmojiSearch_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || EmojiSearch)(i0.ɵɵinject(i1.EmojiService));\n };\n EmojiSearch.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n token: EmojiSearch,\n factory: EmojiSearch.ɵfac,\n providedIn: 'root'\n });\n return EmojiSearch;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet SkinComponent = /*#__PURE__*/(() => {\n class SkinComponent {\n constructor() {\n this.changeSkin = new EventEmitter();\n this.opened = false;\n this.skinTones = [1, 2, 3, 4, 5, 6];\n }\n toggleOpen() {\n this.opened = !this.opened;\n }\n isSelected(skinTone) {\n return skinTone === this.skin;\n }\n isVisible(skinTone) {\n return this.opened || this.isSelected(skinTone);\n }\n pressed(skinTone) {\n return this.opened ? !!this.isSelected(skinTone) : '';\n }\n tabIndex(skinTone) {\n return this.isVisible(skinTone) ? '0' : '';\n }\n expanded(skinTone) {\n return this.isSelected(skinTone) ? this.opened : '';\n }\n handleClick(skin) {\n if (!this.opened) {\n this.opened = true;\n return;\n }\n this.opened = false;\n if (skin !== this.skin) {\n this.changeSkin.emit(skin);\n }\n }\n }\n SkinComponent.ɵfac = function SkinComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || SkinComponent)();\n };\n SkinComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: SkinComponent,\n selectors: [[\"emoji-skins\"]],\n inputs: {\n skin: \"skin\",\n i18n: \"i18n\"\n },\n outputs: {\n changeSkin: \"changeSkin\"\n },\n decls: 2,\n vars: 3,\n consts: [[1, \"emoji-mart-skin-swatches\"], [\"class\", \"emoji-mart-skin-swatch\", 3, \"selected\", 4, \"ngFor\", \"ngForOf\"], [1, \"emoji-mart-skin-swatch\"], [\"role\", \"button\", 3, \"click\", \"keyup.enter\", \"keyup.space\", \"tabIndex\"]],\n template: function SkinComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"section\", 0);\n i0.ɵɵtemplate(1, SkinComponent_span_1_Template, 2, 12, \"span\", 1);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n i0.ɵɵclassProp(\"opened\", ctx.opened);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngForOf\", ctx.skinTones);\n }\n },\n dependencies: [i2.NgForOf],\n encapsulation: 2,\n changeDetection: 0\n });\n return SkinComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet PreviewComponent = /*#__PURE__*/(() => {\n class PreviewComponent {\n constructor(ref, emojiService) {\n this.ref = ref;\n this.emojiService = emojiService;\n this.skinChange = new EventEmitter();\n this.emojiData = {};\n }\n ngOnChanges() {\n if (!this.emoji) {\n return;\n }\n this.emojiData = this.emojiService.getData(this.emoji, this.emojiSkin, this.emojiSet);\n const knownEmoticons = [];\n const listedEmoticons = [];\n const emoitcons = this.emojiData.emoticons || [];\n emoitcons.forEach(emoticon => {\n if (knownEmoticons.indexOf(emoticon.toLowerCase()) >= 0) {\n return;\n }\n knownEmoticons.push(emoticon.toLowerCase());\n listedEmoticons.push(emoticon);\n });\n this.listedEmoticons = listedEmoticons;\n this.ref?.detectChanges();\n }\n }\n PreviewComponent.ɵfac = function PreviewComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PreviewComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.EmojiService));\n };\n PreviewComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: PreviewComponent,\n selectors: [[\"emoji-preview\"]],\n inputs: {\n title: \"title\",\n emoji: \"emoji\",\n idleEmoji: \"idleEmoji\",\n i18n: \"i18n\",\n emojiIsNative: \"emojiIsNative\",\n emojiSkin: \"emojiSkin\",\n emojiSize: \"emojiSize\",\n emojiSet: \"emojiSet\",\n emojiSheetSize: \"emojiSheetSize\",\n emojiBackgroundImageFn: \"emojiBackgroundImageFn\",\n emojiImageUrlFn: \"emojiImageUrlFn\"\n },\n outputs: {\n skinChange: \"skinChange\"\n },\n features: [i0.ɵɵNgOnChangesFeature],\n decls: 9,\n vars: 6,\n consts: [[\"class\", \"emoji-mart-preview\", 4, \"ngIf\"], [1, \"emoji-mart-preview\", 3, \"hidden\"], [1, \"emoji-mart-preview-emoji\"], [3, \"isNative\", \"skin\", \"set\", \"emoji\", \"backgroundImageFn\", \"size\", \"imageUrlFn\", 4, \"ngIf\"], [1, \"emoji-mart-preview-data\"], [1, \"emoji-mart-title-label\"], [1, \"emoji-mart-preview-skins\"], [3, \"changeSkin\", \"skin\", \"i18n\"], [1, \"emoji-mart-preview\"], [3, \"emoji\", \"size\", \"isNative\", \"skin\", \"set\", \"sheetSize\", \"backgroundImageFn\", \"imageUrlFn\"], [1, \"emoji-mart-preview-name\"], [1, \"emoji-mart-preview-shortname\"], [\"class\", \"emoji-mart-preview-shortname\", 4, \"ngFor\", \"ngForOf\"], [1, \"emoji-mart-preview-emoticons\"], [\"class\", \"emoji-mart-preview-emoticon\", 4, \"ngFor\", \"ngForOf\"], [1, \"emoji-mart-preview-emoticon\"], [3, \"isNative\", \"skin\", \"set\", \"emoji\", \"backgroundImageFn\", \"size\", \"imageUrlFn\"]],\n template: function PreviewComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵtemplate(0, PreviewComponent_div_0_Template, 10, 12, \"div\", 0);\n i0.ɵɵelementStart(1, \"div\", 1)(2, \"div\", 2);\n i0.ɵɵtemplate(3, PreviewComponent_ngx_emoji_3_Template, 1, 7, \"ngx-emoji\", 3);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(4, \"div\", 4)(5, \"span\", 5);\n i0.ɵɵtext(6);\n i0.ɵɵelementEnd()();\n i0.ɵɵelementStart(7, \"div\", 6)(8, \"emoji-skins\", 7);\n i0.ɵɵlistener(\"changeSkin\", function PreviewComponent_Template_emoji_skins_changeSkin_8_listener($event) {\n return ctx.skinChange.emit($event);\n });\n i0.ɵɵelementEnd()()();\n }\n if (rf & 2) {\n i0.ɵɵproperty(\"ngIf\", ctx.emoji && ctx.emojiData);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"hidden\", ctx.emoji);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngIf\", ctx.idleEmoji && ctx.idleEmoji.length);\n i0.ɵɵadvance(3);\n i0.ɵɵtextInterpolate(ctx.title);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"skin\", ctx.emojiSkin)(\"i18n\", ctx.i18n);\n }\n },\n dependencies: [i2.NgForOf, i2.NgIf, i1.EmojiComponent, SkinComponent],\n encapsulation: 2,\n changeDetection: 0\n });\n return PreviewComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet id = 0;\nlet SearchComponent = /*#__PURE__*/(() => {\n class SearchComponent {\n constructor(emojiSearch) {\n this.emojiSearch = emojiSearch;\n this.maxResults = 75;\n this.autoFocus = false;\n this.include = [];\n this.exclude = [];\n this.custom = [];\n this.searchResults = new EventEmitter();\n this.enterKey = new EventEmitter();\n this.isSearching = false;\n this.query = '';\n this.inputId = `emoji-mart-search-${++id}`;\n }\n ngOnInit() {\n this.icon = this.icons.search;\n }\n ngAfterViewInit() {\n if (this.autoFocus) {\n this.inputRef.nativeElement.focus();\n }\n }\n clear() {\n this.query = '';\n this.handleSearch('');\n this.inputRef.nativeElement.focus();\n }\n handleEnterKey($event) {\n if (!this.query) {\n return;\n }\n this.enterKey.emit($event);\n $event.preventDefault();\n }\n handleSearch(value) {\n if (value === '') {\n this.icon = this.icons.search;\n this.isSearching = false;\n } else {\n this.icon = this.icons.delete;\n this.isSearching = true;\n }\n const emojis = this.emojiSearch.search(this.query, this.emojisToShowFilter, this.maxResults, this.include, this.exclude, this.custom);\n this.searchResults.emit(emojis);\n }\n handleChange() {\n this.handleSearch(this.query);\n }\n }\n SearchComponent.ɵfac = function SearchComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || SearchComponent)(i0.ɵɵdirectiveInject(EmojiSearch));\n };\n SearchComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: SearchComponent,\n selectors: [[\"emoji-search\"]],\n viewQuery: function SearchComponent_Query(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵviewQuery(_c2, 7);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputRef = _t.first);\n }\n },\n inputs: {\n maxResults: \"maxResults\",\n autoFocus: \"autoFocus\",\n i18n: \"i18n\",\n include: \"include\",\n exclude: \"exclude\",\n custom: \"custom\",\n icons: \"icons\",\n emojisToShowFilter: \"emojisToShowFilter\"\n },\n outputs: {\n searchResults: \"searchResults\",\n enterKey: \"enterKey\"\n },\n decls: 8,\n vars: 9,\n consts: [[\"inputRef\", \"\"], [1, \"emoji-mart-search\"], [\"type\", \"search\", 3, \"keyup.enter\", \"ngModelChange\", \"id\", \"placeholder\", \"autofocus\", \"ngModel\"], [1, \"emoji-mart-sr-only\", 3, \"htmlFor\"], [\"type\", \"button\", 1, \"emoji-mart-search-icon\", 3, \"click\", \"keyup.enter\", \"disabled\"], [\"xmlns\", \"http://www.w3.org/2000/svg\", \"viewBox\", \"0 0 20 20\", \"width\", \"13\", \"height\", \"13\", \"opacity\", \"0.5\"]],\n template: function SearchComponent_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"div\", 1)(1, \"input\", 2, 0);\n i0.ɵɵlistener(\"keyup.enter\", function SearchComponent_Template_input_keyup_enter_1_listener($event) {\n i0.ɵɵrestoreView(_r1);\n return i0.ɵɵresetView(ctx.handleEnterKey($event));\n });\n i0.ɵɵtwoWayListener(\"ngModelChange\", function SearchComponent_Template_input_ngModelChange_1_listener($event) {\n i0.ɵɵrestoreView(_r1);\n i0.ɵɵtwoWayBindingSet(ctx.query, $event) || (ctx.query = $event);\n return i0.ɵɵresetView($event);\n });\n i0.ɵɵlistener(\"ngModelChange\", function SearchComponent_Template_input_ngModelChange_1_listener() {\n i0.ɵɵrestoreView(_r1);\n return i0.ɵɵresetView(ctx.handleChange());\n });\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(3, \"label\", 3);\n i0.ɵɵtext(4);\n i0.ɵɵelementEnd();\n i0.ɵɵelementStart(5, \"button\", 4);\n i0.ɵɵlistener(\"click\", function SearchComponent_Template_button_click_5_listener() {\n i0.ɵɵrestoreView(_r1);\n return i0.ɵɵresetView(ctx.clear());\n })(\"keyup.enter\", function SearchComponent_Template_button_keyup_enter_5_listener() {\n i0.ɵɵrestoreView(_r1);\n return i0.ɵɵresetView(ctx.clear());\n });\n i0.ɵɵnamespaceSVG();\n i0.ɵɵelementStart(6, \"svg\", 5);\n i0.ɵɵelement(7, \"path\");\n i0.ɵɵelementEnd()()();\n }\n if (rf & 2) {\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"id\", ctx.inputId)(\"placeholder\", ctx.i18n.search)(\"autofocus\", ctx.autoFocus);\n i0.ɵɵtwoWayProperty(\"ngModel\", ctx.query);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"htmlFor\", ctx.inputId);\n i0.ɵɵadvance();\n i0.ɵɵtextInterpolate1(\" \", ctx.i18n.search, \" \");\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"disabled\", !ctx.isSearching);\n i0.ɵɵattribute(\"aria-label\", ctx.i18n.clear);\n i0.ɵɵadvance(2);\n i0.ɵɵattribute(\"d\", ctx.icon);\n }\n },\n dependencies: [i2$1.DefaultValueAccessor, i2$1.NgControlStatus, i2$1.NgModel],\n encapsulation: 2\n });\n return SearchComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/* eslint-disable max-len */\nconst categories = {\n activity: `M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24m10 11h-5c.3-2.5 1.3-4.8 2-6.1a10 10 0 0 1 3 6.1m-9 0V2a10 10 0 0 1 4.4 1.6A18 18 0 0 0 15 11h-2zm-2 0H9a18 18 0 0 0-2.4-7.4A10 10 0 0 1 11 2.1V11zm0 2v9a10 10 0 0 1-4.4-1.6A18 18 0 0 0 9 13h2zm4 0a18 18 0 0 0 2.4 7.4 10 10 0 0 1-4.4 1.5V13h2zM5 4.9c.7 1.3 1.7 3.6 2 6.1H2a10 10 0 0 1 3-6.1M2 13h5c-.3 2.5-1.3 4.8-2 6.1A10 10 0 0 1 2 13m17 6.1c-.7-1.3-1.7-3.6-2-6.1h5a10 10 0 0 1-3 6.1`,\n custom: `M10 1h3v21h-3zm10.186 4l1.5 2.598L3.5 18.098 2 15.5zM2 7.598L3.5 5l18.186 10.5-1.5 2.598z`,\n flags: `M0 0l6 24h2L2 0zm21 5h-4l-1-4H4l3 12h3l1 4h13L21 5zM6.6 3h7.8l2 8H8.6l-2-8zm8.8 10l-2.9 1.9-.4-1.9h3.3zm3.6 0l-1.5-6h2l2 8H16l3-2z`,\n foods: `M17 5c-1.8 0-2.9.4-3.7 1 .5-1.3 1.8-3 4.7-3a1 1 0 0 0 0-2c-3 0-4.6 1.3-5.5 2.5l-.2.2c-.6-1.9-1.5-3.7-3-3.7C8.5 0 7.7.3 7 1c-2 1.5-1.7 2.9-.5 4C3.6 5.2 0 7.4 0 13c0 4.6 5 11 9 11 2 0 2.4-.5 3-1 .6.5 1 1 3 1 4 0 9-6.4 9-11 0-6-4-8-7-8M8.2 2.5c.7-.5 1-.5 1-.5.4.2 1 1.4 1.4 3-1.6-.6-2.8-1.3-3-1.8l.6-.7M15 22c-1 0-1.2-.1-1.6-.4l-.1-.2a2 2 0 0 0-2.6 0l-.1.2c-.4.3-.5.4-1.6.4-2.8 0-7-5.4-7-9 0-6 4.5-6 5-6 2 0 2.5.4 3.4 1.2l.3.3a2 2 0 0 0 2.6 0l.3-.3c1-.8 1.5-1.2 3.4-1.2.5 0 5 .1 5 6 0 3.6-4.2 9-7 9`,\n nature: `M15.5 8a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m-7 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m10.43-8h-.02c-.97 0-2.14.79-3.02 1.5A13.88 13.88 0 0 0 12 .99c-1.28 0-2.62.13-3.87.51C7.24.8 6.07 0 5.09 0h-.02C3.35 0 .07 2.67 0 7.03c-.04 2.47.28 4.23 1.04 5 .26.27.88.69 1.3.9.19 3.17.92 5.23 2.53 6.37.9.64 2.19.95 3.2 1.1-.03.2-.07.4-.07.6 0 1.77 2.35 3 4 3s4-1.23 4-3c0-.2-.04-.4-.07-.59 2.57-.38 5.43-1.87 5.92-7.58.4-.22.89-.57 1.1-.8.77-.76 1.09-2.52 1.05-5C23.93 2.67 20.65 0 18.93 0M3.23 9.13c-.24.29-.84 1.16-.9 1.24A9.67 9.67 0 0 1 2 7.08c.05-3.28 2.48-4.97 3.1-5.03.25.02.72.27 1.26.65A7.95 7.95 0 0 0 4 7.82c-.14.55-.4.86-.79 1.31M12 22c-.9 0-1.95-.7-2-1 0-.65.47-1.24 1-1.6v.6a1 1 0 1 0 2 0v-.6c.52.36 1 .95 1 1.6-.05.3-1.1 1-2 1m3-3.48v.02a4.75 4.75 0 0 0-1.26-1.02c1.09-.52 2.24-1.33 2.24-2.22 0-1.84-1.78-2.2-3.98-2.2s-3.98.36-3.98 2.2c0 .89 1.15 1.7 2.24 2.22A4.8 4.8 0 0 0 9 18.54v-.03a6.1 6.1 0 0 1-2.97-.84c-1.3-.92-1.84-3.04-1.86-6.48l.03-.04c.5-.82 1.49-1.45 1.8-3.1C6 6 7.36 4.42 8.36 3.53c1.01-.35 2.2-.53 3.59-.53 1.45 0 2.68.2 3.73.57 1 .9 2.32 2.46 2.32 4.48.31 1.65 1.3 2.27 1.8 3.1l.1.18c-.06 5.97-1.95 7.01-4.9 7.19m6.63-8.2l-.11-.2a7.59 7.59 0 0 0-.74-.98 3.02 3.02 0 0 1-.79-1.32 7.93 7.93 0 0 0-2.35-5.12c.53-.38 1-.63 1.26-.65.64.07 3.05 1.77 3.1 5.03.02 1.81-.35 3.22-.37 3.24`,\n objects: `M12 0a9 9 0 0 0-5 16.5V21s2 3 5 3 5-3 5-3v-4.5A9 9 0 0 0 12 0zm0 2a7 7 0 1 1 0 14 7 7 0 0 1 0-14zM9 17.5a9 9 0 0 0 6 0v.8a7 7 0 0 1-3 .7 7 7 0 0 1-3-.7v-.8zm.2 3a8.9 8.9 0 0 0 2.8.5c1 0 1.9-.2 2.8-.5-.6.7-1.6 1.5-2.8 1.5-1.1 0-2.1-.8-2.8-1.5zm5.5-8.1c-.8 0-1.1-.8-1.5-1.8-.5-1-.7-1.5-1.2-1.5s-.8.5-1.3 1.5c-.4 1-.8 1.8-1.6 1.8h-.3c-.5-.2-.8-.7-1.3-1.8l-.2-1A3 3 0 0 0 7 9a1 1 0 0 1 0-2c1.7 0 2 1.4 2.2 2.1.5-1 1.3-2 2.8-2 1.5 0 2.3 1.1 2.7 2.1.2-.8.6-2.2 2.3-2.2a1 1 0 1 1 0 2c-.2 0-.3.5-.3.7a6.5 6.5 0 0 1-.3 1c-.5 1-.8 1.7-1.7 1.7`,\n people: `M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24m0 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20M8 7a2 2 0 1 0 0 4 2 2 0 0 0 0-4m8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4m-.8 8c-.7 1.2-1.8 2-3.3 2-1.5 0-2.7-.8-3.4-2H15m3-2H6a6 6 0 1 0 12 0`,\n places: `M6.5 12a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5m0 3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5.5.2.5.5-.2.5-.5.5m11-3a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5m0 3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5.5.2.5.5-.2.5-.5.5m5-5.5l-1-.4-.1-.1h.6c.6 0 1-.4 1-1 0-1-.9-2-2-2h-.6l-.8-1.7A3 3 0 0 0 16.8 2H7.2a3 3 0 0 0-2.8 2.3L3.6 6H3a2 2 0 0 0-2 2c0 .6.4 1 1 1h.6v.1l-1 .4a2 2 0 0 0-1.4 2l.7 7.6a1 1 0 0 0 1 .9H3v1c0 1.1.9 2 2 2h2a2 2 0 0 0 2-2v-1h6v1c0 1.1.9 2 2 2h2a2 2 0 0 0 2-2v-1h1.1a1 1 0 0 0 1-.9l.7-7.5a2 2 0 0 0-1.3-2.1M6.3 4.9c.1-.5.5-.9 1-.9h9.5c.4 0 .8.4 1 .9L19.2 9H4.7l1.6-4.1zM7 21H5v-1h2v1zm12 0h-2v-1h2v1zm2.2-3H2.8l-.7-6.6.9-.4h18l.9.4-.7 6.6z`,\n recent: `M13 4h-2v7H9v2h2v2h2v-2h4v-2h-4zm-1-4a12 12 0 1 0 0 24 12 12 0 0 0 0-24m0 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20`,\n symbols: `M0 0h11v2H0zm4 11h3V6h4V4H0v2h4zm11.5 6a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5m0-2.99a.5.5 0 0 1 0 .99c-.28 0-.5-.22-.5-.5s.22-.49.5-.49m6 5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5m0 2.99a.5.5 0 0 1-.5-.5.5.5 0 0 1 1 .01.5.5 0 0 1-.5.49m.5-9l-9 9 1.51 1.5 9-9zm-5-2c2.2 0 4-1.12 4-2.5V2s.98-.16 1.5.95C23 4.05 23 6 23 6s1-1.12 1-3.13C24-.02 21 0 21 0h-2v6.35A5.85 5.85 0 0 0 17 6c-2.2 0-4 1.12-4 2.5s1.8 2.5 4 2.5m-6.7 9.48L8.82 18.9a47.54 47.54 0 0 1-1.44 1.13c-.3-.3-.99-1.02-2.04-2.19.9-.83 1.47-1.46 1.72-1.89s.38-.87.38-1.33c0-.6-.27-1.18-.82-1.76-.54-.58-1.33-.87-2.35-.87-1 0-1.79.29-2.34.87-.56.6-.83 1.18-.83 1.79 0 .81.42 1.75 1.25 2.8a6.57 6.57 0 0 0-1.8 1.79 3.46 3.46 0 0 0-.51 1.83c0 .86.3 1.56.92 2.1a3.5 3.5 0 0 0 2.42.83c1.17 0 2.44-.38 3.81-1.14L8.23 24h2.82l-2.09-2.38 1.34-1.14zM3.56 14.1a1.02 1.02 0 0 1 .73-.28c.31 0 .56.08.75.25a.85.85 0 0 1 .28.66c0 .52-.42 1.11-1.26 1.78-.53-.65-.8-1.23-.8-1.74a.9.9 0 0 1 .3-.67m.18 7.9c-.43 0-.78-.12-1.06-.35-.28-.23-.41-.49-.41-.76 0-.6.5-1.3 1.52-2.09a31.23 31.23 0 0 0 2.25 2.44c-.92.5-1.69.76-2.3.76`\n};\nconst search = {\n search: `M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z`,\n delete: `M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z`\n};\nconst I18N = {\n search: 'Search',\n emojilist: 'List of emoji',\n notfound: 'No Emoji Found',\n clear: 'Clear',\n categories: {\n search: 'Search Results',\n recent: 'Frequently Used',\n people: 'Smileys & People',\n nature: 'Animals & Nature',\n foods: 'Food & Drink',\n activity: 'Activity',\n places: 'Travel & Places',\n objects: 'Objects',\n symbols: 'Symbols',\n flags: 'Flags',\n custom: 'Custom'\n },\n skintones: {\n 1: 'Default Skin Tone',\n 2: 'Light Skin Tone',\n 3: 'Medium-Light Skin Tone',\n 4: 'Medium Skin Tone',\n 5: 'Medium-Dark Skin Tone',\n 6: 'Dark Skin Tone'\n }\n};\nlet PickerComponent = /*#__PURE__*/(() => {\n class PickerComponent {\n constructor(ngZone, renderer, ref, frequently, platformId) {\n this.ngZone = ngZone;\n this.renderer = renderer;\n this.ref = ref;\n this.frequently = frequently;\n this.platformId = platformId;\n this.perLine = 9;\n this.totalFrequentLines = 4;\n this.i18n = {};\n this.style = {};\n this.title = 'Emoji Mart™';\n this.emoji = 'department_store';\n this.darkMode = !!(typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches);\n this.color = '#ae65c5';\n this.hideObsolete = true;\n /** all categories shown */\n this.categories = [];\n /** used to temporarily draw categories */\n this.activeCategories = [];\n this.set = 'apple';\n this.skin = 1;\n /** Renders the native unicode emoji */\n this.isNative = false;\n this.emojiSize = 24;\n this.sheetSize = 64;\n this.showPreview = true;\n this.emojiTooltip = false;\n this.autoFocus = false;\n this.custom = [];\n this.hideRecent = true;\n this.notFoundEmoji = 'sleuth_or_spy';\n this.categoriesIcons = categories;\n this.searchIcons = search;\n this.useButton = false;\n this.enableFrequentEmojiSort = false;\n this.enableSearch = true;\n this.showSingleCategory = false;\n this.virtualize = false;\n this.virtualizeOffset = 0;\n this.emojiClick = new EventEmitter();\n this.emojiSelect = new EventEmitter();\n this.skinChange = new EventEmitter();\n this.scrollHeight = 0;\n this.clientHeight = 0;\n this.clientWidth = 0;\n this.firstRender = true;\n this.previewEmoji = null;\n this.animationFrameRequestId = null;\n this.NAMESPACE = 'emoji-mart';\n this.measureScrollbar = 0;\n this.RECENT_CATEGORY = {\n id: 'recent',\n name: 'Recent',\n emojis: null\n };\n this.SEARCH_CATEGORY = {\n id: 'search',\n name: 'Search',\n emojis: null,\n anchor: false\n };\n this.CUSTOM_CATEGORY = {\n id: 'custom',\n name: 'Custom',\n emojis: []\n };\n this.backgroundImageFn = (set, sheetSize) => `https://unpkg.com/emoji-datasource-${this.set}@6.0.1/img/${this.set}/sheets-256/${this.sheetSize}.png`;\n }\n ngOnInit() {\n // measure scroll\n this.measureScrollbar = measureScrollbar();\n this.i18n = {\n ...I18N,\n ...this.i18n\n };\n this.i18n.categories = {\n ...I18N.categories,\n ...this.i18n.categories\n };\n this.skin = JSON.parse(isPlatformBrowser(this.platformId) && localStorage.getItem(`${this.NAMESPACE}.skin`) || 'null') || this.skin;\n const allCategories = [...categories$1];\n if (this.custom.length > 0) {\n this.CUSTOM_CATEGORY.emojis = this.custom.map(emoji => {\n return {\n ...emoji,\n // `` expects emoji to have an `id`.\n id: emoji.shortNames[0],\n custom: true\n };\n });\n allCategories.push(this.CUSTOM_CATEGORY);\n }\n if (this.include !== undefined) {\n allCategories.sort((a, b) => {\n if (this.include.indexOf(a.id) > this.include.indexOf(b.id)) {\n return 1;\n }\n return -1;\n });\n }\n for (const category of allCategories) {\n const isIncluded = this.include && this.include.length ? this.include.indexOf(category.id) > -1 : true;\n const isExcluded = this.exclude && this.exclude.length ? this.exclude.indexOf(category.id) > -1 : false;\n if (!isIncluded || isExcluded) {\n continue;\n }\n if (this.emojisToShowFilter) {\n const newEmojis = [];\n const {\n emojis\n } = category;\n // eslint-disable-next-line @typescript-eslint/prefer-for-of\n for (let emojiIndex = 0; emojiIndex < emojis.length; emojiIndex++) {\n const emoji = emojis[emojiIndex];\n if (this.emojisToShowFilter(emoji)) {\n newEmojis.push(emoji);\n }\n }\n if (newEmojis.length) {\n const newCategory = {\n emojis: newEmojis,\n name: category.name,\n id: category.id\n };\n this.categories.push(newCategory);\n }\n } else {\n this.categories.push(category);\n }\n this.categoriesIcons = {\n ...categories,\n ...this.categoriesIcons\n };\n this.searchIcons = {\n ...search,\n ...this.searchIcons\n };\n }\n const includeRecent = this.include && this.include.length ? this.include.indexOf(this.RECENT_CATEGORY.id) > -1 : true;\n const excludeRecent = this.exclude && this.exclude.length ? this.exclude.indexOf(this.RECENT_CATEGORY.id) > -1 : false;\n if (includeRecent && !excludeRecent) {\n this.hideRecent = false;\n this.categories.unshift(this.RECENT_CATEGORY);\n }\n if (this.categories[0]) {\n this.categories[0].first = true;\n }\n this.categories.unshift(this.SEARCH_CATEGORY);\n this.selected = this.categories.filter(category => category.first)[0].name;\n // Need to be careful if small number of categories\n const categoriesToLoadFirst = Math.min(this.categories.length, 3);\n this.setActiveCategories(this.activeCategories = this.categories.slice(0, categoriesToLoadFirst));\n // Trim last active category\n const lastActiveCategoryEmojis = this.categories[categoriesToLoadFirst - 1].emojis.slice();\n this.categories[categoriesToLoadFirst - 1].emojis = lastActiveCategoryEmojis.slice(0, 60);\n setTimeout(() => {\n // Restore last category\n this.categories[categoriesToLoadFirst - 1].emojis = lastActiveCategoryEmojis;\n this.setActiveCategories(this.categories);\n // The `setTimeout` will trigger the change detection, but since we're inside\n // the OnPush component we can run change detection locally starting from this\n // component and going down to the children.\n this.ref.detectChanges();\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n isPlatformBrowser(this.platformId) && this.ngZone.runOutsideAngular(() => {\n // The `updateCategoriesSize` doesn't change properties that are used\n // in templates, thus this is run in the context of the root zone to avoid\n // running change detection.\n requestAnimationFrame(() => {\n this.updateCategoriesSize();\n });\n });\n });\n this.ngZone.runOutsideAngular(() => {\n // DOM events that are listened by Angular inside the template trigger change detection\n // and also wrapped into additional functions that call `markForCheck()`. We listen `scroll`\n // in the context of the root zone since it will not trigger change detection each time\n // the `scroll` event is dispatched.\n this.scrollListener = this.renderer.listen(this.scrollRef.nativeElement, 'scroll', () => {\n this.handleScroll();\n });\n });\n }\n ngOnDestroy() {\n this.scrollListener?.();\n // This is called here because the component might be destroyed\n // but there will still be a `requestAnimationFrame` callback in the queue\n // that calls `detectChanges()` on the `ViewRef`. This will lead to a runtime\n // exception if the `detectChanges()` is called after the `ViewRef` is destroyed.\n this.cancelAnimationFrame();\n }\n setActiveCategories(categoriesToMakeActive) {\n if (this.showSingleCategory) {\n this.activeCategories = categoriesToMakeActive.filter(x => x.name === this.selected || x === this.SEARCH_CATEGORY);\n } else {\n this.activeCategories = categoriesToMakeActive;\n }\n }\n updateCategoriesSize() {\n this.categoryRefs.forEach(component => component.memoizeSize());\n if (this.scrollRef) {\n const target = this.scrollRef.nativeElement;\n this.scrollHeight = target.scrollHeight;\n this.clientHeight = target.clientHeight;\n this.clientWidth = target.clientWidth;\n }\n }\n handleAnchorClick($event) {\n this.updateCategoriesSize();\n this.selected = $event.category.name;\n this.setActiveCategories(this.categories);\n if (this.SEARCH_CATEGORY.emojis) {\n this.handleSearch(null);\n this.searchRef?.clear();\n this.handleAnchorClick($event);\n return;\n }\n const component = this.categoryRefs.find(n => n.id === $event.category.id);\n if (component) {\n let {\n top\n } = component;\n if ($event.category.first) {\n top = 0;\n } else {\n top += 1;\n }\n this.scrollRef.nativeElement.scrollTop = top;\n }\n this.nextScroll = $event.category.name;\n // handle component scrolling to load emojis\n for (const category of this.categories) {\n const componentToScroll = this.categoryRefs.find(({\n id\n }) => id === category.id);\n componentToScroll?.handleScroll(this.scrollRef.nativeElement.scrollTop);\n }\n }\n categoryTrack(index, item) {\n return item.id;\n }\n handleScroll(noSelectionChange = false) {\n if (this.nextScroll) {\n this.selected = this.nextScroll;\n this.nextScroll = undefined;\n this.ref.detectChanges();\n return;\n }\n if (!this.scrollRef) {\n return;\n }\n if (this.showSingleCategory) {\n return;\n }\n let activeCategory;\n if (this.SEARCH_CATEGORY.emojis) {\n activeCategory = this.SEARCH_CATEGORY;\n } else {\n const target = this.scrollRef.nativeElement;\n // check scroll is not at bottom\n if (target.scrollTop === 0) {\n // hit the TOP\n activeCategory = this.categories.find(n => n.first === true);\n } else if (target.scrollHeight - target.scrollTop === this.clientHeight) {\n // scrolled to bottom activate last category\n activeCategory = this.categories[this.categories.length - 1];\n } else {\n // scrolling\n for (const category of this.categories) {\n const component = this.categoryRefs.find(({\n id\n }) => id === category.id);\n const active = component?.handleScroll(target.scrollTop);\n if (active) {\n activeCategory = category;\n }\n }\n }\n this.scrollTop = target.scrollTop;\n }\n // This will allow us to run the change detection only when the category changes.\n if (!noSelectionChange && activeCategory && activeCategory.name !== this.selected) {\n this.selected = activeCategory.name;\n this.ref.detectChanges();\n } else if (noSelectionChange) {\n this.ref.detectChanges();\n }\n }\n handleSearch($emojis) {\n this.SEARCH_CATEGORY.emojis = $emojis;\n for (const component of this.categoryRefs.toArray()) {\n if (component.name === 'Search') {\n component.emojis = $emojis;\n component.updateDisplay($emojis ? 'block' : 'none');\n } else {\n component.updateDisplay($emojis ? 'none' : 'block');\n }\n }\n this.scrollRef.nativeElement.scrollTop = 0;\n this.handleScroll();\n }\n handleEnterKey($event, emoji) {\n if (!emoji) {\n if (this.SEARCH_CATEGORY.emojis !== null && this.SEARCH_CATEGORY.emojis.length) {\n emoji = this.SEARCH_CATEGORY.emojis[0];\n if (emoji) {\n this.emojiSelect.emit({\n $event,\n emoji\n });\n } else {\n return;\n }\n }\n }\n if (!this.hideRecent && !this.recent && emoji) {\n this.frequently.add(emoji);\n }\n const component = this.categoryRefs.toArray()[1];\n if (component && this.enableFrequentEmojiSort) {\n component.updateRecentEmojis();\n component.ref.markForCheck();\n }\n }\n handleEmojiOver($event) {\n if (!this.showPreview || !this.previewRef) {\n return;\n }\n const emojiData = this.CUSTOM_CATEGORY.emojis.find(customEmoji => customEmoji.id === $event.emoji.id);\n if (emojiData) {\n $event.emoji = {\n ...emojiData\n };\n }\n this.previewEmoji = $event.emoji;\n this.cancelAnimationFrame();\n this.ref.detectChanges();\n }\n handleEmojiLeave() {\n if (!this.showPreview || !this.previewRef) {\n return;\n }\n // Note: `handleEmojiLeave` will be invoked outside of the Angular zone because of the `mouseleave`\n // event set up outside of the Angular zone in `ngx-emoji`. See `setupMouseLeaveListener`.\n // This is done explicitly because we don't have to run redundant change detection since we\n // would still want to leave the Angular zone here when scheduling animation frame.\n this.animationFrameRequestId = requestAnimationFrame(() => {\n this.previewEmoji = null;\n this.ref.detectChanges();\n });\n }\n handleEmojiClick($event) {\n this.emojiClick.emit($event);\n this.emojiSelect.emit($event);\n this.handleEnterKey($event.$event, $event.emoji);\n }\n handleSkinChange(skin) {\n this.skin = skin;\n localStorage.setItem(`${this.NAMESPACE}.skin`, String(skin));\n this.skinChange.emit(skin);\n }\n getWidth() {\n if (this.style && this.style.width) {\n return this.style.width;\n }\n return this.perLine * (this.emojiSize + 12) + 12 + 2 + this.measureScrollbar + 'px';\n }\n cancelAnimationFrame() {\n if (this.animationFrameRequestId !== null) {\n cancelAnimationFrame(this.animationFrameRequestId);\n this.animationFrameRequestId = null;\n }\n }\n }\n PickerComponent.ɵfac = function PickerComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PickerComponent)(i0.ɵɵdirectiveInject(i0.NgZone), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(EmojiFrequentlyService), i0.ɵɵdirectiveInject(PLATFORM_ID));\n };\n PickerComponent.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: PickerComponent,\n selectors: [[\"emoji-mart\"]],\n viewQuery: function PickerComponent_Query(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵviewQuery(_c3, 7);\n i0.ɵɵviewQuery(PreviewComponent, 5);\n i0.ɵɵviewQuery(SearchComponent, 5);\n i0.ɵɵviewQuery(CategoryComponent, 5);\n }\n if (rf & 2) {\n let _t;\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.scrollRef = _t.first);\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.previewRef = _t.first);\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.searchRef = _t.first);\n i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.categoryRefs = _t);\n }\n },\n inputs: {\n perLine: \"perLine\",\n totalFrequentLines: \"totalFrequentLines\",\n i18n: \"i18n\",\n style: \"style\",\n title: \"title\",\n emoji: \"emoji\",\n darkMode: \"darkMode\",\n color: \"color\",\n hideObsolete: \"hideObsolete\",\n categories: \"categories\",\n activeCategories: \"activeCategories\",\n set: \"set\",\n skin: \"skin\",\n isNative: \"isNative\",\n emojiSize: \"emojiSize\",\n sheetSize: \"sheetSize\",\n emojisToShowFilter: \"emojisToShowFilter\",\n showPreview: \"showPreview\",\n emojiTooltip: \"emojiTooltip\",\n autoFocus: \"autoFocus\",\n custom: \"custom\",\n hideRecent: \"hideRecent\",\n imageUrlFn: \"imageUrlFn\",\n include: \"include\",\n exclude: \"exclude\",\n notFoundEmoji: \"notFoundEmoji\",\n categoriesIcons: \"categoriesIcons\",\n searchIcons: \"searchIcons\",\n useButton: \"useButton\",\n enableFrequentEmojiSort: \"enableFrequentEmojiSort\",\n enableSearch: \"enableSearch\",\n showSingleCategory: \"showSingleCategory\",\n virtualize: \"virtualize\",\n virtualizeOffset: \"virtualizeOffset\",\n recent: \"recent\",\n backgroundImageFn: \"backgroundImageFn\"\n },\n outputs: {\n emojiClick: \"emojiClick\",\n emojiSelect: \"emojiSelect\",\n skinChange: \"skinChange\"\n },\n decls: 8,\n vars: 16,\n consts: [[\"scrollRef\", \"\"], [3, \"ngStyle\"], [1, \"emoji-mart-bar\"], [3, \"anchorClick\", \"categories\", \"color\", \"selected\", \"i18n\", \"icons\"], [3, \"i18n\", \"include\", \"exclude\", \"custom\", \"autoFocus\", \"icons\", \"emojisToShowFilter\", \"searchResults\", \"enterKey\", 4, \"ngIf\"], [1, \"emoji-mart-scroll\"], [3, \"id\", \"name\", \"emojis\", \"perLine\", \"totalFrequentLines\", \"hasStickyPosition\", \"i18n\", \"hideObsolete\", \"notFoundEmoji\", \"custom\", \"recent\", \"virtualize\", \"virtualizeOffset\", \"emojiIsNative\", \"emojiSkin\", \"emojiSize\", \"emojiSet\", \"emojiSheetSize\", \"emojiForceSize\", \"emojiTooltip\", \"emojiBackgroundImageFn\", \"emojiImageUrlFn\", \"emojiUseButton\", \"emojiOverOutsideAngular\", \"emojiLeaveOutsideAngular\", \"emojiClick\", 4, \"ngFor\", \"ngForOf\", \"ngForTrackBy\"], [\"class\", \"emoji-mart-bar\", 4, \"ngIf\"], [3, \"searchResults\", \"enterKey\", \"i18n\", \"include\", \"exclude\", \"custom\", \"autoFocus\", \"icons\", \"emojisToShowFilter\"], [3, \"emojiOverOutsideAngular\", \"emojiLeaveOutsideAngular\", \"emojiClick\", \"id\", \"name\", \"emojis\", \"perLine\", \"totalFrequentLines\", \"hasStickyPosition\", \"i18n\", \"hideObsolete\", \"notFoundEmoji\", \"custom\", \"recent\", \"virtualize\", \"virtualizeOffset\", \"emojiIsNative\", \"emojiSkin\", \"emojiSize\", \"emojiSet\", \"emojiSheetSize\", \"emojiForceSize\", \"emojiTooltip\", \"emojiBackgroundImageFn\", \"emojiImageUrlFn\", \"emojiUseButton\"], [3, \"skinChange\", \"emoji\", \"idleEmoji\", \"emojiIsNative\", \"emojiSize\", \"emojiSkin\", \"emojiSet\", \"i18n\", \"emojiSheetSize\", \"emojiBackgroundImageFn\", \"emojiImageUrlFn\"]],\n template: function PickerComponent_Template(rf, ctx) {\n if (rf & 1) {\n const _r1 = i0.ɵɵgetCurrentView();\n i0.ɵɵelementStart(0, \"section\", 1)(1, \"div\", 2)(2, \"emoji-mart-anchors\", 3);\n i0.ɵɵlistener(\"anchorClick\", function PickerComponent_Template_emoji_mart_anchors_anchorClick_2_listener($event) {\n i0.ɵɵrestoreView(_r1);\n return i0.ɵɵresetView(ctx.handleAnchorClick($event));\n });\n i0.ɵɵelementEnd()();\n i0.ɵɵtemplate(3, PickerComponent_emoji_search_3_Template, 1, 7, \"emoji-search\", 4);\n i0.ɵɵelementStart(4, \"section\", 5, 0);\n i0.ɵɵtemplate(6, PickerComponent_emoji_category_6_Template, 1, 23, \"emoji-category\", 6);\n i0.ɵɵelementEnd();\n i0.ɵɵtemplate(7, PickerComponent_div_7_Template, 2, 11, \"div\", 7);\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n i0.ɵɵclassMapInterpolate1(\"emoji-mart \", ctx.darkMode ? \"emoji-mart-dark\" : \"\", \"\");\n i0.ɵɵstyleProp(\"width\", ctx.getWidth());\n i0.ɵɵproperty(\"ngStyle\", ctx.style);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"categories\", ctx.categories)(\"color\", ctx.color)(\"selected\", ctx.selected)(\"i18n\", ctx.i18n)(\"icons\", ctx.categoriesIcons);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx.enableSearch);\n i0.ɵɵadvance();\n i0.ɵɵattribute(\"aria-label\", ctx.i18n.emojilist);\n i0.ɵɵadvance(2);\n i0.ɵɵproperty(\"ngForOf\", ctx.activeCategories)(\"ngForTrackBy\", ctx.categoryTrack);\n i0.ɵɵadvance();\n i0.ɵɵproperty(\"ngIf\", ctx.showPreview);\n }\n },\n dependencies: [i2.NgForOf, i2.NgIf, i2.NgStyle, AnchorsComponent, CategoryComponent, SearchComponent, PreviewComponent],\n encapsulation: 2,\n changeDetection: 0\n });\n return PickerComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet PickerModule = /*#__PURE__*/(() => {\n class PickerModule {}\n PickerModule.ɵfac = function PickerModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || PickerModule)();\n };\n PickerModule.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: PickerModule\n });\n PickerModule.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n imports: [CommonModule, FormsModule, EmojiModule]\n });\n return PickerModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { AnchorsComponent, CategoryComponent, EmojiFrequentlyService, EmojiSearch, PickerComponent, PickerModule, PreviewComponent, SearchComponent, SkinComponent };\n","import { Component, Inject, OnInit } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { CURRENCY, PRICE_TYPES } from '../../enum/constants';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { PriceForm } from './change-price.inteface';\n\n\n@Component({\n templateUrl: './change-price.component.html',\n styleUrls: ['./change-price.component.scss'],\n})\nexport class ChangePriceComponent implements OnInit {\n\n public priceTypeControl: FormControl = new FormControl('', [Validators.required]);\n public priceForm!: FormGroup;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n ) {}\n\n ngOnInit() {\n const price = this.data?.data;\n if (price?.value) {\n this.priceTypeControl.setValue('price');\n } else if (price?.value === 0) {\n this.priceTypeControl.setValue('free');\n } else if (price?.trade) {\n this.priceTypeControl.setValue('trade');\n }\n this.initForm(price);\n }\n\n private initForm(price: any) {\n this.priceForm = new FormGroup({\n value: new FormControl(price?.value ?? null),\n currency: new FormControl(price?.currency ?? 'UAH'),\n trade: new FormControl(price?.trade ?? null),\n negotiable: new FormControl(price?.negotiable ?? null),\n });\n this.setChoice(this.priceTypeControl.getRawValue());\n }\n\n public setChoice(event: any) {\n if (event.value === 'price') {\n this.priceForm.get('value')?.setValidators(Validators.required);\n this.priceForm.get('currency')?.setValidators(Validators.required);\n this.priceForm.updateValueAndValidity();\n } else {\n this.priceForm.get('value')?.clearValidators();\n this.priceForm.get('currency')?.clearValidators();\n this.priceForm.updateValueAndValidity();\n }\n }\n\n public save() {\n if (this.priceForm.valid) {\n const formValue = this.priceForm.getRawValue();\n\n switch (this.priceTypeControl.value) {\n case 'free':\n formValue.value = 0;\n formValue.trade = false;\n formValue.negotiable = false;\n break;\n case 'trade':\n formValue.value = null;\n formValue.trade = true;\n formValue.negotiable = false;\n break;\n case 'price':\n formValue.trade = false;\n break;\n }\n\n this.dialogRef.close({\n value: formValue.value,\n currency: formValue.currency,\n trade: formValue.trade,\n negotiable: formValue.negotiable\n });\n }\n else {\n this.priceForm.markAllAsTouched();\n this.priceTypeControl.markAsTouched();\n }\n }\n\n public cancel(): void {\n this.dialogRef.close(false);\n }\n\n protected readonly CURRENCY = CURRENCY;\n protected readonly PRICE_TYPES = PRICE_TYPES;\n}\n","
\n \n
\n
\n \n \n
\n \n {{ 'negotiable' | translate }}\n \n
\n
\n\n\n\n
\n \n {{ 'cancel' | translate }}\n \n \n {{ 'save' | translate }}\n \n
\n","export interface IAdvertContact {\n name: string;\n phone?: number;\n}\nexport interface IAdvertImage {\n url: string;\n}\nexport interface IAdvertLocation {\n city_id: number;\n district_id: null;\n latitude: number;\n longitude: number;\n}\n\n\nexport interface IAdvert {\n type: ADVERT_TYPE;\n id: number;\n title: string;\n activated_at: string;\n advertiser_type: string;\n attributes: any[];\n category_id: number;\n category: any;\n contact: IAdvertContact;\n courier: null;\n created_at: string;\n description: string;\n external_id: number;\n external_url: string;\n images: IAdvertImage[];\n location: IAdvertLocation;\n address: any;\n price: {\n value: number;\n currency: string;\n trade: boolean;\n negotiable: boolean;\n };\n double_price: {\n usd: string;\n uah: string;\n };\n salary: null;\n status: string;\n url: string;\n valid_to: string;\n}\n\nexport enum ADVERT_TYPE {\n OLX = 'OLX',\n RIA = 'RIA',\n}\n\nexport interface IAction {\n title: string;\n icon: string;\n key: string;\n action: () => void;\n is_disabled?: boolean;\n}\n","import {\n AfterViewInit,\n ChangeDetectorRef,\n Component,\n ElementRef,\n Input,\n OnChanges,\n SimpleChanges,\n ViewChild,\n} from '@angular/core';\nimport { OlxAdvertService } from '../../../../../services/olx-advert.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { ChangePriceComponent } from '../../../../../dialogs/change-price/change-price.component';\nimport { DEFAULT_ERROR, DEFAULT_PROFILE_ICON, ERROR_MESSAGES, languages } from '../../../../../enum/constants';\nimport { IError } from '../../../../../interfaces';\nimport { parseCurrency, parseCurrencyXXX } from '../../../../../helpers';\nimport { ADVERT_TYPE, IAction, IAdvert } from './chat-advert-info.interface';\nimport { ChatService } from '../../../../../services/chat.service';\nimport { catchError, map, of } from 'rxjs';\n\n@Component({\n selector: 'chat-advert-info',\n templateUrl: './chat-advert-info.component.html',\n styleUrls: ['./chat-advert-info.component.scss'],\n})\nexport class ChatAdvertInfoComponent implements OnChanges, AfterViewInit {\n @Input() advert!: IAdvert;\n @ViewChild('descriptionElement') descriptionElement!: ElementRef;\n protected actions: IAction[] = [\n {\n title: 'change_price',\n icon: 'price_change',\n key: 'change_price',\n action: () => this.changePrice(),\n },\n {\n title: 'sync_advert',\n icon: 'sync',\n key: 'sync_advert',\n action: () => this.clickSyncAdvert(),\n is_disabled: true,\n },\n ];\n protected isCollapsed: boolean = true;\n protected isTextClamped: boolean = false;\n\n constructor(\n private readonly modalService: ModalService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly notificationService: NotificationService,\n private readonly chatService: ChatService,\n private readonly cdref: ChangeDetectorRef,\n ) {}\n\n\n ngAfterViewInit() {\n this.checkTextOverflow();\n this.cdref.detectChanges();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n if(changes['advert'] && this.advert.type === ADVERT_TYPE.OLX) {\n const action = this.actions.find(action => action.key === 'sync_advert') || {} as IAction;\n this.syncAdvert(action);\n }\n }\n\n private changePrice() {\n const modalData = {\n title: 'change_price',\n component: ChangePriceComponent,\n translate: true,\n data: {\n value: this.advert?.price?.value ?? null,\n currency: this.advert?.price?.currency ?? 'UAH',\n trade: this.advert?.price?.trade ?? null,\n negotiable: this.advert?.price?.negotiable ?? null,\n },\n };\n this.modalService.openModal(modalData).subscribe(price => {\n if (!price) return;\n this.olxAdvertService.updatePrice(this.chatService.chat.channel_id, `${this.advert.id}`, price)\n .subscribe({\n next: (res) => {\n Object.assign(this.chatService.advert, res);\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(\n languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error,\n ERROR_MESSAGES[err.code] || DEFAULT_ERROR\n );\n });\n },\n });\n });\n }\n\n private getSyncAdvert() {\n const last_sync_at: number | null = this.chatService.advert.last_sync_at;\n\n if (last_sync_at) {\n const currentDate: Date = new Date();\n const lastSyncDate: Date = new Date(last_sync_at);\n const difference: number = (currentDate.getTime() - lastSyncDate.getTime()) / 1000;\n if (difference <= 5) return of(difference);\n }\n\n return this.olxAdvertService.getLastSync(`${this.advert.id}`).pipe(\n map(res => {\n const receivedDate: Date = new Date(res);\n this.chatService.advert.last_sync_at = receivedDate.getTime();\n const currentDate: Date = new Date();\n if (isNaN(receivedDate.getTime())) throw new Error();\n return (currentDate.getTime() - receivedDate.getTime()) / 1000;\n }),\n catchError(() => {\n return of(null);\n })\n );\n }\n\n private clickSyncAdvert() {\n const action = this.actions.find(action => action.key === 'sync_advert') || {} as IAction;\n action.is_disabled = true;\n const lang = languages[localStorage.getItem('language') || 'ua'].notifications_texts.error;\n\n this.getSyncAdvert().subscribe(response => {\n if (!response) {\n this.notificationService.showError(lang.error, lang.synchronization_error);\n action.is_disabled = false;\n return;\n }\n if (response < 5) {\n this.notificationService.showError(lang.error, lang.since_last_sync_error);\n action.is_disabled = false;\n return;\n }\n this.syncAdvert(action);\n });\n }\n\n private syncAdvert(action: IAction) {\n this.olxAdvertService.syncAdvert(`${this.advert.id}`, this.chatService.chat.channel_id)\n .subscribe(res => {\n if (res) {\n Object.assign(this.chatService.advert, res);\n this.getSyncAdvert();\n action.is_disabled = false;\n }\n });\n }\n\n toggleDescription() {\n this.isCollapsed = !this.isCollapsed;\n }\n\n checkTextOverflow() {\n const element = this.descriptionElement.nativeElement;\n this.isTextClamped = element.scrollHeight > element.clientHeight;\n }\n\n protected readonly parseCurrencyXXX = parseCurrencyXXX;\n protected readonly ADVERT_TYPE = ADVERT_TYPE;\n protected readonly Object = Object;\n protected readonly parseCurrency = parseCurrency;\n protected readonly DEFAULT_PROFILE_ICON = DEFAULT_PROFILE_ICON;\n}\n","
\n \n \n \n
\n
\n info\n
\n {{ advert.title }}\n \n {{ 'first_name' | translate }}\n {{ advert.contact.name }}\n \n \n {{ 'telephone' | translate }}\n {{ advert.contact.phone }}\n \n \n {{ 'price'| translate }}\n \n {{ 'trade' | translate }}\n \n \n {{ parseCurrencyXXX(advert.price.value ?? 0, advert.price.currency) }}\n \n ({{ 'negotiable'| translate }})\n \n \n \n \n {{ 'price'| translate }}\n \n {{ advert.double_price.usd }}$ • \n {{ advert.double_price.uah }} {{ 'UAH' | translate }}\n \n \n \n {{ 'category' | translate }}\n {{ advert.category.name }}\n \n \n {{ 'city' | translate }}\n {{ advert.address.city.name }}\n \n \n {{ 'description' | translate }}\n
\n \n {{ advert.description }}\n \n keyboard_arrow_down\n keyboard_arrow_up\n
\n
\n
\n
\n \n \n \n
\n
\n","import { ATTACHMENTS_TYPES, MESSAGE_TYPES } from '../../../../../enum/constants';\n\nexport interface Attachment {\n type: ATTACHMENTS_TYPES;\n url?: string;\n id?: number;\n loading?: boolean;\n error?: boolean;\n mime_type?: string;\n name?: string;\n size?: number;\n video_gif?: boolean;\n video_sticker?: boolean;\n round?: boolean;\n voice?: boolean;\n sticker?: boolean;\n sizes?: Sizes;\n duration?: number;\n waveform?: Buffer;\n alt?: string;\n nosound?: boolean;\n}\n\ninterface Sizes {\n width: number;\n height: number;\n}\n\nexport interface Message {\n id: string;\n chat_id: string;\n attachments?: { array: Attachment[] };\n phone?: string;\n is_read?: boolean;\n text?: string;\n created_at: string;\n created_by?: string;\n tails?: boolean;\n status?: string;\n is_image?: boolean;\n is_pinned?: boolean;\n is_ai?: boolean;\n is_sent_crm?: boolean;\n type_message: MESSAGE_TYPES;\n x_data: {\n id?: number;\n thread_id?: number;\n text?: string;\n editDate?: string;\n };\n}\n\nexport interface MessageData {\n price?: string;\n comment?: string;\n photo?: string;\n link?: string;\n title?: string;\n term_id?: number;\n term_title?: string;\n term_price?: string;\n}\n\nexport enum BTN_ACTIONS {\n RIA_AUCTION_ACCEPT = 'RIA_AUCTION_ACCEPT',\n RIA_AUCTION_DECLINE = 'RIA_AUCTION_DECLINE',\n RIA_TRADE_ACCEPT = 'RIA_TRADE_ACCEPT',\n RIA_TRADE_DECLINE = 'RIA_TRADE_DECLINE',\n}\n\nexport enum riaMessageTypesNumbersMap {\n RIA_AUCTION_ACCEPT = 32,\n RIA_AUCTION_DECLINE = 33,\n RIA_TRADE_ACCEPT = 38,\n RIA_TRADE_DECLINE = 39,\n};\n\nexport enum RIA_MESSAGE_TYPES_ACTION {\n RIA_AUCTION_ACCEPT = MESSAGE_TYPES.RIA_AUCTION_AGREED,\n RIA_AUCTION_DECLINE = MESSAGE_TYPES.RIA_AUCTION_AGREED,\n RIA_TRADE_ACCEPT = MESSAGE_TYPES.RIA_TRADE_ACCEPT_BUYER,\n RIA_TRADE_DECLINE = MESSAGE_TYPES.RIA_TRADE_DECLINE_BUYER,\n};\n\nexport interface SendMessageActionPayload {\n text: string;\n type_mes: number;\n previous_message_id: number;\n}\n\nexport interface IMenuMessage {\n title: string;\n icon: string;\n is_svg?: boolean;\n action: (event: MouseEvent) => void;\n is_lines?: boolean;\n}\n\nexport const riaAuctionMessageTypes: MESSAGE_TYPES[] = [\n MESSAGE_TYPES.RIA_AUCTION,\n MESSAGE_TYPES.RIA_AUCTION_AGREED,\n MESSAGE_TYPES.RIA_AUCTION_ACCEPT,\n MESSAGE_TYPES.RIA_AUCTION_DECLINE,\n];\n\nexport const riaTradeMessageTypes: MESSAGE_TYPES[] = [\n MESSAGE_TYPES.RIA_TRADE,\n MESSAGE_TYPES.RIA_TRADE_ACCEPT_BUYER,\n MESSAGE_TYPES.RIA_TRADE_DECLINE_BUYER,\n MESSAGE_TYPES.RIA_TRADE_ACCEPT,\n MESSAGE_TYPES.RIA_TRADE_DECLINE,\n];\n\nexport const riaTradeAcceptMessageTypes: MESSAGE_TYPES[] = [\n MESSAGE_TYPES.RIA_TRADE_ACCEPT_BUYER,\n MESSAGE_TYPES.RIA_TRADE_ACCEPT,\n];\n\nexport const riaTradeDeclineMessageTypes: MESSAGE_TYPES[] = [\n MESSAGE_TYPES.RIA_TRADE_DECLINE_BUYER,\n MESSAGE_TYPES.RIA_TRADE_DECLINE,\n];\n\nexport const riaTradePhotoMessageTypes: MESSAGE_TYPES[] = [\n MESSAGE_TYPES.RIA_TRADE,\n MESSAGE_TYPES.RIA_TRADE_ACCEPT_BUYER,\n MESSAGE_TYPES.RIA_TRADE_DECLINE_BUYER,\n];\n\nexport const riaMessageTypes: MESSAGE_TYPES[] = [\n ...riaAuctionMessageTypes,\n ...riaTradeMessageTypes,\n];\n\n// temporary map for tooltip\nexport const attachmentsTooltipMap: Record = {\n [ATTACHMENTS_TYPES.IMAGE]: 'image',\n [ATTACHMENTS_TYPES.VIDEO]: 'video',\n 'gif': 'gif',\n [ATTACHMENTS_TYPES.AUDIO]: 'audio',\n 'voice': 'voice',\n [ATTACHMENTS_TYPES.ROUND]: 'round',\n [ATTACHMENTS_TYPES.STICKER]: 'sticker',\n [ATTACHMENTS_TYPES.DOCUMENT]: 'file',\n};\n","import {\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n ViewChild,\n ViewContainerRef,\n} from '@angular/core';\nimport { ChatService } from '../../../../../services/chat.service';\nimport {\n ATTACHMENTS_TYPES,\n CHAT_MEDIA_CACHE_NAME,\n CHAT_TYPES,\n DEFAULT_ERROR,\n ERROR_MESSAGES,\n languages,\n MESSAGE_TYPES,\n} from '../../../../../enum/constants';\nimport { Clipboard } from '@angular/cdk/clipboard';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport {\n Attachment,\n attachmentsTooltipMap,\n BTN_ACTIONS,\n IMenuMessage,\n Message,\n MessageData,\n riaAuctionMessageTypes,\n riaMessageTypes,\n RIA_MESSAGE_TYPES_ACTION,\n riaMessageTypesNumbersMap,\n riaTradeAcceptMessageTypes,\n riaTradeDeclineMessageTypes,\n riaTradeMessageTypes,\n riaTradePhotoMessageTypes,\n SendMessageActionPayload,\n} from './chat-message.interface';\nimport { BaseService } from '../../../../../services/base.service';\nimport { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { v4 } from 'uuid';\nimport { MessagesTemplatesService } from '../../../../../services/messages-templates.service';\nimport { ITempMessagePayload } from '../chat/chat.interface';\nimport { CacheService } from '../../../../../services/cache.service';\nimport { from, Subject, switchMap, takeUntil } from 'rxjs';\nimport pako from 'pako';\nimport { FileSaveService } from '../../../../../services/file-save.service';\n\n@Component({\n selector: 'app-chat-message',\n templateUrl: './chat-message.component.html',\n styleUrl: './chat-message.component.scss',\n})\nexport class ChatMessageComponent implements OnInit, OnDestroy {\n @Input() message!: Message;\n @Input() isPinned!: boolean;\n @Output() onEditMessage = new EventEmitter();\n @Output() onSendMessage = new EventEmitter();\n @ViewChild('menu') menuTemplate!: TemplateRef;\n private readonly destroy$: Subject = new Subject();\n private overlayRef!: OverlayRef;\n private selectedText = '';\n protected readonly markdownChatTypes: CHAT_TYPES[] = [CHAT_TYPES.TELEGRAM_BOT, CHAT_TYPES.TELEGRAM_USER];\n protected menus: Array = [];\n protected data: MessageData = {};\n protected media: Attachment[] = [];\n protected files: Attachment[] = [];\n protected audio!: Attachment;\n protected round!: Attachment;\n protected sticker!: Attachment;\n\n constructor(\n private readonly messagesTemplatesService: MessagesTemplatesService,\n private readonly clipboard: Clipboard,\n private readonly notificationService: NotificationService,\n private readonly overlay: Overlay,\n private readonly viewContainerRef: ViewContainerRef,\n private readonly cacheService: CacheService,\n private readonly fileSaveService: FileSaveService,\n protected readonly chatService: ChatService,\n protected readonly baseService: BaseService,\n ) {\n }\n\n ngOnInit() {\n this.initData();\n this.initAttachments();\n this.initMenu();\n }\n\n public ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initData(): void {\n if (riaMessageTypes.includes(this.message.type_message) && this.message.x_data?.text) {\n try {\n this.data = JSON.parse(this.message.x_data.text);\n } catch {\n }\n }\n }\n\n private initAttachments(): void {\n this.resetMedia();\n\n this.message.attachments?.array?.forEach((attachment: Attachment) => {\n switch (attachment.type) {\n case ATTACHMENTS_TYPES.IMAGE:\n case ATTACHMENTS_TYPES.VIDEO:\n this.initMedia(attachment);\n break;\n\n case ATTACHMENTS_TYPES.AUDIO:\n this.initAudio(attachment);\n break;\n\n case ATTACHMENTS_TYPES.ROUND:\n this.initRound(attachment);\n break;\n\n case ATTACHMENTS_TYPES.STICKER:\n this.initSticker(attachment);\n break;\n\n case ATTACHMENTS_TYPES.DOCUMENT:\n this.files.push(attachment);\n break;\n }\n });\n }\n\n private initMedia(attachment: Attachment) {\n this.media.push(attachment);\n\n const lastElement = this.media.at(-1);\n if (!lastElement) return;\n\n const cacheKey = `/${this.message.chat_id}/${attachment.id}`;\n\n from(caches.open(CHAT_MEDIA_CACHE_NAME)).pipe(\n switchMap(cache => from(cache.match(cacheKey))),\n takeUntil(this.destroy$)\n ).subscribe((cachedResponse) => {\n if (cachedResponse) {\n this.initMediaUrl(attachment);\n }\n });\n }\n\n private initAudio(attachment: Attachment) {\n this.audio = attachment;\n if (this.audio.voice) this.initMediaUrl(this.audio);\n }\n\n private initRound(attachment: Attachment) {\n this.round = attachment;\n this.initMediaUrl(this.round);\n }\n\n private initSticker(attachment: Attachment) {\n this.sticker = attachment;\n this.getStickerDimensions(this.sticker);\n this.initMediaUrl(this.sticker);\n }\n\n protected initMediaUrl(attachment: Attachment) {\n if (!attachment.id) return;\n\n attachment.loading = true;\n\n const cacheKey = `/${this.message.chat_id}/${attachment.id}`;\n\n const fetch$ = this.chatService.downloadMedia(this.message.chat_id, attachment.id);\n\n this.cacheService.getCachedOrFetchBlob(cacheKey, fetch$, CHAT_MEDIA_CACHE_NAME)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (data) => {\n if (!data) return;\n\n if (attachment.mime_type?.includes('tgsticker')) {\n this.convertTgsToJson(attachment, data);\n return;\n }\n\n attachment.url = URL.createObjectURL(data);\n attachment.loading = false;\n },\n error: () => {\n attachment.loading = false;\n },\n });\n }\n\n private convertTgsToJson(attachment: Attachment, tgsBlob: Blob): any {\n from(tgsBlob.arrayBuffer())\n .subscribe({\n next: (arrayBuffer) => {\n const decompressed = pako.inflate(arrayBuffer, { to: 'string' });\n try {\n attachment.url = JSON.parse(decompressed);\n } catch {\n } finally {\n attachment.loading = false;\n }\n },\n });\n\n }\n\n private resetMedia(): void {\n this.media = [];\n }\n\n protected onBtnAction(action: BTN_ACTIONS) {\n const tempId: string = v4();\n\n // this.message.type_message = riaMessageTypesActionMap[action as riaMessageTypesActionMap] as MESSAGE_TYPES;\n this.message.type_message = RIA_MESSAGE_TYPES_ACTION[action] as unknown as MESSAGE_TYPES;\n const tempMessagePayload: ITempMessagePayload = {\n temp_id: tempId,\n chat_id: this.chatService.chat.id,\n type_message: action as unknown as MESSAGE_TYPES,\n text: this.message.text || '',\n x_data: { text: this.message.x_data.text },\n };\n this.chatService.addTempMessage(tempMessagePayload);\n\n const payload: SendMessageActionPayload = {\n text: this.message.x_data.text || '{}',\n type_mes: riaMessageTypesNumbersMap[action],\n previous_message_id: this.message.x_data.id || 0,\n };\n this.chatService.sendMessageAction(this.chatService.chat.id, payload)\n .subscribe({\n next: (message) => {\n this.chatService.replaceTempMessage(message, tempId);\n this.chatService.reloadChatList();\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n private initMenu() {\n this.menus = [];\n if (this.message.text?.trim()) {\n this.menus.push({ title: 'copy', icon: 'content_copy', action: () => this.onCopy() });\n this.menus.push({ title: 'save_as_a_quick_reply', icon: 'bolt', action: () => this.onSaveQuickReply() });\n }\n\n if (\n this.media.length === 1 ||\n this.files.length === 1 ||\n this.audio ||\n this.round ||\n this.sticker\n ) {\n this.menus.push({\n title: 'download',\n icon: 'download',\n action: () => this.onDownloadAttachments(false),\n });\n this.menus.push({\n title: 'download_as',\n icon: 'save_as',\n action: () => this.onDownloadAttachments(),\n });\n }\n\n if (this.media.length > 1 || this.files.length > 1) {\n this.menus.push({\n title: 'download_all',\n icon: 'download',\n action: () => this.onDownloadAttachments(false),\n });\n }\n\n if (!this.message.is_pinned) {\n this.menus.push({ title: 'pin', icon: 'pin', is_svg: true, action: () => this.onPin() });\n } else {\n this.menus.push({ title: 'unpin', icon: 'unpin', is_svg: true, action: () => this.onPin() });\n }\n\n if (this.message.created_by) {\n if (this.message.status === 'FAIL') {\n this.menus.push({ title: 'resend', icon: 'restart_alt', action: () => this.onResend() });\n }\n\n if ([CHAT_TYPES.TELEGRAM_BOT].includes(this.chatService.chat.entity_type)) {\n this.menus.push({\n title: 'change', icon: 'edit', action: () => this.onEdit(),\n });\n }\n\n if ([CHAT_TYPES.OLX].includes(this.chatService.chat.entity_type)\n && this.message.status === 'SEND'\n && !this.message.is_read) {\n this.menus.push({ title: 'check', icon: 'done_all', action: () => this.onCheckRead() });\n }\n }\n }\n\n private onDownloadAttachments(downloadAs: boolean = true) {\n const attachments: Attachment[] = [\n ...this.media,\n ...this.files,\n this.audio,\n this.round,\n this.sticker,\n ].filter(Boolean);\n\n if (!attachments.length) return;\n\n attachments.forEach((attachment) => this.onDownloadAttachment(attachment, downloadAs));\n }\n\n protected onTextSelect(): void {\n const selection = window.getSelection();\n if (selection) this.selectedText = selection.toString();\n }\n\n private onCopy() {\n const text = this.selectedText || this.message.text;\n if (text && this.clipboard.copy(text)) {\n this.notificationService.showInfo('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.copied);\n // document.addEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n }\n }\n\n private onCheckRead() {\n // document.addEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n this.chatService.reloadMessage(this.chatService.selectedChatId, this.message.id)\n .subscribe({\n next: (reloadedMessage: Message) => {\n const candidate = this.chatService.chatMessagesInfo.items.find((v: Message) => v.id === reloadedMessage.id);\n if (candidate) candidate.is_read = reloadedMessage.is_read;\n if (reloadedMessage.is_read) {\n this.initMenu();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.message_read);\n }\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n private onPin() {\n // document.addEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n this.chatService.doPinnedMessage(this.message, !this.message.is_pinned)\n .subscribe({\n next: (res) => {\n const candidate = this.chatService.chatMessagesInfo.items.find(v => v.id === this.message.id);\n if (candidate) candidate.is_pinned = res.is_pinned;\n this.initMenu();\n this.chatService.getPinnedByChat();\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n private onResend() {\n this.onSendMessage.emit({ text: this.message.text, attachments: this.message.attachments });\n }\n\n private onEdit() {\n this.onEditMessage.emit(this.message);\n }\n\n protected reloadImages(image: Attachment) {\n image.loading = true;\n this.chatService.reloadMessage(this.message.chat_id, this.message.id)\n .subscribe({\n next: (res: any) => {\n image.loading = false;\n this.message = res;\n this.initAttachments();\n },\n error: (res: any) => {\n image.loading = false;\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n protected loadError(event: any) {\n event.error = true;\n }\n\n // openContextMenu(event: PointerEvent) {\n // if (!this.overlayRef) {\n // this.overlayRef = this.overlay.create({\n // positionStrategy: this.overlay\n // .position()\n // .flexibleConnectedTo({ x: event.clientX, y: event.clientY })\n // .withPositions([\n // { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'top' }\n // ]),\n // hasBackdrop: true,\n //\n // });\n // }\n // this.overlayRef.attach(new TemplatePortal(this.menuTemplate, this.viewContainerRef));\n // if (!this.chatService.isSubscribeMenu) {\n // this.chatService.isSubscribeMenu = true;\n // document.addEventListener('click', this.closeMenuOnOutsideClick.bind(this), true);\n // }\n // }\n // closeMenuOnOutsideClick(event: MouseEvent): void {\n // const targetElement = event.target as HTMLElement;\n //\n // if (this.overlayRef && !this.overlayRef.overlayElement.contains(targetElement)) {\n // this.overlayRef.dispose();\n // this.overlayRef = null!;\n // }\n // }\n\n protected goToMessage(): void {\n this.chatService.chatPinnedInfo.visible = false;\n setTimeout(() => {\n this.chatService.findMessage(this.message.id);\n }, 0);\n }\n\n protected onSaveQuickReply(): void {\n if (!this.message.text) return;\n\n this.messagesTemplatesService.save({ text: this.message.text.trim() })\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.fast_answer_save);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.there_was_an_error_please_try_again);\n },\n });\n }\n\n protected onDownloadAttachment(file: Attachment, saveAs?: boolean) {\n if ((!file.url && !file.id) || file.loading) return;\n\n file.loading = true;\n\n if (file.url) {\n this.fileSaveService.downloadMediaFromUrl(file.url)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (blob) => this.saveFileHandle(blob, file, saveAs),\n error: () => file.loading = false,\n });\n return;\n }\n\n const cacheKey = `/${this.message.chat_id}/${file.id}`;\n const fetch$ = this.chatService.downloadMedia(this.message.chat_id, file.id!);\n\n this.cacheService.getCachedOrFetchBlob(cacheKey, fetch$, CHAT_MEDIA_CACHE_NAME)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (blob) => {\n if (!blob) return;\n\n file.url = URL.createObjectURL(blob);\n this.saveFileHandle(blob, file, saveAs);\n },\n error: () => {\n file.loading = false;\n },\n });\n }\n\n private saveFileHandle = (blob: Blob, file: Attachment, saveAs?: boolean) => {\n const notificationSuccess = languages[localStorage.getItem('language') || 'ua'].notifications_texts.success;\n\n this.fileSaveService.saveFile(blob, file.name, saveAs).subscribe({\n next: () => {\n this.notificationService.showSuccess(notificationSuccess.success, notificationSuccess.successfully_downloaded);\n file.loading = false;\n },\n error: () => {\n file.loading = false;\n },\n });\n };\n\n private getStickerDimensions(sticker: Attachment) {\n const width = sticker.sizes?.width;\n const height = sticker.sizes?.height;\n const aspectRatio = (height && width) && height / width;\n const baseWidth = 200;\n const calculatedHeight = aspectRatio ? baseWidth * aspectRatio : baseWidth;\n\n if (aspectRatio && calculatedHeight > baseWidth) {\n sticker.sizes = {\n width: Math.round(baseWidth / aspectRatio),\n height: baseWidth,\n };\n }\n\n sticker.sizes = {\n width: baseWidth,\n height: calculatedHeight,\n };\n }\n\n protected isMessageBackgroundTransparent(): boolean {\n return (!!this.sticker?.url && !this.sticker?.error) || !!this.round || !!this.audio?.url;\n }\n\n protected readonly open = open;\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly BTN_ACTIONS = BTN_ACTIONS;\n protected readonly MESSAGE_TYPES = MESSAGE_TYPES;\n protected readonly riaAuctionMessageTypes = riaAuctionMessageTypes;\n protected readonly riaTradeMessageTypes = riaTradeMessageTypes;\n protected readonly riaTradeAcceptMessageTypes = riaTradeAcceptMessageTypes;\n protected readonly riaTradeDeclineMessageTypes = riaTradeDeclineMessageTypes;\n protected readonly riaTradePhotoMessageTypes = riaTradePhotoMessageTypes;\n protected readonly ATTACHMENTS_TYPES = ATTACHMENTS_TYPES;\n protected readonly attachmentsTooltipMap = attachmentsTooltipMap;\n}\n","
\n \n\n \n arrow_forward\n \n\n
\n \n
\n \n
\n \n
\n download\n \n
\n\n \n \n\n \n \n
\n\n
\n refresh\n \n
\n
\n
\n
\n\n \n
\n \n
\n \n \n draft\n download\n \n
\n {{ file.name || ('file' | translate) }}\n {{ file.size ? (file.size | fileSize) : '??? B' }}\n
\n
\n
\n
\n\n \n
\n
\n download\n \n
\n\n \n
\n\n \n \n\n \n
\n \n {{ sticker.alt }}\n \n \n \n \n \n \n \n
\n\n \n
\n \n \n \n
\n\n \n
\n \n handshake\n {{ 'offer_accepted' | translate }}\n \n \n cancel\n {{ 'offer_rejected' | translate }}\n \n {{ 'offer_an_exchange' | translate }}\n {{ data.price }}\n {{ data.comment }}\n
\n \n {{ 'accept' | translate }}\n \n \n {{ 'reject' | translate }}\n \n
\n
\n\n \n
\n
\n handshake\n {{ 'offer_accepted' | translate }}\n
\n
\n cancel\n {{ 'offer_rejected' | translate }}\n
\n \"ria\n \n {{ 'offer_a_trade' | translate }} {{ data.title }} · {{ data.price }}\n \n \n {{ 'with_your_surcharge' | translate }} · {{ data.term_price }}\n \n \n {{ 'with_my_surcharge' | translate }} · {{ data.term_price }}\n \n {{ data.comment }}\n
\n \n {{ 'accept' | translate }}\n \n \n {{ 'reject' | translate }}\n \n
\n
\n\n \n
\n
\n \n people\n \n {{ message.created_at | date: 'HH:mm' }}\n \n done_all\n check\n \n clock_loader_10\n feedback\n
\n
\n
\n\n \n more_vert\n \n
\n\n\n
\n \n
\n
\n\n\n
\n \n \n
\n
\n","/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n/* eslint-disable space-unary-ops */\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\n//const Z_FILTERED = 1;\n//const Z_HUFFMAN_ONLY = 2;\n//const Z_RLE = 3;\nconst Z_FIXED$1 = 4;\n//const Z_DEFAULT_STRATEGY = 0;\n\n/* Possible values of the data_type field (though see inflate()) */\nconst Z_BINARY = 0;\nconst Z_TEXT = 1;\n//const Z_ASCII = 1; // = Z_TEXT\nconst Z_UNKNOWN$1 = 2;\n\n/*============================================================================*/\n\nfunction zero$1(buf) {\n let len = buf.length;\n while (--len >= 0) {\n buf[len] = 0;\n }\n}\n\n// From zutil.h\n\nconst STORED_BLOCK = 0;\nconst STATIC_TREES = 1;\nconst DYN_TREES = 2;\n/* The three kinds of block type */\n\nconst MIN_MATCH$1 = 3;\nconst MAX_MATCH$1 = 258;\n/* The minimum and maximum match lengths */\n\n// From deflate.h\n/* ===========================================================================\n * Internal compression state.\n */\n\nconst LENGTH_CODES$1 = 29;\n/* number of length codes, not counting the special END_BLOCK code */\n\nconst LITERALS$1 = 256;\n/* number of literal bytes 0..255 */\n\nconst L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1;\n/* number of Literal or Length codes, including the END_BLOCK code */\n\nconst D_CODES$1 = 30;\n/* number of distance codes */\n\nconst BL_CODES$1 = 19;\n/* number of codes used to transfer the bit lengths */\n\nconst HEAP_SIZE$1 = 2 * L_CODES$1 + 1;\n/* maximum heap size */\n\nconst MAX_BITS$1 = 15;\n/* All codes must not exceed MAX_BITS bits */\n\nconst Buf_size = 16;\n/* size of bit buffer in bi_buf */\n\n/* ===========================================================================\n * Constants\n */\n\nconst MAX_BL_BITS = 7;\n/* Bit length codes must not exceed MAX_BL_BITS bits */\n\nconst END_BLOCK = 256;\n/* end of block literal code */\n\nconst REP_3_6 = 16;\n/* repeat previous bit length 3-6 times (2 bits of repeat count) */\n\nconst REPZ_3_10 = 17;\n/* repeat a zero length 3-10 times (3 bits of repeat count) */\n\nconst REPZ_11_138 = 18;\n/* repeat a zero length 11-138 times (7 bits of repeat count) */\n\n/* eslint-disable comma-spacing,array-bracket-spacing */\nconst extra_lbits = /* extra bits for each length code */\nnew Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]);\nconst extra_dbits = /* extra bits for each distance code */\nnew Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]);\nconst extra_blbits = /* extra bits for each bit length code */\nnew Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]);\nconst bl_order = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n/* eslint-enable comma-spacing,array-bracket-spacing */\n\n/* The lengths of the bit length codes are sent in order of decreasing\n * probability, to avoid transmitting the lengths for unused bit length codes.\n */\n\n/* ===========================================================================\n * Local data. These are initialized only once.\n */\n\n// We pre-fill arrays with 0 to avoid uninitialized gaps\n\nconst DIST_CODE_LEN = 512; /* see definition of array dist_code below */\n\n// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1\nconst static_ltree = new Array((L_CODES$1 + 2) * 2);\nzero$1(static_ltree);\n/* The static literal tree. Since the bit lengths are imposed, there is no\n * need for the L_CODES extra codes used during heap construction. However\n * The codes 286 and 287 are needed to build a canonical tree (see _tr_init\n * below).\n */\n\nconst static_dtree = new Array(D_CODES$1 * 2);\nzero$1(static_dtree);\n/* The static distance tree. (Actually a trivial tree since all codes use\n * 5 bits.)\n */\n\nconst _dist_code = new Array(DIST_CODE_LEN);\nzero$1(_dist_code);\n/* Distance codes. The first 256 values correspond to the distances\n * 3 .. 258, the last 256 values correspond to the top 8 bits of\n * the 15 bit distances.\n */\n\nconst _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1);\nzero$1(_length_code);\n/* length code for each normalized match length (0 == MIN_MATCH) */\n\nconst base_length = new Array(LENGTH_CODES$1);\nzero$1(base_length);\n/* First normalized length for each code (0 = MIN_MATCH) */\n\nconst base_dist = new Array(D_CODES$1);\nzero$1(base_dist);\n/* First normalized distance for each code (0 = distance of 1) */\n\nfunction StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {\n this.static_tree = static_tree; /* static tree or NULL */\n this.extra_bits = extra_bits; /* extra bits for each code or NULL */\n this.extra_base = extra_base; /* base index for extra_bits */\n this.elems = elems; /* max number of elements in the tree */\n this.max_length = max_length; /* max bit length for the codes */\n\n // show if `static_tree` has data or dummy - needed for monomorphic objects\n this.has_stree = static_tree && static_tree.length;\n}\nlet static_l_desc;\nlet static_d_desc;\nlet static_bl_desc;\nfunction TreeDesc(dyn_tree, stat_desc) {\n this.dyn_tree = dyn_tree; /* the dynamic tree */\n this.max_code = 0; /* largest code with non zero frequency */\n this.stat_desc = stat_desc; /* the corresponding static tree */\n}\nconst d_code = dist => {\n return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];\n};\n\n/* ===========================================================================\n * Output a short LSB first on the stream.\n * IN assertion: there is enough room in pendingBuf.\n */\nconst put_short = (s, w) => {\n // put_byte(s, (uch)((w) & 0xff));\n // put_byte(s, (uch)((ush)(w) >> 8));\n s.pending_buf[s.pending++] = w & 0xff;\n s.pending_buf[s.pending++] = w >>> 8 & 0xff;\n};\n\n/* ===========================================================================\n * Send a value on a given number of bits.\n * IN assertion: length <= 16 and value fits in length bits.\n */\nconst send_bits = (s, value, length) => {\n if (s.bi_valid > Buf_size - length) {\n s.bi_buf |= value << s.bi_valid & 0xffff;\n put_short(s, s.bi_buf);\n s.bi_buf = value >> Buf_size - s.bi_valid;\n s.bi_valid += length - Buf_size;\n } else {\n s.bi_buf |= value << s.bi_valid & 0xffff;\n s.bi_valid += length;\n }\n};\nconst send_code = (s, c, tree) => {\n send_bits(s, tree[c * 2] /*.Code*/, tree[c * 2 + 1] /*.Len*/);\n};\n\n/* ===========================================================================\n * Reverse the first len bits of a code, using straightforward code (a faster\n * method would use a table)\n * IN assertion: 1 <= len <= 15\n */\nconst bi_reverse = (code, len) => {\n let res = 0;\n do {\n res |= code & 1;\n code >>>= 1;\n res <<= 1;\n } while (--len > 0);\n return res >>> 1;\n};\n\n/* ===========================================================================\n * Flush the bit buffer, keeping at most 7 bits in it.\n */\nconst bi_flush = s => {\n if (s.bi_valid === 16) {\n put_short(s, s.bi_buf);\n s.bi_buf = 0;\n s.bi_valid = 0;\n } else if (s.bi_valid >= 8) {\n s.pending_buf[s.pending++] = s.bi_buf & 0xff;\n s.bi_buf >>= 8;\n s.bi_valid -= 8;\n }\n};\n\n/* ===========================================================================\n * Compute the optimal bit lengths for a tree and update the total bit length\n * for the current block.\n * IN assertion: the fields freq and dad are set, heap[heap_max] and\n * above are the tree nodes sorted by increasing frequency.\n * OUT assertions: the field len is set to the optimal bit length, the\n * array bl_count contains the frequencies for each bit length.\n * The length opt_len is updated; static_len is also updated if stree is\n * not null.\n */\nconst gen_bitlen = (s, desc) => {\n // deflate_state *s;\n // tree_desc *desc; /* the tree descriptor */\n\n const tree = desc.dyn_tree;\n const max_code = desc.max_code;\n const stree = desc.stat_desc.static_tree;\n const has_stree = desc.stat_desc.has_stree;\n const extra = desc.stat_desc.extra_bits;\n const base = desc.stat_desc.extra_base;\n const max_length = desc.stat_desc.max_length;\n let h; /* heap index */\n let n, m; /* iterate over the tree elements */\n let bits; /* bit length */\n let xbits; /* extra bits */\n let f; /* frequency */\n let overflow = 0; /* number of elements with bit length too large */\n\n for (bits = 0; bits <= MAX_BITS$1; bits++) {\n s.bl_count[bits] = 0;\n }\n\n /* In a first pass, compute the optimal bit lengths (which may\n * overflow in the case of the bit length tree).\n */\n tree[s.heap[s.heap_max] * 2 + 1] /*.Len*/ = 0; /* root of the heap */\n\n for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) {\n n = s.heap[h];\n bits = tree[tree[n * 2 + 1] /*.Dad*/ * 2 + 1] /*.Len*/ + 1;\n if (bits > max_length) {\n bits = max_length;\n overflow++;\n }\n tree[n * 2 + 1] /*.Len*/ = bits;\n /* We overwrite tree[n].Dad which is no longer needed */\n\n if (n > max_code) {\n continue;\n } /* not a leaf node */\n\n s.bl_count[bits]++;\n xbits = 0;\n if (n >= base) {\n xbits = extra[n - base];\n }\n f = tree[n * 2] /*.Freq*/;\n s.opt_len += f * (bits + xbits);\n if (has_stree) {\n s.static_len += f * (stree[n * 2 + 1] /*.Len*/ + xbits);\n }\n }\n if (overflow === 0) {\n return;\n }\n\n // Tracev((stderr,\"\\nbit length overflow\\n\"));\n /* This happens for example on obj2 and pic of the Calgary corpus */\n\n /* Find the first bit length which could increase: */\n do {\n bits = max_length - 1;\n while (s.bl_count[bits] === 0) {\n bits--;\n }\n s.bl_count[bits]--; /* move one leaf down the tree */\n s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */\n s.bl_count[max_length]--;\n /* The brother of the overflow item also moves one step up,\n * but this does not affect bl_count[max_length]\n */\n overflow -= 2;\n } while (overflow > 0);\n\n /* Now recompute all bit lengths, scanning in increasing frequency.\n * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all\n * lengths instead of fixing only the wrong ones. This idea is taken\n * from 'ar' written by Haruhiko Okumura.)\n */\n for (bits = max_length; bits !== 0; bits--) {\n n = s.bl_count[bits];\n while (n !== 0) {\n m = s.heap[--h];\n if (m > max_code) {\n continue;\n }\n if (tree[m * 2 + 1] /*.Len*/ !== bits) {\n // Tracev((stderr,\"code %d bits %d->%d\\n\", m, tree[m].Len, bits));\n s.opt_len += (bits - tree[m * 2 + 1] /*.Len*/) * tree[m * 2] /*.Freq*/;\n tree[m * 2 + 1] /*.Len*/ = bits;\n }\n n--;\n }\n }\n};\n\n/* ===========================================================================\n * Generate the codes for a given tree and bit counts (which need not be\n * optimal).\n * IN assertion: the array bl_count contains the bit length statistics for\n * the given tree and the field len is set for all tree elements.\n * OUT assertion: the field code is set for all tree elements of non\n * zero code length.\n */\nconst gen_codes = (tree, max_code, bl_count) => {\n // ct_data *tree; /* the tree to decorate */\n // int max_code; /* largest code with non zero frequency */\n // ushf *bl_count; /* number of codes at each bit length */\n\n const next_code = new Array(MAX_BITS$1 + 1); /* next code value for each bit length */\n let code = 0; /* running code value */\n let bits; /* bit index */\n let n; /* code index */\n\n /* The distribution counts are first used to generate the code values\n * without bit reversal.\n */\n for (bits = 1; bits <= MAX_BITS$1; bits++) {\n code = code + bl_count[bits - 1] << 1;\n next_code[bits] = code;\n }\n /* Check that the bit counts in bl_count are consistent. The last code\n * must be all ones.\n */\n //Assert (code + bl_count[MAX_BITS]-1 == (1< {\n let n; /* iterates over tree elements */\n let bits; /* bit counter */\n let length; /* length value */\n let code; /* code value */\n let dist; /* distance index */\n const bl_count = new Array(MAX_BITS$1 + 1);\n /* number of codes at each bit length for an optimal tree */\n\n // do check in _tr_init()\n //if (static_init_done) return;\n\n /* For some embedded targets, global variables are not initialized: */\n /*#ifdef NO_INIT_GLOBAL_POINTERS\n static_l_desc.static_tree = static_ltree;\n static_l_desc.extra_bits = extra_lbits;\n static_d_desc.static_tree = static_dtree;\n static_d_desc.extra_bits = extra_dbits;\n static_bl_desc.extra_bits = extra_blbits;\n #endif*/\n\n /* Initialize the mapping length (0..255) -> length code (0..28) */\n length = 0;\n for (code = 0; code < LENGTH_CODES$1 - 1; code++) {\n base_length[code] = length;\n for (n = 0; n < 1 << extra_lbits[code]; n++) {\n _length_code[length++] = code;\n }\n }\n //Assert (length == 256, \"tr_static_init: length != 256\");\n /* Note that the length 255 (match length 258) can be represented\n * in two different ways: code 284 + 5 bits or code 285, so we\n * overwrite length_code[255] to use the best encoding:\n */\n _length_code[length - 1] = code;\n\n /* Initialize the mapping dist (0..32K) -> dist code (0..29) */\n dist = 0;\n for (code = 0; code < 16; code++) {\n base_dist[code] = dist;\n for (n = 0; n < 1 << extra_dbits[code]; n++) {\n _dist_code[dist++] = code;\n }\n }\n //Assert (dist == 256, \"tr_static_init: dist != 256\");\n dist >>= 7; /* from now on, all distances are divided by 128 */\n for (; code < D_CODES$1; code++) {\n base_dist[code] = dist << 7;\n for (n = 0; n < 1 << extra_dbits[code] - 7; n++) {\n _dist_code[256 + dist++] = code;\n }\n }\n //Assert (dist == 256, \"tr_static_init: 256+dist != 512\");\n\n /* Construct the codes of the static literal tree */\n for (bits = 0; bits <= MAX_BITS$1; bits++) {\n bl_count[bits] = 0;\n }\n n = 0;\n while (n <= 143) {\n static_ltree[n * 2 + 1] /*.Len*/ = 8;\n n++;\n bl_count[8]++;\n }\n while (n <= 255) {\n static_ltree[n * 2 + 1] /*.Len*/ = 9;\n n++;\n bl_count[9]++;\n }\n while (n <= 279) {\n static_ltree[n * 2 + 1] /*.Len*/ = 7;\n n++;\n bl_count[7]++;\n }\n while (n <= 287) {\n static_ltree[n * 2 + 1] /*.Len*/ = 8;\n n++;\n bl_count[8]++;\n }\n /* Codes 286 and 287 do not exist, but we must include them in the\n * tree construction to get a canonical Huffman tree (longest code\n * all ones)\n */\n gen_codes(static_ltree, L_CODES$1 + 1, bl_count);\n\n /* The static distance tree is trivial: */\n for (n = 0; n < D_CODES$1; n++) {\n static_dtree[n * 2 + 1] /*.Len*/ = 5;\n static_dtree[n * 2] /*.Code*/ = bi_reverse(n, 5);\n }\n\n // Now data ready and we can init static trees\n static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1);\n static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1);\n static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS);\n\n //static_init_done = true;\n};\n\n/* ===========================================================================\n * Initialize a new block.\n */\nconst init_block = s => {\n let n; /* iterates over tree elements */\n\n /* Initialize the trees. */\n for (n = 0; n < L_CODES$1; n++) {\n s.dyn_ltree[n * 2] /*.Freq*/ = 0;\n }\n for (n = 0; n < D_CODES$1; n++) {\n s.dyn_dtree[n * 2] /*.Freq*/ = 0;\n }\n for (n = 0; n < BL_CODES$1; n++) {\n s.bl_tree[n * 2] /*.Freq*/ = 0;\n }\n s.dyn_ltree[END_BLOCK * 2] /*.Freq*/ = 1;\n s.opt_len = s.static_len = 0;\n s.sym_next = s.matches = 0;\n};\n\n/* ===========================================================================\n * Flush the bit buffer and align the output on a byte boundary\n */\nconst bi_windup = s => {\n if (s.bi_valid > 8) {\n put_short(s, s.bi_buf);\n } else if (s.bi_valid > 0) {\n //put_byte(s, (Byte)s->bi_buf);\n s.pending_buf[s.pending++] = s.bi_buf;\n }\n s.bi_buf = 0;\n s.bi_valid = 0;\n};\n\n/* ===========================================================================\n * Compares to subtrees, using the tree depth as tie breaker when\n * the subtrees have equal frequency. This minimizes the worst case length.\n */\nconst smaller = (tree, n, m, depth) => {\n const _n2 = n * 2;\n const _m2 = m * 2;\n return tree[_n2] /*.Freq*/ < tree[_m2] /*.Freq*/ || tree[_n2] /*.Freq*/ === tree[_m2] /*.Freq*/ && depth[n] <= depth[m];\n};\n\n/* ===========================================================================\n * Restore the heap property by moving down the tree starting at node k,\n * exchanging a node with the smallest of its two sons if necessary, stopping\n * when the heap property is re-established (each father smaller than its\n * two sons).\n */\nconst pqdownheap = (s, tree, k) => {\n // deflate_state *s;\n // ct_data *tree; /* the tree to restore */\n // int k; /* node to move down */\n\n const v = s.heap[k];\n let j = k << 1; /* left son of k */\n while (j <= s.heap_len) {\n /* Set j to the smallest of the two sons: */\n if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {\n j++;\n }\n /* Exit if v is smaller than both sons */\n if (smaller(tree, v, s.heap[j], s.depth)) {\n break;\n }\n\n /* Exchange v with the smallest son */\n s.heap[k] = s.heap[j];\n k = j;\n\n /* And continue down the tree, setting j to the left son of k */\n j <<= 1;\n }\n s.heap[k] = v;\n};\n\n// inlined manually\n// const SMALLEST = 1;\n\n/* ===========================================================================\n * Send the block data compressed using the given Huffman trees\n */\nconst compress_block = (s, ltree, dtree) => {\n // deflate_state *s;\n // const ct_data *ltree; /* literal tree */\n // const ct_data *dtree; /* distance tree */\n\n let dist; /* distance of matched string */\n let lc; /* match length or unmatched char (if dist == 0) */\n let sx = 0; /* running index in sym_buf */\n let code; /* the code to send */\n let extra; /* number of extra bits to send */\n\n if (s.sym_next !== 0) {\n do {\n dist = s.pending_buf[s.sym_buf + sx++] & 0xff;\n dist += (s.pending_buf[s.sym_buf + sx++] & 0xff) << 8;\n lc = s.pending_buf[s.sym_buf + sx++];\n if (dist === 0) {\n send_code(s, lc, ltree); /* send a literal byte */\n //Tracecv(isgraph(lc), (stderr,\" '%c' \", lc));\n } else {\n /* Here, lc is the match length - MIN_MATCH */\n code = _length_code[lc];\n send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */\n extra = extra_lbits[code];\n if (extra !== 0) {\n lc -= base_length[code];\n send_bits(s, lc, extra); /* send the extra length bits */\n }\n dist--; /* dist is now the match distance - 1 */\n code = d_code(dist);\n //Assert (code < D_CODES, \"bad d_code\");\n\n send_code(s, code, dtree); /* send the distance code */\n extra = extra_dbits[code];\n if (extra !== 0) {\n dist -= base_dist[code];\n send_bits(s, dist, extra); /* send the extra distance bits */\n }\n } /* literal or match pair ? */\n\n /* Check that the overlay between pending_buf and sym_buf is ok: */\n //Assert(s->pending < s->lit_bufsize + sx, \"pendingBuf overflow\");\n } while (sx < s.sym_next);\n }\n send_code(s, END_BLOCK, ltree);\n};\n\n/* ===========================================================================\n * Construct one Huffman tree and assigns the code bit strings and lengths.\n * Update the total bit length for the current block.\n * IN assertion: the field freq is set for all tree elements.\n * OUT assertions: the fields len and code are set to the optimal bit length\n * and corresponding code. The length opt_len is updated; static_len is\n * also updated if stree is not null. The field max_code is set.\n */\nconst build_tree = (s, desc) => {\n // deflate_state *s;\n // tree_desc *desc; /* the tree descriptor */\n\n const tree = desc.dyn_tree;\n const stree = desc.stat_desc.static_tree;\n const has_stree = desc.stat_desc.has_stree;\n const elems = desc.stat_desc.elems;\n let n, m; /* iterate over heap elements */\n let max_code = -1; /* largest code with non zero frequency */\n let node; /* new node being created */\n\n /* Construct the initial heap, with least frequent element in\n * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].\n * heap[0] is not used.\n */\n s.heap_len = 0;\n s.heap_max = HEAP_SIZE$1;\n for (n = 0; n < elems; n++) {\n if (tree[n * 2] /*.Freq*/ !== 0) {\n s.heap[++s.heap_len] = max_code = n;\n s.depth[n] = 0;\n } else {\n tree[n * 2 + 1] /*.Len*/ = 0;\n }\n }\n\n /* The pkzip format requires that at least one distance code exists,\n * and that at least one bit should be sent even if there is only one\n * possible code. So to avoid special checks later on we force at least\n * two codes of non zero frequency.\n */\n while (s.heap_len < 2) {\n node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;\n tree[node * 2] /*.Freq*/ = 1;\n s.depth[node] = 0;\n s.opt_len--;\n if (has_stree) {\n s.static_len -= stree[node * 2 + 1] /*.Len*/;\n }\n /* node is 0 or 1 so it does not have extra bits */\n }\n desc.max_code = max_code;\n\n /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,\n * establish sub-heaps of increasing lengths:\n */\n for (n = s.heap_len >> 1 /*int /2*/; n >= 1; n--) {\n pqdownheap(s, tree, n);\n }\n\n /* Construct the Huffman tree by repeatedly combining the least two\n * frequent nodes.\n */\n node = elems; /* next internal node of the tree */\n do {\n //pqremove(s, tree, n); /* n = node of least frequency */\n /*** pqremove ***/\n n = s.heap[1 /*SMALLEST*/];\n s.heap[1 /*SMALLEST*/] = s.heap[s.heap_len--];\n pqdownheap(s, tree, 1 /*SMALLEST*/);\n /***/\n\n m = s.heap[1 /*SMALLEST*/]; /* m = node of next least frequency */\n\n s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */\n s.heap[--s.heap_max] = m;\n\n /* Create a new node father of n and m */\n tree[node * 2] /*.Freq*/ = tree[n * 2] /*.Freq*/ + tree[m * 2] /*.Freq*/;\n s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;\n tree[n * 2 + 1] /*.Dad*/ = tree[m * 2 + 1] /*.Dad*/ = node;\n\n /* and insert the new node in the heap */\n s.heap[1 /*SMALLEST*/] = node++;\n pqdownheap(s, tree, 1 /*SMALLEST*/);\n } while (s.heap_len >= 2);\n s.heap[--s.heap_max] = s.heap[1 /*SMALLEST*/];\n\n /* At this point, the fields freq and dad are set. We can now\n * generate the bit lengths.\n */\n gen_bitlen(s, desc);\n\n /* The field len is now set, we can generate the bit codes */\n gen_codes(tree, max_code, s.bl_count);\n};\n\n/* ===========================================================================\n * Scan a literal or distance tree to determine the frequencies of the codes\n * in the bit length tree.\n */\nconst scan_tree = (s, tree, max_code) => {\n // deflate_state *s;\n // ct_data *tree; /* the tree to be scanned */\n // int max_code; /* and its largest code of non zero frequency */\n\n let n; /* iterates over all tree elements */\n let prevlen = -1; /* last emitted length */\n let curlen; /* length of current code */\n\n let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */\n\n let count = 0; /* repeat count of the current code */\n let max_count = 7; /* max repeat count */\n let min_count = 4; /* min repeat count */\n\n if (nextlen === 0) {\n max_count = 138;\n min_count = 3;\n }\n tree[(max_code + 1) * 2 + 1] /*.Len*/ = 0xffff; /* guard */\n\n for (n = 0; n <= max_code; n++) {\n curlen = nextlen;\n nextlen = tree[(n + 1) * 2 + 1] /*.Len*/;\n if (++count < max_count && curlen === nextlen) {\n continue;\n } else if (count < min_count) {\n s.bl_tree[curlen * 2] /*.Freq*/ += count;\n } else if (curlen !== 0) {\n if (curlen !== prevlen) {\n s.bl_tree[curlen * 2] /*.Freq*/++;\n }\n s.bl_tree[REP_3_6 * 2] /*.Freq*/++;\n } else if (count <= 10) {\n s.bl_tree[REPZ_3_10 * 2] /*.Freq*/++;\n } else {\n s.bl_tree[REPZ_11_138 * 2] /*.Freq*/++;\n }\n count = 0;\n prevlen = curlen;\n if (nextlen === 0) {\n max_count = 138;\n min_count = 3;\n } else if (curlen === nextlen) {\n max_count = 6;\n min_count = 3;\n } else {\n max_count = 7;\n min_count = 4;\n }\n }\n};\n\n/* ===========================================================================\n * Send a literal or distance tree in compressed form, using the codes in\n * bl_tree.\n */\nconst send_tree = (s, tree, max_code) => {\n // deflate_state *s;\n // ct_data *tree; /* the tree to be scanned */\n // int max_code; /* and its largest code of non zero frequency */\n\n let n; /* iterates over all tree elements */\n let prevlen = -1; /* last emitted length */\n let curlen; /* length of current code */\n\n let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */\n\n let count = 0; /* repeat count of the current code */\n let max_count = 7; /* max repeat count */\n let min_count = 4; /* min repeat count */\n\n /* tree[max_code+1].Len = -1; */ /* guard already set */\n if (nextlen === 0) {\n max_count = 138;\n min_count = 3;\n }\n for (n = 0; n <= max_code; n++) {\n curlen = nextlen;\n nextlen = tree[(n + 1) * 2 + 1] /*.Len*/;\n if (++count < max_count && curlen === nextlen) {\n continue;\n } else if (count < min_count) {\n do {\n send_code(s, curlen, s.bl_tree);\n } while (--count !== 0);\n } else if (curlen !== 0) {\n if (curlen !== prevlen) {\n send_code(s, curlen, s.bl_tree);\n count--;\n }\n //Assert(count >= 3 && count <= 6, \" 3_6?\");\n send_code(s, REP_3_6, s.bl_tree);\n send_bits(s, count - 3, 2);\n } else if (count <= 10) {\n send_code(s, REPZ_3_10, s.bl_tree);\n send_bits(s, count - 3, 3);\n } else {\n send_code(s, REPZ_11_138, s.bl_tree);\n send_bits(s, count - 11, 7);\n }\n count = 0;\n prevlen = curlen;\n if (nextlen === 0) {\n max_count = 138;\n min_count = 3;\n } else if (curlen === nextlen) {\n max_count = 6;\n min_count = 3;\n } else {\n max_count = 7;\n min_count = 4;\n }\n }\n};\n\n/* ===========================================================================\n * Construct the Huffman tree for the bit lengths and return the index in\n * bl_order of the last bit length code to send.\n */\nconst build_bl_tree = s => {\n let max_blindex; /* index of last bit length code of non zero freq */\n\n /* Determine the bit length frequencies for literal and distance trees */\n scan_tree(s, s.dyn_ltree, s.l_desc.max_code);\n scan_tree(s, s.dyn_dtree, s.d_desc.max_code);\n\n /* Build the bit length tree: */\n build_tree(s, s.bl_desc);\n /* opt_len now includes the length of the tree representations, except\n * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.\n */\n\n /* Determine the number of bit length codes to send. The pkzip format\n * requires that at least 4 bit length codes be sent. (appnote.txt says\n * 3 but the actual value used is 4.)\n */\n for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) {\n if (s.bl_tree[bl_order[max_blindex] * 2 + 1] /*.Len*/ !== 0) {\n break;\n }\n }\n /* Update opt_len to include the bit length tree and counts */\n s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;\n //Tracev((stderr, \"\\ndyn trees: dyn %ld, stat %ld\",\n // s->opt_len, s->static_len));\n\n return max_blindex;\n};\n\n/* ===========================================================================\n * Send the header for a block using dynamic Huffman trees: the counts, the\n * lengths of the bit length codes, the literal tree and the distance tree.\n * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.\n */\nconst send_all_trees = (s, lcodes, dcodes, blcodes) => {\n // deflate_state *s;\n // int lcodes, dcodes, blcodes; /* number of codes for each tree */\n\n let rank; /* index in bl_order */\n\n //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, \"not enough codes\");\n //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,\n // \"too many codes\");\n //Tracev((stderr, \"\\nbl counts: \"));\n send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */\n send_bits(s, dcodes - 1, 5);\n send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */\n for (rank = 0; rank < blcodes; rank++) {\n //Tracev((stderr, \"\\nbl code %2d \", bl_order[rank]));\n send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1] /*.Len*/, 3);\n }\n //Tracev((stderr, \"\\nbl tree: sent %ld\", s->bits_sent));\n\n send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */\n //Tracev((stderr, \"\\nlit tree: sent %ld\", s->bits_sent));\n\n send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */\n //Tracev((stderr, \"\\ndist tree: sent %ld\", s->bits_sent));\n};\n\n/* ===========================================================================\n * Check if the data type is TEXT or BINARY, using the following algorithm:\n * - TEXT if the two conditions below are satisfied:\n * a) There are no non-portable control characters belonging to the\n * \"block list\" (0..6, 14..25, 28..31).\n * b) There is at least one printable character belonging to the\n * \"allow list\" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).\n * - BINARY otherwise.\n * - The following partially-portable control characters form a\n * \"gray list\" that is ignored in this detection algorithm:\n * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).\n * IN assertion: the fields Freq of dyn_ltree are set.\n */\nconst detect_data_type = s => {\n /* block_mask is the bit mask of block-listed bytes\n * set bits 0..6, 14..25, and 28..31\n * 0xf3ffc07f = binary 11110011111111111100000001111111\n */\n let block_mask = 0xf3ffc07f;\n let n;\n\n /* Check for non-textual (\"block-listed\") bytes. */\n for (n = 0; n <= 31; n++, block_mask >>>= 1) {\n if (block_mask & 1 && s.dyn_ltree[n * 2] /*.Freq*/ !== 0) {\n return Z_BINARY;\n }\n }\n\n /* Check for textual (\"allow-listed\") bytes. */\n if (s.dyn_ltree[9 * 2] /*.Freq*/ !== 0 || s.dyn_ltree[10 * 2] /*.Freq*/ !== 0 || s.dyn_ltree[13 * 2] /*.Freq*/ !== 0) {\n return Z_TEXT;\n }\n for (n = 32; n < LITERALS$1; n++) {\n if (s.dyn_ltree[n * 2] /*.Freq*/ !== 0) {\n return Z_TEXT;\n }\n }\n\n /* There are no \"block-listed\" or \"allow-listed\" bytes:\n * this stream either is empty or has tolerated (\"gray-listed\") bytes only.\n */\n return Z_BINARY;\n};\nlet static_init_done = false;\n\n/* ===========================================================================\n * Initialize the tree data structures for a new zlib stream.\n */\nconst _tr_init$1 = s => {\n if (!static_init_done) {\n tr_static_init();\n static_init_done = true;\n }\n s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);\n s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);\n s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);\n s.bi_buf = 0;\n s.bi_valid = 0;\n\n /* Initialize the first block of the first file: */\n init_block(s);\n};\n\n/* ===========================================================================\n * Send a stored block\n */\nconst _tr_stored_block$1 = (s, buf, stored_len, last) => {\n //DeflateState *s;\n //charf *buf; /* input block */\n //ulg stored_len; /* length of input block */\n //int last; /* one if this is the last block for a file */\n\n send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */\n bi_windup(s); /* align on byte boundary */\n put_short(s, stored_len);\n put_short(s, ~stored_len);\n if (stored_len) {\n s.pending_buf.set(s.window.subarray(buf, buf + stored_len), s.pending);\n }\n s.pending += stored_len;\n};\n\n/* ===========================================================================\n * Send one empty static block to give enough lookahead for inflate.\n * This takes 10 bits, of which 7 may remain in the bit buffer.\n */\nconst _tr_align$1 = s => {\n send_bits(s, STATIC_TREES << 1, 3);\n send_code(s, END_BLOCK, static_ltree);\n bi_flush(s);\n};\n\n/* ===========================================================================\n * Determine the best encoding for the current block: dynamic trees, static\n * trees or store, and write out the encoded block.\n */\nconst _tr_flush_block$1 = (s, buf, stored_len, last) => {\n //DeflateState *s;\n //charf *buf; /* input block, or NULL if too old */\n //ulg stored_len; /* length of input block */\n //int last; /* one if this is the last block for a file */\n\n let opt_lenb, static_lenb; /* opt_len and static_len in bytes */\n let max_blindex = 0; /* index of last bit length code of non zero freq */\n\n /* Build the Huffman trees unless a stored block is forced */\n if (s.level > 0) {\n /* Check if the file is binary or text */\n if (s.strm.data_type === Z_UNKNOWN$1) {\n s.strm.data_type = detect_data_type(s);\n }\n\n /* Construct the literal and distance trees */\n build_tree(s, s.l_desc);\n // Tracev((stderr, \"\\nlit data: dyn %ld, stat %ld\", s->opt_len,\n // s->static_len));\n\n build_tree(s, s.d_desc);\n // Tracev((stderr, \"\\ndist data: dyn %ld, stat %ld\", s->opt_len,\n // s->static_len));\n /* At this point, opt_len and static_len are the total bit lengths of\n * the compressed block data, excluding the tree representations.\n */\n\n /* Build the bit length tree for the above two trees, and get the index\n * in bl_order of the last bit length code to send.\n */\n max_blindex = build_bl_tree(s);\n\n /* Determine the best encoding. Compute the block lengths in bytes. */\n opt_lenb = s.opt_len + 3 + 7 >>> 3;\n static_lenb = s.static_len + 3 + 7 >>> 3;\n\n // Tracev((stderr, \"\\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u \",\n // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,\n // s->sym_next / 3));\n\n if (static_lenb <= opt_lenb) {\n opt_lenb = static_lenb;\n }\n } else {\n // Assert(buf != (char*)0, \"lost buf\");\n opt_lenb = static_lenb = stored_len + 5; /* force a stored block */\n }\n if (stored_len + 4 <= opt_lenb && buf !== -1) {\n /* 4: two words for the lengths */\n\n /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.\n * Otherwise we can't have processed more than WSIZE input bytes since\n * the last block flush, because compression would have been\n * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to\n * transform a block into a stored block.\n */\n _tr_stored_block$1(s, buf, stored_len, last);\n } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) {\n send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);\n compress_block(s, static_ltree, static_dtree);\n } else {\n send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);\n send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);\n compress_block(s, s.dyn_ltree, s.dyn_dtree);\n }\n // Assert (s->compressed_len == s->bits_sent, \"bad compressed size\");\n /* The above check is made mod 2^32, for files larger than 512 MB\n * and uLong implemented on 32 bits.\n */\n init_block(s);\n if (last) {\n bi_windup(s);\n }\n // Tracev((stderr,\"\\ncomprlen %lu(%lu) \", s->compressed_len>>3,\n // s->compressed_len-7*last));\n};\n\n/* ===========================================================================\n * Save the match info and tally the frequency counts. Return true if\n * the current block must be flushed.\n */\nconst _tr_tally$1 = (s, dist, lc) => {\n // deflate_state *s;\n // unsigned dist; /* distance of matched string */\n // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */\n\n s.pending_buf[s.sym_buf + s.sym_next++] = dist;\n s.pending_buf[s.sym_buf + s.sym_next++] = dist >> 8;\n s.pending_buf[s.sym_buf + s.sym_next++] = lc;\n if (dist === 0) {\n /* lc is the unmatched char */\n s.dyn_ltree[lc * 2] /*.Freq*/++;\n } else {\n s.matches++;\n /* Here, lc is the match length - MIN_MATCH */\n dist--; /* dist = match distance - 1 */\n //Assert((ush)dist < (ush)MAX_DIST(s) &&\n // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&\n // (ush)d_code(dist) < (ush)D_CODES, \"_tr_tally: bad match\");\n\n s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2] /*.Freq*/++;\n s.dyn_dtree[d_code(dist) * 2] /*.Freq*/++;\n }\n return s.sym_next === s.sym_end;\n};\nvar _tr_init_1 = _tr_init$1;\nvar _tr_stored_block_1 = _tr_stored_block$1;\nvar _tr_flush_block_1 = _tr_flush_block$1;\nvar _tr_tally_1 = _tr_tally$1;\nvar _tr_align_1 = _tr_align$1;\nvar trees = {\n _tr_init: _tr_init_1,\n _tr_stored_block: _tr_stored_block_1,\n _tr_flush_block: _tr_flush_block_1,\n _tr_tally: _tr_tally_1,\n _tr_align: _tr_align_1\n};\n\n// Note: adler32 takes 12% for level 0 and 2% for level 6.\n// It isn't worth it to make additional optimizations as in original.\n// Small size is preferable.\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nconst adler32 = (adler, buf, len, pos) => {\n let s1 = adler & 0xffff | 0,\n s2 = adler >>> 16 & 0xffff | 0,\n n = 0;\n while (len !== 0) {\n // Set limit ~ twice less than 5552, to keep\n // s2 in 31-bits, because we force signed ints.\n // in other case %= will fail.\n n = len > 2000 ? 2000 : len;\n len -= n;\n do {\n s1 = s1 + buf[pos++] | 0;\n s2 = s2 + s1 | 0;\n } while (--n);\n s1 %= 65521;\n s2 %= 65521;\n }\n return s1 | s2 << 16 | 0;\n};\nvar adler32_1 = adler32;\n\n// Note: we can't get significant speed boost here.\n// So write code to minimize size - no pregenerated tables\n// and array tools dependencies.\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n// Use ordinary array, since untyped makes no boost here\nconst makeTable = () => {\n let c,\n table = [];\n for (var n = 0; n < 256; n++) {\n c = n;\n for (var k = 0; k < 8; k++) {\n c = c & 1 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;\n }\n table[n] = c;\n }\n return table;\n};\n\n// Create table on load. Just 255 signed longs. Not a problem.\nconst crcTable = new Uint32Array(makeTable());\nconst crc32 = (crc, buf, len, pos) => {\n const t = crcTable;\n const end = pos + len;\n crc ^= -1;\n for (let i = pos; i < end; i++) {\n crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 0xFF];\n }\n return crc ^ -1; // >>> 0;\n};\nvar crc32_1 = crc32;\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nvar messages = {\n 2: 'need dictionary',\n /* Z_NEED_DICT 2 */\n 1: 'stream end',\n /* Z_STREAM_END 1 */\n 0: '',\n /* Z_OK 0 */\n '-1': 'file error',\n /* Z_ERRNO (-1) */\n '-2': 'stream error',\n /* Z_STREAM_ERROR (-2) */\n '-3': 'data error',\n /* Z_DATA_ERROR (-3) */\n '-4': 'insufficient memory',\n /* Z_MEM_ERROR (-4) */\n '-5': 'buffer error',\n /* Z_BUF_ERROR (-5) */\n '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nvar constants$2 = {\n /* Allowed flush values; see deflate() and inflate() below for details */\n Z_NO_FLUSH: 0,\n Z_PARTIAL_FLUSH: 1,\n Z_SYNC_FLUSH: 2,\n Z_FULL_FLUSH: 3,\n Z_FINISH: 4,\n Z_BLOCK: 5,\n Z_TREES: 6,\n /* Return codes for the compression/decompression functions. Negative values\n * are errors, positive values are used for special but normal events.\n */\n Z_OK: 0,\n Z_STREAM_END: 1,\n Z_NEED_DICT: 2,\n Z_ERRNO: -1,\n Z_STREAM_ERROR: -2,\n Z_DATA_ERROR: -3,\n Z_MEM_ERROR: -4,\n Z_BUF_ERROR: -5,\n //Z_VERSION_ERROR: -6,\n\n /* compression levels */\n Z_NO_COMPRESSION: 0,\n Z_BEST_SPEED: 1,\n Z_BEST_COMPRESSION: 9,\n Z_DEFAULT_COMPRESSION: -1,\n Z_FILTERED: 1,\n Z_HUFFMAN_ONLY: 2,\n Z_RLE: 3,\n Z_FIXED: 4,\n Z_DEFAULT_STRATEGY: 0,\n /* Possible values of the data_type field (though see inflate()) */\n Z_BINARY: 0,\n Z_TEXT: 1,\n //Z_ASCII: 1, // = Z_TEXT (deprecated)\n Z_UNKNOWN: 2,\n /* The deflate compression method */\n Z_DEFLATED: 8\n //Z_NULL: null // Use -1 or null inline, depending on var type\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nconst {\n _tr_init,\n _tr_stored_block,\n _tr_flush_block,\n _tr_tally,\n _tr_align\n} = trees;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\nconst {\n Z_NO_FLUSH: Z_NO_FLUSH$2,\n Z_PARTIAL_FLUSH,\n Z_FULL_FLUSH: Z_FULL_FLUSH$1,\n Z_FINISH: Z_FINISH$3,\n Z_BLOCK: Z_BLOCK$1,\n Z_OK: Z_OK$3,\n Z_STREAM_END: Z_STREAM_END$3,\n Z_STREAM_ERROR: Z_STREAM_ERROR$2,\n Z_DATA_ERROR: Z_DATA_ERROR$2,\n Z_BUF_ERROR: Z_BUF_ERROR$1,\n Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1,\n Z_FILTERED,\n Z_HUFFMAN_ONLY,\n Z_RLE,\n Z_FIXED,\n Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1,\n Z_UNKNOWN,\n Z_DEFLATED: Z_DEFLATED$2\n} = constants$2;\n\n/*============================================================================*/\n\nconst MAX_MEM_LEVEL = 9;\n/* Maximum value for memLevel in deflateInit2 */\nconst MAX_WBITS$1 = 15;\n/* 32K LZ77 window */\nconst DEF_MEM_LEVEL = 8;\nconst LENGTH_CODES = 29;\n/* number of length codes, not counting the special END_BLOCK code */\nconst LITERALS = 256;\n/* number of literal bytes 0..255 */\nconst L_CODES = LITERALS + 1 + LENGTH_CODES;\n/* number of Literal or Length codes, including the END_BLOCK code */\nconst D_CODES = 30;\n/* number of distance codes */\nconst BL_CODES = 19;\n/* number of codes used to transfer the bit lengths */\nconst HEAP_SIZE = 2 * L_CODES + 1;\n/* maximum heap size */\nconst MAX_BITS = 15;\n/* All codes must not exceed MAX_BITS bits */\n\nconst MIN_MATCH = 3;\nconst MAX_MATCH = 258;\nconst MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;\nconst PRESET_DICT = 0x20;\nconst INIT_STATE = 42; /* zlib header -> BUSY_STATE */\n//#ifdef GZIP\nconst GZIP_STATE = 57; /* gzip header -> BUSY_STATE | EXTRA_STATE */\n//#endif\nconst EXTRA_STATE = 69; /* gzip extra block -> NAME_STATE */\nconst NAME_STATE = 73; /* gzip file name -> COMMENT_STATE */\nconst COMMENT_STATE = 91; /* gzip comment -> HCRC_STATE */\nconst HCRC_STATE = 103; /* gzip header CRC -> BUSY_STATE */\nconst BUSY_STATE = 113; /* deflate -> FINISH_STATE */\nconst FINISH_STATE = 666; /* stream complete */\n\nconst BS_NEED_MORE = 1; /* block not completed, need more input or more output */\nconst BS_BLOCK_DONE = 2; /* block flush performed */\nconst BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */\nconst BS_FINISH_DONE = 4; /* finish done, accept no more input or output */\n\nconst OS_CODE = 0x03; // Unix :) . Don't detect, use this default.\n\nconst err = (strm, errorCode) => {\n strm.msg = messages[errorCode];\n return errorCode;\n};\nconst rank = f => {\n return f * 2 - (f > 4 ? 9 : 0);\n};\nconst zero = buf => {\n let len = buf.length;\n while (--len >= 0) {\n buf[len] = 0;\n }\n};\n\n/* ===========================================================================\n * Slide the hash table when sliding the window down (could be avoided with 32\n * bit values at the expense of memory usage). We slide even when level == 0 to\n * keep the hash table consistent if we switch back to level > 0 later.\n */\nconst slide_hash = s => {\n let n, m;\n let p;\n let wsize = s.w_size;\n n = s.hash_size;\n p = n;\n do {\n m = s.head[--p];\n s.head[p] = m >= wsize ? m - wsize : 0;\n } while (--n);\n n = wsize;\n //#ifndef FASTEST\n p = n;\n do {\n m = s.prev[--p];\n s.prev[p] = m >= wsize ? m - wsize : 0;\n /* If n is not on any hash chain, prev[n] is garbage but\n * its value will never be used.\n */\n } while (--n);\n //#endif\n};\n\n/* eslint-disable new-cap */\nlet HASH_ZLIB = (s, prev, data) => (prev << s.hash_shift ^ data) & s.hash_mask;\n// This hash causes less collisions, https://github.com/nodeca/pako/issues/135\n// But breaks binary compatibility\n//let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;\nlet HASH = HASH_ZLIB;\n\n/* =========================================================================\n * Flush as much pending output as possible. All deflate() output, except for\n * some deflate_stored() output, goes through this function so some\n * applications may wish to modify it to avoid allocating a large\n * strm->next_out buffer and copying into it. (See also read_buf()).\n */\nconst flush_pending = strm => {\n const s = strm.state;\n\n //_tr_flush_bits(s);\n let len = s.pending;\n if (len > strm.avail_out) {\n len = strm.avail_out;\n }\n if (len === 0) {\n return;\n }\n strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);\n strm.next_out += len;\n s.pending_out += len;\n strm.total_out += len;\n strm.avail_out -= len;\n s.pending -= len;\n if (s.pending === 0) {\n s.pending_out = 0;\n }\n};\nconst flush_block_only = (s, last) => {\n _tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last);\n s.block_start = s.strstart;\n flush_pending(s.strm);\n};\nconst put_byte = (s, b) => {\n s.pending_buf[s.pending++] = b;\n};\n\n/* =========================================================================\n * Put a short in the pending buffer. The 16-bit value is put in MSB order.\n * IN assertion: the stream state is correct and there is enough room in\n * pending_buf.\n */\nconst putShortMSB = (s, b) => {\n // put_byte(s, (Byte)(b >> 8));\n // put_byte(s, (Byte)(b & 0xff));\n s.pending_buf[s.pending++] = b >>> 8 & 0xff;\n s.pending_buf[s.pending++] = b & 0xff;\n};\n\n/* ===========================================================================\n * Read a new buffer from the current input stream, update the adler32\n * and total number of bytes read. All deflate() input goes through\n * this function so some applications may wish to modify it to avoid\n * allocating a large strm->input buffer and copying from it.\n * (See also flush_pending()).\n */\nconst read_buf = (strm, buf, start, size) => {\n let len = strm.avail_in;\n if (len > size) {\n len = size;\n }\n if (len === 0) {\n return 0;\n }\n strm.avail_in -= len;\n\n // zmemcpy(buf, strm->next_in, len);\n buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);\n if (strm.state.wrap === 1) {\n strm.adler = adler32_1(strm.adler, buf, len, start);\n } else if (strm.state.wrap === 2) {\n strm.adler = crc32_1(strm.adler, buf, len, start);\n }\n strm.next_in += len;\n strm.total_in += len;\n return len;\n};\n\n/* ===========================================================================\n * Set match_start to the longest match starting at the given string and\n * return its length. Matches shorter or equal to prev_length are discarded,\n * in which case the result is equal to prev_length and match_start is\n * garbage.\n * IN assertions: cur_match is the head of the hash chain for the current\n * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1\n * OUT assertion: the match length is not greater than s->lookahead.\n */\nconst longest_match = (s, cur_match) => {\n let chain_length = s.max_chain_length; /* max hash chain length */\n let scan = s.strstart; /* current string */\n let match; /* matched string */\n let len; /* length of current match */\n let best_len = s.prev_length; /* best match length so far */\n let nice_match = s.nice_match; /* stop if match long enough */\n const limit = s.strstart > s.w_size - MIN_LOOKAHEAD ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0 /*NIL*/;\n const _win = s.window; // shortcut\n\n const wmask = s.w_mask;\n const prev = s.prev;\n\n /* Stop when cur_match becomes <= limit. To simplify the code,\n * we prevent matches with the string of window index 0.\n */\n\n const strend = s.strstart + MAX_MATCH;\n let scan_end1 = _win[scan + best_len - 1];\n let scan_end = _win[scan + best_len];\n\n /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.\n * It is easy to get rid of this optimization if necessary.\n */\n // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, \"Code too clever\");\n\n /* Do not waste too much time if we already have a good match: */\n if (s.prev_length >= s.good_match) {\n chain_length >>= 2;\n }\n /* Do not look for matches beyond the end of the input. This is necessary\n * to make deflate deterministic.\n */\n if (nice_match > s.lookahead) {\n nice_match = s.lookahead;\n }\n\n // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, \"need lookahead\");\n\n do {\n // Assert(cur_match < s->strstart, \"no future\");\n match = cur_match;\n\n /* Skip to next match if the match length cannot increase\n * or if the match length is less than 2. Note that the checks below\n * for insufficient lookahead only occur occasionally for performance\n * reasons. Therefore uninitialized memory will be accessed, and\n * conditional jumps will be made that depend on those values.\n * However the length of the match is limited to the lookahead, so\n * the output of deflate is not affected by the uninitialized values.\n */\n\n if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) {\n continue;\n }\n\n /* The check at best_len-1 can be removed because it will be made\n * again later. (This heuristic is not always a win.)\n * It is not necessary to compare scan[2] and match[2] since they\n * are always equal when the other bytes match, given that\n * the hash keys are equal and that HASH_BITS >= 8.\n */\n scan += 2;\n match++;\n // Assert(*scan == *match, \"match[2]?\");\n\n /* We check for insufficient lookahead only every 8th comparison;\n * the 256th check will be made at strstart+258.\n */\n do {\n /*jshint noempty:false*/\n } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend);\n\n // Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n\n len = MAX_MATCH - (strend - scan);\n scan = strend - MAX_MATCH;\n if (len > best_len) {\n s.match_start = cur_match;\n best_len = len;\n if (len >= nice_match) {\n break;\n }\n scan_end1 = _win[scan + best_len - 1];\n scan_end = _win[scan + best_len];\n }\n } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);\n if (best_len <= s.lookahead) {\n return best_len;\n }\n return s.lookahead;\n};\n\n/* ===========================================================================\n * Fill the window when the lookahead becomes insufficient.\n * Updates strstart and lookahead.\n *\n * IN assertion: lookahead < MIN_LOOKAHEAD\n * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD\n * At least one byte has been read, or avail_in == 0; reads are\n * performed for at least two bytes (required for the zip translate_eol\n * option -- not supported here).\n */\nconst fill_window = s => {\n const _w_size = s.w_size;\n let n, more, str;\n\n //Assert(s->lookahead < MIN_LOOKAHEAD, \"already enough lookahead\");\n\n do {\n more = s.window_size - s.lookahead - s.strstart;\n\n // JS ints have 32 bit, block below not needed\n /* Deal with !@#$% 64K limit: */\n //if (sizeof(int) <= 2) {\n // if (more == 0 && s->strstart == 0 && s->lookahead == 0) {\n // more = wsize;\n //\n // } else if (more == (unsigned)(-1)) {\n // /* Very unlikely, but possible on 16 bit machine if\n // * strstart == 0 && lookahead == 1 (input done a byte at time)\n // */\n // more--;\n // }\n //}\n\n /* If the window is almost full and there is insufficient lookahead,\n * move the upper half to the lower one to make room in the upper half.\n */\n if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {\n s.window.set(s.window.subarray(_w_size, _w_size + _w_size - more), 0);\n s.match_start -= _w_size;\n s.strstart -= _w_size;\n /* we now have strstart >= MAX_DIST */\n s.block_start -= _w_size;\n if (s.insert > s.strstart) {\n s.insert = s.strstart;\n }\n slide_hash(s);\n more += _w_size;\n }\n if (s.strm.avail_in === 0) {\n break;\n }\n\n /* If there was no sliding:\n * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&\n * more == window_size - lookahead - strstart\n * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)\n * => more >= window_size - 2*WSIZE + 2\n * In the BIG_MEM or MMAP case (not yet supported),\n * window_size == input_size + MIN_LOOKAHEAD &&\n * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.\n * Otherwise, window_size == 2*WSIZE so more >= 2.\n * If there was sliding, more >= WSIZE. So in all cases, more >= 2.\n */\n //Assert(more >= 2, \"more < 2\");\n n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);\n s.lookahead += n;\n\n /* Initialize the hash value now that we have some input: */\n if (s.lookahead + s.insert >= MIN_MATCH) {\n str = s.strstart - s.insert;\n s.ins_h = s.window[str];\n\n /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */\n s.ins_h = HASH(s, s.ins_h, s.window[str + 1]);\n //#if MIN_MATCH != 3\n // Call update_hash() MIN_MATCH-3 more times\n //#endif\n while (s.insert) {\n /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);\n s.prev[str & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = str;\n str++;\n s.insert--;\n if (s.lookahead + s.insert < MIN_MATCH) {\n break;\n }\n }\n }\n /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,\n * but this is not important since only literal bytes will be emitted.\n */\n } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);\n\n /* If the WIN_INIT bytes after the end of the current data have never been\n * written, then zero those bytes in order to avoid memory check reports of\n * the use of uninitialized (or uninitialised as Julian writes) bytes by\n * the longest match routines. Update the high water mark for the next\n * time through here. WIN_INIT is set to MAX_MATCH since the longest match\n * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.\n */\n // if (s.high_water < s.window_size) {\n // const curr = s.strstart + s.lookahead;\n // let init = 0;\n //\n // if (s.high_water < curr) {\n // /* Previous high water mark below current data -- zero WIN_INIT\n // * bytes or up to end of window, whichever is less.\n // */\n // init = s.window_size - curr;\n // if (init > WIN_INIT)\n // init = WIN_INIT;\n // zmemzero(s->window + curr, (unsigned)init);\n // s->high_water = curr + init;\n // }\n // else if (s->high_water < (ulg)curr + WIN_INIT) {\n // /* High water mark at or above current data, but below current data\n // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up\n // * to end of window, whichever is less.\n // */\n // init = (ulg)curr + WIN_INIT - s->high_water;\n // if (init > s->window_size - s->high_water)\n // init = s->window_size - s->high_water;\n // zmemzero(s->window + s->high_water, (unsigned)init);\n // s->high_water += init;\n // }\n // }\n //\n // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,\n // \"not enough room for search\");\n};\n\n/* ===========================================================================\n * Copy without compression as much as possible from the input stream, return\n * the current block state.\n *\n * In case deflateParams() is used to later switch to a non-zero compression\n * level, s->matches (otherwise unused when storing) keeps track of the number\n * of hash table slides to perform. If s->matches is 1, then one hash table\n * slide will be done when switching. If s->matches is 2, the maximum value\n * allowed here, then the hash table will be cleared, since two or more slides\n * is the same as a clear.\n *\n * deflate_stored() is written to minimize the number of times an input byte is\n * copied. It is most efficient with large input and output buffers, which\n * maximizes the opportunites to have a single copy from next_in to next_out.\n */\nconst deflate_stored = (s, flush) => {\n /* Smallest worthy block size when not flushing or finishing. By default\n * this is 32K. This can be as small as 507 bytes for memLevel == 1. For\n * large input and output buffers, the stored block size will be larger.\n */\n let min_block = s.pending_buf_size - 5 > s.w_size ? s.w_size : s.pending_buf_size - 5;\n\n /* Copy as many min_block or larger stored blocks directly to next_out as\n * possible. If flushing, copy the remaining available input to next_out as\n * stored blocks, if there is enough space.\n */\n let len,\n left,\n have,\n last = 0;\n let used = s.strm.avail_in;\n do {\n /* Set len to the maximum size block that we can copy directly with the\n * available input data and output space. Set left to how much of that\n * would be copied from what's left in the window.\n */\n len = 65535 /* MAX_STORED */; /* maximum deflate stored block length */\n have = s.bi_valid + 42 >> 3; /* number of header bytes */\n if (s.strm.avail_out < have) {\n /* need room for header */\n break;\n }\n /* maximum stored block length that will fit in avail_out: */\n have = s.strm.avail_out - have;\n left = s.strstart - s.block_start; /* bytes left in window */\n if (len > left + s.strm.avail_in) {\n len = left + s.strm.avail_in; /* limit len to the input */\n }\n if (len > have) {\n len = have; /* limit len to the output */\n }\n\n /* If the stored block would be less than min_block in length, or if\n * unable to copy all of the available input when flushing, then try\n * copying to the window and the pending buffer instead. Also don't\n * write an empty block when flushing -- deflate() does that.\n */\n if (len < min_block && (len === 0 && flush !== Z_FINISH$3 || flush === Z_NO_FLUSH$2 || len !== left + s.strm.avail_in)) {\n break;\n }\n\n /* Make a dummy stored block in pending to get the header bytes,\n * including any pending bits. This also updates the debugging counts.\n */\n last = flush === Z_FINISH$3 && len === left + s.strm.avail_in ? 1 : 0;\n _tr_stored_block(s, 0, 0, last);\n\n /* Replace the lengths in the dummy stored block with len. */\n s.pending_buf[s.pending - 4] = len;\n s.pending_buf[s.pending - 3] = len >> 8;\n s.pending_buf[s.pending - 2] = ~len;\n s.pending_buf[s.pending - 1] = ~len >> 8;\n\n /* Write the stored block header bytes. */\n flush_pending(s.strm);\n\n //#ifdef ZLIB_DEBUG\n // /* Update debugging counts for the data about to be copied. */\n // s->compressed_len += len << 3;\n // s->bits_sent += len << 3;\n //#endif\n\n /* Copy uncompressed bytes from the window to next_out. */\n if (left) {\n if (left > len) {\n left = len;\n }\n //zmemcpy(s->strm->next_out, s->window + s->block_start, left);\n s.strm.output.set(s.window.subarray(s.block_start, s.block_start + left), s.strm.next_out);\n s.strm.next_out += left;\n s.strm.avail_out -= left;\n s.strm.total_out += left;\n s.block_start += left;\n len -= left;\n }\n\n /* Copy uncompressed bytes directly from next_in to next_out, updating\n * the check value.\n */\n if (len) {\n read_buf(s.strm, s.strm.output, s.strm.next_out, len);\n s.strm.next_out += len;\n s.strm.avail_out -= len;\n s.strm.total_out += len;\n }\n } while (last === 0);\n\n /* Update the sliding window with the last s->w_size bytes of the copied\n * data, or append all of the copied data to the existing window if less\n * than s->w_size bytes were copied. Also update the number of bytes to\n * insert in the hash tables, in the event that deflateParams() switches to\n * a non-zero compression level.\n */\n used -= s.strm.avail_in; /* number of input bytes directly copied */\n if (used) {\n /* If any input was used, then no unused input remains in the window,\n * therefore s->block_start == s->strstart.\n */\n if (used >= s.w_size) {\n /* supplant the previous history */\n s.matches = 2; /* clear hash */\n //zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);\n s.window.set(s.strm.input.subarray(s.strm.next_in - s.w_size, s.strm.next_in), 0);\n s.strstart = s.w_size;\n s.insert = s.strstart;\n } else {\n if (s.window_size - s.strstart <= used) {\n /* Slide the window down. */\n s.strstart -= s.w_size;\n //zmemcpy(s->window, s->window + s->w_size, s->strstart);\n s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);\n if (s.matches < 2) {\n s.matches++; /* add a pending slide_hash() */\n }\n if (s.insert > s.strstart) {\n s.insert = s.strstart;\n }\n }\n //zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);\n s.window.set(s.strm.input.subarray(s.strm.next_in - used, s.strm.next_in), s.strstart);\n s.strstart += used;\n s.insert += used > s.w_size - s.insert ? s.w_size - s.insert : used;\n }\n s.block_start = s.strstart;\n }\n if (s.high_water < s.strstart) {\n s.high_water = s.strstart;\n }\n\n /* If the last block was written to next_out, then done. */\n if (last) {\n return BS_FINISH_DONE;\n }\n\n /* If flushing and all input has been consumed, then done. */\n if (flush !== Z_NO_FLUSH$2 && flush !== Z_FINISH$3 && s.strm.avail_in === 0 && s.strstart === s.block_start) {\n return BS_BLOCK_DONE;\n }\n\n /* Fill the window with any remaining input. */\n have = s.window_size - s.strstart;\n if (s.strm.avail_in > have && s.block_start >= s.w_size) {\n /* Slide the window down. */\n s.block_start -= s.w_size;\n s.strstart -= s.w_size;\n //zmemcpy(s->window, s->window + s->w_size, s->strstart);\n s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);\n if (s.matches < 2) {\n s.matches++; /* add a pending slide_hash() */\n }\n have += s.w_size; /* more space now */\n if (s.insert > s.strstart) {\n s.insert = s.strstart;\n }\n }\n if (have > s.strm.avail_in) {\n have = s.strm.avail_in;\n }\n if (have) {\n read_buf(s.strm, s.window, s.strstart, have);\n s.strstart += have;\n s.insert += have > s.w_size - s.insert ? s.w_size - s.insert : have;\n }\n if (s.high_water < s.strstart) {\n s.high_water = s.strstart;\n }\n\n /* There was not enough avail_out to write a complete worthy or flushed\n * stored block to next_out. Write a stored block to pending instead, if we\n * have enough input for a worthy block, or if flushing and there is enough\n * room for the remaining input as a stored block in the pending buffer.\n */\n have = s.bi_valid + 42 >> 3; /* number of header bytes */\n /* maximum stored block length that will fit in pending: */\n have = s.pending_buf_size - have > 65535 /* MAX_STORED */ ? 65535 /* MAX_STORED */ : s.pending_buf_size - have;\n min_block = have > s.w_size ? s.w_size : have;\n left = s.strstart - s.block_start;\n if (left >= min_block || (left || flush === Z_FINISH$3) && flush !== Z_NO_FLUSH$2 && s.strm.avail_in === 0 && left <= have) {\n len = left > have ? have : left;\n last = flush === Z_FINISH$3 && s.strm.avail_in === 0 && len === left ? 1 : 0;\n _tr_stored_block(s, s.block_start, len, last);\n s.block_start += len;\n flush_pending(s.strm);\n }\n\n /* We've done all we can with the available input and output. */\n return last ? BS_FINISH_STARTED : BS_NEED_MORE;\n};\n\n/* ===========================================================================\n * Compress as much as possible from the input stream, return the current\n * block state.\n * This function does not perform lazy evaluation of matches and inserts\n * new strings in the dictionary only for unmatched strings or for short\n * matches. It is used only for the fast compression options.\n */\nconst deflate_fast = (s, flush) => {\n let hash_head; /* head of the hash chain */\n let bflush; /* set if current block must be flushed */\n\n for (;;) {\n /* Make sure that we always have enough lookahead, except\n * at the end of the input file. We need MAX_MATCH bytes\n * for the next match, plus MIN_MATCH bytes to insert the\n * string following the next match.\n */\n if (s.lookahead < MIN_LOOKAHEAD) {\n fill_window(s);\n if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {\n return BS_NEED_MORE;\n }\n if (s.lookahead === 0) {\n break; /* flush the current block */\n }\n }\n\n /* Insert the string window[strstart .. strstart+2] in the\n * dictionary, and set hash_head to the head of the hash chain:\n */\n hash_head = 0 /*NIL*/;\n if (s.lookahead >= MIN_MATCH) {\n /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = s.strstart;\n /***/\n }\n\n /* Find the longest match, discarding those <= prev_length.\n * At this point we have always match_length < MIN_MATCH\n */\n if (hash_head !== 0 /*NIL*/ && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) {\n /* To simplify the code, we prevent matches with the string\n * of window index 0 (in particular we have to avoid a match\n * of the string with itself at the start of the input file).\n */\n s.match_length = longest_match(s, hash_head);\n /* longest_match() sets match_start */\n }\n if (s.match_length >= MIN_MATCH) {\n // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only\n\n /*** _tr_tally_dist(s, s.strstart - s.match_start,\n s.match_length - MIN_MATCH, bflush); ***/\n bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);\n s.lookahead -= s.match_length;\n\n /* Insert new strings in the hash table only if the match length\n * is not too large. This saves time but degrades compression.\n */\n if (s.match_length <= s.max_lazy_match /*max_insert_length*/ && s.lookahead >= MIN_MATCH) {\n s.match_length--; /* string at strstart already in table */\n do {\n s.strstart++;\n /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = s.strstart;\n /***/\n /* strstart never exceeds WSIZE-MAX_MATCH, so there are\n * always MIN_MATCH bytes ahead.\n */\n } while (--s.match_length !== 0);\n s.strstart++;\n } else {\n s.strstart += s.match_length;\n s.match_length = 0;\n s.ins_h = s.window[s.strstart];\n /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */\n s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]);\n\n //#if MIN_MATCH != 3\n // Call UPDATE_HASH() MIN_MATCH-3 more times\n //#endif\n /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not\n * matter since it will be recomputed at next deflate call.\n */\n }\n } else {\n /* No match, output a literal byte */\n //Tracevv((stderr,\"%c\", s.window[s.strstart]));\n /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n bflush = _tr_tally(s, 0, s.window[s.strstart]);\n s.lookahead--;\n s.strstart++;\n }\n if (bflush) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n }\n s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;\n if (flush === Z_FINISH$3) {\n /*** FLUSH_BLOCK(s, 1); ***/\n flush_block_only(s, true);\n if (s.strm.avail_out === 0) {\n return BS_FINISH_STARTED;\n }\n /***/\n return BS_FINISH_DONE;\n }\n if (s.sym_next) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n return BS_BLOCK_DONE;\n};\n\n/* ===========================================================================\n * Same as above, but achieves better compression. We use a lazy\n * evaluation for matches: a match is finally adopted only if there is\n * no better match at the next window position.\n */\nconst deflate_slow = (s, flush) => {\n let hash_head; /* head of hash chain */\n let bflush; /* set if current block must be flushed */\n\n let max_insert;\n\n /* Process the input block. */\n for (;;) {\n /* Make sure that we always have enough lookahead, except\n * at the end of the input file. We need MAX_MATCH bytes\n * for the next match, plus MIN_MATCH bytes to insert the\n * string following the next match.\n */\n if (s.lookahead < MIN_LOOKAHEAD) {\n fill_window(s);\n if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {\n return BS_NEED_MORE;\n }\n if (s.lookahead === 0) {\n break;\n } /* flush the current block */\n }\n\n /* Insert the string window[strstart .. strstart+2] in the\n * dictionary, and set hash_head to the head of the hash chain:\n */\n hash_head = 0 /*NIL*/;\n if (s.lookahead >= MIN_MATCH) {\n /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = s.strstart;\n /***/\n }\n\n /* Find the longest match, discarding those <= prev_length.\n */\n s.prev_length = s.match_length;\n s.prev_match = s.match_start;\n s.match_length = MIN_MATCH - 1;\n if (hash_head !== 0 /*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD /*MAX_DIST(s)*/) {\n /* To simplify the code, we prevent matches with the string\n * of window index 0 (in particular we have to avoid a match\n * of the string with itself at the start of the input file).\n */\n s.match_length = longest_match(s, hash_head);\n /* longest_match() sets match_start */\n\n if (s.match_length <= 5 && (s.strategy === Z_FILTERED || s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096 /*TOO_FAR*/)) {\n /* If prev_match is also MIN_MATCH, match_start is garbage\n * but we will ignore the current match anyway.\n */\n s.match_length = MIN_MATCH - 1;\n }\n }\n /* If there was a match at the previous step and the current\n * match is not better, output the previous match:\n */\n if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {\n max_insert = s.strstart + s.lookahead - MIN_MATCH;\n /* Do not insert strings in hash table beyond this. */\n\n //check_match(s, s.strstart-1, s.prev_match, s.prev_length);\n\n /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,\n s.prev_length - MIN_MATCH, bflush);***/\n bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);\n /* Insert in hash table all strings up to the end of the match.\n * strstart-1 and strstart are already inserted. If there is not\n * enough lookahead, the last two strings are not inserted in\n * the hash table.\n */\n s.lookahead -= s.prev_length - 1;\n s.prev_length -= 2;\n do {\n if (++s.strstart <= max_insert) {\n /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);\n hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = s.strstart;\n /***/\n }\n } while (--s.prev_length !== 0);\n s.match_available = 0;\n s.match_length = MIN_MATCH - 1;\n s.strstart++;\n if (bflush) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n } else if (s.match_available) {\n /* If there was no match at the previous position, output a\n * single literal. If there was a match but the current match\n * is longer, truncate the previous match to a single literal.\n */\n //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);\n if (bflush) {\n /*** FLUSH_BLOCK_ONLY(s, 0) ***/\n flush_block_only(s, false);\n /***/\n }\n s.strstart++;\n s.lookahead--;\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n } else {\n /* There is no previous match to compare with, wait for\n * the next step to decide.\n */\n s.match_available = 1;\n s.strstart++;\n s.lookahead--;\n }\n }\n //Assert (flush != Z_NO_FLUSH, \"no flush?\");\n if (s.match_available) {\n //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);\n s.match_available = 0;\n }\n s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;\n if (flush === Z_FINISH$3) {\n /*** FLUSH_BLOCK(s, 1); ***/\n flush_block_only(s, true);\n if (s.strm.avail_out === 0) {\n return BS_FINISH_STARTED;\n }\n /***/\n return BS_FINISH_DONE;\n }\n if (s.sym_next) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n return BS_BLOCK_DONE;\n};\n\n/* ===========================================================================\n * For Z_RLE, simply look for runs of bytes, generate matches only of distance\n * one. Do not maintain a hash table. (It will be regenerated if this run of\n * deflate switches away from Z_RLE.)\n */\nconst deflate_rle = (s, flush) => {\n let bflush; /* set if current block must be flushed */\n let prev; /* byte at distance one to match */\n let scan, strend; /* scan goes up to strend for length of run */\n\n const _win = s.window;\n for (;;) {\n /* Make sure that we always have enough lookahead, except\n * at the end of the input file. We need MAX_MATCH bytes\n * for the longest run, plus one for the unrolled loop.\n */\n if (s.lookahead <= MAX_MATCH) {\n fill_window(s);\n if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) {\n return BS_NEED_MORE;\n }\n if (s.lookahead === 0) {\n break;\n } /* flush the current block */\n }\n\n /* See how many times the previous byte repeats */\n s.match_length = 0;\n if (s.lookahead >= MIN_MATCH && s.strstart > 0) {\n scan = s.strstart - 1;\n prev = _win[scan];\n if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {\n strend = s.strstart + MAX_MATCH;\n do {\n /*jshint noempty:false*/\n } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend);\n s.match_length = MAX_MATCH - (strend - scan);\n if (s.match_length > s.lookahead) {\n s.match_length = s.lookahead;\n }\n }\n //Assert(scan <= s->window+(uInt)(s->window_size-1), \"wild scan\");\n }\n\n /* Emit match if have run of MIN_MATCH or longer, else emit literal */\n if (s.match_length >= MIN_MATCH) {\n //check_match(s, s.strstart, s.strstart - 1, s.match_length);\n\n /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/\n bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH);\n s.lookahead -= s.match_length;\n s.strstart += s.match_length;\n s.match_length = 0;\n } else {\n /* No match, output a literal byte */\n //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n bflush = _tr_tally(s, 0, s.window[s.strstart]);\n s.lookahead--;\n s.strstart++;\n }\n if (bflush) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n }\n s.insert = 0;\n if (flush === Z_FINISH$3) {\n /*** FLUSH_BLOCK(s, 1); ***/\n flush_block_only(s, true);\n if (s.strm.avail_out === 0) {\n return BS_FINISH_STARTED;\n }\n /***/\n return BS_FINISH_DONE;\n }\n if (s.sym_next) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n return BS_BLOCK_DONE;\n};\n\n/* ===========================================================================\n * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.\n * (It will be regenerated if this run of deflate switches away from Huffman.)\n */\nconst deflate_huff = (s, flush) => {\n let bflush; /* set if current block must be flushed */\n\n for (;;) {\n /* Make sure that we have a literal to write. */\n if (s.lookahead === 0) {\n fill_window(s);\n if (s.lookahead === 0) {\n if (flush === Z_NO_FLUSH$2) {\n return BS_NEED_MORE;\n }\n break; /* flush the current block */\n }\n }\n\n /* Output a literal byte */\n s.match_length = 0;\n //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n bflush = _tr_tally(s, 0, s.window[s.strstart]);\n s.lookahead--;\n s.strstart++;\n if (bflush) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n }\n s.insert = 0;\n if (flush === Z_FINISH$3) {\n /*** FLUSH_BLOCK(s, 1); ***/\n flush_block_only(s, true);\n if (s.strm.avail_out === 0) {\n return BS_FINISH_STARTED;\n }\n /***/\n return BS_FINISH_DONE;\n }\n if (s.sym_next) {\n /*** FLUSH_BLOCK(s, 0); ***/\n flush_block_only(s, false);\n if (s.strm.avail_out === 0) {\n return BS_NEED_MORE;\n }\n /***/\n }\n return BS_BLOCK_DONE;\n};\n\n/* Values for max_lazy_match, good_match and max_chain_length, depending on\n * the desired pack level (0..9). The values given below have been tuned to\n * exclude worst case performance for pathological files. Better values may be\n * found for specific files.\n */\nfunction Config(good_length, max_lazy, nice_length, max_chain, func) {\n this.good_length = good_length;\n this.max_lazy = max_lazy;\n this.nice_length = nice_length;\n this.max_chain = max_chain;\n this.func = func;\n}\nconst configuration_table = [/* good lazy nice chain */\nnew Config(0, 0, 0, 0, deflate_stored), /* 0 store only */\nnew Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */\nnew Config(4, 5, 16, 8, deflate_fast), /* 2 */\nnew Config(4, 6, 32, 32, deflate_fast), /* 3 */\n\nnew Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */\nnew Config(8, 16, 32, 32, deflate_slow), /* 5 */\nnew Config(8, 16, 128, 128, deflate_slow), /* 6 */\nnew Config(8, 32, 128, 256, deflate_slow), /* 7 */\nnew Config(32, 128, 258, 1024, deflate_slow), /* 8 */\nnew Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */];\n\n/* ===========================================================================\n * Initialize the \"longest match\" routines for a new zlib stream\n */\nconst lm_init = s => {\n s.window_size = 2 * s.w_size;\n\n /*** CLEAR_HASH(s); ***/\n zero(s.head); // Fill with NIL (= 0);\n\n /* Set the default configuration parameters:\n */\n s.max_lazy_match = configuration_table[s.level].max_lazy;\n s.good_match = configuration_table[s.level].good_length;\n s.nice_match = configuration_table[s.level].nice_length;\n s.max_chain_length = configuration_table[s.level].max_chain;\n s.strstart = 0;\n s.block_start = 0;\n s.lookahead = 0;\n s.insert = 0;\n s.match_length = s.prev_length = MIN_MATCH - 1;\n s.match_available = 0;\n s.ins_h = 0;\n};\nfunction DeflateState() {\n this.strm = null; /* pointer back to this zlib stream */\n this.status = 0; /* as the name implies */\n this.pending_buf = null; /* output still pending */\n this.pending_buf_size = 0; /* size of pending_buf */\n this.pending_out = 0; /* next pending byte to output to the stream */\n this.pending = 0; /* nb of bytes in the pending buffer */\n this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */\n this.gzhead = null; /* gzip header information to write */\n this.gzindex = 0; /* where in extra, name, or comment */\n this.method = Z_DEFLATED$2; /* can only be DEFLATED */\n this.last_flush = -1; /* value of flush param for previous deflate call */\n\n this.w_size = 0; /* LZ77 window size (32K by default) */\n this.w_bits = 0; /* log2(w_size) (8..16) */\n this.w_mask = 0; /* w_size - 1 */\n\n this.window = null;\n /* Sliding window. Input bytes are read into the second half of the window,\n * and move to the first half later to keep a dictionary of at least wSize\n * bytes. With this organization, matches are limited to a distance of\n * wSize-MAX_MATCH bytes, but this ensures that IO is always\n * performed with a length multiple of the block size.\n */\n\n this.window_size = 0;\n /* Actual size of window: 2*wSize, except when the user input buffer\n * is directly used as sliding window.\n */\n\n this.prev = null;\n /* Link to older string with same hash index. To limit the size of this\n * array to 64K, this link is maintained only for the last 32K strings.\n * An index in this array is thus a window index modulo 32K.\n */\n\n this.head = null; /* Heads of the hash chains or NIL. */\n\n this.ins_h = 0; /* hash index of string to be inserted */\n this.hash_size = 0; /* number of elements in hash table */\n this.hash_bits = 0; /* log2(hash_size) */\n this.hash_mask = 0; /* hash_size-1 */\n\n this.hash_shift = 0;\n /* Number of bits by which ins_h must be shifted at each input\n * step. It must be such that after MIN_MATCH steps, the oldest\n * byte no longer takes part in the hash key, that is:\n * hash_shift * MIN_MATCH >= hash_bits\n */\n\n this.block_start = 0;\n /* Window position at the beginning of the current output block. Gets\n * negative when the window is moved backwards.\n */\n\n this.match_length = 0; /* length of best match */\n this.prev_match = 0; /* previous match */\n this.match_available = 0; /* set if previous match exists */\n this.strstart = 0; /* start of string to insert */\n this.match_start = 0; /* start of matching string */\n this.lookahead = 0; /* number of valid bytes ahead in window */\n\n this.prev_length = 0;\n /* Length of the best match at previous step. Matches not greater than this\n * are discarded. This is used in the lazy match evaluation.\n */\n\n this.max_chain_length = 0;\n /* To speed up deflation, hash chains are never searched beyond this\n * length. A higher limit improves compression ratio but degrades the\n * speed.\n */\n\n this.max_lazy_match = 0;\n /* Attempt to find a better match only when the current match is strictly\n * smaller than this value. This mechanism is used only for compression\n * levels >= 4.\n */\n // That's alias to max_lazy_match, don't use directly\n //this.max_insert_length = 0;\n /* Insert new strings in the hash table only if the match length is not\n * greater than this length. This saves time but degrades compression.\n * max_insert_length is used only for compression levels <= 3.\n */\n\n this.level = 0; /* compression level (1..9) */\n this.strategy = 0; /* favor or force Huffman coding*/\n\n this.good_match = 0;\n /* Use a faster search when the previous match is longer than this */\n\n this.nice_match = 0; /* Stop searching when current match exceeds this */\n\n /* used by trees.c: */\n\n /* Didn't use ct_data typedef below to suppress compiler warning */\n\n // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */\n // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */\n // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */\n\n // Use flat array of DOUBLE size, with interleaved fata,\n // because JS does not support effective\n this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2);\n this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2);\n this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2);\n zero(this.dyn_ltree);\n zero(this.dyn_dtree);\n zero(this.bl_tree);\n this.l_desc = null; /* desc. for literal tree */\n this.d_desc = null; /* desc. for distance tree */\n this.bl_desc = null; /* desc. for bit length tree */\n\n //ush bl_count[MAX_BITS+1];\n this.bl_count = new Uint16Array(MAX_BITS + 1);\n /* number of codes at each bit length for an optimal tree */\n\n //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */\n this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */\n zero(this.heap);\n this.heap_len = 0; /* number of elements in the heap */\n this.heap_max = 0; /* element of largest frequency */\n /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.\n * The same heap array is used to build all trees.\n */\n\n this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1];\n zero(this.depth);\n /* Depth of each subtree used as tie breaker for trees of equal frequency\n */\n\n this.sym_buf = 0; /* buffer for distances and literals/lengths */\n\n this.lit_bufsize = 0;\n /* Size of match buffer for literals/lengths. There are 4 reasons for\n * limiting lit_bufsize to 64K:\n * - frequencies can be kept in 16 bit counters\n * - if compression is not successful for the first block, all input\n * data is still in the window so we can still emit a stored block even\n * when input comes from standard input. (This can also be done for\n * all blocks if lit_bufsize is not greater than 32K.)\n * - if compression is not successful for a file smaller than 64K, we can\n * even emit a stored file instead of a stored block (saving 5 bytes).\n * This is applicable only for zip (not gzip or zlib).\n * - creating new Huffman trees less frequently may not provide fast\n * adaptation to changes in the input data statistics. (Take for\n * example a binary file with poorly compressible code followed by\n * a highly compressible string table.) Smaller buffer sizes give\n * fast adaptation but have of course the overhead of transmitting\n * trees more frequently.\n * - I can't count above 4\n */\n\n this.sym_next = 0; /* running index in sym_buf */\n this.sym_end = 0; /* symbol table full when sym_next reaches this */\n\n this.opt_len = 0; /* bit length of current block with optimal trees */\n this.static_len = 0; /* bit length of current block with static trees */\n this.matches = 0; /* number of string matches in current block */\n this.insert = 0; /* bytes at end of window left to insert */\n\n this.bi_buf = 0;\n /* Output buffer. bits are inserted starting at the bottom (least\n * significant bits).\n */\n this.bi_valid = 0;\n /* Number of valid bits in bi_buf. All bits above the last valid bit\n * are always zero.\n */\n\n // Used for window memory init. We safely ignore it for JS. That makes\n // sense only for pointers and memory check tools.\n //this.high_water = 0;\n /* High water mark offset in window for initialized bytes -- bytes above\n * this are set to zero in order to avoid memory check warnings when\n * longest match routines access bytes past the input. This is then\n * updated to the new high water mark.\n */\n}\n\n/* =========================================================================\n * Check for a valid deflate stream state. Return 0 if ok, 1 if not.\n */\nconst deflateStateCheck = strm => {\n if (!strm) {\n return 1;\n }\n const s = strm.state;\n if (!s || s.strm !== strm || s.status !== INIT_STATE &&\n //#ifdef GZIP\n s.status !== GZIP_STATE &&\n //#endif\n s.status !== EXTRA_STATE && s.status !== NAME_STATE && s.status !== COMMENT_STATE && s.status !== HCRC_STATE && s.status !== BUSY_STATE && s.status !== FINISH_STATE) {\n return 1;\n }\n return 0;\n};\nconst deflateResetKeep = strm => {\n if (deflateStateCheck(strm)) {\n return err(strm, Z_STREAM_ERROR$2);\n }\n strm.total_in = strm.total_out = 0;\n strm.data_type = Z_UNKNOWN;\n const s = strm.state;\n s.pending = 0;\n s.pending_out = 0;\n if (s.wrap < 0) {\n s.wrap = -s.wrap;\n /* was made negative by deflate(..., Z_FINISH); */\n }\n s.status =\n //#ifdef GZIP\n s.wrap === 2 ? GZIP_STATE :\n //#endif\n s.wrap ? INIT_STATE : BUSY_STATE;\n strm.adler = s.wrap === 2 ? 0 // crc32(0, Z_NULL, 0)\n : 1; // adler32(0, Z_NULL, 0)\n s.last_flush = -2;\n _tr_init(s);\n return Z_OK$3;\n};\nconst deflateReset = strm => {\n const ret = deflateResetKeep(strm);\n if (ret === Z_OK$3) {\n lm_init(strm.state);\n }\n return ret;\n};\nconst deflateSetHeader = (strm, head) => {\n if (deflateStateCheck(strm) || strm.state.wrap !== 2) {\n return Z_STREAM_ERROR$2;\n }\n strm.state.gzhead = head;\n return Z_OK$3;\n};\nconst deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {\n if (!strm) {\n // === Z_NULL\n return Z_STREAM_ERROR$2;\n }\n let wrap = 1;\n if (level === Z_DEFAULT_COMPRESSION$1) {\n level = 6;\n }\n if (windowBits < 0) {\n /* suppress zlib wrapper */\n wrap = 0;\n windowBits = -windowBits;\n } else if (windowBits > 15) {\n wrap = 2; /* write gzip wrapper instead */\n windowBits -= 16;\n }\n if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || windowBits === 8 && wrap !== 1) {\n return err(strm, Z_STREAM_ERROR$2);\n }\n if (windowBits === 8) {\n windowBits = 9;\n }\n /* until 256-byte window bug fixed */\n\n const s = new DeflateState();\n strm.state = s;\n s.strm = strm;\n s.status = INIT_STATE; /* to pass state test in deflateReset() */\n\n s.wrap = wrap;\n s.gzhead = null;\n s.w_bits = windowBits;\n s.w_size = 1 << s.w_bits;\n s.w_mask = s.w_size - 1;\n s.hash_bits = memLevel + 7;\n s.hash_size = 1 << s.hash_bits;\n s.hash_mask = s.hash_size - 1;\n s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);\n s.window = new Uint8Array(s.w_size * 2);\n s.head = new Uint16Array(s.hash_size);\n s.prev = new Uint16Array(s.w_size);\n\n // Don't need mem init magic for JS.\n //s.high_water = 0; /* nothing written to s->window yet */\n\n s.lit_bufsize = 1 << memLevel + 6; /* 16K elements by default */\n\n /* We overlay pending_buf and sym_buf. This works since the average size\n * for length/distance pairs over any compressed block is assured to be 31\n * bits or less.\n *\n * Analysis: The longest fixed codes are a length code of 8 bits plus 5\n * extra bits, for lengths 131 to 257. The longest fixed distance codes are\n * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest\n * possible fixed-codes length/distance pair is then 31 bits total.\n *\n * sym_buf starts one-fourth of the way into pending_buf. So there are\n * three bytes in sym_buf for every four bytes in pending_buf. Each symbol\n * in sym_buf is three bytes -- two for the distance and one for the\n * literal/length. As each symbol is consumed, the pointer to the next\n * sym_buf value to read moves forward three bytes. From that symbol, up to\n * 31 bits are written to pending_buf. The closest the written pending_buf\n * bits gets to the next sym_buf symbol to read is just before the last\n * code is written. At that time, 31*(n-2) bits have been written, just\n * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at\n * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1\n * symbols are written.) The closest the writing gets to what is unread is\n * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and\n * can range from 128 to 32768.\n *\n * Therefore, at a minimum, there are 142 bits of space between what is\n * written and what is read in the overlain buffers, so the symbols cannot\n * be overwritten by the compressed data. That space is actually 139 bits,\n * due to the three-bit fixed-code block header.\n *\n * That covers the case where either Z_FIXED is specified, forcing fixed\n * codes, or when the use of fixed codes is chosen, because that choice\n * results in a smaller compressed block than dynamic codes. That latter\n * condition then assures that the above analysis also covers all dynamic\n * blocks. A dynamic-code block will only be chosen to be emitted if it has\n * fewer bits than a fixed-code block would for the same set of symbols.\n * Therefore its average symbol length is assured to be less than 31. So\n * the compressed data for a dynamic block also cannot overwrite the\n * symbols from which it is being constructed.\n */\n\n s.pending_buf_size = s.lit_bufsize * 4;\n s.pending_buf = new Uint8Array(s.pending_buf_size);\n\n // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)\n //s->sym_buf = s->pending_buf + s->lit_bufsize;\n s.sym_buf = s.lit_bufsize;\n\n //s->sym_end = (s->lit_bufsize - 1) * 3;\n s.sym_end = (s.lit_bufsize - 1) * 3;\n /* We avoid equality with lit_bufsize*3 because of wraparound at 64K\n * on 16 bit machines and because stored blocks are restricted to\n * 64K-1 bytes.\n */\n\n s.level = level;\n s.strategy = strategy;\n s.method = method;\n return deflateReset(strm);\n};\nconst deflateInit = (strm, level) => {\n return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1);\n};\n\n/* ========================================================================= */\nconst deflate$2 = (strm, flush) => {\n if (deflateStateCheck(strm) || flush > Z_BLOCK$1 || flush < 0) {\n return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;\n }\n const s = strm.state;\n if (!strm.output || strm.avail_in !== 0 && !strm.input || s.status === FINISH_STATE && flush !== Z_FINISH$3) {\n return err(strm, strm.avail_out === 0 ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);\n }\n const old_flush = s.last_flush;\n s.last_flush = flush;\n\n /* Flush as much pending output as possible */\n if (s.pending !== 0) {\n flush_pending(strm);\n if (strm.avail_out === 0) {\n /* Since avail_out is 0, deflate will be called again with\n * more output space, but possibly with both pending and\n * avail_in equal to zero. There won't be anything to do,\n * but this is not an error situation so make sure we\n * return OK instead of BUF_ERROR at next call of deflate:\n */\n s.last_flush = -1;\n return Z_OK$3;\n }\n\n /* Make sure there is something to do and avoid duplicate consecutive\n * flushes. For repeated and useless calls with Z_FINISH, we keep\n * returning Z_STREAM_END instead of Z_BUF_ERROR.\n */\n } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) {\n return err(strm, Z_BUF_ERROR$1);\n }\n\n /* User must not provide more input after the first FINISH: */\n if (s.status === FINISH_STATE && strm.avail_in !== 0) {\n return err(strm, Z_BUF_ERROR$1);\n }\n\n /* Write the header */\n if (s.status === INIT_STATE && s.wrap === 0) {\n s.status = BUSY_STATE;\n }\n if (s.status === INIT_STATE) {\n /* zlib header */\n let header = Z_DEFLATED$2 + (s.w_bits - 8 << 4) << 8;\n let level_flags = -1;\n if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {\n level_flags = 0;\n } else if (s.level < 6) {\n level_flags = 1;\n } else if (s.level === 6) {\n level_flags = 2;\n } else {\n level_flags = 3;\n }\n header |= level_flags << 6;\n if (s.strstart !== 0) {\n header |= PRESET_DICT;\n }\n header += 31 - header % 31;\n putShortMSB(s, header);\n\n /* Save the adler32 of the preset dictionary: */\n if (s.strstart !== 0) {\n putShortMSB(s, strm.adler >>> 16);\n putShortMSB(s, strm.adler & 0xffff);\n }\n strm.adler = 1; // adler32(0L, Z_NULL, 0);\n s.status = BUSY_STATE;\n\n /* Compression must start with an empty pending buffer */\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n }\n //#ifdef GZIP\n if (s.status === GZIP_STATE) {\n /* gzip header */\n strm.adler = 0; //crc32(0L, Z_NULL, 0);\n put_byte(s, 31);\n put_byte(s, 139);\n put_byte(s, 8);\n if (!s.gzhead) {\n // s->gzhead == Z_NULL\n put_byte(s, 0);\n put_byte(s, 0);\n put_byte(s, 0);\n put_byte(s, 0);\n put_byte(s, 0);\n put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);\n put_byte(s, OS_CODE);\n s.status = BUSY_STATE;\n\n /* Compression must start with an empty pending buffer */\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n } else {\n put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16));\n put_byte(s, s.gzhead.time & 0xff);\n put_byte(s, s.gzhead.time >> 8 & 0xff);\n put_byte(s, s.gzhead.time >> 16 & 0xff);\n put_byte(s, s.gzhead.time >> 24 & 0xff);\n put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);\n put_byte(s, s.gzhead.os & 0xff);\n if (s.gzhead.extra && s.gzhead.extra.length) {\n put_byte(s, s.gzhead.extra.length & 0xff);\n put_byte(s, s.gzhead.extra.length >> 8 & 0xff);\n }\n if (s.gzhead.hcrc) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0);\n }\n s.gzindex = 0;\n s.status = EXTRA_STATE;\n }\n }\n if (s.status === EXTRA_STATE) {\n if (s.gzhead.extra /* != Z_NULL*/) {\n let beg = s.pending; /* start of bytes to update crc */\n let left = (s.gzhead.extra.length & 0xffff) - s.gzindex;\n while (s.pending + left > s.pending_buf_size) {\n let copy = s.pending_buf_size - s.pending;\n // zmemcpy(s.pending_buf + s.pending,\n // s.gzhead.extra + s.gzindex, copy);\n s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending);\n s.pending = s.pending_buf_size;\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n s.gzindex += copy;\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n beg = 0;\n left -= copy;\n }\n // JS specific: s.gzhead.extra may be TypedArray or Array for backward compatibility\n // TypedArray.slice and TypedArray.from don't exist in IE10-IE11\n let gzhead_extra = new Uint8Array(s.gzhead.extra);\n // zmemcpy(s->pending_buf + s->pending,\n // s->gzhead->extra + s->gzindex, left);\n s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);\n s.pending += left;\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n s.gzindex = 0;\n }\n s.status = NAME_STATE;\n }\n if (s.status === NAME_STATE) {\n if (s.gzhead.name /* != Z_NULL*/) {\n let beg = s.pending; /* start of bytes to update crc */\n let val;\n do {\n if (s.pending === s.pending_buf_size) {\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n beg = 0;\n }\n // JS specific: little magic to add zero terminator to end of string\n if (s.gzindex < s.gzhead.name.length) {\n val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;\n } else {\n val = 0;\n }\n put_byte(s, val);\n } while (val !== 0);\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n s.gzindex = 0;\n }\n s.status = COMMENT_STATE;\n }\n if (s.status === COMMENT_STATE) {\n if (s.gzhead.comment /* != Z_NULL*/) {\n let beg = s.pending; /* start of bytes to update crc */\n let val;\n do {\n if (s.pending === s.pending_buf_size) {\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n beg = 0;\n }\n // JS specific: little magic to add zero terminator to end of string\n if (s.gzindex < s.gzhead.comment.length) {\n val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;\n } else {\n val = 0;\n }\n put_byte(s, val);\n } while (val !== 0);\n //--- HCRC_UPDATE(beg) ---//\n if (s.gzhead.hcrc && s.pending > beg) {\n strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);\n }\n //---//\n }\n s.status = HCRC_STATE;\n }\n if (s.status === HCRC_STATE) {\n if (s.gzhead.hcrc) {\n if (s.pending + 2 > s.pending_buf_size) {\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n }\n put_byte(s, strm.adler & 0xff);\n put_byte(s, strm.adler >> 8 & 0xff);\n strm.adler = 0; //crc32(0L, Z_NULL, 0);\n }\n s.status = BUSY_STATE;\n\n /* Compression must start with an empty pending buffer */\n flush_pending(strm);\n if (s.pending !== 0) {\n s.last_flush = -1;\n return Z_OK$3;\n }\n }\n //#endif\n\n /* Start a new block or continue the current one.\n */\n if (strm.avail_in !== 0 || s.lookahead !== 0 || flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE) {\n let bstate = s.level === 0 ? deflate_stored(s, flush) : s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush);\n if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {\n s.status = FINISH_STATE;\n }\n if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {\n if (strm.avail_out === 0) {\n s.last_flush = -1;\n /* avoid BUF_ERROR next call, see above */\n }\n return Z_OK$3;\n /* If flush != Z_NO_FLUSH && avail_out == 0, the next call\n * of deflate should use the same flush parameter to make sure\n * that the flush is complete. So we don't have to output an\n * empty block here, this will be done at next call. This also\n * ensures that for a very small output buffer, we emit at most\n * one empty block.\n */\n }\n if (bstate === BS_BLOCK_DONE) {\n if (flush === Z_PARTIAL_FLUSH) {\n _tr_align(s);\n } else if (flush !== Z_BLOCK$1) {\n /* FULL_FLUSH or SYNC_FLUSH */\n\n _tr_stored_block(s, 0, 0, false);\n /* For a full flush, this empty block will be recognized\n * as a special marker by inflate_sync().\n */\n if (flush === Z_FULL_FLUSH$1) {\n /*** CLEAR_HASH(s); ***/ /* forget history */\n zero(s.head); // Fill with NIL (= 0);\n\n if (s.lookahead === 0) {\n s.strstart = 0;\n s.block_start = 0;\n s.insert = 0;\n }\n }\n }\n flush_pending(strm);\n if (strm.avail_out === 0) {\n s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */\n return Z_OK$3;\n }\n }\n }\n if (flush !== Z_FINISH$3) {\n return Z_OK$3;\n }\n if (s.wrap <= 0) {\n return Z_STREAM_END$3;\n }\n\n /* Write the trailer */\n if (s.wrap === 2) {\n put_byte(s, strm.adler & 0xff);\n put_byte(s, strm.adler >> 8 & 0xff);\n put_byte(s, strm.adler >> 16 & 0xff);\n put_byte(s, strm.adler >> 24 & 0xff);\n put_byte(s, strm.total_in & 0xff);\n put_byte(s, strm.total_in >> 8 & 0xff);\n put_byte(s, strm.total_in >> 16 & 0xff);\n put_byte(s, strm.total_in >> 24 & 0xff);\n } else {\n putShortMSB(s, strm.adler >>> 16);\n putShortMSB(s, strm.adler & 0xffff);\n }\n flush_pending(strm);\n /* If avail_out is zero, the application will call deflate again\n * to flush the rest.\n */\n if (s.wrap > 0) {\n s.wrap = -s.wrap;\n }\n /* write the trailer only once! */\n return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3;\n};\nconst deflateEnd = strm => {\n if (deflateStateCheck(strm)) {\n return Z_STREAM_ERROR$2;\n }\n const status = strm.state.status;\n strm.state = null;\n return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3;\n};\n\n/* =========================================================================\n * Initializes the compression dictionary from the given byte\n * sequence without producing any compressed output.\n */\nconst deflateSetDictionary = (strm, dictionary) => {\n let dictLength = dictionary.length;\n if (deflateStateCheck(strm)) {\n return Z_STREAM_ERROR$2;\n }\n const s = strm.state;\n const wrap = s.wrap;\n if (wrap === 2 || wrap === 1 && s.status !== INIT_STATE || s.lookahead) {\n return Z_STREAM_ERROR$2;\n }\n\n /* when using zlib wrappers, compute Adler-32 for provided dictionary */\n if (wrap === 1) {\n /* adler32(strm->adler, dictionary, dictLength); */\n strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0);\n }\n s.wrap = 0; /* avoid computing Adler-32 in read_buf */\n\n /* if dictionary would fill window, just replace the history */\n if (dictLength >= s.w_size) {\n if (wrap === 0) {\n /* already empty otherwise */\n /*** CLEAR_HASH(s); ***/\n zero(s.head); // Fill with NIL (= 0);\n s.strstart = 0;\n s.block_start = 0;\n s.insert = 0;\n }\n /* use the tail */\n // dictionary = dictionary.slice(dictLength - s.w_size);\n let tmpDict = new Uint8Array(s.w_size);\n tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);\n dictionary = tmpDict;\n dictLength = s.w_size;\n }\n /* insert dictionary into window and hash */\n const avail = strm.avail_in;\n const next = strm.next_in;\n const input = strm.input;\n strm.avail_in = dictLength;\n strm.next_in = 0;\n strm.input = dictionary;\n fill_window(s);\n while (s.lookahead >= MIN_MATCH) {\n let str = s.strstart;\n let n = s.lookahead - (MIN_MATCH - 1);\n do {\n /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);\n s.prev[str & s.w_mask] = s.head[s.ins_h];\n s.head[s.ins_h] = str;\n str++;\n } while (--n);\n s.strstart = str;\n s.lookahead = MIN_MATCH - 1;\n fill_window(s);\n }\n s.strstart += s.lookahead;\n s.block_start = s.strstart;\n s.insert = s.lookahead;\n s.lookahead = 0;\n s.match_length = s.prev_length = MIN_MATCH - 1;\n s.match_available = 0;\n strm.next_in = next;\n strm.input = input;\n strm.avail_in = avail;\n s.wrap = wrap;\n return Z_OK$3;\n};\nvar deflateInit_1 = deflateInit;\nvar deflateInit2_1 = deflateInit2;\nvar deflateReset_1 = deflateReset;\nvar deflateResetKeep_1 = deflateResetKeep;\nvar deflateSetHeader_1 = deflateSetHeader;\nvar deflate_2$1 = deflate$2;\nvar deflateEnd_1 = deflateEnd;\nvar deflateSetDictionary_1 = deflateSetDictionary;\nvar deflateInfo = 'pako deflate (from Nodeca project)';\n\n/* Not implemented\nmodule.exports.deflateBound = deflateBound;\nmodule.exports.deflateCopy = deflateCopy;\nmodule.exports.deflateGetDictionary = deflateGetDictionary;\nmodule.exports.deflateParams = deflateParams;\nmodule.exports.deflatePending = deflatePending;\nmodule.exports.deflatePrime = deflatePrime;\nmodule.exports.deflateTune = deflateTune;\n*/\n\nvar deflate_1$2 = {\n deflateInit: deflateInit_1,\n deflateInit2: deflateInit2_1,\n deflateReset: deflateReset_1,\n deflateResetKeep: deflateResetKeep_1,\n deflateSetHeader: deflateSetHeader_1,\n deflate: deflate_2$1,\n deflateEnd: deflateEnd_1,\n deflateSetDictionary: deflateSetDictionary_1,\n deflateInfo: deflateInfo\n};\nconst _has = (obj, key) => {\n return Object.prototype.hasOwnProperty.call(obj, key);\n};\nvar assign = function (obj /*from1, from2, from3, ...*/) {\n const sources = Array.prototype.slice.call(arguments, 1);\n while (sources.length) {\n const source = sources.shift();\n if (!source) {\n continue;\n }\n if (typeof source !== 'object') {\n throw new TypeError(source + 'must be non-object');\n }\n for (const p in source) {\n if (_has(source, p)) {\n obj[p] = source[p];\n }\n }\n }\n return obj;\n};\n\n// Join array of chunks to single array.\nvar flattenChunks = chunks => {\n // calculate data length\n let len = 0;\n for (let i = 0, l = chunks.length; i < l; i++) {\n len += chunks[i].length;\n }\n\n // join chunks\n const result = new Uint8Array(len);\n for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {\n let chunk = chunks[i];\n result.set(chunk, pos);\n pos += chunk.length;\n }\n return result;\n};\nvar common = {\n assign: assign,\n flattenChunks: flattenChunks\n};\n\n// String encode/decode helpers\n\n// Quick check if we can use fast array to bin string conversion\n//\n// - apply(Array) can fail on Android 2.2\n// - apply(Uint8Array) can fail on iOS 5.1 Safari\n//\nlet STR_APPLY_UIA_OK = true;\ntry {\n String.fromCharCode.apply(null, new Uint8Array(1));\n} catch (__) {\n STR_APPLY_UIA_OK = false;\n}\n\n// Table with utf8 lengths (calculated by first byte of sequence)\n// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,\n// because max possible codepoint is 0x10ffff\nconst _utf8len = new Uint8Array(256);\nfor (let q = 0; q < 256; q++) {\n _utf8len[q] = q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1;\n}\n_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start\n\n// convert string to array (typed, when possible)\nvar string2buf = str => {\n if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) {\n return new TextEncoder().encode(str);\n }\n let buf,\n c,\n c2,\n m_pos,\n i,\n str_len = str.length,\n buf_len = 0;\n\n // count binary size\n for (m_pos = 0; m_pos < str_len; m_pos++) {\n c = str.charCodeAt(m_pos);\n if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {\n c2 = str.charCodeAt(m_pos + 1);\n if ((c2 & 0xfc00) === 0xdc00) {\n c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);\n m_pos++;\n }\n }\n buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;\n }\n\n // allocate buffer\n buf = new Uint8Array(buf_len);\n\n // convert\n for (i = 0, m_pos = 0; i < buf_len; m_pos++) {\n c = str.charCodeAt(m_pos);\n if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {\n c2 = str.charCodeAt(m_pos + 1);\n if ((c2 & 0xfc00) === 0xdc00) {\n c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);\n m_pos++;\n }\n }\n if (c < 0x80) {\n /* one byte */\n buf[i++] = c;\n } else if (c < 0x800) {\n /* two bytes */\n buf[i++] = 0xC0 | c >>> 6;\n buf[i++] = 0x80 | c & 0x3f;\n } else if (c < 0x10000) {\n /* three bytes */\n buf[i++] = 0xE0 | c >>> 12;\n buf[i++] = 0x80 | c >>> 6 & 0x3f;\n buf[i++] = 0x80 | c & 0x3f;\n } else {\n /* four bytes */\n buf[i++] = 0xf0 | c >>> 18;\n buf[i++] = 0x80 | c >>> 12 & 0x3f;\n buf[i++] = 0x80 | c >>> 6 & 0x3f;\n buf[i++] = 0x80 | c & 0x3f;\n }\n }\n return buf;\n};\n\n// Helper\nconst buf2binstring = (buf, len) => {\n // On Chrome, the arguments in a function call that are allowed is `65534`.\n // If the length of the buffer is smaller than that, we can use this optimization,\n // otherwise we will take a slower path.\n if (len < 65534) {\n if (buf.subarray && STR_APPLY_UIA_OK) {\n return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len));\n }\n }\n let result = '';\n for (let i = 0; i < len; i++) {\n result += String.fromCharCode(buf[i]);\n }\n return result;\n};\n\n// convert array to string\nvar buf2string = (buf, max) => {\n const len = max || buf.length;\n if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) {\n return new TextDecoder().decode(buf.subarray(0, max));\n }\n let i, out;\n\n // Reserve max possible length (2 words per char)\n // NB: by unknown reasons, Array is significantly faster for\n // String.fromCharCode.apply than Uint16Array.\n const utf16buf = new Array(len * 2);\n for (out = 0, i = 0; i < len;) {\n let c = buf[i++];\n // quick process ascii\n if (c < 0x80) {\n utf16buf[out++] = c;\n continue;\n }\n let c_len = _utf8len[c];\n // skip 5 & 6 byte codes\n if (c_len > 4) {\n utf16buf[out++] = 0xfffd;\n i += c_len - 1;\n continue;\n }\n\n // apply mask on first byte\n c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;\n // join the rest\n while (c_len > 1 && i < len) {\n c = c << 6 | buf[i++] & 0x3f;\n c_len--;\n }\n\n // terminated by end of string?\n if (c_len > 1) {\n utf16buf[out++] = 0xfffd;\n continue;\n }\n if (c < 0x10000) {\n utf16buf[out++] = c;\n } else {\n c -= 0x10000;\n utf16buf[out++] = 0xd800 | c >> 10 & 0x3ff;\n utf16buf[out++] = 0xdc00 | c & 0x3ff;\n }\n }\n return buf2binstring(utf16buf, out);\n};\n\n// Calculate max possible position in utf8 buffer,\n// that will not break sequence. If that's not possible\n// - (very small limits) return max size as is.\n//\n// buf[] - utf8 bytes array\n// max - length limit (mandatory);\nvar utf8border = (buf, max) => {\n max = max || buf.length;\n if (max > buf.length) {\n max = buf.length;\n }\n\n // go back from last position, until start of sequence found\n let pos = max - 1;\n while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) {\n pos--;\n }\n\n // Very small and broken sequence,\n // return max, because we should return something anyway.\n if (pos < 0) {\n return max;\n }\n\n // If we came to start of buffer - that means buffer is too small,\n // return max too.\n if (pos === 0) {\n return max;\n }\n return pos + _utf8len[buf[pos]] > max ? pos : max;\n};\nvar strings = {\n string2buf: string2buf,\n buf2string: buf2string,\n utf8border: utf8border\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nfunction ZStream() {\n /* next input byte */\n this.input = null; // JS specific, because we have no pointers\n this.next_in = 0;\n /* number of bytes available at input */\n this.avail_in = 0;\n /* total number of input bytes read so far */\n this.total_in = 0;\n /* next output byte should be put there */\n this.output = null; // JS specific, because we have no pointers\n this.next_out = 0;\n /* remaining free space at output */\n this.avail_out = 0;\n /* total number of bytes output so far */\n this.total_out = 0;\n /* last error message, NULL if no error */\n this.msg = '' /*Z_NULL*/;\n /* not visible by applications */\n this.state = null;\n /* best guess about the data type: binary or text */\n this.data_type = 2 /*Z_UNKNOWN*/;\n /* adler32 value of the uncompressed data */\n this.adler = 0;\n}\nvar zstream = ZStream;\nconst toString$1 = Object.prototype.toString;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\nconst {\n Z_NO_FLUSH: Z_NO_FLUSH$1,\n Z_SYNC_FLUSH,\n Z_FULL_FLUSH,\n Z_FINISH: Z_FINISH$2,\n Z_OK: Z_OK$2,\n Z_STREAM_END: Z_STREAM_END$2,\n Z_DEFAULT_COMPRESSION,\n Z_DEFAULT_STRATEGY,\n Z_DEFLATED: Z_DEFLATED$1\n} = constants$2;\n\n/* ===========================================================================*/\n\n/**\n * class Deflate\n *\n * Generic JS-style wrapper for zlib calls. If you don't need\n * streaming behaviour - use more simple functions: [[deflate]],\n * [[deflateRaw]] and [[gzip]].\n **/\n\n/* internal\n * Deflate.chunks -> Array\n *\n * Chunks of output data, if [[Deflate#onData]] not overridden.\n **/\n\n/**\n * Deflate.result -> Uint8Array\n *\n * Compressed result, generated by default [[Deflate#onData]]\n * and [[Deflate#onEnd]] handlers. Filled after you push last chunk\n * (call [[Deflate#push]] with `Z_FINISH` / `true` param).\n **/\n\n/**\n * Deflate.err -> Number\n *\n * Error code after deflate finished. 0 (Z_OK) on success.\n * You will not need it in real life, because deflate errors\n * are possible only on wrong options or bad `onData` / `onEnd`\n * custom handlers.\n **/\n\n/**\n * Deflate.msg -> String\n *\n * Error message, if [[Deflate.err]] != 0\n **/\n\n/**\n * new Deflate(options)\n * - options (Object): zlib deflate options.\n *\n * Creates new deflator instance with specified params. Throws exception\n * on bad params. Supported options:\n *\n * - `level`\n * - `windowBits`\n * - `memLevel`\n * - `strategy`\n * - `dictionary`\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Additional options, for internal needs:\n *\n * - `chunkSize` - size of generated data chunks (16K by default)\n * - `raw` (Boolean) - do raw deflate\n * - `gzip` (Boolean) - create gzip wrapper\n * - `header` (Object) - custom header for gzip\n * - `text` (Boolean) - true if compressed data believed to be text\n * - `time` (Number) - modification time, unix timestamp\n * - `os` (Number) - operation system code\n * - `extra` (Array) - array of bytes with extra data (max 65536)\n * - `name` (String) - file name (binary string)\n * - `comment` (String) - comment (binary string)\n * - `hcrc` (Boolean) - true if header crc should be added\n *\n * ##### Example:\n *\n * ```javascript\n * const pako = require('pako')\n * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])\n * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);\n *\n * const deflate = new pako.Deflate({ level: 3});\n *\n * deflate.push(chunk1, false);\n * deflate.push(chunk2, true); // true -> last chunk\n *\n * if (deflate.err) { throw new Error(deflate.err); }\n *\n * console.log(deflate.result);\n * ```\n **/\nfunction Deflate$1(options) {\n this.options = common.assign({\n level: Z_DEFAULT_COMPRESSION,\n method: Z_DEFLATED$1,\n chunkSize: 16384,\n windowBits: 15,\n memLevel: 8,\n strategy: Z_DEFAULT_STRATEGY\n }, options || {});\n let opt = this.options;\n if (opt.raw && opt.windowBits > 0) {\n opt.windowBits = -opt.windowBits;\n } else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) {\n opt.windowBits += 16;\n }\n this.err = 0; // error code, if happens (0 = Z_OK)\n this.msg = ''; // error message\n this.ended = false; // used to avoid multiple onEnd() calls\n this.chunks = []; // chunks of compressed data\n\n this.strm = new zstream();\n this.strm.avail_out = 0;\n let status = deflate_1$2.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy);\n if (status !== Z_OK$2) {\n throw new Error(messages[status]);\n }\n if (opt.header) {\n deflate_1$2.deflateSetHeader(this.strm, opt.header);\n }\n if (opt.dictionary) {\n let dict;\n // Convert data if needed\n if (typeof opt.dictionary === 'string') {\n // If we need to compress text, change encoding to utf8.\n dict = strings.string2buf(opt.dictionary);\n } else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') {\n dict = new Uint8Array(opt.dictionary);\n } else {\n dict = opt.dictionary;\n }\n status = deflate_1$2.deflateSetDictionary(this.strm, dict);\n if (status !== Z_OK$2) {\n throw new Error(messages[status]);\n }\n this._dict_set = true;\n }\n}\n\n/**\n * Deflate#push(data[, flush_mode]) -> Boolean\n * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be\n * converted to utf8 byte sequence.\n * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.\n * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.\n *\n * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with\n * new compressed chunks. Returns `true` on success. The last data block must\n * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending\n * buffers and call [[Deflate#onEnd]].\n *\n * On fail call [[Deflate#onEnd]] with error code and return false.\n *\n * ##### Example\n *\n * ```javascript\n * push(chunk, false); // push one of data chunks\n * ...\n * push(chunk, true); // push last chunk\n * ```\n **/\nDeflate$1.prototype.push = function (data, flush_mode) {\n const strm = this.strm;\n const chunkSize = this.options.chunkSize;\n let status, _flush_mode;\n if (this.ended) {\n return false;\n }\n if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1;\n\n // Convert data if needed\n if (typeof data === 'string') {\n // If we need to compress text, change encoding to utf8.\n strm.input = strings.string2buf(data);\n } else if (toString$1.call(data) === '[object ArrayBuffer]') {\n strm.input = new Uint8Array(data);\n } else {\n strm.input = data;\n }\n strm.next_in = 0;\n strm.avail_in = strm.input.length;\n for (;;) {\n if (strm.avail_out === 0) {\n strm.output = new Uint8Array(chunkSize);\n strm.next_out = 0;\n strm.avail_out = chunkSize;\n }\n\n // Make sure avail_out > 6 to avoid repeating markers\n if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) {\n this.onData(strm.output.subarray(0, strm.next_out));\n strm.avail_out = 0;\n continue;\n }\n status = deflate_1$2.deflate(strm, _flush_mode);\n\n // Ended => flush and finish\n if (status === Z_STREAM_END$2) {\n if (strm.next_out > 0) {\n this.onData(strm.output.subarray(0, strm.next_out));\n }\n status = deflate_1$2.deflateEnd(this.strm);\n this.onEnd(status);\n this.ended = true;\n return status === Z_OK$2;\n }\n\n // Flush if out buffer full\n if (strm.avail_out === 0) {\n this.onData(strm.output);\n continue;\n }\n\n // Flush if requested and has data\n if (_flush_mode > 0 && strm.next_out > 0) {\n this.onData(strm.output.subarray(0, strm.next_out));\n strm.avail_out = 0;\n continue;\n }\n if (strm.avail_in === 0) break;\n }\n return true;\n};\n\n/**\n * Deflate#onData(chunk) -> Void\n * - chunk (Uint8Array): output data.\n *\n * By default, stores data blocks in `chunks[]` property and glue\n * those in `onEnd`. Override this handler, if you need another behaviour.\n **/\nDeflate$1.prototype.onData = function (chunk) {\n this.chunks.push(chunk);\n};\n\n/**\n * Deflate#onEnd(status) -> Void\n * - status (Number): deflate status. 0 (Z_OK) on success,\n * other if not.\n *\n * Called once after you tell deflate that the input stream is\n * complete (Z_FINISH). By default - join collected chunks,\n * free memory and fill `results` / `err` properties.\n **/\nDeflate$1.prototype.onEnd = function (status) {\n // On success - join\n if (status === Z_OK$2) {\n this.result = common.flattenChunks(this.chunks);\n }\n this.chunks = [];\n this.err = status;\n this.msg = this.strm.msg;\n};\n\n/**\n * deflate(data[, options]) -> Uint8Array\n * - data (Uint8Array|ArrayBuffer|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * Compress `data` with deflate algorithm and `options`.\n *\n * Supported options are:\n *\n * - level\n * - windowBits\n * - memLevel\n * - strategy\n * - dictionary\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Sugar (options):\n *\n * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify\n * negative windowBits implicitly.\n *\n * ##### Example:\n *\n * ```javascript\n * const pako = require('pako')\n * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]);\n *\n * console.log(pako.deflate(data));\n * ```\n **/\nfunction deflate$1(input, options) {\n const deflator = new Deflate$1(options);\n deflator.push(input, true);\n\n // That will never happens, if you don't cheat with options :)\n if (deflator.err) {\n throw deflator.msg || messages[deflator.err];\n }\n return deflator.result;\n}\n\n/**\n * deflateRaw(data[, options]) -> Uint8Array\n * - data (Uint8Array|ArrayBuffer|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * The same as [[deflate]], but creates raw data, without wrapper\n * (header and adler32 crc).\n **/\nfunction deflateRaw$1(input, options) {\n options = options || {};\n options.raw = true;\n return deflate$1(input, options);\n}\n\n/**\n * gzip(data[, options]) -> Uint8Array\n * - data (Uint8Array|ArrayBuffer|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * The same as [[deflate]], but create gzip wrapper instead of\n * deflate one.\n **/\nfunction gzip$1(input, options) {\n options = options || {};\n options.gzip = true;\n return deflate$1(input, options);\n}\nvar Deflate_1$1 = Deflate$1;\nvar deflate_2 = deflate$1;\nvar deflateRaw_1$1 = deflateRaw$1;\nvar gzip_1$1 = gzip$1;\nvar constants$1 = constants$2;\nvar deflate_1$1 = {\n Deflate: Deflate_1$1,\n deflate: deflate_2,\n deflateRaw: deflateRaw_1$1,\n gzip: gzip_1$1,\n constants: constants$1\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n// See state defs from inflate.js\nconst BAD$1 = 16209; /* got a data error -- remain here until reset */\nconst TYPE$1 = 16191; /* i: waiting for type bits, including last-flag bit */\n\n/*\n Decode literal, length, and distance codes and write out the resulting\n literal and match bytes until either not enough input or output is\n available, an end-of-block is encountered, or a data error is encountered.\n When large enough input and output buffers are supplied to inflate(), for\n example, a 16K input buffer and a 64K output buffer, more than 95% of the\n inflate execution time is spent in this routine.\n\n Entry assumptions:\n\n state.mode === LEN\n strm.avail_in >= 6\n strm.avail_out >= 258\n start >= strm.avail_out\n state.bits < 8\n\n On return, state.mode is one of:\n\n LEN -- ran out of enough output space or enough available input\n TYPE -- reached end of block code, inflate() to interpret next block\n BAD -- error in block data\n\n Notes:\n\n - The maximum input bits used by a length/distance pair is 15 bits for the\n length code, 5 bits for the length extra, 15 bits for the distance code,\n and 13 bits for the distance extra. This totals 48 bits, or six bytes.\n Therefore if strm.avail_in >= 6, then there is enough input to avoid\n checking for available input while decoding.\n\n - The maximum bytes that a single length/distance pair can output is 258\n bytes, which is the maximum length that can be coded. inflate_fast()\n requires strm.avail_out >= 258 for each loop to avoid checking for\n output space.\n */\nvar inffast = function inflate_fast(strm, start) {\n let _in; /* local strm.input */\n let last; /* have enough input while in < last */\n let _out; /* local strm.output */\n let beg; /* inflate()'s initial strm.output */\n let end; /* while out < end, enough space available */\n //#ifdef INFLATE_STRICT\n let dmax; /* maximum distance from zlib header */\n //#endif\n let wsize; /* window size or zero if not using window */\n let whave; /* valid bytes in the window */\n let wnext; /* window write index */\n // Use `s_window` instead `window`, avoid conflict with instrumentation tools\n let s_window; /* allocated sliding window, if wsize != 0 */\n let hold; /* local strm.hold */\n let bits; /* local strm.bits */\n let lcode; /* local strm.lencode */\n let dcode; /* local strm.distcode */\n let lmask; /* mask for first level of length codes */\n let dmask; /* mask for first level of distance codes */\n let here; /* retrieved table entry */\n let op; /* code bits, operation, extra bits, or */\n /* window position, window bytes to copy */\n let len; /* match length, unused bytes */\n let dist; /* match distance */\n let from; /* where to copy match from */\n let from_source;\n let input, output; // JS specific, because we have no pointers\n\n /* copy state to local variables */\n const state = strm.state;\n //here = state.here;\n _in = strm.next_in;\n input = strm.input;\n last = _in + (strm.avail_in - 5);\n _out = strm.next_out;\n output = strm.output;\n beg = _out - (start - strm.avail_out);\n end = _out + (strm.avail_out - 257);\n //#ifdef INFLATE_STRICT\n dmax = state.dmax;\n //#endif\n wsize = state.wsize;\n whave = state.whave;\n wnext = state.wnext;\n s_window = state.window;\n hold = state.hold;\n bits = state.bits;\n lcode = state.lencode;\n dcode = state.distcode;\n lmask = (1 << state.lenbits) - 1;\n dmask = (1 << state.distbits) - 1;\n\n /* decode literals and length/distances until end-of-block or not enough\n input data or output space */\n\n top: do {\n if (bits < 15) {\n hold += input[_in++] << bits;\n bits += 8;\n hold += input[_in++] << bits;\n bits += 8;\n }\n here = lcode[hold & lmask];\n dolen: for (;;) {\n // Goto emulation\n op = here >>> 24 /*here.bits*/;\n hold >>>= op;\n bits -= op;\n op = here >>> 16 & 0xff /*here.op*/;\n if (op === 0) {\n /* literal */\n //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n // \"inflate: literal '%c'\\n\" :\n // \"inflate: literal 0x%02x\\n\", here.val));\n output[_out++] = here & 0xffff /*here.val*/;\n } else if (op & 16) {\n /* length base */\n len = here & 0xffff /*here.val*/;\n op &= 15; /* number of extra bits */\n if (op) {\n if (bits < op) {\n hold += input[_in++] << bits;\n bits += 8;\n }\n len += hold & (1 << op) - 1;\n hold >>>= op;\n bits -= op;\n }\n //Tracevv((stderr, \"inflate: length %u\\n\", len));\n if (bits < 15) {\n hold += input[_in++] << bits;\n bits += 8;\n hold += input[_in++] << bits;\n bits += 8;\n }\n here = dcode[hold & dmask];\n dodist: for (;;) {\n // goto emulation\n op = here >>> 24 /*here.bits*/;\n hold >>>= op;\n bits -= op;\n op = here >>> 16 & 0xff /*here.op*/;\n if (op & 16) {\n /* distance base */\n dist = here & 0xffff /*here.val*/;\n op &= 15; /* number of extra bits */\n if (bits < op) {\n hold += input[_in++] << bits;\n bits += 8;\n if (bits < op) {\n hold += input[_in++] << bits;\n bits += 8;\n }\n }\n dist += hold & (1 << op) - 1;\n //#ifdef INFLATE_STRICT\n if (dist > dmax) {\n strm.msg = 'invalid distance too far back';\n state.mode = BAD$1;\n break top;\n }\n //#endif\n hold >>>= op;\n bits -= op;\n //Tracevv((stderr, \"inflate: distance %u\\n\", dist));\n op = _out - beg; /* max distance in output */\n if (dist > op) {\n /* see if copy from window */\n op = dist - op; /* distance back in window */\n if (op > whave) {\n if (state.sane) {\n strm.msg = 'invalid distance too far back';\n state.mode = BAD$1;\n break top;\n }\n\n // (!) This block is disabled in zlib defaults,\n // don't enable it for binary compatibility\n //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n // if (len <= op - whave) {\n // do {\n // output[_out++] = 0;\n // } while (--len);\n // continue top;\n // }\n // len -= op - whave;\n // do {\n // output[_out++] = 0;\n // } while (--op > whave);\n // if (op === 0) {\n // from = _out - dist;\n // do {\n // output[_out++] = output[from++];\n // } while (--len);\n // continue top;\n // }\n //#endif\n }\n from = 0; // window index\n from_source = s_window;\n if (wnext === 0) {\n /* very common case */\n from += wsize - op;\n if (op < len) {\n /* some from window */\n len -= op;\n do {\n output[_out++] = s_window[from++];\n } while (--op);\n from = _out - dist; /* rest from output */\n from_source = output;\n }\n } else if (wnext < op) {\n /* wrap around window */\n from += wsize + wnext - op;\n op -= wnext;\n if (op < len) {\n /* some from end of window */\n len -= op;\n do {\n output[_out++] = s_window[from++];\n } while (--op);\n from = 0;\n if (wnext < len) {\n /* some from start of window */\n op = wnext;\n len -= op;\n do {\n output[_out++] = s_window[from++];\n } while (--op);\n from = _out - dist; /* rest from output */\n from_source = output;\n }\n }\n } else {\n /* contiguous in window */\n from += wnext - op;\n if (op < len) {\n /* some from window */\n len -= op;\n do {\n output[_out++] = s_window[from++];\n } while (--op);\n from = _out - dist; /* rest from output */\n from_source = output;\n }\n }\n while (len > 2) {\n output[_out++] = from_source[from++];\n output[_out++] = from_source[from++];\n output[_out++] = from_source[from++];\n len -= 3;\n }\n if (len) {\n output[_out++] = from_source[from++];\n if (len > 1) {\n output[_out++] = from_source[from++];\n }\n }\n } else {\n from = _out - dist; /* copy direct from output */\n do {\n /* minimum length is three */\n output[_out++] = output[from++];\n output[_out++] = output[from++];\n output[_out++] = output[from++];\n len -= 3;\n } while (len > 2);\n if (len) {\n output[_out++] = output[from++];\n if (len > 1) {\n output[_out++] = output[from++];\n }\n }\n }\n } else if ((op & 64) === 0) {\n /* 2nd level distance code */\n here = dcode[(here & 0xffff /*here.val*/) + (hold & (1 << op) - 1)];\n continue dodist;\n } else {\n strm.msg = 'invalid distance code';\n state.mode = BAD$1;\n break top;\n }\n break; // need to emulate goto via \"continue\"\n }\n } else if ((op & 64) === 0) {\n /* 2nd level length code */\n here = lcode[(here & 0xffff /*here.val*/) + (hold & (1 << op) - 1)];\n continue dolen;\n } else if (op & 32) {\n /* end-of-block */\n //Tracevv((stderr, \"inflate: end of block\\n\"));\n state.mode = TYPE$1;\n break top;\n } else {\n strm.msg = 'invalid literal/length code';\n state.mode = BAD$1;\n break top;\n }\n break; // need to emulate goto via \"continue\"\n }\n } while (_in < last && _out < end);\n\n /* return unused bytes (on entry, bits < 8, so in won't go too far back) */\n len = bits >> 3;\n _in -= len;\n bits -= len << 3;\n hold &= (1 << bits) - 1;\n\n /* update state and return */\n strm.next_in = _in;\n strm.next_out = _out;\n strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);\n strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);\n state.hold = hold;\n state.bits = bits;\n return;\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nconst MAXBITS = 15;\nconst ENOUGH_LENS$1 = 852;\nconst ENOUGH_DISTS$1 = 592;\n//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);\n\nconst CODES$1 = 0;\nconst LENS$1 = 1;\nconst DISTS$1 = 2;\nconst lbase = new Uint16Array([/* Length codes 257..285 base */\n3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0]);\nconst lext = new Uint8Array([/* Length codes 257..285 extra */\n16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78]);\nconst dbase = new Uint16Array([/* Distance codes 0..29 base */\n1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0]);\nconst dext = new Uint8Array([/* Distance codes 0..29 extra */\n16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64]);\nconst inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => {\n const bits = opts.bits;\n //here = opts.here; /* table entry for duplication */\n\n let len = 0; /* a code's length in bits */\n let sym = 0; /* index of code symbols */\n let min = 0,\n max = 0; /* minimum and maximum code lengths */\n let root = 0; /* number of index bits for root table */\n let curr = 0; /* number of index bits for current table */\n let drop = 0; /* code bits to drop for sub-table */\n let left = 0; /* number of prefix codes available */\n let used = 0; /* code entries in table used */\n let huff = 0; /* Huffman code */\n let incr; /* for incrementing code, index */\n let fill; /* index for replicating entries */\n let low; /* low bits for current root entry */\n let mask; /* mask for low root bits */\n let next; /* next available space in table */\n let base = null; /* base value table to use */\n // let shoextra; /* extra bits table to use */\n let match; /* use base and extra for symbol >= match */\n const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */\n const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */\n let extra = null;\n let here_bits, here_op, here_val;\n\n /*\n Process a set of code lengths to create a canonical Huffman code. The\n code lengths are lens[0..codes-1]. Each length corresponds to the\n symbols 0..codes-1. The Huffman code is generated by first sorting the\n symbols by length from short to long, and retaining the symbol order\n for codes with equal lengths. Then the code starts with all zero bits\n for the first code of the shortest length, and the codes are integer\n increments for the same length, and zeros are appended as the length\n increases. For the deflate format, these bits are stored backwards\n from their more natural integer increment ordering, and so when the\n decoding tables are built in the large loop below, the integer codes\n are incremented backwards.\n This routine assumes, but does not check, that all of the entries in\n lens[] are in the range 0..MAXBITS. The caller must assure this.\n 1..MAXBITS is interpreted as that code length. zero means that that\n symbol does not occur in this code.\n The codes are sorted by computing a count of codes for each length,\n creating from that a table of starting indices for each length in the\n sorted table, and then entering the symbols in order in the sorted\n table. The sorted table is work[], with that space being provided by\n the caller.\n The length counts are used for other purposes as well, i.e. finding\n the minimum and maximum length codes, determining if there are any\n codes at all, checking for a valid set of lengths, and looking ahead\n at length counts to determine sub-table sizes when building the\n decoding tables.\n */\n\n /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */\n for (len = 0; len <= MAXBITS; len++) {\n count[len] = 0;\n }\n for (sym = 0; sym < codes; sym++) {\n count[lens[lens_index + sym]]++;\n }\n\n /* bound code lengths, force root to be within code lengths */\n root = bits;\n for (max = MAXBITS; max >= 1; max--) {\n if (count[max] !== 0) {\n break;\n }\n }\n if (root > max) {\n root = max;\n }\n if (max === 0) {\n /* no symbols to code at all */\n //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */\n //table.bits[opts.table_index] = 1; //here.bits = (var char)1;\n //table.val[opts.table_index++] = 0; //here.val = (var short)0;\n table[table_index++] = 1 << 24 | 64 << 16 | 0;\n\n //table.op[opts.table_index] = 64;\n //table.bits[opts.table_index] = 1;\n //table.val[opts.table_index++] = 0;\n table[table_index++] = 1 << 24 | 64 << 16 | 0;\n opts.bits = 1;\n return 0; /* no symbols, but wait for decoding to report error */\n }\n for (min = 1; min < max; min++) {\n if (count[min] !== 0) {\n break;\n }\n }\n if (root < min) {\n root = min;\n }\n\n /* check for an over-subscribed or incomplete set of lengths */\n left = 1;\n for (len = 1; len <= MAXBITS; len++) {\n left <<= 1;\n left -= count[len];\n if (left < 0) {\n return -1;\n } /* over-subscribed */\n }\n if (left > 0 && (type === CODES$1 || max !== 1)) {\n return -1; /* incomplete set */\n }\n\n /* generate offsets into symbol table for each length for sorting */\n offs[1] = 0;\n for (len = 1; len < MAXBITS; len++) {\n offs[len + 1] = offs[len] + count[len];\n }\n\n /* sort symbols by length, by symbol order within each length */\n for (sym = 0; sym < codes; sym++) {\n if (lens[lens_index + sym] !== 0) {\n work[offs[lens[lens_index + sym]]++] = sym;\n }\n }\n\n /*\n Create and fill in decoding tables. In this loop, the table being\n filled is at next and has curr index bits. The code being used is huff\n with length len. That code is converted to an index by dropping drop\n bits off of the bottom. For codes where len is less than drop + curr,\n those top drop + curr - len bits are incremented through all values to\n fill the table with replicated entries.\n root is the number of index bits for the root table. When len exceeds\n root, sub-tables are created pointed to by the root entry with an index\n of the low root bits of huff. This is saved in low to check for when a\n new sub-table should be started. drop is zero when the root table is\n being filled, and drop is root when sub-tables are being filled.\n When a new sub-table is needed, it is necessary to look ahead in the\n code lengths to determine what size sub-table is needed. The length\n counts are used for this, and so count[] is decremented as codes are\n entered in the tables.\n used keeps track of how many table entries have been allocated from the\n provided *table space. It is checked for LENS and DIST tables against\n the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in\n the initial root table size constants. See the comments in inftrees.h\n for more information.\n sym increments through all symbols, and the loop terminates when\n all codes of length max, i.e. all codes, have been processed. This\n routine permits incomplete codes, so another loop after this one fills\n in the rest of the decoding tables with invalid code markers.\n */\n\n /* set up for code type */\n // poor man optimization - use if-else instead of switch,\n // to avoid deopts in old v8\n if (type === CODES$1) {\n base = extra = work; /* dummy value--not used */\n match = 20;\n } else if (type === LENS$1) {\n base = lbase;\n extra = lext;\n match = 257;\n } else {\n /* DISTS */\n base = dbase;\n extra = dext;\n match = 0;\n }\n\n /* initialize opts for loop */\n huff = 0; /* starting code */\n sym = 0; /* starting code symbol */\n len = min; /* starting code length */\n next = table_index; /* current table to fill in */\n curr = root; /* current table index bits */\n drop = 0; /* current bits to drop from code for index */\n low = -1; /* trigger new sub-table when len > root */\n used = 1 << root; /* use root table entries */\n mask = used - 1; /* mask for comparing low */\n\n /* check available table space */\n if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {\n return 1;\n }\n\n /* process all codes and make table entries */\n for (;;) {\n /* create table entry */\n here_bits = len - drop;\n if (work[sym] + 1 < match) {\n here_op = 0;\n here_val = work[sym];\n } else if (work[sym] >= match) {\n here_op = extra[work[sym] - match];\n here_val = base[work[sym] - match];\n } else {\n here_op = 32 + 64; /* end of block */\n here_val = 0;\n }\n\n /* replicate for those indices with low len bits equal to huff */\n incr = 1 << len - drop;\n fill = 1 << curr;\n min = fill; /* save offset to next table */\n do {\n fill -= incr;\n table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0;\n } while (fill !== 0);\n\n /* backwards increment the len-bit code huff */\n incr = 1 << len - 1;\n while (huff & incr) {\n incr >>= 1;\n }\n if (incr !== 0) {\n huff &= incr - 1;\n huff += incr;\n } else {\n huff = 0;\n }\n\n /* go to next symbol, update count, len */\n sym++;\n if (--count[len] === 0) {\n if (len === max) {\n break;\n }\n len = lens[lens_index + work[sym]];\n }\n\n /* create new sub-table if needed */\n if (len > root && (huff & mask) !== low) {\n /* if first time, transition to sub-tables */\n if (drop === 0) {\n drop = root;\n }\n\n /* increment past last table */\n next += min; /* here min is 1 << curr */\n\n /* determine length of next table */\n curr = len - drop;\n left = 1 << curr;\n while (curr + drop < max) {\n left -= count[curr + drop];\n if (left <= 0) {\n break;\n }\n curr++;\n left <<= 1;\n }\n\n /* check for enough space */\n used += 1 << curr;\n if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {\n return 1;\n }\n\n /* point entry in root table to sub-table */\n low = huff & mask;\n /*table.op[low] = curr;\n table.bits[low] = root;\n table.val[low] = next - opts.table_index;*/\n table[low] = root << 24 | curr << 16 | next - table_index | 0;\n }\n }\n\n /* fill in remaining table entry if code is incomplete (guaranteed to have\n at most one remaining entry, since if the code is incomplete, the\n maximum code length that was allowed to get this far is one bit) */\n if (huff !== 0) {\n //table.op[next + huff] = 64; /* invalid code marker */\n //table.bits[next + huff] = len - drop;\n //table.val[next + huff] = 0;\n table[next + huff] = len - drop << 24 | 64 << 16 | 0;\n }\n\n /* set return parameters */\n //opts.table_index += used;\n opts.bits = root;\n return 0;\n};\nvar inftrees = inflate_table;\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nconst CODES = 0;\nconst LENS = 1;\nconst DISTS = 2;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\nconst {\n Z_FINISH: Z_FINISH$1,\n Z_BLOCK,\n Z_TREES,\n Z_OK: Z_OK$1,\n Z_STREAM_END: Z_STREAM_END$1,\n Z_NEED_DICT: Z_NEED_DICT$1,\n Z_STREAM_ERROR: Z_STREAM_ERROR$1,\n Z_DATA_ERROR: Z_DATA_ERROR$1,\n Z_MEM_ERROR: Z_MEM_ERROR$1,\n Z_BUF_ERROR,\n Z_DEFLATED\n} = constants$2;\n\n/* STATES ====================================================================*/\n/* ===========================================================================*/\n\nconst HEAD = 16180; /* i: waiting for magic header */\nconst FLAGS = 16181; /* i: waiting for method and flags (gzip) */\nconst TIME = 16182; /* i: waiting for modification time (gzip) */\nconst OS = 16183; /* i: waiting for extra flags and operating system (gzip) */\nconst EXLEN = 16184; /* i: waiting for extra length (gzip) */\nconst EXTRA = 16185; /* i: waiting for extra bytes (gzip) */\nconst NAME = 16186; /* i: waiting for end of file name (gzip) */\nconst COMMENT = 16187; /* i: waiting for end of comment (gzip) */\nconst HCRC = 16188; /* i: waiting for header crc (gzip) */\nconst DICTID = 16189; /* i: waiting for dictionary check value */\nconst DICT = 16190; /* waiting for inflateSetDictionary() call */\nconst TYPE = 16191; /* i: waiting for type bits, including last-flag bit */\nconst TYPEDO = 16192; /* i: same, but skip check to exit inflate on new block */\nconst STORED = 16193; /* i: waiting for stored size (length and complement) */\nconst COPY_ = 16194; /* i/o: same as COPY below, but only first time in */\nconst COPY = 16195; /* i/o: waiting for input or output to copy stored block */\nconst TABLE = 16196; /* i: waiting for dynamic block table lengths */\nconst LENLENS = 16197; /* i: waiting for code length code lengths */\nconst CODELENS = 16198; /* i: waiting for length/lit and distance code lengths */\nconst LEN_ = 16199; /* i: same as LEN below, but only first time in */\nconst LEN = 16200; /* i: waiting for length/lit/eob code */\nconst LENEXT = 16201; /* i: waiting for length extra bits */\nconst DIST = 16202; /* i: waiting for distance code */\nconst DISTEXT = 16203; /* i: waiting for distance extra bits */\nconst MATCH = 16204; /* o: waiting for output space to copy string */\nconst LIT = 16205; /* o: waiting for output space to write literal */\nconst CHECK = 16206; /* i: waiting for 32-bit check value */\nconst LENGTH = 16207; /* i: waiting for 32-bit length (gzip) */\nconst DONE = 16208; /* finished check, done -- remain here until reset */\nconst BAD = 16209; /* got a data error -- remain here until reset */\nconst MEM = 16210; /* got an inflate() memory error -- remain here until reset */\nconst SYNC = 16211; /* looking for synchronization bytes to restart inflate() */\n\n/* ===========================================================================*/\n\nconst ENOUGH_LENS = 852;\nconst ENOUGH_DISTS = 592;\n//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);\n\nconst MAX_WBITS = 15;\n/* 32K LZ77 window */\nconst DEF_WBITS = MAX_WBITS;\nconst zswap32 = q => {\n return (q >>> 24 & 0xff) + (q >>> 8 & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24);\n};\nfunction InflateState() {\n this.strm = null; /* pointer back to this zlib stream */\n this.mode = 0; /* current inflate mode */\n this.last = false; /* true if processing last block */\n this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip,\n bit 2 true to validate check value */\n this.havedict = false; /* true if dictionary provided */\n this.flags = 0; /* gzip header method and flags (0 if zlib), or\n -1 if raw or no header yet */\n this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */\n this.check = 0; /* protected copy of check value */\n this.total = 0; /* protected copy of output count */\n // TODO: may be {}\n this.head = null; /* where to save gzip header information */\n\n /* sliding window */\n this.wbits = 0; /* log base 2 of requested window size */\n this.wsize = 0; /* window size or zero if not using window */\n this.whave = 0; /* valid bytes in the window */\n this.wnext = 0; /* window write index */\n this.window = null; /* allocated sliding window, if needed */\n\n /* bit accumulator */\n this.hold = 0; /* input bit accumulator */\n this.bits = 0; /* number of bits in \"in\" */\n\n /* for string and stored block copying */\n this.length = 0; /* literal or length of data to copy */\n this.offset = 0; /* distance back to copy string from */\n\n /* for table and code decoding */\n this.extra = 0; /* extra bits needed */\n\n /* fixed and dynamic code tables */\n this.lencode = null; /* starting table for length/literal codes */\n this.distcode = null; /* starting table for distance codes */\n this.lenbits = 0; /* index bits for lencode */\n this.distbits = 0; /* index bits for distcode */\n\n /* dynamic table building */\n this.ncode = 0; /* number of code length code lengths */\n this.nlen = 0; /* number of length code lengths */\n this.ndist = 0; /* number of distance code lengths */\n this.have = 0; /* number of code lengths in lens[] */\n this.next = null; /* next available space in codes[] */\n\n this.lens = new Uint16Array(320); /* temporary storage for code lengths */\n this.work = new Uint16Array(288); /* work area for code table building */\n\n /*\n because we don't have pointers in js, we use lencode and distcode directly\n as buffers so we don't need codes\n */\n //this.codes = new Int32Array(ENOUGH); /* space for code tables */\n this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */\n this.distdyn = null; /* dynamic table for distance codes (JS specific) */\n this.sane = 0; /* if false, allow invalid distance too far */\n this.back = 0; /* bits back of last unprocessed length/lit */\n this.was = 0; /* initial length of match */\n}\nconst inflateStateCheck = strm => {\n if (!strm) {\n return 1;\n }\n const state = strm.state;\n if (!state || state.strm !== strm || state.mode < HEAD || state.mode > SYNC) {\n return 1;\n }\n return 0;\n};\nconst inflateResetKeep = strm => {\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n const state = strm.state;\n strm.total_in = strm.total_out = state.total = 0;\n strm.msg = ''; /*Z_NULL*/\n if (state.wrap) {\n /* to support ill-conceived Java test suite */\n strm.adler = state.wrap & 1;\n }\n state.mode = HEAD;\n state.last = 0;\n state.havedict = 0;\n state.flags = -1;\n state.dmax = 32768;\n state.head = null /*Z_NULL*/;\n state.hold = 0;\n state.bits = 0;\n //state.lencode = state.distcode = state.next = state.codes;\n state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS);\n state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS);\n state.sane = 1;\n state.back = -1;\n //Tracev((stderr, \"inflate: reset\\n\"));\n return Z_OK$1;\n};\nconst inflateReset = strm => {\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n const state = strm.state;\n state.wsize = 0;\n state.whave = 0;\n state.wnext = 0;\n return inflateResetKeep(strm);\n};\nconst inflateReset2 = (strm, windowBits) => {\n let wrap;\n\n /* get the state */\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n const state = strm.state;\n\n /* extract wrap request from windowBits parameter */\n if (windowBits < 0) {\n wrap = 0;\n windowBits = -windowBits;\n } else {\n wrap = (windowBits >> 4) + 5;\n if (windowBits < 48) {\n windowBits &= 15;\n }\n }\n\n /* set number of window bits, free window if different */\n if (windowBits && (windowBits < 8 || windowBits > 15)) {\n return Z_STREAM_ERROR$1;\n }\n if (state.window !== null && state.wbits !== windowBits) {\n state.window = null;\n }\n\n /* update state and reset the rest of it */\n state.wrap = wrap;\n state.wbits = windowBits;\n return inflateReset(strm);\n};\nconst inflateInit2 = (strm, windowBits) => {\n if (!strm) {\n return Z_STREAM_ERROR$1;\n }\n //strm.msg = Z_NULL; /* in case we return an error */\n\n const state = new InflateState();\n\n //if (state === Z_NULL) return Z_MEM_ERROR;\n //Tracev((stderr, \"inflate: allocated\\n\"));\n strm.state = state;\n state.strm = strm;\n state.window = null /*Z_NULL*/;\n state.mode = HEAD; /* to pass state test in inflateReset2() */\n const ret = inflateReset2(strm, windowBits);\n if (ret !== Z_OK$1) {\n strm.state = null /*Z_NULL*/;\n }\n return ret;\n};\nconst inflateInit = strm => {\n return inflateInit2(strm, DEF_WBITS);\n};\n\n/*\n Return state with length and distance decoding tables and index sizes set to\n fixed code decoding. Normally this returns fixed tables from inffixed.h.\n If BUILDFIXED is defined, then instead this routine builds the tables the\n first time it's called, and returns those tables the first time and\n thereafter. This reduces the size of the code by about 2K bytes, in\n exchange for a little execution time. However, BUILDFIXED should not be\n used for threaded applications, since the rewriting of the tables and virgin\n may not be thread-safe.\n */\nlet virgin = true;\nlet lenfix, distfix; // We have no pointers in JS, so keep tables separate\n\nconst fixedtables = state => {\n /* build fixed huffman tables if first call (may not be thread safe) */\n if (virgin) {\n lenfix = new Int32Array(512);\n distfix = new Int32Array(32);\n\n /* literal/length table */\n let sym = 0;\n while (sym < 144) {\n state.lens[sym++] = 8;\n }\n while (sym < 256) {\n state.lens[sym++] = 9;\n }\n while (sym < 280) {\n state.lens[sym++] = 7;\n }\n while (sym < 288) {\n state.lens[sym++] = 8;\n }\n inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, {\n bits: 9\n });\n\n /* distance table */\n sym = 0;\n while (sym < 32) {\n state.lens[sym++] = 5;\n }\n inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, {\n bits: 5\n });\n\n /* do this just once */\n virgin = false;\n }\n state.lencode = lenfix;\n state.lenbits = 9;\n state.distcode = distfix;\n state.distbits = 5;\n};\n\n/*\n Update the window with the last wsize (normally 32K) bytes written before\n returning. If window does not exist yet, create it. This is only called\n when a window is already in use, or when output has been written during this\n inflate call, but the end of the deflate stream has not been reached yet.\n It is also called to create a window for dictionary data when a dictionary\n is loaded.\n\n Providing output buffers larger than 32K to inflate() should provide a speed\n advantage, since only the last 32K of output is copied to the sliding window\n upon return from inflate(), and since all distances after the first 32K of\n output will fall in the output data, making match copies simpler and faster.\n The advantage may be dependent on the size of the processor's data caches.\n */\nconst updatewindow = (strm, src, end, copy) => {\n let dist;\n const state = strm.state;\n\n /* if it hasn't been done already, allocate space for the window */\n if (state.window === null) {\n state.wsize = 1 << state.wbits;\n state.wnext = 0;\n state.whave = 0;\n state.window = new Uint8Array(state.wsize);\n }\n\n /* copy state->wsize or less output bytes into the circular window */\n if (copy >= state.wsize) {\n state.window.set(src.subarray(end - state.wsize, end), 0);\n state.wnext = 0;\n state.whave = state.wsize;\n } else {\n dist = state.wsize - state.wnext;\n if (dist > copy) {\n dist = copy;\n }\n //zmemcpy(state->window + state->wnext, end - copy, dist);\n state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);\n copy -= dist;\n if (copy) {\n //zmemcpy(state->window, end - copy, copy);\n state.window.set(src.subarray(end - copy, end), 0);\n state.wnext = copy;\n state.whave = state.wsize;\n } else {\n state.wnext += dist;\n if (state.wnext === state.wsize) {\n state.wnext = 0;\n }\n if (state.whave < state.wsize) {\n state.whave += dist;\n }\n }\n }\n return 0;\n};\nconst inflate$2 = (strm, flush) => {\n let state;\n let input, output; // input/output buffers\n let next; /* next input INDEX */\n let put; /* next output INDEX */\n let have, left; /* available input and output */\n let hold; /* bit buffer */\n let bits; /* bits in bit buffer */\n let _in, _out; /* save starting available input and output */\n let copy; /* number of stored or match bytes to copy */\n let from; /* where to copy match bytes from */\n let from_source;\n let here = 0; /* current decoding table entry */\n let here_bits, here_op, here_val; // paked \"here\" denormalized (JS specific)\n //let last; /* parent table entry */\n let last_bits, last_op, last_val; // paked \"last\" denormalized (JS specific)\n let len; /* length to copy for repeats, bits to drop */\n let ret; /* return code */\n const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */\n let opts;\n let n; // temporary variable for NEED_BITS\n\n const order = /* permutation of code lengths */\n new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);\n if (inflateStateCheck(strm) || !strm.output || !strm.input && strm.avail_in !== 0) {\n return Z_STREAM_ERROR$1;\n }\n state = strm.state;\n if (state.mode === TYPE) {\n state.mode = TYPEDO;\n } /* skip check */\n\n //--- LOAD() ---\n put = strm.next_out;\n output = strm.output;\n left = strm.avail_out;\n next = strm.next_in;\n input = strm.input;\n have = strm.avail_in;\n hold = state.hold;\n bits = state.bits;\n //---\n\n _in = have;\n _out = left;\n ret = Z_OK$1;\n inf_leave:\n // goto emulation\n for (;;) {\n switch (state.mode) {\n case HEAD:\n if (state.wrap === 0) {\n state.mode = TYPEDO;\n break;\n }\n //=== NEEDBITS(16);\n while (bits < 16) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if (state.wrap & 2 && hold === 0x8b1f) {\n /* gzip header */\n if (state.wbits === 0) {\n state.wbits = 15;\n }\n state.check = 0 /*crc32(0L, Z_NULL, 0)*/;\n //=== CRC2(state.check, hold);\n hbuf[0] = hold & 0xff;\n hbuf[1] = hold >>> 8 & 0xff;\n state.check = crc32_1(state.check, hbuf, 2, 0);\n //===//\n\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = FLAGS;\n break;\n }\n if (state.head) {\n state.head.done = false;\n }\n if (!(state.wrap & 1) || /* check if zlib header allowed */\n (((hold & 0xff /*BITS(8)*/) << 8) + (hold >> 8)) % 31) {\n strm.msg = 'incorrect header check';\n state.mode = BAD;\n break;\n }\n if ((hold & 0x0f /*BITS(4)*/) !== Z_DEFLATED) {\n strm.msg = 'unknown compression method';\n state.mode = BAD;\n break;\n }\n //--- DROPBITS(4) ---//\n hold >>>= 4;\n bits -= 4;\n //---//\n len = (hold & 0x0f /*BITS(4)*/) + 8;\n if (state.wbits === 0) {\n state.wbits = len;\n }\n if (len > 15 || len > state.wbits) {\n strm.msg = 'invalid window size';\n state.mode = BAD;\n break;\n }\n\n // !!! pako patch. Force use `options.windowBits` if passed.\n // Required to always use max window size by default.\n state.dmax = 1 << state.wbits;\n //state.dmax = 1 << len;\n\n state.flags = 0; /* indicate zlib header */\n //Tracev((stderr, \"inflate: zlib header ok\\n\"));\n strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/;\n state.mode = hold & 0x200 ? DICTID : TYPE;\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n break;\n case FLAGS:\n //=== NEEDBITS(16); */\n while (bits < 16) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.flags = hold;\n if ((state.flags & 0xff) !== Z_DEFLATED) {\n strm.msg = 'unknown compression method';\n state.mode = BAD;\n break;\n }\n if (state.flags & 0xe000) {\n strm.msg = 'unknown header flags set';\n state.mode = BAD;\n break;\n }\n if (state.head) {\n state.head.text = hold >> 8 & 1;\n }\n if (state.flags & 0x0200 && state.wrap & 4) {\n //=== CRC2(state.check, hold);\n hbuf[0] = hold & 0xff;\n hbuf[1] = hold >>> 8 & 0xff;\n state.check = crc32_1(state.check, hbuf, 2, 0);\n //===//\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = TIME;\n /* falls through */\n case TIME:\n //=== NEEDBITS(32); */\n while (bits < 32) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if (state.head) {\n state.head.time = hold;\n }\n if (state.flags & 0x0200 && state.wrap & 4) {\n //=== CRC4(state.check, hold)\n hbuf[0] = hold & 0xff;\n hbuf[1] = hold >>> 8 & 0xff;\n hbuf[2] = hold >>> 16 & 0xff;\n hbuf[3] = hold >>> 24 & 0xff;\n state.check = crc32_1(state.check, hbuf, 4, 0);\n //===\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = OS;\n /* falls through */\n case OS:\n //=== NEEDBITS(16); */\n while (bits < 16) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if (state.head) {\n state.head.xflags = hold & 0xff;\n state.head.os = hold >> 8;\n }\n if (state.flags & 0x0200 && state.wrap & 4) {\n //=== CRC2(state.check, hold);\n hbuf[0] = hold & 0xff;\n hbuf[1] = hold >>> 8 & 0xff;\n state.check = crc32_1(state.check, hbuf, 2, 0);\n //===//\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = EXLEN;\n /* falls through */\n case EXLEN:\n if (state.flags & 0x0400) {\n //=== NEEDBITS(16); */\n while (bits < 16) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.length = hold;\n if (state.head) {\n state.head.extra_len = hold;\n }\n if (state.flags & 0x0200 && state.wrap & 4) {\n //=== CRC2(state.check, hold);\n hbuf[0] = hold & 0xff;\n hbuf[1] = hold >>> 8 & 0xff;\n state.check = crc32_1(state.check, hbuf, 2, 0);\n //===//\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n } else if (state.head) {\n state.head.extra = null /*Z_NULL*/;\n }\n state.mode = EXTRA;\n /* falls through */\n case EXTRA:\n if (state.flags & 0x0400) {\n copy = state.length;\n if (copy > have) {\n copy = have;\n }\n if (copy) {\n if (state.head) {\n len = state.head.extra_len - state.length;\n if (!state.head.extra) {\n // Use untyped array for more convenient processing later\n state.head.extra = new Uint8Array(state.head.extra_len);\n }\n state.head.extra.set(input.subarray(next,\n // extra field is limited to 65536 bytes\n // - no need for additional size check\n next + copy), /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/\n len);\n //zmemcpy(state.head.extra + len, next,\n // len + copy > state.head.extra_max ?\n // state.head.extra_max - len : copy);\n }\n if (state.flags & 0x0200 && state.wrap & 4) {\n state.check = crc32_1(state.check, input, copy, next);\n }\n have -= copy;\n next += copy;\n state.length -= copy;\n }\n if (state.length) {\n break inf_leave;\n }\n }\n state.length = 0;\n state.mode = NAME;\n /* falls through */\n case NAME:\n if (state.flags & 0x0800) {\n if (have === 0) {\n break inf_leave;\n }\n copy = 0;\n do {\n // TODO: 2 or 1 bytes?\n len = input[next + copy++];\n /* use constant limit because in js we should not preallocate memory */\n if (state.head && len && state.length < 65536 /*state.head.name_max*/) {\n state.head.name += String.fromCharCode(len);\n }\n } while (len && copy < have);\n if (state.flags & 0x0200 && state.wrap & 4) {\n state.check = crc32_1(state.check, input, copy, next);\n }\n have -= copy;\n next += copy;\n if (len) {\n break inf_leave;\n }\n } else if (state.head) {\n state.head.name = null;\n }\n state.length = 0;\n state.mode = COMMENT;\n /* falls through */\n case COMMENT:\n if (state.flags & 0x1000) {\n if (have === 0) {\n break inf_leave;\n }\n copy = 0;\n do {\n len = input[next + copy++];\n /* use constant limit because in js we should not preallocate memory */\n if (state.head && len && state.length < 65536 /*state.head.comm_max*/) {\n state.head.comment += String.fromCharCode(len);\n }\n } while (len && copy < have);\n if (state.flags & 0x0200 && state.wrap & 4) {\n state.check = crc32_1(state.check, input, copy, next);\n }\n have -= copy;\n next += copy;\n if (len) {\n break inf_leave;\n }\n } else if (state.head) {\n state.head.comment = null;\n }\n state.mode = HCRC;\n /* falls through */\n case HCRC:\n if (state.flags & 0x0200) {\n //=== NEEDBITS(16); */\n while (bits < 16) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if (state.wrap & 4 && hold !== (state.check & 0xffff)) {\n strm.msg = 'header crc mismatch';\n state.mode = BAD;\n break;\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n }\n if (state.head) {\n state.head.hcrc = state.flags >> 9 & 1;\n state.head.done = true;\n }\n strm.adler = state.check = 0;\n state.mode = TYPE;\n break;\n case DICTID:\n //=== NEEDBITS(32); */\n while (bits < 32) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n strm.adler = state.check = zswap32(hold);\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = DICT;\n /* falls through */\n case DICT:\n if (state.havedict === 0) {\n //--- RESTORE() ---\n strm.next_out = put;\n strm.avail_out = left;\n strm.next_in = next;\n strm.avail_in = have;\n state.hold = hold;\n state.bits = bits;\n //---\n return Z_NEED_DICT$1;\n }\n strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/;\n state.mode = TYPE;\n /* falls through */\n case TYPE:\n if (flush === Z_BLOCK || flush === Z_TREES) {\n break inf_leave;\n }\n /* falls through */\n case TYPEDO:\n if (state.last) {\n //--- BYTEBITS() ---//\n hold >>>= bits & 7;\n bits -= bits & 7;\n //---//\n state.mode = CHECK;\n break;\n }\n //=== NEEDBITS(3); */\n while (bits < 3) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.last = hold & 0x01 /*BITS(1)*/;\n //--- DROPBITS(1) ---//\n hold >>>= 1;\n bits -= 1;\n //---//\n\n switch (hold & 0x03 /*BITS(2)*/) {\n case 0:\n /* stored block */\n //Tracev((stderr, \"inflate: stored block%s\\n\",\n // state.last ? \" (last)\" : \"\"));\n state.mode = STORED;\n break;\n case 1:\n /* fixed block */\n fixedtables(state);\n //Tracev((stderr, \"inflate: fixed codes block%s\\n\",\n // state.last ? \" (last)\" : \"\"));\n state.mode = LEN_; /* decode codes */\n if (flush === Z_TREES) {\n //--- DROPBITS(2) ---//\n hold >>>= 2;\n bits -= 2;\n //---//\n break inf_leave;\n }\n break;\n case 2:\n /* dynamic block */\n //Tracev((stderr, \"inflate: dynamic codes block%s\\n\",\n // state.last ? \" (last)\" : \"\"));\n state.mode = TABLE;\n break;\n case 3:\n strm.msg = 'invalid block type';\n state.mode = BAD;\n }\n //--- DROPBITS(2) ---//\n hold >>>= 2;\n bits -= 2;\n //---//\n break;\n case STORED:\n //--- BYTEBITS() ---// /* go to byte boundary */\n hold >>>= bits & 7;\n bits -= bits & 7;\n //---//\n //=== NEEDBITS(32); */\n while (bits < 32) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if ((hold & 0xffff) !== (hold >>> 16 ^ 0xffff)) {\n strm.msg = 'invalid stored block lengths';\n state.mode = BAD;\n break;\n }\n state.length = hold & 0xffff;\n //Tracev((stderr, \"inflate: stored length %u\\n\",\n // state.length));\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n state.mode = COPY_;\n if (flush === Z_TREES) {\n break inf_leave;\n }\n /* falls through */\n case COPY_:\n state.mode = COPY;\n /* falls through */\n case COPY:\n copy = state.length;\n if (copy) {\n if (copy > have) {\n copy = have;\n }\n if (copy > left) {\n copy = left;\n }\n if (copy === 0) {\n break inf_leave;\n }\n //--- zmemcpy(put, next, copy); ---\n output.set(input.subarray(next, next + copy), put);\n //---//\n have -= copy;\n next += copy;\n left -= copy;\n put += copy;\n state.length -= copy;\n break;\n }\n //Tracev((stderr, \"inflate: stored end\\n\"));\n state.mode = TYPE;\n break;\n case TABLE:\n //=== NEEDBITS(14); */\n while (bits < 14) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.nlen = (hold & 0x1f /*BITS(5)*/) + 257;\n //--- DROPBITS(5) ---//\n hold >>>= 5;\n bits -= 5;\n //---//\n state.ndist = (hold & 0x1f /*BITS(5)*/) + 1;\n //--- DROPBITS(5) ---//\n hold >>>= 5;\n bits -= 5;\n //---//\n state.ncode = (hold & 0x0f /*BITS(4)*/) + 4;\n //--- DROPBITS(4) ---//\n hold >>>= 4;\n bits -= 4;\n //---//\n //#ifndef PKZIP_BUG_WORKAROUND\n if (state.nlen > 286 || state.ndist > 30) {\n strm.msg = 'too many length or distance symbols';\n state.mode = BAD;\n break;\n }\n //#endif\n //Tracev((stderr, \"inflate: table sizes ok\\n\"));\n state.have = 0;\n state.mode = LENLENS;\n /* falls through */\n case LENLENS:\n while (state.have < state.ncode) {\n //=== NEEDBITS(3);\n while (bits < 3) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.lens[order[state.have++]] = hold & 0x07; //BITS(3);\n //--- DROPBITS(3) ---//\n hold >>>= 3;\n bits -= 3;\n //---//\n }\n while (state.have < 19) {\n state.lens[order[state.have++]] = 0;\n }\n // We have separate tables & no pointers. 2 commented lines below not needed.\n //state.next = state.codes;\n //state.lencode = state.next;\n // Switch to use dynamic table\n state.lencode = state.lendyn;\n state.lenbits = 7;\n opts = {\n bits: state.lenbits\n };\n ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);\n state.lenbits = opts.bits;\n if (ret) {\n strm.msg = 'invalid code lengths set';\n state.mode = BAD;\n break;\n }\n //Tracev((stderr, \"inflate: code lengths ok\\n\"));\n state.have = 0;\n state.mode = CODELENS;\n /* falls through */\n case CODELENS:\n while (state.have < state.nlen + state.ndist) {\n for (;;) {\n here = state.lencode[hold & (1 << state.lenbits) - 1]; /*BITS(state.lenbits)*/\n here_bits = here >>> 24;\n here_op = here >>> 16 & 0xff;\n here_val = here & 0xffff;\n if (here_bits <= bits) {\n break;\n }\n //--- PULLBYTE() ---//\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n //---//\n }\n if (here_val < 16) {\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n state.lens[state.have++] = here_val;\n } else {\n if (here_val === 16) {\n //=== NEEDBITS(here.bits + 2);\n n = here_bits + 2;\n while (bits < n) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n if (state.have === 0) {\n strm.msg = 'invalid bit length repeat';\n state.mode = BAD;\n break;\n }\n len = state.lens[state.have - 1];\n copy = 3 + (hold & 0x03); //BITS(2);\n //--- DROPBITS(2) ---//\n hold >>>= 2;\n bits -= 2;\n //---//\n } else if (here_val === 17) {\n //=== NEEDBITS(here.bits + 3);\n n = here_bits + 3;\n while (bits < n) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n len = 0;\n copy = 3 + (hold & 0x07); //BITS(3);\n //--- DROPBITS(3) ---//\n hold >>>= 3;\n bits -= 3;\n //---//\n } else {\n //=== NEEDBITS(here.bits + 7);\n n = here_bits + 7;\n while (bits < n) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n len = 0;\n copy = 11 + (hold & 0x7f); //BITS(7);\n //--- DROPBITS(7) ---//\n hold >>>= 7;\n bits -= 7;\n //---//\n }\n if (state.have + copy > state.nlen + state.ndist) {\n strm.msg = 'invalid bit length repeat';\n state.mode = BAD;\n break;\n }\n while (copy--) {\n state.lens[state.have++] = len;\n }\n }\n }\n\n /* handle error breaks in while */\n if (state.mode === BAD) {\n break;\n }\n\n /* check for end-of-block code (better have one) */\n if (state.lens[256] === 0) {\n strm.msg = 'invalid code -- missing end-of-block';\n state.mode = BAD;\n break;\n }\n\n /* build code tables -- note: do not change the lenbits or distbits\n values here (9 and 6) without reading the comments in inftrees.h\n concerning the ENOUGH constants, which depend on those values */\n state.lenbits = 9;\n opts = {\n bits: state.lenbits\n };\n ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);\n // We have separate tables & no pointers. 2 commented lines below not needed.\n // state.next_index = opts.table_index;\n state.lenbits = opts.bits;\n // state.lencode = state.next;\n\n if (ret) {\n strm.msg = 'invalid literal/lengths set';\n state.mode = BAD;\n break;\n }\n state.distbits = 6;\n //state.distcode.copy(state.codes);\n // Switch to use dynamic table\n state.distcode = state.distdyn;\n opts = {\n bits: state.distbits\n };\n ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);\n // We have separate tables & no pointers. 2 commented lines below not needed.\n // state.next_index = opts.table_index;\n state.distbits = opts.bits;\n // state.distcode = state.next;\n\n if (ret) {\n strm.msg = 'invalid distances set';\n state.mode = BAD;\n break;\n }\n //Tracev((stderr, 'inflate: codes ok\\n'));\n state.mode = LEN_;\n if (flush === Z_TREES) {\n break inf_leave;\n }\n /* falls through */\n case LEN_:\n state.mode = LEN;\n /* falls through */\n case LEN:\n if (have >= 6 && left >= 258) {\n //--- RESTORE() ---\n strm.next_out = put;\n strm.avail_out = left;\n strm.next_in = next;\n strm.avail_in = have;\n state.hold = hold;\n state.bits = bits;\n //---\n inffast(strm, _out);\n //--- LOAD() ---\n put = strm.next_out;\n output = strm.output;\n left = strm.avail_out;\n next = strm.next_in;\n input = strm.input;\n have = strm.avail_in;\n hold = state.hold;\n bits = state.bits;\n //---\n\n if (state.mode === TYPE) {\n state.back = -1;\n }\n break;\n }\n state.back = 0;\n for (;;) {\n here = state.lencode[hold & (1 << state.lenbits) - 1]; /*BITS(state.lenbits)*/\n here_bits = here >>> 24;\n here_op = here >>> 16 & 0xff;\n here_val = here & 0xffff;\n if (here_bits <= bits) {\n break;\n }\n //--- PULLBYTE() ---//\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n //---//\n }\n if (here_op && (here_op & 0xf0) === 0) {\n last_bits = here_bits;\n last_op = here_op;\n last_val = here_val;\n for (;;) {\n here = state.lencode[last_val + ((hold & (1 << last_bits + last_op) - 1 /*BITS(last.bits + last.op)*/) >> last_bits)];\n here_bits = here >>> 24;\n here_op = here >>> 16 & 0xff;\n here_val = here & 0xffff;\n if (last_bits + here_bits <= bits) {\n break;\n }\n //--- PULLBYTE() ---//\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n //---//\n }\n //--- DROPBITS(last.bits) ---//\n hold >>>= last_bits;\n bits -= last_bits;\n //---//\n state.back += last_bits;\n }\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n state.back += here_bits;\n state.length = here_val;\n if (here_op === 0) {\n //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n // \"inflate: literal '%c'\\n\" :\n // \"inflate: literal 0x%02x\\n\", here.val));\n state.mode = LIT;\n break;\n }\n if (here_op & 32) {\n //Tracevv((stderr, \"inflate: end of block\\n\"));\n state.back = -1;\n state.mode = TYPE;\n break;\n }\n if (here_op & 64) {\n strm.msg = 'invalid literal/length code';\n state.mode = BAD;\n break;\n }\n state.extra = here_op & 15;\n state.mode = LENEXT;\n /* falls through */\n case LENEXT:\n if (state.extra) {\n //=== NEEDBITS(state.extra);\n n = state.extra;\n while (bits < n) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.length += hold & (1 << state.extra) - 1 /*BITS(state.extra)*/;\n //--- DROPBITS(state.extra) ---//\n hold >>>= state.extra;\n bits -= state.extra;\n //---//\n state.back += state.extra;\n }\n //Tracevv((stderr, \"inflate: length %u\\n\", state.length));\n state.was = state.length;\n state.mode = DIST;\n /* falls through */\n case DIST:\n for (;;) {\n here = state.distcode[hold & (1 << state.distbits) - 1]; /*BITS(state.distbits)*/\n here_bits = here >>> 24;\n here_op = here >>> 16 & 0xff;\n here_val = here & 0xffff;\n if (here_bits <= bits) {\n break;\n }\n //--- PULLBYTE() ---//\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n //---//\n }\n if ((here_op & 0xf0) === 0) {\n last_bits = here_bits;\n last_op = here_op;\n last_val = here_val;\n for (;;) {\n here = state.distcode[last_val + ((hold & (1 << last_bits + last_op) - 1 /*BITS(last.bits + last.op)*/) >> last_bits)];\n here_bits = here >>> 24;\n here_op = here >>> 16 & 0xff;\n here_val = here & 0xffff;\n if (last_bits + here_bits <= bits) {\n break;\n }\n //--- PULLBYTE() ---//\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n //---//\n }\n //--- DROPBITS(last.bits) ---//\n hold >>>= last_bits;\n bits -= last_bits;\n //---//\n state.back += last_bits;\n }\n //--- DROPBITS(here.bits) ---//\n hold >>>= here_bits;\n bits -= here_bits;\n //---//\n state.back += here_bits;\n if (here_op & 64) {\n strm.msg = 'invalid distance code';\n state.mode = BAD;\n break;\n }\n state.offset = here_val;\n state.extra = here_op & 15;\n state.mode = DISTEXT;\n /* falls through */\n case DISTEXT:\n if (state.extra) {\n //=== NEEDBITS(state.extra);\n n = state.extra;\n while (bits < n) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n state.offset += hold & (1 << state.extra) - 1 /*BITS(state.extra)*/;\n //--- DROPBITS(state.extra) ---//\n hold >>>= state.extra;\n bits -= state.extra;\n //---//\n state.back += state.extra;\n }\n //#ifdef INFLATE_STRICT\n if (state.offset > state.dmax) {\n strm.msg = 'invalid distance too far back';\n state.mode = BAD;\n break;\n }\n //#endif\n //Tracevv((stderr, \"inflate: distance %u\\n\", state.offset));\n state.mode = MATCH;\n /* falls through */\n case MATCH:\n if (left === 0) {\n break inf_leave;\n }\n copy = _out - left;\n if (state.offset > copy) {\n /* copy from window */\n copy = state.offset - copy;\n if (copy > state.whave) {\n if (state.sane) {\n strm.msg = 'invalid distance too far back';\n state.mode = BAD;\n break;\n }\n // (!) This block is disabled in zlib defaults,\n // don't enable it for binary compatibility\n //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n // Trace((stderr, \"inflate.c too far\\n\"));\n // copy -= state.whave;\n // if (copy > state.length) { copy = state.length; }\n // if (copy > left) { copy = left; }\n // left -= copy;\n // state.length -= copy;\n // do {\n // output[put++] = 0;\n // } while (--copy);\n // if (state.length === 0) { state.mode = LEN; }\n // break;\n //#endif\n }\n if (copy > state.wnext) {\n copy -= state.wnext;\n from = state.wsize - copy;\n } else {\n from = state.wnext - copy;\n }\n if (copy > state.length) {\n copy = state.length;\n }\n from_source = state.window;\n } else {\n /* copy from output */\n from_source = output;\n from = put - state.offset;\n copy = state.length;\n }\n if (copy > left) {\n copy = left;\n }\n left -= copy;\n state.length -= copy;\n do {\n output[put++] = from_source[from++];\n } while (--copy);\n if (state.length === 0) {\n state.mode = LEN;\n }\n break;\n case LIT:\n if (left === 0) {\n break inf_leave;\n }\n output[put++] = state.length;\n left--;\n state.mode = LEN;\n break;\n case CHECK:\n if (state.wrap) {\n //=== NEEDBITS(32);\n while (bits < 32) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n // Use '|' instead of '+' to make sure that result is signed\n hold |= input[next++] << bits;\n bits += 8;\n }\n //===//\n _out -= left;\n strm.total_out += _out;\n state.total += _out;\n if (state.wrap & 4 && _out) {\n strm.adler = state.check = /*UPDATE_CHECK(state.check, put - _out, _out);*/\n state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out);\n }\n _out = left;\n // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too\n if (state.wrap & 4 && (state.flags ? hold : zswap32(hold)) !== state.check) {\n strm.msg = 'incorrect data check';\n state.mode = BAD;\n break;\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n //Tracev((stderr, \"inflate: check matches trailer\\n\"));\n }\n state.mode = LENGTH;\n /* falls through */\n case LENGTH:\n if (state.wrap && state.flags) {\n //=== NEEDBITS(32);\n while (bits < 32) {\n if (have === 0) {\n break inf_leave;\n }\n have--;\n hold += input[next++] << bits;\n bits += 8;\n }\n //===//\n if (state.wrap & 4 && hold !== (state.total & 0xffffffff)) {\n strm.msg = 'incorrect length check';\n state.mode = BAD;\n break;\n }\n //=== INITBITS();\n hold = 0;\n bits = 0;\n //===//\n //Tracev((stderr, \"inflate: length matches trailer\\n\"));\n }\n state.mode = DONE;\n /* falls through */\n case DONE:\n ret = Z_STREAM_END$1;\n break inf_leave;\n case BAD:\n ret = Z_DATA_ERROR$1;\n break inf_leave;\n case MEM:\n return Z_MEM_ERROR$1;\n case SYNC:\n /* falls through */\n default:\n return Z_STREAM_ERROR$1;\n }\n }\n\n // inf_leave <- here is real place for \"goto inf_leave\", emulated via \"break inf_leave\"\n\n /*\n Return from inflate(), updating the total counts and the check value.\n If there was no progress during the inflate() call, return a buffer\n error. Call updatewindow() to create and/or update the window state.\n Note: a memory error from inflate() is non-recoverable.\n */\n\n //--- RESTORE() ---\n strm.next_out = put;\n strm.avail_out = left;\n strm.next_in = next;\n strm.avail_in = have;\n state.hold = hold;\n state.bits = bits;\n //---\n\n if (state.wsize || _out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1)) {\n if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ;\n }\n _in -= strm.avail_in;\n _out -= strm.avail_out;\n strm.total_in += _in;\n strm.total_out += _out;\n state.total += _out;\n if (state.wrap & 4 && _out) {\n strm.adler = state.check = /*UPDATE_CHECK(state.check, strm.next_out - _out, _out);*/\n state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out);\n }\n strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);\n if ((_in === 0 && _out === 0 || flush === Z_FINISH$1) && ret === Z_OK$1) {\n ret = Z_BUF_ERROR;\n }\n return ret;\n};\nconst inflateEnd = strm => {\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n let state = strm.state;\n if (state.window) {\n state.window = null;\n }\n strm.state = null;\n return Z_OK$1;\n};\nconst inflateGetHeader = (strm, head) => {\n /* check state */\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n const state = strm.state;\n if ((state.wrap & 2) === 0) {\n return Z_STREAM_ERROR$1;\n }\n\n /* save header structure */\n state.head = head;\n head.done = false;\n return Z_OK$1;\n};\nconst inflateSetDictionary = (strm, dictionary) => {\n const dictLength = dictionary.length;\n let state;\n let dictid;\n let ret;\n\n /* check state */\n if (inflateStateCheck(strm)) {\n return Z_STREAM_ERROR$1;\n }\n state = strm.state;\n if (state.wrap !== 0 && state.mode !== DICT) {\n return Z_STREAM_ERROR$1;\n }\n\n /* check for correct dictionary identifier */\n if (state.mode === DICT) {\n dictid = 1; /* adler32(0, null, 0)*/\n /* dictid = adler32(dictid, dictionary, dictLength); */\n dictid = adler32_1(dictid, dictionary, dictLength, 0);\n if (dictid !== state.check) {\n return Z_DATA_ERROR$1;\n }\n }\n /* copy dictionary to window using updatewindow(), which will amend the\n existing dictionary if appropriate */\n ret = updatewindow(strm, dictionary, dictLength, dictLength);\n if (ret) {\n state.mode = MEM;\n return Z_MEM_ERROR$1;\n }\n state.havedict = 1;\n // Tracev((stderr, \"inflate: dictionary set\\n\"));\n return Z_OK$1;\n};\nvar inflateReset_1 = inflateReset;\nvar inflateReset2_1 = inflateReset2;\nvar inflateResetKeep_1 = inflateResetKeep;\nvar inflateInit_1 = inflateInit;\nvar inflateInit2_1 = inflateInit2;\nvar inflate_2$1 = inflate$2;\nvar inflateEnd_1 = inflateEnd;\nvar inflateGetHeader_1 = inflateGetHeader;\nvar inflateSetDictionary_1 = inflateSetDictionary;\nvar inflateInfo = 'pako inflate (from Nodeca project)';\n\n/* Not implemented\nmodule.exports.inflateCodesUsed = inflateCodesUsed;\nmodule.exports.inflateCopy = inflateCopy;\nmodule.exports.inflateGetDictionary = inflateGetDictionary;\nmodule.exports.inflateMark = inflateMark;\nmodule.exports.inflatePrime = inflatePrime;\nmodule.exports.inflateSync = inflateSync;\nmodule.exports.inflateSyncPoint = inflateSyncPoint;\nmodule.exports.inflateUndermine = inflateUndermine;\nmodule.exports.inflateValidate = inflateValidate;\n*/\n\nvar inflate_1$2 = {\n inflateReset: inflateReset_1,\n inflateReset2: inflateReset2_1,\n inflateResetKeep: inflateResetKeep_1,\n inflateInit: inflateInit_1,\n inflateInit2: inflateInit2_1,\n inflate: inflate_2$1,\n inflateEnd: inflateEnd_1,\n inflateGetHeader: inflateGetHeader_1,\n inflateSetDictionary: inflateSetDictionary_1,\n inflateInfo: inflateInfo\n};\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n// claim that you wrote the original software. If you use this software\n// in a product, an acknowledgment in the product documentation would be\n// appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n// misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nfunction GZheader() {\n /* true if compressed data believed to be text */\n this.text = 0;\n /* modification time */\n this.time = 0;\n /* extra flags (not used when writing a gzip file) */\n this.xflags = 0;\n /* operating system */\n this.os = 0;\n /* pointer to extra field or Z_NULL if none */\n this.extra = null;\n /* extra field length (valid if extra != Z_NULL) */\n this.extra_len = 0; // Actually, we don't need it in JS,\n // but leave for few code modifications\n\n //\n // Setup limits is not necessary because in js we should not preallocate memory\n // for inflate use constant limit in 65536 bytes\n //\n\n /* space at extra (only when reading header) */\n // this.extra_max = 0;\n /* pointer to zero-terminated file name or Z_NULL */\n this.name = '';\n /* space at name (only when reading header) */\n // this.name_max = 0;\n /* pointer to zero-terminated comment or Z_NULL */\n this.comment = '';\n /* space at comment (only when reading header) */\n // this.comm_max = 0;\n /* true if there was or will be a header crc */\n this.hcrc = 0;\n /* true when done reading gzip header (not used when writing a gzip file) */\n this.done = false;\n}\nvar gzheader = GZheader;\nconst toString = Object.prototype.toString;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\nconst {\n Z_NO_FLUSH,\n Z_FINISH,\n Z_OK,\n Z_STREAM_END,\n Z_NEED_DICT,\n Z_STREAM_ERROR,\n Z_DATA_ERROR,\n Z_MEM_ERROR\n} = constants$2;\n\n/* ===========================================================================*/\n\n/**\n * class Inflate\n *\n * Generic JS-style wrapper for zlib calls. If you don't need\n * streaming behaviour - use more simple functions: [[inflate]]\n * and [[inflateRaw]].\n **/\n\n/* internal\n * inflate.chunks -> Array\n *\n * Chunks of output data, if [[Inflate#onData]] not overridden.\n **/\n\n/**\n * Inflate.result -> Uint8Array|String\n *\n * Uncompressed result, generated by default [[Inflate#onData]]\n * and [[Inflate#onEnd]] handlers. Filled after you push last chunk\n * (call [[Inflate#push]] with `Z_FINISH` / `true` param).\n **/\n\n/**\n * Inflate.err -> Number\n *\n * Error code after inflate finished. 0 (Z_OK) on success.\n * Should be checked if broken data possible.\n **/\n\n/**\n * Inflate.msg -> String\n *\n * Error message, if [[Inflate.err]] != 0\n **/\n\n/**\n * new Inflate(options)\n * - options (Object): zlib inflate options.\n *\n * Creates new inflator instance with specified params. Throws exception\n * on bad params. Supported options:\n *\n * - `windowBits`\n * - `dictionary`\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Additional options, for internal needs:\n *\n * - `chunkSize` - size of generated data chunks (16K by default)\n * - `raw` (Boolean) - do raw inflate\n * - `to` (String) - if equal to 'string', then result will be converted\n * from utf8 to utf16 (javascript) string. When string output requested,\n * chunk length can differ from `chunkSize`, depending on content.\n *\n * By default, when no options set, autodetect deflate/gzip data format via\n * wrapper header.\n *\n * ##### Example:\n *\n * ```javascript\n * const pako = require('pako')\n * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])\n * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);\n *\n * const inflate = new pako.Inflate({ level: 3});\n *\n * inflate.push(chunk1, false);\n * inflate.push(chunk2, true); // true -> last chunk\n *\n * if (inflate.err) { throw new Error(inflate.err); }\n *\n * console.log(inflate.result);\n * ```\n **/\nfunction Inflate$1(options) {\n this.options = common.assign({\n chunkSize: 1024 * 64,\n windowBits: 15,\n to: ''\n }, options || {});\n const opt = this.options;\n\n // Force window size for `raw` data, if not set directly,\n // because we have no header for autodetect.\n if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) {\n opt.windowBits = -opt.windowBits;\n if (opt.windowBits === 0) {\n opt.windowBits = -15;\n }\n }\n\n // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate\n if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) {\n opt.windowBits += 32;\n }\n\n // Gzip header has no info about windows size, we can do autodetect only\n // for deflate. So, if window size not set, force it to max when gzip possible\n if (opt.windowBits > 15 && opt.windowBits < 48) {\n // bit 3 (16) -> gzipped data\n // bit 4 (32) -> autodetect gzip/deflate\n if ((opt.windowBits & 15) === 0) {\n opt.windowBits |= 15;\n }\n }\n this.err = 0; // error code, if happens (0 = Z_OK)\n this.msg = ''; // error message\n this.ended = false; // used to avoid multiple onEnd() calls\n this.chunks = []; // chunks of compressed data\n\n this.strm = new zstream();\n this.strm.avail_out = 0;\n let status = inflate_1$2.inflateInit2(this.strm, opt.windowBits);\n if (status !== Z_OK) {\n throw new Error(messages[status]);\n }\n this.header = new gzheader();\n inflate_1$2.inflateGetHeader(this.strm, this.header);\n\n // Setup dictionary\n if (opt.dictionary) {\n // Convert data if needed\n if (typeof opt.dictionary === 'string') {\n opt.dictionary = strings.string2buf(opt.dictionary);\n } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {\n opt.dictionary = new Uint8Array(opt.dictionary);\n }\n if (opt.raw) {\n //In raw mode we need to set the dictionary early\n status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary);\n if (status !== Z_OK) {\n throw new Error(messages[status]);\n }\n }\n }\n}\n\n/**\n * Inflate#push(data[, flush_mode]) -> Boolean\n * - data (Uint8Array|ArrayBuffer): input data\n * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE\n * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH,\n * `true` means Z_FINISH.\n *\n * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with\n * new output chunks. Returns `true` on success. If end of stream detected,\n * [[Inflate#onEnd]] will be called.\n *\n * `flush_mode` is not needed for normal operation, because end of stream\n * detected automatically. You may try to use it for advanced things, but\n * this functionality was not tested.\n *\n * On fail call [[Inflate#onEnd]] with error code and return false.\n *\n * ##### Example\n *\n * ```javascript\n * push(chunk, false); // push one of data chunks\n * ...\n * push(chunk, true); // push last chunk\n * ```\n **/\nInflate$1.prototype.push = function (data, flush_mode) {\n const strm = this.strm;\n const chunkSize = this.options.chunkSize;\n const dictionary = this.options.dictionary;\n let status, _flush_mode, last_avail_out;\n if (this.ended) return false;\n if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH;\n\n // Convert data if needed\n if (toString.call(data) === '[object ArrayBuffer]') {\n strm.input = new Uint8Array(data);\n } else {\n strm.input = data;\n }\n strm.next_in = 0;\n strm.avail_in = strm.input.length;\n for (;;) {\n if (strm.avail_out === 0) {\n strm.output = new Uint8Array(chunkSize);\n strm.next_out = 0;\n strm.avail_out = chunkSize;\n }\n status = inflate_1$2.inflate(strm, _flush_mode);\n if (status === Z_NEED_DICT && dictionary) {\n status = inflate_1$2.inflateSetDictionary(strm, dictionary);\n if (status === Z_OK) {\n status = inflate_1$2.inflate(strm, _flush_mode);\n } else if (status === Z_DATA_ERROR) {\n // Replace code with more verbose\n status = Z_NEED_DICT;\n }\n }\n\n // Skip snyc markers if more data follows and not raw mode\n while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) {\n inflate_1$2.inflateReset(strm);\n status = inflate_1$2.inflate(strm, _flush_mode);\n }\n switch (status) {\n case Z_STREAM_ERROR:\n case Z_DATA_ERROR:\n case Z_NEED_DICT:\n case Z_MEM_ERROR:\n this.onEnd(status);\n this.ended = true;\n return false;\n }\n\n // Remember real `avail_out` value, because we may patch out buffer content\n // to align utf8 strings boundaries.\n last_avail_out = strm.avail_out;\n if (strm.next_out) {\n if (strm.avail_out === 0 || status === Z_STREAM_END) {\n if (this.options.to === 'string') {\n let next_out_utf8 = strings.utf8border(strm.output, strm.next_out);\n let tail = strm.next_out - next_out_utf8;\n let utf8str = strings.buf2string(strm.output, next_out_utf8);\n\n // move tail & realign counters\n strm.next_out = tail;\n strm.avail_out = chunkSize - tail;\n if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0);\n this.onData(utf8str);\n } else {\n this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out));\n }\n }\n }\n\n // Must repeat iteration if out buffer is full\n if (status === Z_OK && last_avail_out === 0) continue;\n\n // Finalize if end of stream reached.\n if (status === Z_STREAM_END) {\n status = inflate_1$2.inflateEnd(this.strm);\n this.onEnd(status);\n this.ended = true;\n return true;\n }\n if (strm.avail_in === 0) break;\n }\n return true;\n};\n\n/**\n * Inflate#onData(chunk) -> Void\n * - chunk (Uint8Array|String): output data. When string output requested,\n * each chunk will be string.\n *\n * By default, stores data blocks in `chunks[]` property and glue\n * those in `onEnd`. Override this handler, if you need another behaviour.\n **/\nInflate$1.prototype.onData = function (chunk) {\n this.chunks.push(chunk);\n};\n\n/**\n * Inflate#onEnd(status) -> Void\n * - status (Number): inflate status. 0 (Z_OK) on success,\n * other if not.\n *\n * Called either after you tell inflate that the input stream is\n * complete (Z_FINISH). By default - join collected chunks,\n * free memory and fill `results` / `err` properties.\n **/\nInflate$1.prototype.onEnd = function (status) {\n // On success - join\n if (status === Z_OK) {\n if (this.options.to === 'string') {\n this.result = this.chunks.join('');\n } else {\n this.result = common.flattenChunks(this.chunks);\n }\n }\n this.chunks = [];\n this.err = status;\n this.msg = this.strm.msg;\n};\n\n/**\n * inflate(data[, options]) -> Uint8Array|String\n * - data (Uint8Array|ArrayBuffer): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * Decompress `data` with inflate/ungzip and `options`. Autodetect\n * format via wrapper header by default. That's why we don't provide\n * separate `ungzip` method.\n *\n * Supported options are:\n *\n * - windowBits\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information.\n *\n * Sugar (options):\n *\n * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify\n * negative windowBits implicitly.\n * - `to` (String) - if equal to 'string', then result will be converted\n * from utf8 to utf16 (javascript) string. When string output requested,\n * chunk length can differ from `chunkSize`, depending on content.\n *\n *\n * ##### Example:\n *\n * ```javascript\n * const pako = require('pako');\n * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9]));\n * let output;\n *\n * try {\n * output = pako.inflate(input);\n * } catch (err) {\n * console.log(err);\n * }\n * ```\n **/\nfunction inflate$1(input, options) {\n const inflator = new Inflate$1(options);\n inflator.push(input);\n\n // That will never happens, if you don't cheat with options :)\n if (inflator.err) throw inflator.msg || messages[inflator.err];\n return inflator.result;\n}\n\n/**\n * inflateRaw(data[, options]) -> Uint8Array|String\n * - data (Uint8Array|ArrayBuffer): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * The same as [[inflate]], but creates raw data, without wrapper\n * (header and adler32 crc).\n **/\nfunction inflateRaw$1(input, options) {\n options = options || {};\n options.raw = true;\n return inflate$1(input, options);\n}\n\n/**\n * ungzip(data[, options]) -> Uint8Array|String\n * - data (Uint8Array|ArrayBuffer): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * Just shortcut to [[inflate]], because it autodetects format\n * by header.content. Done for convenience.\n **/\n\nvar Inflate_1$1 = Inflate$1;\nvar inflate_2 = inflate$1;\nvar inflateRaw_1$1 = inflateRaw$1;\nvar ungzip$1 = inflate$1;\nvar constants = constants$2;\nvar inflate_1$1 = {\n Inflate: Inflate_1$1,\n inflate: inflate_2,\n inflateRaw: inflateRaw_1$1,\n ungzip: ungzip$1,\n constants: constants\n};\nconst {\n Deflate,\n deflate,\n deflateRaw,\n gzip\n} = deflate_1$1;\nconst {\n Inflate,\n inflate,\n inflateRaw,\n ungzip\n} = inflate_1$1;\nvar Deflate_1 = Deflate;\nvar deflate_1 = deflate;\nvar deflateRaw_1 = deflateRaw;\nvar gzip_1 = gzip;\nvar Inflate_1 = Inflate;\nvar inflate_1 = inflate;\nvar inflateRaw_1 = inflateRaw;\nvar ungzip_1 = ungzip;\nvar constants_1 = constants$2;\nvar pako = {\n Deflate: Deflate_1,\n deflate: deflate_1,\n deflateRaw: deflateRaw_1,\n gzip: gzip_1,\n Inflate: Inflate_1,\n inflate: inflate_1,\n inflateRaw: inflateRaw_1,\n ungzip: ungzip_1,\n constants: constants_1\n};\nexport { Deflate_1 as Deflate, Inflate_1 as Inflate, constants_1 as constants, pako as default, deflate_1 as deflate, deflateRaw_1 as deflateRaw, gzip_1 as gzip, inflate_1 as inflate, inflateRaw_1 as inflateRaw, ungzip_1 as ungzip };","import { Injectable } from '@angular/core';\nimport { from, of, throwError } from 'rxjs';\nimport { catchError, switchMap } from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class FileSaveService {\n\n constructor(private readonly http: HttpClient) {\n }\n\n saveFile(blob: Blob, suggestedName = 'UnknownFile', saveAs: boolean = true) {\n if (!('showSaveFilePicker' in window) || !saveAs) {\n const a = document.createElement('a');\n const objectUrl = URL.createObjectURL(blob);\n a.href = objectUrl;\n a.download = suggestedName;\n a.click();\n URL.revokeObjectURL(objectUrl);\n return of(null);\n }\n\n return from(\n (window as any).showSaveFilePicker({\n suggestedName\n })\n ).pipe(\n switchMap((handle: any) =>\n from(handle.createWritable()).pipe(\n switchMap((writable: any) =>\n from(writable.write(blob)).pipe(\n switchMap(() => from(writable.close()))\n )\n )\n )\n ),\n catchError(() => {\n return throwError(() => {});\n })\n );\n }\n\n public downloadMediaFromUrl(url: string) {\n return this.http.get(url, { responseType: 'blob' });\n }\n}\n","/**\n * marked v12.0.2 - a markdown parser\n * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)\n * https://github.com/markedjs/marked\n */\n\n/**\n * DO NOT EDIT THIS FILE\n * The code in this file is generated from files in ./src/\n */\n\n/**\n * Gets the original marked default options.\n */\nfunction _getDefaults() {\n return {\n async: false,\n breaks: false,\n extensions: null,\n gfm: true,\n hooks: null,\n pedantic: false,\n renderer: null,\n silent: false,\n tokenizer: null,\n walkTokens: null\n };\n}\nlet _defaults = _getDefaults();\nfunction changeDefaults(newDefaults) {\n _defaults = newDefaults;\n}\n\n/**\n * Helpers\n */\nconst escapeTest = /[&<>\"']/;\nconst escapeReplace = new RegExp(escapeTest.source, 'g');\nconst escapeTestNoEncode = /[<>\"']|&(?!(#\\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\\w+);)/;\nconst escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');\nconst escapeReplacements = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n};\nconst getEscapeReplacement = ch => escapeReplacements[ch];\nfunction escape$1(html, encode) {\n if (encode) {\n if (escapeTest.test(html)) {\n return html.replace(escapeReplace, getEscapeReplacement);\n }\n } else {\n if (escapeTestNoEncode.test(html)) {\n return html.replace(escapeReplaceNoEncode, getEscapeReplacement);\n }\n }\n return html;\n}\nconst unescapeTest = /&(#(?:\\d+)|(?:#x[0-9A-Fa-f]+)|(?:\\w+));?/ig;\nfunction unescape(html) {\n // explicitly match decimal, hex, and named HTML entities\n return html.replace(unescapeTest, (_, n) => {\n n = n.toLowerCase();\n if (n === 'colon') return ':';\n if (n.charAt(0) === '#') {\n return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));\n }\n return '';\n });\n}\nconst caret = /(^|[^\\[])\\^/g;\nfunction edit(regex, opt) {\n let source = typeof regex === 'string' ? regex : regex.source;\n opt = opt || '';\n const obj = {\n replace: (name, val) => {\n let valSource = typeof val === 'string' ? val : val.source;\n valSource = valSource.replace(caret, '$1');\n source = source.replace(name, valSource);\n return obj;\n },\n getRegex: () => {\n return new RegExp(source, opt);\n }\n };\n return obj;\n}\nfunction cleanUrl(href) {\n try {\n href = encodeURI(href).replace(/%25/g, '%');\n } catch (e) {\n return null;\n }\n return href;\n}\nconst noopTest = {\n exec: () => null\n};\nfunction splitCells(tableRow, count) {\n // ensure that every cell-delimiting pipe has a space\n // before it to distinguish it from an escaped pipe\n const row = tableRow.replace(/\\|/g, (match, offset, str) => {\n let escaped = false;\n let curr = offset;\n while (--curr >= 0 && str[curr] === '\\\\') escaped = !escaped;\n if (escaped) {\n // odd number of slashes means | is escaped\n // so we leave it alone\n return '|';\n } else {\n // add space before unescaped |\n return ' |';\n }\n }),\n cells = row.split(/ \\|/);\n let i = 0;\n // First/last cell in a row cannot be empty if it has no leading/trailing pipe\n if (!cells[0].trim()) {\n cells.shift();\n }\n if (cells.length > 0 && !cells[cells.length - 1].trim()) {\n cells.pop();\n }\n if (count) {\n if (cells.length > count) {\n cells.splice(count);\n } else {\n while (cells.length < count) cells.push('');\n }\n }\n for (; i < cells.length; i++) {\n // leading or trailing whitespace is ignored per the gfm spec\n cells[i] = cells[i].trim().replace(/\\\\\\|/g, '|');\n }\n return cells;\n}\n/**\n * Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').\n * /c*$/ is vulnerable to REDOS.\n *\n * @param str\n * @param c\n * @param invert Remove suffix of non-c chars instead. Default falsey.\n */\nfunction rtrim(str, c, invert) {\n const l = str.length;\n if (l === 0) {\n return '';\n }\n // Length of suffix matching the invert condition.\n let suffLen = 0;\n // Step left until we fail to match the invert condition.\n while (suffLen < l) {\n const currChar = str.charAt(l - suffLen - 1);\n if (currChar === c && !invert) {\n suffLen++;\n } else if (currChar !== c && invert) {\n suffLen++;\n } else {\n break;\n }\n }\n return str.slice(0, l - suffLen);\n}\nfunction findClosingBracket(str, b) {\n if (str.indexOf(b[1]) === -1) {\n return -1;\n }\n let level = 0;\n for (let i = 0; i < str.length; i++) {\n if (str[i] === '\\\\') {\n i++;\n } else if (str[i] === b[0]) {\n level++;\n } else if (str[i] === b[1]) {\n level--;\n if (level < 0) {\n return i;\n }\n }\n }\n return -1;\n}\nfunction outputLink(cap, link, raw, lexer) {\n const href = link.href;\n const title = link.title ? escape$1(link.title) : null;\n const text = cap[1].replace(/\\\\([\\[\\]])/g, '$1');\n if (cap[0].charAt(0) !== '!') {\n lexer.state.inLink = true;\n const token = {\n type: 'link',\n raw,\n href,\n title,\n text,\n tokens: lexer.inlineTokens(text)\n };\n lexer.state.inLink = false;\n return token;\n }\n return {\n type: 'image',\n raw,\n href,\n title,\n text: escape$1(text)\n };\n}\nfunction indentCodeCompensation(raw, text) {\n const matchIndentToCode = raw.match(/^(\\s+)(?:```)/);\n if (matchIndentToCode === null) {\n return text;\n }\n const indentToCode = matchIndentToCode[1];\n return text.split('\\n').map(node => {\n const matchIndentInNode = node.match(/^\\s+/);\n if (matchIndentInNode === null) {\n return node;\n }\n const [indentInNode] = matchIndentInNode;\n if (indentInNode.length >= indentToCode.length) {\n return node.slice(indentToCode.length);\n }\n return node;\n }).join('\\n');\n}\n/**\n * Tokenizer\n */\nclass _Tokenizer {\n options;\n rules; // set by the lexer\n lexer; // set by the lexer\n constructor(options) {\n this.options = options || _defaults;\n }\n space(src) {\n const cap = this.rules.block.newline.exec(src);\n if (cap && cap[0].length > 0) {\n return {\n type: 'space',\n raw: cap[0]\n };\n }\n }\n code(src) {\n const cap = this.rules.block.code.exec(src);\n if (cap) {\n const text = cap[0].replace(/^ {1,4}/gm, '');\n return {\n type: 'code',\n raw: cap[0],\n codeBlockStyle: 'indented',\n text: !this.options.pedantic ? rtrim(text, '\\n') : text\n };\n }\n }\n fences(src) {\n const cap = this.rules.block.fences.exec(src);\n if (cap) {\n const raw = cap[0];\n const text = indentCodeCompensation(raw, cap[3] || '');\n return {\n type: 'code',\n raw,\n lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2],\n text\n };\n }\n }\n heading(src) {\n const cap = this.rules.block.heading.exec(src);\n if (cap) {\n let text = cap[2].trim();\n // remove trailing #s\n if (/#$/.test(text)) {\n const trimmed = rtrim(text, '#');\n if (this.options.pedantic) {\n text = trimmed.trim();\n } else if (!trimmed || / $/.test(trimmed)) {\n // CommonMark requires space before trailing #s\n text = trimmed.trim();\n }\n }\n return {\n type: 'heading',\n raw: cap[0],\n depth: cap[1].length,\n text,\n tokens: this.lexer.inline(text)\n };\n }\n }\n hr(src) {\n const cap = this.rules.block.hr.exec(src);\n if (cap) {\n return {\n type: 'hr',\n raw: cap[0]\n };\n }\n }\n blockquote(src) {\n const cap = this.rules.block.blockquote.exec(src);\n if (cap) {\n // precede setext continuation with 4 spaces so it isn't a setext\n let text = cap[0].replace(/\\n {0,3}((?:=+|-+) *)(?=\\n|$)/g, '\\n $1');\n text = rtrim(text.replace(/^ *>[ \\t]?/gm, ''), '\\n');\n const top = this.lexer.state.top;\n this.lexer.state.top = true;\n const tokens = this.lexer.blockTokens(text);\n this.lexer.state.top = top;\n return {\n type: 'blockquote',\n raw: cap[0],\n tokens,\n text\n };\n }\n }\n list(src) {\n let cap = this.rules.block.list.exec(src);\n if (cap) {\n let bull = cap[1].trim();\n const isordered = bull.length > 1;\n const list = {\n type: 'list',\n raw: '',\n ordered: isordered,\n start: isordered ? +bull.slice(0, -1) : '',\n loose: false,\n items: []\n };\n bull = isordered ? `\\\\d{1,9}\\\\${bull.slice(-1)}` : `\\\\${bull}`;\n if (this.options.pedantic) {\n bull = isordered ? bull : '[*+-]';\n }\n // Get next list item\n const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\\t ][^\\\\n]*)?(?:\\\\n|$))`);\n let raw = '';\n let itemContents = '';\n let endsWithBlankLine = false;\n // Check if current bullet point can start a new List Item\n while (src) {\n let endEarly = false;\n if (!(cap = itemRegex.exec(src))) {\n break;\n }\n if (this.rules.block.hr.test(src)) {\n // End list if bullet was actually HR (possibly move into itemRegex?)\n break;\n }\n raw = cap[0];\n src = src.substring(raw.length);\n let line = cap[2].split('\\n', 1)[0].replace(/^\\t+/, t => ' '.repeat(3 * t.length));\n let nextLine = src.split('\\n', 1)[0];\n let indent = 0;\n if (this.options.pedantic) {\n indent = 2;\n itemContents = line.trimStart();\n } else {\n indent = cap[2].search(/[^ ]/); // Find first non-space char\n indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent\n itemContents = line.slice(indent);\n indent += cap[1].length;\n }\n let blankLine = false;\n if (!line && /^ *$/.test(nextLine)) {\n // Items begin with at most one blank line\n raw += nextLine + '\\n';\n src = src.substring(nextLine.length + 1);\n endEarly = true;\n }\n if (!endEarly) {\n const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\\\d{1,9}[.)])((?:[ \\t][^\\\\n]*)?(?:\\\\n|$))`);\n const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$)`);\n const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\\`\\`\\`|~~~)`);\n const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);\n // Check if following lines should be included in List Item\n while (src) {\n const rawLine = src.split('\\n', 1)[0];\n nextLine = rawLine;\n // Re-align to follow commonmark nesting rules\n if (this.options.pedantic) {\n nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');\n }\n // End list item if found code fences\n if (fencesBeginRegex.test(nextLine)) {\n break;\n }\n // End list item if found start of new heading\n if (headingBeginRegex.test(nextLine)) {\n break;\n }\n // End list item if found start of new bullet\n if (nextBulletRegex.test(nextLine)) {\n break;\n }\n // Horizontal rule found\n if (hrRegex.test(src)) {\n break;\n }\n if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) {\n // Dedent if possible\n itemContents += '\\n' + nextLine.slice(indent);\n } else {\n // not enough indentation\n if (blankLine) {\n break;\n }\n // paragraph continuation unless last line was a different block level element\n if (line.search(/[^ ]/) >= 4) {\n // indented code block\n break;\n }\n if (fencesBeginRegex.test(line)) {\n break;\n }\n if (headingBeginRegex.test(line)) {\n break;\n }\n if (hrRegex.test(line)) {\n break;\n }\n itemContents += '\\n' + nextLine;\n }\n if (!blankLine && !nextLine.trim()) {\n // Check if current line is blank\n blankLine = true;\n }\n raw += rawLine + '\\n';\n src = src.substring(rawLine.length + 1);\n line = nextLine.slice(indent);\n }\n }\n if (!list.loose) {\n // If the previous item ended with a blank line, the list is loose\n if (endsWithBlankLine) {\n list.loose = true;\n } else if (/\\n *\\n *$/.test(raw)) {\n endsWithBlankLine = true;\n }\n }\n let istask = null;\n let ischecked;\n // Check for task list items\n if (this.options.gfm) {\n istask = /^\\[[ xX]\\] /.exec(itemContents);\n if (istask) {\n ischecked = istask[0] !== '[ ] ';\n itemContents = itemContents.replace(/^\\[[ xX]\\] +/, '');\n }\n }\n list.items.push({\n type: 'list_item',\n raw,\n task: !!istask,\n checked: ischecked,\n loose: false,\n text: itemContents,\n tokens: []\n });\n list.raw += raw;\n }\n // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic\n list.items[list.items.length - 1].raw = raw.trimEnd();\n list.items[list.items.length - 1].text = itemContents.trimEnd();\n list.raw = list.raw.trimEnd();\n // Item child tokens handled here at end because we needed to have the final item to trim it first\n for (let i = 0; i < list.items.length; i++) {\n this.lexer.state.top = false;\n list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);\n if (!list.loose) {\n // Check if list should be loose\n const spacers = list.items[i].tokens.filter(t => t.type === 'space');\n const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\\n.*\\n/.test(t.raw));\n list.loose = hasMultipleLineBreaks;\n }\n }\n // Set all items to loose if list is loose\n if (list.loose) {\n for (let i = 0; i < list.items.length; i++) {\n list.items[i].loose = true;\n }\n }\n return list;\n }\n }\n html(src) {\n const cap = this.rules.block.html.exec(src);\n if (cap) {\n const token = {\n type: 'html',\n block: true,\n raw: cap[0],\n pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',\n text: cap[0]\n };\n return token;\n }\n }\n def(src) {\n const cap = this.rules.block.def.exec(src);\n if (cap) {\n const tag = cap[1].toLowerCase().replace(/\\s+/g, ' ');\n const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';\n const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];\n return {\n type: 'def',\n tag,\n raw: cap[0],\n href,\n title\n };\n }\n }\n table(src) {\n const cap = this.rules.block.table.exec(src);\n if (!cap) {\n return;\n }\n if (!/[:|]/.test(cap[2])) {\n // delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading\n return;\n }\n const headers = splitCells(cap[1]);\n const aligns = cap[2].replace(/^\\||\\| *$/g, '').split('|');\n const rows = cap[3] && cap[3].trim() ? cap[3].replace(/\\n[ \\t]*$/, '').split('\\n') : [];\n const item = {\n type: 'table',\n raw: cap[0],\n header: [],\n align: [],\n rows: []\n };\n if (headers.length !== aligns.length) {\n // header and align columns must be equal, rows can be different.\n return;\n }\n for (const align of aligns) {\n if (/^ *-+: *$/.test(align)) {\n item.align.push('right');\n } else if (/^ *:-+: *$/.test(align)) {\n item.align.push('center');\n } else if (/^ *:-+ *$/.test(align)) {\n item.align.push('left');\n } else {\n item.align.push(null);\n }\n }\n for (const header of headers) {\n item.header.push({\n text: header,\n tokens: this.lexer.inline(header)\n });\n }\n for (const row of rows) {\n item.rows.push(splitCells(row, item.header.length).map(cell => {\n return {\n text: cell,\n tokens: this.lexer.inline(cell)\n };\n }));\n }\n return item;\n }\n lheading(src) {\n const cap = this.rules.block.lheading.exec(src);\n if (cap) {\n return {\n type: 'heading',\n raw: cap[0],\n depth: cap[2].charAt(0) === '=' ? 1 : 2,\n text: cap[1],\n tokens: this.lexer.inline(cap[1])\n };\n }\n }\n paragraph(src) {\n const cap = this.rules.block.paragraph.exec(src);\n if (cap) {\n const text = cap[1].charAt(cap[1].length - 1) === '\\n' ? cap[1].slice(0, -1) : cap[1];\n return {\n type: 'paragraph',\n raw: cap[0],\n text,\n tokens: this.lexer.inline(text)\n };\n }\n }\n text(src) {\n const cap = this.rules.block.text.exec(src);\n if (cap) {\n return {\n type: 'text',\n raw: cap[0],\n text: cap[0],\n tokens: this.lexer.inline(cap[0])\n };\n }\n }\n escape(src) {\n const cap = this.rules.inline.escape.exec(src);\n if (cap) {\n return {\n type: 'escape',\n raw: cap[0],\n text: escape$1(cap[1])\n };\n }\n }\n tag(src) {\n const cap = this.rules.inline.tag.exec(src);\n if (cap) {\n if (!this.lexer.state.inLink && /^/i.test(cap[0])) {\n this.lexer.state.inLink = false;\n }\n if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\\s|>)/i.test(cap[0])) {\n this.lexer.state.inRawBlock = true;\n } else if (this.lexer.state.inRawBlock && /^<\\/(pre|code|kbd|script)(\\s|>)/i.test(cap[0])) {\n this.lexer.state.inRawBlock = false;\n }\n return {\n type: 'html',\n raw: cap[0],\n inLink: this.lexer.state.inLink,\n inRawBlock: this.lexer.state.inRawBlock,\n block: false,\n text: cap[0]\n };\n }\n }\n link(src) {\n const cap = this.rules.inline.link.exec(src);\n if (cap) {\n const trimmedUrl = cap[2].trim();\n if (!this.options.pedantic && /^$/.test(trimmedUrl)) {\n return;\n }\n // ending angle bracket cannot be escaped\n const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\\\');\n if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {\n return;\n }\n } else {\n // find closing parenthesis\n const lastParenIndex = findClosingBracket(cap[2], '()');\n if (lastParenIndex > -1) {\n const start = cap[0].indexOf('!') === 0 ? 5 : 4;\n const linkLen = start + cap[1].length + lastParenIndex;\n cap[2] = cap[2].substring(0, lastParenIndex);\n cap[0] = cap[0].substring(0, linkLen).trim();\n cap[3] = '';\n }\n }\n let href = cap[2];\n let title = '';\n if (this.options.pedantic) {\n // split pedantic href and title\n const link = /^([^'\"]*[^\\s])\\s+(['\"])(.*)\\2/.exec(href);\n if (link) {\n href = link[1];\n title = link[3];\n }\n } else {\n title = cap[3] ? cap[3].slice(1, -1) : '';\n }\n href = href.trim();\n if (/^$/.test(trimmedUrl)) {\n // pedantic allows starting angle bracket without ending angle bracket\n href = href.slice(1);\n } else {\n href = href.slice(1, -1);\n }\n }\n return outputLink(cap, {\n href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,\n title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title\n }, cap[0], this.lexer);\n }\n }\n reflink(src, links) {\n let cap;\n if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {\n const linkString = (cap[2] || cap[1]).replace(/\\s+/g, ' ');\n const link = links[linkString.toLowerCase()];\n if (!link) {\n const text = cap[0].charAt(0);\n return {\n type: 'text',\n raw: text,\n text\n };\n }\n return outputLink(cap, link, cap[0], this.lexer);\n }\n }\n emStrong(src, maskedSrc, prevChar = '') {\n let match = this.rules.inline.emStrongLDelim.exec(src);\n if (!match) return;\n // _ can't be between two alphanumerics. \\p{L}\\p{N} includes non-english alphabet/numbers as well\n if (match[3] && prevChar.match(/[\\p{L}\\p{N}]/u)) return;\n const nextChar = match[1] || match[2] || '';\n if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {\n // unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)\n const lLength = [...match[0]].length - 1;\n let rDelim,\n rLength,\n delimTotal = lLength,\n midDelimTotal = 0;\n const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;\n endReg.lastIndex = 0;\n // Clip maskedSrc to same section of string as src (move to lexer?)\n maskedSrc = maskedSrc.slice(-1 * src.length + lLength);\n while ((match = endReg.exec(maskedSrc)) != null) {\n rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];\n if (!rDelim) continue; // skip single * in __abc*abc__\n rLength = [...rDelim].length;\n if (match[3] || match[4]) {\n // found another Left Delim\n delimTotal += rLength;\n continue;\n } else if (match[5] || match[6]) {\n // either Left or Right Delim\n if (lLength % 3 && !((lLength + rLength) % 3)) {\n midDelimTotal += rLength;\n continue; // CommonMark Emphasis Rules 9-10\n }\n }\n delimTotal -= rLength;\n if (delimTotal > 0) continue; // Haven't found enough closing delimiters\n // Remove extra characters. *a*** -> *a*\n rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);\n // char length can be >1 for unicode characters;\n const lastCharLength = [...match[0]][0].length;\n const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);\n // Create `em` if smallest delimiter has odd char count. *a***\n if (Math.min(lLength, rLength) % 2) {\n const text = raw.slice(1, -1);\n return {\n type: 'em',\n raw,\n text,\n tokens: this.lexer.inlineTokens(text)\n };\n }\n // Create 'strong' if smallest delimiter has even char count. **a***\n const text = raw.slice(2, -2);\n return {\n type: 'strong',\n raw,\n text,\n tokens: this.lexer.inlineTokens(text)\n };\n }\n }\n }\n codespan(src) {\n const cap = this.rules.inline.code.exec(src);\n if (cap) {\n let text = cap[2].replace(/\\n/g, ' ');\n const hasNonSpaceChars = /[^ ]/.test(text);\n const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);\n if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {\n text = text.substring(1, text.length - 1);\n }\n text = escape$1(text, true);\n return {\n type: 'codespan',\n raw: cap[0],\n text\n };\n }\n }\n br(src) {\n const cap = this.rules.inline.br.exec(src);\n if (cap) {\n return {\n type: 'br',\n raw: cap[0]\n };\n }\n }\n del(src) {\n const cap = this.rules.inline.del.exec(src);\n if (cap) {\n return {\n type: 'del',\n raw: cap[0],\n text: cap[2],\n tokens: this.lexer.inlineTokens(cap[2])\n };\n }\n }\n autolink(src) {\n const cap = this.rules.inline.autolink.exec(src);\n if (cap) {\n let text, href;\n if (cap[2] === '@') {\n text = escape$1(cap[1]);\n href = 'mailto:' + text;\n } else {\n text = escape$1(cap[1]);\n href = text;\n }\n return {\n type: 'link',\n raw: cap[0],\n text,\n href,\n tokens: [{\n type: 'text',\n raw: text,\n text\n }]\n };\n }\n }\n url(src) {\n let cap;\n if (cap = this.rules.inline.url.exec(src)) {\n let text, href;\n if (cap[2] === '@') {\n text = escape$1(cap[0]);\n href = 'mailto:' + text;\n } else {\n // do extended autolink path validation\n let prevCapZero;\n do {\n prevCapZero = cap[0];\n cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';\n } while (prevCapZero !== cap[0]);\n text = escape$1(cap[0]);\n if (cap[1] === 'www.') {\n href = 'http://' + cap[0];\n } else {\n href = cap[0];\n }\n }\n return {\n type: 'link',\n raw: cap[0],\n text,\n href,\n tokens: [{\n type: 'text',\n raw: text,\n text\n }]\n };\n }\n }\n inlineText(src) {\n const cap = this.rules.inline.text.exec(src);\n if (cap) {\n let text;\n if (this.lexer.state.inRawBlock) {\n text = cap[0];\n } else {\n text = escape$1(cap[0]);\n }\n return {\n type: 'text',\n raw: cap[0],\n text\n };\n }\n }\n}\n\n/**\n * Block-Level Grammar\n */\nconst newline = /^(?: *(?:\\n|$))+/;\nconst blockCode = /^( {4}[^\\n]+(?:\\n(?: *(?:\\n|$))*)?)+/;\nconst fences = /^ {0,3}(`{3,}(?=[^`\\n]*(?:\\n|$))|~{3,})([^\\n]*)(?:\\n|$)(?:|([\\s\\S]*?)(?:\\n|$))(?: {0,3}\\1[~`]* *(?=\\n|$)|$)/;\nconst hr = /^ {0,3}((?:-[\\t ]*){3,}|(?:_[ \\t]*){3,}|(?:\\*[ \\t]*){3,})(?:\\n+|$)/;\nconst heading = /^ {0,3}(#{1,6})(?=\\s|$)(.*)(?:\\n+|$)/;\nconst bullet = /(?:[*+-]|\\d{1,9}[.)])/;\nconst lheading = edit(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\\n(?!\\s*?\\n|bull |blockCode|fences|blockquote|heading|html))+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/).replace(/bull/g, bullet) // lists can interrupt\n.replace(/blockCode/g, / {4}/) // indented code blocks can interrupt\n.replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt\n.replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt\n.replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt\n.replace(/html/g, / {0,3}<[^\\n>]+>\\n/) // block html can interrupt\n.getRegex();\nconst _paragraph = /^([^\\n]+(?:\\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\\n)[^\\n]+)*)/;\nconst blockText = /^[^\\n]+/;\nconst _blockLabel = /(?!\\s*\\])(?:\\\\.|[^\\[\\]\\\\])+/;\nconst def = edit(/^ {0,3}\\[(label)\\]: *(?:\\n *)?([^<\\s][^\\s]*|<.*?>)(?:(?: +(?:\\n *)?| *\\n *)(title))? *(?:\\n+|$)/).replace('label', _blockLabel).replace('title', /(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/).getRegex();\nconst list = edit(/^( {0,3}bull)([ \\t][^\\n]+?)?(?:\\n|$)/).replace(/bull/g, bullet).getRegex();\nconst _tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title' + '|tr|track|ul';\nconst _comment = /|$))/;\nconst html = edit('^ {0,3}(?:' // optional indentation\n+ '<(script|pre|style|textarea)[\\\\s>][\\\\s\\\\S]*?(?:[^\\\\n]*\\\\n+|$)' // (1)\n+ '|comment[^\\\\n]*(\\\\n+|$)' // (2)\n+ '|<\\\\?[\\\\s\\\\S]*?(?:\\\\?>\\\\n*|$)' // (3)\n+ '|\\\\n*|$)' // (4)\n+ '|\\\\n*|$)' // (5)\n+ '|)[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (6)\n+ '|<(?!script|pre|style|textarea)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (7) open tag\n+ '|(?=[ \\\\t]*(?:\\\\n|$))[\\\\s\\\\S]*?(?:(?:\\\\n *)+\\\\n|$)' // (7) closing tag\n+ ')', 'i').replace('comment', _comment).replace('tag', _tag).replace('attribute', / +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/).getRegex();\nconst paragraph = edit(_paragraph).replace('hr', hr).replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)').replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs\n.replace('|table', '').replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n.replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', _tag) // pars can be interrupted by type (6) html blocks\n.getRegex();\nconst blockquote = edit(/^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/).replace('paragraph', paragraph).getRegex();\n/**\n * Normal Block Grammar\n */\nconst blockNormal = {\n blockquote,\n code: blockCode,\n def,\n fences,\n heading,\n hr,\n html,\n lheading,\n list,\n newline,\n paragraph,\n table: noopTest,\n text: blockText\n};\n/**\n * GFM Block Grammar\n */\nconst gfmTable = edit('^ *([^\\\\n ].*)\\\\n' // Header\n+ ' {0,3}((?:\\\\| *)?:?-+:? *(?:\\\\| *:?-+:? *)*(?:\\\\| *)?)' // Align\n+ '(?:\\\\n((?:(?! *\\\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\\\n|$))*)\\\\n*|$)') // Cells\n.replace('hr', hr).replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n.replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', _tag) // tables can be interrupted by type (6) html blocks\n.getRegex();\nconst blockGfm = {\n ...blockNormal,\n table: gfmTable,\n paragraph: edit(_paragraph).replace('hr', hr).replace('heading', ' {0,3}#{1,6}(?:\\\\s|$)').replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs\n .replace('table', gfmTable) // interrupt paragraphs with table\n .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\\\n]*\\\\n)|~{3,})[^\\\\n]*\\\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt\n .replace('html', ')|<(?:script|pre|style|textarea|!--)').replace('tag', _tag) // pars can be interrupted by type (6) html blocks\n .getRegex()\n};\n/**\n * Pedantic grammar (original John Gruber's loose markdown specification)\n */\nconst blockPedantic = {\n ...blockNormal,\n html: edit('^ *(?:comment *(?:\\\\n|\\\\s*$)' + '|<(tag)[\\\\s\\\\S]+? *(?:\\\\n{2,}|\\\\s*$)' // closed tag\n + '|\\\\s]*)*?/?> *(?:\\\\n{2,}|\\\\s*$))').replace('comment', _comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\\\b)\\\\w+(?!:|[^\\\\w\\\\s@]*@)\\\\b').getRegex(),\n def: /^ *\\[([^\\]]+)\\]: *]+)>?(?: +([\"(][^\\n]+[\")]))? *(?:\\n+|$)/,\n heading: /^(#{1,6})(.*)(?:\\n+|$)/,\n fences: noopTest,\n // fences not supported\n lheading: /^(.+?)\\n {0,3}(=+|-+) *(?:\\n+|$)/,\n paragraph: edit(_paragraph).replace('hr', hr).replace('heading', ' *#{1,6} *[^\\n]').replace('lheading', lheading).replace('|table', '').replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').replace('|tag', '').getRegex()\n};\n/**\n * Inline-Level Grammar\n */\nconst escape = /^\\\\([!\"#$%&'()*+,\\-./:;<=>?@\\[\\]\\\\^_`{|}~])/;\nconst inlineCode = /^(`+)([^`]|[^`][\\s\\S]*?[^`])\\1(?!`)/;\nconst br = /^( {2,}|\\\\)\\n(?!\\s*$)/;\nconst inlineText = /^(`+|[^`])(?:(?= {2,}\\n)|[\\s\\S]*?(?:(?=[\\\\\nconst blockSkip = /\\[[^[\\]]*?\\]\\([^\\(\\)]*?\\)|`[^`]*?`|<[^<>]*?>/g;\nconst emStrongLDelim = edit(/^(?:\\*+(?:((?!\\*)[punct])|[^\\s*]))|^_+(?:((?!_)[punct])|([^\\s_]))/, 'u').replace(/punct/g, _punctuation).getRegex();\nconst emStrongRDelimAst = edit('^[^_*]*?__[^_*]*?\\\\*[^_*]*?(?=__)' // Skip orphan inside strong\n+ '|[^*]+(?=[^*])' // Consume to delim\n+ '|(?!\\\\*)[punct](\\\\*+)(?=[\\\\s]|$)' // (1) #*** can only be a Right Delimiter\n+ '|[^punct\\\\s](\\\\*+)(?!\\\\*)(?=[punct\\\\s]|$)' // (2) a***#, a*** can only be a Right Delimiter\n+ '|(?!\\\\*)[punct\\\\s](\\\\*+)(?=[^punct\\\\s])' // (3) #***a, ***a can only be Left Delimiter\n+ '|[\\\\s](\\\\*+)(?!\\\\*)(?=[punct])' // (4) ***# can only be Left Delimiter\n+ '|(?!\\\\*)[punct](\\\\*+)(?!\\\\*)(?=[punct])' // (5) #***# can be either Left or Right Delimiter\n+ '|[^punct\\\\s](\\\\*+)(?=[^punct\\\\s])', 'gu') // (6) a***a can be either Left or Right Delimiter\n.replace(/punct/g, _punctuation).getRegex();\n// (6) Not allowed for _\nconst emStrongRDelimUnd = edit('^[^_*]*?\\\\*\\\\*[^_*]*?_[^_*]*?(?=\\\\*\\\\*)' // Skip orphan inside strong\n+ '|[^_]+(?=[^_])' // Consume to delim\n+ '|(?!_)[punct](_+)(?=[\\\\s]|$)' // (1) #___ can only be a Right Delimiter\n+ '|[^punct\\\\s](_+)(?!_)(?=[punct\\\\s]|$)' // (2) a___#, a___ can only be a Right Delimiter\n+ '|(?!_)[punct\\\\s](_+)(?=[^punct\\\\s])' // (3) #___a, ___a can only be Left Delimiter\n+ '|[\\\\s](_+)(?!_)(?=[punct])' // (4) ___# can only be Left Delimiter\n+ '|(?!_)[punct](_+)(?!_)(?=[punct])', 'gu') // (5) #___# can be either Left or Right Delimiter\n.replace(/punct/g, _punctuation).getRegex();\nconst anyPunctuation = edit(/\\\\([punct])/, 'gu').replace(/punct/g, _punctuation).getRegex();\nconst autolink = edit(/^<(scheme:[^\\s\\x00-\\x1f<>]*|email)>/).replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex();\nconst _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();\nconst tag = edit('^comment' + '|^' // self-closing tag\n+ '|^<[a-zA-Z][\\\\w-]*(?:attribute)*?\\\\s*/?>' // open tag\n+ '|^<\\\\?[\\\\s\\\\S]*?\\\\?>' // processing instruction, e.g. \n+ '|^' // declaration, e.g. \n+ '|^') // CDATA section\n.replace('comment', _inlineComment).replace('attribute', /\\s+[a-zA-Z:_][\\w.:-]*(?:\\s*=\\s*\"[^\"]*\"|\\s*=\\s*'[^']*'|\\s*=\\s*[^\\s\"'=<>`]+)?/).getRegex();\nconst _inlineLabel = /(?:\\[(?:\\\\.|[^\\[\\]\\\\])*\\]|\\\\.|`[^`]*`|[^\\[\\]\\\\`])*?/;\nconst link = edit(/^!?\\[(label)\\]\\(\\s*(href)(?:\\s+(title))?\\s*\\)/).replace('label', _inlineLabel).replace('href', /<(?:\\\\.|[^\\n<>\\\\])+>|[^\\s\\x00-\\x1f]*/).replace('title', /\"(?:\\\\\"?|[^\"\\\\])*\"|'(?:\\\\'?|[^'\\\\])*'|\\((?:\\\\\\)?|[^)\\\\])*\\)/).getRegex();\nconst reflink = edit(/^!?\\[(label)\\]\\[(ref)\\]/).replace('label', _inlineLabel).replace('ref', _blockLabel).getRegex();\nconst nolink = edit(/^!?\\[(ref)\\](?:\\[\\])?/).replace('ref', _blockLabel).getRegex();\nconst reflinkSearch = edit('reflink|nolink(?!\\\\()', 'g').replace('reflink', reflink).replace('nolink', nolink).getRegex();\n/**\n * Normal Inline Grammar\n */\nconst inlineNormal = {\n _backpedal: noopTest,\n // only used for GFM url\n anyPunctuation,\n autolink,\n blockSkip,\n br,\n code: inlineCode,\n del: noopTest,\n emStrongLDelim,\n emStrongRDelimAst,\n emStrongRDelimUnd,\n escape,\n link,\n nolink,\n punctuation,\n reflink,\n reflinkSearch,\n tag,\n text: inlineText,\n url: noopTest\n};\n/**\n * Pedantic Inline Grammar\n */\nconst inlinePedantic = {\n ...inlineNormal,\n link: edit(/^!?\\[(label)\\]\\((.*?)\\)/).replace('label', _inlineLabel).getRegex(),\n reflink: edit(/^!?\\[(label)\\]\\s*\\[([^\\]]*)\\]/).replace('label', _inlineLabel).getRegex()\n};\n/**\n * GFM Inline Grammar\n */\nconst inlineGfm = {\n ...inlineNormal,\n escape: edit(escape).replace('])', '~|])').getRegex(),\n url: edit(/^((?:ftp|https?):\\/\\/|www\\.)(?:[a-zA-Z0-9\\-]+\\.?)+[^\\s<]*|^email/, 'i').replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),\n _backpedal: /(?:[^?!.,:;*_'\"~()&]+|\\([^)]*\\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'\"~)]+(?!$))+/,\n del: /^(~~?)(?=[^\\s~])([\\s\\S]*?[^\\s~])\\1(?=[^~]|$)/,\n text: /^([`~]+|[^`~])(?:(?= {2,}\\n)|(?=[a-zA-Z0-9.!#$%&'*+\\/=?_`{\\|}~-]+@)|[\\s\\S]*?(?:(?=[\\\\ {\n return leading + ' '.repeat(tabs.length);\n });\n }\n let token;\n let lastToken;\n let cutSrc;\n let lastParagraphClipped;\n while (src) {\n if (this.options.extensions && this.options.extensions.block && this.options.extensions.block.some(extTokenizer => {\n if (token = extTokenizer.call({\n lexer: this\n }, src, tokens)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n return true;\n }\n return false;\n })) {\n continue;\n }\n // newline\n if (token = this.tokenizer.space(src)) {\n src = src.substring(token.raw.length);\n if (token.raw.length === 1 && tokens.length > 0) {\n // if there's a single \\n as a spacer, it's terminating the last line,\n // so move it there so that we don't get unnecessary paragraph tags\n tokens[tokens.length - 1].raw += '\\n';\n } else {\n tokens.push(token);\n }\n continue;\n }\n // code\n if (token = this.tokenizer.code(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n // An indented code block cannot interrupt a paragraph.\n if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n } else {\n tokens.push(token);\n }\n continue;\n }\n // fences\n if (token = this.tokenizer.fences(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // heading\n if (token = this.tokenizer.heading(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // hr\n if (token = this.tokenizer.hr(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // blockquote\n if (token = this.tokenizer.blockquote(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // list\n if (token = this.tokenizer.list(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // html\n if (token = this.tokenizer.html(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // def\n if (token = this.tokenizer.def(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.raw;\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n } else if (!this.tokens.links[token.tag]) {\n this.tokens.links[token.tag] = {\n href: token.href,\n title: token.title\n };\n }\n continue;\n }\n // table (gfm)\n if (token = this.tokenizer.table(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // lheading\n if (token = this.tokenizer.lheading(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // top-level paragraph\n // prevent paragraph consuming extensions by clipping 'src' to extension start\n cutSrc = src;\n if (this.options.extensions && this.options.extensions.startBlock) {\n let startIndex = Infinity;\n const tempSrc = src.slice(1);\n let tempStart;\n this.options.extensions.startBlock.forEach(getStartIndex => {\n tempStart = getStartIndex.call({\n lexer: this\n }, tempSrc);\n if (typeof tempStart === 'number' && tempStart >= 0) {\n startIndex = Math.min(startIndex, tempStart);\n }\n });\n if (startIndex < Infinity && startIndex >= 0) {\n cutSrc = src.substring(0, startIndex + 1);\n }\n }\n if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {\n lastToken = tokens[tokens.length - 1];\n if (lastParagraphClipped && lastToken.type === 'paragraph') {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue.pop();\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n } else {\n tokens.push(token);\n }\n lastParagraphClipped = cutSrc.length !== src.length;\n src = src.substring(token.raw.length);\n continue;\n }\n // text\n if (token = this.tokenizer.text(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && lastToken.type === 'text') {\n lastToken.raw += '\\n' + token.raw;\n lastToken.text += '\\n' + token.text;\n this.inlineQueue.pop();\n this.inlineQueue[this.inlineQueue.length - 1].src = lastToken.text;\n } else {\n tokens.push(token);\n }\n continue;\n }\n if (src) {\n const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);\n if (this.options.silent) {\n console.error(errMsg);\n break;\n } else {\n throw new Error(errMsg);\n }\n }\n }\n this.state.top = true;\n return tokens;\n }\n inline(src, tokens = []) {\n this.inlineQueue.push({\n src,\n tokens\n });\n return tokens;\n }\n /**\n * Lexing/Compiling\n */\n inlineTokens(src, tokens = []) {\n let token, lastToken, cutSrc;\n // String with links masked to avoid interference with em and strong\n let maskedSrc = src;\n let match;\n let keepPrevChar, prevChar;\n // Mask out reflinks\n if (this.tokens.links) {\n const links = Object.keys(this.tokens.links);\n if (links.length > 0) {\n while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {\n if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {\n maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);\n }\n }\n }\n }\n // Mask out other blocks\n while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {\n maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);\n }\n // Mask out escaped characters\n while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {\n maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);\n }\n while (src) {\n if (!keepPrevChar) {\n prevChar = '';\n }\n keepPrevChar = false;\n // extensions\n if (this.options.extensions && this.options.extensions.inline && this.options.extensions.inline.some(extTokenizer => {\n if (token = extTokenizer.call({\n lexer: this\n }, src, tokens)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n return true;\n }\n return false;\n })) {\n continue;\n }\n // escape\n if (token = this.tokenizer.escape(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // tag\n if (token = this.tokenizer.tag(src)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && token.type === 'text' && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n } else {\n tokens.push(token);\n }\n continue;\n }\n // link\n if (token = this.tokenizer.link(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // reflink, nolink\n if (token = this.tokenizer.reflink(src, this.tokens.links)) {\n src = src.substring(token.raw.length);\n lastToken = tokens[tokens.length - 1];\n if (lastToken && token.type === 'text' && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n } else {\n tokens.push(token);\n }\n continue;\n }\n // em & strong\n if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // code\n if (token = this.tokenizer.codespan(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // br\n if (token = this.tokenizer.br(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // del (gfm)\n if (token = this.tokenizer.del(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // autolink\n if (token = this.tokenizer.autolink(src)) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // url (gfm)\n if (!this.state.inLink && (token = this.tokenizer.url(src))) {\n src = src.substring(token.raw.length);\n tokens.push(token);\n continue;\n }\n // text\n // prevent inlineText consuming extensions by clipping 'src' to extension start\n cutSrc = src;\n if (this.options.extensions && this.options.extensions.startInline) {\n let startIndex = Infinity;\n const tempSrc = src.slice(1);\n let tempStart;\n this.options.extensions.startInline.forEach(getStartIndex => {\n tempStart = getStartIndex.call({\n lexer: this\n }, tempSrc);\n if (typeof tempStart === 'number' && tempStart >= 0) {\n startIndex = Math.min(startIndex, tempStart);\n }\n });\n if (startIndex < Infinity && startIndex >= 0) {\n cutSrc = src.substring(0, startIndex + 1);\n }\n }\n if (token = this.tokenizer.inlineText(cutSrc)) {\n src = src.substring(token.raw.length);\n if (token.raw.slice(-1) !== '_') {\n // Track prevChar before string of ____ started\n prevChar = token.raw.slice(-1);\n }\n keepPrevChar = true;\n lastToken = tokens[tokens.length - 1];\n if (lastToken && lastToken.type === 'text') {\n lastToken.raw += token.raw;\n lastToken.text += token.text;\n } else {\n tokens.push(token);\n }\n continue;\n }\n if (src) {\n const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);\n if (this.options.silent) {\n console.error(errMsg);\n break;\n } else {\n throw new Error(errMsg);\n }\n }\n }\n return tokens;\n }\n}\n\n/**\n * Renderer\n */\nclass _Renderer {\n options;\n constructor(options) {\n this.options = options || _defaults;\n }\n code(code, infostring, escaped) {\n const lang = (infostring || '').match(/^\\S*/)?.[0];\n code = code.replace(/\\n$/, '') + '\\n';\n if (!lang) {\n return '
' + (escaped ? code : escape$1(code, true)) + '
\\n';\n }\n return '
' + (escaped ? code : escape$1(code, true)) + '
\\n';\n }\n blockquote(quote) {\n return `
\\n${quote}
\\n`;\n }\n html(html, block) {\n return html;\n }\n heading(text, level, raw) {\n // ignore IDs\n return `${text}\\n`;\n }\n hr() {\n return '
\\n';\n }\n list(body, ordered, start) {\n const type = ordered ? 'ol' : 'ul';\n const startatt = ordered && start !== 1 ? ' start=\"' + start + '\"' : '';\n return '<' + type + startatt + '>\\n' + body + '\\n';\n }\n listitem(text, task, checked) {\n return `
  • ${text}
  • \\n`;\n }\n checkbox(checked) {\n return '';\n }\n paragraph(text) {\n return `

    ${text}

    \\n`;\n }\n table(header, body) {\n if (body) body = `${body}`;\n return '\\n' + '\\n' + header + '\\n' + body + '
    \\n';\n }\n tablerow(content) {\n return `\\n${content}\\n`;\n }\n tablecell(content, flags) {\n const type = flags.header ? 'th' : 'td';\n const tag = flags.align ? `<${type} align=\"${flags.align}\">` : `<${type}>`;\n return tag + content + `\\n`;\n }\n /**\n * span level renderer\n */\n strong(text) {\n return `${text}`;\n }\n em(text) {\n return `${text}`;\n }\n codespan(text) {\n return `${text}`;\n }\n br() {\n return '
    ';\n }\n del(text) {\n return `${text}`;\n }\n link(href, title, text) {\n const cleanHref = cleanUrl(href);\n if (cleanHref === null) {\n return text;\n }\n href = cleanHref;\n let out = '
    ';\n return out;\n }\n image(href, title, text) {\n const cleanHref = cleanUrl(href);\n if (cleanHref === null) {\n return text;\n }\n href = cleanHref;\n let out = `\"${text}\"`;\n 0 && item.tokens[0].type === 'paragraph') {\n item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;\n if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {\n item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;\n }\n } else {\n item.tokens.unshift({\n type: 'text',\n text: checkbox + ' '\n });\n }\n } else {\n itemBody += checkbox + ' ';\n }\n }\n itemBody += this.parse(item.tokens, loose);\n body += this.renderer.listitem(itemBody, task, !!checked);\n }\n out += this.renderer.list(body, ordered, start);\n continue;\n }\n case 'html':\n {\n const htmlToken = token;\n out += this.renderer.html(htmlToken.text, htmlToken.block);\n continue;\n }\n case 'paragraph':\n {\n const paragraphToken = token;\n out += this.renderer.paragraph(this.parseInline(paragraphToken.tokens));\n continue;\n }\n case 'text':\n {\n let textToken = token;\n let body = textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text;\n while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {\n textToken = tokens[++i];\n body += '\\n' + (textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text);\n }\n out += top ? this.renderer.paragraph(body) : body;\n continue;\n }\n default:\n {\n const errMsg = 'Token with \"' + token.type + '\" type was not found.';\n if (this.options.silent) {\n console.error(errMsg);\n return '';\n } else {\n throw new Error(errMsg);\n }\n }\n }\n }\n return out;\n }\n /**\n * Parse Inline Tokens\n */\n parseInline(tokens, renderer) {\n renderer = renderer || this.renderer;\n let out = '';\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i];\n // Run any renderer extensions\n if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {\n const ret = this.options.extensions.renderers[token.type].call({\n parser: this\n }, token);\n if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {\n out += ret || '';\n continue;\n }\n }\n switch (token.type) {\n case 'escape':\n {\n const escapeToken = token;\n out += renderer.text(escapeToken.text);\n break;\n }\n case 'html':\n {\n const tagToken = token;\n out += renderer.html(tagToken.text);\n break;\n }\n case 'link':\n {\n const linkToken = token;\n out += renderer.link(linkToken.href, linkToken.title, this.parseInline(linkToken.tokens, renderer));\n break;\n }\n case 'image':\n {\n const imageToken = token;\n out += renderer.image(imageToken.href, imageToken.title, imageToken.text);\n break;\n }\n case 'strong':\n {\n const strongToken = token;\n out += renderer.strong(this.parseInline(strongToken.tokens, renderer));\n break;\n }\n case 'em':\n {\n const emToken = token;\n out += renderer.em(this.parseInline(emToken.tokens, renderer));\n break;\n }\n case 'codespan':\n {\n const codespanToken = token;\n out += renderer.codespan(codespanToken.text);\n break;\n }\n case 'br':\n {\n out += renderer.br();\n break;\n }\n case 'del':\n {\n const delToken = token;\n out += renderer.del(this.parseInline(delToken.tokens, renderer));\n break;\n }\n case 'text':\n {\n const textToken = token;\n out += renderer.text(textToken.text);\n break;\n }\n default:\n {\n const errMsg = 'Token with \"' + token.type + '\" type was not found.';\n if (this.options.silent) {\n console.error(errMsg);\n return '';\n } else {\n throw new Error(errMsg);\n }\n }\n }\n }\n return out;\n }\n}\nclass _Hooks {\n options;\n constructor(options) {\n this.options = options || _defaults;\n }\n static passThroughHooks = new Set(['preprocess', 'postprocess', 'processAllTokens']);\n /**\n * Process markdown before marked\n */\n preprocess(markdown) {\n return markdown;\n }\n /**\n * Process HTML after marked is finished\n */\n postprocess(html) {\n return html;\n }\n /**\n * Process all tokens before walk tokens\n */\n processAllTokens(tokens) {\n return tokens;\n }\n}\nclass Marked {\n defaults = _getDefaults();\n options = this.setOptions;\n parse = this.#parseMarkdown(_Lexer.lex, _Parser.parse);\n parseInline = this.#parseMarkdown(_Lexer.lexInline, _Parser.parseInline);\n Parser = _Parser;\n Renderer = _Renderer;\n TextRenderer = _TextRenderer;\n Lexer = _Lexer;\n Tokenizer = _Tokenizer;\n Hooks = _Hooks;\n constructor(...args) {\n this.use(...args);\n }\n /**\n * Run callback for every token\n */\n walkTokens(tokens, callback) {\n let values = [];\n for (const token of tokens) {\n values = values.concat(callback.call(this, token));\n switch (token.type) {\n case 'table':\n {\n const tableToken = token;\n for (const cell of tableToken.header) {\n values = values.concat(this.walkTokens(cell.tokens, callback));\n }\n for (const row of tableToken.rows) {\n for (const cell of row) {\n values = values.concat(this.walkTokens(cell.tokens, callback));\n }\n }\n break;\n }\n case 'list':\n {\n const listToken = token;\n values = values.concat(this.walkTokens(listToken.items, callback));\n break;\n }\n default:\n {\n const genericToken = token;\n if (this.defaults.extensions?.childTokens?.[genericToken.type]) {\n this.defaults.extensions.childTokens[genericToken.type].forEach(childTokens => {\n const tokens = genericToken[childTokens].flat(Infinity);\n values = values.concat(this.walkTokens(tokens, callback));\n });\n } else if (genericToken.tokens) {\n values = values.concat(this.walkTokens(genericToken.tokens, callback));\n }\n }\n }\n }\n return values;\n }\n use(...args) {\n const extensions = this.defaults.extensions || {\n renderers: {},\n childTokens: {}\n };\n args.forEach(pack => {\n // copy options to new object\n const opts = {\n ...pack\n };\n // set async to true if it was set to true before\n opts.async = this.defaults.async || opts.async || false;\n // ==-- Parse \"addon\" extensions --== //\n if (pack.extensions) {\n pack.extensions.forEach(ext => {\n if (!ext.name) {\n throw new Error('extension name required');\n }\n if ('renderer' in ext) {\n // Renderer extensions\n const prevRenderer = extensions.renderers[ext.name];\n if (prevRenderer) {\n // Replace extension with func to run new extension but fall back if false\n extensions.renderers[ext.name] = function (...args) {\n let ret = ext.renderer.apply(this, args);\n if (ret === false) {\n ret = prevRenderer.apply(this, args);\n }\n return ret;\n };\n } else {\n extensions.renderers[ext.name] = ext.renderer;\n }\n }\n if ('tokenizer' in ext) {\n // Tokenizer Extensions\n if (!ext.level || ext.level !== 'block' && ext.level !== 'inline') {\n throw new Error(\"extension level must be 'block' or 'inline'\");\n }\n const extLevel = extensions[ext.level];\n if (extLevel) {\n extLevel.unshift(ext.tokenizer);\n } else {\n extensions[ext.level] = [ext.tokenizer];\n }\n if (ext.start) {\n // Function to check for start of token\n if (ext.level === 'block') {\n if (extensions.startBlock) {\n extensions.startBlock.push(ext.start);\n } else {\n extensions.startBlock = [ext.start];\n }\n } else if (ext.level === 'inline') {\n if (extensions.startInline) {\n extensions.startInline.push(ext.start);\n } else {\n extensions.startInline = [ext.start];\n }\n }\n }\n }\n if ('childTokens' in ext && ext.childTokens) {\n // Child tokens to be visited by walkTokens\n extensions.childTokens[ext.name] = ext.childTokens;\n }\n });\n opts.extensions = extensions;\n }\n // ==-- Parse \"overwrite\" extensions --== //\n if (pack.renderer) {\n const renderer = this.defaults.renderer || new _Renderer(this.defaults);\n for (const prop in pack.renderer) {\n if (!(prop in renderer)) {\n throw new Error(`renderer '${prop}' does not exist`);\n }\n if (prop === 'options') {\n // ignore options property\n continue;\n }\n const rendererProp = prop;\n const rendererFunc = pack.renderer[rendererProp];\n const prevRenderer = renderer[rendererProp];\n // Replace renderer with func to run extension, but fall back if false\n renderer[rendererProp] = (...args) => {\n let ret = rendererFunc.apply(renderer, args);\n if (ret === false) {\n ret = prevRenderer.apply(renderer, args);\n }\n return ret || '';\n };\n }\n opts.renderer = renderer;\n }\n if (pack.tokenizer) {\n const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);\n for (const prop in pack.tokenizer) {\n if (!(prop in tokenizer)) {\n throw new Error(`tokenizer '${prop}' does not exist`);\n }\n if (['options', 'rules', 'lexer'].includes(prop)) {\n // ignore options, rules, and lexer properties\n continue;\n }\n const tokenizerProp = prop;\n const tokenizerFunc = pack.tokenizer[tokenizerProp];\n const prevTokenizer = tokenizer[tokenizerProp];\n // Replace tokenizer with func to run extension, but fall back if false\n // @ts-expect-error cannot type tokenizer function dynamically\n tokenizer[tokenizerProp] = (...args) => {\n let ret = tokenizerFunc.apply(tokenizer, args);\n if (ret === false) {\n ret = prevTokenizer.apply(tokenizer, args);\n }\n return ret;\n };\n }\n opts.tokenizer = tokenizer;\n }\n // ==-- Parse Hooks extensions --== //\n if (pack.hooks) {\n const hooks = this.defaults.hooks || new _Hooks();\n for (const prop in pack.hooks) {\n if (!(prop in hooks)) {\n throw new Error(`hook '${prop}' does not exist`);\n }\n if (prop === 'options') {\n // ignore options property\n continue;\n }\n const hooksProp = prop;\n const hooksFunc = pack.hooks[hooksProp];\n const prevHook = hooks[hooksProp];\n if (_Hooks.passThroughHooks.has(prop)) {\n // @ts-expect-error cannot type hook function dynamically\n hooks[hooksProp] = arg => {\n if (this.defaults.async) {\n return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {\n return prevHook.call(hooks, ret);\n });\n }\n const ret = hooksFunc.call(hooks, arg);\n return prevHook.call(hooks, ret);\n };\n } else {\n // @ts-expect-error cannot type hook function dynamically\n hooks[hooksProp] = (...args) => {\n let ret = hooksFunc.apply(hooks, args);\n if (ret === false) {\n ret = prevHook.apply(hooks, args);\n }\n return ret;\n };\n }\n }\n opts.hooks = hooks;\n }\n // ==-- Parse WalkTokens extensions --== //\n if (pack.walkTokens) {\n const walkTokens = this.defaults.walkTokens;\n const packWalktokens = pack.walkTokens;\n opts.walkTokens = function (token) {\n let values = [];\n values.push(packWalktokens.call(this, token));\n if (walkTokens) {\n values = values.concat(walkTokens.call(this, token));\n }\n return values;\n };\n }\n this.defaults = {\n ...this.defaults,\n ...opts\n };\n });\n return this;\n }\n setOptions(opt) {\n this.defaults = {\n ...this.defaults,\n ...opt\n };\n return this;\n }\n lexer(src, options) {\n return _Lexer.lex(src, options ?? this.defaults);\n }\n parser(tokens, options) {\n return _Parser.parse(tokens, options ?? this.defaults);\n }\n #parseMarkdown(lexer, parser) {\n return (src, options) => {\n const origOpt = {\n ...options\n };\n const opt = {\n ...this.defaults,\n ...origOpt\n };\n // Show warning if an extension set async to true but the parse was called with async: false\n if (this.defaults.async === true && origOpt.async === false) {\n if (!opt.silent) {\n console.warn('marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored.');\n }\n opt.async = true;\n }\n const throwError = this.#onError(!!opt.silent, !!opt.async);\n // throw error in case of non string input\n if (typeof src === 'undefined' || src === null) {\n return throwError(new Error('marked(): input parameter is undefined or null'));\n }\n if (typeof src !== 'string') {\n return throwError(new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'));\n }\n if (opt.hooks) {\n opt.hooks.options = opt;\n }\n if (opt.async) {\n return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src).then(src => lexer(src, opt)).then(tokens => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens).then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens).then(tokens => parser(tokens, opt)).then(html => opt.hooks ? opt.hooks.postprocess(html) : html).catch(throwError);\n }\n try {\n if (opt.hooks) {\n src = opt.hooks.preprocess(src);\n }\n let tokens = lexer(src, opt);\n if (opt.hooks) {\n tokens = opt.hooks.processAllTokens(tokens);\n }\n if (opt.walkTokens) {\n this.walkTokens(tokens, opt.walkTokens);\n }\n let html = parser(tokens, opt);\n if (opt.hooks) {\n html = opt.hooks.postprocess(html);\n }\n return html;\n } catch (e) {\n return throwError(e);\n }\n };\n }\n #onError(silent, async) {\n return e => {\n e.message += '\\nPlease report this to https://github.com/markedjs/marked.';\n if (silent) {\n const msg = '

    An error occurred:

    ' + escape$1(e.message + '', true) + '
    ';\n if (async) {\n return Promise.resolve(msg);\n }\n return msg;\n }\n if (async) {\n return Promise.reject(e);\n }\n throw e;\n };\n }\n}\nconst markedInstance = new Marked();\nfunction marked(src, opt) {\n return markedInstance.parse(src, opt);\n}\n/**\n * Sets the default options.\n *\n * @param options Hash of options\n */\nmarked.options = marked.setOptions = function (options) {\n markedInstance.setOptions(options);\n marked.defaults = markedInstance.defaults;\n changeDefaults(marked.defaults);\n return marked;\n};\n/**\n * Gets the original marked default options.\n */\nmarked.getDefaults = _getDefaults;\nmarked.defaults = _defaults;\n/**\n * Use Extension\n */\nmarked.use = function (...args) {\n markedInstance.use(...args);\n marked.defaults = markedInstance.defaults;\n changeDefaults(marked.defaults);\n return marked;\n};\n/**\n * Run callback for every token\n */\nmarked.walkTokens = function (tokens, callback) {\n return markedInstance.walkTokens(tokens, callback);\n};\n/**\n * Compiles markdown to HTML without enclosing `p` tag.\n *\n * @param src String of markdown source to be compiled\n * @param options Hash of options\n * @return String of compiled HTML\n */\nmarked.parseInline = markedInstance.parseInline;\n/**\n * Expose\n */\nmarked.Parser = _Parser;\nmarked.parser = _Parser.parse;\nmarked.Renderer = _Renderer;\nmarked.TextRenderer = _TextRenderer;\nmarked.Lexer = _Lexer;\nmarked.lexer = _Lexer.lex;\nmarked.Tokenizer = _Tokenizer;\nmarked.Hooks = _Hooks;\nmarked.parse = marked;\nconst options = marked.options;\nconst setOptions = marked.setOptions;\nconst use = marked.use;\nconst walkTokens = marked.walkTokens;\nconst parseInline = marked.parseInline;\nconst parse = marked;\nconst parser = _Parser.parse;\nconst lexer = _Lexer.lex;\nexport { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };\n","import { AsyncPipe, isPlatformBrowser, CommonModule } from '@angular/common';\nimport * as i0 from '@angular/core';\nimport { Component, ChangeDetectionStrategy, InjectionToken, Pipe, PLATFORM_ID, Injectable, Inject, Optional, EventEmitter, Input, Output, SecurityContext, NgModule } from '@angular/core';\nimport { Subject, merge, of, timer } from 'rxjs';\nimport { switchMap, mapTo, distinctUntilChanged, shareReplay, startWith, map, takeUntil, first } from 'rxjs/operators';\nimport { Renderer, marked } from 'marked';\nconst _c0 = [\"*\"];\nexport { Renderer as MarkedRenderer } from 'marked';\nimport * as i1 from '@angular/common/http';\nimport * as i2 from '@angular/platform-browser';\nconst BUTTON_TEXT_COPY = 'Copy';\nconst BUTTON_TEXT_COPIED = 'Copied';\nlet ClipboardButtonComponent = /*#__PURE__*/(() => {\n class ClipboardButtonComponent {\n constructor() {\n this._buttonClick$ = new Subject();\n this.copied$ = this._buttonClick$.pipe(switchMap(() => merge(of(true), timer(3000).pipe(mapTo(false)))), distinctUntilChanged(), shareReplay(1));\n this.copiedText$ = this.copied$.pipe(startWith(false), map(copied => copied ? BUTTON_TEXT_COPIED : BUTTON_TEXT_COPY));\n }\n onCopyToClipboardClick() {\n this._buttonClick$.next();\n }\n static {\n this.ɵfac = function ClipboardButtonComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || ClipboardButtonComponent)();\n };\n }\n static {\n this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: ClipboardButtonComponent,\n selectors: [[\"markdown-clipboard\"]],\n standalone: true,\n features: [i0.ɵɵStandaloneFeature],\n decls: 4,\n vars: 7,\n consts: [[1, \"markdown-clipboard-button\", 3, \"click\"]],\n template: function ClipboardButtonComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵelementStart(0, \"button\", 0);\n i0.ɵɵpipe(1, \"async\");\n i0.ɵɵlistener(\"click\", function ClipboardButtonComponent_Template_button_click_0_listener() {\n return ctx.onCopyToClipboardClick();\n });\n i0.ɵɵtext(2);\n i0.ɵɵpipe(3, \"async\");\n i0.ɵɵelementEnd();\n }\n if (rf & 2) {\n i0.ɵɵclassProp(\"copied\", i0.ɵɵpipeBind1(1, 3, ctx.copied$));\n i0.ɵɵadvance(2);\n i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(3, 5, ctx.copiedText$));\n }\n },\n dependencies: [AsyncPipe],\n encapsulation: 2,\n changeDetection: 0\n });\n }\n }\n return ClipboardButtonComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nconst CLIPBOARD_OPTIONS = new InjectionToken('CLIPBOARD_OPTIONS');\n\n/* eslint-disable */\nclass KatexSpecificOptions {}\nlet LanguagePipe = /*#__PURE__*/(() => {\n class LanguagePipe {\n transform(value, language) {\n if (value == null) {\n value = '';\n }\n if (language == null) {\n language = '';\n }\n if (typeof value !== 'string') {\n console.error(`LanguagePipe has been invoked with an invalid value type [${typeof value}]`);\n return value;\n }\n if (typeof language !== 'string') {\n console.error(`LanguagePipe has been invoked with an invalid parameter [${typeof language}]`);\n return value;\n }\n return '```' + language + '\\n' + value + '\\n```';\n }\n static {\n this.ɵfac = function LanguagePipe_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || LanguagePipe)();\n };\n }\n static {\n this.ɵpipe = /* @__PURE__ */i0.ɵɵdefinePipe({\n name: \"language\",\n type: LanguagePipe,\n pure: true,\n standalone: true\n });\n }\n }\n return LanguagePipe;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nvar PrismPlugin = /*#__PURE__*/function (PrismPlugin) {\n PrismPlugin[\"CommandLine\"] = \"command-line\";\n PrismPlugin[\"LineHighlight\"] = \"line-highlight\";\n PrismPlugin[\"LineNumbers\"] = \"line-numbers\";\n return PrismPlugin;\n}(PrismPlugin || {});\nconst MARKED_EXTENSIONS = new InjectionToken('MARKED_EXTENSIONS');\nconst MARKED_OPTIONS = new InjectionToken('MARKED_OPTIONS');\n\n/* eslint-disable max-len */\nconst errorJoyPixelsNotLoaded = '[ngx-markdown] When using the `emoji` attribute you *have to* include Emoji-Toolkit files to `angular.json` or use imports. See README for more information';\nconst errorKatexNotLoaded = '[ngx-markdown] When using the `katex` attribute you *have to* include KaTeX files to `angular.json` or use imports. See README for more information';\nconst errorMermaidNotLoaded = '[ngx-markdown] When using the `mermaid` attribute you *have to* include Mermaid files to `angular.json` or use imports. See README for more information';\nconst errorClipboardNotLoaded = '[ngx-markdown] When using the `clipboard` attribute you *have to* include Clipboard files to `angular.json` or use imports. See README for more information';\nconst errorClipboardViewContainerRequired = '[ngx-markdown] When using the `clipboard` attribute you *have to* provide the `viewContainerRef` parameter to `MarkdownService.render()` function';\nconst errorSrcWithoutHttpClient = '[ngx-markdown] When using the `src` attribute you *have to* pass the `HttpClient` as a parameter of the `forRoot` method. See README for more information';\n/* eslint-enable max-len */\nconst SECURITY_CONTEXT = new InjectionToken('SECURITY_CONTEXT');\nclass ExtendedRenderer extends Renderer {\n constructor() {\n super(...arguments);\n this.ɵNgxMarkdownRendererExtendedForExtensions = false;\n this.ɵNgxMarkdownRendererExtendedForMermaid = false;\n }\n}\nlet MarkdownService = /*#__PURE__*/(() => {\n class MarkdownService {\n get options() {\n return this._options;\n }\n set options(value) {\n this._options = {\n ...this.DEFAULT_MARKED_OPTIONS,\n ...value\n };\n }\n get renderer() {\n return this.options.renderer;\n }\n set renderer(value) {\n this.options.renderer = value;\n }\n constructor(clipboardOptions, extensions, options, platform, securityContext, http, sanitizer) {\n this.clipboardOptions = clipboardOptions;\n this.extensions = extensions;\n this.platform = platform;\n this.securityContext = securityContext;\n this.http = http;\n this.sanitizer = sanitizer;\n this.DEFAULT_MARKED_OPTIONS = {\n renderer: new Renderer()\n };\n this.DEFAULT_KATEX_OPTIONS = {\n delimiters: [{\n left: '$$',\n right: '$$',\n display: true\n }, {\n left: '$',\n right: '$',\n display: false\n }, {\n left: '\\\\(',\n right: '\\\\)',\n display: false\n }, {\n left: '\\\\begin{equation}',\n right: '\\\\end{equation}',\n display: true\n }, {\n left: '\\\\begin{align}',\n right: '\\\\end{align}',\n display: true\n }, {\n left: '\\\\begin{alignat}',\n right: '\\\\end{alignat}',\n display: true\n }, {\n left: '\\\\begin{gather}',\n right: '\\\\end{gather}',\n display: true\n }, {\n left: '\\\\begin{CD}',\n right: '\\\\end{CD}',\n display: true\n }, {\n left: '\\\\[',\n right: '\\\\]',\n display: true\n }]\n };\n this.DEFAULT_MERMAID_OPTIONS = {\n startOnLoad: false\n };\n this.DEFAULT_CLIPBOARD_OPTIONS = {\n buttonComponent: undefined\n };\n this.DEFAULT_PARSE_OPTIONS = {\n decodeHtml: false,\n inline: false,\n emoji: false,\n mermaid: false,\n markedOptions: undefined,\n disableSanitizer: false\n };\n this.DEFAULT_RENDER_OPTIONS = {\n clipboard: false,\n clipboardOptions: undefined,\n katex: false,\n katexOptions: undefined,\n mermaid: false,\n mermaidOptions: undefined\n };\n this._reload$ = new Subject();\n this.reload$ = this._reload$.asObservable();\n this.options = options;\n }\n parse(markdown, parseOptions = this.DEFAULT_PARSE_OPTIONS) {\n const {\n decodeHtml,\n inline,\n emoji,\n mermaid,\n disableSanitizer\n } = parseOptions;\n const markedOptions = {\n ...this.options,\n ...parseOptions.markedOptions\n };\n const renderer = markedOptions.renderer || this.renderer || new Renderer();\n if (this.extensions) {\n this.renderer = this.extendsRendererForExtensions(renderer);\n }\n if (mermaid) {\n this.renderer = this.extendsRendererForMermaid(renderer);\n }\n const trimmed = this.trimIndentation(markdown);\n const decoded = decodeHtml ? this.decodeHtml(trimmed) : trimmed;\n const emojified = emoji ? this.parseEmoji(decoded) : decoded;\n const marked = this.parseMarked(emojified, markedOptions, inline);\n const sanitized = disableSanitizer ? marked : this.sanitizer.sanitize(this.securityContext, marked);\n return sanitized || '';\n }\n render(element, options = this.DEFAULT_RENDER_OPTIONS, viewContainerRef) {\n const {\n clipboard,\n clipboardOptions,\n katex,\n katexOptions,\n mermaid,\n mermaidOptions\n } = options;\n if (katex) {\n this.renderKatex(element, {\n ...this.DEFAULT_KATEX_OPTIONS,\n ...katexOptions\n });\n }\n if (mermaid) {\n this.renderMermaid(element, {\n ...this.DEFAULT_MERMAID_OPTIONS,\n ...mermaidOptions\n });\n }\n if (clipboard) {\n this.renderClipboard(element, viewContainerRef, {\n ...this.DEFAULT_CLIPBOARD_OPTIONS,\n ...this.clipboardOptions,\n ...clipboardOptions\n });\n }\n this.highlight(element);\n }\n reload() {\n this._reload$.next();\n }\n getSource(src) {\n if (!this.http) {\n throw new Error(errorSrcWithoutHttpClient);\n }\n return this.http.get(src, {\n responseType: 'text'\n }).pipe(map(markdown => this.handleExtension(src, markdown)));\n }\n highlight(element) {\n if (!isPlatformBrowser(this.platform)) {\n return;\n }\n if (typeof Prism === 'undefined' || typeof Prism.highlightAllUnder === 'undefined') {\n return;\n }\n if (!element) {\n element = document;\n }\n const noLanguageElements = element.querySelectorAll('pre code:not([class*=\"language-\"])');\n Array.prototype.forEach.call(noLanguageElements, x => x.classList.add('language-none'));\n Prism.highlightAllUnder(element);\n }\n decodeHtml(html) {\n if (!isPlatformBrowser(this.platform)) {\n return html;\n }\n const textarea = document.createElement('textarea');\n textarea.innerHTML = html;\n return textarea.value;\n }\n extendsRendererForExtensions(renderer) {\n const extendedRenderer = renderer;\n if (extendedRenderer.ɵNgxMarkdownRendererExtendedForExtensions === true) {\n return renderer;\n }\n if (this.extensions?.length > 0) {\n marked.use(...this.extensions);\n }\n extendedRenderer.ɵNgxMarkdownRendererExtendedForExtensions = true;\n return renderer;\n }\n extendsRendererForMermaid(renderer) {\n const extendedRenderer = renderer;\n if (extendedRenderer.ɵNgxMarkdownRendererExtendedForMermaid === true) {\n return renderer;\n }\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const defaultCode = renderer.code;\n renderer.code = function (code, language, isEscaped) {\n return language === 'mermaid' ? `
    ${code}
    ` : defaultCode.call(this, code, language, isEscaped);\n };\n extendedRenderer.ɵNgxMarkdownRendererExtendedForMermaid = true;\n return renderer;\n }\n handleExtension(src, markdown) {\n const urlProtocolIndex = src.lastIndexOf('://');\n const urlWithoutProtocol = urlProtocolIndex > -1 ? src.substring(urlProtocolIndex + 4) : src;\n const lastSlashIndex = urlWithoutProtocol.lastIndexOf('/');\n const lastUrlSegment = lastSlashIndex > -1 ? urlWithoutProtocol.substring(lastSlashIndex + 1).split('?')[0] : '';\n const lastDotIndex = lastUrlSegment.lastIndexOf('.');\n const extension = lastDotIndex > -1 ? lastUrlSegment.substring(lastDotIndex + 1) : '';\n return !!extension && extension !== 'md' ? '```' + extension + '\\n' + markdown + '\\n```' : markdown;\n }\n parseMarked(html, markedOptions, inline = false) {\n if (markedOptions.renderer) {\n // clone renderer and remove extended flags otherwise\n // marked throws an error thinking it is a renderer prop\n const renderer = {\n ...markedOptions.renderer\n };\n delete renderer.ɵNgxMarkdownRendererExtendedForExtensions;\n delete renderer.ɵNgxMarkdownRendererExtendedForMermaid;\n // remove renderer from markedOptions because if renderer is\n // passed to marked.parse method, it will ignore all extensions\n delete markedOptions.renderer;\n marked.use({\n renderer\n });\n }\n return inline ? marked.parseInline(html, markedOptions) : marked.parse(html, markedOptions);\n }\n parseEmoji(html) {\n if (!isPlatformBrowser(this.platform)) {\n return html;\n }\n if (typeof joypixels === 'undefined' || typeof joypixels.shortnameToUnicode === 'undefined') {\n throw new Error(errorJoyPixelsNotLoaded);\n }\n return joypixels.shortnameToUnicode(html);\n }\n renderKatex(element, options) {\n if (!isPlatformBrowser(this.platform)) {\n return;\n }\n if (typeof katex === 'undefined' || typeof renderMathInElement === 'undefined') {\n throw new Error(errorKatexNotLoaded);\n }\n renderMathInElement(element, options);\n }\n renderClipboard(element, viewContainerRef, options) {\n if (!isPlatformBrowser(this.platform)) {\n return;\n }\n if (typeof ClipboardJS === 'undefined') {\n throw new Error(errorClipboardNotLoaded);\n }\n if (!viewContainerRef) {\n throw new Error(errorClipboardViewContainerRequired);\n }\n const {\n buttonComponent,\n buttonTemplate\n } = options;\n // target every
     elements\n      const preElements = element.querySelectorAll('pre');\n      for (let i = 0; i < preElements.length; i++) {\n        const preElement = preElements.item(i);\n        // create 
     wrapper element\n        const preWrapperElement = document.createElement('div');\n        preWrapperElement.style.position = 'relative';\n        preElement.parentNode.insertBefore(preWrapperElement, preElement);\n        preWrapperElement.appendChild(preElement);\n        // create toolbar element\n        const toolbarWrapperElement = document.createElement('div');\n        toolbarWrapperElement.classList.add('markdown-clipboard-toolbar');\n        toolbarWrapperElement.style.position = 'absolute';\n        toolbarWrapperElement.style.top = '.5em';\n        toolbarWrapperElement.style.right = '.5em';\n        toolbarWrapperElement.style.zIndex = '1';\n        preWrapperElement.insertAdjacentElement('beforeend', toolbarWrapperElement);\n        // register listener to show/hide toolbar\n        preWrapperElement.onmouseenter = () => toolbarWrapperElement.classList.add('hover');\n        preWrapperElement.onmouseleave = () => toolbarWrapperElement.classList.remove('hover');\n        // declare embeddedViewRef holding variable\n        let embeddedViewRef;\n        // use provided component via input property\n        // or provided via ClipboardOptions provider\n        if (buttonComponent) {\n          const componentRef = viewContainerRef.createComponent(buttonComponent);\n          embeddedViewRef = componentRef.hostView;\n          componentRef.changeDetectorRef.markForCheck();\n        }\n        // use provided template via input property\n        else if (buttonTemplate) {\n          embeddedViewRef = viewContainerRef.createEmbeddedView(buttonTemplate);\n        }\n        // use default component\n        else {\n          const componentRef = viewContainerRef.createComponent(ClipboardButtonComponent);\n          embeddedViewRef = componentRef.hostView;\n          componentRef.changeDetectorRef.markForCheck();\n        }\n        // declare clipboard instance variable\n        let clipboardInstance;\n        // attach clipboard.js to root node\n        embeddedViewRef.rootNodes.forEach(node => {\n          toolbarWrapperElement.appendChild(node);\n          clipboardInstance = new ClipboardJS(node, {\n            text: () => preElement.innerText\n          });\n        });\n        // destroy clipboard instance when view is destroyed\n        embeddedViewRef.onDestroy(() => clipboardInstance.destroy());\n      }\n    }\n    renderMermaid(element, options = this.DEFAULT_MERMAID_OPTIONS) {\n      if (!isPlatformBrowser(this.platform)) {\n        return;\n      }\n      if (typeof mermaid === 'undefined' || typeof mermaid.initialize === 'undefined') {\n        throw new Error(errorMermaidNotLoaded);\n      }\n      const mermaidElements = element.querySelectorAll('.mermaid');\n      if (mermaidElements.length === 0) {\n        return;\n      }\n      mermaid.initialize(options);\n      mermaid.run({\n        nodes: mermaidElements\n      });\n    }\n    trimIndentation(markdown) {\n      if (!markdown) {\n        return '';\n      }\n      let indentStart;\n      return markdown.split('\\n').map(line => {\n        let lineIdentStart = indentStart;\n        if (line.length > 0) {\n          lineIdentStart = isNaN(lineIdentStart) ? line.search(/\\S|$/) : Math.min(line.search(/\\S|$/), lineIdentStart);\n        }\n        if (isNaN(indentStart)) {\n          indentStart = lineIdentStart;\n        }\n        return lineIdentStart ? line.substring(lineIdentStart) : line;\n      }).join('\\n');\n    }\n    static {\n      this.ɵfac = function MarkdownService_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || MarkdownService)(i0.ɵɵinject(CLIPBOARD_OPTIONS, 8), i0.ɵɵinject(MARKED_EXTENSIONS, 8), i0.ɵɵinject(MARKED_OPTIONS, 8), i0.ɵɵinject(PLATFORM_ID), i0.ɵɵinject(SECURITY_CONTEXT), i0.ɵɵinject(i1.HttpClient, 8), i0.ɵɵinject(i2.DomSanitizer));\n      };\n    }\n    static {\n      this.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n        token: MarkdownService,\n        factory: MarkdownService.ɵfac\n      });\n    }\n  }\n  return MarkdownService;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet MarkdownComponent = /*#__PURE__*/(() => {\n  class MarkdownComponent {\n    get disableSanitizer() {\n      return this._disableSanitizer;\n    }\n    set disableSanitizer(value) {\n      this._disableSanitizer = this.coerceBooleanProperty(value);\n    }\n    get inline() {\n      return this._inline;\n    }\n    set inline(value) {\n      this._inline = this.coerceBooleanProperty(value);\n    }\n    // Plugin - clipboard\n    get clipboard() {\n      return this._clipboard;\n    }\n    set clipboard(value) {\n      this._clipboard = this.coerceBooleanProperty(value);\n    }\n    // Plugin - emoji\n    get emoji() {\n      return this._emoji;\n    }\n    set emoji(value) {\n      this._emoji = this.coerceBooleanProperty(value);\n    }\n    // Plugin - katex\n    get katex() {\n      return this._katex;\n    }\n    set katex(value) {\n      this._katex = this.coerceBooleanProperty(value);\n    }\n    // Plugin - mermaid\n    get mermaid() {\n      return this._mermaid;\n    }\n    set mermaid(value) {\n      this._mermaid = this.coerceBooleanProperty(value);\n    }\n    // Plugin - lineHighlight\n    get lineHighlight() {\n      return this._lineHighlight;\n    }\n    set lineHighlight(value) {\n      this._lineHighlight = this.coerceBooleanProperty(value);\n    }\n    // Plugin - lineNumbers\n    get lineNumbers() {\n      return this._lineNumbers;\n    }\n    set lineNumbers(value) {\n      this._lineNumbers = this.coerceBooleanProperty(value);\n    }\n    // Plugin - commandLine\n    get commandLine() {\n      return this._commandLine;\n    }\n    set commandLine(value) {\n      this._commandLine = this.coerceBooleanProperty(value);\n    }\n    constructor(element, markdownService, viewContainerRef) {\n      this.element = element;\n      this.markdownService = markdownService;\n      this.viewContainerRef = viewContainerRef;\n      // Event emitters\n      this.error = new EventEmitter();\n      this.load = new EventEmitter();\n      this.ready = new EventEmitter();\n      this._clipboard = false;\n      this._commandLine = false;\n      this._disableSanitizer = false;\n      this._emoji = false;\n      this._inline = false;\n      this._katex = false;\n      this._lineHighlight = false;\n      this._lineNumbers = false;\n      this._mermaid = false;\n      this.destroyed$ = new Subject();\n    }\n    ngOnChanges() {\n      this.loadContent();\n    }\n    loadContent() {\n      if (this.data != null) {\n        this.handleData();\n        return;\n      }\n      if (this.src != null) {\n        this.handleSrc();\n        return;\n      }\n    }\n    ngAfterViewInit() {\n      if (!this.data && !this.src) {\n        this.handleTransclusion();\n      }\n      this.markdownService.reload$.pipe(takeUntil(this.destroyed$)).subscribe(() => this.loadContent());\n    }\n    ngOnDestroy() {\n      this.destroyed$.next();\n      this.destroyed$.complete();\n    }\n    async render(markdown, decodeHtml = false) {\n      const parsedOptions = {\n        decodeHtml,\n        inline: this.inline,\n        emoji: this.emoji,\n        mermaid: this.mermaid,\n        disableSanitizer: this.disableSanitizer\n      };\n      const renderOptions = {\n        clipboard: this.clipboard,\n        clipboardOptions: this.getClipboardOptions(),\n        katex: this.katex,\n        katexOptions: this.katexOptions,\n        mermaid: this.mermaid,\n        mermaidOptions: this.mermaidOptions\n      };\n      const parsed = await this.markdownService.parse(markdown, parsedOptions);\n      this.element.nativeElement.innerHTML = parsed;\n      this.handlePlugins();\n      this.markdownService.render(this.element.nativeElement, renderOptions, this.viewContainerRef);\n      this.ready.emit();\n    }\n    coerceBooleanProperty(value) {\n      return value != null && `${String(value)}` !== 'false';\n    }\n    getClipboardOptions() {\n      if (this.clipboardButtonComponent || this.clipboardButtonTemplate) {\n        return {\n          buttonComponent: this.clipboardButtonComponent,\n          buttonTemplate: this.clipboardButtonTemplate\n        };\n      }\n      return undefined;\n    }\n    handleData() {\n      this.render(this.data);\n    }\n    handleSrc() {\n      this.markdownService.getSource(this.src).subscribe({\n        next: markdown => {\n          this.render(markdown).then(() => {\n            this.load.emit(markdown);\n          });\n        },\n        error: error => this.error.emit(error)\n      });\n    }\n    handleTransclusion() {\n      this.render(this.element.nativeElement.innerHTML, true);\n    }\n    handlePlugins() {\n      if (this.commandLine) {\n        this.setPluginClass(this.element.nativeElement, PrismPlugin.CommandLine);\n        this.setPluginOptions(this.element.nativeElement, {\n          dataFilterOutput: this.filterOutput,\n          dataHost: this.host,\n          dataPrompt: this.prompt,\n          dataOutput: this.output,\n          dataUser: this.user\n        });\n      }\n      if (this.lineHighlight) {\n        this.setPluginOptions(this.element.nativeElement, {\n          dataLine: this.line,\n          dataLineOffset: this.lineOffset\n        });\n      }\n      if (this.lineNumbers) {\n        this.setPluginClass(this.element.nativeElement, PrismPlugin.LineNumbers);\n        this.setPluginOptions(this.element.nativeElement, {\n          dataStart: this.start\n        });\n      }\n    }\n    setPluginClass(element, plugin) {\n      const preElements = element.querySelectorAll('pre');\n      for (let i = 0; i < preElements.length; i++) {\n        const classes = plugin instanceof Array ? plugin : [plugin];\n        preElements.item(i).classList.add(...classes);\n      }\n    }\n    setPluginOptions(element, options) {\n      const preElements = element.querySelectorAll('pre');\n      for (let i = 0; i < preElements.length; i++) {\n        Object.keys(options).forEach(option => {\n          const attributeValue = options[option];\n          if (attributeValue) {\n            const attributeName = this.toLispCase(option);\n            preElements.item(i).setAttribute(attributeName, attributeValue.toString());\n          }\n        });\n      }\n    }\n    toLispCase(value) {\n      const upperChars = value.match(/([A-Z])/g);\n      if (!upperChars) {\n        return value;\n      }\n      let str = value.toString();\n      for (let i = 0, n = upperChars.length; i < n; i++) {\n        str = str.replace(new RegExp(upperChars[i]), '-' + upperChars[i].toLowerCase());\n      }\n      if (str.slice(0, 1) === '-') {\n        str = str.slice(1);\n      }\n      return str;\n    }\n    static {\n      this.ɵfac = function MarkdownComponent_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || MarkdownComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(MarkdownService), i0.ɵɵdirectiveInject(i0.ViewContainerRef));\n      };\n    }\n    static {\n      this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n        type: MarkdownComponent,\n        selectors: [[\"markdown\"], [\"\", \"markdown\", \"\"]],\n        inputs: {\n          data: \"data\",\n          src: \"src\",\n          disableSanitizer: \"disableSanitizer\",\n          inline: \"inline\",\n          clipboard: \"clipboard\",\n          clipboardButtonComponent: \"clipboardButtonComponent\",\n          clipboardButtonTemplate: \"clipboardButtonTemplate\",\n          emoji: \"emoji\",\n          katex: \"katex\",\n          katexOptions: \"katexOptions\",\n          mermaid: \"mermaid\",\n          mermaidOptions: \"mermaidOptions\",\n          lineHighlight: \"lineHighlight\",\n          line: \"line\",\n          lineOffset: \"lineOffset\",\n          lineNumbers: \"lineNumbers\",\n          start: \"start\",\n          commandLine: \"commandLine\",\n          filterOutput: \"filterOutput\",\n          host: \"host\",\n          prompt: \"prompt\",\n          output: \"output\",\n          user: \"user\"\n        },\n        outputs: {\n          error: \"error\",\n          load: \"load\",\n          ready: \"ready\"\n        },\n        standalone: true,\n        features: [i0.ɵɵNgOnChangesFeature, i0.ɵɵStandaloneFeature],\n        ngContentSelectors: _c0,\n        decls: 1,\n        vars: 0,\n        template: function MarkdownComponent_Template(rf, ctx) {\n          if (rf & 1) {\n            i0.ɵɵprojectionDef();\n            i0.ɵɵprojection(0);\n          }\n        },\n        encapsulation: 2\n      });\n    }\n  }\n  return MarkdownComponent;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet MarkdownPipe = /*#__PURE__*/(() => {\n  class MarkdownPipe {\n    constructor(domSanitizer, elementRef, markdownService, viewContainerRef, zone) {\n      this.domSanitizer = domSanitizer;\n      this.elementRef = elementRef;\n      this.markdownService = markdownService;\n      this.viewContainerRef = viewContainerRef;\n      this.zone = zone;\n    }\n    async transform(value, options) {\n      if (value == null) {\n        return '';\n      }\n      if (typeof value !== 'string') {\n        console.error(`MarkdownPipe has been invoked with an invalid value type [${typeof value}]`);\n        return value;\n      }\n      const markdown = await this.markdownService.parse(value, options);\n      this.zone.onStable.pipe(first()).subscribe(() => this.markdownService.render(this.elementRef.nativeElement, options, this.viewContainerRef));\n      return this.domSanitizer.bypassSecurityTrustHtml(markdown);\n    }\n    static {\n      this.ɵfac = function MarkdownPipe_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || MarkdownPipe)(i0.ɵɵdirectiveInject(i2.DomSanitizer, 16), i0.ɵɵdirectiveInject(i0.ElementRef, 16), i0.ɵɵdirectiveInject(MarkdownService, 16), i0.ɵɵdirectiveInject(i0.ViewContainerRef, 16), i0.ɵɵdirectiveInject(i0.NgZone, 16));\n      };\n    }\n    static {\n      this.ɵpipe = /* @__PURE__ */i0.ɵɵdefinePipe({\n        name: \"markdown\",\n        type: MarkdownPipe,\n        pure: true,\n        standalone: true\n      });\n    }\n  }\n  return MarkdownPipe;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction provideMarkdown(markdownModuleConfig) {\n  return [MarkdownService, markdownModuleConfig?.loader ?? [], markdownModuleConfig?.clipboardOptions ?? [], markdownModuleConfig?.markedOptions ?? [], {\n    provide: MARKED_EXTENSIONS,\n    useValue: markdownModuleConfig?.markedExtensions ?? []\n  }, {\n    provide: SECURITY_CONTEXT,\n    useValue: markdownModuleConfig?.sanitize ?? SecurityContext.HTML\n  }];\n}\nconst sharedDeclarations = [ClipboardButtonComponent, LanguagePipe, MarkdownComponent, MarkdownPipe];\nlet MarkdownModule = /*#__PURE__*/(() => {\n  class MarkdownModule {\n    static forRoot(markdownModuleConfig) {\n      return {\n        ngModule: MarkdownModule,\n        providers: [provideMarkdown(markdownModuleConfig)]\n      };\n    }\n    static forChild() {\n      return {\n        ngModule: MarkdownModule\n      };\n    }\n    static {\n      this.ɵfac = function MarkdownModule_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || MarkdownModule)();\n      };\n    }\n    static {\n      this.ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n        type: MarkdownModule\n      });\n    }\n    static {\n      this.ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n        imports: [CommonModule]\n      });\n    }\n  }\n  return MarkdownModule;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/* eslint-disable */\nvar MermaidAPI;\n(function (MermaidAPI) {\n  let SecurityLevel;\n  (function (SecurityLevel) {\n    /**\n     * (default) tags in text are encoded, click functionality is disabled\n     */\n    SecurityLevel[\"Strict\"] = \"strict\";\n    /**\n     * tags in text are allowed, click functionality is enabled\n     */\n    SecurityLevel[\"Loose\"] = \"loose\";\n    /**\n     * html tags in text are allowed, (only script element is removed), click functionality is enabled\n     */\n    SecurityLevel[\"Antiscript\"] = \"antiscript\";\n    /**\n     * with this security level all rendering takes place in a sandboxed iframe.\n     * This prevent any javascript running in the context.\n     * This may hinder interactive functionality of the diagram like scripts,\n     * popups in sequence diagram or links to other tabs/targets etc.\n     */\n    SecurityLevel[\"Sandbox\"] = \"sandbox\";\n  })(SecurityLevel = MermaidAPI.SecurityLevel || (MermaidAPI.SecurityLevel = {}));\n  let Theme;\n  (function (Theme) {\n    /**\n     * Designed to modified, as the name implies it is supposed to be used as the base for making custom themes.\n     */\n    Theme[\"Base\"] = \"base\";\n    /**\n     * A theme full of light greens that is easy on the eyes.\n     */\n    Theme[\"Forest\"] = \"forest\";\n    /**\n     * A theme that would go well with other dark colored elements.\n     */\n    Theme[\"Dark\"] = \"dark\";\n    /**\n     *  The default theme for all diagrams.\n     */\n    Theme[\"Default\"] = \"default\";\n    /**\n     * The theme to be used for black and white printing\n     */\n    Theme[\"Neutral\"] = \"neutral\";\n  })(Theme = MermaidAPI.Theme || (MermaidAPI.Theme = {}));\n  let LogLevel;\n  (function (LogLevel) {\n    LogLevel[LogLevel[\"Debug\"] = 1] = \"Debug\";\n    LogLevel[LogLevel[\"Info\"] = 2] = \"Info\";\n    LogLevel[LogLevel[\"Warn\"] = 3] = \"Warn\";\n    LogLevel[LogLevel[\"Error\"] = 4] = \"Error\";\n    LogLevel[LogLevel[\"Fatal\"] = 5] = \"Fatal\";\n  })(LogLevel = MermaidAPI.LogLevel || (MermaidAPI.LogLevel = {}));\n})(MermaidAPI || (MermaidAPI = {}));\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { CLIPBOARD_OPTIONS, ClipboardButtonComponent, ExtendedRenderer, KatexSpecificOptions, LanguagePipe, MARKED_EXTENSIONS, MARKED_OPTIONS, MarkdownComponent, MarkdownModule, MarkdownPipe, MarkdownService, MermaidAPI, PrismPlugin, SECURITY_CONTEXT, errorClipboardNotLoaded, errorClipboardViewContainerRequired, errorJoyPixelsNotLoaded, errorKatexNotLoaded, errorMermaidNotLoaded, errorSrcWithoutHttpClient, provideMarkdown };\n","/**\n * @license Angular v18.2.8\n * (c) 2010-2024 Google LLC. https://angular.io/\n * License: MIT\n */\n\nimport { assertInInjectionContext, inject, DestroyRef, ɵRuntimeError, ɵgetOutputDestroyRef, Injector, effect, untracked, assertNotInReactiveContext, signal, computed } from '@angular/core';\nimport { Observable, ReplaySubject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n/**\n * Operator which completes the Observable when the calling context (component, directive, service,\n * etc) is destroyed.\n *\n * @param destroyRef optionally, the `DestroyRef` representing the current context. This can be\n *     passed explicitly to use `takeUntilDestroyed` outside of an [injection\n * context](guide/di/dependency-injection-context). Otherwise, the current `DestroyRef` is injected.\n *\n * @developerPreview\n */\nfunction takeUntilDestroyed(destroyRef) {\n  if (!destroyRef) {\n    assertInInjectionContext(takeUntilDestroyed);\n    destroyRef = inject(DestroyRef);\n  }\n  const destroyed$ = new Observable(observer => {\n    const unregisterFn = destroyRef.onDestroy(observer.next.bind(observer));\n    return unregisterFn;\n  });\n  return source => {\n    return source.pipe(takeUntil(destroyed$));\n  };\n}\n\n/**\n * Implementation of `OutputRef` that emits values from\n * an RxJS observable source.\n *\n * @internal\n */\nclass OutputFromObservableRef {\n  constructor(source) {\n    this.source = source;\n    this.destroyed = false;\n    this.destroyRef = inject(DestroyRef);\n    this.destroyRef.onDestroy(() => {\n      this.destroyed = true;\n    });\n  }\n  subscribe(callbackFn) {\n    if (this.destroyed) {\n      throw new ɵRuntimeError(953 /* ɵRuntimeErrorCode.OUTPUT_REF_DESTROYED */, ngDevMode && 'Unexpected subscription to destroyed `OutputRef`. ' + 'The owning directive/component is destroyed.');\n    }\n    // Stop yielding more values when the directive/component is already destroyed.\n    const subscription = this.source.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n      next: value => callbackFn(value)\n    });\n    return {\n      unsubscribe: () => subscription.unsubscribe()\n    };\n  }\n}\n/**\n * Declares an Angular output that is using an RxJS observable as a source\n * for events dispatched to parent subscribers.\n *\n * The behavior for an observable as source is defined as followed:\n *    1. New values are forwarded to the Angular output (next notifications).\n *    2. Errors notifications are not handled by Angular. You need to handle these manually.\n *       For example by using `catchError`.\n *    3. Completion notifications stop the output from emitting new values.\n *\n * @usageNotes\n * Initialize an output in your directive by declaring a\n * class field and initializing it with the `outputFromObservable()` function.\n *\n * ```ts\n * @Directive({..})\n * export class MyDir {\n *   nameChange$ = ;\n *   nameChange = outputFromObservable(this.nameChange$);\n * }\n * ```\n *\n * @developerPreview\n */\nfunction outputFromObservable(observable, opts) {\n  ngDevMode && assertInInjectionContext(outputFromObservable);\n  return new OutputFromObservableRef(observable);\n}\n\n/**\n * Converts an Angular output declared via `output()` or `outputFromObservable()`\n * to an observable.\n *\n * You can subscribe to the output via `Observable.subscribe` then.\n *\n * @developerPreview\n */\nfunction outputToObservable(ref) {\n  const destroyRef = ɵgetOutputDestroyRef(ref);\n  return new Observable(observer => {\n    // Complete the observable upon directive/component destroy.\n    // Note: May be `undefined` if an `EventEmitter` is declared outside\n    // of an injection context.\n    destroyRef?.onDestroy(() => observer.complete());\n    const subscription = ref.subscribe(v => observer.next(v));\n    return () => subscription.unsubscribe();\n  });\n}\n\n/**\n * Exposes the value of an Angular `Signal` as an RxJS `Observable`.\n *\n * The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.\n *\n * `toObservable` must be called in an injection context unless an injector is provided via options.\n *\n * @developerPreview\n */\nfunction toObservable(source, options) {\n  !options?.injector && assertInInjectionContext(toObservable);\n  const injector = options?.injector ?? inject(Injector);\n  const subject = new ReplaySubject(1);\n  const watcher = effect(() => {\n    let value;\n    try {\n      value = source();\n    } catch (err) {\n      untracked(() => subject.error(err));\n      return;\n    }\n    untracked(() => subject.next(value));\n  }, {\n    injector,\n    manualCleanup: true\n  });\n  injector.get(DestroyRef).onDestroy(() => {\n    watcher.destroy();\n    subject.complete();\n  });\n  return subject.asObservable();\n}\n\n/**\n * Get the current value of an `Observable` as a reactive `Signal`.\n *\n * `toSignal` returns a `Signal` which provides synchronous reactive access to values produced\n * by the given `Observable`, by subscribing to that `Observable`. The returned `Signal` will always\n * have the most recent value emitted by the subscription, and will throw an error if the\n * `Observable` errors.\n *\n * With `requireSync` set to `true`, `toSignal` will assert that the `Observable` produces a value\n * immediately upon subscription. No `initialValue` is needed in this case, and the returned signal\n * does not include an `undefined` type.\n *\n * By default, the subscription will be automatically cleaned up when the current [injection\n * context](guide/di/dependency-injection-context) is destroyed. For example, when `toSignal` is\n * called during the construction of a component, the subscription will be cleaned up when the\n * component is destroyed. If an injection context is not available, an explicit `Injector` can be\n * passed instead.\n *\n * If the subscription should persist until the `Observable` itself completes, the `manualCleanup`\n * option can be specified instead, which disables the automatic subscription teardown. No injection\n * context is needed in this configuration as well.\n *\n * @developerPreview\n */\nfunction toSignal(source, options) {\n  ngDevMode && assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' + 'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.');\n  const requiresCleanup = !options?.manualCleanup;\n  requiresCleanup && !options?.injector && assertInInjectionContext(toSignal);\n  const cleanupRef = requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null;\n  const equal = makeToSignalEqual(options?.equal);\n  // Note: T is the Observable value type, and U is the initial value type. They don't have to be\n  // the same - the returned signal gives values of type `T`.\n  let state;\n  if (options?.requireSync) {\n    // Initially the signal is in a `NoValue` state.\n    state = signal({\n      kind: 0 /* StateKind.NoValue */\n    }, {\n      equal\n    });\n  } else {\n    // If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.\n    state = signal({\n      kind: 1 /* StateKind.Value */,\n      value: options?.initialValue\n    }, {\n      equal\n    });\n  }\n  // Note: This code cannot run inside a reactive context (see assertion above). If we'd support\n  // this, we would subscribe to the observable outside of the current reactive context, avoiding\n  // that side-effect signal reads/writes are attribute to the current consumer. The current\n  // consumer only needs to be notified when the `state` signal changes through the observable\n  // subscription. Additional context (related to async pipe):\n  // https://github.com/angular/angular/pull/50522.\n  const sub = source.subscribe({\n    next: value => state.set({\n      kind: 1 /* StateKind.Value */,\n      value\n    }),\n    error: error => {\n      if (options?.rejectErrors) {\n        // Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes\n        // the error to end up as an uncaught exception.\n        throw error;\n      }\n      state.set({\n        kind: 2 /* StateKind.Error */,\n        error\n      });\n    }\n    // Completion of the Observable is meaningless to the signal. Signals don't have a concept of\n    // \"complete\".\n  });\n  if (options?.requireSync && state().kind === 0 /* StateKind.NoValue */) {\n    throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');\n  }\n  // Unsubscribe when the current context is destroyed, if requested.\n  cleanupRef?.onDestroy(sub.unsubscribe.bind(sub));\n  // The actual returned signal is a `computed` of the `State` signal, which maps the various states\n  // to either values or errors.\n  return computed(() => {\n    const current = state();\n    switch (current.kind) {\n      case 1 /* StateKind.Value */:\n        return current.value;\n      case 2 /* StateKind.Error */:\n        throw current.error;\n      case 0 /* StateKind.NoValue */:\n        // This shouldn't really happen because the error is thrown on creation.\n        throw new ɵRuntimeError(601 /* ɵRuntimeErrorCode.REQUIRE_SYNC_WITHOUT_SYNC_EMIT */, (typeof ngDevMode === 'undefined' || ngDevMode) && '`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');\n    }\n  }, {\n    equal: options?.equal\n  });\n}\nfunction makeToSignalEqual(userEquality = Object.is) {\n  return (a, b) => a.kind === 1 /* StateKind.Value */ && b.kind === 1 /* StateKind.Value */ && userEquality(a.value, b.value);\n}\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { outputFromObservable, outputToObservable, takeUntilDestroyed, toObservable, toSignal };\n","import * as i0 from '@angular/core';\nimport { InjectionToken, inject, NgZone, ɵisPromise as _isPromise, Injectable, input, PLATFORM_ID, Directive, Output, ElementRef, Component, ChangeDetectionStrategy, ViewChild, makeStateKey } from '@angular/core';\nimport { from, of, Observable, Subject, BehaviorSubject, defer } from 'rxjs';\nimport { map, tap, shareReplay, mergeMap, filter, switchMap } from 'rxjs/operators';\nimport { isPlatformBrowser, NgStyle, NgClass } from '@angular/common';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nconst _c0 = [\"container\"];\nconst LOTTIE_OPTIONS = new InjectionToken('LottieOptions');\nfunction convertPlayerOrLoaderToObservable() {\n  const ngZone = inject(NgZone);\n  const {\n    player,\n    useWebWorker\n  } = inject(LOTTIE_OPTIONS);\n  const playerOrLoader = ngZone.runOutsideAngular(() => player());\n  // We need to use `isPromise` instead of checking whether\n  // `result instanceof Promise`. In zone.js patched environments, `global.Promise`\n  // is the `ZoneAwarePromise`. Some APIs, which are likely not patched by zone.js\n  // for certain reasons, might not work with `instanceof`. For instance, the dynamic\n  // import `() => import('./chunk.js')` returns a native promise (not a `ZoneAwarePromise`),\n  // causing this check to be falsy.\n  const player$ = _isPromise(playerOrLoader) ? from(playerOrLoader).pipe(map(module => module.default || module)) : of(playerOrLoader);\n  return player$.pipe(\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  tap(player => player.useWebWorker?.(useWebWorker)), shareReplay({\n    bufferSize: 1,\n    refCount: true\n  }));\n}\nlet AnimationLoader = /*#__PURE__*/(() => {\n  class AnimationLoader {\n    constructor() {\n      this.player$ = convertPlayerOrLoaderToObservable().pipe(mergeMap(player => raf$(this.ngZone).pipe(map(() => player))));\n      this.ngZone = inject(NgZone);\n    }\n    loadAnimation(options) {\n      return this.player$.pipe(map(player => this.createAnimationItem(player, options)));\n    }\n    resolveOptions(options, container) {\n      return Object.assign({\n        container,\n        renderer: 'svg',\n        loop: true,\n        autoplay: true\n      }, options);\n    }\n    createAnimationItem(player, options) {\n      return this.ngZone.runOutsideAngular(() => player.loadAnimation(options));\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = function AnimationLoader_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || AnimationLoader)();\n      };\n    }\n    /** @nocollapse */\n    static {\n      this.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n        token: AnimationLoader,\n        factory: AnimationLoader.ɵfac,\n        providedIn: 'root'\n      });\n    }\n  }\n  return AnimationLoader;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction raf$(ngZone) {\n  return new Observable(subscriber => {\n    const requestId = ngZone.runOutsideAngular(() => requestAnimationFrame(() => {\n      subscriber.next();\n      subscriber.complete();\n    }));\n    return () => cancelAnimationFrame(requestId);\n  });\n}\nlet CacheableAnimationLoader = /*#__PURE__*/(() => {\n  class CacheableAnimationLoader extends AnimationLoader {\n    constructor() {\n      super(...arguments);\n      this.cache = new Map();\n    }\n    ngOnDestroy() {\n      this.cache.clear();\n    }\n    loadAnimation(options) {\n      return this.player$.pipe(map(player => {\n        const animationItem = this.createAnimationItem(player, this.transformOptions(options));\n        this.awaitConfigAndCache(options, animationItem);\n        return animationItem;\n      }));\n    }\n    awaitConfigAndCache(options, animationItem) {\n      if (this.isAnimationConfigWithPath(options)) {\n        // Don't wait for the `config_ready` event if it has been cached previously.\n        if (this.cache.has(options.path)) {\n          return;\n        }\n        animationItem.addEventListener('config_ready', () => {\n          // See the comments below on why we're storing the animation data as a string.\n          this.cache.set(options.path, JSON.stringify(animationItem['animationData']));\n        });\n      }\n    }\n    transformOptions(options) {\n      if (this.isAnimationConfigWithPath(options) && this.cache.has(options.path)) {\n        return {\n          ...options,\n          path: undefined,\n          // Caretaker note: `lottie-web` cannot re-use the `animationData` object between animations, and we\n          // have to retrieve a new object each time an animation is created.\n          // https://github.com/airbnb/lottie-web#html\n          // See comments for the `animationData` property.\n          animationData: JSON.parse(this.cache.get(options.path))\n        };\n      } else {\n        return options;\n      }\n    }\n    isAnimationConfigWithPath(options) {\n      return typeof options.path === 'string';\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = /* @__PURE__ */(() => {\n        let ɵCacheableAnimationLoader_BaseFactory;\n        return function CacheableAnimationLoader_Factory(__ngFactoryType__) {\n          return (ɵCacheableAnimationLoader_BaseFactory || (ɵCacheableAnimationLoader_BaseFactory = i0.ɵɵgetInheritedFactory(CacheableAnimationLoader)))(__ngFactoryType__ || CacheableAnimationLoader);\n        };\n      })();\n    }\n    /** @nocollapse */\n    static {\n      this.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n        token: CacheableAnimationLoader,\n        factory: CacheableAnimationLoader.ɵfac,\n        providedIn: 'root'\n      });\n    }\n  }\n  return CacheableAnimationLoader;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction provideCacheableAnimationLoader() {\n  return [{\n    provide: AnimationLoader,\n    useExisting: CacheableAnimationLoader\n  }];\n}\nfunction provideLottieOptions(options) {\n  return [{\n    provide: LOTTIE_OPTIONS,\n    useValue: options\n  }];\n}\nlet BaseDirective = /*#__PURE__*/(() => {\n  class BaseDirective {\n    constructor() {\n      this.options = input(null);\n      this.containerClass = input(null);\n      this.styles = input(null);\n      /**\n       * `animationCreated` is dispatched after calling `loadAnimation`.\n       */\n      this.animationCreated = this.getAnimationItem();\n      /**\n       * `complete` is dispatched after completing the last frame.\n       */\n      this.complete = this.awaitAnimationItemAndStartListening('complete');\n      /**\n       * `loopComplete` is dispatched after completing the frame loop.\n       */\n      this.loopComplete = this.awaitAnimationItemAndStartListening('loopComplete');\n      /**\n       * `enterFrame` is dispatched after entering the new frame.\n       */\n      this.enterFrame = this.awaitAnimationItemAndStartListening('enterFrame');\n      /**\n       * `segmentStart` is dispatched when the new segment is adjusted.\n       */\n      this.segmentStart = this.awaitAnimationItemAndStartListening('segmentStart');\n      /**\n       * Original event name is `config_ready`. `config_ready` is dispatched\n       * after the needed renderer is configured.\n       */\n      this.configReady = this.awaitAnimationItemAndStartListening('config_ready');\n      /**\n       * Original event name is `data_ready`. `data_ready` is dispatched\n       * when all parts of the animation have been loaded.\n       */\n      this.dataReady = this.awaitAnimationItemAndStartListening('data_ready');\n      /**\n       * Original event name is `DOMLoaded`. `DOMLoaded` is dispatched\n       * when elements have been added to the DOM.\n       */\n      this.domLoaded = this.awaitAnimationItemAndStartListening('DOMLoaded');\n      /**\n       * `destroy` will be dispatched when the component gets destroyed,\n       * it's handy for releasing resources.\n       */\n      this.destroy = this.awaitAnimationItemAndStartListening('destroy');\n      /**\n       * `error` will be dispatched if the Lottie player could not render\n       * some frame or parse config.\n       */\n      this.error = this.awaitAnimationItemAndStartListening('error');\n      this.ngZone = inject(NgZone);\n      this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n      this.animationLoader = inject(AnimationLoader);\n      this.loadAnimation$ = new Subject();\n      this.animationItem$ = new BehaviorSubject(null);\n      this.setupLoadAnimationListener();\n    }\n    ngOnDestroy() {\n      this.destroyAnimation();\n    }\n    loadAnimation(changes, container) {\n      this.ngZone.runOutsideAngular(() => this.loadAnimation$.next([changes, container]));\n    }\n    getAnimationItem() {\n      return defer(() => this.animationItem$).pipe(filter(animationItem => animationItem !== null));\n    }\n    awaitAnimationItemAndStartListening(name) {\n      return this.getAnimationItem().pipe(switchMap(animationItem =>\n      // `fromEvent` will try to call `removeEventListener` when `unsubscribe()` is invoked.\n      // The problem is that `ngOnDestroy()` is called before Angular unsubscribes from\n      // `@Output()` properties, thus `animationItem` will be `null` already, also `lottie-web`\n      // removes event listeners when calling `destroy()`.\n      new Observable(observer => {\n        this.ngZone.runOutsideAngular(() => {\n          animationItem.addEventListener(name, event => {\n            this.ngZone.runOutsideAngular(() => {\n              observer.next(event);\n            });\n          });\n        });\n      })));\n    }\n    setupLoadAnimationListener() {\n      const loadAnimation$ = this.loadAnimation$.pipe(filter(([changes]) => this.isBrowser && changes.options !== undefined));\n      loadAnimation$.pipe(switchMap(([changes, container]) => {\n        this.destroyAnimation();\n        return this.animationLoader.loadAnimation(this.animationLoader.resolveOptions(changes.options.currentValue, container));\n      }), takeUntilDestroyed()).subscribe(animationItem => {\n        this.ngZone.run(() => this.animationItem$.next(animationItem));\n      });\n    }\n    destroyAnimation() {\n      const animationItem = this.animationItem$.getValue();\n      // The `ng-lottie` component or the `lottie` directive can be destroyed\n      // before the `animationItem` is set, thus it will fail with\n      // `Cannot read property 'destroy' of null`.\n      // Potentially it can happen if the directive gets destroyed before change\n      // detection is run.\n      if (animationItem === null) {\n        return;\n      }\n      // `destroy()` will remove all events listeners.\n      animationItem.destroy();\n      this.animationItem$.next(null);\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = function BaseDirective_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || BaseDirective)();\n      };\n    }\n    /** @nocollapse */\n    static {\n      this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n        type: BaseDirective,\n        selectors: [[\"\", \"lottie\", \"\"]],\n        inputs: {\n          options: [1, \"options\"],\n          containerClass: [1, \"containerClass\"],\n          styles: [1, \"styles\"]\n        },\n        outputs: {\n          animationCreated: \"animationCreated\",\n          complete: \"complete\",\n          loopComplete: \"loopComplete\",\n          enterFrame: \"enterFrame\",\n          segmentStart: \"segmentStart\",\n          configReady: \"configReady\",\n          dataReady: \"dataReady\",\n          domLoaded: \"domLoaded\",\n          destroy: \"destroy\",\n          error: \"error\"\n        }\n      });\n    }\n  }\n  return BaseDirective;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet LottieDirective = /*#__PURE__*/(() => {\n  class LottieDirective extends BaseDirective {\n    constructor() {\n      super(...arguments);\n      this.host = inject(ElementRef);\n    }\n    ngOnChanges(changes) {\n      super.loadAnimation(changes, this.host.nativeElement);\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = /* @__PURE__ */(() => {\n        let ɵLottieDirective_BaseFactory;\n        return function LottieDirective_Factory(__ngFactoryType__) {\n          return (ɵLottieDirective_BaseFactory || (ɵLottieDirective_BaseFactory = i0.ɵɵgetInheritedFactory(LottieDirective)))(__ngFactoryType__ || LottieDirective);\n        };\n      })();\n    }\n    /** @nocollapse */\n    static {\n      this.ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n        type: LottieDirective,\n        selectors: [[\"\", \"lottie\", \"\"]],\n        standalone: true,\n        features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature]\n      });\n    }\n  }\n  return LottieDirective;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet LottieComponent = /*#__PURE__*/(() => {\n  class LottieComponent extends BaseDirective {\n    constructor() {\n      super(...arguments);\n      this.width = input(null);\n      this.height = input(null);\n      this.container = null;\n    }\n    ngOnChanges(changes) {\n      super.loadAnimation(changes, this.container.nativeElement);\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = /* @__PURE__ */(() => {\n        let ɵLottieComponent_BaseFactory;\n        return function LottieComponent_Factory(__ngFactoryType__) {\n          return (ɵLottieComponent_BaseFactory || (ɵLottieComponent_BaseFactory = i0.ɵɵgetInheritedFactory(LottieComponent)))(__ngFactoryType__ || LottieComponent);\n        };\n      })();\n    }\n    /** @nocollapse */\n    static {\n      this.ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n        type: LottieComponent,\n        selectors: [[\"ng-lottie\"]],\n        viewQuery: function LottieComponent_Query(rf, ctx) {\n          if (rf & 1) {\n            i0.ɵɵviewQuery(_c0, 7);\n          }\n          if (rf & 2) {\n            let _t;\n            i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.container = _t.first);\n          }\n        },\n        inputs: {\n          width: [1, \"width\"],\n          height: [1, \"height\"]\n        },\n        standalone: true,\n        features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature, i0.ɵɵStandaloneFeature],\n        decls: 2,\n        vars: 6,\n        consts: [[\"container\", \"\"], [3, \"ngStyle\", \"ngClass\"]],\n        template: function LottieComponent_Template(rf, ctx) {\n          if (rf & 1) {\n            i0.ɵɵelement(0, \"div\", 1, 0);\n          }\n          if (rf & 2) {\n            i0.ɵɵstyleProp(\"width\", ctx.width() || \"100%\")(\"height\", ctx.height() || \"100%\");\n            i0.ɵɵproperty(\"ngStyle\", ctx.styles())(\"ngClass\", ctx.containerClass());\n          }\n        },\n        dependencies: [NgStyle, NgClass],\n        encapsulation: 2,\n        changeDetection: 0\n      });\n    }\n  }\n  return LottieComponent;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nfunction transformAnimationFilenameToKey(animation) {\n  const [animationName] = animation.split('.json');\n  return `animation-${animationName}`;\n}\nlet LottieTransferState = /*#__PURE__*/(() => {\n  class LottieTransferState {\n    constructor(transferState) {\n      this.transferState = transferState;\n    }\n    get(animation) {\n      const animationKey = transformAnimationFilenameToKey(animation);\n      const stateKey = makeStateKey(animationKey);\n      return this.transferState.get(stateKey, null);\n    }\n    /** @nocollapse */\n    static {\n      this.ɵfac = function LottieTransferState_Factory(__ngFactoryType__) {\n        return new (__ngFactoryType__ || LottieTransferState)(i0.ɵɵinject(i0.TransferState));\n      };\n    }\n    /** @nocollapse */\n    static {\n      this.ɵprov = /* @__PURE__ */i0.ɵɵdefineInjectable({\n        token: LottieTransferState,\n        factory: LottieTransferState.ɵfac,\n        providedIn: 'root'\n      });\n    }\n  }\n  return LottieTransferState;\n})();\n(() => {\n  (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { AnimationLoader, BaseDirective, LottieComponent, LottieDirective, LottieTransferState, provideCacheableAnimationLoader, provideLottieOptions, transformAnimationFilenameToKey, CacheableAnimationLoader as ɵCacheableAnimationLoader, LOTTIE_OPTIONS as ɵLOTTIE_OPTIONS };\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n  name: 'mediaSeconds'\n})\nexport class MediaSecondsPipe implements PipeTransform {\n\n  transform(seconds: number | string) {\n    if (typeof seconds === 'string')\n      seconds = parseInt(seconds);\n\n    if (isNaN(seconds) || seconds < 0)\n      return '0:00';\n\n    const minutes = Math.floor(seconds / 60);\n    const remainingSeconds = Math.floor(seconds % 60);\n\n    const res = `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`;\n    return res;\n  }\n}\n","import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';\n\nconst ROUND_SIZE: number = 300;\n\n@Component({\n  selector: 'app-round',\n  templateUrl: './round.component.html',\n  styleUrls: ['./round.component.scss']\n})\nexport class RoundComponent implements OnInit {\n  @ViewChild('videoElement') videoElement!: ElementRef;\n  @Input() url: string | undefined;\n  @Input() duration: string | number | undefined;\n  @Input() isLoading: boolean | undefined = false;\n  @Output() onLoadRound = new EventEmitter();\n  protected isSound: boolean = false;\n  protected isActive = false;\n  protected progress = 0;\n  protected remainingDuration!: string | number | undefined;\n\n  ngOnInit() {\n    this.remainingDuration = this.duration;\n  }\n\n  protected loadRound() {\n    this.onLoadRound.emit();\n  }\n\n  protected togglePlayPause(): void {\n    const video: HTMLVideoElement = this.videoElement.nativeElement;\n\n    if (video.paused || !this.isActive)\n      video.play();\n    else\n      video.pause();\n\n    this.isSound = !video.paused;\n\n    if (!this.isActive) {\n      this.isActive = true;\n      video.currentTime = 0;\n      this.videoElement.nativeElement.addEventListener('timeupdate', this.updateProgress);\n    }\n  }\n\n  protected updateProgress = () => {\n    if (this.videoElement) {\n      const video: HTMLVideoElement = this.videoElement.nativeElement;\n      this.progress = (video.currentTime / video.duration) * 100;\n      this.remainingDuration = video.duration - video.currentTime;\n    }\n  }\n\n  protected onVideoEnded() {\n    const video: HTMLVideoElement = this.videoElement.nativeElement;\n    video.currentTime = 0;\n    video.play();\n    this.progress = 0;\n    this.remainingDuration = this.duration;\n    this.isActive = false;\n    this.isSound = false;\n    this.videoElement.nativeElement.removeEventListener('timeupdate', this.updateProgress);\n  }\n\n  protected readonly ROUND_SIZE = ROUND_SIZE;\n  protected readonly Math = Math;\n}\n","
    \n
    \n
    \n {{ remainingDuration | mediaSeconds }}\n no_sound\n
    \n \n close\n \n
    \n \n download\n \n \n \n \n
    \n \n \n \n
    \n
    \n
    \n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'linkify'\n})\nexport class LinkifyPipe implements PipeTransform {\n\n transform(text: string) {\n return text.replace(/(https?:\\/\\/[^\\s]+)/g, url => `
    ${url}`);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'fileSize'\n})\nexport class FileSizePipe implements PipeTransform {\n\n transform(bytes: number, decimal: number = 2): string {\n if (bytes === 0) {\n return '-';\n }\n\n const k = 1024;\n const dm = decimal < 0 ? 0 : decimal;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const size = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));\n\n return `${size} ${sizes[i]}`;\n }\n\n}","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'highlight'\n})\nexport class HighlightPipe implements PipeTransform {\n transform(value: string, args: string[] | string): string {\n if (!args.length) {\n return value;\n }\n const pattern = Array.isArray(args)\n ? args\n .filter(arg => !!arg)\n .map(this.escapeRegex)\n .join(\"|\")\n : this.escapeRegex(args);\n const regex = new RegExp(pattern.concat(\"|<[^>]*>\"), \"gi\");\n return value.replace(regex, match =>\n /<[^>]*>/g.test(match)\n ? match\n : `${match}`\n );\n }\n\n private escapeRegex(word: string) {\n return word.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\n@Pipe({\n name: 'safeHtml'\n})\nexport class SafeHtmlPipe implements PipeTransform {\n constructor(private sanitizer: DomSanitizer) {}\n\n transform(value: string): SafeHtml {\n return this.sanitizer.bypassSecurityTrustHtml(value);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'truncate'\n})\nexport class TruncatePipe implements PipeTransform {\n transform(value: string, maxLength: number = 20): string {\n if (!value || value.length <= maxLength) {\n return value;\n }\n const start = value.substring(0, Math.floor(maxLength / 2));\n const end = value.substring(value.length - Math.ceil(maxLength / 2));\n return `${start}...${end}`;\n }\n}\n","import { Component, Input } from '@angular/core';\n\nexport enum NO_RESULTS_TYPES {\n DEFAULT = 'DEFAULT',\n}\n\n@Component({\n selector: 'app-no-results',\n templateUrl: './no-results.component.html',\n styleUrl: './no-results.component.scss'\n})\nexport class NoResultsComponent {\n @Input() wrap: boolean = false;\n @Input() type: NO_RESULTS_TYPES = NO_RESULTS_TYPES.DEFAULT;\n @Input() text: string = '';\n\n protected readonly NO_RESULTS_TYPES = NO_RESULTS_TYPES;\n}\n","\n \n
    \n
    \n
    \n
    \n","import { Directive, ElementRef, HostListener, OnInit } from '@angular/core';\n\n@Directive({\n selector: '[appTextareaAutoresize]',\n})\nexport class TextareaAutoresizeDirective implements OnInit {\n constructor(private elementRef: ElementRef) {}\n\n ngOnInit() {\n if (this.elementRef.nativeElement.scrollHeight) {\n setTimeout(() => this.resize());\n }\n }\n\n @HostListener(':input')\n onInput() {\n this.resize();\n }\n\n resize() {\n this.elementRef.nativeElement.style.height = 0;\n this.elementRef.nativeElement.style.height = this.elementRef.nativeElement.scrollHeight + 'px';\n }\n}\n","import { FormControl } from '@angular/forms';\n\nexport interface ISettingsItems {\n key: SUB_SETTINGS;\n title: string;\n icon: string;\n is_svg?: boolean;\n action: (item: ISettingsItems) => void;\n}\n\nexport interface ISubSetting {\n key: SUB_SETTINGS;\n title: string;\n}\n\nexport interface SettingsForm {\n keyboard: FormControl,\n is_message_read: FormControl,\n is_lazy_loading: FormControl,\n}\n\nexport enum SUB_SETTINGS {\n GENERAL_SETTINGS = 'GENERAL_SETTINGS',\n BLOCKED_USERS = 'BLOCKED_USERS',\n FAST_REPLY = 'FAST_REPLY',\n}\n\nexport enum SETTINGS_KEYS {\n HOT_KEY_SEND_CHAT = 'hot_key_send_chat',\n IS_MESSAGE_READ = 'is_message_read',\n IS_LAZY_LOADING = 'is_lazy_loading',\n}\n\nexport enum KEYBOARD {\n ENTER = 'enter',\n CMD_ENTER = 'cmd_enter',\n SHIFT_ENTER = 'shift_enter',\n}\n\nexport interface IBlockedUser {\n id: string;\n name: string;\n user_id: number;\n}\n","import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n\n@Component({\n selector: 'app-radio',\n templateUrl: './radio.component.html',\n styleUrls: ['./radio.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n multi: true,\n useExisting: forwardRef(() => RadioComponent)\n }\n ]\n})\nexport class RadioComponent implements OnInit, ControlValueAccessor {\n @Input() name: string = '';\n @Input() value: string = '';\n @Input() label: string = '';\n @Input() disabled: boolean = false;\n @Output() onChangeClick = new EventEmitter();\n @Output() onChangeSave = new EventEmitter();\n @Output() onChangeCancel = new EventEmitter();\n @Output() onValueChange = new EventEmitter();\n\n\n @Input() input:string = '';\n copyInput: string = '';\n\n ngOnInit(): void {\n }\n\n onChange = (data:any) => {}\n onTouch = (_:any) => {}\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouch = fn;\n }\n\n writeValue(value: string) {\n this.input = value;\n this.onChange(value);\n }\n\n /**\n *Func triggered on input change\n */\n onchange(event:any){\n this.input=event;\n this.onChange(event);\n this.onValueChange.emit(event);\n }\n}\n","\n","import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';\nimport { ChatService } from '../../../../../services/chat.service';\nimport {\n ISettingsItems,\n ISubSetting,\n KEYBOARD,\n SETTINGS_KEYS,\n SettingsForm,\n SUB_SETTINGS,\n} from './chat-settings.interface';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { BaseService } from '../../../../../services/base.service';\nimport { SettingsService } from '../../../../../services/settings.service';\nimport { Subject, switchMap } from 'rxjs';\nimport { ITemplate } from '../../../chats-sidebar-old/components/chats-settings-old/chats-settings-old.interface';\nimport { MessagesTemplatesService } from '../../../../../services/messages-templates.service';\nimport { languages } from '../../../../../enum/constants';\nimport { TextAreaForm } from '../../../right-sidebar/components/chat-list-old/chat-old/chat-old.interface';\nimport { DialogService } from '../../../../../services/dialog.service';\nimport { NotificationService } from '../../../../../services/notifications.service';\n\n@Component({\n selector: 'app-chat-settings',\n templateUrl: './chat-settings.component.html',\n styleUrl: './chat-settings.component.scss'\n})\nexport class ChatSettingsComponent implements OnInit, OnDestroy {\n @ViewChild('replyInput') replyInput!: ElementRef;\n @Output() onOpenChats = new EventEmitter();\n private readonly destroy$: Subject = new Subject();\n private readonly subSettings: ISubSetting[] = [\n { key: SUB_SETTINGS.GENERAL_SETTINGS, title: 'chat_settings.general_settings' },\n { key: SUB_SETTINGS.BLOCKED_USERS, title: 'chat_settings.blocked_users' },\n { key: SUB_SETTINGS.FAST_REPLY, title: 'chat_settings.fast_replies' },\n ];\n protected isUpdate!: ITemplate | null;\n protected selectedSubSetting!: ISubSetting | null;\n protected textAreaControl!: FormControl;\n protected settingsItems!: ISettingsItems[];\n protected replies!: ITemplate[];\n protected settingsForm!: FormGroup;\n\n constructor(\n protected readonly baseService: BaseService,\n protected readonly chatService: ChatService,\n protected readonly settingsService: SettingsService,\n protected readonly messagesTemplatesService: MessagesTemplatesService,\n private readonly dialogService: DialogService,\n private readonly notificationService: NotificationService,\n ) {}\n\n public ngOnInit(): void {\n this.initSettings();\n this.initSettingsForm();\n this.initReplies();\n this.initForm();\n }\n\n public ngOnDestroy(): void {\n this.initSettingsForm();\n this.chatService.blockedUsers = [];\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n protected initForm() {\n this.textAreaControl = new FormControl(null, Validators.required);\n }\n\n private initSettings(): void {\n this.settingsItems = [];\n\n this.settingsItems.push({\n key: SUB_SETTINGS.GENERAL_SETTINGS,\n title: 'chat_settings.general_settings',\n icon: 'settings',\n action: (item) => this.openSettings(item.key),\n });\n\n this.settingsItems.push({\n key: SUB_SETTINGS.BLOCKED_USERS,\n title: 'chat_settings.blocked_users',\n icon: 'block',\n action: (item) => this.openSettings(item.key),\n });\n\n this.settingsItems.push({\n key: SUB_SETTINGS.FAST_REPLY,\n title: 'chat_settings.fast_replies',\n icon: 'reply',\n action: (item) => this.openSettings(item.key),\n });\n }\n\n private initSettingsForm() {\n this.settingsForm = new FormGroup({\n keyboard: new FormControl(this.baseService.currentUserInfo.settings.hot_key_send_chat),\n is_message_read: new FormControl(this.baseService.currentUserInfo.settings.is_message_read),\n is_lazy_loading: new FormControl(this.baseService.currentUserInfo.settings.is_lazy_loading),\n })\n }\n\n private openSettings(name: SUB_SETTINGS): void {\n const item = this.subSettings.find((item) => item.key === name);\n if (item) {\n if (item.key === SUB_SETTINGS.BLOCKED_USERS) this.chatService.getUsersBlocked();\n this.selectedSubSetting = item;\n }\n }\n\n protected closeLeftMenu(): void {\n if (this.selectedSubSetting) {\n this.selectedSubSetting = null;\n return;\n }\n this.chatService.closeLeftMenu();\n }\n\n protected changeSettings(key: string, value: boolean) {\n this.settingsService.updateSettings({ [key]: value })\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe()\n }\n\n protected isMacOS(): boolean {\n return /Macintosh|MacIntel|MacPPC|Mac68K/.test(window.navigator.userAgent);\n }\n\n protected unblockUser(id: number): void {\n this.chatService.unblockUser(id);\n }\n\n protected initReplies() {\n this.messagesTemplatesService.get()\n .subscribe({\n next: (res) => {\n this.replies = res;\n },\n });\n }\n\n editTemplate(template: ITemplate) {\n this.isUpdate = template;\n this.textAreaControl.patchValue(template.text);\n this.replyInput.nativeElement.focus();\n }\n\n delete(template: ITemplate) {\n const dialogRef = this.dialogService.openConfirm({\n // text: `\"delete_reply_text\" +':\\n${template.text}?'`,\n text: 'dialogs.chats-settings',\n });\n dialogRef.subscribe({\n next: (result) => {\n if (result) {\n this.messagesTemplatesService.delete(template.id)\n .subscribe({\n next: () => {\n this.initReplies();\n },\n });\n }\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_delete);\n },\n });\n }\n\n cancel() {\n this.textAreaControl.patchValue('');\n this.isUpdate = null;\n }\n\n update() {\n const text = this.textAreaControl.getRawValue();\n const parseText = (text || '').trim();\n if (this.textAreaControl.valid && parseText && this.isUpdate) {\n this.messagesTemplatesService.update(this.isUpdate.id, { text: parseText })\n .subscribe({\n next: () => {\n this.isUpdate = null;\n this.textAreaControl.reset();\n this.initReplies();\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_updating);\n },\n });\n }\n }\n\n save() {\n const text = this.textAreaControl.getRawValue();\n const parseText = (text || '').trim();\n\n if (this.textAreaControl.valid && parseText) {\n this.messagesTemplatesService.save({ text: parseText })\n .subscribe({\n next: () => {\n this.textAreaControl.reset();\n this.initReplies();\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_saving);\n },\n });\n }\n }\n\n protected readonly Object = Object;\n protected readonly SUB_SETTINGS = SUB_SETTINGS;\n protected readonly KEYBOARD = KEYBOARD;\n protected readonly SETTINGS_KEYS = SETTINGS_KEYS;\n}\n","
    \n
    \n \n arrow_back\n \n {{ selectedSubSetting?.title || 'settings' | translate }}\n
    \n
    \n
    \n
    \n
    \n \n {{ item.icon }}\n {{ item.title | translate }}\n
    \n
    \n
    \n
    \n\n\n \n \n
    \n {{ 'chat_settings.keyboard' | translate }}\n \n \n \n
    \n
    \n {{ 'chat_settings.notifications' | translate }}\n \n
    \n
    \n {{ 'chat_settings.other' | translate }}\n \n
    \n
    \n \n
    \n \n {{ 'chat_settings.no_blocked_users' | translate }}\n \n
    \n \n no_encryption\n \n {{ user.name }}\n
    \n
    \n
    \n \n
    \n
    \n {{ 'chat_settings.add_fast_replies' | translate }}\n
    \n \n
    \n
    \n {{ 'cancel' | translate }}\n
    \n
    \n
    {{ 'change' | translate }}\n
    \n
    {{ 'save' | translate }}\n
    \n
    \n
    \n
    \n
    \n {{ 'chat_settings.saved_fast_replies' | translate }}\n \n {{ 'chat_settings.no_replies' | translate }}\n \n
    \n
    \n {{ reply.text }}\n
    \n \n edit\n \n \n delete\n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n","export interface IAboutAction {\n title: string;\n icon: string;\n tool: TOOLS;\n action: (item: IAboutAction) => void;\n key?: string;\n value?: boolean;\n is_svg?: boolean;\n}\n\nexport enum TOOLS {\n TOGGLE = 'TOGGLE',\n BUTTON = 'BUTTON',\n}\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\n\n\n@Component({\n templateUrl: './one-field.component.html',\n styleUrls: ['./one-field.component.scss'],\n})\nexport class OneFieldComponent {\n\n x_data!: any;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n ) {\n this.x_data = data?.data ?? {};\n }\n\n save() {\n this.dialogRef.close(this.x_data.input?.trim());\n }\n\n onNoClick(): void {\n this.dialogRef.close(false);\n }\n\n}\n","
    \n\n \n\n
    \n\n\n\n
    \n \n {{ x_data.no ?? 'cancel' | translate }}\n \n \n {{ x_data.yes ?? ('save' | translate) }}\n \n
    \n","import { Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';\nimport { ChatService } from '../../../../../services/chat.service';\nimport { CHAT_TYPES, DEFAULT_PROFILE_ICON } from '../../../../../enum/constants';\nimport { BaseService } from '../../../../../services/base.service';\nimport { IChat, IColumnCard } from '../../../../../interfaces';\nimport { parseCurrencyXXX } from '../../../../../helpers';\nimport { Subject, takeUntil } from 'rxjs';\nimport { IAboutAction, TOOLS } from './chat-about.interface';\nimport { OneFieldComponent } from '../../../../../dialogs/one-field/one-field.component';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { CHAT_TABS } from '../chat-list/chat-list.interface';\n\n@Component({\n selector: 'app-chat-about',\n templateUrl: './chat-about.component.html',\n styleUrl: './chat-about.component.scss',\n})\nexport class ChatAboutComponent implements OnChanges, OnDestroy {\n @Input() chat!: IChat;\n private readonly destroy$: Subject = new Subject();\n protected aboutActions!: IAboutAction[];\n\n constructor(\n private readonly modalService: ModalService,\n protected readonly chatService: ChatService,\n protected readonly baseService: BaseService,\n ) {}\n\n ngOnChanges(changes:SimpleChanges): void {\n if(changes['chat']) {\n this.initActions();\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initActions() {\n this.aboutActions = [];\n\n // this.aboutActions.push({\n // title: 'notifications',\n // icon: 'notifications',\n // key: 'notifications',\n // tool: TOOLS.TOGGLE,\n // value: true,\n // action: (item) => this.toggleNotifications(item),\n // });\n\n if (this.chat.is_ai_automation) {\n this.aboutActions.push({\n title: 'automation',\n icon: 'robot',\n is_svg: true,\n key: 'automation',\n tool: TOOLS.TOGGLE,\n value: this.chat.settings?.enable_ai_assistant ?? true,\n action: (item) => this.toggleAutomation(item),\n });\n }\n\n this.aboutActions.push({\n title: this.chat.is_favorite ? 'unpin' : 'pin',\n icon: this.chat.is_favorite ? 'unpin' : 'pin',\n is_svg: true,\n tool: TOOLS.BUTTON,\n action: () => this.onChangePin(),\n });\n\n if (this.chat.status === CHAT_TABS.ACTIVE) {\n this.aboutActions.push({\n title: 'close_chat',\n icon: 'archive',\n tool: TOOLS.BUTTON,\n action: () => this.onMoveChat(),\n });\n } else if (this.chat.status === CHAT_TABS.CLOSED) {\n this.aboutActions.push({\n title: 'open_a_chat',\n icon: 'unarchive',\n tool: TOOLS.BUTTON,\n action: () => this.onMoveChat(),\n });\n }\n\n if (this.chat.entity_type === CHAT_TYPES.OLX) {\n this.aboutActions.push({\n title: this.chat.is_blocked ? 'unblock' : 'block',\n icon: this.chat.is_blocked ? 'do_not_disturb_off' : 'do_not_disturb_on',\n tool: TOOLS.BUTTON,\n action: () => this.onBlockChat(),\n });\n }\n }\n\n private onChangePin() {\n this.chatService.updateChat(this.chat.id, { is_favorite: !this.chat.is_favorite })\n .subscribe();\n }\n\n private onBlockChat() {\n if (this.chat.is_blocked) {\n this.chatService.unblockUser(this.chat.data.interlocutor_id);\n } else {\n this.chatService.blockUser(this.chat.channel_id, this.chat.data.interlocutor_id)\n .subscribe();\n }\n }\n\n private onMoveChat() {\n const status = this.chat.status === CHAT_TABS.ACTIVE ? CHAT_TABS.CLOSED : CHAT_TABS.ACTIVE;\n this.chatService.onMoveChat(this.chat.id, status);\n }\n\n private toggleNotifications(item: IAboutAction) {\n console.log(item.value);\n }\n\n private toggleAutomation(item: IAboutAction) {\n this.chatService.updateSettings(this.chatService.chat.id, {\n enable_ai_assistant: item.value,\n }).subscribe()\n }\n\n protected openDeal(deal: IColumnCard) {\n const url = `${location.origin}/crm/deals?deal=${deal.id}`;\n this.baseService.openSite(url);\n }\n\n protected closeRightMenu() {\n this.chatService.closeRightMenu();\n }\n\n protected onChangeTitleChat() {\n const modalData = {\n title: 'change_name',\n translate: true,\n component: OneFieldComponent,\n data: {\n input: this.chatService.chat.title,\n },\n };\n\n this.modalService.openModal(modalData)\n .pipe(takeUntil(this.destroy$))\n .subscribe(title => {\n if (title) {\n this.chatService.updateChat(this.chatService.selectedChatId, { title })\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n }\n });\n }\n\n protected readonly parseCurrencyXXX = parseCurrencyXXX;\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly Object = Object;\n protected readonly TOOLS = TOOLS;\n protected readonly DEFAULT_PROFILE_ICON = DEFAULT_PROFILE_ICON;\n}\n","
    \n
    \n \n close\n \n {{ 'about_chat' | translate }}\n
    \n \n edit\n \n
    \n\n
    \n \n
    \n\n
    \n
    \n \n \n {{ chat.title }}\n \n {{ chatService.userInfo?.name || chatService.userInfo?.person || 'Client' }}\n \n \n {{ chatService.userInfo?.name }}\n \n \n
    \n
    \n \n
    \n \n
    \n \n
    \n {{ deal.name }}\n {{ deal.column.name }}\n
    \n
    \n {{ parseCurrencyXXX(deal.price ?? 0, deal.currency) }}\n
    \n
    \n
    \n
    \n\n","import { Component, OnDestroy } from '@angular/core';\nimport { RightSidebarService } from '../../../services/right-sidebar.service';\nimport { LEFT_MENU, MenuItem, RIGHT_MENU } from './chat-sidebar.interface';\nimport { CHAT_TYPES } from '../../../enum/constants';\nimport { ChatService } from '../../../services/chat.service';\nimport { BaseService } from '../../../services/base.service';\nimport { Subject } from 'rxjs';\n\n@Component({\n selector: 'app-chats-sidebar',\n templateUrl: './chat-sidebar.component.html',\n styleUrls: ['./chat-sidebar.component.scss']\n})\nexport class ChatSidebarComponent implements OnDestroy {\n private readonly destroy$: Subject = new Subject();\n protected readonly menuItems: MenuItem[] = this.chatService.menuItems;\n\n constructor(\n protected readonly rightSidebarService: RightSidebarService,\n protected readonly chatService: ChatService,\n protected readonly baseService: BaseService,\n ) {\n this.initSelectedTab();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initSelectedTab(): void {\n const selectedTabKey = localStorage.getItem('selectedTabKey');\n if (selectedTabKey) {\n this.chatService.selectedTab = this.menuItems.find(menu => menu.key === selectedTabKey) as MenuItem;\n }\n if (!this.chatService.selectedTab) {\n this.chatService.selectedTab = this.menuItems[0];\n }\n }\n\n protected onClose() {\n this.chatService.closeLeftMenu();\n this.chatService.closeRightMenu();\n this.rightSidebarService.closeChats();\n }\n\n protected onChangeTab(tabKey: string) {\n this.chatService.selectedTab = this.menuItems.find(menu => menu.key === tabKey) || this.menuItems[0];\n localStorage.setItem('selectedTabKey', this.chatService.selectedTab.key);\n }\n\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly LEFT_MENU = LEFT_MENU;\n protected readonly RIGHT_MENU = RIGHT_MENU;\n}\n","\n \n
    \n
    \n
    \n \n close\n \n
    \n \n
    \n \n
    \n {{ item.icon }}\n \"chat\n {{ 'chats_sidebar_menu.' + item.key | translate }}\n
    \n
    \n
    \n
    \n \n
    \n
    \n \n \n\n
    \n \n \n
    \n
    \n
    \n\n \n \n\n
    \n \n \n
    \n
    \n
    \n
    \n
    \n","import { ChangeDetectorRef, Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';\nimport { BaseService } from '../../services/base.service';\nimport { RightSidebarService } from '../../services/right-sidebar.service';\nimport { WebSocketService } from '../../services/gateway.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ChatService } from '../../services/chat.service';\nimport { ThemeService } from '../../services/theme.service';\nimport { IError } from '../../interfaces';\nimport { DEFAULT_ERROR, languages } from '../../enum/constants';\nimport { Subject, takeUntil } from 'rxjs';\n\n@Component({\n selector: 'app-layout',\n templateUrl: './layout.component.html',\n styleUrls: ['./layout.component.scss'],\n})\nexport class LayoutComponent implements OnInit {\n private readonly destroy$: Subject = new Subject();\n protected isTransparent: boolean = false;\n\n constructor(\n private readonly cdr: ChangeDetectorRef,\n private readonly notificationService: NotificationService,\n private readonly chatService: ChatService,\n private readonly webSocketService: WebSocketService,\n protected readonly baseService: BaseService,\n protected readonly rightSidebarService: RightSidebarService,\n protected readonly themeService: ThemeService,\n ) {}\n\n @ViewChild('videoRef', { static: false }) videoRef!: ElementRef;\n\n @HostListener('window:resize', ['$event'])\n onResize() {\n this.baseService.isMobile = window.innerWidth <= 700;\n }\n\n @HostListener('window:orientationchange', ['$event'])\n onOrientation() {\n // if (screen.orientation.angle === 90) {\n // this.notificationService.showInfo('Не роби цього!', 'Одному із наших розробників стало дуже боляче, поверни як було!');\n // }\n }\n\n\n ngOnInit() {\n this.themeService.getTheme();\n this.isTransparent = this.rightSidebarService.getOptions()?.transparent;\n this.webSocketService.notificationV2()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (data) => {\n console.log(data);\n if (data[4] === 'success') {\n this.notificationService.showSuccess(data[1], data[2]);\n }\n else if (data[4] === 'error') {\n this.notificationService.showError(data[1], data[2]);\n }\n else if (data[4] === 'warning') {\n this.notificationService.showWarning(data[1], data[2]);\n }\n else {\n this.notificationService.showInfo(data[1], data[2]);\n }\n this.notificationService.getCountNotification()\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n },\n error: () => {},\n });\n this.webSocketService.notification()\n .subscribe({\n next: (data) => {\n this.chatService.loadCount();\n if (this.baseService.currentUserInfo.settings.is_notification) {\n this.notificationService.showInfo(`${languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.new_messages} (${data})`, '');\n if (this.baseService.currentUserInfo.settings.is_notification_sound) {\n this.notificationService.playSound();\n }\n }\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, DEFAULT_ERROR);\n });\n },\n });\n // this.webSocketService.getMessageCount().subscribe({\n // next: (value) => {\n // if (value !== this.chatService.message_count) {\n // this.chatService.message_count = value;\n // }\n // },\n // error: (error: any) => {\n // console.log(error);\n // },\n // });\n\n this.webSocketService.getNewMessage2()\n .subscribe({\n next: (data) => {\n const { chat, message } = data;\n\n const disableNotification = this.chatService.getNewMessage(chat, message);\n\n if (this.baseService.currentUserInfo.settings.is_notification && !disableNotification) {\n const notificationRef = this.notificationService.showInfoV2(chat.title, message.text);\n notificationRef.onTap.subscribe(() => {\n if (this.rightSidebarService.chatsOpened) {\n this.chatService.selectChat(chat.id);\n } else {\n this.rightSidebarService.openChats(chat.id).subscribe();\n }\n });\n if (this.baseService.currentUserInfo.settings.is_notification_sound) {\n this.notificationService.playSound();\n }\n }\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, DEFAULT_ERROR);\n });\n },\n });\n\n this.cdr.detectChanges();\n }\n\n public getClass() {\n if (this.baseService.isMobile) return 'max-mobile';\n else if (this.rightSidebarService.getOptions().max) return 'max';\n else if (this.rightSidebarService.getOptions().medium) return 'medium';\n else if (this.rightSidebarService.getOptions().min) return 'min';\n return '';\n }\n\n public closeDrawer(chats: boolean = false) {\n if (chats) {\n this.rightSidebarService.closeChats();\n } else {\n this.rightSidebarService.close();\n }\n }\n\n videoPlay() {\n const media = this.videoRef.nativeElement;\n media.muted = true;\n media.play();\n }\n}\n","\n\n \n
    \n close\n
    \n \n \n\n \n \n \n \n\n \n
    \n
    \n \n
    \n \n
    \n \n
    \n \n \n
    \n \n
    \n
    \n
    \n\n\n","import { Component, EventEmitter, Input, OnInit, Optional, Output } from '@angular/core';\nimport { SidebarService } from '../../services/sidebar.service';\nimport { AuthService } from '../../services/auth.service';\nimport { BaseService } from '../../services/base.service';\nimport { Router } from '@angular/router';\n\n@Component({\n selector: 'app-integration-block',\n templateUrl: './integration-block.component.html',\n styleUrls: ['./integration-block.component.scss']\n})\nexport class IntegrationBlockComponent implements OnInit {\n\n @Input() data: any = {};\n @Output() onChangeClick = new EventEmitter();\n\n @Input() title: string = '';\n @Input() bg: string = '#ffffff';\n @Input() color: string = '#000000';\n @Input() dev: boolean = false;\n @Input() new: boolean = false;\n @Input() count: number = 0;\n @Input() active: boolean = false;\n @Input() beta: boolean = false;\n\n constructor(\n public readonly authService: AuthService,\n public readonly baseService: BaseService,\n private readonly sidebarService: SidebarService,\n private readonly router: Router,\n ) {\n }\n\n ngOnInit() {\n if (Object.keys(this.data).length) {\n this.title = this.data.title;\n this.bg = this.data.bg;\n this.color = this.data.color;\n this.dev = this.data.dev;\n this.new = this.data.new;\n }\n }\n\n click(event: any) {\n this.onChangeClick.emit();\n }\n\n}\n","\n \n {{title}}\n \n \n {{'connected' | translate}}\n \n \n {{count}}\n \n \n {{'new' | translate}}!\n \n \n {{'in_development'| translate}}!\n \n\n \n {{'test_mode'| translate}}\n \n\n\n","import {\n Component,\n EventEmitter,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Output,\n SimpleChanges,\n TemplateRef\n} from '@angular/core';\nimport { AuthService } from '../../services/auth.service';\nimport { BaseService } from '../../services/base.service';\nimport { CdkDragDrop, CdkDragMove, CdkDragStart, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';\nimport { Board } from '../../model/board.model';\nimport { Tasks } from '../../model/tasks.model';\nimport { parseCurrencyXXX } from '../../helpers';\nimport { Subject } from 'rxjs';\nimport { IFunnel } from \"../../interfaces\";\n\n\n@Component({\n selector: 'app-board',\n templateUrl: './board.component.html',\n styleUrls: ['./board.component.scss'],\n})\nexport class BoardComponent implements OnInit, OnDestroy, OnChanges {\n// Destroys\n private readonly destroy$: Subject = new Subject();\n\n// Inputs\n @Input() board!: Board;\n @Input() funnel!: IFunnel;\n @Input() isCreate!: Board;\n @Input() createTemplateRef!: TemplateRef;\n @Input() cardTemplateRef!: TemplateRef;\n @Input() connectedList!: string[];\n\n// Outputs\n @Output() onChangeCard = new EventEmitter();\n @Output() onCreateCard = new EventEmitter();\n @Output() onClickTitleCard = new EventEmitter();\n @Output() onClickCard = new EventEmitter();\n @Output() onClickContactCard = new EventEmitter();\n @Output() onScrollEndColumn = new EventEmitter();\n @Output() onQuickCreate = new EventEmitter();\n\n// Var-s\n public quickDealsId: string = '';\n public offsetHeight: number = 130;\n private nameInputVisibility: { [key: string]: boolean } = {};\n\n private scrollSpeed: number = 15;\n private scrollInterval!: any;\n\n protected readonly parseCurrencyXXX = parseCurrencyXXX;\n\n constructor(\n public readonly authService: AuthService,\n public readonly baseService: BaseService,\n ) {}\n\n ngOnInit(): void {\n if (!this.connectedList)\n this.connectedList = this.board?.columns?.map((v: any) => v.id);\n }\n\n ngOnChanges(changes: SimpleChanges | any): void {\n if (changes.board && !changes.board.isFirstChange()) {\n this.connectedList = this.board?.columns?.map((v: any) => v.id);\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public dropGrid(event: CdkDragDrop): void {\n moveItemInArray(this.board.columns, event.previousIndex, event.currentIndex);\n }\n\n public drop(event: CdkDragDrop) {\n if (event.previousContainer === event.container) {\n moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);\n } else {\n transferArrayItem(\n event.previousContainer.data,\n event.container.data,\n event.previousIndex,\n event.currentIndex,\n );\n }\n }\n\n public toggleNameInput(columnId: string) {\n if (!this.isNameInputVisible(columnId)) {\n this.nameInputVisibility[columnId] = true;\n }\n }\n\n public isNameInputVisible(columnId: string): boolean {\n return this.nameInputVisibility[columnId];\n }\n\n public createCard(column: any): void {\n this.closeCreateCard();\n column.is_create = true;\n this.onQuickCreate.emit();\n }\n\n public closeCreateCard(): void {\n this.board.columns.forEach(v => v.is_create = false);\n }\n\n public dragDropped(event: any, item: any): void {\n // const position: number = this.calcPosition(event.currentIndex, event.previousIndex, event.container.id, event.previousContainer.id);\n\n const data = { ...event, currentItem: item };\n this.onChangeCard.emit({ ...data });\n }\n\n private calcPosition(ci: number, pi: number, container_id: string, previousContainer_id: string): number {\n if (previousContainer_id !== container_id) {\n const currentColumn = this.board.columns.filter(v => v.id === container_id)[0]\n\n const prevCardPosition = currentColumn.cards[ci - 1]?.position;\n const currentCardPosition = currentColumn.cards[ci]?.position;\n const isLast: boolean = ci === currentColumn.cards.length;\n\n if (isLast && (currentColumn.cards.length !== 0)) return (prevCardPosition + (prevCardPosition + 1000)) / 2;\n else if (isLast && (currentColumn.cards.length === 0)) return 1000;\n if (prevCardPosition && currentCardPosition) return (prevCardPosition + currentCardPosition) / 2;\n if (currentCardPosition) return currentCardPosition / 2;\n else return 1000;\n } else {\n const currentColumn = this.board.columns.filter(v => v.id === container_id)[0]\n\n if (ci === 0) return (currentColumn.cards[0].position / 2);\n\n if (ci === (currentColumn.cards.length - 1)) {\n const lastPosition: number = currentColumn.cards[currentColumn.cards.length - 1].position;\n\n return ((lastPosition + (lastPosition + 1000)) / 2);\n }\n\n if (ci > pi) return ((currentColumn.cards[ci].position + currentColumn.cards[ci + 1].position) / 2);\n if (pi < ci) return ((currentColumn.cards[ci].position + currentColumn.cards[ci - 1].position) / 2);\n\n return ((currentColumn.cards[ci + 1].position + currentColumn.cards[ci - 1].position) / 2);\n }\n }\n\n public startDrag(event: CdkDragStart) {\n this.offsetHeight = event.source.element.nativeElement.offsetHeight;\n if (window.document.body)\n window.document.body.className = 'is-dragging';\n }\n\n public dragMoved(event: CdkDragMove) {\n this.checkScroll(event.pointerPosition.x);\n }\n\n public endedDrag() {\n this.stopScrolling();\n if (window.document.body)\n window.document.body.className = '';\n }\n\n public open(item: any) {\n this.onClickTitleCard.emit(item);\n }\n\n public clickContact(item: any) {\n this.onClickContactCard.emit(item);\n }\n\n private checkScroll(pointerX: number) {\n const boardElement = document.querySelector('.board');\n if (!boardElement) return;\n\n const { left, right, width } = boardElement.getBoundingClientRect();\n\n if (pointerX < left + width * 0.2) {\n this.startScrolling(-this.scrollSpeed);\n } else if (pointerX > right - width * 0.2) {\n this.startScrolling(this.scrollSpeed);\n } else {\n this.stopScrolling();\n }\n }\n\n private startScrolling(speed: number) {\n this.stopScrolling();\n this.scrollInterval = setInterval(() => {\n const boardElement = document.querySelector('.board');\n if (boardElement) {\n boardElement.scrollLeft += speed;\n }\n }, 20);\n }\n\n private stopScrolling() {\n if (this.scrollInterval) {\n clearInterval(this.scrollInterval);\n this.scrollInterval = null;\n }\n }\n\n scrollTo(e: any, column: any) {\n if (e.target.offsetHeight + e.target.scrollTop >= e.target.scrollHeight - 10) {\n this.onScrollEndColumn.emit(column);\n }\n }\n}\n","
    \n
    \n
    \n
    \n \n
    {{ column.name }}
    ({{ column.count_cards ?? 0 }})\n
    \n
    {{ parseCurrencyXXX(column.profit ?? 0, funnel.currency) }}
    \n \n
    \n add\n {{'quick_deal' | translate}}\n
    \n \n
    \n \n \n \n \n \n
    \n \n
    \n
    \n \n \n\n","import { Injectable } from '@angular/core';\nimport { map, Subject, takeUntil } from 'rxjs';\nimport { HttpClient, HttpHeaders } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\nimport { BaseService } from './base.service';\nimport { Router } from '@angular/router';\nimport { NotificationService } from './notifications.service';\nimport { languages } from '../enum/constants';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ProfileService {\n// URLs\n private profileUrl = environment.baseURL + '/profile/'\n private profilePhoneUrl = environment.baseURL + '/profile/phone'\n private profilePasswordUrl = environment.baseURL + '/profile/password'\n private profileAvatarUrl = environment.baseURL + '/profile/avatar'\n private profileAdditionalUrl = environment.baseURL + '/profile/additional'\n\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly http: HttpClient,\n private readonly router: Router,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) { }\n\n public update(data: any) {\n return this.http.put(this.profileUrl, data)\n .pipe(map(res => res.data));\n }\n\n\n public updatePhone(phone: string) {\n return this.http.post(this.profilePhoneUrl, { phone })\n .pipe(map(res => res.data));\n }\n\n public createAdditional(payload: any) {\n return this.http.post(this.profileAdditionalUrl, payload)\n .pipe(map(res => res.data));\n }\n\n public checkPhone() {\n this.baseService.getProfile()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next:(response) => {\n if (!response.phone) {\n this.router.navigate(['/additional']);\n }\n },\n error: (e) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_getting_profile)\n }\n });\n }\n\n\n changePassword(data: { old_password: string; new_password: string }) {\n return this.http.put(this.profilePhoneUrl, data)\n .pipe(map(res => res.data));\n }\n\n changeAvatar(file: File) {\n const formData = new FormData();\n formData.append('docId', 'opppp');\n formData.append('file', file);\n const headers = new HttpHeaders({\n // 'Content-Type': 'multipart/form-data; boundary {}',\n 'Content-Type': 'multipart/form-data',\n })\n\n return this.http.post(this.profileAvatarUrl, formData)\n }\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, take, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ProfileService } from '../../services/profile.service';\nimport { Router } from '@angular/router';\nimport { BaseService } from '../../services/base.service';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { PhoneForm } from './additional-info.interface';\nimport { languages, NAME_PROJECT } from '../../enum/constants';\nimport { AuthService } from '../../services/auth.service';\n\n\n@Component({\n selector: 'app-additional-info',\n templateUrl: './additional-info.component.html',\n styleUrls: ['./additional-info.component.scss'],\n})\nexport class AdditionalInfoComponent implements OnInit, OnDestroy {\n// Class variables declaration\n protected readonly NAME_PROJECT = NAME_PROJECT;\n\n public phone!: string;\n\n public validation: boolean = false;\n public focused: boolean = false;\n public isSubmitted: boolean = false;\n public isButtonDisabled: boolean = false;\n\n public phoneForm!: FormGroup;\n\n// For destroy\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly router: Router,\n private readonly profileService: ProfileService,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n private readonly authService: AuthService,\n ) {\n }\n\n ngOnInit(): void {\n this.baseService.getProfile()\n .pipe(takeUntil(this.destroy$))\n .pipe(take(1))\n .subscribe((res) => {\n if (res && res.phone) {\n this.router.navigate(['/companies']);\n }\n })\n const user = this.baseService.currentUserInfo;\n if (user && user.phone) {\n this.router.navigate(['/companies']);\n }\n this.phoneForm = new FormGroup({\n phone: new FormControl('', [Validators.required]),\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n handleClick() {\n this.isButtonDisabled = true;\n }\n\n isPhoneValid(phone: string) {\n if (!phone) {\n this.validation = true;\n return false;\n }\n if (!phone.startsWith('380')) {\n return this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.invalid_phone_number_t, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.invalid_phone_number_m_1);\n } else if (phone.length !== 12) {\n return this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.invalid_phone_number_t, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.invalid_phone_number_m_2);\n }\n if (this.validation)\n this.validation = false;\n return true;\n }\n\n addPhoneIntoProfile(): void {\n const referral_code = localStorage.getItem('utm_source');\n this.isSubmitted = true;\n if (this.isPhoneValid(this.phoneForm.controls.phone.value)) {\n this.handleClick();\n this.profileService.createAdditional({\n phone: this.phoneForm.controls.phone.value,\n referral_code: referral_code,\n }).subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_added);\n this.router.navigate(['/companies']);\n },\n error: (errors) => {\n this.isButtonDisabled = false;\n if (errors?.error?.errors[0]?.code === 'PHONE_ALREADY_EXISTS') {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.number_already_exists);\n this.phoneForm.controls.phone.setErrors({});\n } else {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_2);\n console.log('addPhoneError:', errors);\n }\n },\n });\n }\n }\n\n goBack() {\n this.authService.logout();\n }\n}\n","
    \n

    \n {{ NAME_PROJECT }}\n

    \n
    \n

    {{ 'additional_information' | translate }}

    \n
    \n
    \n \n
    \n \n
    \n
    \n
    \n
    \n \n
    \n
    \n {{ 'go_back' | translate }}\n
    \n
    \n
    \n\n","import {\n AfterViewInit,\n Component, ElementRef,\n EventEmitter, HostListener,\n Input,\n OnChanges,\n OnDestroy,\n OnInit,\n Output,\n SimpleChanges,\n ViewChild,\n} from '@angular/core';\nimport { IRiaAdvert, Message, MessageGroup, Tabs } from '../chats-old.interface';\nimport { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';\nimport { MESSAGE_TYPE, SendMessageActionPayload, TextAreaForm } from './chat-old.interface';\nimport { isObjectEmpty, parseCurrency, parseUTC } from 'src/app/helpers';\nimport {\n ADVERT_TYPE,\n CHAT_TYPES,\n DEFAULT_ERROR,\n DEFAULT_PROFILE_ICON,\n ERROR_MESSAGES,\n keyboards,\n languages,\n} from 'src/app/enum/constants';\nimport { Subject, takeUntil, tap } from 'rxjs';\nimport { ChatOldService } from '../../../../../../services/chat-old.service';\nimport { WebSocketService } from '../../../../../../services/gateway.service';\nimport { NotificationService } from '../../../../../../services/notifications.service';\nimport { ModalService } from '../../../../../../services/modal.service';\nimport { BaseService } from '../../../../../../services/base.service';\nimport { DialogService } from '../../../../../../services/dialog.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { LogisticService } from '../../../../../../services/logistic.service';\nimport { MatBottomSheet } from '@angular/material/bottom-sheet';\nimport { Router } from '@angular/router';\nimport { IChat, IError } from '../../../../../../interfaces';\nimport { ProductInfoComponent } from '../../../../../product-info/product-info.component';\nimport { FixTextComponent } from '../../../../../dialogs/fix-text/fix-text.component';\nimport moment from 'moment';\nimport {\n MessageTemplatesComponent\n} from '../../../../../../bottom-sheet/message-templates/message-templates.component';\nimport { OneFieldComponent } from '../../../../../../dialogs/one-field/one-field.component';\nimport { RightSidebarService } from '../../../../../../services/right-sidebar.service';\n\n\n@Component({\n selector: 'app-chat-old',\n templateUrl: './chat-old.component.html',\n styleUrls: ['./chat-old.component.scss'],\n})\n\nexport class ChatOldComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges {\n\n// Inputs for current component\n @Input() chat_id!: string;\n\n// Outputs for current component\n @Output() onReloadChat = new EventEmitter();\n @Output() onReloadChats = new EventEmitter();\n @Output() onCloseChat = new EventEmitter();\n @Output() onLoadChat = new EventEmitter();\n private chat!: IChat;\n\n\n blurChat() {\n this.onCloseChat.emit();\n }\n\n// Variables for current component\n public pinnedMessages: Message[] = [];\n public pinnedLastMessage!: Message;\n public messages: Message[] = [];\n public messagesGroups: MessageGroup[] = [];\n public advert: any = {};\n public me: any = {};\n public interlocutor: any = {};\n public isLoaderMessage: boolean = false;\n public isLoaderIframe: boolean = false;\n public isArrow: boolean = false;\n public textAreaForm!: FormGroup;\n public isShowAdvent = false;\n public files: { file: File, url: string }[] = [];\n public details: any[] = [];\n public is_pause: boolean = false;\n public editableMessage!: number | null;\n public integration_id!: string;\n public searchText: string = '';\n public searchMessages: Message[] = [];\n public messageSearch!: string;\n public currentPinnedIndex = 0;\n public client!: any;\n\n\n showInput: boolean = false;\n searchCount: boolean = false;\n\n\n listPageNumber: number = 1;\n listTotal!: number;\n listSize!: number;\n listPageCount!: number;\n\n\n// actionFile\n public actionFile: any[] = [];\n\n\n// Imported helpers or other methods\n protected readonly isObjectEmpty = isObjectEmpty;\n protected readonly parseUTC = parseUTC;\n protected readonly parseCurrency = parseCurrency;\n protected readonly CHAT_TYPES = CHAT_TYPES;\n protected readonly DEFAULT_PROFILE_ICON = DEFAULT_PROFILE_ICON;\n\n// Views child for current component\n @ViewChild('message') private messageContainer!: ElementRef;\n @ViewChild('inputFile') inputFile!: ElementRef;\n @ViewChild('textArea') textArea!: ElementRef;\n\n// Special for destroy\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n public readonly chatService: ChatOldService,\n private readonly formBuilder: FormBuilder,\n private webSocketService: WebSocketService,\n private notificationService: NotificationService,\n private modalService: ModalService,\n public baseService: BaseService,\n private readonly dialogService: DialogService,\n public readonly dialog: MatDialog,\n public logisticsService: LogisticService,\n public matBottomSheet: MatBottomSheet,\n public router: Router,\n public rightSidebarService: RightSidebarService,\n ) {\n }\n\n ngAfterViewInit(): void {\n // this.autoResizeTextarea();\n // throw new Error('Method not implemented.');\n // this.selectChat();\n }\n\n ngOnInit(): void {\n this.initChat();\n this.webSocketService.getUpdateMessage()\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n // this.initChat();\n\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n this.cleanThreadData();\n this.destroyRiaChat();\n }\n\n ngOnChanges(changes: SimpleChanges | any) {\n if (changes.chat_id && !changes.chat_id.isFirstChange()) {\n // this.isLoaderIframe = true;\n this.cleanThreadData();\n this.resetTextArea();\n this.destroyRiaChat();\n this.listPageNumber = 1;\n this.initChat();\n // this.selectChat();\n this.searchText = '';\n this.showInput = false;\n }\n }\n\n initChat() {\n this.isLoaderIframe = true;\n this.chatService.getChatById(this.chat_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe(thread => {\n\n this.integration_id = this.chatService.openedChat.data?.integration_id;\n this.chat = thread;\n\n this.onLoadChat.emit(this.chatService.openedChat);\n\n if (this.chatService.openedChat.entity_type === CHAT_TYPES.OLX) {\n this.initOlx(this.chat_id);\n }\n\n if ([CHAT_TYPES.RIA_WH, CHAT_TYPES.DOM_RIA_WH, CHAT_TYPES.AUTO_RIA_WH, CHAT_TYPES.AGRO_RIA_WH]\n .includes(this.chatService.openedChat.entity_type)) {\n this.generateRiaChat();\n }\n\n if (this.chatService.openedChat.entity_type === CHAT_TYPES.TELEGRAM_BOT) {\n this.initTg(this.chat_id);\n }\n\n if ([CHAT_TYPES.RIA, CHAT_TYPES.DOM_RIA, CHAT_TYPES.AUTO_RIA, CHAT_TYPES.AGRO_RIA]\n .includes(this.chatService.openedChat.entity_type)) {\n this.initRia(this.chat_id);\n }\n\n\n if (this.baseService.currentUserInfo.settings.is_message_read) this.readMessages(this.chat_id);\n\n this.textAreaForm = new FormGroup({\n text: new FormControl(null, Validators.required),\n });\n });\n }\n\n @HostListener('window:paste', ['$event'])\n eventPaste(e: ClipboardEvent) {\n if (!e.clipboardData) return;\n const items: any[] = Array.from(e.clipboardData.items).filter((x: any) => /^image\\//.test(x.type));\n if (items && items.length) {\n const files = items.map((v: any) => v.getAsFile());\n this.attachFile({ target: { files: files } })\n e.preventDefault();\n }\n }\n\n initOlx(chat_id: string) {\n this.initAdvert(chat_id);\n this.initUser(this.chat?.data?.interlocutor_id);\n this.initMessages(chat_id);\n this.initPinnedMessages(chat_id);\n }\n\n initRia(chat_id: string) {\n this.initRiaAdvert();\n this.initMessages(chat_id);\n }\n\n initTg(chat_id: string) {\n this.initTgBot(chat_id);\n }\n\n initPinnedMessages(chat_id: string) {\n this.chatService.getPinnedMessages(chat_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any[]) => {\n this.pinnedMessages = res;\n this.pinnedLastMessage = res.at(-1);\n },\n error: (e) => {\n console.error('Error getPinnedMessages:', e);\n },\n });\n }\n\n\n initTgBot(chat_id: string) {\n // this.isLoaderMessage = true;\n // this.isLoaderIframe = true;\n this.is_pause = false;\n // this.chatService.openedChat.avatar = this.chatService.openedChat.avatar || DEFAULT_PROFILE_ICON;\n const paginationParams = {\n page: 1,\n take: 50,\n };\n this.chatService.getMessages(chat_id, paginationParams)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n const messages = res.items as any[];\n this.messages = messages;\n\n this.listTotal = res.total;\n this.listSize = res.size;\n this.listPageCount = Math.ceil(\n this.listTotal / this.listSize,\n );\n\n // this.messages = messages.map((v, i, array) => {\n //\n // if (['sent', 'received'].includes(v.type)) {\n // // if (i + 1 >= messages.length) return v;\n // const v2 = messages[i + 1];\n // if (!v2) return {...v, tails: true};\n //\n // if (this.formatDate(v.created_at) !== this.formatDate(v2.created_at)) {\n // return {...v, tails: true};\n // }\n //\n // if (this.formatDate(v.created_at) === this.formatDate(v2.created_at) && v.type === v2.type) {\n // return {...v, tails: false};\n // } else {\n // return {...v, tails: true};\n // }\n // }\n // return v;\n // });\n // this.me = (response.data.me || {});\n // this.interlocutor = (response.data.interlocutor || {});\n this.messagesGroups = this.msgGrouping(this.messages);\n setTimeout(() => {\n this.scrollBottom();\n this.isLoaderMessage = false;\n\n const options = {\n // root: document.querySelector( '#viewport' ), // я закомментил строку, чтобы использовать значение по умолчанию\n rootMargin: '0px',\n threshold: [0, 0.5],\n };\n\n const observer = new IntersectionObserver(() => {\n return this.messages.filter(message => message.is_pinned);\n }, options);\n const message = this.pinnedMessages.at(-1);\n if (message) {\n const target = document.getElementById(message.id);\n if (target) observer.observe(target);\n }\n\n }, 700);\n },\n error: (res) => {\n console.error('Error getMessages:', res);\n this.isLoaderMessage = false;\n this.isLoaderIframe = false;\n this.cleanThreadData();\n (res.error?.errors || []).forEach((res: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_OLX_messages, ERROR_MESSAGES[res.code]);\n });\n },\n });\n }\n\n initMessages(chat_id: string) {\n // this.isLoaderMessage = true;\n // this.isLoaderIframe = true;\n this.is_pause = false;\n // this.chatService.openedChat.avatar = this.chatService.openedChat.avatar || DEFAULT_PROFILE_ICON;\n const paginationParams = {\n page: 1,\n take: 50,\n };\n this.chatService.getMessages(chat_id, paginationParams)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n const messages = res.items as any[];\n this.messages = messages;\n\n this.listTotal = res.total;\n this.listSize = res.size;\n this.listPageCount = Math.ceil(\n this.listTotal / this.listSize,\n );\n\n // this.messages = messages.map((v, i, array) => {\n //\n // if (['sent', 'received'].includes(v.type)) {\n // // if (i + 1 >= messages.length) return v;\n // const v2 = messages[i + 1];\n // if (!v2) return {...v, tails: true};\n //\n // if (this.formatDate(v.created_at) !== this.formatDate(v2.created_at)) {\n // return {...v, tails: true};\n // }\n //\n // if (this.formatDate(v.created_at) === this.formatDate(v2.created_at) && v.type === v2.type) {\n // return {...v, tails: false};\n // } else {\n // return {...v, tails: true};\n // }\n // }\n // return v;\n // });\n // this.me = (response.data.me || {});\n // this.interlocutor = (response.data.interlocutor || {});\n this.messagesGroups = this.msgGrouping(this.messages);\n setTimeout(() => {\n this.scrollBottom();\n this.isLoaderMessage = false;\n\n const options = {\n // root: document.querySelector( '#viewport' ), // я закомментил строку, чтобы использовать значение по умолчанию\n rootMargin: '0px',\n threshold: [0, 0.5],\n };\n\n const observer = new IntersectionObserver(() => {\n return this.messages.filter(message => message.is_pinned);\n }, options);\n const message = this.pinnedMessages.at(-1);\n if (message) {\n const target = document.getElementById(message.id);\n if (target) observer.observe(target);\n }\n\n }, 700);\n },\n error: (res) => {\n console.error('Error getMessages:', res);\n this.isLoaderMessage = false;\n this.isLoaderIframe = false;\n this.cleanThreadData();\n (res.error?.errors || []).forEach((res: IError) => {\n this.is_pause = res.code === 'ACCOUNT_OLX_PAUSE';\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_OLX_messages, ERROR_MESSAGES[res.code]);\n });\n },\n });\n // this.webSocketService.getSocketMessages()\n // .pipe(takeUntil(this.destroy$))\n // .subscribe((message: any) => {\n // const socketMessages = []\n // socketMessages.push(message)\n // this.messages = [...this.messages, ...socketMessages]\n // });\n }\n\n public pressCmdEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'cmd_enter' && !this.baseService.isMobile && !this.chatService.openedChat.is_blocked) {\n this.sendMessage(this.chat_id);\n }\n }\n\n public pressShiftEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'shift_enter' && !this.baseService.isMobile && !this.chatService.openedChat.is_blocked) {\n this.sendMessage(this.chat_id);\n }\n }\n\n public pressEnter(e: any): any {\n if (this.baseService.currentUserInfo.settings.hot_key_send_chat === 'enter' && !this.baseService.isMobile && !this.chatService.openedChat.is_blocked) {\n e.preventDefault();\n this.sendMessage(this.chat_id);\n }\n }\n\n public resetForm() {\n this.textAreaForm.reset();\n this.files = [];\n }\n\n public sendMessage(chat_id: string) {\n const text = (this.textAreaForm.controls.text.value || '').trim();\n const files = [...this.files.map(v => v.file)];\n if (files?.length) {\n this.resetForm();\n this.sendMessageFile(chat_id, text, files);\n } else if (text) {\n this.resetForm();\n this.sendMessageText(chat_id, text);\n }\n }\n\n private sendMessageFile(chat_id: string, text: string, files: File[]) {\n const tempMessages = [...this.messages];\n tempMessages.unshift({\n id: '',\n chat_id: this.chat_id,\n text: text,\n type: 'sent',\n created_at: new Date().toISOString(),\n status: 'WAIT',\n x_data: {},\n });\n this.messagesGroups = this.msgGrouping(tempMessages);\n this.scrollBottom();\n\n this.chatService.sendFilesMessages(chat_id, text || '.', files)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.readMessages(this.chat_id);\n this.messages.unshift(res);\n this.messagesGroups = this.msgGrouping(this.messages);\n this.scrollBottom();\n\n },\n error: (res) => {\n this.isLoaderMessage = false;\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'MAX_FILE_SIZE') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.file_size_error);\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n }\n });\n },\n });\n }\n\n private sendMessageText(chat_id: string, text: string) {\n const tempMessages = [...this.messages];\n tempMessages.unshift({\n id: '',\n chat_id: this.chat_id,\n text: text,\n type: 'sent',\n created_at: new Date().toISOString(),\n status: 'WAIT',\n x_data: {},\n });\n if(this.editableMessage){\n this.scrollBottom();\n this.chatService.editMessage(chat_id, this.editableMessage, text)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (message) => {\n this.readMessages(this.chat_id);\n this.editableMessage = null;\n this.messages = this.messages.map((v) => {\n if ( v.id === message.id) {\n return message;\n }\n\n return v;\n });\n this.messagesGroups = this.msgGrouping(this.messages);\n this.scrollBottom();\n },\n error: (e) => {\n this.isLoaderMessage = false;\n console.error('Error sendMessage:', e);\n //(ㅤ)\n },\n });\n } else {\n this.messagesGroups = this.msgGrouping(tempMessages);\n this.scrollBottom();\n this.chatService.sendMessages(chat_id, text)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (message) => {\n this.readMessages(this.chat_id);\n this.messages.unshift(message);\n this.messagesGroups = this.msgGrouping(this.messages);\n this.onReloadChats.emit(true);\n this.scrollBottom();\n },\n error: (e) => {\n this.isLoaderMessage = false;\n console.error('Error sendMessage:', e);\n //(ㅤ)\n },\n });\n }\n\n }\n\n public onSendMessageAction(event: any) {\n const payload: SendMessageActionPayload = {\n text: event.message.x_data.text || '{}',\n type_mes: MESSAGE_TYPE[event.action],\n previous_message_id: event.message.x_data.id || 0,\n }\n this.chatService.sendMessageAction(event.message.chat_id, payload)\n .subscribe({\n next: (message) => {\n this.readMessages(this.chat_id);\n this.messages.unshift(message);\n this.messagesGroups = this.msgGrouping(this.messages);\n this.onReloadChats.emit(true);\n this.scrollBottom();\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n }\n\n private readMessages(chat_id: string) {\n this.chatService.readMessages(chat_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n // this.onChangeRead.emit(chat_id);\n this.onReloadChat.emit(chat_id);\n },\n error: (e) => {\n console.error('Error readMessages:', e);\n },\n });\n }\n\n public advertMoreInfo() {\n const modalData = {\n title: this.advert.title,\n component: ProductInfoComponent,\n width: '50vh',\n data: {\n product: this.advert,\n },\n };\n\n this.modalService.openModal(modalData)\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n }\n\n cleanThreadData() {\n this.messages = [];\n this.messagesGroups = [];\n this.advert = {};\n this.interlocutor = {};\n this.me = {};\n }\n\n scrollBottom() {\n setTimeout(() => {\n if (this.messageContainer) {\n const options = {\n left: 0,\n top: 0,\n behavior: 'smooth',\n };\n this.messageContainer.nativeElement.scrollTo(options);\n }\n }, 0);\n }\n\n scrollUp() {\n setTimeout(() => {\n if (this.messageContainer) {\n const options = {\n left: 0,\n top: -this.messageContainer.nativeElement.scrollHeight,\n behavior: 'smooth',\n };\n this.messageContainer.nativeElement.scrollTo(options);\n }\n }, 0);\n }\n\n\n private initAdvert(chat_id: string) {\n this.chatService.getAdvert(chat_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n if (res) {\n const olxAdvert = res;\n olxAdvert.type = ADVERT_TYPE.OLX;\n this.advert = olxAdvert;\n }\n },\n error: () => {\n\n },\n });\n }\n\n private initRiaAdvert() {\n const riaAdvert: IRiaAdvert = this.chat.data.advert;\n if (!riaAdvert) return;\n this.advert = {\n type: ADVERT_TYPE.RIA,\n id: riaAdvert.id,\n title: riaAdvert.title,\n url: riaAdvert.url,\n images: [{ url: riaAdvert.main_photo_url }],\n double_price: {\n usd: riaAdvert.price_usd,\n uah: riaAdvert.price,\n },\n }\n }\n\n setStatus(status: string) {\n if (status === Tabs.ACTIVE) {\n this.chatService.updateChat(this.chat_id, { status })\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.onReloadChats.emit();\n this.blurChat();\n });\n }\n if (status === Tabs.CLOSED) {\n const dialogRef = this.dialogService.openConfirm({\n title: 'end_the_conversation_t',\n text: 'end_the_conversation',\n customClass: 'text-center',\n confirmText: 'end',\n });\n dialogRef\n .pipe(takeUntil(this.destroy$))\n .subscribe((res) => {\n if (res) {\n this.chatService.updateChat(this.chat_id, { status, is_favorite: false })\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.onReloadChats.emit();\n this.blurChat();\n });\n }\n });\n }\n }\n\n setFavorite(is_favorite: boolean) {\n this.chatService.updateChat(this.chat_id, { is_favorite: is_favorite })\n .pipe(takeUntil(this.destroy$))\n .subscribe(res => {\n this.onReloadChats.emit();\n this.onReloadChat.emit(this.chat_id);\n });\n }\n\n resetTextArea() {\n this.textAreaForm.controls.text.setValue('');\n this.files = [];\n }\n\n attachFile(event: any) {\n const tempFiles = [...event.target.files as []];\n // this.files.push();\n\n if ((this.files.length + tempFiles.length) > 9) {\n tempFiles.splice(9 - tempFiles.length);\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.no_more_upload);\n }\n const newFiles = tempFiles.map(v => ({ file: v, url: URL.createObjectURL(v) }));\n this.files.push(...newFiles);\n this.resetFileInput();\n }\n\n removeFile(file: File) {\n this.files = this.files.filter(f => f.file !== file);\n }\n\n resetFileInput() {\n this.inputFile.nativeElement.value = '';\n }\n\n fixText() {\n const text = this.textAreaForm.controls.text.value as string;\n let autoDetect: 'en' | 'uk' | 'ru' | '' = '';\n switch (true) {\n case /[a-zA-Z]/.test(text):\n autoDetect = 'en';\n break;\n case /[\\u0400-\\u04FF]/.test(text):\n autoDetect = 'uk';\n break;\n }\n\n const modalData = {\n title: 'what_on_what',\n translate: true,\n component: FixTextComponent,\n width: '50vh',\n data: {\n auto_detect: autoDetect,\n },\n };\n\n this.modalService.openModal(modalData)\n .pipe(takeUntil(this.destroy$))\n .subscribe(res => {\n if (res) {\n const newText = text.split('').map((v: string) => {\n return keyboards[res][v] || v;\n }).join('');\n this.textAreaForm.controls.text.setValue(newText);\n }\n });\n }\n\n public addEmoji(event: any) {\n this.textAreaForm.controls.text.setValue(this.textAreaForm.controls.text.value + `\\\\${event.emoji.native}`);\n }\n\n private formatDate(date: any) {\n if (!date) return date;\n return moment.utc(date).format('YYYY-MM-DD');\n };\n\n private msgGrouping1 = (arr: any[]) => {\n return arr.reduce((result, obj) => {\n if (obj.created_at) {\n const createdAt = obj.created_at;\n const existingGroup = result.find((group: MessageGroup) => this.formatDate(group.created_at) === this.formatDate(createdAt));\n\n if (existingGroup) {\n existingGroup.messages.push({\n ...obj,\n id: obj.id ?? null,\n text: obj.text ?? null,\n });\n } else {\n result.push({\n messages: [{\n ...obj,\n id: obj.id ?? null,\n text: obj.text ?? null,\n }],\n created_at: createdAt,\n });\n }\n } else {\n result.push({ ...obj });\n }\n\n return result;\n }, []);\n };\n\n private msgGrouping = (arr: any[]) => {\n const groups: MessageGroup[] = [];\n // TODO: Problem sent performance\n arr.forEach((obj: Message, i: number, array: Message[]) => {\n const createdAt = obj.created_at;\n const existingGroup = groups.find((group: MessageGroup) => this.formatDate(group.created_at) === this.formatDate(createdAt));\n if (existingGroup) {\n existingGroup.messages.unshift({\n ...obj,\n });\n } else {\n groups.push({\n messages: [{\n ...obj,\n id: obj.id,\n text: obj.text,\n }],\n created_at: createdAt,\n });\n }\n });\n return groups;\n };\n\n protected readonly Tabs = Tabs;\n\n openTemplates() {\n const bottomRef = this.matBottomSheet.open(MessageTemplatesComponent, { hasBackdrop: true });\n bottomRef.afterDismissed()\n .pipe(takeUntil(this.destroy$))\n .subscribe((res) => {\n if (res) {\n this.textAreaForm.controls.text.setValue(this.textAreaForm.controls.text.value ? this.textAreaForm.controls.text.value + `\\n${res.text}` : res.text);\n }\n });\n }\n\n clearText() {\n this.textAreaForm.controls.text.setValue('');\n }\n\n onResendMessage(text: any) {\n return this.sendMessageText(this.chat_id, text);\n }\n\n onChangeTitleChat() {\n const modalData = {\n title: 'change_name',\n translate: true,\n component: OneFieldComponent,\n data: {\n input: this.chatService.openedChat?.title,\n },\n };\n\n this.modalService.openModal(modalData)\n .pipe(takeUntil(this.destroy$))\n .subscribe(title => {\n if (title) {\n this.chatService.updateChat(this.chat_id, { title })\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.onReloadChats.emit();\n this.onReloadChat.emit(this.chat_id);\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n });\n }\n });\n }\n\n onScroll(e: any) {\n this.isArrow = true;\n this.scrollTo(e);\n // if (!this.isLoaderMessage && this.isFirstHost && (e.target.scrollTop <= 25) && (this.listPageNumber <= this.listPageCount)) {\n // this.listPageNumber++;\n // this.initMessages(this.chat_id);\n // }\n }\n\n onScrollEnd(event: any) {\n setTimeout(() => {\n this.isArrow = false;\n }, 3000);\n }\n\n private selectChat() {\n // TODO: Add opportunity do it optional\n if (!this.baseService.isMobile) {\n setTimeout(() => {\n this.textArea?.nativeElement?.select();\n }, 250);\n }\n }\n\n onReloadCurrentChat() {\n this.initAdvert(this.chat_id);\n }\n\n generateRiaLink() {\n const urlSearchParams = new URLSearchParams(this.chatService.openedChat?.data?.adv_link);\n const url = new URL(this.chatService.openedChat?.data?.adv_link);\n const newURL = `https://chat.ria.com/0/0/${this.chatService.openedChat?.data?.chat_id}/?key=${urlSearchParams.get('chat_key')}&chat_on_site=${url.host}&a`;\n return newURL;\n }\n\n generateRiaChat() {\n this.is_pause = false;\n const ifrm = document.createElement('iframe');\n ifrm.setAttribute('src', this.generateRiaLink());\n ifrm.setAttribute('id', 'ria');\n ifrm.style.width = '0';\n ifrm.style.height = '0';\n ifrm.addEventListener('load', () => {\n this.isLoaderIframe = false;\n ifrm.style.width = '335px';\n ifrm.style.height = '568px';\n // const a: HTMLElement | null = ifrm.querySelector('.forward.left');\n // if (a) {\n // a.style.display = 'none';\n // }\n });\n\n setTimeout(() => {\n const div = document.getElementById('iframe-box');\n if (!div) return;\n // this.destroyAutoRiaChat();\n div.appendChild(ifrm);\n }, 500);\n\n }\n\n destroyRiaChat() {\n const div = document.getElementById('iframe-box');\n const iframe = document.getElementById('ria');\n if (div && iframe) div?.removeChild(iframe);\n }\n\n toPinnedMessage() {\n const messageDiv = document.getElementById(this.pinnedLastMessage.id);\n if (messageDiv) {\n messageDiv.scrollIntoView({ behavior: 'smooth' });\n const index = this.pinnedMessages.findIndex(message => message.id === this.pinnedLastMessage.id);\n const nextIndex = index - 1;\n if (nextIndex === -1) {\n const firstPinnedMessage = this.pinnedMessages.at(-1);\n if (firstPinnedMessage) this.pinnedLastMessage = firstPinnedMessage;\n } else {\n this.pinnedLastMessage = this.pinnedMessages[nextIndex];\n }\n } else {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.old_messages)\n }\n }\n\n protected readonly URL = URL;\n\n onReloadPinnedMessages() {\n this.initPinnedMessages(this.chat_id);\n }\n\n onEditMessage(event: any) {\n this.textAreaForm.controls.text.setValue(event.text);\n this.editableMessage = event.id;\n }\n\n onClearEdit() {\n this.textAreaForm.controls.text.setValue(null);\n this.editableMessage = null;\n }\n\n onVisible(message: Message) {\n if (message.is_pinned) {\n const index = this.pinnedMessages.findIndex(v => v.id === message.id);\n this.pinnedLastMessage = this.pinnedMessages[index - 1];\n }\n }\n\n onUnVisible(message: Message) {\n // if (message.is_pinned) {\n // }\n }\n\n blockUser() {\n this.chatService.blockUser(this.chatService.openedChat.data.integration_id, this.chatService.openedChat.data.interlocutor_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.chatService.openedChat.is_blocked = !!res;\n this.notificationService.showSuccess('', `${languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.user} ${res.name} ${languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.blocked}`);\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n searchMessage() {\n this.showInput = !this.showInput;\n }\n\n doSearch() {\n this.searchCount = true;\n this.onSearch();\n }\n\n onSearchText(text: string) {\n this.searchText = text;\n if (!this.searchText) this.doSearch();\n }\n\n onSearch() {\n const text = this.searchText;\n if (!this.searchText) {\n this.searchCount = false;\n return;\n }\n this.searchMessages = this.messages.filter(message => message.text && message.text.toLowerCase().includes(text.toLowerCase())).reverse();\n if (this.messageContainer) {\n this.messageSearch = this.searchMessages[0].id;\n const messageDiv = document.getElementById(this.messageSearch);\n if (messageDiv) {\n messageDiv.scrollIntoView({ behavior: 'smooth' });\n messageDiv.classList.add('highlighted');\n setTimeout(() => {\n messageDiv.classList.remove('highlighted');\n }, 1000);\n }\n }\n\n }\n\n toSearchUp() {\n const messageIndex = this.searchMessages.findIndex(message => message.id === this.messageSearch);\n if (messageIndex !== -1 && messageIndex < this.searchMessages.length - 1) {\n const message = this.searchMessages[messageIndex + 1];\n this.messageSearch = message?.id;\n const messageDiv = document.getElementById(message.id);\n if (messageDiv) {\n messageDiv.scrollIntoView({ behavior: 'smooth' });\n messageDiv.classList.add('highlighted');\n setTimeout(() => {\n messageDiv.classList.remove('highlighted');\n }, 1000);\n }\n }\n }\n\n toSearchDown() {\n const messageIndex = this.searchMessages.findIndex(message => message.id === this.messageSearch);\n if (messageIndex !== -1 && messageIndex > 0) {\n const message = this.searchMessages[messageIndex - 1];\n this.messageSearch = message?.id;\n const messageDiv = document.getElementById(message.id);\n if (messageDiv) {\n messageDiv.scrollIntoView({ behavior: 'smooth' });\n messageDiv.classList.add('highlighted');\n setTimeout(() => {\n messageDiv.classList.remove('highlighted');\n }, 1000);\n }\n }\n }\n\n clear() {\n this.searchMessages = [];\n this.searchMessage();\n this.searchCount = false;\n }\n\n scrollTo(e: any) {\n // if (e.target.scrollHeight + e.target.scrollTop <= e.target.clientHeight + 700) {\n // const paginationParams = {\n // page: ++this.listPageNumber,\n // take: 50,\n // };\n // if (this.messages.length !== this.listTotal) {\n // this.chatService.getMessages(this.chat_id, paginationParams)\n // .pipe(takeUntil(this.destroy$))\n // .subscribe(res => {\n // const messages = res.items as any[];\n // this.messages = [...this.messages, ...messages];\n // this.messagesGroups = this.msgGrouping(this.messages);\n // });\n // }\n // }\n }\n\n animateTextInput(text: string, element: HTMLTextAreaElement) {\n this.resetTextArea();\n let index = 0;\n const interval = setInterval(() => {\n if (index < text.length) {\n element.value += text[index];\n index++;\n } else {\n clearInterval(interval);\n this.textAreaForm.controls.text.setValue(text);\n }\n }, 15);\n }\n\n getAiAnswer() {\n const isAi = this.baseService.currentCompanyInfo.application_keys.includes('GEN_AI');\n\n if (!isAi) {\n const dialogRef = this.dialogService.openConfirm({\n text: \"buy_ai\",\n });\n dialogRef.subscribe((res) => {\n if (res) {\n this.router.navigate(['/crm/marketplace']);\n this.rightSidebarService.closeChats();\n }\n })\n } else {\n this.chatService.getAiAnswer(this.chat_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n if (res) {\n this.animateTextInput(res, this.textArea.nativeElement)\n this.baseService.getCompanyInfo()\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n }\n },\n error: e => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n })\n }\n });\n }\n }\n\n clickAutomation() {\n this.chatService.updateSettings(this.chatService.chat_id, {\n enable_ai_assistant: this.chatService.openedChat.settings ? !this.chatService.openedChat.settings?.enable_ai_assistant : false,\n }).subscribe(() => {\n this.onReloadChats.emit();\n this.onReloadChat.emit(this.chat_id);\n })\n }\n\n loadUpMessage() {\n const paginationParams = {\n page: ++this.listPageNumber,\n take: 50,\n };\n if (this.messages.length !== this.listTotal) {\n this.chatService.getMessages(this.chat_id, paginationParams)\n .pipe(takeUntil(this.destroy$))\n .subscribe(res => {\n const messages = res.items as any[];\n this.messages = [...this.messages, ...messages];\n this.messagesGroups = this.msgGrouping(this.messages);\n });\n }\n }\n\n private initUser(user_id: string) {\n this.chatService.getOlxUserInfo(user_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n this.client = (res || null);\n },\n error: () => {\n\n },\n });\n }\n}\n","
    \n \n
    \n \n arrow_back\n \n
    \n \n {{ chatService.openedChat?.title || 'Client - ' + chatService.openedChat?.channel_name }}\n
    \n
    \n \n \n
    \n \n \n \n \n
    \n
    {{ searchMessages.length }}
    \n
    \n
    \n expand_less\n
    \n
    \n expand_more\n
    \n
    \n
    \n
    \n \n
    \n \n search\n \n \n info_outline\n \n \n edit\n \n \n \n \n \n \n task_alt\n \n \n keyboard_return\n \n
    \n\n\n \n more_vert\n \n \n \n
    \n\n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n\n \n
    \n \n
    \n \n {{ 'pinned_messages' | translate }}\n {{ pinnedLastMessage.text }}\n \n\n
    \n \n \n
    \n \n \n \n\n \n
    \n \n\n \n\n \n
    \n\n
    \n\n
    \n
    \n {{ 'upload_more'| translate }}...\n \n
    \n \n {{ 'update_ad_price' | translate }} {{ 'go_to_the_marketplace' | translate }}\n \n \n \n\n
    \n
    \n

    \n \n error\n {{ 'this_account_is_paused:_please_contact_the_administrator'| translate }}\n {{ 'error_receiving_message:_please_contact_the_administrator' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

    \n
    \n
    \n\n
    \n
    \n \n close\n \n \"photo\"\n \n
    \n
    \n\n \n\n \n
    \n
    \n
    \n
    \n
    \n edit\n \n close\n
    \n
    \n
    \n
    \n\n \n\n
    \n bolt\n
    \n\n
    \n auto_awesome\n
    \n\n
    \n clear\n
    \n\n
    \n \n
    \n\n
    \n\n \n \n send\n \n \n\n \n
    \n
    \n
    \n
    \n \n\n\n\n
    \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
    \n
    \n\n\n
    \n \n expand_less\n \n \n expand_more\n \n
    \n
    \n","import { Injectable } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { environment } from '../../environments/environment';\nimport { CHAT_TYPES, LOGISTIC_TYPES } from '../enum/constants';\nimport { BehaviorSubject, map, tap } from 'rxjs';\nimport { IParams } from '../model/http';\n\nexport const initItemsTgParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport interface logisticBody {\n logistic_type: LOGISTIC_TYPES;\n chat_type: CHAT_TYPES;\n ttn: any;\n chat_uuid: string;\n phone?: any;\n is_check: boolean;\n}\n\nexport interface checkBindsBody {\n chat_uuid: string;\n}\n\n@Injectable({\n providedIn: 'root'\n})\n\nexport class LogisticService {\n constructor(\n private readonly http: HttpClient\n ) { }\n\n private readonly itemsTgSubject$ = new BehaviorSubject({});\n public readonly itemsTg$ = this.itemsTgSubject$.asObservable();\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryItemsTgParams = { ...initItemsTgParams };\n\n\n syncLogistic(payload: logisticBody) {\n return this.http.post(environment.baseURL + '/logistic', payload);\n }\n\n checkBinds(payload: checkBindsBody) {\n return this.http.post(environment.baseURL + '/logistic/sync', payload);\n }\n\n checkBindsCount(payload: checkBindsBody) {\n return this.http.post<{data: Record}>(environment.baseURL + '/logistic/count', payload)\n .pipe(map(res => res.data));\n }\n\n private getItemsTg(params: any) {\n return this.http.get(environment.baseURL + '/logistic/telegram', { params })\n .pipe(map(v => v.data))\n }\n\n loadItemsTg() {\n this.isLoader$.next(true);\n return this.getItemsTg(this.queryItemsTgParams)\n .pipe(tap((users) => {\n this.itemsTgSubject$.next(users);\n this.isLoader$.next(false);\n }));\n }\n\n setQueryItemsTgParams(params: IParams) {\n const { sort, order, search, take, page } = params;\n this.queryItemsTgParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryItemsTgParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryItemsTgParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryItemsTgParams.search,\n take: take || this.queryItemsTgParams.take,\n page: page || this.queryItemsTgParams.page,\n };\n return this.loadItemsTg();\n }\n\n addPhoneIntegration(phone: string) {\n const payload = {\n phone,\n }\n return this.http.post<{data: Record}>(environment.baseURL + '/logistic/telegram', payload)\n .pipe(map(res => res.data));\n }\n removePhoneIntegration(uuid: string) {\n return this.http.delete<{data: Record}>(environment.baseURL + `/logistic/telegram/${uuid}`)\n .pipe(map(res => res.data));\n }\n}\n","import { ColumnSortType, TableColumnTypes } from '../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'IntDocNumber',\n sort: ColumnSortType.NONE,\n name: 'ТТН',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'StateName',\n sort: ColumnSortType.NONE,\n name: 'Статус',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'CreateTime',\n sort: ColumnSortType.NONE,\n name: 'Дата створення',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'EstimatedDeliveryDate',\n sort: ColumnSortType.NONE,\n name: 'Плановий час доставки',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'Cost',\n sort: ColumnSortType.NONE,\n name: 'Оголошена вартість',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'CostOnSite',\n sort: ColumnSortType.NONE,\n name: 'Вартість доставки',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'Description',\n sort: ColumnSortType.NONE,\n name: 'Опис відправлення',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'SenderContactPerson',\n sort: ColumnSortType.NONE,\n name: 'Відправник',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'SendersPhone',\n sort: ColumnSortType.NONE,\n name: 'Телефон відправника',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'CitySenderDescription',\n sort: ColumnSortType.NONE,\n name: 'Місто відправника',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'BackwardDeliverySum',\n sort: ColumnSortType.NONE,\n name: 'Грошовий переказ',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'SenderAddressDescription',\n sort: ColumnSortType.NONE,\n name: 'Адреса відправника',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'RecipientContactPerson',\n sort: ColumnSortType.NONE,\n name: 'Отримувач',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'RecipientsPhone',\n sort: ColumnSortType.NONE,\n name: 'Телефон отримувача',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'CityRecipientDescription',\n sort: ColumnSortType.NONE,\n name: 'Місто отримання',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'RecipientAddressDescription',\n sort: ColumnSortType.NONE,\n name: 'Адреса отримання',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'PayerType',\n sort: ColumnSortType.NONE,\n name: 'Платник за доставку',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'RecipientDateTime',\n sort: ColumnSortType.NONE,\n name: 'Дата отримання',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'AdditionalInformation',\n sort: ColumnSortType.NONE,\n name: 'Додаткова інформація',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'DateLastUpdatedStatus',\n sort: ColumnSortType.NONE,\n name: 'Обновленно',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'gps_fixed', label: 'add_in_tracker', value: 'ADD_TRACKER' },\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n","import {Component, Input} from '@angular/core';\nimport {initItemsInvoiceParams, NovaPoshtaService} from '../../services/nova-poshta.service';\nimport {IParams} from '../../model/http';\nimport {map, Observable, Subject, take, takeUntil} from 'rxjs';\nimport {AppTableSource, ColumnSortType} from '../../model/table.model';\nimport {NotificationService} from '../../services/notifications.service';\nimport {IntegrationService} from '../../services/integration.service';\nimport {BaseService} from '../../services/base.service';\nimport {tableHeaders} from './tab-invoices.interface';\n\n@Component({\n selector: 'app-tab-invoices',\n templateUrl: './tab-invoices.component.html',\n styleUrls: ['./tab-invoices.component.scss']\n})\nexport class TabInvoicesComponent {\n private readonly destroy$: Subject = new Subject();\n\n @Input() apiKey!: any;\n\n dataSource$: Observable = this.novaPoshtaService.itemsMyInvoices$\n .pipe(map((v) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n\n return {\n headers: tableHeaders,\n rows: v.data as any,\n pageSize: v?.pageSize || 0,\n pageIndex: v.pageIndex ? v.pageIndex - 1 : 0,\n length: v.totalCount || 0,\n }\n }))\n visibleColumns = tableHeaders.map(v => v.value);\n\n // Variables\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n private timerSearch: any;\n\n constructor(\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly novaPoshtaService: NovaPoshtaService,\n public readonly baseService: BaseService,\n ) {\n }\n\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.itemsRequest = this.novaPoshtaService\n .setQueryListInvoicesParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.novaPoshtaService.loadListMyInvoices(this.apiKey)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.novaPoshtaService.changeMyInvoicesSort(sortState);\n }\n\n onPaginationChange(paginationDetails: {\n previousPageIndex: number;\n pageIndex: number;\n pageSize: number;\n length: number;\n }): void {\n this.novaPoshtaService.changeMyInvoicesPagination(paginationDetails);\n }\n\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n\n }\n if (event.label === 'ADD_TRACKER') {\n\n }\n }\n\n onSearchChange(search: string) {\n this.novaPoshtaService.searchMyInvoices(search);\n }\n}\n","
    \n
    \n
    \n \n
    \n Створити ЕН\n
    \n \n
    \n \n
    \n
    \n \n \n \n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'title',\n sort: ColumnSortType.NONE,\n name: 'Назва',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'description',\n sort: ColumnSortType.NONE,\n name: 'Опис відправлення',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'weight',\n sort: ColumnSortType.NONE,\n name: 'Вага',\n type: TableColumnTypes.NOVA_POSHTA_STATUS,\n className: 'table-header',\n },\n {\n value: 'place',\n sort: ColumnSortType.NONE,\n name: 'Місце',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n","import { Component, Inject, OnDestroy, OnInit } from '@angular/core';\nimport { Clipboard } from '@angular/cdk/clipboard';\nimport { NotificationService } from '../../../services/notifications.service';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { PropsService } from '../../../services/props.service';\nimport { paymentBody, IPaymentData } from './create-template.interface';\nimport { BaseService } from '../../../services/base.service';\nimport {\n FormArray,\n FormArrayName,\n FormControl,\n FormControlDirective,\n FormGroup,\n isFormControl, isFormGroup,\n Validators\n} from '@angular/forms';\nimport { NovaPoshtaService } from '../../../services/nova-poshta.service';\nimport { map, Observable, startWith } from 'rxjs';\nimport { DOLLAR, languages } from '../../../enum/constants';\n\n\ninterface CreateForm {\n name: FormControl,\n box: FormControl,\n description_product: FormControl,\n price: FormControl,\n params: FormControl,\n global_params: any,\n payer_delivery: FormControl,\n form_payment: FormControl,\n internal_shipment_number: FormControl,\n document: FormControl,\n package_number: FormControl,\n additional_information: FormControl,\n address: FormControl,\n remittance: FormControl,\n remittance_payer_delivery: FormControl,\n contact: FormControl,\n global_weight: FormControl,\n volume_length: FormControl,\n number_seats: FormControl,\n manual: FormControl,\n\n}\n\n@Component({\n selector: 'app-create-template',\n templateUrl: './create-template.component.html',\n styleUrls: ['./create-template.component.scss']\n})\nexport class CreateTemplateComponent implements OnInit, OnDestroy {\n public isLoader: boolean = false;\n public paymentData: IPaymentData;\n public card = '4028082004159990';\n public cardView = '4028 0820 0415 9990';\n public addresses: any[] = [\n {value: 'steak-0', viewValue: 'Steak'},\n {value: 'pizza-1', viewValue: 'Pizza'},\n {value: 'tacos-2', viewValue: 'Tacos'}\n ];\n public cargos: any[] = [];\n public filteredCargos!: Observable;\n\n public createForm!: FormGroup;\n public contacts: any[] = [];\n\n formSubmitted = false;\n\n constructor(\n private clipboard: Clipboard,\n private notificationService: NotificationService,\n private dialogRef: MatDialogRef,\n private propsService: PropsService,\n public baseService: BaseService,\n public novaPoshtaService: NovaPoshtaService,\n @Inject(MAT_DIALOG_DATA) public data: any\n ) {\n this.paymentData = this.data.data;\n }\n\n ngOnInit(): void {\n this.loadCargo();\n this.loadUserInfo();\n\n this.createForm = new FormGroup({\n name: new FormControl('', Validators.required),\n box: new FormControl('1'),\n description_product: new FormControl(''),\n price: new FormControl('1'),\n params: new FormControl(false),\n global_params: new FormArray([new FormGroup({\n weight: new FormControl(''),\n length: new FormControl(''),\n width: new FormControl(''),\n height: new FormControl('')\n })]),\n payer_delivery: new FormControl('1'),\n form_payment: new FormControl('1'),\n internal_shipment_number: new FormControl(''),\n document: new FormControl(''),\n package_number: new FormControl(''),\n additional_information: new FormControl(''),\n address: new FormControl(''),\n remittance: new FormControl(false),\n remittance_payer_delivery: new FormControl(''),\n contact: new FormControl(''),\n global_weight: new FormControl(''),\n volume_length: new FormControl(''),\n number_seats: new FormControl(''),\n manual: new FormControl('')\n\n });\n this.filteredCargos = this.createForm.controls.description_product.valueChanges.pipe(\n startWith(''),\n map((value: string) => this.cargos.filter(fv =>\n (fv.Description)\n .toLowerCase().includes(value.toLowerCase())))\n );\n }\n\n ngOnDestroy(): void {\n\n }\n\n copyProps(data: string) {\n this.clipboard.copy(data);\n this.notificationService.showInfo('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.copied);\n }\n\n parsePrice(price: any) {\n return (price * DOLLAR).toFixed();\n }\n\n confirmAdmin(): void {\n this.isLoader = true;\n const body: paymentBody = {\n payment_code: this.baseService.currentCompanyInfo?.code,\n tariff: this.paymentData.tariff,\n payment_type: 'tariff',\n payment_method: this.paymentData.type,\n price: this.paymentData.price\n };\n\n this.propsService.notifyAdmin(body)\n .subscribe(\n () => {\n this.isLoader = false;\n this.notificationService.showInfo(languages[localStorage.getItem('language') || 'ua'].notifications_texts.info.thanks_for_payment, '');\n this.dialogRef.close(true);\n },\n (error) => {\n this.isLoader = false;\n console.error('Ошибка при отправке запроса:', error);\n }\n );\n }\n\n\n private loadCargo() {\n this.novaPoshtaService.postCall({\n 'apiKey': this.paymentData.api_key,\n 'modelName': 'Common',\n 'calledMethod': 'getCargoDescriptionList',\n 'methodProperties': {\n 'FindByString': '',\n 'Page': '1'\n }\n }).subscribe((res) => {\n this.cargos = res.data;\n });\n }\n\n private loadUserInfo() {\n this.novaPoshtaService.postCall({\n 'apiKey': this.paymentData.api_key,\n 'modelName': 'Counterparty',\n 'calledMethod': 'getCounterparties',\n 'methodProperties': {\n 'CounterpartyProperty': 'Recipient',\n 'GetPrivatePerson': '1'\n }\n }).subscribe(res => {\n const data = res.data[0];\n\n this.novaPoshtaService.postCall({\n 'apiKey': this.paymentData.api_key,\n 'modelName': 'ContactPersonGeneral',\n 'calledMethod': 'getContactPersonsList',\n 'methodProperties': {\n ContactProperty: 'Sender',\n CounterpartyRef: data.CounterpartyRef,\n FindByString: '',\n Limit: 200,\n Page: 1\n }\n }).subscribe((res) => {\n this.contacts = res.data;\n if (res.data.length === 1) {\n this.createForm.controls.contact?.setValue(this.contacts[0]);\n }\n this.novaPoshtaService.postCall({\n 'apiKey': this.paymentData.api_key,\n 'modelName': 'Counterparty',\n 'calledMethod': 'getCounterpartyContactPersons',\n 'methodProperties': {\n Ref: res.data[0].CounterpartyRef,\n Page: \"1\",\n }\n }).subscribe((res) => {\n this.novaPoshtaService.postCall({\n 'apiKey': this.paymentData.api_key,\n 'modelName': 'AddressContactPersonGeneral',\n 'calledMethod': 'getAddresses',\n 'methodProperties': {\n 'ContactPersonRef': res.data[0].Ref,\n }\n }).subscribe((res) => {\n this.addresses = res.data[0]?.Warehouses;\n if (res.data.length === 1) {\n this.createForm.controls.address?.setValue(this.addresses[0]);\n }\n });\n });\n });\n });\n }\n\n createPlace() {\n const globalParams = this.createForm.controls.global_params as FormArray;\n globalParams.push(\n new FormGroup({\n weight: new FormControl(''),\n length: new FormControl(''),\n width: new FormControl(''),\n height: new FormControl('')\n })\n );\n }\n\n getGlobalParams() {\n return ((this.createForm.get('global_params') as FormArray));\n }\n\n copyPlace(event: any) {\n const globalParams = this.createForm.controls.global_params as FormArray;\n globalParams.push(\n new FormGroup({\n weight: new FormControl(event.controls.weight.value),\n length: new FormControl(event.controls.length.value),\n width: new FormControl(event.controls.width.value),\n height: new FormControl(event.controls.height.value)\n })\n );\n }\n\n removePlace(index: any) {\n this.getGlobalParams().removeAt(index);\n }\n\n calcWeight(item?: any) {\n if (item) {\n const weight = item.controls.weight.value;\n const length = item.controls.length.value;\n const width = item.controls.width.value;\n const height = item.controls.height.value;\n const result = (length * width * height) / 4000;\n if (length > 0 && width > 0 && height > 0) {\n return result < 0.10 ? 0.10 : result;\n } else {\n return '-';\n }\n\n }\n const global_weight = this.createForm.controls.global_weight.value;\n const volume_length = this.createForm.controls.volume_length.value;\n const number_seats = this.createForm.controls.number_seats.value;\n\n const globalResult = global_weight * volume_length * number_seats * 240;\n\n if (global_weight > 0 && volume_length > 0 && number_seats > 0) {\n return globalResult < 0.10 ? 0.10 : globalResult;\n }\n return '-';\n\n return 0;\n }\n\n manualDisabled(item: any): boolean {\n const weight = item.controls.weight.value;\n const length = item.controls.length.value;\n const width = item.controls.width.value;\n const height = item.controls.height.value;\n const res = this.calcWeight(item);\n const ifWeight = weight && weight <= 30;\n const ifLength = length && length <= 120;\n const ifWidth = width && width <= 120;\n const ifHeight = height && height <= 120;\n const ifRes = res && res !== '-' && res <= 30;\n return ifWeight && ifLength && ifWidth && ifHeight && ifRes;\n }\n\n cancel() {\n\n }\n\n save() {\n this.formSubmitted = true;\n if (this.createForm.valid) {\n this.dialogRef.close();\n }\n\n\n }\n\n\n}\n\n\n","
    \n \n
    \n\n\n
    \n \n {{'name_template' | translate}}\n \n {{'field_cannot_be_empty' | translate}}.\n \n
    \n
    \n

    {{'shipping_parameters' | translate}}

    \n
    \n \n {{'parcels_and_cargoes' | translate}}\n {{'documents' | translate}}\n {{'pallets' | translate}}\n {{'tires_and_wheels' | translate}}\n \n\n
    \n
    \n
    \n \n {{'product_description' | translate}}\n \n \n {{cargo.Description}}\n \n \n
    \n
    \n
    \n \n \n {{'from_the_transaction' | translate}}\n {{'minimum'| translate}}\n \n
    \n
    \n
    \n Загальні параметри\n
    \n
    \n \n {{'total_weight' | translate}}\n \n \n

    кг

    \n \n {{'total_volume' | translate}}\n \n \n

    m3

    \n \n {{'number_of_places' | translate}}\n \n \n

    шт

    \n \n {{'volumetric_weight' | translate}} help_outline\n\n \n \n
    \n
    \n
    \n \n \n
    \n \n {{'weight' | translate}}\n \n \n

    kg

    \n \n {{'length'| translate}}\n \n \n\n \n {{'width' | translate}}\n \n \n\n \n Висота\n \n \n

    см

    \n \n {{'volumetric_weight' | translate}} help_outline\n \n \n content_copy\n 1\" (onChangeClick)=\"removePlace(iParams)\" btnType=\"icon\" matTooltip=\"{{'delete_a_place' | translate}}\">close\n
    \n
    \n \n {{'manual_processing' | translate}}\n help_outline\n \n
    \n\n
    \n\n
    \n
    \n
    \n
    \n {{'add a place'| translate}}\n
    \n
    \n\n
    \n
    \n \n \n {{'recipient' | translate}}\n {{'sender' | translate}}\n \n
    \n
    \n \n \n {{'cash' | translate}}\n {{'cashless' | translate}}\n help_outline\n \n
    \n
    \n \n {{'Internal_number _of_the_shipment' | translate}}({{'optional' | translate}})\n \n \n \n {{'accompanying_documents' | translate}}({{'optional' | translate}})\n \n \n \n {{'package_number' | translate}}({{'optional' | translate}})\n \n \n \n {{'additional_information_about_the_shipment' | translate}}({{'optional' | translate}})\n \n \n
    \n\n
    \n

    {{'sender' | translate}}

    \n
    \n
    \n \n {{'contact' | translate}}\n \n {{contact.Description}} ({{contact.Phones}})\n \n \n \n {{'address'| translate}}\n \n {{address.AddressDescription}}\n \n \n
    \n
    \n

    {{'additional_services' | translate}}

    \n
    \n
    \n {{'money_transfer' | translate}}\n
    \n \n \n {{'sender' | translate}}\n {{'recipient' | translate}}\n \n
    \n {{'payment_control' | translate}}\n {{'return_delivery_of_documents' | translate}}\n {{'return_delivery_of_signed_documents' | translate}}\n {{'return_delivery_of_document_subtypes' | translate}}\n {{'control_of_piecewise_transmission' | translate}}\n {{'packaging' | translate}}\n {{'climbing_to_the_floor' | translate}}\n
    \n
    \n
    \n
    \n {{'cancel' | translate}}\n {{ 'save' | translate }}\n
    \n
    \n
    \n
    \n","import { Component, Input } from '@angular/core';\nimport { initItemsInvoiceParams, NovaPoshtaService } from '../../services/nova-poshta.service';\nimport { IParams } from '../../model/http';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IntegrationService } from '../../services/integration.service';\nimport { BaseService } from '../../services/base.service';\nimport { tableHeaders } from './tab-templates.interface';\nimport { MatDialog } from '@angular/material/dialog';\nimport { CreateTemplateComponent } from '../../components/modals/create-template/create-template.component';\nimport { ModalService } from '../../services/modal.service';\n\n\n@Component({\n selector: 'app-tab-templates',\n templateUrl: './tab-templates.component.html',\n styleUrls: ['./tab-templates.component.scss']\n})\nexport class TabTemplatesComponent {\n private readonly destroy$: Subject = new Subject();\n\n @Input() apiKey!: any;\n\n dataSource$: Observable = this.novaPoshtaService.itemsInvoices$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items as any,\n pageSize: localStorage?.getItem('channelTake') || data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n }\n }))\n visibleColumns = tableHeaders.map(v => v.value);\n\n // Variables\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n private timerSearch: any;\n\n constructor(\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly novaPoshtaService: NovaPoshtaService,\n public readonly baseService: BaseService,\n public readonly dialog: MatDialog,\n public readonly modalService: ModalService,\n ) {\n }\n\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.itemsRequest = this.novaPoshtaService\n .setQueryListInvoicesParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.novaPoshtaService.loadListInvoices()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n localStorage.removeItem('invoiceTake');\n localStorage.setItem('invoiceTake', pageSize);\n this.novaPoshtaService.updateInvoiceTake();\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take, }, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n this.novaPoshtaService.deleteInvoice(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n });\n },\n })\n }\n }\n\n onSearchChange(search: string) {\n if (this.timerSearch)\n clearTimeout(this.timerSearch)\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n search,\n }, true);\n }, 500)\n\n }\n\n onAdd() {\n const modalData = {\n title: 'create_template',\n translate: true,\n component: CreateTemplateComponent,\n width: '60vw',\n data: {\n api_key: this.apiKey,\n }\n };\n\n this.modalService.openModal(modalData)\n .pipe(takeUntil(this.destroy$))\n .subscribe();\n }\n}\n","
    \n
    \n
    \n \n
    \n {{'create_template'|translate}}\n
    \n \n
    \n \n
    \n
    \n \n \n \n
    \n
    \n","import {Component, Input} from '@angular/core';\n\nimport {BaseService} from '../../services/base.service';\n\n\n@Component({\n selector: 'app-tab-support-video',\n templateUrl: './tab-support-video.component.html',\n styleUrls: ['./tab-support-video.component.scss']\n})\nexport class TabSupportVideoComponent {\n\n constructor(\n\n public readonly baseService: BaseService,\n ) {\n }\n\n\n\n\n ngOnInit() {\n\n }\n\n\n\n\n\n}\n","\n \n\n\n
    \n
    \n \n
    \n

    {{ 'part' | translate }} 1. {{ 'presentation' | translate }}

    \n \n
    \n\n
    \n
    \n

    {{ 'part' | translate }} 2. {{ 'functionality' | translate }}

    \n \n
    \n\n
    \n
    \n
    \n","import {Component, Input} from '@angular/core';\n\nimport {BaseService} from '../../services/base.service';\n\n\n@Component({\n selector: 'app-tab-support-faq',\n templateUrl: './tab-support-faq.component.html',\n styleUrls: ['./tab-support-faq.component.scss']\n})\nexport class TabSupportFaqComponent {\n\n panelOpenState = false;\n dataFAQ = [\n {\n question: 'support_questions.question_1.question',\n answer: 'support_questions.question_1.answer'\n },\n {\n question: 'support_questions.question_2.question',\n answer: 'support_questions.question_2.answer'\n },\n {\n question: 'support_questions.question_3.question',\n answer: 'support_questions.question_3.answer'\n },\n {\n question: 'support_questions.question_4.question',\n answer: 'support_questions.question_4.answer'\n },\n {\n question: 'support_questions.question_5.question',\n answer: 'support_questions.question_5.answer'\n },\n {\n question: 'support_questions.question_6.question',\n answer: 'support_questions.question_6.answer'\n },\n {\n question: 'support_questions.question_7.question',\n answer: 'support_questions.question_7.answer'\n },\n {\n question: 'support_questions.question_8.question',\n answer: 'support_questions.question_8.answer'\n },\n {\n question: 'support_questions.question_9.question',\n answer: 'support_questions.question_9.answer'\n },\n {\n question: 'support_questions.question_10.question',\n answer: 'support_questions.question_10.answer'\n },\n {\n question: 'support_questions.question_11.question',\n answer: 'support_questions.question_11.answer'\n },\n {\n question: 'support_questions.question_12.question',\n answer: 'support_questions.question_12.answer'\n },\n {\n question: 'support_questions.question_13.question',\n answer: 'support_questions.question_13.answer'\n },\n ]\n\n constructor(\n\n public readonly baseService: BaseService,\n ) {\n }\n\n open() {\n this.panelOpenState = true\n }\n\n close() {\n this.panelOpenState = false\n }\n\n\n\n\n ngOnInit() {\n\n }\n\n\n\n\n\n}\n","\n \n \n \n {{data.question | translate}}\n \n \n

    {{data.answer | translate}}

    \n
    \n
    \n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport {BaseService} from '../../services/base.service';\nimport { FormControl, FormGroup, Validators } from \"@angular/forms\";\nimport { Ticket, TicketForm } from './tab-support-ticket.interface';\nimport { RightSidebarService} from \"../../services/right-sidebar.service\";\nimport { FileUploadComponent } from \"../../modals/file-upload/file-upload.component\";\nimport { ModalData } from \"../../model/modal-data.model\";\nimport { ModalComponent } from \"../../components/modal/modal.component\";\nimport { FileSystemService } from \"../../services/file-system.service\";\nimport { MatDialog } from \"@angular/material/dialog\";\nimport { TicketService} from \"../../services/ticket.service\";\nimport { Subject, Subscription, takeUntil } from \"rxjs\";\nimport { DatePipe } from \"@angular/common\";\nimport { PageEvent } from '@angular/material/paginator';\nimport { environment } from '../../../environments/environment';\n\n@Component({\n selector: 'app-tab-support-ticket',\n templateUrl: './tab-support-ticket.component.html',\n styleUrls: ['./tab-support-ticket.component.scss']\n})\nexport class TabSupportTicketComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n\n public params = {\n page: 1,\n take: 25,\n search: '',\n }\n\n public totalItems = 0;\n currentIndex : any = 0;\n currentTake : any = 0;\n public ticketForm!: FormGroup;\n public ticketList: any[] = [];\n public ticketFiles: any[] = [];\n public isLoader: boolean = false;\n public searchQuery: string = '';\n private ticketSubscription: Subscription;\n\n constructor(\n public readonly baseService: BaseService,\n public readonly ticketService: TicketService,\n public rightSidebarService: RightSidebarService,\n public fileSystemService: FileSystemService,\n private dialog: MatDialog,\n private datePipe: DatePipe,\n ) {\n this.ticketSubscription = Subscription.EMPTY;\n }\n\n openModal(data: ModalData) {\n\n const modalRef = this.dialog\n .open(ModalComponent, {\n data,\n panelClass: data.containerClass,\n enterAnimationDuration: data?.enterAnimationDuration || '0ms',\n exitAnimationDuration: data?.exitAnimationDuration || '0ms',\n autoFocus: data?.autoFocus || true,\n width: this.baseService.isMobile ? '100%' : data?.width,\n maxWidth: this.baseService.isMobile ? '100vw' : '80vw',\n height: data?.height,\n });\n\n return modalRef.afterClosed();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n ngOnInit() {\n this.initForm();\n this.initTickets(this.params);\n }\n\n public initForm() {\n this.ticketForm = new FormGroup({\n name: new FormControl('', [Validators.required, Validators.pattern(/\\S+/),]),\n text: new FormControl('', [Validators.required, Validators.pattern(/\\S+/),]),\n });\n }\n\n public initTickets(params: any, search: string = ''){\n this.ticketList.splice(0, this.ticketList.length)\n const payload = {\n page: params.page,\n take: params.take,\n search: search,\n }\n\n this.isLoader = true;\n if (this.ticketSubscription) {\n this.ticketSubscription.unsubscribe();\n }\n this.ticketSubscription = this.ticketService.getAllTickets(payload)\n .subscribe((res: { items: Ticket[]; total: number }) => {\n this.ticketList.push(...res.items);\n this.totalItems = res.total;\n this.isLoader = false;\n });\n }\n\n public onCreateTicket() {\n if(this.ticketForm.valid){\n const ticket = this.ticketForm.getRawValue();\n\n const payload = {\n name: ticket.name,\n text: ticket.text,\n status: \"active\",\n attachment: this.ticketFiles,\n }\n this.ticketService.addTicket(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.ticketForm.reset();\n this.ticketFiles.splice(0, this.ticketFiles.length);\n this.initTickets(this.params);\n },\n });\n }\n }\n\n onOpenCard(ticket: Ticket) {\n this.rightSidebarService.open('ticket',\n {\n medium: true,\n data: {\n ticket: ticket\n }\n })\n }\n\n openFileUpload() {\n const dialogData = {\n title: 'choose_files',\n translate: true,\n component: FileUploadComponent,\n width: '70vw',\n };\n this.openModal(dialogData)\n .subscribe((res: string[]) => {\n res.forEach((fileId: string ) => {\n this.ticketFiles.push({url: environment.baseURL + '/assets/ticket/' + fileId});\n })\n });\n }\n\n deletePhoto(index: number) {\n this.ticketFiles.splice(index, 1);\n }\n\n formatDate(date: string): string {\n return this.datePipe.transform(date, 'dd.MM.yyyy HH:mm') ?? '';\n }\n\n onSearchChange(search: string): void {\n const trimmedSearch = search.trim();\n this.searchQuery = trimmedSearch;\n this.currentIndex = 0;\n this.initTickets( { page: this.currentIndex + 1, take: this.currentTake }, trimmedSearch);\n }\n\n onPaginationChange(paginationDetails: PageEvent) {\n const { pageIndex, pageSize } = paginationDetails;\n this.currentIndex = pageIndex;\n this.currentTake = pageSize;\n this.initTickets({ page: pageIndex + 1, take: pageSize });\n }\n}\n","
    \n \n \n {{ 'ticket_system.new_ticket' | translate }}\n \n \n
    \n \n {{ 'ticket_system.topic' | translate }}\n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n \n {{ 'ticket_system.message' | translate }}\n \n \n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n {{ 'field_cannot_be_empty' | translate }}\n \n \n \n\n
    \n \n {{ 'ticket_system.files' | translate }}\n \n
    \n
    \n \n delete\n \n \"{{i}}\"\n
    \n
    \n
    \n\n
    \n {{ 'ticket_system.attach_a_file' | translate }}\n {{ 'ticket_system.add_ticket' | translate }}\n
    \n
    \n
    \n
    \n\n \n \n {{ 'ticket_system.list_of_tickets' | translate }}\n \n \n\n \n
    \n \n
    \n
    \n\n \n \n \n \n \n
    \n {{ 'ticket_system.code' | translate }}: {{ticket.code}}\n {{ 'ticket_system.name' | translate }}: {{ ticket.name }}\n {{ 'ticket_system.date' | translate }}: {{ formatDate(ticket.created_at) }}\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n
    \n \n \n \n \n
    \n
    \n
    \n\n","import { FormControl } from '@angular/forms';\n\n\nexport const MockedList: any[] = [\n {\n name: 'Рома'\n },\n {\n name: 'Ілля'\n },\n]\n\n\nexport interface FunnelForm {\n name: FormControl,\n currency: FormControl,\n}\n","import { Component, Input } from '@angular/core';\nimport { BaseService } from '../../services/base.service';\nimport { FunnelForm, MockedList } from './tab-deals-settings-common.interface';\nimport { Subject, takeUntil } from 'rxjs';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { IFunnel } from '../../interfaces';\nimport { CrmService } from '../../services/crm.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { Router } from '@angular/router';\nimport { DialogService } from '../../services/dialog.service';\nimport * as _ from 'lodash';\nimport { CURRENCY, languages } from '../../enum/constants';\n\n\n@Component({\n selector: 'app-tab-deals-setting-common',\n templateUrl: './tab-deals-settings-common.component.html',\n styleUrls: ['./tab-deals-settings-common.component.scss']\n})\nexport class TabDealsSettingsCommonComponent {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Imported const`s, enums and others\n protected list = MockedList;\n protected currency = CURRENCY;\n\n// Forms relative\n public funnelForm!: FormGroup;\n\n public hasChange: boolean = false;\n public dollar: number = 0;\n\n// Inputs\n @Input() funnelData!: IFunnel;\n\n constructor(\n public readonly baseService: BaseService,\n public readonly crmService: CrmService,\n public readonly notificationService: NotificationService,\n public readonly router: Router,\n public readonly dialogService: DialogService,\n ) {\n }\n\n ngOnInit() {\n this.initForm();\n this.crmService.getDollar()\n .subscribe({\n next: (res: any) => {\n this.dollar = res;\n }\n });\n\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n this.funnelForm = new FormGroup({\n name: new FormControl(this.funnelData?.name, Validators.required),\n currency: new FormControl(this.funnelData?.currency, Validators.required),\n });\n\n this.onCreateGroupFormValueChange();\n }\n\n private onCreateGroupFormValueChange() {\n const initialValue: any = { ...this.funnelForm.getRawValue() };\n this.funnelForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.hasChange = !_.isEqual(value, initialValue);\n });\n }\n\n public onSaveFunnel() {\n const funnelName = this.funnelForm.controls.name.value.trim();\n\n if (funnelName !== '') {\n const payload = this.funnelForm.getRawValue();\n this.crmService.updateFunnel(payload, this.funnelData.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n this.funnelData = res;\n this.initForm();\n this.hasChange = false;\n\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_saving);\n }\n })\n } else {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.empty_name);\n }\n }\n\n public onDeleteFunnel() {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab-deals-settings-common`,\n });\n\n dialogRef.subscribe(result => {\n if (result) {\n this.crmService.deleteFunnel(this.funnelData.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n this.router.navigate(['/crm/deals']);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_delete_1);\n }\n })\n }\n });\n }\n\n protected readonly CURRENCY = CURRENCY;\n}\n","
    \n \n
    \n
    \n
    {{'name_funnel'| translate}}
    \n
    \n \n \n {{'field_cannot_be_empty' | translate}}.\n \n
    \n
    \n\n\n\n\n\n\n\n\n\n\n
    \n
    {{'main_currency' | translate}}
    \n \n
    \n {{'the_dollar_rate_is_calculated_by' | translate}} {{ this.dollar }} {{'UAH' | translate}}.*\n
    \n
    \n
    \n
    \n {{ 'save' | translate }}\n {{'delete_funnel' | translate}}\n
    \n
    \n
    \n\n","import { Component, Inject } from '@angular/core';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { CustomAttributesService } from '../../../services/custom-attributes.service';\nimport { NotificationService } from '../../../services/notifications.service';\nimport { Subject, takeUntil } from 'rxjs';\nimport { ERROR_MESSAGES, languages, LIST_CA } from '../../../enum/constants';\nimport { trimValidator } from '../../../helpers';\nimport { IError } from '../../../interfaces';\nimport { CreateForm } from './create-deals-fields.interface';\nimport * as _ from 'lodash';\n\n\n@Component({\n templateUrl: './create-deals-fields.component.html',\n styleUrls: ['./create-deals-fields.component.scss'],\n})\nexport class CreateDealsFields {\n private readonly destroy$: Subject = new Subject();\n\n public list: any[] = [...LIST_CA]\n\n public createForm!: FormGroup;\n public funnel_id!: string;\n public position: number = 1000;\n public field: any;\n\n public hasChanged: boolean = false;\n\n constructor(\n public dialogRef: MatDialogRef,\n private readonly customAttributesService: CustomAttributesService,\n private readonly notificationService: NotificationService,\n @Inject(MAT_DIALOG_DATA) public data: any,\n ) {\n this.funnel_id = this.data.data.funnel_id;\n this.position = this.data.data.position;\n this.field = this.data.data.field;\n }\n\n ngOnInit(): void {\n if (this.field) {\n this.createForm = new FormGroup({\n title: new FormControl(this.field.title, [Validators.required, trimValidator]),\n type: new FormControl(this.list.find((v) => v.value === this.field.type)),\n key: new FormControl(this.field.key, [Validators.required, trimValidator]),\n is_view_card: new FormControl(this.field.is_view_card),\n });\n } else {\n this.createForm = new FormGroup({\n title: new FormControl('', [Validators.required, trimValidator]),\n type: new FormControl(this.list[0]),\n key: new FormControl('', [Validators.required, trimValidator]),\n is_view_card: new FormControl(false),\n });\n }\n this.onCreateFormValueChange();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onCancelClick(): void {\n this.dialogRef.close();\n }\n\n createField() {\n const { title, type, key, is_view_card } = this.createForm.getRawValue();\n this.createForm.markAllAsTouched();\n\n if (this.createForm.valid) {\n const payload = {\n funnel_id: this.funnel_id,\n title: title,\n type: type?.value,\n key: key,\n is_view_card: is_view_card,\n position: this.position + 1000,\n };\n this.customAttributesService.createCA(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved)\n this.dialogRef.close({\n success: true\n });\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (ERROR_MESSAGES[err.code] === ERROR_MESSAGES['CUSTOM_FIELD_ALREADY_EXISTS']) this.createForm.controls['key'].setErrors({ 'incorrect': true });\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code]);\n })\n }\n })\n }\n }\n\n private onCreateFormValueChange() {\n const initialValue: any = { ...this.createForm.getRawValue() };\n this.createForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.hasChanged = !_.isEqual(value, initialValue);\n });\n }\n\n updateField() {\n const { title, is_view_card } = this.createForm.getRawValue();\n this.createForm.markAllAsTouched();\n\n if (title) {\n this.customAttributesService.updateCA({ title, is_view_card }, this.field.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved)\n this.dialogRef.close({\n success: true\n });\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_saving)\n }\n });\n }\n }\n}\n","
    \n
    \n
    \n \n \n {{'field_cannot_be_empty' | translate}}.\n \n
    \n
    \n \n \n {{'field_cannot_be_empty' | translate}}.\n \n \n {{'this_field_must_be_unique' | translate}}.\n \n
    \n
    \n \n {{'type_tooltip' | translate}}\n
    \n \n {{'display_in_quick_trades' | translate}}\n \n
    \n\n
    \n
    \n {{'cancel' | translate}}\n {{'add' | translate}}\n {{ 'save' | translate }}\n
    \n
    \n
    \n","import { Component, Input } from '@angular/core';\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { BaseService } from '../../services/base.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { CreateDealsFields } from '../../components/modals/create-deals-fields/create-deals-fields.component';\nimport { ICA } from './tab-deals-settings-fields.interface';\nimport { Subject, takeUntil } from 'rxjs';\nimport { IFunnel } from '../../interfaces';\nimport { CustomAttributesService } from '../../services/custom-attributes.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ModalService } from '../../services/modal.service';\nimport { languages, LIST_CA } from '../../enum/constants';\nimport { DialogService } from '../../services/dialog.service';\n\n\n@Component({\n selector: 'app-tab-funnel-fields',\n templateUrl: './tab-deals-settings-fields.component.html',\n styleUrls: ['./tab-deals-settings-fields.component.scss'],\n})\nexport class TabDealsSettingsFieldsComponent {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Enums and other imports\n protected readonly LIST_CA = LIST_CA;\n\n// Inputs\n @Input() funnelData!: IFunnel;\n\n// Vars\n public items: ICA[] = [];\n\n public isLoader: boolean = false\n\n constructor(\n public readonly dialog: MatDialog,\n public readonly baseService: BaseService,\n public readonly notificationService: NotificationService,\n public readonly customAttributesService: CustomAttributesService,\n public readonly modalService: ModalService,\n public readonly dialogService: DialogService,\n ) {\n }\n\n ngOnInit() {\n this.onGetAllFields();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n openModal() {\n const dialogData = {\n title: 'create_field',\n translate: true,\n data: {\n funnel_id: this.funnelData.id,\n position: this.items[this.items.length - 1]?.position || 0,\n },\n component: CreateDealsFields,\n width: '30vw',\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) this.onGetAllFields();\n });\n }\n\n public drop(event: CdkDragDrop): void {\n if (this.items[event.currentIndex].id === this.items[event.previousIndex].id) return;\n\n const position: number = this.calcMovedPosition(event.currentIndex, event.previousIndex);\n\n moveItemInArray(this.items, event.previousIndex, event.currentIndex);\n\n this.customAttributesService.updateCA({ position: position }, this.items[event.currentIndex].id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_moved);\n this.onGetAllFields(true);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_update_field_position)\n }\n })\n }\n\n private calcMovedPosition(cp: number, pp: number): number {\n if (cp === 0) return (this.items[0].position / 2);\n\n if (cp === (this.items.length - 1)) {\n const lastPosition: number = this.items[this.items.length - 1].position;\n\n return ((lastPosition + (lastPosition + 1000)) / 2);\n }\n\n if (cp > pp) return ((this.items[cp].position + this.items[cp + 1].position) / 2);\n if (cp < pp) return ((this.items[cp].position + this.items[cp - 1].position) / 2);\n\n return ((this.items[cp + 1].position + this.items[cp - 1].position) / 2);\n }\n\n public onGetAllFields(isPositionUpdate: boolean = false) {\n if (!isPositionUpdate) this.isLoader = true;\n\n const payload = {\n funnel_id: this.funnelData?.id,\n page: 1,\n take: 50\n }\n\n this.customAttributesService.getAllCA(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n this.items = [...res.items];\n this.isLoader = false;\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_getting_field);\n this.isLoader = false;\n }\n })\n }\n\n updateCA(item: ICA) {\n this.customAttributesService.updateCA({ is_view: item.is_view }, item.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.onGetAllFields();\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_update_field)\n }\n })\n }\n\n deleteCA(item: ICA) {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab-deals-settings-fields`,\n });\n dialogRef.subscribe( {\n next: (result: any) => {\n if (result) {\n this.customAttributesService.deleteCA(item.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n this.onGetAllFields();\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_delete_field)\n }\n })\n }\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_delete);\n }\n })\n }\n\n edit(item: ICA) {\n const dialogData = {\n title: 'change_field',\n translate: true,\n data: {\n funnel_id: this.funnelData.id,\n field: item,\n },\n component: CreateDealsFields,\n width: '30vw',\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) {\n this.onGetAllFields();\n }\n });\n }\n\n findByType(type: string) {\n return LIST_CA.find(v => v.value === type);\n }\n}\n","
    \n \n
    \n
    \n\n\n\n\n\n\n\n\n\n
    \n
    \n \n drag_indicator\n \n
    \n
    \n {{item.title}}\n {{findByType(item.type)?.name}}\n
    \n
    \n
    \n \n \n edit\n \n \n delete\n \n
    \n
    \n
    \n
    \n \n
    \n \n
    \n
    \n
    \n {{'no_one_field' | translate}}\n {{'аdd_field' | translate}}\n
    \n
    \n","import {Component, Input} from '@angular/core';\n\nimport {BaseService} from '../../services/base.service';\n\n\n@Component({\n selector: 'app-tab-support-contact',\n templateUrl: './tab-support-contact.component.html',\n styleUrls: ['./tab-support-contact.component.scss']\n})\nexport class TabSupportContactComponent {\n\n panelOpenState = false;\n\n constructor(\n\n public readonly baseService: BaseService,\n ) {\n }\n\n open() {\n this.panelOpenState = true\n }\n\n close() {\n this.panelOpenState = false\n }\n\n\n\n\n ngOnInit() {\n\n }\n\n\n\n\n\n}\n","
    \n
    \n
    \n \n

    Телеграм

    \n
    \n
    \n \n

    Фейсбук

    \n
    \n
    \n \n

    Тік Ток

    \n
    \n
    \n \n

    Ютуб

    \n
    \n
    \n \n

    {{'mail'| translate}}

    \n
    \n
    \n \n

    {{'phone'| translate}}

    \n
    \n
    \n\n
    \n","import { ColumnSortType, TableColumnTypes } from '../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'phone_number',\n sort: ColumnSortType.NONE,\n name: 'phone',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'tg_id',\n sort: ColumnSortType.NONE,\n name: 'approved',\n type: TableColumnTypes.BOOLEAN,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n","import { Component, Input } from '@angular/core';\nimport { initItemsInvoiceParams, NovaPoshtaService } from '../../services/nova-poshta.service';\nimport { IParams } from '../../model/http';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IntegrationService } from '../../services/integration.service';\nimport { BaseService } from '../../services/base.service';\nimport { tableHeaders } from './tab-logistic-telegram.interface';\nimport { MatDialog } from '@angular/material/dialog';\nimport { ModalService } from '../../services/modal.service';\nimport { initUsersParams } from '../../services/telegram.service';\nimport { IError } from '../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES } from '../../enum/constants';\nimport { LogisticService } from '../../services/logistic.service';\nimport { environment } from '../../../environments/environment';\n\n\n@Component({\n selector: 'app-tab-logistic-telegram',\n templateUrl: './tab-logistic-telegram.component.html',\n styleUrls: ['./tab-logistic-telegram.component.scss']\n})\nexport class TabLogisticTelegramComponent {\n private readonly destroy$: Subject = new Subject();\n\n @Input() apiKey!: any;\n\n dataSource$: Observable = this.logisticService.itemsTg$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items as any,\n pageSize: localStorage?.getItem('channelTake') || data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n }\n }))\n visibleColumns = tableHeaders.map(v => v.value);\n\n // Variables\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n private timerSearch: any;\n public phoneInput!: string;\n\n constructor(\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly novaPoshtaService: NovaPoshtaService,\n private readonly logisticService: LogisticService,\n public readonly baseService: BaseService,\n public readonly dialog: MatDialog,\n public readonly modalService: ModalService,\n ) {\n }\n\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.itemsRequest = this.logisticService\n .setQueryItemsTgParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n },\n error: (e) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n }\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.logisticService.loadItemsTg()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: (e) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n }\n });\n }\n\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n localStorage.removeItem('invoiceTake');\n localStorage.setItem('invoiceTake', pageSize);\n this.novaPoshtaService.updateInvoiceTake();\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take, }, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n this.logisticService.removePhoneIntegration(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n });\n },\n })\n }\n }\n\n onSearchChange(search: string) {\n if (this.timerSearch)\n clearTimeout(this.timerSearch)\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n search,\n }, true);\n }, 500)\n }\n\n addPhone() {\n this.logisticService.addPhoneIntegration(this.phoneInput)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({\n ...initUsersParams,\n });\n this.phoneInput = '';\n },\n error: (res: any) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'MAX_INTEGRATION') {\n this.notificationService.showError('', ERROR_MESSAGES[err.code].replace('{0}', 'Telegram'));\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n }\n })\n }\n });\n }\n\n protected readonly environment = environment;\n}\n","
    \n
    \n
    \"Telegram
    \n
    \n
    {{'add_a_phone_number'| translate}}
    \n

    {{'you_can_delete_at_any_time' | translate}}

    \n
    \n \n {{'add' | translate}}\n
    \n
    \n
    \n
    \n
    {{'connection_instructions' | translate}}:
    \n
    \n
    1. {{'add_users_who_will_have_access_to_Telegram' | translate}}.
    \n
    2. {{'go_to_the_Telegram_bot_and_register' | translate}}.
    \n
    {{'the_bot_is_available_at_this_link' | translate}}: @{{environment.logistic.telegram}}
    \n
    \n
    \n
    \n\n\n
    \n
    \n
    \n \n
    \n
    \n \n
    \n \n
    \n
    \n \n \n \n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../model/table.model';\n\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'ttn',\n sort: ColumnSortType.NONE,\n name: 'ttn',\n type: TableColumnTypes.TEXT_COPY,\n className: 'table-header',\n translate: true\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'status',\n type: TableColumnTypes.NOVA_POSHTA_STATUS,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'np_status',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'phone',\n sort: ColumnSortType.NONE,\n name: 'phone',\n type: TableColumnTypes.PHONE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'comments',\n sort: ColumnSortType.NONE,\n name: 'comment',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'np_sending_date',\n sort: ColumnSortType.NONE,\n name: 'sent_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'np_delivery_date',\n sort: ColumnSortType.NONE,\n name: 'delivered_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'np_scheduled_delivery_date',\n sort: ColumnSortType.NONE,\n name: 'approximate_arrival_date',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true\n },\n // {\n // value: 'created_at',\n // sort: ColumnSortType.NONE,\n // name: 'Створено',\n // type: TableColumnTypes.DATE,\n // className: 'table-header',\n // },\n // {\n // value: 'updated_at',\n // sort: ColumnSortType.NONE,\n // name: 'Обновлено',\n // type: TableColumnTypes.DATE,\n // className: 'table-header',\n // },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n {icon: 'edit', label: 'edit', value: 'EDIT'},\n {icon: 'delete', label: 'delete', value: 'DELETE'},\n ],\n translate: true\n },\n];\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, map, Observable, tap } from 'rxjs';\nimport { IParams } from '../model/http';\nimport { initItemsParams } from './channel.service';\n\n\nexport let invoiceTake = localStorage?.getItem('invoiceTake');\n\nexport const initListsParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport const initItemsInvoiceParams: IParams = {\n sort: '',\n order: '',\n search: '',\n take: 25,\n page: 1,\n};\n\nexport interface IListsApiResponse {\n items?: any[];\n total?: number;\n size?: number;\n page?: number;\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class UkrPoshtaService {\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n private readonly listsSubject$ = new BehaviorSubject({});\n public readonly lists$ = this.listsSubject$.asObservable();\n\n private readonly itemsInvoicesSubject$ = new BehaviorSubject({});\n public readonly itemsInvoices$ = this.itemsInvoicesSubject$.asObservable();\n\n private readonly itemsMyInvoicesSubject$ = new BehaviorSubject({});\n public readonly itemsMyInvoices$ = this.itemsMyInvoicesSubject$.asObservable();\n\n private itemsMyInvoices: any;\n private pageSize!: number;\n\n public readonly isLoader$ = new BehaviorSubject(false);\n\n public queryListsParams = {...initListsParams};\n public queryListInvoicesParams = {...initItemsInvoiceParams};\n\n\n // List Integration Nova Poshta\n private getAll(params: any) {\n return this.http.get(environment.baseURL + '/ukr/all', { params })\n .pipe(map(v => v.data));\n }\n\n loadLists() {\n this.isLoader$.next(true);\n return this.getAll(this.queryListsParams)\n .pipe(tap((data) => {\n this.listsSubject$.next(data);\n this.isLoader$.next(false);\n }));\n }\n\n setQueryListsParams(params: IParams) {\n const {sort, order, search, take, page} = params;\n this.queryListsParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryListsParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryListsParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryListsParams.search,\n take: take || this.queryListsParams.take,\n page: page || this.queryListsParams.page,\n };\n return this.loadLists();\n }\n\n // List Invoices Nova Poshta\n private getInvoicesAll(params: any) {\n return this.http.get(environment.baseURL + '/ukr/invoices/all', {params})\n .pipe(map(v => v.data));\n }\n\n private getAllMyInvoices(params: any) {\n return this.http.post(environment.nova_poshta, {...params})\n .pipe(map(v => v));\n }\n\n loadListInvoices() {\n this.isLoader$.next(true);\n return this.getInvoicesAll(this.queryListInvoicesParams)\n .pipe(tap((data) => {\n this.itemsInvoicesSubject$.next(data);\n this.isLoader$.next(false);\n }));\n }\n\n loadListMyInvoices(api_key: string, save = true) {\n this.isLoader$.next(true);\n return this.getAllMyInvoices({\n apiKey: api_key,\n modelName: 'InternetDocument',\n calledMethod: 'getDocumentList',\n methodProperties: {\n DateTimeFrom: '01.01.2000',\n DateTimeTo: '01.01.2024',\n Page: '1',\n GetFullList: '1',\n },\n })\n .pipe(tap((v) => {\n this.itemsMyInvoices = {...v};\n this.itemsMyInvoicesSubject$.next({\n ...v,\n data: v.data.slice(0, initItemsInvoiceParams.take || 25),\n totalCount: v.data.length,\n pageSize: initItemsInvoiceParams.take || 0,\n pageIndex: initItemsInvoiceParams.page || 0,\n });\n this.isLoader$.next(false);\n }));\n }\n\n changeMyInvoicesSort(sortState: {\n active: string;\n direction: string;\n }) {\n const {active, direction} = sortState;\n\n const sortFunction = (a: any, b: any) => {\n const [valueA, valueB] = [a[active], b[active]];\n return valueA.localeCompare(valueB) * (direction === 'asc' ? 1 : -1);\n };\n\n const sortedData = [...this.itemsMyInvoices.data].sort(sortFunction);\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.pageSize ? sortedData.slice(0, this.pageSize) : sortedData.slice(0, 25),\n totalCount: sortedData.length,\n });\n }\n\n changeMyInvoicesPagination(paginationDetails: {\n previousPageIndex: number;\n pageIndex: number;\n pageSize: number;\n length: number;\n }) {\n this.pageSize = paginationDetails.pageSize;\n\n const start = Math.max(paginationDetails.pageIndex, 0) * paginationDetails.pageSize;\n const end = start + paginationDetails.pageSize;\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.itemsMyInvoices.data.slice(start, end - 1),\n totalCount: this.itemsMyInvoices.data.length,\n });\n }\n\n searchMyInvoices(searchedValue: string) {\n const filteredData = this.itemsMyInvoices.data.filter((item: any) => {\n return [item.Description, item.IntDocNumber, item.StateName, item.RecipientContactPerson, item.RecipientsPhone, item.AdditionalInformation].some(v => v.includes(searchedValue));\n });\n\n this.itemsMyInvoicesSubject$.next({\n ...this.itemsMyInvoices,\n data: this.pageSize ? filteredData.slice(0, this.pageSize) : filteredData.slice(0, 25),\n totalCount: filteredData.length,\n });\n }\n\n setQueryListInvoicesParams(params: IParams) {\n const {sort, order, search, take, page} = params;\n this.queryListInvoicesParams = {\n sort: sort || typeof (sort) === 'string' ? sort : this.queryListInvoicesParams.sort,\n order: order || typeof (order) === 'string' ? order : this.queryListInvoicesParams.order,\n search: search || typeof (search) === 'string' ? search : this.queryListInvoicesParams.search,\n take: take || this.queryListInvoicesParams.take,\n page: page || this.queryListInvoicesParams.page,\n };\n return this.loadListInvoices();\n }\n\n\n addApiKey(payload: { title: string, api_key: string }) {\n return this.http.post(environment.baseURL + '/ukr/', payload);\n }\n\n removeApiKey(uuid: string) {\n return this.http.delete(environment.baseURL + `/ukr/${uuid}`);\n }\n\n checkApiKey(api_key: string) {\n return this.http.post(environment.nova_poshta, {\n apiKey: api_key,\n calledMethod: 'getCounterparties',\n methodProperties: {CounterpartyProperty: 'Sender'},\n modelName: 'Counterparty',\n });\n }\n\n getCabinets(search?: string) {\n const params: { search?: string } = {};\n if (search) {\n params.search = search.trim();\n }\n return this.http.get(environment.baseURL + '/ukr/cabinets', {params})\n .pipe(map(v => v.data));\n }\n\n updateInvoiceTake() {\n invoiceTake = localStorage?.getItem('invoiceTake');\n initItemsParams.take = (invoiceTake && +invoiceTake) || 25;\n }\n\n createInvoice(payload: any) {\n return this.http.post(environment.baseURL + '/ukr/invoices', payload);\n }\n\n updateInvoice(id: string, payload: any) {\n return this.http.put(environment.baseURL + `/ukr/invoices/${id}`, payload);\n }\n\n deleteInvoice(uuid: string) {\n return this.http.delete(environment.baseURL + `/ukr/invoices/${uuid}`);\n }\n\n deleteBulk(ids: string[]): Observable {\n const payload = {\n ids,\n }\n return this.http.patch<{ data: string[] }>(environment.baseURL + `/ukr/invoices/bulk/delete`, payload).pipe(\n map((res) => res.data),\n );\n }\n\n postCall(params: any) {\n return this.http.post(environment.nova_poshta, {...params})\n .pipe(map(v => v));\n }\n\n getTTNDetails(payload: any) {\n return this.http.post(environment.baseURL + '/ukr/ttn', payload);\n }\n}\n","import { Component, Inject, Input } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { BaseService } from '../../services/base.service';\nimport { IError } from \"../../interfaces\";\nimport { ERROR_MESSAGES, LOGISTIC_TYPES, languages } from \"../../enum/constants\";\nimport { NotificationService } from \"../../services/notifications.service\";\nimport { UkrPoshtaService } from \"../../services/ukr-poshta.service\";\nimport { NovaPoshtaService } from \"../../services/nova-poshta.service\";\n\n\ninterface IInvoiceForm {\n comments: FormControl,\n ttn: FormControl,\n phone: FormControl\n}\n\n@Component({\n templateUrl: './create-track-invoice.component.html',\n styleUrls: ['./create-track-invoice.component.scss'],\n})\nexport class CreateTrackInvoiceComponent {\n @Input() Error: boolean = false;\n\n public isFormSubmitted: boolean = false;\n public isValidSubmitted: boolean = false;\n public form!: FormGroup;\n public list: any[] = [\n { value: 'string', name: 'Строка' },\n { value: 'number', name: 'Число' },\n { value: 'date', name: 'Дата та час' },\n { value: 'link', name: 'Посилання' },\n { value: 'lists', name: 'Список' },\n { value: 'checkbox', name: 'Чекбокс' },\n ];\n public isUpdate: boolean = false;\n private readonly type: LOGISTIC_TYPES;\n\n constructor(\n public dialogRef: MatDialogRef,\n private readonly ukrPoshtaService: UkrPoshtaService,\n private readonly novaPoshtaService: NovaPoshtaService,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n @Inject(MAT_DIALOG_DATA) public data: any,\n ) {\n this.isUpdate = data.data?.is_update;\n this.type = data.data?.type;\n }\n\n async ngOnInit() {\n if (this.isUpdate) {\n this.form = new FormGroup({\n comments: new FormControl(this.data.data?.comments, Validators.maxLength(5000)),\n ttn: new FormControl(this.data.data?.ttn, Validators.required),\n phone: new FormControl(this.baseService.currentUserInfo.phone),\n });\n } else {\n this.form = new FormGroup({\n comments: new FormControl('', Validators.maxLength(5000)),\n ttn: new FormControl('', Validators.required),\n phone: new FormControl(this.baseService.currentUserInfo.phone, Validators.required),\n });\n }\n }\n\n onNoClick(): void {\n this.dialogRef.close();\n }\n\n onSubmit() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n if (this.type === LOGISTIC_TYPES.UKR_POSHTA) {\n this.ukrPoshtaService.createInvoice(this.form.getRawValue())\n .subscribe({\n next: () => {\n this.dialogRef.close(true);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'INTEGRATION_ALREADY_EXISTS') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.attached_waybill);\n this.isValidSubmitted = false;\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n this.isValidSubmitted = false;\n }\n });\n },\n });\n }\n\n if (this.type === LOGISTIC_TYPES.NOVA_POSHTA) {\n this.novaPoshtaService.createInvoice(this.form.getRawValue())\n .subscribe({\n next: () => {\n this.dialogRef.close(true);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'INTEGRATION_ALREADY_EXISTS') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.attached_waybill);\n this.isValidSubmitted = false;\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n this.isValidSubmitted = false;\n }\n });\n },\n });\n }\n }\n\n }\n\n onUpdate() {\n this.isFormSubmitted = true;\n\n if (this.form.valid) {\n this.isValidSubmitted = true;\n\n if (this.type === LOGISTIC_TYPES.NOVA_POSHTA) {\n this.novaPoshtaService.updateInvoice(this?.data?.data?.id, this.form.getRawValue())\n .subscribe({\n next: () => {\n this.dialogRef.close(true);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'INTEGRATION_ALREADY_EXISTS') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.attached_waybill);\n this.isValidSubmitted = false;\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n this.isValidSubmitted = false;\n }\n });\n },\n });\n }\n\n if (this.type === LOGISTIC_TYPES.UKR_POSHTA) {\n this.ukrPoshtaService.updateInvoice(this?.data?.data?.id, this.form.getRawValue())\n .subscribe({\n next: () => {\n this.dialogRef.close(true);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'INTEGRATION_ALREADY_EXISTS') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.attached_waybill);\n this.isValidSubmitted = false;\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n this.isValidSubmitted = false;\n }\n });\n },\n });\n }\n }\n }\n}\n","
    \n\n \n
    \n {{'field_cannot_be_empty' | translate}}.\n
    \n\n \n
    \n {{'field_cannot_be_empty' | translate}}..\n
    \n\n
    \n {{'max_characters' | translate}} 200.\n
    \n\n
    \n \n \n
    \n
    \n
    \n {{'max_characters' | translate}} 5000.\n
    \n
    \n
    \n {{form.controls.comments.value.length}}/5000\n
    \n
    \n
    \n
    \n\n\n\n
    \n\n \n \n {{'cancel' | translate}}\n \n \n\n \n {{'add' | translate}}\n \n\n \n {{'update' | translate}}\n \n\n
    \n","import { Component } from '@angular/core';\nimport {\n initItemsInvoiceParams,\n NovaPoshtaService,\n} from '../../services/nova-poshta.service';\nimport { IParams } from '../../model/http';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IntegrationService } from '../../services/integration.service';\nimport { BaseService } from '../../services/base.service';\nimport { tableHeaders } from './np-tab-tracker-invoice.interface';\nimport { CreateTrackInvoiceComponent } from '../../dialogs/create-track-invoice/create-track-invoice.component';\nimport { MatDialog } from '@angular/material/dialog';\nimport { ModalService } from '../../services/modal.service';\nimport { parseNovaPoshta, parseNovaPoshtaStatus } from '../../helpers';\nimport { DialogService } from '../../services/dialog.service';\nimport { languages, LOGISTIC_TYPES } from '../../enum/constants';\nimport { SelectionModel } from '@angular/cdk/collections';\n\n@Component({\n selector: 'app-np-tab-tracker-invoice',\n templateUrl: './np-tab-tracker-invoice.component.html',\n styleUrls: ['./np-tab-tracker-invoice.component.scss'],\n})\nexport class NpTabTrackerInvoiceComponent {\n visibleColumns = tableHeaders.map((v) => v.value);\n public isLoader: boolean = false;\n private readonly destroy$: Subject = new Subject();\n selectionTable = new SelectionModel(true, []);\n\n // Variables\n private itemsRequest: any;\n private dataSort: any;\n dataSource$: Observable =\n this.novaPoshtaService.itemsInvoices$.pipe(\n map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items.map((v: any) => {\n return {\n ...v,\n status: parseNovaPoshtaStatus(\n v?.np_data?.StatusCode,\n v?.np_data?.ActualDeliveryDate,\n ),\n np_status: v?.np_data?.Status,\n np_delivery_date: v?.np_data?.ActualDeliveryDate,\n np_scheduled_delivery_date: parseNovaPoshta(\n v?.np_data?.ScheduledDeliveryDate,\n ),\n };\n }) as any,\n pageSize: localStorage?.getItem('channelTake') || data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }),\n );\n private timerSearch: any;\n\n bulkActionList: any[] = [{ label: 'delete', value: 'DELETE' }];\n\n constructor(\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly novaPoshtaService: NovaPoshtaService,\n public readonly baseService: BaseService,\n public readonly dialog: MatDialog,\n public readonly modalService: ModalService,\n private readonly dialogService: DialogService,\n ) {}\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.selectionTable.clear();\n this.itemsRequest = this.novaPoshtaService\n .setQueryListInvoicesParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams(\n {\n page: res.page - 1,\n take: res.take,\n },\n true,\n );\n }\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.novaPoshtaService\n .loadListInvoices()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n localStorage.removeItem('invoiceTake');\n localStorage.setItem('invoiceTake', pageSize);\n this.novaPoshtaService.updateInvoiceTake();\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take }, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.ttn_delete`,\n });\n dialogRef.subscribe((result) => {\n if (result) {\n this.novaPoshtaService\n .deleteInvoice(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n });\n }\n\n if (event.label === 'EDIT') {\n const dialogData = {\n title: 'update',\n translate: true,\n component: CreateTrackInvoiceComponent,\n width: '30vw',\n data: {\n is_update: true,\n id: event.id,\n ttn: event.ttn,\n comments: event.comments,\n type: LOGISTIC_TYPES.NOVA_POSHTA,\n },\n };\n\n this.modalService.openModal(dialogData).subscribe((result) => {\n if (result) {\n this.getItemsWithUpdatedParams({});\n }\n });\n }\n }\n\n onSearchChange(search: string) {\n if (this.timerSearch) clearTimeout(this.timerSearch);\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams(\n {\n ...initItemsInvoiceParams,\n search,\n },\n true,\n );\n }, 500);\n }\n\n onAdd() {\n const dialogData = {\n title: 'add_ttn',\n translate: true,\n component: CreateTrackInvoiceComponent,\n width: '30vw',\n data: {\n type: LOGISTIC_TYPES.NOVA_POSHTA,\n },\n };\n\n this.modalService.openModal(dialogData).subscribe((result) => {\n if (result) {\n this.getItemsWithUpdatedParams({});\n }\n });\n }\n\n bulkActionPopup(event: any) {\n if (event.value === 'DELETE') {\n const ids = this.selectionTable.selected.map((v) => v.id);\n this.novaPoshtaService.deleteBulk(ids).subscribe({\n next: (res) => {\n if (res?.length) {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted,);\n this.getItemsWithUpdatedParams({});\n }\n },\n error: (res) => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_delete_2);\n },\n });\n }\n }\n}\n","
    \n
    \n
    \n \n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{ 'selected'| translate }}\n {{\n t.menuOpen ? 'expand_less' : 'expand_more'\n }}\n
    \n\n
    \n \n \n \n
    \n
    \n {{'add_ttn'| translate}}\n
    \n \n
    \n \n
    \n
    \n \n \n \n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../model/table.model';\n\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'ttn',\n sort: ColumnSortType.NONE,\n name: 'ТТН',\n type: TableColumnTypes.TEXT_COPY,\n className: 'table-header',\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'status',\n type: TableColumnTypes.UKR_POSHTA_STATUS,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'ukr_status',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.TEXT_TRANSLATE,\n className: 'table-header',\n },\n {\n value: 'phone',\n sort: ColumnSortType.NONE,\n name: 'phone',\n type: TableColumnTypes.PHONE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'comments',\n sort: ColumnSortType.NONE,\n name: 'comment',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'updated_at',\n sort: ColumnSortType.NONE,\n name: 'updated_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n {icon: 'edit', label: 'edit', value: 'EDIT'},\n {icon: 'delete', label: 'delete', value: 'DELETE'},\n ],\n translate: true,\n },\n];\n","import { Component } from '@angular/core';\nimport { initItemsInvoiceParams, UkrPoshtaService } from '../../services/ukr-poshta.service';\nimport { IParams } from '../../model/http';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IntegrationService } from '../../services/integration.service';\nimport { BaseService } from '../../services/base.service';\nimport { tableHeaders } from './ukr-tab-tracker-invoice.interface';\nimport { MatDialog } from '@angular/material/dialog';\nimport { ModalService } from '../../services/modal.service';\nimport { DialogService } from \"../../services/dialog.service\";\nimport { getUkrPoshtaStatus } from \"../../helpers\";\nimport { CreateTrackInvoiceComponent } from \"../../dialogs/create-track-invoice/create-track-invoice.component\";\nimport { languages, LOGISTIC_TYPES } from '../../enum/constants';\nimport { SelectionModel } from \"@angular/cdk/collections\";\n\n\n@Component({\n selector: 'app-ukr-tab-tracker-invoice',\n templateUrl: './ukr-tab-tracker-invoice.component.html',\n styleUrls: ['./ukr-tab-tracker-invoice.component.scss'],\n})\nexport class UkrTabTrackerInvoiceComponent {\n visibleColumns = tableHeaders.map(v => v.value);\n public isLoader: boolean = false;\n private readonly destroy$: Subject = new Subject();\n selectionTable = new SelectionModel(true, []);\n\n\n // Variables\n private itemsRequest: any;\n private dataSort: any;\n dataSource$: Observable = this.ukrPoshtaService.itemsInvoices$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items.map((v: any) => {\n return {\n ...v,\n status: v?.status,\n ukr_status: getUkrPoshtaStatus(v?.status),\n };\n }) as any,\n pageSize: localStorage?.getItem('channelTake') || data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }));\n private timerSearch: any;\n\n bulkActionList: any[] = [\n { label: 'delete', value: 'DELETE' }\n ]\n\n constructor(\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly ukrPoshtaService: UkrPoshtaService,\n public readonly baseService: BaseService,\n public readonly dialog: MatDialog,\n public readonly modalService: ModalService,\n private readonly dialogService: DialogService,\n ) {}\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n\n this.isLoader = isLoader;\n this.selectionTable.clear();\n this.itemsRequest = this.ukrPoshtaService\n .setQueryListInvoicesParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n }, true);\n }\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.ukrPoshtaService.loadListInvoices()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const {pageIndex, pageSize} = paginationDetails;\n localStorage.removeItem('invoiceTake');\n localStorage.setItem('invoiceTake', pageSize);\n this.ukrPoshtaService.updateInvoiceTake();\n\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({page, take}, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'DELETE') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.ttn_delete`,\n });\n\n dialogRef.subscribe(result => {\n if (result) {\n this.ukrPoshtaService.deleteInvoice(event.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({\n });\n },\n });\n }\n });\n }\n\n if (event.label === 'EDIT') {\n const dialogData = {\n title: 'update',\n translate: true,\n component: CreateTrackInvoiceComponent,\n width: '30vw',\n data: {\n is_update: true,\n id: event.id,\n ttn: event.ttn,\n phone: event.phone,\n comments: event.comments,\n type: LOGISTIC_TYPES.UKR_POSHTA\n },\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) {\n this.getItemsWithUpdatedParams({\n });\n }\n });\n }\n }\n\n onSearchChange(search: string) {\n if (this.timerSearch) {\n clearTimeout(this.timerSearch);\n }\n\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n search,\n }, true);\n }, 500);\n }\n\n onAdd() {\n const dialogData = {\n title: 'add_ttn',\n translate: true,\n component: CreateTrackInvoiceComponent,\n width: '30vw',\n data: {\n type: LOGISTIC_TYPES.UKR_POSHTA\n },\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) {\n this.getItemsWithUpdatedParams({\n ...initItemsInvoiceParams,\n });\n }\n });\n }\n bulkActionPopup(event: any) {\n if (event.value === 'DELETE') {\n const ids = this.selectionTable.selected.map(v => v.id);\n this.ukrPoshtaService.deleteBulk(ids).subscribe({\n next: (res) => {\n if (res?.length) {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n this.getItemsWithUpdatedParams({});\n }\n },\n error: (res) => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_delete_2);\n }\n });\n\n }\n }\n}\n","
    \n
    \n
    \n \n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{'selected'| translate}}\n {{\n t.menuOpen ? 'expand_less' : 'expand_more'\n }}\n
    \n
    \n \n \n \n
    \n
    \n {{'add_ttn'| translate}}\n
    \n \n
    \n \n
    \n
    \n \n \n \n
    \n
    \n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { BaseService } from '../../services/base.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { FUNNEL_STATUS, FUNNEL_TYPE } from '../../enum/constants';\nimport { languages } from '../../enum/constants';\n\n@Component({\n selector: 'app-create-funnel',\n templateUrl: './create-funnel.component.html',\n styleUrls: ['./create-funnel.component.scss']\n})\nexport class CreateFunnelComponent {\n\n// Funnel relative var-s\n public funnelName!: string;\n private readonly funnelType!: FUNNEL_TYPE;\n private readonly funnelStatus: FUNNEL_STATUS = FUNNEL_STATUS.DEFAULT;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) {\n this.funnelName = this.data.data?.funnel\n this.funnelType = this.data.data.type\n this.funnelStatus = this.data.data?.status || FUNNEL_STATUS.DEFAULT;\n }\n\n onSaveClick() {\n if (!(this.funnelName || '').trim()) {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.empty_funnel);\n } else {\n this.dialogRef.close({\n name: this.funnelName,\n type: this.funnelType,\n status: this.funnelStatus\n });\n }\n }\n\n onCancelClick(): void {\n this.dialogRef.close();\n }\n}\n","
    \n
    \n
    \n \n \n
    \n\n
    \n \n \n {{'cancel' | translate}}\n \n \n\n \n {{ 'save' | translate }}\n \n
    \n
    \n
    \n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { CreateColumnForm } from './create-column.interface';\nimport { Subject, takeUntil } from 'rxjs';\nimport { CrmService } from '../../services/crm.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IColumn } from '../../interfaces';\nimport { trimValidator } from '../../helpers';\nimport * as _ from 'lodash';\nimport { languages } from '../../enum/constants';\n\n\n@Component({\n selector: 'app-create-column',\n templateUrl: './create-column.component.html',\n styleUrls: ['./create-column.component.scss']\n})\nexport class CreateColumnComponent {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Var-s\n private readonly funnel_id!: string;\n private readonly position!: number;\n\n public readonly column!: IColumn;\n public createColumnForm!: FormGroup;\n\n public isSubmitted: boolean = false;\n public hasChange: boolean = false;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n public crmService: CrmService,\n public notificationService: NotificationService,\n ) {\n this.funnel_id = this.data.data.funnel_id;\n this.column = this.data.data.column;\n this.position = this.data.data.position;\n }\n\n ngOnInit() {\n this.initForm();\n }\n\n ngOnDestroy() {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initForm() {\n if (this.column) {\n this.createColumnForm = new FormGroup({\n name: new FormControl(this.column.name, [Validators.required, trimValidator]),\n bg_color: new FormControl(this.column.bg_color, Validators.required),\n text_color: new FormControl(this.column.text_color, Validators.required),\n });\n\n this.onCreateGroupFormValueChange();\n } else {\n this.createColumnForm = new FormGroup({\n name: new FormControl('', [Validators.required, trimValidator]),\n bg_color: new FormControl('#000000', Validators.required),\n text_color: new FormControl('#ffffff', Validators.required),\n });\n }\n }\n\n private onCreateGroupFormValueChange() {\n const initialValue: any = { ...this.createColumnForm.getRawValue() };\n this.createColumnForm.valueChanges\n .pipe(takeUntil(this.destroy$))\n .subscribe((value: any) => {\n this.hasChange = !_.isEqual(value, initialValue);\n });\n }\n\n public onSaveClick() {\n this.isSubmitted = true;\n const { name, bg_color, text_color } = this.createColumnForm.getRawValue();\n\n const payload = {\n funnel_id: this.funnel_id,\n name: name,\n bg_color: bg_color,\n text_color: text_color,\n position: this.position\n };\n\n if (!this.createColumnForm.valid) return;\n\n if (this.column) {\n this.crmService.updateColumn(payload, this.column.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved)\n this.dialogRef.close({\n success: true\n });\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_saving)\n }\n })\n } else {\n this.crmService.createColumn(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_saved)\n this.dialogRef.close({\n success: true\n });\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_saving)\n }\n })\n }\n }\n\n public onCancelClick() {\n this.dialogRef.close();\n }\n}\n","
    \n
    \n \n \n\n \n {{'field_cannot_be_empty' | translate}}.\n \n
    \n\n
    \n \n \n
    \n\n
    \n \n \n
    \n\n \n\n
    \n \n \n {{'cancel' | translate}}\n \n \n\n \n {{ 'save' | translate }}\n \n
    \n
    ","import { Component, Input } from '@angular/core';\nimport { MatDialog } from '@angular/material/dialog';\nimport { BaseService } from '../../services/base.service';\nimport { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { IAllColumnsResponse, IColumn, IFunnel } from '../../interfaces';\nimport { CrmService } from '../../services/crm.service';\nimport { Subject, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../services/notifications.service';\nimport { CreateColumnComponent } from '../../modals/create-column/create-column.component';\nimport { ModalService } from '../../services/modal.service';\nimport { DialogService } from '../../services/dialog.service';\nimport { languages } from '../../enum/constants';\n\n\n@Component({\n selector: 'app-tab-funnel-columns',\n templateUrl: './tab-funnel-columns.component.html',\n styleUrls: ['./tab-funnel-columns.component.scss']\n})\nexport class TabFunnelColumnsComponent {\n// Destroys\n private readonly destroy$: Subject = new Subject();\n\n// Inputs\n @Input() funnelData!: IFunnel;\n\n// Var-s\n public items: IColumn[] = [];\n public isLoader: boolean = false;\n\n\n constructor(\n public readonly dialog: MatDialog,\n public readonly baseService: BaseService,\n public readonly crmService: CrmService,\n private notificationService: NotificationService,\n private modalService: ModalService,\n private dialogService: DialogService,\n ) {\n }\n\n ngOnInit() {\n this.onGetAllColumns();\n }\n\n public onGetAllColumns(isPositionUpdate: boolean = false) {\n if (!isPositionUpdate) this.isLoader = true;\n const payload = {\n funnel_id: this.funnelData.id,\n page: 1,\n take: 50\n }\n\n this.crmService.getAllColumns(payload)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: IAllColumnsResponse) => {\n this.items = [...res.items];\n this.isLoader = false;\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_getting_columns)\n this.isLoader = false;\n }\n })\n }\n\n public openCreateModal(item?: IColumn) {\n const position: number = this.calcAddedPosition(item);\n\n const dialogData = {\n title: 'create_column',\n translate: true,\n data: {\n funnel_id: this.funnelData.id,\n position: position,\n },\n component: CreateColumnComponent,\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) {\n this.onGetAllColumns();\n }\n });\n }\n\n\n public drop(event: CdkDragDrop): void {\n if (this.items[event.currentIndex].id === this.items[event.previousIndex].id) return;\n\n const position: number = this.calcMovedPosition(event.currentIndex, event.previousIndex);\n moveItemInArray(this.items, event.previousIndex, event.currentIndex);\n\n this.crmService.updateColumn({ position: position }, this.items[event.currentIndex].id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_moved)\n this.onGetAllColumns(true)\n },\n error: (err) => {\n console.log(err)\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_update_column_position)\n }\n })\n }\n\n private calcMovedPosition(cp: number, pp: number): number {\n if (cp === 0) return (this.items[0].position / 2);\n\n if (cp === (this.items.length - 1)) {\n const lastPosition: number = this.items[this.items.length - 1].position;\n\n return ((lastPosition + (lastPosition + 1000)) / 2);\n }\n\n if (cp > pp) return ((this.items[cp].position + this.items[cp + 1].position) / 2);\n if (cp < pp) return ((this.items[cp].position + this.items[cp - 1].position) / 2);\n\n return ((this.items[cp + 1].position + this.items[cp - 1].position) / 2);\n }\n\n private calcAddedPosition(item?: IColumn): number {\n if (item) {\n const cp: number = this.items.indexOf(item);\n if (cp === 0) {\n if (this.items[1]?.position) {\n return (this.items[0].position + this.items[1].position) / 2;\n } else {\n return (this.items[0].position + 1000) / 2;\n }\n } else if (cp === (this.items.length - 1)) {\n return (this.items[cp].position + (this.items[cp].position + 1000)) / 2;\n } else {\n return (this.items[cp].position + this.items[cp + 1].position) / 2;\n }\n } else {\n return 1000;\n }\n }\n\n\n onDeleteColumn(column: IColumn) {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab-funnel-columns`,\n });\n\n dialogRef.subscribe({\n next: (result: any) => {\n if (result) {\n this.crmService.deleteColumn(column.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted)\n this.onGetAllColumns()\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_delete_column)\n }\n })\n }\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_1)\n }\n });\n }\n\n onUpdateColumn(column: IColumn) {\n const dialogData = {\n title: 'change_column',\n translate: true,\n data: {\n funnel_id: this.funnelData.id,\n column: column,\n },\n component: CreateColumnComponent,\n };\n\n this.modalService.openModal(dialogData)\n .subscribe(result => {\n if (result) this.onGetAllColumns();\n });\n }\n}\n","
    \n \n
    \n
    \n
    \n
    \n \n drag_indicator\n \n
    \n
    \n {{item.name}}\n
    \n
    \n\n
    \n \n edit\n \n\n \n delete\n \n
    \n\n
    \n
    \n add_circle_outline\n
    \n
    \n
    \n
    \n
    \n \n
    \n \n
    \n
    \n
    \n {{'there_are_no_columns' | translate}}\n {{'add_a_column'| translate}}\n
    \n
    \n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'daysLeft',\n pure: true\n})\nexport class DaysLeftPipe implements PipeTransform {\n constructor() {}\n\n transform(item: any): string | null {\n if (!item) {\n return null;\n }\n\n const currentDate = new Date();\n const activeToDate = new Date(item);\n\n const diffInTime = activeToDate.getTime() - currentDate.getTime();\n const diffInDays = Math.ceil(diffInTime / (1000 * 3600 * 24));\n\n if (diffInDays > 0) {\n return `${diffInDays}`;\n } else {\n return '0';\n }\n }\n}\n","import { Component, OnInit } from '@angular/core';\nimport { OlxAdvertService} from \"../../../../services/olx-advert.service\";\nimport { BaseService } from \"../../../../services/base.service\";\nimport { map, Observable, take, takeUntil } from \"rxjs\";\nimport { ActivatedRoute } from \"@angular/router\";\nimport { IParams } from \"../../../../model/http\";\nimport { ChannelService } from '../../../../services/channel.service';\nimport { CHANNEL_STATUS } from '../../../../enum/constants';\n\n\n@Component({\n selector: 'app-my-packets',\n templateUrl: './my-packets.component.html',\n styleUrls: ['./my-packets.component.scss'],\n})\nexport class MyPacketsComponent implements OnInit {\n packets: any[] = [];\n public items: any[] = [];\n public itemsRequest: any;\n public currentChannelId: any = null;\n public isLoader: boolean = false;\n private channelQuery = this.activatedRoute.snapshot.queryParams['channel']\n\n constructor(\n private readonly olxAdvertService: OlxAdvertService,\n public readonly baseService: BaseService,\n private readonly activatedRoute: ActivatedRoute,\n private readonly channelService: ChannelService,\n ) {\n }\n\n public filterStatuses = [\n { name: 'olx.packets.active', value: 'active', color: 'green', active: true, selected: false },\n { name: 'olx.packets.inactive', value: 'inactive', color: 'red', active: true, selected: false },\n ];\n\n dataSource$: Observable = this.olxAdvertService.itemsPackets$\n .pipe(map((data) => {\n return {\n items: data.data,\n pageSize: 10,\n pageIndex: data.page ? data.page : 0,\n length: data.total || 0,\n };\n }));\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.itemsRequest = this.olxAdvertService\n .setQueryPacketsParams(this.currentChannelId, params)\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n onValueChange(event: any) {\n this.currentChannelId = event?.id;\n this.baseService.setQuery('channel', this.currentChannelId);\n this.getItemsWithUpdatedParams({}, true);\n }\n\n onFilterChange(item: any) {\n if (item.selected) {\n this.filterStatuses.forEach(v => v.active = true);\n this.filterStatuses.forEach(v => v.selected = false);\n this.getItemsWithUpdatedParams({ page: 0, filters: '' }, true);\n } else {\n this.filterStatuses.forEach(v => v.active = false);\n this.filterStatuses.forEach(v => v.selected = false);\n item.active = true;\n item.selected = true;\n this.getItemsWithUpdatedParams({ page: 0, filters: item.value }, true);\n }\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.channelService.getChannels({ type: 'OLX', status: CHANNEL_STATUS.CONNECTED })\n .subscribe({\n next: (channels: any[]) => {\n this.items = channels;\n const data = {\n id: this.channelQuery || channels[0]?.id || null,\n };\n this.onValueChange(data);\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex } = paginationDetails;\n const page = pageIndex;\n this.getItemsWithUpdatedParams({ page }, true );\n }\n}\n","
    \n \n \n \n
    \n
    \n \n {{ item.name | translate }}\n
    \n \n
    \n \n \n \n \n\n\n
    \n \n
    \n
    \n\n
    \n\n\n\n\n\n\n\n\n\n
    \n \n
    \n
    \n
    \n
    {{ item.categories_labels[0] }}
    \n
    \n
    \n {{ 'olx.packets.start' | translate }}\n {{ 'olx.packets.premium' | translate }}\n {{ 'olx.packets.mega' | translate }}\n
    \n
    {{ item.name }}
    \n
    \n
    \n
    \n
    {{ 'olx.packets.location' | translate}}:
    \n
    {{ item.location_name}}
    \n
    \n
    \n
    \n
    \n
    \n
    {{ 'olx.packets.available' | translate }}:
    \n
    {{ item.left }} / {{ item.size }} {{ 'olx.packets.placements' | translate }}
    \n
    \n
    \n
    {{ 'olx.packets.valid_until' | translate }}:
    \n
    \n
    {{ item.active_to | date: 'dd.MM.YY в HH:mm' }}
    \n
    \n {{ 'olx.packets.active_for' | translate }} {{ item.active_to | daysLeft }} {{ 'olx.packets.days' | translate }}\n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n {{ 'nothing_found' | translate }}\n
    \n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'id',\n sort: ColumnSortType.NONE,\n name: 'ID',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'images',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.OLX_IMAGE,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'title',\n sort: ColumnSortType.NONE,\n name: 'name',\n type: TableColumnTypes.TITLE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'description',\n sort: ColumnSortType.NONE,\n name: 'description',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'status',\n type: TableColumnTypes.OLX_ADVERT_STATUS,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'price_value',\n sort: ColumnSortType.NONE,\n name: 'price',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'contact_name',\n sort: ColumnSortType.NONE,\n name: 'contact',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'full_location',\n sort: ColumnSortType.NONE,\n name: 'city',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'contact_phone',\n sort: ColumnSortType.NONE,\n name: 'contact_phone',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'url',\n sort: ColumnSortType.NONE,\n name: 'link',\n type: TableColumnTypes.LINK,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'type',\n sort: ColumnSortType.NONE,\n name: 'type',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'valid_to',\n sort: ColumnSortType.NONE,\n name: 'valid_to',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'planned_count',\n sort: ColumnSortType.NONE,\n name: 'planned_activations',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'schedule', label: 'to_plan', value: 'PLANNER', color_icon: 'gray' },\n { icon: 'toggle_on', label: 'activate_now', value: 'ACTIVE_NOW', color_icon: 'green' },\n { icon: 'toggle_off', label: 'deactivate_now', value: 'DEACTIVATE_NOW', color_icon: 'red' },\n { icon: 'vertical_align_top', label: 'move_up_list', value: 'PUSH_UP', color_icon: 'green' },\n { icon: 'edit', label: 'change_price', value: 'CHANGE_PRICE', color_icon: 'gray' },\n // { icon: 'edit', label: 'change', value: 'CHANGE', color_icon: 'gray' },\n { icon: 'difference', label: 'duplicate', value: 'DUPLICATION_ADVERT', color_icon: 'gray' },\n { icon: 'delete', label: 'delete', value: 'DELETE_ADVERT', color_icon: 'red' },\n // { icon: 'paid', label: 'Рекламувати', value: 'PAID', color_icon: 'blue' },\n ],\n translate: true,\n },\n];\n","import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';\nimport { RightSidebarService } from '../../../../services/right-sidebar.service';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../../../services/notifications.service';\nimport { BaseService } from '../../../../services/base.service';\nimport { ChannelService, initItemsParams } from '../../../../services/channel.service';\nimport { ModalService } from '../../../../services/modal.service';\nimport { AppTableSource, ColumnSortType } from '../../../../model/table.model';\nimport { tableHeaders } from './my-adverts.interface';\nimport { IParams } from '../../../../model/http';\nimport { OlxAdvertService } from '../../../../services/olx-advert.service';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport { parseCurrencyXXX } from '../../../../helpers';\nimport { DialogService } from '../../../../services/dialog.service';\nimport { IError } from '../../../../interfaces';\nimport { CHANNEL_STATUS, DEFAULT_ERROR, ERROR_MESSAGES, languages, MODAL_TYPES } from '../../../../enum/constants';\nimport { ActivatedRoute } from '@angular/router';\nimport { ChangePriceComponent } from '../../../../dialogs/change-price/change-price.component';\nimport { PlannerComponent } from '../../../../modals/planner/planner.component';\nimport moment from 'moment';\n\n\n@Component({\n selector: 'app-my-adverts',\n templateUrl: './my-adverts.component.html',\n styleUrls: ['./my-adverts.component.scss'],\n})\nexport class MyAdvertsComponent implements OnInit, OnDestroy {\n private readonly destroy$: Subject = new Subject();\n dataSource$: Observable = this.olxAdvertService.items$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n this.statistic = data.statistic || {};\n return {\n headers: tableHeaders,\n rows: data.items?.map((v: any) => {\n return {\n ...v,\n full_location: [v.city?.name, v.district?.name].filter(Boolean).join(', '),\n price_value: v.price?.trade ? languages[localStorage.getItem('language') || 'ua'].trade : parseCurrencyXXX(v.price?.value || 0, v.price?.currency),\n planned_count: 0,\n contact_name: v.contact?.name,\n contact_phone: v.contact?.phone,\n type: v.advertiser_type,\n };\n }) as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }));\n visibleColumns = tableHeaders.map(v => v.value);\n selectionTable = new SelectionModel(true, []);\n\n public bulkActionList: any[] = [\n // { label: 'to_plan', value: 'PLANNER' },\n { label: 'activate_now', value: 'ACTIVE_NOW' },\n { label: 'deactivate_now', value: 'DEACTIVATE_NOW' },\n { label: 'move_up_list', value: 'PUSH_UP' },\n // { label: 'ads', value: 'PAY_ADS'}\n ];\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n\n public items: any[] = [];\n public currentChannelId: any = null;\n public statistic: any = {};\n public balance: any;\n\n private channelQuery = this.activatedRoute.snapshot.queryParams['channel'];\n\n public filterStatuses = [\n { name: 'olx.adverts.active', value: ['active'], key: 'active', color: 'green', active: true, selected: false },\n {\n name: 'olx.adverts.pending',\n value: ['new', 'disabled', 'unconfirmed', 'blocked'],\n key: 'waiting',\n color: 'gray',\n active: true,\n selected: false,\n },\n {\n name: 'olx.adverts.unpaid',\n value: ['limited', 'unpaid'],\n key: 'payment',\n color: 'orangered',\n active: true,\n selected: false,\n },\n {\n name: 'olx.adverts.inactive',\n value: ['outdated', 'removed_by_user'],\n key: 'deactivate',\n color: 'orange',\n active: true,\n selected: false,\n },\n {\n name: 'olx.adverts.rejected',\n value: ['moderated', 'removed_by_moderator'],\n key: 'canceled',\n color: 'red',\n active: true,\n selected: false,\n },\n ];\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n public readonly baseService: BaseService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly modalService: ModalService,\n private readonly dialogService: DialogService,\n private readonly activatedRoute: ActivatedRoute,\n private readonly channelService: ChannelService,\n ) {\n }\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.selectionTable.clear();\n this.itemsRequest = this.olxAdvertService\n .setQueryItemsParams(this.currentChannelId, params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n // this.cdr.detectChanges();\n\n // this.loadPlanners(res.items.map((v: any) => v.id));\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n }, true);\n }\n },\n error: () => {\n this.isLoader = false;\n // this.cdr.detectChanges();\n },\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n if (this.baseService.currentCompanyInfo && this.baseService.currentCompanyInfo.settings?.is_olx_ads) {\n this.bulkActionList.push({ label: 'ads', value: 'PAY_ADS' });\n }\n\n this.channelService.getChannels({ type: 'OLX', status: CHANNEL_STATUS.CONNECTED })\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (channels: any[]) => {\n this.items = channels;\n const data = {\n id: this.channelQuery || channels[0]?.id || null,\n };\n this.onValueChange(data);\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnDestroy(): void {\n this.olxAdvertService.destroy();\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState?.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take }, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'ACTIVE_NOW') {\n if (['active', 'new'].includes(event.status)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.not_possible_for_active_ads);\n } else {\n this.olxAdvertService.updateStatus(this.currentChannelId, event.id, 'activate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_activation_1);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n }\n\n if (event.label === 'DEACTIVATE_NOW') {\n if (!['active', 'limited'].includes(event.status)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.possible_for_active_ads);\n } else {\n this.olxAdvertService.updateStatus(this.currentChannelId, event.id, 'deactivate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_deactivation_1);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n }\n\n if (event.label === 'PLANNER') {\n this.modalService.openModal({\n title: 'announcement_scheduler',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n advert_id: event.id,\n advert: event,\n channel_id: this.currentChannelId,\n },\n }).subscribe((res) => {\n if (res) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n const payload = {\n name: res.name,\n type: res.x_data.type,\n status: res.x_data.status,\n planned_at: dateTime,\n x_data: {\n advert: event,\n channel_id: this.currentChannelId,\n },\n };\n this.olxAdvertService.createPlanner(payload)\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_planned);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_planner);\n },\n });\n }\n });\n }\n\n if (event.label === 'CHANGE_PRICE') {\n const modalData = {\n title: 'change_price',\n translate: true,\n component: ChangePriceComponent,\n data: {\n value: event.price?.value ?? null,\n currency: event.price?.currency ?? 'UAH',\n trade: event.price?.trade ?? null,\n negotiable: event.price?.negotiable ?? null,\n },\n };\n this.modalService.openModal(modalData).subscribe({\n next: (res) => {\n if (!res) return;\n this.olxAdvertService.updatePrice(this.currentChannelId, `${event.id}`, res)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || DEFAULT_ERROR);\n });\n },\n });\n },\n error: (err) => {\n console.log(err);\n },\n });\n }\n\n if (event.label === 'DUPLICATION_ADVERT') {\n this.olxAdvertService.duplicationAdvert(this.currentChannelId, `${event.id}`)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n\n if (event.label === 'DELETE_ADVERT') {\n if (['active'].includes(event.status)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.not_possible_for_active_ads);\n } else {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.my-adverts`,\n });\n dialogRef.subscribe({\n next: (res) => {\n if (!res) return;\n this.olxAdvertService.deleteAdvert(this.currentChannelId, `${event.id}`)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n },\n });\n },\n });\n }\n }\n\n if (event.label === 'PUSH_UP') {\n if (!['active'].includes(event.status)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.possible_for_active_ads);\n } else {\n const dialogRef = this.dialogService.openConfirm({\n text: 'dialogs.push_up_ad',\n });\n dialogRef.subscribe(result => {\n if (result) {\n this.olxAdvertService.pushUpAdvert(this.currentChannelId, `${event.id}`)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.initBalance();\n const message = languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.push_up_ad + event.id;\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, message);\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n });\n }\n }\n }\n\n timerSearch: any;\n\n onSearchChange(search: string) {\n if (this.timerSearch)\n clearTimeout(this.timerSearch);\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search,\n }, true);\n }, 500);\n }\n\n onValueChange(event: any) {\n this.currentChannelId = event?.id;\n this.baseService.setQuery('channel', this.currentChannelId);\n this.initBalance();\n this.getItemsWithUpdatedParams({}, true);\n }\n\n bulkActionPopup(event: any) {\n const ids: string[] = this.selectionTable.selected\n .map(v => v.id)\n .filter(Boolean);\n\n const allIds: string[] = this.selectionTable.selected.map(v => v.id);\n\n if (event.value === 'ACTIVE_NOW') {\n this.olxAdvertService.updateBulkStatus(this.currentChannelId, ids, 'activate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_activation_2);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n\n if (event.value === 'DEACTIVATE_NOW') {\n this.olxAdvertService.updateBulkStatus(this.currentChannelId, ids, 'deactivate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_deactivation_2);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n\n if (event.value === 'PLANNER') {\n if (ids.length) {\n if (ids.length >= 2) {\n this.notificationService.showWarning(languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.warn, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.mass_activation_of_ads);\n }\n this.modalService.openModal({\n title: 'ads_activation_time',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n adverts: ids,\n channel_id: this.currentChannelId,\n },\n }).pipe(takeUntil(this.destroy$))\n .subscribe((res) => {\n if (res) {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n const payload = {\n adverts: this.selectionTable.selected,\n name: res.name,\n status: res.x_data.status,\n type: res.x_data.type,\n planned_at: dateTime,\n channel_id: this.currentChannelId,\n step: res.x_data.step,\n };\n this.olxAdvertService.bulkPlanners(payload)\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_planned);\n },\n error: () => {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error_while_planner);\n },\n });\n }\n });\n }\n }\n\n if (event.value === 'PAY_ADS') {\n this.rightSidebarService.open('create-ads', {\n max: true,\n data: {\n selectionTable: this.selectionTable.selected,\n channel_id: this.currentChannelId,\n },\n }).subscribe({\n next: (res) => {\n if (res === 'close') {\n this.getItemsWithUpdatedParams({});\n }\n },\n });\n }\n\n if (event.value === 'PUSH_UP') {\n const allStatus = this.selectionTable.selected.map(v => v.status);\n if (allStatus.some(status => status !== 'active')) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.possible_for_active_ads);\n } else {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.push_up_ad`,\n });\n dialogRef.subscribe(result => {\n if (result) {\n this.olxAdvertService.bulkPushUpAdvert(this.currentChannelId, allIds)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.initBalance();\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.bulk_push_up_ad);\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n });\n }\n }\n }\n\n onFilterChange(event: string, item: any) {\n if (item.selected) {\n this.filterStatuses.forEach(v => v.active = true);\n this.filterStatuses.forEach(v => v.selected = false);\n this.getItemsWithUpdatedParams({\n filters: { status: null },\n }, true);\n } else {\n this.filterStatuses.forEach(v => v.active = false);\n this.filterStatuses.forEach(v => v.selected = false);\n item.active = true;\n item.selected = true;\n this.getItemsWithUpdatedParams({\n filters: { status: item?.value },\n }, true);\n }\n }\n\n public initBalance() {\n this.olxAdvertService.getBalance(this.currentChannelId)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (balance) => {\n this.balance = balance;\n },\n error: () => {\n },\n });\n }\n\n public changeAdvert(advert: any) {\n this.rightSidebarService.open('change_advert', {\n medium: true,\n data: {\n advert,\n channel_id: this.currentChannelId,\n type_action: 'UPDATE',\n },\n }).subscribe({\n next: (res) => {\n if (res === 'close') {\n this.getItemsWithUpdatedParams({});\n }\n },\n });\n }\n\n public buyPackage() {\n this.rightSidebarService.open('buy-package', {\n max: true,\n data: { channel_id: this.currentChannelId },\n }).subscribe({\n next: (res) => {\n if (res === 'close') {\n this.getItemsWithUpdatedParams({});\n }\n },\n });\n }\n\n createAdvert() {\n this.rightSidebarService.open('change_advert', {\n max: true,\n data: {\n type_action: 'CREATE',\n channel_id: this.currentChannelId,\n },\n }).subscribe({\n next: (res) => {\n if (res === 'close') {\n this.getItemsWithUpdatedParams({});\n }\n },\n });\n }\n}\n","
    \n \n\n\n\n\n\n\n\n\n\n\n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{'selected'| translate}}\n {{\n t.menuOpen ? 'expand_less' : 'expand_more'\n }}\n
    \n
    \n \n \n \n
    \n
    \n
    \n {{ 'buy_package' | translate }}\n
    \n
    {{'your_account_information' | translate}}: {{ balance?.sum }} {{'UAH' | translate}}.
    \n
    {{'available_balance'| translate}}: {{ balance?.bonus }} {{'bonuses' | translate}}
    \n
    \n \n
    \n \n
    \n
    \n
    \n
    \n \n {{ item.name | translate }}: {{ statistic[item.key] ? statistic[item.key] : '0' }}\n
    \n \n
    \n
    \n Створити\n
    \n\n\n\n
    \n \n
    \n
    \n\n \n\n","import { ColumnSortType, TableColumnTypes } from '../../../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'images',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.OLX_IMAGE,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'title',\n sort: ColumnSortType.NONE,\n name: 'Назва',\n type: TableColumnTypes.TITLE,\n className: 'table-header',\n },\n {\n value: 'description',\n sort: ColumnSortType.NONE,\n name: 'Опис',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'Статус',\n type: TableColumnTypes.OLX_ADVERT_STATUS,\n className: 'table-header',\n },\n {\n value: 'price_value',\n sort: ColumnSortType.NONE,\n name: 'Ціна',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'contact_name',\n sort: ColumnSortType.NONE,\n name: 'Контакт Назва',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'contact_phone',\n sort: ColumnSortType.NONE,\n name: 'Контакт Телефон',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'url',\n sort: ColumnSortType.NONE,\n name: 'Силка',\n type: TableColumnTypes.LINK,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'Створений',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n {\n value: 'valid_to',\n sort: ColumnSortType.NONE,\n name: 'Доступний до',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'planned_at',\n sort: ColumnSortType.NONE,\n name: 'Дата наступної активації',\n type: TableColumnTypes.DATETIME,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'toggle_on', label: 'activate_now', value: 'ACTIVE_NOW', color_icon: 'green' },\n { icon: 'toggle_off', label: 'deactivate_now', value: 'DEACTIVATE_NOW', color_icon: 'red' },\n { icon: 'schedule', label: 'plan_activation', value: 'ACTIVE_PLAIN', color_icon: 'gray' },\n // { icon: 'vertical_align_top', label: 'Підняття вгору списку', value: 'PUSHUP', color_icon: 'green' },\n // { icon: 'paid', label: 'Рекламувати', value: 'PAID', color_icon: 'blue' },\n ],\n translate: true,\n },\n];\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../services/right-sidebar.service';\nimport { map, Observable, Subject } from 'rxjs';\nimport { NotificationService } from '../../../../services/notifications.service';\nimport { BaseService } from '../../../../services/base.service';\nimport { initItemsParams } from '../../../../services/channel.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { IntegrationService } from '../../../../services/integration.service';\nimport { ModalService } from '../../../../services/modal.service';\nimport { AppTableSource, ColumnSortType } from '../../../../model/table.model';\nimport { tableHeaders } from './my-templates.interface';\nimport { IParams } from '../../../../model/http';\nimport { OlxAdvertService } from '../../../../services/olx-advert.service';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport { parseCurrencyXXX } from '../../../../helpers';\n\n\n@Component({\n selector: 'app-my-adverts-templates',\n templateUrl: './my-templates.component.html',\n styleUrls: ['./my-templates.component.scss'],\n})\nexport class MyTemplatesComponent implements OnInit, OnDestroy {\n // Destroy relative\n private readonly destroy$: Subject = new Subject();\n dataSource$: Observable = this.olxAdvertService.items$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items.map((v: any) => {\n return {\n ...v,\n price_value: parseCurrencyXXX(v.price?.value, v.price?.currency),\n contact_name: v.contact?.name,\n contact_phone: v.contact?.phone,\n };\n }) as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }));\n visibleColumns = tableHeaders.map(v => v.value);\n selectionTable = new SelectionModel(true, []);\n\n public bulkActionList: any[] = [\n { label: 'Активувати зараз', value: 'ACTIVE_NOW' },\n { label: 'Деактивувати зараз', value: 'DEACTIVATE_NOW' },\n { label: 'Запланувати активацію', value: 'ACTIVE_PLANNER' },\n ];\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly baseService: BaseService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly dialog: MatDialog,\n private readonly modalService: ModalService,\n ) {\n }\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.selectionTable.clear();\n // this.itemsRequest = this.olxAdvertService\n // .setQueryItemsParams(this.currentOlxAccount, params)\n // .pipe(takeUntil(this.destroy$), take(1))\n // .subscribe({\n // next: (res) => {\n // this.isLoader = false;\n // this.loadPlanners(res.items.map((v: any) => v.id));\n // if (!res.items.length && res.page > 1) {\n // this.getItemsWithUpdatedParams({\n // page: res.page - 1,\n // take: res.take\n // }, true);\n // }\n // },\n // error: () => {\n // this.isLoader = false;\n // }\n // });\n }\n\n ngOnInit() {\n this.isLoader = true;\n // this.olxAdvertService.getIntegrationOlx()\n // .pipe(takeUntil(this.destroy$), take(1))\n // .subscribe({\n // next: (res) => {\n // this.items = res;\n // this.onValueChange(res[0] || null);\n // this.olxAdvertService.loadItems(this.currentOlxAccount)\n // .pipe(takeUntil(this.destroy$), take(1))\n // .subscribe({\n // next: () => {\n // this.isLoader = false;\n // },\n // error: () => {\n // this.isLoader = false;\n // }\n // });\n // },\n // error: () => {\n // this.isLoader = false;\n // }\n // });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take, }, true);\n }\n\n onActionChange(event: any) {\n }\n\n timerSearch: any;\n\n onSearchChange(search: string) {\n if (this.timerSearch)\n clearTimeout(this.timerSearch);\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search,\n }, true);\n }, 500);\n }\n\n onValueChange(event: any) {\n this.getItemsWithUpdatedParams({}, true);\n }\n\n bulkActionPopup(event: any) {\n }\n\n onFilterChange(event: string, item: any) {\n this.getItemsWithUpdatedParams({\n filters: { status: item?.value },\n });\n }\n\n protected readonly Object = Object;\n}\n","
    \n \n \n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{ 'selected'| translate }}\n {{\n t.menuOpen ? 'expand_less' : 'expand_more'\n }}\n
    \n\n
    \n \n \n \n
    \n
    \n \n
    \n\n
    \n \n
    \n
    \n\n \n\n","import { IProfileTab } from '../../../../logistic/components/nova-poshta/nova-poshta.interface';\nimport { IParams } from '../../../../../model/http';\nimport { ColumnSortType, TableColumnTypes } from '../../../../../model/table.model';\n\n\nexport enum Tabs {\n INVITERS = 'INVITERS',\n MANAGERS = 'MANAGERS',\n GROUPS = 'GROUPS',\n}\n\nexport const tabs: IProfileTab[] = [\n {\n name: 'invited_users',\n value: Tabs.INVITERS,\n count: 0,\n active: true,\n beta: false,\n translate: true\n },\n {\n name: 'managers',\n value: Tabs.MANAGERS,\n count: 0,\n active: false,\n beta: false,\n translate: true\n },\n {\n name: 'roles',\n value: Tabs.GROUPS,\n count: 0,\n active: false,\n beta: false,\n translate: true\n },\n];\n\nexport const tableInvitersHeaders = [\n {\n value: 'email',\n sort: ColumnSortType.NONE,\n name: 'email',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'status',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'role',\n sort: ColumnSortType.NONE,\n name: 'role',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'outgoing_mail', label: 'resend', value: 'RESEND' },\n { icon: 'backspace', label: 'cancel', value: 'CANCEL' },\n ],\n translate: true,\n },\n];\n\nexport const tableManagersHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox',\n translate: true,\n },\n {\n value: 'image_url',\n sort: ColumnSortType.NONE,\n name: 'avatar',\n type: TableColumnTypes.IMAGE,\n className: 'table-header',\n disabled: true,\n translate: true\n },\n {\n value: 'first_name',\n sort: ColumnSortType.NONE,\n name: 'first_name',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'last_name',\n sort: ColumnSortType.NONE,\n name: 'last_name',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'role',\n sort: ColumnSortType.NONE,\n name: 'type',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'group',\n sort: ColumnSortType.NONE,\n name: 'role',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'email',\n sort: ColumnSortType.NONE,\n name: 'email',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'phone',\n sort: ColumnSortType.NONE,\n name: 'phone_number',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'edit', label: 'update_data', value: 'EDIT' },\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n\nexport const tableGroupsHeaders = [\n {\n value: 'name',\n sort: ColumnSortType.NONE,\n name: 'name',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'edit', label: 'update_data', value: 'EDIT' },\n { icon: 'delete', label: 'delete', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { CrmService } from '../../services/crm.service';\nimport { tableManagersHeaders } from '../../modules/crm/team/components/team/team.interface';\nimport { IError } from '../../interfaces';\nimport { DEFAULT_PROFILE_ICON, ERROR_MESSAGES, languages } from '../../enum/constants';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IParams } from '../../model/http';\nimport { initItemsParams } from '../../services/channel.service';\nimport { RightSidebarService } from '../../services/right-sidebar.service';\nimport { DialogService } from '../../services/dialog.service';\nimport { environment } from \"../../../environments/environment\";\nimport { SelectionModel } from \"@angular/cdk/collections\";\n\n\n@Component({\n selector: 'app-tab-managers',\n templateUrl: './tab-managers.component.html',\n styleUrl: './tab-managers.component.scss'\n})\nexport class TabManagersComponent implements OnInit, OnDestroy {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Var`s\n public isLoader: boolean = false;\n public visibleColumns: string[] = tableManagersHeaders.map(v => v.value);\n public itemsRequest: any;\n public timerSearch: any;\n\n// Table relative\n private dataSort: any;\n public dataSource$: Observable = this.crmService.itemsManagers$\n .pipe(map((v) => {\n tableManagersHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n\n return {\n headers: tableManagersHeaders,\n rows: (v.items || []).map((item: any) => {\n const url = item.avatar_url ? `${environment.baseURL}/assets/avatar/${item.id}` : DEFAULT_PROFILE_ICON;\n // https://dev.jecrm.org/api/assets/avatar/c2ee8f33-c723-409e-911e-05d7baf73797\n const role = item.role === 'OWNER' ? languages[localStorage.getItem('language') || 'ua'].owner : languages[localStorage.getItem('language') || 'ua'].manager\n return { ...item, image_url: url, role }\n }) as any,\n pageSize: v?.size || 0,\n pageIndex: v.page ? v.page - 1 : 0,\n length: v.total || 0,\n }\n }))\n\n public selectionTable = new SelectionModel(true, []);\n public bulkActionList: any[] = [\n { label: 'change_role', value: 'CHANGE_ROLE' },\n { label: 'delete', value: 'DELETE_MANAGERS' },\n ];\n\n\n constructor(\n private readonly crmService: CrmService,\n private readonly dialogService: DialogService,\n private readonly notificationService: NotificationService,\n private readonly rightSidebarService: RightSidebarService,\n ) {\n }\n\n public ngOnInit(): void {\n this.onLoadListManagers();\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onLoadListManagers(): void {\n this.isLoader = true;\n this.selectionTable.clear();\n this.crmService.loadListManagers()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public getItemsWithUpdatedParams(params: IParams): void {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n\n this.isLoader = true;\n this.selectionTable.clear();\n this.itemsRequest = this.crmService\n .setQueryManagersParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res: any) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n });\n }\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public onSearchChange(search: string): void {\n if (this.timerSearch)\n clearTimeout(this.timerSearch)\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search: search,\n });\n }, 500)\n }\n\n public onSortChange(e: any): void {\n this.dataSort = e;\n this.getItemsWithUpdatedParams({\n sort: e.active,\n order: e.direction,\n });\n }\n\n public onPaginationChange(e: any): void {\n const { pageIndex, pageSize } = e;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take });\n }\n\n public onActionChange(e: any): void {\n if (e.label === 'EDIT') {\n this.rightSidebarService.open('update-manager', {\n medium: true,\n data: { manager_id: e.id },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.onLoadListManagers();\n }\n });\n }\n\n if (e.label === 'DELETE') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab_managers_1`,\n });\n\n dialogRef\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n if (res) {\n this.crmService.deleteManager(e.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted, '')\n this.onLoadListManagers();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n }\n\n public onRowClick(e: any): void {\n this.rightSidebarService.open('update-manager', {\n medium: true,\n data: { manager_id: e.id },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.onLoadListManagers();\n }\n });\n }\n\n public bulkActionPopup(event: any): void {\n const ids = this.selectionTable.selected.map((v) => v?.id)\n\n if (event.value === 'CHANGE_ROLE') {\n this.rightSidebarService.open('update-manager', {\n medium: true,\n data: { managers: ids },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.onLoadListManagers();\n }\n });\n }\n\n if (event.value === 'DELETE_MANAGERS') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab_managers_2`,\n });\n\n dialogRef\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n if (res) {\n this.crmService.bulkDeleteManager(ids)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted, '')\n this.onLoadListManagers();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n }\n}\n","
    \n \n \n
    \n \n
    \n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{ 'selected'| translate }}\n \n {{ t.menuOpen ? 'expand_less' : 'expand_more'}}\n \n
    \n
    \n \n \n \n
    \n
    \n\n \n
    \n \n
    \n
    \n\n \n \n\n \n
    \n
    \n
    \n","import { Component } from '@angular/core';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { CrmService } from '../../services/crm.service';\nimport { AppTableSource, ColumnSortType } from '../../model/table.model';\nimport { tableGroupsHeaders } from '../../modules/crm/team/components/team/team.interface';\nimport { RightSidebarService } from '../../services/right-sidebar.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { IError } from '../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages, PERMISSIONS } from '../../enum/constants';\nimport { IParams } from '../../model/http';\nimport { initItemsParams } from '../../services/channel.service';\nimport { DialogService } from '../../services/dialog.service';\n\n\n@Component({\n selector: 'app-tab-groups',\n templateUrl: './tab-groups.component.html',\n styleUrl: './tab-groups.component.scss'\n})\nexport class TabGroupsComponent {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Var`s\n public isLoader: boolean = false;\n public visibleColumns: string[] = tableGroupsHeaders.map(v => v.value);\n public itemsRequest: any;\n public timerSearch: any;\n\n// Table relative\n private dataSort: any;\n public dataSource$: Observable = this.crmService.itemsGroups$\n .pipe(map((v) => {\n tableGroupsHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n\n return {\n headers: tableGroupsHeaders,\n rows: v.items as any,\n pageSize: v?.size || 0,\n pageIndex: v.page ? v.page - 1 : 0,\n length: v.total || 0,\n }\n }))\n\n\n constructor(\n private readonly crmService: CrmService,\n private readonly dialogService: DialogService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n ) {\n }\n\n public ngOnInit(): void {\n this.onLoadListGroups();\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onLoadListGroups(): void {\n this.isLoader = true;\n this.crmService.loadListGroups()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: (e) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n public getItemsWithUpdatedParams(params: IParams): void {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n\n this.isLoader = true;\n this.itemsRequest = this.crmService\n .setQueryGroupsParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res: any) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n });\n }\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public onCreateGroup(): void {\n this.rightSidebarService.open('create-group', {\n max: true,\n }).pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_created)\n this.onLoadListGroups();\n }\n });\n }\n\n public onSearchChange(search: string): void {\n if (this.timerSearch)\n clearTimeout(this.timerSearch)\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search: search,\n });\n }, 500)\n }\n\n public onSortChange(e: any): void {\n this.dataSort = e;\n this.getItemsWithUpdatedParams({\n sort: e.active,\n order: e.direction,\n });\n }\n\n public onPaginationChange(e: any): void {\n const { pageIndex, pageSize } = e;\n const page = pageIndex + 1;\n const take = pageSize;\n\n this.getItemsWithUpdatedParams({ page, take });\n }\n\n public onActionChange(e: any): void {\n if (e.label === 'EDIT') {\n this.rightSidebarService.open('create-group', {\n max: true,\n data: { group_id: e.id },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.getItemsWithUpdatedParams({});\n }\n });\n }\n\n if (e.label === 'DELETE') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.tab_groups`,\n });\n\n dialogRef\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res: any) => {\n if (res) {\n this.crmService.deleteGroup(e.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted, '')\n this.onLoadListGroups();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n }\n\n public onRowClick(e: any): void {\n this.rightSidebarService.open('create-group', {\n max: true,\n data: { group_id: e.id },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.onLoadListGroups();\n }\n });\n }\n}\n","
    \n \n \n
    \n \n
    \n {{'create_role' | translate}}\n
    \n\n \n
    \n \n
    \n
    \n\n \n \n \n
    \n
    \n
    \n","import {Component} from '@angular/core';\nimport {map, Observable, Subject, take, takeUntil} from 'rxjs';\nimport {CrmService} from '../../services/crm.service';\nimport {AppTableSource, ColumnSortType} from '../../model/table.model';\nimport {tableInvitersHeaders} from '../../modules/crm/team/components/team/team.interface';\nimport {RightSidebarService} from '../../services/right-sidebar.service';\nimport {initItemsParams} from '../../services/channel.service';\nimport {IParams} from '../../model/http';\nimport {NotificationService} from '../../services/notifications.service';\nimport {IError} from '../../interfaces';\nimport { ERROR_MESSAGES, languages } from '../../enum/constants';\n\n\n@Component({\n selector: 'app-tab-inviters',\n templateUrl: './tab-inviters.component.html',\n styleUrl: './tab-inviters.component.scss'\n})\nexport class TabInvitersComponent {\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n// Var`s\n public isLoader: boolean = false;\n public visibleColumns: string[] = tableInvitersHeaders.map(v => v.value);\n public timerSearch: any;\n\n// Table relative\n private dataSort: any;\n private itemsRequest: any;\n public dataSource$: Observable = this.crmService.itemsInviters$\n .pipe(map((v) => {\n tableInvitersHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) column.sort = this.dataSort.direction;\n return column;\n });\n\n return {\n headers: tableInvitersHeaders,\n rows: v.items.map((item: any) => {\n const status = item.status === 'PENDING' ? languages[localStorage.getItem('language') || 'ua'].in_process : item.status;\n return { ...item, status };\n }),\n pageSize: v?.size || 0,\n pageIndex: v.page ? v.page - 1 : 0,\n length: v.total || 0,\n }\n }))\n\n\n constructor(\n private readonly crmService: CrmService,\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService\n ) {\n }\n\n public ngOnInit(): void {\n this.onLoadListInviters();\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onLoadListInviters(): void {\n this.isLoader = true;\n this.crmService.loadListInviters()\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: () => {\n this.isLoader = false;\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public onInviteManager(): void {\n this.rightSidebarService.open('invite-manager', {\n medium: true,\n }).pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'success') {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_sent)\n this.onLoadListInviters();\n }\n });\n }\n\n public getItemsWithUpdatedParams(params: IParams): void {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n\n this.isLoader = true;\n this.itemsRequest = this.crmService\n .setQueryInvitersParams(params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res: any) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n });\n }\n },\n error: (e: any) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public onSearchChange(search: string): void {\n if (this.timerSearch) clearTimeout(this.timerSearch);\n\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search: search,\n });\n }, 500)\n }\n\n public onSortChange(e: any): void {\n this.dataSort = e;\n\n this.getItemsWithUpdatedParams({\n sort: e.active,\n order: e.direction,\n });\n }\n\n public onPaginationChange(e: any): void {\n const { pageIndex, pageSize } = e;\n const page = pageIndex + 1;\n const take = pageSize;\n\n this.getItemsWithUpdatedParams({ page, take });\n }\n\n public onActionChange(e: any): void {\n if (e.label === 'RESEND') {\n this.crmService.resendInvite(e, e.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_resent)\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n })\n }\n else if (e.label === 'CANCEL') {\n this.crmService.cancelInvite(e, e.id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.onLoadListInviters();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_canceled)\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n })\n }\n }\n\n public onRowClick(e: any): void {\n }\n\n protected readonly languages = languages;\n}\n","
    \n \n \n
    \n \n
    \n {{ 'invite' | translate }}\n
    \n\n \n
    \n \n
    \n
    \n\n \n \n \n
    \n
    \n
    \n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { tabs, Tabs } from './team.interface';\nimport { PERMISSIONS } from \"../../../../../enum/constants\";\nimport { BaseService } from \"../../../../../services/base.service\";\nimport { Router } from \"@angular/router\";\n\n\n@Component({\n selector: 'app-team',\n templateUrl: './team.component.html',\n styleUrl: './team.component.scss'\n})\nexport class TeamComponent implements OnInit, OnDestroy {\n// Var`s\n public teamLoader: boolean = false;\n\n// Imported var-s, const`s, enums and other\n protected readonly Tabs = Tabs;\n protected readonly tabs = tabs;\n protected activeTab: any;\n\n// Destroy\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly baseService: BaseService,\n private readonly router: Router,\n ) {\n }\n\n public ngOnInit(): void {\n this.baseService.checkPermission(PERMISSIONS.TEAM_ACCESS);\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onChangeTab(selectedTab: any): void {\n this.activeTab = selectedTab;\n }\n}\n","
    \n\n

    {{ 'team' | translate }}

    \n\n \n\n
    \n \n
    \n\n \n\n \n
    \n \n
    \n \n
    \n\n \n
    \n \n
    \n \n
    \n\n \n
    \n \n
    \n \n
    \n\n
    \n
    \n
    \n\n","import {Component, Inject} from '@angular/core';\nimport {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';\nimport {BaseService} from \"../../services/base.service\";\nimport {NotificationService} from \"../../services/notifications.service\";\nimport {IntegrationService} from \"../../services/integration.service\";\nimport { IError } from '../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../enum/constants';\n\n\n@Component({\n templateUrl: './archived-channel.component.html',\n styleUrls: ['./archived-channel.component.scss'],\n})\nexport class ArchivedChannelComponent {\n\n itemsList: any[] = [];\n itemsListPageNumber: number = 1;\n itemsListTotal!: number;\n itemsListSize!: number;\n itemsListPageCount!: number;\n\n constructor(\n public dialogRef: MatDialogRef,\n @Inject(MAT_DIALOG_DATA) public data: any,\n private readonly baseService: BaseService,\n private readonly integrationService: IntegrationService,\n private readonly notificationService: NotificationService,\n ) {\n }\n\n ngOnInit() {\n this.initChannels();\n }\n\n initChannels(pagination?: boolean) {\n this.integrationService.getArchivedChannels(this.itemsListPageNumber).subscribe({\n next: (response) => {\n if (pagination) {\n this.itemsList = this.itemsList.concat(\n response.items,\n );\n } else {\n this.itemsList = (response.items || []);\n }\n\n\n this.itemsListTotal = response.total;\n this.itemsListSize = response.size;\n this.itemsListPageCount = Math.round(\n this.itemsListTotal / this.itemsListSize,\n );\n },\n error: (err) => {\n console.log('err', err);\n },\n });\n }\n\n returnChannel(channel: any) {\n this.integrationService.unarchiveChannel(channel.id)\n .subscribe({\n next: (res) => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.channel_restored);\n this.dialogRef.close();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n onNoClick(): void {\n this.dialogRef.close();\n }\n\n scrollToEnd(e: any) {\n if (e.target.offsetHeight + e.target.scrollTop >= e.target.scrollHeight) {\n this.loadChats();\n }\n }\n\n loadChats() {\n if (this.itemsListPageNumber <= this.itemsListPageCount) {\n this.itemsListPageNumber++;\n this.initChannels(true);\n }\n }\n\n}\n","
    \n
    \n
    \n
    {{ channel.name }}
    \n keyboard_return\n
    \n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'planner_name',\n sort: ColumnSortType.NONE,\n name: 'olx.planners.planner_name',\n type: TableColumnTypes.TITLE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'advert_id',\n sort: ColumnSortType.NONE,\n name: 'ID',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'advert_name',\n sort: ColumnSortType.NONE,\n name: 'olx.planners.advert_name',\n type: TableColumnTypes.TITLE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'status',\n sort: ColumnSortType.NONE,\n name: 'status',\n type: TableColumnTypes.PLANNER_STATUS,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'type_planner',\n sort: ColumnSortType.NONE,\n name: 'type',\n type: TableColumnTypes.PLANNER_TYPE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n translate: true,\n },\n {\n value: 'planned_at',\n sort: ColumnSortType.NONE,\n name: 'run_at',\n type: TableColumnTypes.DATETIME,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'reason',\n sort: ColumnSortType.NONE,\n name: 'reason_error',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n translate: true,\n actions:\n [\n { icon: 'manage_history', label: 'change_advert', value: 'CHANGE_ADVERT', color_icon: 'green' },\n // { icon: 'manage_history', label: 'change_planning', value: 'UPDATE_PLANNER', color_icon: 'green' },\n { icon: 'manage_history', label: 'to_draft', value: 'TO_DRAFT', color_icon: 'green' },\n { icon: 'manage_history', label: 'to_active', value: 'TO_ACTIVE', color_icon: 'green' },\n // { icon: 'replay', label: 're_planning', value: 'REPLAN_PLANNER', color_icon: 'gray' },\n { icon: 'delete', label: 'cancel_planning', value: 'CANCEL_PLANNER', color_icon: 'red' },\n ]\n },\n];\n","import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';\nimport { RightSidebarService } from '../../../../services/right-sidebar.service';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../../../services/notifications.service';\nimport { BaseService } from '../../../../services/base.service';\nimport { IntegrationService } from '../../../../services/integration.service';\nimport { AppTableSource, ColumnSortType } from '../../../../model/table.model';\nimport { tableHeaders } from './my-planners.interface';\nimport { IParams } from '../../../../model/http';\nimport { initItemsParams, OlxAdvertService } from '../../../../services/olx-advert.service';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport { IError } from '../../../../interfaces';\nimport {\n ADVERT_PLANNER_STATUS, CHANNEL_STATUS,\n DEFAULT_ERROR,\n ERROR_MESSAGES,\n languages,\n MODAL_TYPES,\n} from '../../../../enum/constants';\nimport { ModalService } from '../../../../services/modal.service';\nimport { DialogService } from '../../../../services/dialog.service';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { PlannerComponent } from '../../../../modals/planner/planner.component';\nimport moment from 'moment';\nimport { ChannelService } from '../../../../services/channel.service';\n\n\n@Component({\n selector: 'app-my-planners',\n templateUrl: './my-planners.component.html',\n styleUrls: ['./my-planners.component.scss'],\n})\nexport class MyPlannersComponent implements OnInit, OnDestroy {\n// Destroy relative\n private readonly destroy$: Subject = new Subject();\n\n// Outputs\n @Output() onReloadChat = new EventEmitter();\n\n public filterStatuses = [\n {\n name: 'olx.adverts.successfully',\n value: ['COMPLETED'],\n key: 'COMPLETED',\n color: 'green',\n active: true,\n selected: false,\n },\n {\n name: 'olx.adverts.in_progress',\n value: ['PROGRESS'],\n key: 'PROGRESS',\n color: 'gray',\n active: true,\n selected: false,\n },\n { name: 'olx.adverts.draft', value: ['DRAFT'], key: 'DRAFT', color: 'black', active: true, selected: false },\n {\n name: 'olx.adverts.waiting',\n value: ['WAITING'],\n key: 'WAITING',\n color: 'orangered',\n active: true,\n selected: false,\n },\n {\n name: 'olx.adverts.canceled',\n value: ['CANCELED'],\n key: 'CANCELED',\n color: 'orange',\n active: true,\n selected: false,\n },\n { name: 'olx.adverts.fail', value: ['FAIL'], key: 'FAIL', color: 'red', active: true, selected: false },\n ];\n\n public statistic: any = {};\n\n// DataSource\n dataSource$: Observable = this.olxAdvertService.itemsPlanners$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n this.statistic = data.statistic || {};\n return {\n headers: tableHeaders,\n rows: data.items?.map((v: any) => {\n return {\n advert_id: v.x_data?.advert?.id,\n advert_name: v.x_data?.advert?.title,\n images: v.x_data?.advert?.images,\n reason: v.x_data?.reason,\n planner_name: v.name,\n ...v,\n };\n }) as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }));\n\n public bulkActionList: any[] = [\n // { label: 'change_planning', value: 'UPDATE_PLANNERS' },\n { label: 'cancel_planning', value: 'CANCEL_PLANNERS' },\n { label: 'to_draft', value: 'TO_DRAFT' },\n { label: 'to_active', value: 'TO_ACTIVE' },\n ];\n\n public visibleColumns: string[] = tableHeaders.map(v => v.value);\n public selectionTable = new SelectionModel(true, []);\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n\n public items: any[] = [];\n public currentChannelId: any = null;\n public balance: any;\n public timerSearch: any;\n private channelQuery = this.activatedRoute.snapshot.queryParams['channel'];\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly dialogService: DialogService,\n private readonly modalService: ModalService,\n public readonly baseService: BaseService,\n public readonly router: Router,\n private readonly activatedRoute: ActivatedRoute,\n private readonly channelService: ChannelService,\n ) {\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.channelService.getChannels({ type: 'OLX', status: CHANNEL_STATUS.CONNECTED })\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (channels: any[]) => {\n this.items = channels;\n const data = {\n id: this.channelQuery || channels[0]?.id || null,\n };\n this.onValueChange(data);\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnDestroy(): void {\n this.olxAdvertService.destroy();\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private getItemsWithUpdatedParams(isLoader = false, params: IParams = {}) {\n if (this.itemsRequest) this.itemsRequest.unsubscribe();\n\n this.isLoader = isLoader;\n this.selectionTable.clear();\n\n this.itemsRequest = this.olxAdvertService\n .setQueryPlannersParams(this.currentChannelId, params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams(true, {\n page: res.page - 1,\n take: res.take,\n });\n }\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n private initBalance(): void {\n this.olxAdvertService.getBalance(this.currentChannelId)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (balance) => {\n this.balance = balance;\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n public onValueChange(event: any): void {\n this.currentChannelId = event?.id;\n this.baseService.setQuery('channel', this.currentChannelId);\n this.initBalance();\n this.getItemsWithUpdatedParams(true);\n }\n\n public onSearchChange(search: string): void {\n if (this.timerSearch) clearTimeout(this.timerSearch);\n\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams(true, {\n ...initItemsParams,\n search,\n });\n }, 500);\n }\n\n public onSortChange(sortState: any) {\n this.dataSort = sortState;\n\n const selectedFilter = this.filterStatuses.find(filter => filter.selected);\n\n this.getItemsWithUpdatedParams(false, {\n sort: sortState?.active,\n order: sortState.direction,\n filters: { status: selectedFilter?.value || null },\n });\n }\n\n public onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n\n this.getItemsWithUpdatedParams(true, { page, take });\n }\n\n public onActionChange(event: any) {\n if (event.label === 'REPLAN_PLANNER') {\n if (event.status === ADVERT_PLANNER_STATUS.WAITING) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.action_not_possible);\n return;\n }\n\n this.modalService.openModal({\n title: 're_planning',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n advert_id: event.advert_id,\n },\n }).subscribe(() => {\n this.getItemsWithUpdatedParams();\n });\n }\n\n // if (event.status !== ADVERT_PLANNER_STATUS.WAITING && event.type_planner !== 'PACKET') {\n // this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.action_not_possible)\n // return;\n // }\n console.log(event);\n\n if (event.label === 'CHANGE_ADVERT' && event.type_planner === 'CREATE_ADVERT') {\n this.rightSidebarService.open('change_advert', {\n medium: true,\n data: {\n is_planner: true,\n advert: event.x_data.advert,\n channel_id: this.currentChannelId,\n type_action: 'UPDATE',\n },\n }).subscribe({\n next: (res) => {\n if (res === 'close') {\n this.getItemsWithUpdatedParams();\n }\n },\n });\n }\n\n if (event.label === 'TO_DRAFT') {\n const payload = {\n id: event.id,\n status: 'DRAFT',\n };\n this.olxAdvertService.updateStatusPacket(payload)\n .subscribe(() => {\n this.getItemsWithUpdatedParams();\n });\n }\n\n if (event.label === 'TO_ACTIVE') {\n const payload = {\n id: event.id,\n status: 'WAITING',\n };\n this.olxAdvertService.updateStatusPacket(payload)\n .subscribe(() => {\n this.getItemsWithUpdatedParams();\n });\n }\n\n if (event.label === 'CANCEL_PLANNER') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.my-planners`,\n });\n\n dialogRef.subscribe((result: any) => {\n if (result) {\n this.olxAdvertService.cancelPlanner(event.id).subscribe({\n next: () => {\n this.getItemsWithUpdatedParams();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n });\n }\n }\n\n private preprocessPlanners(arr: any[]) {\n if (arr.every(v => v.status !== ADVERT_PLANNER_STATUS.WAITING)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.no_valid_status);\n return [];\n }\n\n if (arr.some(v => v.status !== ADVERT_PLANNER_STATUS.WAITING)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.contain_invalid_statuses);\n }\n\n return arr\n .filter((item) => !!item && item.status === ADVERT_PLANNER_STATUS.WAITING)\n .map(v => v.id);\n }\n\n public bulkActionPopup(event: any): void {\n // const ids = this.preprocessPlanners(this.selectionTable.selected);\n // if (!ids.length) return;\n const ids = this.selectionTable.selected.map(v => v.id);\n\n if (event.value === 'UPDATE_PLANNERS') {\n this.modalService.openModal({\n title: 'change_the_activation_time_of_the_ads',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n planners: ids,\n },\n }).subscribe({\n next: () => {\n this.getItemsWithUpdatedParams();\n },\n error: e => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n if (event.value === 'TO_ACTIVE') {\n this.olxAdvertService.bulkPlannersUpdate(ids, { status: 'WAITING' })\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n if (event.value === 'TO_DRAFT') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.to_draft`,\n });\n\n dialogRef.subscribe((result: any) => {\n if (result) {\n this.olxAdvertService.bulkPlannersUpdate(ids, { status: 'DRAFT' })\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n });\n }\n\n\n if (event.value === 'CANCEL_PLANNERS') {\n const dialogRef = this.dialogService.openConfirm({\n text: `dialogs.my-planners`,\n });\n\n dialogRef.subscribe((result: any) => {\n if (result) {\n this.olxAdvertService.cancelBulkPlanners(ids)\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams();\n },\n error: (e: any) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n });\n }\n }\n\n onFilterChange(event: string, item: any) {\n if (item.selected) {\n this.filterStatuses.forEach(v => v.active = true);\n this.filterStatuses.forEach(v => v.selected = false);\n this.getItemsWithUpdatedParams(true, {\n filters: { status: null },\n });\n } else {\n this.filterStatuses.forEach(v => v.active = false);\n this.filterStatuses.forEach(v => v.selected = false);\n item.active = true;\n item.selected = true;\n this.getItemsWithUpdatedParams(true, {\n filters: { status: item?.value },\n });\n }\n }\n\n onRowClick(event: any) {\n this.modalService.openModal({\n title: 'change_the_ad_activation_time',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n planner_id: event.id,\n info: event,\n },\n }).subscribe((res) => {\n const dateToString = moment(res.x_data.date).format('DD/MM/YYYY');\n const dateTime = moment(`${dateToString} ${res.x_data.time}`, 'DD/MM/YYYY HH:mm');\n const payload = {\n planned_at: dateTime,\n name: res.name,\n status: res.x_data.status,\n id: event.id,\n };\n this.olxAdvertService.updatePlanner2(payload, event)\n .subscribe(() => {\n this.getItemsWithUpdatedParams();\n });\n });\n }\n}\n","
    \n \n\n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{'selected'| translate}}\n \n {{t.menuOpen ? 'expand_less' : 'expand_more'}}\n \n
    \n
    \n \n \n \n
    \n\n
    \n\n
    \n
    {{'your_account_information' | translate}}: {{ balance?.sum }} {{'UAH' | translate}}.
    \n
    {{'available_balance' | translate}}: {{ balance?.bonus }} {{'bonuses' | translate}}
    \n
    \n\n \n
    \n\n
    \n
    \n \n {{ item.name | translate }}\n
    \n \n\n\n\n
    \n \n
    \n
    \n\n\n \n\n","import { Injectable } from '@angular/core';\nimport { environment } from '../../environments/environment';\nimport { map } from 'rxjs';\nimport { HttpClient } from '@angular/common/http';\n\n\n@Injectable({\n providedIn: 'root',\n})\nexport class MarketService {\n constructor(\n private readonly http: HttpClient,\n ) {\n }\n\n public getApplications() {\n return this.http.get(environment.baseURL + '/application')\n .pipe(map(v => v.data));\n }\n\n public install(application_id: string) {\n return this.http.post(environment.baseURL + `/application/install/${application_id}`, {})\n .pipe(map(v => v.data));\n }\n\n public uninstall(application_id: string) {\n return this.http.post(environment.baseURL + `/application/uninstall/${application_id}`, {})\n .pipe(map(v => v.data));\n }\n\n}\n","import { Component, Inject } from '@angular/core';\nimport { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';\nimport { RightSidebarService } from '../../services/right-sidebar.service';\nimport { take } from 'rxjs';\nimport { ModalService } from '../../services/modal.service';\nimport { MarketService } from '../../services/market.service';\n\n\n@Component({\n selector: 'app-tg-bot-modal',\n templateUrl: './chanel-right-bar.component.html',\n styleUrls: ['./chanel-right-bar.component.scss']\n})\nexport class ChanelRightBarComponent{\n\n public channel = [];\n public app = [];\n public tgBot = '';\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private dialogRef: MatDialogRef,\n private readonly modalService: ModalService,\n public readonly marketService: MarketService,\n @Inject(MAT_DIALOG_DATA) public data: any,\n\n ) {\n this.channel = data?.data?.channel;\n this.app = data?.data.app;\n this.tgBot = data?.data.tg;\n }\n\n onsubmit(key: any) {\n if (key === 'TELEGRAM') {\n this.rightSidebarService.open('integration-telegram-bot', {\n medium: true,\n data: {\n channel: this.channel,\n },\n })\n .pipe(take(2))\n .subscribe(() => {\n });\n this.onClose();\n } else {\n this.rightSidebarService.open('integration-olx', {\n medium: true,\n data: {\n channel: this.channel,\n app: this.app,\n },\n })\n .pipe(take(2))\n .subscribe((res: any) => {\n if (res === 'close') {\n // this.getItemsWithUpdatedParams({}, true);\n // this.loadStatistics();\n }\n });\n this.onClose();\n }\n }\n\n onClose() {\n this.dialogRef.close();\n }\n\n}\n","
    \n
    \n Telegram\n
    \n
    \n Olx\n
    \n
    \n","import { ColumnSortType, TableColumnTypes } from '../../../../model/table.model';\n\nexport const tableHeaders = [\n {\n value: 'primary',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.CHECKBOX,\n className: 'table-checkbox'\n },\n {\n value: 'id',\n sort: ColumnSortType.NONE,\n name: 'ID',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'images',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.OLX_IMAGE,\n className: 'table-header',\n disabled: true,\n },\n {\n value: 'title',\n sort: ColumnSortType.NONE,\n name: 'name',\n type: TableColumnTypes.TITLE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'description',\n sort: ColumnSortType.NONE,\n name: 'description',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'price_value',\n sort: ColumnSortType.NONE,\n name: 'price',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'contact_name',\n sort: ColumnSortType.NONE,\n name: 'contact',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'contact_phone',\n sort: ColumnSortType.NONE,\n name: 'contact_phone',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'url',\n sort: ColumnSortType.NONE,\n name: 'link',\n type: TableColumnTypes.LINK,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'created_at_olx',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'created_at_crm',\n sort: ColumnSortType.NONE,\n name: 'created_at_crm',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n disabled: true,\n translate: true,\n },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n { icon: 'restore', label: 'restore', value: 'RESTORE_ADVERT' },\n ],\n translate: true,\n },\n];\n","import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';\nimport { RightSidebarService } from '../../../../services/right-sidebar.service';\nimport { map, Observable, Subject, take, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../../../services/notifications.service';\nimport { BaseService } from '../../../../services/base.service';\nimport { ChannelService, initItemsParams } from '../../../../services/channel.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { IntegrationService } from '../../../../services/integration.service';\nimport { ModalService } from '../../../../services/modal.service';\nimport { AppTableSource, ColumnSortType } from '../../../../model/table.model';\nimport { tableHeaders } from './my-backups.interface';\nimport { IParams } from '../../../../model/http';\nimport { OlxAdvertService } from '../../../../services/olx-advert.service';\nimport { SelectionModel } from '@angular/cdk/collections';\nimport { parseCurrencyXXX } from '../../../../helpers';\nimport { DialogService } from '../../../../services/dialog.service';\nimport { IError } from '../../../../interfaces';\nimport {\n CHANNEL_TYPES_OBJECT,\n DEFAULT_ERROR,\n ERROR_MESSAGES,\n languages,\n MODAL_TYPES,\n} from '../../../../enum/constants';\nimport { ActivatedRoute } from '@angular/router';\nimport { PlannerComponent } from '../../../../modals/planner/planner.component';\n\n\n@Component({\n selector: 'app-my-backups',\n templateUrl: './my-backups.component.html',\n styleUrls: ['./my-backups.component.scss'],\n})\nexport class MyBackupsComponent implements OnInit, OnDestroy {\n // Destroy relative\n @Output() onReloadChat = new EventEmitter();\n\n private readonly destroy$: Subject = new Subject();\n dataSource$: Observable = this.olxAdvertService.items$\n .pipe(map((data) => {\n tableHeaders.map((column) => {\n column.sort = ColumnSortType.NONE;\n if (column.value === this.dataSort?.active) {\n column.sort = this.dataSort.direction;\n }\n return column;\n });\n return {\n headers: tableHeaders,\n rows: data.items?.map((v: any) => {\n return {\n ...v,\n price_value: parseCurrencyXXX(v.price?.value, v.price?.currency),\n contact_name: v.contact?.name,\n contact_phone: v.contact?.phone,\n };\n }) as any,\n pageSize: data?.size || 0,\n pageIndex: data.page ? data.page - 1 : 0,\n length: data.total || 0,\n };\n }));\n visibleColumns = tableHeaders.map(v => v.value);\n selectionTable = new SelectionModel(true, []);\n\n public bulkActionList: any[] = [\n { label: 'restore', value: 'PLANNER' },\n ];\n\n private itemsRequest: any;\n private dataSort: any;\n public isLoader: boolean = false;\n\n public items: any[] = [];\n public currentChannelId: any = null;\n public balance: any;\n private channelQuery = this.activatedRoute.snapshot.queryParams['channel'];\n\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n public readonly baseService: BaseService,\n private readonly olxAdvertService: OlxAdvertService,\n private readonly dialog: MatDialog,\n private readonly modalService: ModalService,\n private readonly dialogService: DialogService,\n private readonly activatedRoute: ActivatedRoute,\n private readonly channelService: ChannelService,\n ) {\n }\n\n getItemsWithUpdatedParams(params: IParams, isLoader = false) {\n if (this.itemsRequest) {\n this.itemsRequest.unsubscribe();\n }\n this.isLoader = isLoader;\n this.selectionTable.clear();\n this.itemsRequest = this.olxAdvertService\n .setQueryBackupsParams(this.currentChannelId, params)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (res) => {\n this.isLoader = false;\n // this.cdr.detectChanges();\n\n // this.loadPlanners(res.items.map((v: any) => v.id));\n if (!res.items.length && res.page > 1) {\n this.getItemsWithUpdatedParams({\n page: res.page - 1,\n take: res.take,\n }, true);\n }\n },\n error: () => {\n this.isLoader = false;\n // this.cdr.detectChanges();\n },\n });\n }\n\n ngOnInit() {\n this.isLoader = true;\n this.channelService.getChannels({ type: 'OLX' })\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (channels: any[]) => {\n this.items = channels;\n const data = {\n id: this.channelQuery || channels[0]?.id || null,\n };\n this.onValueChange(data);\n },\n error: () => {\n this.isLoader = false;\n }\n });\n }\n\n ngOnDestroy(): void {\n this.olxAdvertService.destroy();\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onSortChange(sortState: any) {\n this.dataSort = sortState;\n this.getItemsWithUpdatedParams({\n sort: sortState?.active,\n order: sortState.direction,\n });\n }\n\n onPaginationChange(paginationDetails: any) {\n const { pageIndex, pageSize } = paginationDetails;\n const page = pageIndex + 1;\n const take = pageSize;\n this.getItemsWithUpdatedParams({ page, take }, true);\n }\n\n onActionChange(event: any) {\n if (event.label === 'RESTORE_ADVERT') {\n this.olxAdvertService.backupRestore(this.currentChannelId, event.crm_id)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n }\n\n timerSearch: any;\n\n onSearchChange(search: string) {\n if (this.timerSearch)\n clearTimeout(this.timerSearch);\n this.timerSearch = setTimeout(() => {\n this.getItemsWithUpdatedParams({\n ...initItemsParams,\n search,\n }, true);\n }, 500);\n }\n\n onValueChange(event: any) {\n this.currentChannelId = event?.id;\n this.baseService.setQuery('channel', this.currentChannelId);\n this.initBalance();\n this.getItemsWithUpdatedParams({}, true);\n }\n\n bulkActionPopup(event: any) {\n const ids: string[] = this.selectionTable.selected\n .filter((v) => v.id && !v?.planned_count)\n .map(v => v.id);\n\n const allIds: string[] = this.selectionTable.selected.map(v => v.id);\n\n if (event.value === 'ACTIVE_NOW') {\n this.olxAdvertService.updateBulkStatus(this.currentChannelId, ids, 'activate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_activation_2);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n\n if (event.value === 'DEACTIVATE_NOW') {\n this.olxAdvertService.updateBulkStatus(this.currentChannelId, ids, 'deactivate')\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.submitted_for_deactivation_2);\n this.getItemsWithUpdatedParams({});\n },\n });\n }\n\n if (event.value === 'PLANNER') {\n if (ids.length) {\n if (ids.length >= 2) {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.warn, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.mass_activation_of_ads);\n }\n this.modalService.openModal({\n title: 'ads_activation_time',\n translate: true,\n component: PlannerComponent,\n data: {\n modalType: MODAL_TYPES.ADVERT,\n adverts: allIds,\n channel_id: this.currentChannelId,\n },\n }).pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n this.getItemsWithUpdatedParams({});\n });\n }\n }\n if (event.value === 'PUSH_UP') {\n const allStatus = this.selectionTable.selected.map(v => v.status);\n if (allStatus.includes(event.status)) {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.possible_for_active_ads);\n } else {\n this.olxAdvertService.bulkPushUpAdvert(this.currentChannelId, allIds)\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: () => {\n this.getItemsWithUpdatedParams({});\n this.initBalance();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'EXTERNAL_ERROR') {\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.redo_the_ad);\n } else {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n }\n });\n },\n });\n }\n }\n }\n\n onFilterChange(event: string, item: any) {\n this.getItemsWithUpdatedParams({\n filters: { status: item?.value },\n });\n }\n\n public initBalance() {\n this.olxAdvertService.getBalance(this.currentChannelId)\n .pipe(takeUntil(this.destroy$), take(1))\n .subscribe({\n next: (balance) => {\n this.balance = balance;\n },\n error: () => {\n },\n });\n }\n}\n","
    \n \n
    \n \n
    \n ({{ selectionTable.selected.length }}) {{'selected'| translate}}\n {{\n t.menuOpen ? 'expand_less' : 'expand_more'\n }}\n
    \n
    \n \n \n \n
    \n
    \n \n \n
    \n\n
    \n \n
    \n
    \n\n \n\n","import { FormControl } from '@angular/forms';\nimport { IPaymentForm } from '../../../payment/components/payment/payment.interface';\n\n\nexport interface IProfileTab {\n name: string;\n value: string;\n active: boolean;\n count: number;\n disabled?: boolean;\n dev?: boolean;\n translate?: boolean;\n}\n\nexport enum ProfileTabs {\n PROFILE = 'PROFILE',\n SETTINGS = 'SETTINGS',\n}\n\nexport interface ProfileForm {\n first_name: FormControl,\n last_name: FormControl,\n phone: FormControl,\n email: FormControl,\n}\n\nexport interface PasswordForm {\n old_password: FormControl,\n new_password: FormControl,\n}\n\nexport interface AvatarForm {\n file: FormControl,\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { Subject, switchMap, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { ChannelService } from '../../../../../services/channel.service';\nimport { MatDialog } from '@angular/material/dialog';\nimport { IntegrationService } from '../../../../../services/integration.service';\nimport { ModalService } from '../../../../../services/modal.service';\nimport { AvatarForm, IProfileTab, PasswordForm, ProfileForm, ProfileTabs } from './profile.interface';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { ProfileService } from '../../../../../services/profile.service';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { IError } from '../../../../../interfaces';\nimport { ERROR_MESSAGES, languages } from '../../../../../enum/constants';\n\n\n@Component({\n selector: 'app-adverts',\n templateUrl: './profile.component.html',\n styleUrls: ['./profile.component.scss'],\n})\nexport class ProfileComponent implements OnInit, OnDestroy {\n // Destroy relative\n private readonly destroy$: Subject = new Subject();\n private readonly tabQuery: string = this.activatedRoute.snapshot.queryParams['tab'];\n\n\n tabs: IProfileTab[] = [\n { name: \"profile\", value: ProfileTabs.PROFILE, count: 0, active: true, translate: true },\n { name: \"settings\", value: ProfileTabs.SETTINGS, count: 0, active: false, disabled: true, dev: true, translate: true },\n ];\n\n protected activeTab: any;\n\n public profileForm!: FormGroup;\n public passwordForm!: FormGroup;\n public avatarForm!: FormGroup;\n public formSubmitted: boolean = false;\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n public readonly baseService: BaseService,\n private readonly profileService: ProfileService,\n private readonly channelService: ChannelService,\n private readonly dialog: MatDialog,\n private readonly modalService: ModalService,\n private readonly activatedRoute: ActivatedRoute,\n private readonly router: Router,\n ) {\n }\n\n ngOnInit() {\n if (this.tabQuery === 'settings') {\n this.tabs[0].active = false;\n this.tabs[1].active = true;\n this.onChangeTab(this.tabs[1]);\n const snapshot = this.activatedRoute.snapshot;\n const params: any = { ...snapshot.queryParams };\n delete params.tab;\n this.router.navigate([], { queryParams: params });\n } else {\n this.tabs[0].active = true;\n this.tabs[1].active = false;\n this.onChangeTab(this.tabs[0]);\n }\n\n this.profileForm = new FormGroup({\n first_name: new FormControl(this.baseService.currentUserInfo?.first_name, Validators.required),\n last_name: new FormControl(this.baseService.currentUserInfo?.last_name, Validators.required),\n phone: new FormControl(this.baseService.currentUserInfo?.phone, [Validators.pattern(/^380/), Validators.minLength(12), Validators.required]),\n email: new FormControl(this.baseService.currentUserInfo?.email, Validators.required),\n })\n\n this.passwordForm = new FormGroup({\n old_password: new FormControl('', Validators.required),\n new_password: new FormControl('', Validators.required),\n })\n\n this.avatarForm = new FormGroup({\n file: new FormControl(null, [Validators.required]),\n })\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n onChangeTab(selectedTab: any) {\n this.activeTab = selectedTab;\n }\n\n protected readonly ProfileTabs = ProfileTabs;\n\n saveProfile() {\n this.formSubmitted = true;\n if (this.profileForm.valid) {\n const data = {\n first_name: this.profileForm.controls.first_name.value,\n last_name: this.profileForm.controls.last_name.value,\n }\n const phone = this.profileForm.controls.phone.value;\n if ((data.first_name !== this.baseService.currentUserInfo.first_name) || (data.last_name !== this.baseService.currentUserInfo.last_name)) {\n this.profileService.update(data)\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe({\n next: (res) => {\n this.formSubmitted = false;\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n }\n });\n }\n if (phone !== this.baseService.currentUserInfo.phone) {\n this.profileService.updatePhone(this.profileForm.controls.phone.value)\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe({\n next: (res) => {\n this.formSubmitted = false;\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n })\n },\n });\n }\n }\n }\n\n changePassword() {\n this.formSubmitted = true;\n if (this.passwordForm.valid) {\n const data = {\n old_password: this.passwordForm.controls.old_password.value,\n new_password: this.passwordForm.controls.new_password.value,\n }\n this.profileService.changePassword(data)\n .subscribe({\n next: (res) => {\n this.formSubmitted = false;\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.password_changed);\n }\n });\n }\n }\n\n isChangeData() {\n return (this.profileForm.getRawValue().first_name === this.baseService.currentUserInfo.first_name) &&\n (this.profileForm.getRawValue().last_name === this.baseService.currentUserInfo.last_name) &&\n (this.profileForm.getRawValue().phone === this.baseService.currentUserInfo.phone)\n }\n\n onFileChange(event: any) {\n const files = event.target.files as FileList;\n if (files.length > 0) {\n const file = files[0];\n this.avatarForm.patchValue({ file: file })\n }\n }\n\n resetFile() {\n this.avatarForm.reset();\n }\n\n getURL() {\n return URL.createObjectURL(this.avatarForm.controls.file.value);\n }\n\n saveFile() {\n this.profileService.changeAvatar(this.avatarForm.controls.file.value)\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe({\n next: (res) => {\n this.resetFile();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (res) => {\n (res.error?.errors || []).forEach( (err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n },\n });\n }\n\n public getPhoneValidationError(): string | null {\n if (this.profileForm && this.profileForm.controls.phone) {\n const control = this.profileForm.controls.phone;\n console.log(control);\n if (control.invalid && this.formSubmitted) {\n if (control.errors?.['required']) {\n return languages[localStorage.getItem('language') || 'ua'].please_enter_phone\n }\n else if (control.errors?.['pattern']) {\n return languages[localStorage.getItem('language') || 'ua'].wrong_phone_start\n }\n else if (control.errors?.['minlength']) {\n return languages[localStorage.getItem('language') || 'ua'].please_enter_valid_phone\n }\n }\n }\n return null;\n }\n}\n","
    \n
    \n
    \n \n
    \n
    \n\n
    \n
    \n
    \n \n {{baseService.currentUserInfo.first_name}} {{baseService.currentUserInfo.last_name}}\n
    \n {{ 'cancel'| translate}}\n {{ 'save' | translate }}\n
    \n
    \n
    \n \n \n
    \n
    \n
    \n \n
    \n {{'field_cannot_be_empty' | translate}}.\n
    \n \n
    \n {{'field_cannot_be_empty' | translate}}.\n
    \n \n
    \n {{ getPhoneValidationError() }}\n
    \n \n
    \n \n {{'field_cannot_be_empty' | translate}}, {{'and_must_be_an_email_address' | translate}}.\n \n
    \n
    \n\n {{ 'save' | translate }}\n
    \n
    \n
    \n \n
    \n
    \n
    \n \n
    \n {{'field_cannot_be_empty' | translate}}.\n
    \n \n
    \n {{'field_cannot_be_empty' | translate}}.\n
    \n
    \n\n {{'change_password' | translate}}\n
    \n
    \n
    \n
    \n\n
    \n\n\n\n\n
    \n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { RightSidebarService } from '../../../../../services/right-sidebar.service';\nimport { Subject, switchMap, takeUntil } from 'rxjs';\nimport { NotificationService } from '../../../../../services/notifications.service';\nimport { BaseService } from '../../../../../services/base.service';\nimport { IntegrationService } from '../../../../../services/integration.service';\nimport { FormControl, FormGroup } from '@angular/forms';\nimport { SettingsForm } from './settings.interface';\nimport { SettingsService } from '../../../../../services/settings.service';\nimport { Router } from '@angular/router';\nimport { languages, PERMISSIONS } from '../../../../../enum/constants';\nimport { TranslateService } from '@ngx-translate/core';\nimport { CompanyService } from \"../../../../../services/company.service\";\n\n@Component({\n selector: 'app-settings',\n templateUrl: './settings.component.html',\n styleUrls: ['./settings.component.scss'],\n})\nexport class SettingsComponent implements OnInit, OnDestroy {\n // Destroy relative\n private readonly destroy$: Subject = new Subject();\n public settingsForm!: FormGroup;\n public formSubmitted: boolean = false;\n public languages = [\n { name: 'Українська', value: 'ua' },\n { name: 'English', value: 'en' },\n ];\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n private readonly integrationService: IntegrationService,\n private readonly baseService: BaseService,\n public readonly settingsService: SettingsService,\n public readonly router: Router,\n ) {}\n\n ngOnInit() {\n this.baseService.checkPermission(PERMISSIONS.SETTINGS_ACCESS);\n this.initForm();\n }\n\n private initForm() {\n this.settingsForm = new FormGroup({\n is_notification: new FormControl(\n this.baseService.currentUserInfo.settings.is_notification,\n ),\n is_notification_sound: new FormControl(\n this.baseService.currentUserInfo.settings.is_notification_sound,\n ),\n is_message_read: new FormControl(\n this.baseService.currentUserInfo.settings.is_message_read,\n ),\n is_lazy_loading: new FormControl(\n this.baseService.currentUserInfo.settings.is_lazy_loading,\n ),\n language: new FormControl(\n this.baseService.currentUserInfo.settings.language,\n ),\n });\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n cancelChanges() {\n this.settingsForm.reset(this.baseService.currentUserInfo.settings);\n }\n\n isSave() {\n return !(\n this.settingsForm.controls.is_notification.value !==\n this.baseService.currentUserInfo.settings.is_notification ||\n this.settingsForm.controls.is_notification_sound.value !==\n this.baseService.currentUserInfo.settings.is_notification_sound ||\n this.settingsForm.controls.is_message_read.value !==\n this.baseService.currentUserInfo.settings.is_message_read ||\n this.settingsForm.controls.is_lazy_loading.value !==\n this.baseService.currentUserInfo.settings.is_lazy_loading ||\n this.settingsForm.controls.language.value !==\n this.baseService.currentUserInfo.settings.language\n );\n }\n\n public saveChanges() {\n this.formSubmitted = true;\n if (this.settingsForm.valid) {\n this.settingsService\n .updateSettings(this.settingsForm.getRawValue())\n .pipe(takeUntil(this.destroy$))\n .pipe(switchMap(() => this.baseService.getProfile()))\n .subscribe({\n next: (res) => {\n window.location.reload();\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_updated);\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', err.message);\n });\n },\n });\n }\n }\n}\n","
    \n

    {{ 'general' | translate }}

    \n\n
    \n
    \n \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n\n
    \n \n \n
    \n\n
    \n \n \n \n
    \n
    \n
    \n {{\n 'save' | translate\n }}\n {{\n 'cancel' | translate\n }}\n
    \n
    \n
    \n\n","import { NAME_PROJECT } from '../../enum/constants';\n\n\nexport interface Additional {\n name: string;\n included: boolean;\n additionally?: boolean;\n}\n\nexport interface Keys {\n olx: number;\n telegram: number;\n}\n\nexport interface Tariff {\n readonly id: string;\n readonly key: string;\n keys: Keys\n title: string;\n color: string;\n price: number;\n sales_price: number;\n sales_percent: number;\n package_percent: number;\n sales: number;\n additional: Additional[];\n}\n\nexport interface Advantage {\n img: string;\n description: string;\n}\n\nexport interface Statist {\n name: string;\n count: string;\n}\n\nexport interface Feedback {\n user: string;\n role: string;\n feedBack: string;\n}\n\nexport interface Ability {\n img: string;\n description: string[]\n}\n\nexport interface UsContact {\n icon: string;\n text: string;\n copyData?: string;\n}\n\nexport const arrFeedbacks: Feedback[] = [\n {\n feedBack: 'Це чудове програмне забезпечення, я задоволений його надійністю, простотою та швидкістю роботи.',\n user: 'Dan Kaul',\n role: 'IT Consultant'\n },\n {\n feedBack: 'Завдяки відмінній підтримці, готовій вирішувати всі питання та виявляти помилки, ви завжди на кроці впереду, уникаючи небезпеки суперечок. Надійна підтримка — це наша сила.',\n user: 'Saru Mat',\n role: 'Customer'\n },\n {\n feedBack: `Перевершуючи очікування, ${NAME_PROJECT} пропонує надійну підтримку, яка завжди готова допомогти вам у вирішенні будь-яких питань та уникненні конфліктів. Не ризикуйте помилками, обирайте надійність з ${NAME_PROJECT}.`,\n user: 'Yommi Pat',\n role: 'Customer'\n }]\n\nexport const arrAbilities: Ability[] = [\n {\n img: '/assets/images/bg/mock_picture.png',\n description:\n [\n '• Робота з будь якою кількістю акаунтів OLX.',\n `• Моментальне спілкування з клієнтами прямо з ${NAME_PROJECT} без входу в OLX.`\n ]\n },\n {\n img: '/assets/images/bg/crossplatform.avif',\n description:\n [\n `• Працюйте в ${NAME_PROJECT} на всіх своїх пристроях.`,\n '• Спілкуйтесь з усіма клієнтами з усіх ваших акаунтів прямо з вашого телефона.',\n ]\n },\n {\n img: '/assets/images/bg/messaging.png',\n description:\n [\n '• Всі повідомлення приходять миттєво без збоїв та затримок.',\n '• Отримуйте зображення від клієнта, в будь-який час, без втрат.'\n ]\n },\n {\n img: '/assets/images/bg/telegram-notification.jpeg',\n description:\n [\n '• Отримуйте сповіщення про нові повідомлення від клієнта прямо в свій телеграм.',\n ]\n },\n {\n img: '/assets/images/bg/tech.jpg',\n description:\n [\n `• ${NAME_PROJECT} базується на сучасних, прогресивних та захищених технологіях.`,\n `• ${NAME_PROJECT} працює на серверах Amazon, що забезпечує її стабільну і безпечну роботу.`,\n '• Ваші акаунти повністю в безпеці і захищені.',\n `• Робота ${NAME_PROJECT} з OLX відбувається по офіційному API.`,\n '• Акаунти OLX не будуть заблоковані і переведені на бізнес.'\n ]\n },\n {\n img: '/assets/images/bg/support.avif',\n description:\n [\n '• Максимальна підтримка від нашої команди.',\n '• Телефон, чат підтримки.'\n ]\n },\n {\n img: '/assets/images/bg/future.webp',\n description:\n [\n '• Логістика, трекінг ваших посилок через сервіси: Нова Пошта, УП, МІСТ',\n `• Сповіщення в ${NAME_PROJECT} про оформлення OLX-доставки.`,\n '• Підключення E-mail сервісів.',\n `• Моментальне спілкування з клієнтами прямо з ${NAME_PROJECT} без входу в OLX.`\n ]\n },\n]\n\nexport const arrStatistics: Statist[] = [\n {\n name: 'АКТИВНИХ КОРИСТУВАЧІВ',\n count: '100+'\n },\n {\n name: 'ПІДКЛЮЧЕНИХ АКАУНТІВ',\n count: '500+'\n },\n // {\n // name: 'УСПІШНИХ УГОД',\n // count: '20+'\n // },\n {\n name: 'ПОВІДОМЛЕНЬ ЗА ДЕНЬ',\n count: '1000+'\n },\n]\n\nexport const arrAdvantages: Advantage[] = [\n {\n img: '/assets/svgs/home/user.svg',\n description: 'Зручний інтерфейс та інтуїтивний дизайн для легкого використання.'\n },\n {\n img: '/assets/svgs/home/users.svg',\n description: 'Широкий спектр інтеграцій з іншими системами та сервісами.'\n },\n {\n img: '/assets/svgs/home/productivity.svg',\n description: 'Висока робоча продуктивність і автоматизація завдань.'\n },\n {\n img: '/assets/svgs/home/support.svg',\n description: 'Підтримка та сервісна служба.'\n },\n {\n img: '/assets/svgs/home/shield.svg',\n description: 'Надійність і безпека даних.'\n },\n {\n img: '/assets/svgs/home/prices.svg',\n description: 'Широкий вибір планів та цінових пропозицій.'\n },\n]\n\nexport const arrUsContacts: UsContact[] = [\n {\n icon: 'mail',\n text: 'jecrm.office@gmail.com',\n copyData: 'mailto:jecrm.office@gmail.com'\n },\n {\n icon: 'phone',\n text: '+380(99)048-49-80',\n copyData: 'tel:+380(99)048-49-80'\n },\n {\n icon: '',\n text: '+380(89)546-73-56',\n copyData: 'tel:+380(89)546-73-56'\n },\n {\n icon: 'location_city',\n text: 'Україна, м.Вінниця'\n },\n];\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport {\n Ability,\n Advantage,\n arrAbilities,\n arrAdvantages,\n arrFeedbacks,\n arrStatistics,\n arrUsContacts,\n Feedback,\n Statist,\n Tariff,\n UsContact,\n} from './home.component.interface';\nimport { ERROR_MESSAGES, NAME_PROJECT } from '../../enum/constants';\nimport { BinotelService } from '../../services/binotel.service';\nimport { AuthService } from '../../services/auth.service';\nimport { ITariff } from '../../modules/crm/payment/components/payment/payment.interface';\nimport { IError } from '../../interfaces';\nimport { TariffService } from '../../services/tariff.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { parseCurrency, scrollTo } from '../../helpers';\nimport { ActivatedRoute, Router } from '@angular/router';\n\n\n@Component({\n selector: 'app-home',\n templateUrl: './home.component.html',\n styleUrls: ['./home.component.scss'],\n})\n\nexport class HomeComponent implements OnInit, OnDestroy {\n// Imported types, constants and similar\n protected readonly arrFeedbacks: Feedback[] = arrFeedbacks;\n protected readonly arrAbilities: Ability[] = arrAbilities;\n protected readonly arrAdvantages: Advantage[] = arrAdvantages;\n protected readonly arrUsContacts: UsContact[] = arrUsContacts;\n protected readonly arrStatistics: Statist[] = arrStatistics;\n public tariffPackages: Tariff[] = [];\n\n// Variables for destroy\n private readonly destroy$: Subject = new Subject();\n\n// Other public variables\n protected readonly NAME_PROJECT = NAME_PROJECT;\n protected readonly currentYear = new Date().getFullYear();\n protected readonly parseCurrency = parseCurrency;\n protected readonly scrollTo = scrollTo;\n public isHeaderHidden: boolean = false;\n public lastScrollPosition: number = 0;\n\n constructor(\n private binotelService: BinotelService,\n private authService: AuthService,\n private tariffService: TariffService,\n private notificationService: NotificationService,\n private router: Router,\n private activatedRoute: ActivatedRoute,\n ) {\n }\n\n ngOnInit() {\n this.initUtm();\n this.binotelService.loadScripts();\n this.tariffService.getTariffs()\n .subscribe({\n next: (res) => {\n this.tariffPackages = res.map((t: ITariff) => {\n return {\n ...t,\n sales: t.sales_percent ? t.price - ((t.price * t.sales_percent) / 100) : t.sales_price,\n }\n })\n },\n error: (res) => {\n (res.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code]);\n });\n }\n })\n }\n\n private initUtm() {\n const utm_source = this.activatedRoute.snapshot.queryParams['utm_source'];\n if (utm_source) {\n this.router.navigate([], {\n queryParams: {\n 'utm_source': null,\n },\n queryParamsHandling: 'merge'\n });\n localStorage.setItem('utm_source', utm_source);\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n goToLogin() {\n this.router.navigate(['/login']);\n }\n\n goToRegister() {\n this.router.navigate(['/register']);\n }\n\n originalInstagramImage = '/assets/svgs/social/instagram_unactive.svg';\n hoverInstagramImage = '/assets/svgs/social/instagram_active.svg';\n\n originalFacebookImage = '/assets/svgs/social/facebook_unactive.svg';\n hoverFacebookImage = '/assets/svgs/social/facebook_active.svg';\n\n originalLinkedinImage = '/assets/svgs/social/linkedin_unactive.svg';\n hoverLinkedinImage = '/assets/svgs/social/linkedin_active.svg';\n\n changeImage(event: MouseEvent, url: string) {\n const imageElement = (event.target as HTMLImageElement);\n imageElement.src = url;\n }\n\n restoreImage(event: MouseEvent, url: string) {\n const imageElement = (event.target as HTMLImageElement);\n imageElement.src = url;\n }\n\n parseName(name: string, keys: any): string {\n return name\n .replace('{olx}', keys['olx'])\n .replace('{tg}', keys['telegram']);\n }\n\n // @HostListener('window:scroll', ['$event'])\n // onScroll(): void {\n // const currentScrollPosition = window.pageYOffset;\n // this.isHeaderHidden = currentScrollPosition > this.lastScrollPosition;\n // this.lastScrollPosition = currentScrollPosition;\n // }\n}\n","
    \n
    \n
    \n
    \n \"JECRM\n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n

    \n Розкрийте потенціал OLX з {{NAME_PROJECT}}\n

    \n

    \n Почніть заробляти власні кошти там, де раніше витрачали.\n

    \n
    \n
    \n \n \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n

    Оптимізуйте спілкування через всі OLX акаунти

    \n

    Наш зручний інтерфейс дозволяє вам однаково легко керувати і взаємодіяти з усіма вашими підключеними акаунтами,\n надаючи вам центральний доступ до всіх ваших комунікаційних потоків і інших ресурсів.

    \n

    Це зробить вашу роботу більш ефективною та продуктивною, освіжить вашу комунікацію та відкриє нові можливості\n для\n співпраці та зв'язку з вашими обліковими записами.

    \n
    \n
    \n \"Channels\n
    \n
    \n\n
    \n
    \n \"Chats\n
    \n
    \n

    {{'messages_from_all_accounts_in_a_convenient_interface' | translate}}

    \n

    {{'communicate_with_your_customers_easily_and_efficiently._Our_service_combines_all_your_OLX_accounts_in_one_convenient_interface' | translate}}

    \n

    {{'now_you_can' | translate}}.

    \n
    \n
    \n\n
    \n
    \n
    \n

    \n {{'introducing_the_PowerOLX_app' | translate}}\n

    \n

    \n {{'powerOLX_is_a_JECRM_tool_that_allows_you_to_automate_the_work_with_ads_on_OLX,_activate_your_ads_instantly_or_on_a_schedule._This_allows_you_to_reach_a_larger_audience_and_get_more_sales'| translate}}.\n

    \n \n {{'we_invite_you_to_test_PowerOLX_in_the_Applications_section_JECRM_System_Store' | translate}}.\n \n
    \n
    \n \"PowerOLX\"\n
    \n
    \n
    \n\n
    \n
    \n
    \n

    \n {{'more_about_the_functionality' | translate}}\n

    \n

    \n {{'learn_more_about_our_products_and_how_they_work_in_our_short_video' | translate}}.\n {{'see_how_our_solutions_can_make_your_life_and_business_easier' | translate}}.\n

    \n \n {{'enjoy_the_video'| translate}}!\n \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n

    {{'we_offer_you_the_following_pricing_plans'| translate}}:

    \n
    \n
    \n
    \n

    {{tariff.title}}

    \n

    \n \n {{parseCurrency(tariff.price, 'USD')}}\n \n

    \n
    \n
    \n

    \n \n \n add\n \n \n \n \n \n check\n \n \n \n \n \n close\n \n \n {{parseName(additional.name, tariff.keys)}}\n

    \n
    \n
    \n
    \n
    \n
    \n

    {{'by_choosing_us_you_get' | translate}}:

    \n
    \n
    \n \"Advantage\"\n

    {{advantage.description}}

    \n
    \n
    \n
    \n
    \n
    \n
    \n \"Users\n
    \n
    \n

    ❤️ Just your Electronic system Customer Relations Management ❤️

    \n

    {{'we_care_about_our_customers' | translate}}

    \n

    {{'our_primary_goal_is_your_complete_satisfaction._We_dedicate_many_hours_and_effort_to_provide_you_with_the_best_service_and_provide_you_with_solutions_that_truly_meet_your_needs'| translate}}.

    \n

    {{'you_are_our_main_goal_and_we_are_proud_of_our_ability_to_make_your_life_better' | translate}}.

    \n
    \n \n
    \n
    \n

    {{stat.count}}

    \n

    {{stat.name}}

    \n
    \n
    \n \n
    \n
    \n
    \n \"JECRM\n
    \n \n
    \n
    \n {{contact.icon}}\n {{contact.text}}\n {{contact.text}}\n
    \n
    \n
    \n
    \n

    Copyright© {{currentYear}} {{NAME_PROJECT}}. Created by Jestos

    \n
    \n
    \n
    \n","import { Component, Inject } from '@angular/core';\nimport { FormControl, Validators } from '@angular/forms';\nimport { MAT_DIALOG_DATA } from '@angular/material/dialog';\nimport { NotificationService } from '../../../services/notifications.service';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../../enum/constants';\nimport { IError } from '../../../interfaces';\nimport { AuthService } from '../../../services/auth.service';\n\n@Component({\n selector: 'app-reset-password',\n templateUrl: './reset-password.component.html',\n styleUrls: ['./reset-password.component.scss'],\n})\nexport class ResetPasswordComponent {\n public emailControl: FormControl;\n public isSending: boolean = false;\n\n constructor(\n @Inject(MAT_DIALOG_DATA) public data: any,\n private readonly authService: AuthService,\n private readonly notificationService: NotificationService,\n ) {\n this.emailControl = new FormControl(data.data.email || '', [Validators.required, Validators.email]);\n }\n\n public onSubmit(): void {\n this.isSending = true;\n\n if (this.emailControl.invalid) {\n this.emailControl.markAsTouched();\n this.isSending = false;\n return;\n }\n\n this.authService.resetPassword(this.emailControl.value).subscribe({\n next: () => {\n this.isSending = false;\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.sent_reset_password);\n },\n error: (e) => {\n this.isSending = false;\n console.log(e);\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n}\n","
    \n
    \n \n \n \n \n {{ 'please_enter_email' | translate }}\n \n \n {{ 'please_enter_valid_email' | translate }}\n \n \n
    \n \n {{ 'reset_password.btn' | translate }}\n \n
    \n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { Subject, takeUntil } from 'rxjs';\nimport { AuthService } from '../../services/auth.service';\nimport { KeycloakService } from 'keycloak-angular';\nimport { BaseService } from '../../services/base.service';\nimport { environment } from '../../../environments/environment';\nimport { LoginForm } from './login.interface';\nimport { IError } from '../../interfaces';\nimport { DEFAULT_ERROR, ERROR_MESSAGES, languages } from '../../enum/constants';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ModalService } from '../../services/modal.service';\nimport { ResetPasswordComponent } from '../../components/modals/reset-password/reset-password.component';\nimport { trimValidator } from '../../helpers';\n\n\n@Component({\n selector: 'app-login',\n templateUrl: './login.component.html',\n styleUrls: ['./login.component.scss'],\n})\nexport class LoginComponent implements OnInit, OnDestroy {\n private readonly destroy$ = new Subject();\n\n constructor(\n private readonly router: Router,\n private readonly authService: AuthService,\n private readonly keycloakService: KeycloakService,\n private readonly notificationService: NotificationService,\n private readonly modalService: ModalService,\n public readonly baseService: BaseService,\n ) {\n }\n\n public homeUrl: string = `${window.location.protocol}//${environment.web_url}`;\n public loginForm!: FormGroup;\n public loading: boolean = true;\n public isLogging: boolean = false;\n\n public ngOnInit() {\n this.initForm();\n this.authService.initUtm();\n if (!this.keycloakService.isLoggedIn()) {\n this.loading = false;\n return;\n }\n\n this.baseService.getProfile()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (response) => {\n response?.phone ? this.router.navigate(['/companies']) : this.router.navigate(['/additional']);\n },\n error: (error) => {\n console.log(error);\n this.loading = false;\n },\n });\n }\n\n public ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n public onSubmit() {\n this.isLogging = true;\n\n if (this.loginForm.invalid) {\n this.loginForm.markAllAsTouched();\n this.isLogging = false;\n return;\n }\n\n const payload = this.loginForm.getRawValue();\n\n this.authService.login(payload)\n .subscribe({\n next: async (res) => {\n const token = res.access_token;\n const refreshToken = res.refresh_token;\n const keycloakOptions = {\n config: { ...environment.keycloak },\n initOptions: {\n checkLoginIframe: false,\n token: token,\n refreshToken: refreshToken,\n },\n };\n try {\n await this.keycloakService.init(keycloakOptions);\n\n localStorage.setItem('token', token);\n localStorage.setItem('refreshToken', refreshToken);\n if (this.baseService.getCompany()) {\n this.router.navigate(['/crm']);\n } else {\n this.router.navigate(['/companies']);\n }\n } catch {\n this.router.navigate(['/login']);\n }\n this.isLogging = false;\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n if (err.code === 'NOT_VERIFIED_EMAIL') {\n this.authService.openVerifyEmailModal(payload.email);\n return;\n }\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n this.isLogging = false;\n },\n });\n }\n\n public onResetPassword() {\n const dialogData = {\n title: 'reset_password.title',\n translate: true,\n component: ResetPasswordComponent,\n data: {\n email: this.loginForm.controls.email.getRawValue(),\n },\n };\n\n this.modalService.openModal(dialogData);\n }\n\n public onRegister() {\n this.router.navigate(['/register']);\n }\n\n private initForm() {\n this.loginForm = new FormGroup({\n email: new FormControl('', [Validators.required, trimValidator]),\n password: new FormControl('', [Validators.required, trimValidator]),\n });\n }\n}\n","
    \n
    \n JECRM\n
    \n
    \n
    \n

    {{ 'sign_in_account' | translate }}

    \n
    \n
    \n
    \n
    \n \n \n {{ 'please_enter_email' | translate }}\n \n
    \n
    \n \n \n {{ 'please_enter_password' | translate }}\n \n
    \n \n {{ 'forgot_password' | translate }}\n \n \n {{ 'sign_in' | translate }}\n \n
    \n
    \n
    \n {{ 'new_user' | translate }}\n {{ 'sign_up' | translate }}\n
    \n
    \n
    \n
    \n
    \n\n
    \n
    \n \n
    \n
    \n\n","import { isPlatformServer, CommonModule } from '@angular/common';\nimport * as i0 from '@angular/core';\nimport { output, inject, ElementRef, NgZone, PLATFORM_ID, forwardRef, Component, Input, Renderer2, Directive, NgModule } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/**\n * Slick component\n */\nconst _c0 = [\"*\"];\nlet SlickCarouselComponent = /*#__PURE__*/(() => {\n class SlickCarouselComponent {\n config;\n afterChange = output();\n beforeChange = output();\n breakpoint = output();\n destroy = output();\n init = output();\n $instance;\n // access from parent component can be a problem with change detection timing. Please use afterChange output\n currentIndex = 0;\n slides = [];\n initialized = false;\n _removedSlides = [];\n _addedSlides = [];\n el = inject(ElementRef);\n zone = inject(NgZone);\n isServer = isPlatformServer(inject(PLATFORM_ID));\n /**\n * On component destroy\n */\n ngOnDestroy() {\n this.unslick();\n }\n ngAfterViewInit() {\n this.ngAfterViewChecked();\n }\n /**\n * On component view checked\n */\n ngAfterViewChecked() {\n if (this.isServer) {\n return;\n }\n if (this._addedSlides.length > 0 || this._removedSlides.length > 0) {\n const nextSlidesLength = this.slides.length - this._removedSlides.length + this._addedSlides.length;\n if (!this.initialized) {\n if (nextSlidesLength > 0) {\n this.initSlick();\n }\n // if nextSlidesLength is zere, do nothing\n } else if (nextSlidesLength === 0) {\n // unslick case\n this.unslick();\n } else {\n this._addedSlides.forEach(slickItem => {\n this.slides.push(slickItem);\n this.$instance.slick('slickAdd', slickItem.el.nativeElement);\n });\n this._addedSlides = [];\n this._removedSlides.forEach(slickItem => {\n const idx = this.slides.indexOf(slickItem);\n this.slides = this.slides.filter(s => s !== slickItem);\n this.$instance.slick('slickRemove', idx);\n });\n this._removedSlides = [];\n }\n }\n }\n /**\n * init slick\n */\n initSlick() {\n this.slides = this._addedSlides;\n this._addedSlides = [];\n this._removedSlides = [];\n this.$instance = jQuery(this.el.nativeElement);\n this.$instance.on('init', (event, slick) => {\n this.zone.run(() => {\n this.init.emit({\n event,\n slick\n });\n });\n });\n this.$instance.slick(this.config);\n this.zone.run(() => {\n this.initialized = true;\n this.currentIndex = this.config?.initialSlide || 0;\n });\n this.$instance.on('afterChange', (event, slick, currentSlide) => {\n this.zone.run(() => {\n this.afterChange.emit({\n event,\n slick,\n currentSlide,\n first: currentSlide === 0,\n last: slick.$slides.length === currentSlide + slick.options.slidesToScroll\n });\n this.currentIndex = currentSlide;\n });\n });\n this.$instance.on('beforeChange', (event, slick, currentSlide, nextSlide) => {\n this.zone.run(() => {\n this.beforeChange.emit({\n event,\n slick,\n currentSlide,\n nextSlide\n });\n this.currentIndex = nextSlide;\n });\n });\n this.$instance.on('breakpoint', (event, slick, breakpoint) => {\n this.zone.run(() => {\n this.breakpoint.emit({\n event,\n slick,\n breakpoint\n });\n });\n });\n this.$instance.on('destroy', (event, slick) => {\n this.zone.run(() => {\n this.destroy.emit({\n event,\n slick\n });\n this.initialized = false;\n });\n });\n }\n addSlide(slickItem) {\n this._addedSlides.push(slickItem);\n }\n removeSlide(slickItem) {\n this._removedSlides.push(slickItem);\n }\n /**\n * Slick Method\n */\n slickGoTo(index) {\n this.$instance.slick('slickGoTo', index);\n }\n slickNext() {\n this.$instance.slick('slickNext');\n }\n slickPrev() {\n this.$instance.slick('slickPrev');\n }\n slickPause() {\n this.$instance.slick('slickPause');\n }\n slickPlay() {\n this.$instance.slick('slickPlay');\n }\n unslick() {\n if (this.$instance) {\n this.$instance.slick('unslick');\n this.$instance = undefined;\n }\n this.initialized = false;\n }\n ngOnChanges(changes) {\n if (this.initialized) {\n const config = changes['config'];\n if (config.previousValue !== config.currentValue && config.currentValue !== undefined) {\n const refresh = config.currentValue['refresh'];\n const newOptions = Object.assign({}, config.currentValue);\n delete newOptions['refresh'];\n this.$instance.slick('slickSetOption', newOptions, refresh);\n }\n }\n }\n /** @nocollapse */\n static ɵfac = function SlickCarouselComponent_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || SlickCarouselComponent)();\n };\n /** @nocollapse */\n static ɵcmp = /* @__PURE__ */i0.ɵɵdefineComponent({\n type: SlickCarouselComponent,\n selectors: [[\"ngx-slick-carousel\"]],\n inputs: {\n config: \"config\"\n },\n outputs: {\n afterChange: \"afterChange\",\n beforeChange: \"beforeChange\",\n breakpoint: \"breakpoint\",\n destroy: \"destroy\",\n init: \"init\"\n },\n exportAs: [\"slick-carousel\"],\n features: [i0.ɵɵProvidersFeature([{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SlickCarouselComponent),\n multi: true\n }]), i0.ɵɵNgOnChangesFeature],\n ngContentSelectors: _c0,\n decls: 1,\n vars: 0,\n template: function SlickCarouselComponent_Template(rf, ctx) {\n if (rf & 1) {\n i0.ɵɵprojectionDef();\n i0.ɵɵprojection(0);\n }\n },\n encapsulation: 2\n });\n }\n return SlickCarouselComponent;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet SlickItemDirective = /*#__PURE__*/(() => {\n class SlickItemDirective {\n carousel = inject(SlickCarouselComponent, {\n host: true\n });\n renderer = inject(Renderer2);\n el = inject(ElementRef);\n isServer = isPlatformServer(inject(PLATFORM_ID));\n ngOnInit() {\n this.carousel.addSlide(this);\n if (this.isServer && this.carousel.slides.length > 0) {\n // Do not show other slides in server side rendering (broken ui can be affacted to Core Web Vitals)\n this.renderer.setStyle(this.el, 'display', 'none');\n }\n }\n ngOnDestroy() {\n this.carousel.removeSlide(this);\n }\n /** @nocollapse */\n static ɵfac = function SlickItemDirective_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || SlickItemDirective)();\n };\n /** @nocollapse */\n static ɵdir = /* @__PURE__ */i0.ɵɵdefineDirective({\n type: SlickItemDirective,\n selectors: [[\"\", \"ngxSlickItem\", \"\"]]\n });\n }\n return SlickItemDirective;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\nlet SlickCarouselModule = /*#__PURE__*/(() => {\n class SlickCarouselModule {\n /** @nocollapse */static ɵfac = function SlickCarouselModule_Factory(__ngFactoryType__) {\n return new (__ngFactoryType__ || SlickCarouselModule)();\n };\n /** @nocollapse */\n static ɵmod = /* @__PURE__ */i0.ɵɵdefineNgModule({\n type: SlickCarouselModule\n });\n /** @nocollapse */\n static ɵinj = /* @__PURE__ */i0.ɵɵdefineInjector({\n imports: [CommonModule]\n });\n }\n return SlickCarouselModule;\n})();\n(() => {\n (typeof ngDevMode === \"undefined\" || ngDevMode) && void 0;\n})();\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { SlickCarouselComponent, SlickCarouselModule, SlickItemDirective };\n","import { Component, HostListener, OnInit } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { FormGroup } from '@angular/forms';\nimport { Subject } from 'rxjs';\nimport { AuthService } from '../../services/auth.service';\nimport { KeycloakService } from 'keycloak-angular';\nimport { NotificationService } from '../../services/notifications.service';\nimport { ICompany } from '../../model/company.model';\nimport { BaseService } from '../../services/base.service';\nimport { ProfileService } from '../../services/profile.service';\nimport { environment } from '../../../environments/environment';\nimport { NAME_PROJECT } from '../../enum/constants';\n\n\n@Component({\n templateUrl: './companies.component.html',\n styleUrls: ['./companies.component.scss'],\n})\nexport class CompaniesComponent implements OnInit {\n private readonly destroy$: Subject = new Subject();\n protected readonly environment = environment;\n\n slideConfig={\n slidesToShow: 1,\n slidesToScroll: 1,\n dots: true,\n autoplay: true,\n autoplaySpeed: 3000,\n arrows: false,\n }\n\n images = [\n {img: \"/assets/images/special/slide2.jpg\", heading: \"companies_info.you_are_just_a_step_away_from\", description: \"companies_info.slide2\"},\n {img: \"/assets/images/special/slide3.jpg\", heading: \"companies_info.you_are_just_a_few_minutes_from\", description: \"companies_info.slide3\"},\n {img: \"/assets/images/special/slide4.jpg\", heading: \"companies_info.you_are_just_a_step_away_from\", description: \"companies_info.slide4\"},\n {img: \"/assets/images/special/slide5.jpg\", heading: \"companies_info.you_are_just_a_moment_away_from\", description: \"companies_info.slide5\"},\n {img: \"/assets/images/special/slide1.jpg\", heading: \"companies_info.you_are_just_a_moment_away_from\", description: \"companies_info.slide1\"}\n ]\n\n constructor(\n private readonly router: Router,\n public readonly authService: AuthService,\n private readonly keycloakService: KeycloakService,\n private readonly profileService: ProfileService,\n private readonly notificationService: NotificationService,\n public readonly baseService: BaseService,\n ) { }\n\n public companies: ICompany[] = [];\n public subdomain!: string;\n public isLoader: boolean = false;\n\n ngOnInit() {\n this.profileService.checkPhone();\n this.initCompanies();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private initCompanies() {\n this.isLoader = true;\n this.authService.getCompanies()\n .subscribe({\n next: (res) => {\n this.companies = res.data;\n this.isLoader = false;\n },\n error: () => {\n this.isLoader = false;\n },\n });\n }\n\n createCompany() {\n this.isLoader = true;\n this.authService.createCompany()\n .subscribe({\n next: () => {\n this.initCompanies();\n },\n error: (res) => {\n this.isLoader = false;\n console.log(res.error);\n (res.error?.errors || []).forEach((err: any) => {\n this.notificationService.showError('', err.message);\n });\n },\n });\n }\n\n onSelectCompany(subdomain: string) {\n this.subdomain = subdomain;\n\n const token = this.keycloakService.getKeycloakInstance().token;\n const refreshToken = this.keycloakService.getKeycloakInstance().refreshToken;\n const protocol = window.location.protocol;\n let host;\n\n if (window.location.host.includes('auth')) {\n host = window.location.host.replace('auth', this.subdomain);\n } else if (environment.default_schema) {\n host = window.location.host;\n } else {\n switch (environment.env) {\n case 'local':\n host = window.location.host.replace('localhost', `${this.subdomain}.localhost`);\n break;\n case 'dev':\n host = window.location.host.replace('dev', `${this.subdomain}.dev`);\n break;\n case 'prod':\n host = window.location.host.replace('jecrm', `${this.subdomain}.jecrm`);\n break;\n }\n }\n\n window.location.href = `${protocol}//${host}/sso/${token}/${refreshToken}`;\n }\n\n\n logout() {\n this.authService.logout();\n }\n}\n","
    \n
    \n
    \n
    \n \"logo-of-JECRM\"\n
    \n
    \n

    {{ 'choose_company'|translate }}

    \n
    \n\n
    \n \n
    \n \n
    \n
    \n \n
    \n {{company.name}}\n {{ 'owner' | translate }}\n {{ 'manager' | translate }}\n\n \n\n
    \n\n \n
    \n
    \n
    \n
    \n\n\n
    \n \n
    \n
    \n
    \n
    \n\n \n
    \n
    \n \"Slide-Image\"\n
    \n
    \n

    {{ image.heading | translate }}

    \n

    {{ image.description | translate }}

    \n
    \n
    \n
    \n
    \n
    \n\n
    \n\n
    \n","import { Component, OnInit } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { FormControl, FormGroup, Validators } from '@angular/forms';\nimport { combineLatest, Subject, take } from 'rxjs';\nimport { AuthService } from '../../services/auth.service';\nimport { KeycloakService } from 'keycloak-angular';\nimport { environment } from '../../../environments/environment';\nimport { BaseService } from '../../services/base.service';\nimport { NotificationService } from '../../services/notifications.service';\nimport { languages } from '../../enum/constants';\n\n@Component({\n templateUrl: './single-auth.component.html',\n styleUrls: ['./single-auth.component.scss']\n})\nexport class SingleAuthComponent implements OnInit {\n private readonly destroy$: Subject = new Subject();\n\n constructor(\n private readonly router: Router,\n private readonly activatedRoute: ActivatedRoute,\n private readonly authService: AuthService,\n private readonly keycloakService: KeycloakService,\n private readonly baseService: BaseService,\n private readonly notificationService: NotificationService,\n ) { }\n\n async ngOnInit() {\n const token = this.activatedRoute.snapshot.params['token'];\n const refreshToken = this.activatedRoute.snapshot.params['r_token'];\n const keycloakOptions = {\n config: { ...environment.keycloak },\n initOptions: {\n checkLoginIframe: false,\n token: token,\n refreshToken: refreshToken,\n }\n };\n try {\n const isInitialized = await this.keycloakService.init(keycloakOptions);\n\n if (!isInitialized) {\n // this.router.navigate(['/crm']);\n return;\n }\n\n const token = this.keycloakService.getKeycloakInstance().token as string;\n const refreshToken = this.keycloakService.getKeycloakInstance().refreshToken as string;\n\n localStorage.setItem('token', token);\n localStorage.setItem('refreshToken', refreshToken);\n this.router.navigate(['/crm']);\n } catch (err) {\n this.router.navigate(['/login']);\n }\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n click() {\n this.router.navigate(['/login']);\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_2);\n }\n}\n","export const policySections = [\n { title: 'Загальні положення',\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'\n },\n { title: 'Інформація, яку ми збираємо',\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'\n },\n { title: 'Використання інформації',\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'},\n { title: 'Розкриття інформації',\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'},\n { title: 'Зміни до політики конфіденційності',\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'},\n { title: \"Зв'яжіться з нами\",\n text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga fugiat nobis officiis. Aliquam impedit iste, minima nesciunt quos sequi vero!'},\n\n];\n","import {Component, ElementRef, OnInit} from '@angular/core';\nimport {policySections} from './policy.component.interface';\nimport {Location} from '@angular/common';\n\n@Component({\n selector: 'app-policy',\n templateUrl: './policy.component.html',\n styleUrls: ['./policy.component.scss']\n})\nexport class PolicyComponent {\n\n protected readonly policySections = policySections;\n toggle: boolean = false;\n\n constructor(\n private location: Location\n ) {\n }\n\n sidebarToggle() {\n setTimeout(() => {\n this.toggle = !this.toggle;\n }, 25);\n }\n\n goBack(): void {\n this.location.back();\n }\n}\n","\n \n
    \n
    \n
    \n \n
    \n
    \n \"logo-icon\"\n
    \n

    Центр підтримки клієнтів

    \n
    \n
    \n

    {{'private_policy'| translate}}

    \n
    \n
    {{ section.title }}
    \n

    {{ section.text }}

    \n
    \n
    \n
    \n

    {{'policy_file_cookie'| translate}}

    \n
    \n
    {{ section.title }}
    \n

    {{ section.text }}

    \n
    \n
    \n
    \n

    Умови використання

    \n
    \n
    {{ section.title }}
    \n

    {{ section.text }}

    \n
    \n
    \n \n
    \n
    \n \n \n \n
    \n\n","import { ColumnSortType, TableColumnTypes } from '../../../../model/table.model';\n\n\nexport const tableHeaders = [\n {\n value: 'name',\n sort: ColumnSortType.NONE,\n name: 'Назва',\n type: TableColumnTypes.TEXT,\n className: 'table-header',\n },\n {\n value: 'olx_status',\n sort: ColumnSortType.NONE,\n name: 'Olx статус',\n type: TableColumnTypes.OLX_STATUS,\n className: 'table-header',\n },\n {\n value: 'created_at',\n sort: ColumnSortType.NONE,\n name: 'Створений',\n type: TableColumnTypes.DATE,\n className: 'table-header',\n },\n // {\n // value: 'updated_at',\n // sort: ColumnSortType.NONE,\n // name: 'Обновлений',\n // type: TableColumnTypes.DATE,\n // className: 'table-header',\n // },\n {\n value: 'action',\n sort: ColumnSortType.NONE,\n name: '',\n type: TableColumnTypes.ACTION,\n className: 'three-dot',\n actions:\n [\n {icon: 'edit', label: 'edit', value: 'EDIT'},\n // { icon: 'delete', label: 'Видалити', value: 'DELETE' },\n ],\n translate: true,\n },\n];\n\nexport interface Application {\n title: string,\n name: string,\n brief_description: string,\n bg_color: string,\n text_color: string,\n type: string\n}\n\nexport enum APPLICATION_TYPE {\n OLX = 'OLX_TOOL',\n CHECKBOX = 'CHECKBOX',\n LOGISTIC = 'LOGISTIC',\n RINGOSTAT = 'RINGOSTAT',\n TELEGRAM_BOT = 'TELEGRAM_BOT',\n GEN_AI = 'GEN_AI',\n}\n","import { Component, OnDestroy, OnInit } from '@angular/core';\nimport { Subject, switchMap, takeUntil } from 'rxjs';\nimport { RightSidebarService } from '../../../../services/right-sidebar.service';\nimport { NotificationService } from '../../../../services/notifications.service';\nimport { BaseService } from '../../../../services/base.service';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Application, APPLICATION_TYPE } from './market.interface';\nimport { MarketService } from '../../../../services/market.service';\nimport { parseCurrency } from '../../../../helpers';\nimport { PropsComponent } from '../../../../modals/props/props.component';\nimport { ModalService } from '../../../../services/modal.service';\nimport { APPLICATION_STATUS, DEFAULT_ERROR, ERROR_MESSAGES, languages, PERMISSIONS } from '../../../../enum/constants';\nimport { IError } from \"../../../../interfaces\";\nimport { FormControl, FormGroup, Validators } from \"@angular/forms\";\nimport moment from 'moment';\n\n\ninterface IRingostatControlForm {\n api_key: FormControl,\n}\n\n@Component({\n selector: 'app-marketplace',\n templateUrl: './market.component.html',\n styleUrls: ['./market.component.scss'],\n})\nexport class MarketComponent implements OnInit, OnDestroy {\n // Destroy relative\n public form!: FormGroup;\n public isFormSubmitted: boolean = false;\n private readonly destroy$: Subject = new Subject();\n private readonly reasonQuery: string = this.activatedRoute.snapshot.queryParams['reason'];\n\n\n public isLoader: boolean = false;\n public applications: any[] = [];\n\n constructor(\n private readonly rightSidebarService: RightSidebarService,\n private readonly notificationService: NotificationService,\n public readonly baseService: BaseService,\n public readonly marketService: MarketService,\n public readonly modalService: ModalService,\n private readonly router: Router,\n private readonly activatedRoute: ActivatedRoute,\n ) {\n }\n\n ngOnInit() {\n this.form = new FormGroup({\n api_key: new FormControl('', Validators.required),\n })\n this.baseService.checkPermission(PERMISSIONS.APPS_ACCESS);\n\n this.initQuery();\n this.init();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next(null);\n this.destroy$.complete();\n }\n\n private init() {\n this.isLoader = true;\n this.marketService.getApplications()\n .pipe(takeUntil(this.destroy$))\n .subscribe({\n next: (res) => {\n const items = res;\n items.forEach((v: any) => v.is_loader = false);\n this.applications = [...items];\n this.isLoader = false;\n },\n error: (e) => {\n this.isLoader = false;\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n },\n });\n }\n\n onClick(event: string, is_install=true) {\n if (!is_install) return;\n if (event === APPLICATION_TYPE.OLX) this.router.navigate(['/crm/application/olx']);\n if (event === APPLICATION_TYPE.RINGOSTAT) this.router.navigate(['/crm/application/ringostat']);\n if (event === APPLICATION_TYPE.TELEGRAM_BOT) this.router.navigate(['/crm/channel']);\n if (event === APPLICATION_TYPE.CHECKBOX) this.rightSidebarService.open('integration-checkbox', {medium: true}).subscribe();\n if (event === APPLICATION_TYPE.LOGISTIC) this.router.navigate(['/crm/logistic/settings']);\n if (event === APPLICATION_TYPE.GEN_AI) this.rightSidebarService.open('integration-ai', {}).subscribe();\n }\n\n onDownload = (item: any, e: any) => {\n item.is_loader = true;\n this.marketService.install(item.id)\n .pipe(takeUntil(this.destroy$))\n .pipe(switchMap(() => this.baseService.getCompanyInfo()))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess(languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.success, languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_installed);\n this.init();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n this.notificationService.showError(languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.error, languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.downloaded);\n this.init();\n },\n })\n // setTimeout(() => {\n // item.is_loader = false;\n // item.is_install = true;\n // this.notificationService.showSuccess('Success!', 'Downloaded!');\n // }, 4000);\n }\n\n onOpen() {\n\n }\n\n onDelete(item: any) {\n this.marketService.uninstall(item.id)\n .pipe(takeUntil(this.destroy$))\n .pipe(switchMap(() => this.baseService.getCompanyInfo()))\n .subscribe({\n next: () => {\n this.notificationService.showSuccess('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.success.successfully_deleted);\n this.init();\n },\n error: (e) => {\n (e.error?.errors || []).forEach((err: IError) => {\n this.notificationService.showError('', ERROR_MESSAGES[err.code] || ERROR_MESSAGES[DEFAULT_ERROR]);\n });\n this.notificationService.showError('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.error.something_went_wrong_1);\n this.init();\n },\n })\n }\n\n onConfigure(app: any) {\n if (app.key === 'TELEGRAM_BOT'){\n const tg = this.rightSidebarService.open('integration-telegram-bot', {});\n } else {\n const res = this.rightSidebarService.open('integration-ringostat', {});\n }\n }\n\n protected readonly parseCurrency = parseCurrency;\n\n onPay(app: any) {\n this.router.navigate(['/crm/payment'])\n }\n\n protected readonly APPLICATION_STATUS = APPLICATION_STATUS;\n\n private initQuery() {\n if (this.reasonQuery === 'NOT_INSTALL') {\n this.notificationService.showWarning('', languages[localStorage.getItem('language') || 'ua'].notifications_texts.warn.application_not_installed)\n this.router.navigate([], { queryParams: {} });\n } else {\n // this.tabs.forEach(v => v.active = false);\n // this.tabs[0].active = true;\n // this.onChangeTab(this.tabs[0]);\n }\n }\n\n protected readonly moment = moment;\n}\n","
    \n

    {{'store' | translate}}

    \n\n
    \n
    \n \n
    \n
    \n
    \n\n\n\n
    \n {{'test_regime' | translate}}\n
    \n \n {{ app.title }}\n
    \n
    \n
    \n {{ app.json.name }}\n
    \n
    \n {{ app.description }}\n
    \n
    \n
    \n \n \n
    \n {{ 'install' | translate }}\n download\n
    \n\n
    \n {{'buy'| translate}}\n {{parseCurrency(app.price, 'USD')}}\n\n
    \n
    \n {{ 'wait_for_confirmation' | translate }}\n
    \n
    \n
    \n
    \n settings\n
    \n
    \n delete_outline\n
    \n
    \n {{ 'open' | translate }}\n \n
    \n
    \n
    \n \n
    \n {{ 'installation' | translate }}\n\n \n
    \n
    \n \n
    \n","class TranslateHttpLoader {\n http;\n prefix;\n suffix;\n constructor(http, prefix = \"/assets/i18n/\", suffix = \".json\") {\n this.http = http;\n this.prefix = prefix;\n this.suffix = suffix;\n }\n /**\n * Gets the translations from the server\n */\n getTranslation(lang) {\n return this.http.get(`${this.prefix}${lang}${this.suffix}`);\n }\n}\n\n/**\n * Generated bundle index. Do not edit.\n */\n\nexport { TranslateHttpLoader };\n","/**\r\n * ============================================================================\r\n * MAIN CLASS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Represents a relative value (percent).\r\n *\r\n * The Percent object, can be instantiated using two ways:\r\n *\r\n * * Via `new Percent(X)`.\r\n * * Via `am5.percent(X)`.\r\n *\r\n * You can also use shortcut functions for `0%`, `50%`, and `100%`:\r\n * * `am5.p0`\r\n * * `am5.p50`\r\n * * `am5.p100`\r\n */\nexport class Percent {\n /**\r\n * Constructor.\r\n *\r\n * @param percent Percent value\r\n */\n constructor(percent) {\n /**\r\n * Value in percent.\r\n */\n Object.defineProperty(this, \"_value\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._value = percent;\n }\n /**\r\n * Relative value.\r\n *\r\n * E.g. 100% is 1, 50% is 0.5, etc.\r\n *\r\n * This is useful to apply transformations to other values. E.g.:\r\n *\r\n * ```TypeScript\r\n * let value = 256;\r\n * let percent = new am5.p50;\r\n * console.log(value * percent.value); // outputs 128\r\n * ```\r\n * ```JavaScript\r\n * var value = 256;\r\n * var percent = new am5.p50;\r\n * console.log(value * percent.value); // outputs 128\r\n * ```\r\n *\r\n * Alternatively, you can use `am5.percent()` helper function:\r\n *\r\n * ```TypeScript\r\n * let value = 256;\r\n * let percent = am5.p50;\r\n * console.log(value * percent.value); // outputs 128\r\n * ```\r\n * ```JavaScript\r\n * var value = 256;\r\n * var percent = am5.p50;\r\n * console.log(value * percent.value); // outputs 128\r\n * ```\r\n *\r\n * @readonly\r\n * @return Relative value\r\n */\n get value() {\n return this._value / 100;\n }\n /**\r\n * Value in percent.\r\n *\r\n * @readonly\r\n * @return Percent\r\n */\n get percent() {\n return this._value;\n }\n toString() {\n return \"\" + this._value + \"%\";\n }\n interpolate(min, max) {\n return min + this.value * (max - min);\n }\n static normalize(percent, min, max) {\n if (percent instanceof Percent) {\n return percent;\n } else {\n if (min === max) {\n return new Percent(0);\n } else {\n return new Percent(Math.min(Math.max((percent - min) * (1 / (max - min)), 0), 1) * 100);\n }\n }\n }\n}\n/**\r\n * Converts numeric percent value to a proper [[Percent]] object.\r\n *\r\n * ```TypeScript\r\n * pieSeries.set(\"radius\", am5.percent(80));\r\n * ```\r\n * ```JavaScript\r\n * pieSeries.set(\"radius\", am5.percent(80));\r\n * ```\r\n *\r\n * @param value Percent\r\n * @return Percent object\r\n */\nexport function percent(value) {\n return new Percent(value);\n}\n/**\r\n * A shortcut function to `am5.percent(0)`.\r\n */\nexport const p0 = percent(0);\n/**\r\n * A shortcut function to `am5.percent(100)`.\r\n */\nexport const p100 = percent(100);\n/**\r\n * A shortcut function to `am5.percent(50)`.\r\n */\nexport const p50 = percent(50);\n/**\r\n * Checks if value is a [[Percent]] object.\r\n *\r\n * @ignore Exclude from docs\r\n * @param value Input value\r\n * @return Is percent?\r\n */\nexport function isPercent(value) {\n return value instanceof Percent;\n}\n","/**\r\n * A collection of utility functions for various type checks and conversion\r\n * @hidden\r\n */\n/**\r\n * ============================================================================\r\n * TYPE CHECK\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Returns `true` if value is not a number (NaN).\r\n *\r\n * @param value Input value\r\n * @return Is NaN?\r\n */\nexport function isNaN(value) {\n return Number(value) !== value;\n}\n/**\r\n * Returns a type of the value.\r\n *\r\n * @param value Input value\r\n * @return Type of the value\r\n * @ignore\r\n */\nexport function getType(value) {\n return {}.toString.call(value);\n}\n/**\r\n * Asserts that the condition is true.\r\n *\r\n * @param condition Condition to check\r\n * @param message Message to display in the error\r\n * @ignore\r\n */\nexport function assert(condition, message = \"Assertion failed\") {\n if (!condition) {\n throw new Error(message);\n }\n}\n/**\r\n * ============================================================================\r\n * QUICK CONVERSION\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Converts any value into a `number`.\r\n *\r\n * @param value Source value\r\n * @return Number representation of value\r\n */\nexport function toNumber(value) {\n if (value != null && !isNumber(value)) {\n let converted = Number(value);\n if (isNaN(converted) && isString(value) && value != \"\") {\n return toNumber(value.replace(/[^0-9.\\-]+/g, ''));\n }\n return converted;\n }\n return value;\n}\n/**\r\n * Converts anything to Date object.\r\n *\r\n * @param value A value of any type\r\n * @return Date object representing a value\r\n */\nexport function toDate(value) {\n if (isDate(value)) {\n // TODO maybe don't create a new Date ?\n return new Date(value);\n } else if (isNumber(value)) {\n return new Date(value);\n } else {\n // Try converting to number (assuming timestamp)\n let num = Number(value);\n if (!isNumber(num)) {\n return new Date(value);\n } else {\n return new Date(num);\n }\n }\n}\n/**\r\n * Converts numeric value into string. Deals with large or small numbers that\r\n * would otherwise use exponents.\r\n *\r\n * @param value Numeric value\r\n * @return Numeric value as string\r\n */\nexport function numberToString(value) {\n // TODO handle Infinity and -Infinity\n if (isNaN(value)) {\n return \"NaN\";\n }\n if (value === Infinity) {\n return \"Infinity\";\n }\n if (value === -Infinity) {\n return \"-Infinity\";\n }\n // Negative 0\n if (value === 0 && 1 / value === -Infinity) {\n return \"-0\";\n }\n // Preserve negative and deal with absoute values\n let negative = value < 0;\n value = Math.abs(value);\n // TODO test this\n let parsed = /^([0-9]+)(?:\\.([0-9]+))?(?:e[\\+\\-]([0-9]+))?$/.exec(\"\" + value);\n let digits = parsed[1];\n let decimals = parsed[2] || \"\";\n let res;\n // Leave the nummber as it is if it does not use exponents\n if (parsed[3] === undefined) {\n res = decimals === \"\" ? digits : digits + \".\" + decimals;\n } else {\n let exponent = +parsed[3];\n // Deal with decimals\n if (value < 1) {\n let zeros = exponent - 1;\n res = \"0.\" + repeat(\"0\", zeros) + digits + decimals;\n // Deal with integers\n } else {\n let zeros = exponent - decimals.length;\n if (zeros === 0) {\n res = digits + decimals;\n } else if (zeros < 0) {\n res = digits + decimals.slice(0, zeros) + \".\" + decimals.slice(zeros);\n } else {\n res = digits + decimals + repeat(\"0\", zeros);\n }\n }\n }\n return negative ? \"-\" + res : res;\n}\n/**\r\n * Repeats a `string` number of times as set in `amount`.\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Make this faster\r\n * @param string Source string\r\n * @param amount Number of times to repeat string\r\n * @return New string\r\n */\nexport function repeat(string, amount) {\n return new Array(amount + 1).join(string);\n}\n/**\r\n * ============================================================================\r\n * TYPE CHECK\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Checks if parameter is `Date`.\r\n *\r\n * @param value Input value\r\n * @return Is Date?\r\n */\nexport function isDate(value) {\n return getType(value) === \"[object Date]\";\n}\n/**\r\n * Checks if parameter is `string`.\r\n *\r\n * @param value Input value\r\n * @return Is string?\r\n */\nexport function isString(value) {\n return typeof value === \"string\";\n}\n/**\r\n * Checks if parameter is `number`.\r\n *\r\n * @param value Input value\r\n * @return Is number?\r\n */\nexport function isNumber(value) {\n return typeof value === \"number\" && Number(value) == value;\n}\n/**\r\n * Checks if parameter is `object`.\r\n *\r\n * @param value Input value\r\n * @return Is object?\r\n */\nexport function isObject(value) {\n return typeof value === \"object\" && value !== null;\n}\n/**\r\n * Checks if parameter is `Array`.\r\n *\r\n * @param value Input value\r\n * @return Is Array?\r\n */\nexport function isArray(value) {\n return Array.isArray(value);\n}\n/**\r\n * ============================================================================\r\n * STATIC CONSTANTS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * @ignore Exclude from docs\r\n */\nexport const PLACEHOLDER = \"__§§§__\";\n/**\r\n * @ignore Exclude from docs\r\n */\nexport const PLACEHOLDER2 = \"__§§§§__\";\n","import * as $type from \"./Type\";\n/**\r\n * ============================================================================\r\n * UTILITY FUNCTIONS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Searches `array` for `value`.\r\n *\r\n * Returns -1 if not found.\r\n *\r\n * @param array Source array\r\n * @param value Value to search\r\n * @returns Index\r\n */\nexport function indexOf(array, value) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n // TODO handle NaN\n if (array[i] === value) {\n return i;\n }\n }\n return -1;\n}\n/**\r\n * Calls `test` for each element in `array`.\r\n *\r\n * If `test` returns `true` then it immediately returns `true`.\r\n *\r\n * If `test` returns `false` for all of the elements in `array` then it returns `false`.\r\n *\r\n * @param array Source array\r\n * @param test Function which is called on each element\r\n * @returns Whether `test` returned true or not\r\n */\nexport function any(array, test) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n if (test(array[i])) {\n return true;\n }\n }\n return false;\n}\n/**\r\n * Calls `fn` function for every member of array and returns a new array out\r\n * of all outputs.\r\n *\r\n * @param array Source array\r\n * @param fn Callback function\r\n * @returns New array\r\n */\nexport function map(array, fn) {\n const length = array.length;\n const output = new Array(length);\n for (let i = 0; i < length; ++i) {\n output[i] = fn(array[i], i);\n }\n return output;\n}\n/**\r\n * Iterates through all items in array and calls `fn` function for each of\r\n * them.\r\n *\r\n * @param array Source array\r\n * @param fn Callback function\r\n */\nexport function each(array, fn) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n fn(array[i], i);\n }\n}\n/**\r\n * Iterates through all items in array in reverse order and calls `fn` function for each of\r\n * them.\r\n *\r\n * @param array Source array\r\n * @param fn Callback function\r\n */\nexport function eachReverse(array, fn) {\n let i = array.length;\n while (i > 0) {\n --i;\n fn(array[i], i);\n }\n}\n/**\r\n * Iterates through all items in array and calls `fn` function for each of\r\n * them.\r\n *\r\n * If `fn` call evaluates to `false`, further iteration is cancelled.\r\n *\r\n * @param array Source array\r\n * @param fn Callback function\r\n */\nexport function eachContinue(array, fn) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n if (!fn(array[i], i)) {\n break;\n }\n }\n}\n/**\r\n * Shifts an item at `index` towards beginning of the array.\r\n *\r\n * @param array Source array\r\n * @param index Target element index\r\n */\nexport function shiftLeft(array, index) {\n const length = array.length;\n for (let i = index; i < length; ++i) {\n array[i - index] = array[i];\n }\n array.length = length - index;\n}\n/**\r\n * Returns the last item of the array.\r\n *\r\n * @param array Source array\r\n * @returns Last item\r\n */\nexport function last(array) {\n const length = array.length;\n return length ? array[length - 1] : undefined;\n}\n/**\r\n * Returns the first item of the array.\r\n *\r\n * @param array Source array\r\n * @returns Last item\r\n */\nexport function first(array) {\n return array[0];\n}\n/**\r\n * Inserts `element` into `array` at `index`.\r\n *\r\n * Caps `index` to be between `0` and `array.length`\r\n *\r\n * @param array Source array\r\n * @param element Item to insert\r\n * @param array Index to insert item at\r\n */\nexport function insert(array, element, index) {\n //if (array) {\n index = Math.max(0, Math.min(index, array.length));\n array.splice(index, 0, element);\n //}\n}\n/**\r\n * Removes all copies of `element` from `array` (if they exist) and then\r\n * inserts `element` at `index`.\r\n *\r\n * @param array Source array\r\n * @param element Item\r\n * @param array Index to move item to\r\n */\nexport function setIndex(array, element, index) {\n remove(array, element);\n insert(array, element, index);\n}\n/**\r\n * Pushes all of the elements from `input` into `array`.\r\n *\r\n * @param array Output array\r\n * @param input Input array\r\n */\nexport function pushAll(array, input) {\n const length = input.length;\n for (let i = 0; i < length; ++i) {\n array.push(input[i]);\n }\n}\n/**\r\n * Removes `element` from `array`.\r\n *\r\n * If there are multiple copies of `element`, they are all removed.\r\n *\r\n * @param array Source array\r\n * @param element Item to remove\r\n */\nexport function remove(array, element) {\n let found = false;\n let index = 0;\n for (;;) {\n index = array.indexOf(element, index);\n if (index === -1) {\n return found;\n } else {\n found = true;\n array.splice(index, 1);\n }\n }\n}\nexport function removeFirst(array, element) {\n let index = array.indexOf(element);\n if (index !== -1) {\n array.splice(index, 1);\n return true;\n } else {\n return false;\n }\n}\n/**\r\n * Adds an `element` to `array`.\r\n *\r\n * If array already contains and item like this, it is removed before adding\r\n * it again.\r\n *\r\n * Optionally `toIndex` can be specified to add element at specific index.\r\n *\r\n * @param array Source array\r\n * @param element Item to add\r\n * @param array Index to move item to\r\n */\nexport function move(array, element, toIndex) {\n // @todo this implementation must be the same as the List.moveValue method\n // @todo don't do anything if the desired index is the same as the current index\n let index = indexOf(array, element);\n // @todo remove all old values rather than only the first ?\n if (index !== -1) {\n removeIndex(array, index);\n }\n if (toIndex == null) {\n array.push(element);\n } else {\n insertIndex(array, toIndex, element);\n }\n}\n/**\r\n * Inserts `element` into `array` at `index`.\r\n *\r\n * If `index` is not provided, it will insert `element` at the end of `array`.\r\n *\r\n * @param array Source array\r\n * @param element Item to add\r\n * @param array Index to add item at\r\n */\nexport function add(array, element, index) {\n // Append to the end if index is not set\n if (!$type.isNumber(index)) {\n array.push(element);\n }\n // Add to the beginning of array if index is 0\n else if (index === 0) {\n array.unshift(element);\n }\n // Add to indicated place if index is set\n else {\n array.splice(index, 0, element);\n }\n}\n/**\r\n * Pushes `element` into `array` if it doesn't already exist.\r\n *\r\n * @param array Source array\r\n * @param element Item to add\r\n */\nexport function pushOne(array, element) {\n if (array.indexOf(element) === -1) {\n array.push(element);\n }\n}\n/**\r\n * Removes `element` from `array` (if it exists) and then inserts `element` at\r\n * `index`.\r\n *\r\n * If `index` is not provided, it will insert `element` at the end of `array`.\r\n *\r\n * @param array Source array\r\n * @param element Item to remove\r\n * @param array Index to move item to\r\n */\nexport function replace(array, element, index) {\n // check if exists\n let ind = array.indexOf(element);\n // remove if exists\n if (ind !== -1) {\n array.splice(ind, 1);\n }\n // add to end if index is not set\n if (!$type.isNumber(index)) {\n array.push(element);\n }\n // add to indicated place if index is set\n else {\n array.splice(index, 0, element);\n }\n}\n/**\r\n * Wraps `input` in an array, if it isn't already an array.\r\n *\r\n * @param input Source value\r\n * @return An array\r\n */\nexport function toArray(input) {\n if (Array.isArray(input)) {\n return input;\n } else {\n return [input];\n }\n}\n/**\r\n * Returns `true` if `element` exists in `array`.\r\n *\r\n * @param array Source array\r\n * @param element Item to search for\r\n * @returns Item in array?\r\n */\nexport function has(array, element) {\n return indexOf(array, element) !== -1;\n}\n/**\r\n * Returns a shallow copy of `array`.\r\n *\r\n * @param array Source array\r\n * @returns Copy of the array\r\n */\nexport function copy(array) {\n const length = array.length;\n // It's faster to create the array with a pre-defined length\n const output = new Array(length);\n for (let i = 0; i < length; ++i) {\n // Because the array has a pre-defined length, we have to assign rather than push\n // This is also faster than pushing\n output[i] = array[i];\n }\n return output;\n}\n/**\r\n * Returns a copy of `array` which contains all the elements between `start`\r\n * and `end`. (including `start` and excluding `end`)\r\n *\r\n * If `end` is not provided, it defaults to `array.length`.\r\n *\r\n * @param array Source array\r\n * @param start Start index\r\n * @param end End index\r\n * @returns Part of the array\r\n */\nexport function slice(array, start, end = array.length) {\n const output = new Array(end - start);\n for (let i = start; i < end; ++i) {\n output[i - start] = array[i];\n }\n return output;\n}\n/**\r\n * Inserts a value into array at specific index.\r\n *\r\n * @param array Source array\r\n * @param index Index\r\n * @param value Value to insert\r\n */\nexport function insertIndex(array, index, value) {\n array.splice(index, 0, value);\n}\n/**\r\n * Removes a value from array at specific index.\r\n *\r\n * @param array Source array\r\n * @param index Index\r\n */\nexport function removeIndex(array, index) {\n array.splice(index, 1);\n}\n/**\r\n * Searches the array using custom function and returns index of the item if\r\n * found.\r\n *\r\n * Will call `matches` function on all items of the array. If return value\r\n * evaluates to `true`, index is returned.\r\n *\r\n * Otherwise returns -1.\r\n *\r\n * @param array Source array\r\n * @param matches Search function\r\n * @returns Index of the item if found\r\n */\nexport function findIndex(array, matches) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n if (matches(array[i], i)) {\n return i;\n }\n }\n return -1;\n}\n/**\r\n * This is the same as `findIndex` except it searches from right to left.\r\n *\r\n * @param array Source array\r\n * @param matches Search function\r\n * @returns Index of the item if found\r\n */\nexport function findIndexReverse(array, matches) {\n let i = array.length;\n while (i > 0) {\n --i;\n if (matches(array[i], i)) {\n return i;\n }\n }\n return -1;\n}\n/**\r\n * Searches the array using custom function and returns item if found.\r\n *\r\n * Will call `matches` function on all items of the array. If return value\r\n * evaluates to `true`, index is returned.\r\n *\r\n * Otherwise returns `undefined`.\r\n *\r\n * @param array Source array\r\n * @param matches Search function\r\n * @returns Item if found\r\n */\nexport function find(array, matches) {\n const index = findIndex(array, matches);\n if (index !== -1) {\n return array[index];\n }\n}\n/**\r\n * This is the same as `find` except it searches from right to left.\r\n *\r\n * @param array Source array\r\n * @param matches Search function\r\n * @returns Item if found\r\n */\nexport function findReverse(array, matches) {\n const index = findIndexReverse(array, matches);\n if (index !== -1) {\n return array[index];\n }\n}\n/**\r\n * Searches the array using custom function and returns item if found.\r\n *\r\n * Will call `matches` function on all items of the array. If value\r\n * is not `undefined`, it returns it.\r\n *\r\n * Otherwise returns `undefined`.\r\n *\r\n * @param array Source array\r\n * @param matches Search function\r\n * @returns Item if found\r\n */\nexport function findMap(array, matches) {\n const length = array.length;\n for (let i = 0; i < length; ++i) {\n const value = matches(array[i], i);\n if (value !== undefined) {\n return value;\n }\n }\n}\n/**\r\n * Iterates through all items in array and calls `fn` function for each of\r\n * them.\r\n *\r\n * @param array Source array\r\n * @param fn Callback function\r\n */\nexport function shuffle(array) {\n // https://stackoverflow.com/a/2450976/449477\n let currentIndex = array.length,\n temporaryValue,\n randomIndex;\n // While there remain elements to shuffle...\n while (0 !== currentIndex) {\n // Pick a remaining element...\n randomIndex = Math.floor(Math.random() * currentIndex);\n currentIndex -= 1;\n // And swap it with the current element.\n temporaryValue = array[currentIndex];\n array[currentIndex] = array[randomIndex];\n array[randomIndex] = temporaryValue;\n }\n}\n/**\r\n * Orders an array using specific `ordering` function and returns right-most index of\r\n * the `value`.\r\n *\r\n * @ignore Exclude from docs\r\n * @param array Source array\r\n * @param ordering An ordering function\r\n * @returns Result of the search\r\n */\nexport function getSortedIndex(array, ordering) {\n let start = 0;\n let end = array.length;\n let found = false;\n while (start < end) {\n // TODO is this faster/slower than using Math.floor ?\n const pivot = start + end >> 1;\n const order = ordering(array[pivot]);\n // less\n if (order < 0) {\n start = pivot + 1;\n // equal\n } else if (order === 0) {\n found = true;\n start = pivot + 1;\n // more\n } else {\n end = pivot;\n }\n }\n return {\n found: found,\n index: found ? start - 1 : start\n };\n}\n/**\r\n * Orders an array using specific `ordering` function and returns left-most index of\r\n * the `value`.\r\n *\r\n * @ignore Exclude from docs\r\n * @param array Source array\r\n * @param ordering An ordering function\r\n * @returns Result of the search\r\n */\nexport function getFirstSortedIndex(array, ordering) {\n let start = 0;\n let end = array.length;\n let found = false;\n while (start < end) {\n // TODO is this faster/slower than using Math.floor ?\n const pivot = start + end >> 1;\n const order = ordering(array[pivot]);\n // less\n if (order < 0) {\n start = pivot + 1;\n // equal\n } else if (order === 0) {\n found = true;\n end = pivot;\n // more\n } else {\n end = pivot;\n }\n }\n return {\n found: found,\n index: start\n };\n}\nexport function keepIf(array, keep) {\n let i = array.length;\n while (i > 0) {\n --i;\n if (!keep(array[i])) {\n array.splice(i, 1);\n }\n }\n}\n","import * as $array from \"./Array\";\nexport function keys(object) {\n return Object.keys(object);\n}\n/**\r\n * Returns an array of object's property names ordered using specific ordering\r\n * function.\r\n *\r\n * @param object Source object\r\n * @param order Ordering function\r\n * @returns Object property names\r\n */\nexport function keysOrdered(object, order) {\n return keys(object).sort(order);\n}\nexport function copy(object) {\n return Object.assign({}, object);\n}\nexport function each(object, f) {\n keys(object).forEach(key => {\n f(key, object[key]);\n });\n}\n/**\r\n * Iterates through all properties of the object calling `fn` for each of them.\r\n *\r\n * If return value of the function evaluates to `false` further iteration is\r\n * cancelled.\r\n *\r\n * @param object Source object\r\n * @param fn Callback function\r\n */\nexport function eachContinue(object, fn) {\n for (let key in object) {\n if (hasKey(object, key)) {\n if (!fn(key, object[key])) {\n break;\n }\n }\n }\n}\n/**\r\n * Orders object properties using custom `ord` function and iterates through\r\n * them calling `fn` for each of them.\r\n *\r\n * @param object Source object\r\n * @param fn Callback function\r\n * @param order Ordering function\r\n */\nexport function eachOrdered(object, fn, ord) {\n $array.each(keysOrdered(object, ord), key => {\n fn(key, object[key]);\n });\n}\n/**\r\n * Checks if `object` has a specific `key`.\r\n *\r\n * @param object Source object\r\n * @param key Property name\r\n * @returns Has key?\r\n */\nexport function hasKey(object, key) {\n return {}.hasOwnProperty.call(object, key);\n}\n/**\r\n * Copies all properties of one object to the other, omitting undefined, but only if property in target object doesn't have a value set.\r\n *\r\n * @param fromObject Source object\r\n * @param toObject Target object\r\n * @return Updated target object\r\n * @todo Maybe consolidate with utils.copy?\r\n */\nexport function softCopyProperties(source, target) {\n each(source, (key, value) => {\n // only if value is set\n //if ($type.hasValue(value) && !($type.hasValue((target)[key]))) {\n if (value != null && target[key] == null) {\n target[key] = value;\n }\n });\n return target;\n}\n","/**\r\n * ============================================================================\r\n * IMPORTS\r\n * ============================================================================\r\n * @hidden\r\n */\nimport * as $array from \"./Array\";\n/**\r\n * A base class for disposable objects.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport class DisposerClass {\n /**\r\n * Constructor.\r\n */\n constructor() {\n /**\r\n * Is object disposed?\r\n */\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._disposed = false;\n }\n /**\r\n * Checks if object is disposed.\r\n *\r\n * @return Disposed?\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Disposes the object.\r\n */\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._dispose();\n }\n }\n}\n/**\r\n * A class for creating an IDisposer.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport class Disposer {\n /**\r\n * Constructor.\r\n *\r\n * @param dispose Function that disposes object\r\n */\n constructor(dispose) {\n /**\r\n * Is object disposed?\r\n */\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Method that disposes the object.\r\n */\n Object.defineProperty(this, \"_dispose\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._disposed = false;\n this._dispose = dispose;\n }\n /**\r\n * Checks if object is disposed.\r\n *\r\n * @return Disposed?\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Disposes the object.\r\n */\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._dispose();\n }\n }\n}\n/**\r\n * This can be extended by other classes to add a `_disposers` property.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport class ArrayDisposer extends DisposerClass {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_disposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n }\n _dispose() {\n $array.each(this._disposers, x => {\n x.dispose();\n });\n }\n}\n/**\r\n * A collection of related disposers that can be disposed in one go.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport class MultiDisposer extends DisposerClass {\n constructor(disposers) {\n super();\n Object.defineProperty(this, \"_disposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._disposers = disposers;\n }\n _dispose() {\n $array.each(this._disposers, x => {\n x.dispose();\n });\n }\n get disposers() {\n return this._disposers;\n }\n}\n/**\r\n * A special kind of Disposer that has attached value set.\r\n *\r\n * If a new value is set using `set()` method, the old disposer value is\r\n * disposed.\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n */\nexport class MutableValueDisposer extends DisposerClass {\n constructor() {\n super(...arguments);\n /**\r\n * Current disposer.\r\n */\n Object.defineProperty(this, \"_disposer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Current value.\r\n */\n Object.defineProperty(this, \"_value\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n _dispose() {\n if (this._disposer != null) {\n this._disposer.dispose();\n this._disposer = undefined;\n }\n }\n /**\r\n * Returns current value.\r\n *\r\n * @return Value\r\n */\n get() {\n return this._value;\n }\n /**\r\n * Sets value and disposes previous disposer if it was set.\r\n *\r\n * @param value New value\r\n * @param disposer Disposer\r\n */\n set(value, disposer) {\n if (this._disposer != null) {\n this._disposer.dispose();\n }\n this._disposer = disposer;\n this._value = value;\n }\n /**\r\n * Resets the disposer value.\r\n */\n reset() {\n this.set(undefined, undefined);\n }\n}\n/**\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n */\nexport class CounterDisposer extends Disposer {\n constructor() {\n super(...arguments);\n /**\r\n * [_counter description]\r\n *\r\n * @todo Description\r\n */\n Object.defineProperty(this, \"_counter\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n }\n /**\r\n * [increment description]\r\n *\r\n * @todo Description\r\n */\n increment() {\n // TODO throw an error if it is disposed\n ++this._counter;\n // TODO make this more efficient\n return new Disposer(() => {\n --this._counter;\n if (this._counter === 0) {\n this.dispose();\n }\n });\n }\n}\n","import * as $type from \"./Type\";\nimport * as $array from \"./Array\";\nimport * as $object from \"./Object\";\nimport { Disposer, DisposerClass } from \"./Disposer\";\n/**\r\n * ============================================================================\r\n * DOM FUNCTIONS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Execute a function when DOM is ready.\r\n *\r\n * @since 5.0.2\r\n * @param f Callback\r\n */\nexport function ready(f) {\n if (document.readyState !== \"loading\") {\n f();\n } else {\n const listener = () => {\n if (document.readyState !== \"loading\") {\n document.removeEventListener(\"readystatechange\", listener);\n f();\n }\n };\n document.addEventListener(\"readystatechange\", listener);\n }\n}\n/**\r\n * Removes a DOM element.\r\n * @param el Target element\r\n */\nexport function removeElement(el) {\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n}\n/**\r\n * Function that adds a disposable event listener directly to a DOM element.\r\n *\r\n * @ignore Exclude from docs\r\n * @param dom A DOM element to add event to\r\n * @param type Event type\r\n * @param listener Event listener\r\n * @returns Disposable event\r\n */\nexport function addEventListener(dom, type, listener, options) {\n //@todo proper type check for options: EventListenerOptions | boolean (TS for some reason gives error on passive parameter)\n dom.addEventListener(type, listener, options || false);\n return new Disposer(() => {\n dom.removeEventListener(type, listener, options || false);\n });\n}\n/**\r\n * Function that adds an event listener which is triggered when the browser's zoom changes.\r\n *\r\n * @param listener Event listener\r\n * @returns Disposable event\r\n */\nexport function onZoom(listener) {\n // TODO use matchMedia instead ?\n return addEventListener(window, \"resize\", _ev => {\n listener();\n });\n}\n/**\r\n * @ignore\r\n */\nexport function supports(cap) {\n switch (cap) {\n case \"touchevents\":\n //return \"ontouchstart\" in document.documentElement;\n return window.hasOwnProperty(\"TouchEvent\");\n case \"pointerevents\":\n return window.hasOwnProperty(\"PointerEvent\");\n case \"mouseevents\":\n return window.hasOwnProperty(\"MouseEvent\");\n case \"wheelevents\":\n return window.hasOwnProperty(\"WheelEvent\");\n case \"keyboardevents\":\n return window.hasOwnProperty(\"KeyboardEvent\");\n }\n return false;\n}\n/**\r\n * @ignore\r\n */\nexport function getPointerId(event) {\n let id = event.pointerId || 0;\n return id;\n}\n/**\r\n * Removes focus from any element by shifting focus to body.\r\n *\r\n * @ignore\r\n */\nexport function blur() {\n if (document.activeElement && document.activeElement != document.body) {\n if (document.activeElement.blur) {\n document.activeElement.blur();\n } else {\n let input = document.createElement(\"button\");\n input.style.position = \"fixed\";\n input.style.top = \"0px\";\n input.style.left = \"-10000px\";\n document.body.appendChild(input);\n input.focus();\n input.blur();\n document.body.removeChild(input);\n }\n }\n}\n/**\r\n * Focuses element.\r\n *\r\n * @ignore\r\n */\nexport function focus(el) {\n if (el) {\n el.focus();\n }\n}\n/**\r\n * @ignore\r\n */\nexport function getRendererEvent(key) {\n if (supports(\"pointerevents\")) {\n return key;\n } else if (supports(\"touchevents\")) {\n switch (key) {\n case \"pointerover\":\n return \"touchstart\";\n case \"pointerout\":\n return \"touchend\";\n case \"pointerleave\":\n return \"touchend\";\n case \"pointerdown\":\n return \"touchstart\";\n case \"pointermove\":\n return \"touchmove\";\n case \"pointerup\":\n return \"touchend\";\n case \"click\":\n return \"click\";\n case \"dblclick\":\n return \"dblclick\";\n }\n } else if (supports(\"mouseevents\")) {\n switch (key) {\n case \"pointerover\":\n return \"mouseover\";\n case \"pointerout\":\n return \"mouseout\";\n case \"pointerleave\":\n return \"mouseleave\";\n case \"pointerdown\":\n return \"mousedown\";\n case \"pointermove\":\n return \"mousemove\";\n case \"pointerup\":\n return \"mouseup\";\n case \"click\":\n return \"click\";\n case \"dblclick\":\n return \"dblclick\";\n }\n }\n return key;\n}\n/**\r\n * Determines if pointer event originated from a touch pointer or mouse.\r\n *\r\n * @param ev Original event\r\n * @return Touch pointer?\r\n */\nexport function isTouchEvent(ev) {\n if (typeof Touch !== \"undefined\" && ev instanceof Touch) {\n return true;\n } else if (typeof PointerEvent !== \"undefined\" && ev instanceof PointerEvent && ev.pointerType != null) {\n switch (ev.pointerType) {\n case \"touch\":\n case \"pen\":\n case 2:\n return true;\n case \"mouse\":\n case 4:\n return false;\n default:\n return !(ev instanceof MouseEvent);\n }\n } else if (ev.type != null) {\n if (ev.type.match(/^mouse/)) {\n return false;\n }\n }\n return true;\n}\n/**\r\n * Sets style property on DOM element.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport function setStyle(dom, property, value) {\n dom.style[property] = value;\n}\nexport function getStyle(dom, property) {\n return dom.style[property];\n}\n/**\r\n * Gets the target of the event, works for shadow DOM too.\r\n */\nexport function getEventTarget(event) {\n if (event.composedPath) {\n const path = event.composedPath();\n if (path.length === 0) {\n return null;\n } else {\n return path[0];\n }\n } else {\n return event.target;\n }\n}\n/**\r\n * Checks of element `a` contains element `b`.\r\n *\r\n * @param a Aleged ascendant\r\n * @param b Aleged descendant\r\n * @return Contains?\r\n */\nexport function contains(a, b) {\n let cursor = b;\n while (true) {\n if (a === cursor) {\n return true;\n } else if (cursor.parentNode === null) {\n // TODO better ShadowRoot detection\n if (cursor.host == null) {\n return false;\n } else {\n cursor = cursor.host;\n }\n } else {\n cursor = cursor.parentNode;\n }\n }\n}\n/**\r\n * Returns `true` if pointer event originated on an element within Root.\r\n *\r\n * @since 5.2.8\r\n * @param event Event\r\n * @param target Target element\r\n */\nexport function isLocalEvent(event, target) {\n return event.target && contains(target.root.dom, event.target);\n}\n/**\r\n * Disables or enables interactivity of a DOM element.\r\n *\r\n * @param target Target element\r\n * @param interactive Interactive?\r\n */\nexport function setInteractive(target, interactive) {\n if (interactive) {\n target.style.pointerEvents = \"auto\";\n } else {\n target.style.pointerEvents = \"none\";\n }\n}\nexport function getEventKey(event) {\n if (event.key !== undefined) {\n return event.key;\n }\n switch (event.keyCode) {\n case 9:\n return \"Tab\";\n case 13:\n return \"Enter\";\n case 16:\n return \"Shift\";\n case 17:\n return \"Control\";\n case 27:\n return \"Escape\";\n case 32:\n return \" \";\n case 37:\n return \"ArrowLeft\";\n case 38:\n return \"ArrowUp\";\n case 39:\n return \"ArrowRight\";\n case 40:\n return \"ArrowDown\";\n case 46:\n return \"Delete\";\n }\n return \"\" + event.keyCode;\n}\n/**\r\n * Returns the shadow root of the element or null\r\n *\r\n * @param a Node\r\n * @return Root\r\n */\nexport function getShadowRoot(a) {\n let cursor = a;\n while (true) {\n if (cursor.parentNode === null) {\n // TODO better ShadowRoot detection\n if (cursor.host != null) {\n return cursor;\n } else {\n return null;\n }\n } else {\n cursor = cursor.parentNode;\n }\n }\n}\n/**\r\n * [rootStylesheet description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n */\nlet rootStylesheet;\n/**\r\n * @ignore Exclude from docs\r\n */\nfunction createStylesheet(element, text, nonce = \"\") {\n // TODO use createElementNS ?\n const e = document.createElement(\"style\");\n e.type = \"text/css\";\n if (nonce != \"\") {\n e.setAttribute(\"nonce\", nonce);\n }\n e.textContent = text;\n if (element === null) {\n document.head.appendChild(e);\n } else {\n element.appendChild(e);\n }\n return e;\n}\n/**\r\n * [getStylesheet description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n * @return [description]\r\n */\nfunction getStylesheet(element, nonce = \"\") {\n if (element === null) {\n if (rootStylesheet == null) {\n // TODO use createElementNS ?\n const e = document.createElement(\"style\");\n e.type = \"text/css\";\n if (nonce != \"\") {\n e.setAttribute(\"nonce\", nonce);\n }\n document.head.appendChild(e);\n rootStylesheet = e.sheet;\n }\n return rootStylesheet;\n } else {\n // TODO use createElementNS ?\n const e = document.createElement(\"style\");\n e.type = \"text/css\";\n if (nonce != \"\") {\n e.setAttribute(\"nonce\", nonce);\n }\n element.appendChild(e);\n return e.sheet;\n }\n}\n/**\r\n * [makeStylesheet description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n * @param selector [description]\r\n * @return [description]\r\n */\nfunction appendStylesheet(root, selector) {\n const index = root.cssRules.length;\n root.insertRule(selector + \"{}\", index);\n return root.cssRules[index];\n}\n/**\r\n * Defines a class for a CSS rule.\r\n *\r\n * Can be used to dynamically add CSS to the document.\r\n */\nexport class StyleRule extends DisposerClass {\n /**\r\n * Constructor.\r\n *\r\n * @param selector CSS selector\r\n * @param styles An object of style attribute - value pairs\r\n */\n constructor(element, selector, styles, nonce = \"\") {\n super();\n Object.defineProperty(this, \"_root\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * CSS rule.\r\n */\n Object.defineProperty(this, \"_rule\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._root = getStylesheet(element, nonce);\n try {\n this._rule = appendStylesheet(this._root, selector);\n } catch (err) {\n // Create an empty rule on failed selectors\n this._rule = appendStylesheet(this._root, \":not(*)\");\n }\n $object.each(styles, (key, value) => {\n this.setStyle(key, value);\n });\n }\n /**\r\n * A CSS selector text.\r\n *\r\n * E.g.: `.myClass p`\r\n *\r\n * @param selector CSS selector\r\n */\n set selector(selector) {\n this._rule.selectorText = selector;\n }\n /**\r\n * @return CSS selector\r\n */\n get selector() {\n return this._rule.selectorText;\n }\n // TODO test this\n _dispose() {\n // TODO a bit hacky\n const index = $array.indexOf(this._root.cssRules, this._rule);\n if (index === -1) {\n throw new Error(\"Could not dispose StyleRule\");\n } else {\n // TODO if it's empty remove it from the DOM ?\n this._root.deleteRule(index);\n }\n }\n /**\r\n * Sets the same style properties with browser-specific prefixes.\r\n *\r\n * @param name Attribute name\r\n * @param value Attribute value\r\n */\n _setVendorPrefixName(name, value) {\n const style = this._rule.style;\n style.setProperty(\"-webkit-\" + name, value, \"\");\n style.setProperty(\"-moz-\" + name, value, \"\");\n style.setProperty(\"-ms-\" + name, value, \"\");\n style.setProperty(\"-o-\" + name, value, \"\");\n style.setProperty(name, value, \"\");\n }\n /**\r\n * Sets a value for specific style attribute.\r\n *\r\n * @param name Attribute\r\n * @param value Value\r\n */\n setStyle(name, value) {\n if (name === \"transition\") {\n this._setVendorPrefixName(name, value);\n } else {\n this._rule.style.setProperty(name, value, \"\");\n }\n }\n}\n/**\r\n * Defines a class for an entire CSS style sheet.\r\n *\r\n * Can be used to dynamically add CSS to the document.\r\n */\nexport class StyleSheet extends DisposerClass {\n /**\r\n * Constructor.\r\n *\r\n * @param text CSS stylesheet\r\n */\n constructor(element, text, nonce = \"\") {\n super();\n Object.defineProperty(this, \"_element\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._element = createStylesheet(element, text, nonce);\n }\n _dispose() {\n if (this._element.parentNode) {\n this._element.parentNode.removeChild(this._element);\n }\n }\n}\n/**\r\n * Adds a class name to an HTML or SVG element.\r\n *\r\n * @ignore Exclude from docs\r\n * @param element Element\r\n * @param className Class name to add\r\n */\nexport function addClass(element, className) {\n if (!element) {\n return;\n }\n if (element.classList) {\n const classes = className.split(\" \");\n $array.each(classes, name => {\n element.classList.add(name);\n });\n } else {\n let currentClassName = element.getAttribute(\"class\");\n if (currentClassName) {\n element.setAttribute(\"class\", currentClassName.split(\" \").filter(item => {\n return item !== className;\n }).join(\" \") + \" \" + className);\n } else {\n element.setAttribute(\"class\", className);\n }\n }\n}\n/**\r\n * Removes a class name from an HTML or SVG element.\r\n *\r\n * @ignore Exclude from docs\r\n * @param element Element\r\n * @param className Class name to add\r\n */\nexport function removeClass(element, className) {\n if (!element) {\n return;\n }\n if (element.classList) {\n element.classList.remove(className);\n } else {\n let currentClassName = element.getAttribute(\"class\");\n if (currentClassName) {\n element.setAttribute(\"class\", currentClassName.split(\" \").filter(item => {\n return item !== className;\n }).join(\" \"));\n }\n }\n}\n// /**\n// * Applies a set of styles to an element. Stores the original styles so they\n// * can be restored later.\n// *\n// * @ignore\n// * @param io Element\n// */\n// export function prepElementForDrag(dom: HTMLElement): void {\n// \t// @todo: save current values\n// \t// Define possible props\n// \tlet props = [\n// \t\t\"touchAction\", \"webkitTouchAction\", \"MozTouchAction\", \"MSTouchAction\", \"msTouchAction\", \"oTouchAction\",\n// \t\t\"userSelect\", \"webkitUserSelect\", \"MozUserSelect\", \"MSUserSelect\", \"msUserSelect\", \"oUserSelect\",\n// \t\t\"touchSelect\", \"webkitTouchSelect\", \"MozTouchSelect\", \"MSTouchSelect\", \"msTouchSelect\", \"oTouchSelect\",\n// \t\t\"touchCallout\", \"webkitTouchCallout\", \"MozTouchCallout\", \"MSTouchCallout\", \"msTouchCallout\", \"oTouchCallout\",\n// \t\t\"contentZooming\", \"webkitContentZooming\", \"MozContentZooming\", \"MSContentZooming\", \"msContentZooming\", \"oContentZooming\",\n// \t\t\"userDrag\", \"webkitUserDrag\", \"MozUserDrag\", \"MSUserDrag\", \"msUserDrag\", \"oUserDrag\"\n// \t];\n// \tfor (let i = 0; i < props.length; i++) {\n// \t\tif (props[i] in dom.style) {\n// \t\t\tsetStyle(dom, props[i], \"none\");\n// \t\t}\n// \t}\n// \t// Remove iOS-specific selection;\n// \tsetStyle(dom, \"tapHighlightColor\", \"rgba(0, 0, 0, 0)\");\n// }\n// /**\n// * Restores replaced styles\n// *\n// * @ignore\n// * @param io Element\n// */\n// export function unprepElementForDrag(dom: HTMLElement): void {\n// \t// Define possible props\n// \tlet props = [\n// \t\t\"touchAction\", \"webkitTouchAction\", \"MozTouchAction\", \"MSTouchAction\", \"msTouchAction\", \"oTouchAction\",\n// \t\t\"userSelect\", \"webkitUserSelect\", \"MozUserSelect\", \"MSUserSelect\", \"msUserSelect\", \"oUserSelect\",\n// \t\t\"touchSelect\", \"webkitTouchSelect\", \"MozTouchSelect\", \"MSTouchSelect\", \"msTouchSelect\", \"oTouchSelect\",\n// \t\t\"touchCallout\", \"webkitTouchCallout\", \"MozTouchCallout\", \"MSTouchCallout\", \"msTouchCallout\", \"oTouchCallout\",\n// \t\t\"contentZooming\", \"webkitContentZooming\", \"MozContentZooming\", \"MSContentZooming\", \"msContentZooming\", \"oContentZooming\",\n// \t\t\"userDrag\", \"webkitUserDrag\", \"MozUserDrag\", \"MSUserDrag\", \"msUserDrag\", \"oUserDrag\"\n// \t];\n// \tfor (let i = 0; i < props.length; i++) {\n// \t\tif (props[i] in dom.style) {\n// \t\t\tsetStyle(dom, props[i], \"\");\n// \t\t}\n// \t}\n// \t// Remove iOS-specific selection;\n// \tsetStyle(dom, \"tapHighlightColor\", \"\");\n// }\nexport function iOS() {\n return /apple/i.test(navigator.vendor) && \"ontouchend\" in document;\n}\nexport function getSafeResolution() {\n return iOS() ? 1 : undefined;\n}\nexport function relativeToValue(percent, full) {\n if ($type.isNumber(percent)) {\n return percent;\n } else if (percent != null && $type.isNumber(percent.value) && $type.isNumber(full)) {\n return full * percent.value;\n } else {\n return 0;\n }\n}\n/**\r\n * Returns number of decimals\r\n *\r\n * @ignore Exclude from docs\r\n * @param number Input number\r\n * @return Number of decimals\r\n */\nexport function decimalPlaces(number) {\n let match = ('' + number).match(/(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$/);\n if (!match) {\n return 0;\n }\n return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));\n}\n/**\r\n * ============================================================================\r\n * STRING FORMATTING FUNCTIONS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Pads a string with additional characters to certain length.\r\n *\r\n * @param value A numeric value\r\n * @param len Result string length in characters\r\n * @param char A character to use for padding\r\n * @return Padded value as string\r\n */\nexport function padString(value, len = 0, char = \"0\") {\n if (typeof value !== \"string\") {\n value = value.toString();\n }\n return len > value.length ? Array(len - value.length + 1).join(char) + value : value;\n}\nexport function trimLeft(text) {\n return text.replace(/^[\\s]*/, \"\");\n}\nexport function trimRight(text) {\n return text.replace(/[\\s]*$/, \"\");\n}\nexport function trim(text) {\n return trimLeft(trimRight(text));\n}\nexport function truncateTextWithEllipsis(text, maxLength, breakWords = false, ellipsis = \"...\") {\n if (text.length > maxLength) {\n // Find the last non-alphanumeric character before maxLength\n let lastNonAlphanumericIndex = maxLength - 1;\n while (lastNonAlphanumericIndex >= 0 && text.charAt(lastNonAlphanumericIndex).match(/\\w/)) {\n lastNonAlphanumericIndex--;\n }\n if (lastNonAlphanumericIndex >= 0 && breakWords == false) {\n return text.substring(0, lastNonAlphanumericIndex + 1) + '...';\n } else {\n // If no non-alphanumeric character found, truncate without breaking words\n return text.substring(0, maxLength) + ellipsis;\n }\n } else {\n return text;\n }\n}\n/**\r\n * Tries to determine format type.\r\n *\r\n * @ignore Exclude from docs\r\n * @param format Format string\r\n * @return Format type (\"string\" | \"number\" | \"date\" | \"duration\")\r\n */\nexport function getFormat(format) {\n // Undefined?\n if (typeof format === \"undefined\") {\n return \"string\";\n }\n // Cleanup and lowercase format\n format = format.toLowerCase().replace(/^\\[[^\\]]*\\]/, \"\");\n // Remove style tags\n format = format.replace(/\\[[^\\]]+\\]/, \"\");\n // Trim\n format = format.trim();\n // Check for any explicit format hints (i.e. /Date)\n let hints = format.match(/\\/(date|number|duration)$/);\n if (hints) {\n return hints[1];\n }\n // Check for explicit hints\n if (format === \"number\") {\n return \"number\";\n }\n if (format === \"date\") {\n return \"date\";\n }\n if (format === \"duration\") {\n return \"duration\";\n }\n // Detect number formatting symbols\n if (format.match(/[#0]/)) {\n return \"number\";\n }\n // Detect date formatting symbols\n if (format.match(/[ymwdhnsqaxkzgtei]/)) {\n return \"date\";\n }\n // Nothing? Let's display as string\n return \"string\";\n}\n/**\r\n * Cleans up format:\r\n * * Strips out formatter hints\r\n *\r\n * @ignore Exclude from docs\r\n * @param format Format\r\n * @return Cleaned format\r\n */\nexport function cleanFormat(format) {\n return format.replace(/\\/(date|number|duration)$/i, \"\");\n}\n/**\r\n * Strips all tags from the string.\r\n *\r\n * @param text Source string\r\n * @return String without tags\r\n */\nexport function stripTags(text) {\n return text ? text.replace(/<[^>]*>/g, \"\") : text;\n}\n/**\r\n * Removes new lines and tags from a string.\r\n *\r\n * @param text String to conver\r\n * @return Converted string\r\n */\nexport function plainText(text) {\n return text ? stripTags((\"\" + text).replace(/[\\n\\r]+/g, \". \")) : text;\n}\n/**\r\n * Escapes string so it can safely be used in a Regex.\r\n *\r\n * @param value Unsescaped string\r\n * @return Escaped string\r\n */\nexport function escapeForRgex(value) {\n return value.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&');\n}\n/**\r\n * Adds space before each uppercase letter.\r\n *\r\n * @param str Input string\r\n * @return Output string\r\n */\nexport function addSpacing(str) {\n let result = \"\";\n for (let i = 0; i < str.length; i++) {\n const char = str.charAt(i);\n if (char.toUpperCase() == char && i != 0) {\n result += \" \";\n }\n result += char;\n }\n return result;\n}\n/**\r\n * Splits the string into separate characters. Keeps RTL words non-split.\r\n *\r\n * @param source Input\r\n * @return Split text\r\n */\nexport function splitString(source) {\n // Regular expression to identify RTL characters\n const rtlChar = /[\\u0590-\\u05FF\\u0600-\\u06FF\\u0750-\\u077F\\u08A0-\\u08FF\\uFB50-\\uFDFF\\uFE70-\\uFEFF]/;\n // Regular expression to capture segments ending with specific Arabic characters\n const splitPattern = /([^اأدذرزو]*[اأدذرزو])/gi;\n // Split input string into array of words or characters, including whitespace\n let segments = source.split(/(\\s+)/); // Split by whitespace, capturing it\n let result = [];\n segments.forEach(segment => {\n if (segment.match(/^\\s+$/)) {\n // If the segment is purely whitespace\n if (segment = \" \") {\n segment = \" \";\n }\n result.push(segment);\n } else if (rtlChar.test(segment)) {\n // If the segment contains RTL characters, handle special splits\n let parts = segment.split(splitPattern).filter(part => part !== '');\n // Concatenate parts processed by the split pattern directly to result\n result = result.concat(parts);\n } else {\n // Treat this segment as LTR: split into characters\n result = result.concat([...segment]);\n }\n });\n return result;\n}\n/**\r\n * ============================================================================\r\n * DATE-RELATED FUNCTIONS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * Returns a year day.\r\n *\r\n * @param date Date\r\n * @param utc Assume UTC dates?\r\n * @return Year day\r\n * @todo Account for UTC\r\n */\nexport function getYearDay(date, utc = false) {\n // TODO: utc needed?\n utc;\n const start = new Date(date.getFullYear(), 0, 0);\n const diff = date.getTime() - start.getTime() + (start.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000;\n const oneDay = 1000 * 60 * 60 * 24;\n return Math.floor(diff / oneDay);\n}\n/**\r\n * Returns week number for a given date.\r\n *\r\n * @param date Date\r\n * @param utc Assume UTC dates?\r\n * @return Week number\r\n * @todo Account for UTC\r\n */\nexport function getWeek(date, _utc = false) {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));\n const day = d.getUTCDay() || 7;\n d.setUTCDate(d.getUTCDate() + 4 - day);\n const firstDay = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n return Math.ceil(((d.getTime() - firstDay.getTime()) / 86400000 + 1) / 7);\n}\n/**\r\n * Returns a \"week year\" of the given date.\r\n *\r\n * @param date Date\r\n * @param utc Assume UTC dates?\r\n * @return Year of week\r\n * @since 5.3.0\r\n * @todo Account for UTC\r\n */\nexport function getWeekYear(date, _utc = false) {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));\n const day = d.getUTCDay() || 7;\n d.setUTCDate(d.getUTCDate() + 4 - day);\n const firstDay = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n return firstDay.getFullYear();\n}\n/**\r\n * Returns a week number in the month.\r\n *\r\n * @param date Source Date\r\n * @param utc Assume UTC dates?\r\n * @return Week number in month\r\n */\nexport function getMonthWeek(date, utc = false) {\n const firstWeek = getWeek(new Date(date.getFullYear(), date.getMonth(), 1), utc);\n let currentWeek = getWeek(date, utc);\n if (currentWeek == 1) {\n currentWeek = 53;\n }\n return currentWeek - firstWeek + 1;\n}\n/**\r\n * Returns a year day out of the given week number.\r\n *\r\n * @param week Week\r\n * @param year Year\r\n * @param weekday Weekday\r\n * @param utc Assume UTC dates\r\n * @return Day in a year\r\n */\nexport function getDayFromWeek(week, year, weekday = 1, utc = false) {\n let date = new Date(year, 0, 4, 0, 0, 0, 0);\n if (utc) {\n date.setUTCFullYear(year);\n }\n let day = week * 7 + weekday - ((date.getDay() || 7) + 3);\n return day;\n}\n/**\r\n * Returns 12-hour representation out of the 24-hour hours.\r\n *\r\n * @param hours 24-hour number\r\n * @return 12-hour number\r\n */\nexport function get12Hours(hours, base) {\n if (hours > 12) {\n hours -= 12;\n } else if (hours === 0) {\n hours = 12;\n }\n return base != null ? hours + (base - 1) : hours;\n}\n/**\r\n * Returns a string name of the time zone.\r\n *\r\n * @param date Date object\r\n * @param long Should return long (\"Pacific Standard Time\") or short abbreviation (\"PST\")\r\n * @param savings Include information if it's in daylight savings mode\r\n * @param utc Assume UTC dates\r\n * @return Time zone name\r\n */\nexport function getTimeZone(date, long = false, savings = false, utc = false, timezone) {\n if (utc) {\n return long ? \"Coordinated Universal Time\" : \"UTC\";\n } else if (timezone) {\n const d1 = date.toLocaleString(\"en-US\", {\n timeZone: timezone\n });\n const d2 = date.toLocaleString(\"en-US\", {\n timeZone: timezone,\n timeZoneName: long ? \"long\" : \"short\"\n });\n return trim(d2.substr(d1.length));\n }\n let wotz = date.toLocaleString(\"UTC\");\n let wtz = date.toLocaleString(\"UTC\", {\n timeZoneName: long ? \"long\" : \"short\"\n }).substr(wotz.length);\n //wtz = wtz.replace(/[+-]+[0-9]+$/, \"\");\n if (savings === false) {\n wtz = wtz.replace(/ (standard|daylight|summer|winter) /i, \" \");\n }\n return trim(wtz);\n}\nexport function getTimezoneOffset(timezone) {\n const date = new Date(Date.UTC(2012, 0, 1, 0, 0, 0, 0));\n const utcDate = new Date(date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\"\n }));\n const tzDate = new Date(date.toLocaleString(\"en-US\", {\n timeZone: timezone\n }));\n return (tzDate.getTime() - utcDate.getTime()) / 6e4 * -1;\n}\nexport function capitalizeFirst(text) {\n return text.charAt(0).toUpperCase() + text.slice(1);\n}\n/**\r\n * The functions below are taken and adapted from Garry Tan's blog post:\r\n * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\r\n *\r\n * The further attributions go mjijackson.com, which now seems to be defunct.\r\n */\n/**\r\n * Converts an HSL color value to RGB. Conversion formula\r\n * adapted from http://en.wikipedia.org/wiki/HSL_color_space.\r\n * Assumes h, s, and l are contained in the set [0, 1] and\r\n * returns r, g, and b in the set [0, 255].\r\n *\r\n * Function adapted from:\r\n * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\r\n *\r\n * @param h The hue\r\n * @param s The saturation\r\n * @param l The lightness\r\n * @return The RGB representation\r\n */\nexport function hslToRgb(color) {\n let r, g, b;\n let h = color.h;\n let s = color.s;\n let l = color.l;\n if (s == 0) {\n r = g = b = l; // achromatic\n } else {\n let hue2rgb = function hue2rgb(p, q, t) {\n if (t < 0) {\n t += 1;\n }\n if (t > 1) {\n t -= 1;\n }\n if (t < 1 / 6) {\n return p + (q - p) * 6 * t;\n }\n if (t < 1 / 2) {\n return q;\n }\n if (t < 2 / 3) {\n return p + (q - p) * (2 / 3 - t) * 6;\n }\n return p;\n };\n let q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n let p = 2 * l - q;\n r = hue2rgb(p, q, h + 1 / 3);\n g = hue2rgb(p, q, h);\n b = hue2rgb(p, q, h - 1 / 3);\n }\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(b * 255)\n };\n}\n/**\r\n * Converts an RGB color value to HSL. Conversion formula\r\n * adapted from http://en.wikipedia.org/wiki/HSL_color_space.\r\n * Assumes r, g, and b are contained in the set [0, 255] and\r\n * returns h, s, and l in the set [0, 1].\r\n *\r\n * Function adapted from:\r\n * http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\r\n *\r\n * @param r The red color value\r\n * @param g The green color value\r\n * @param b The blue color value\r\n * @return The HSL representation\r\n */\nexport function rgbToHsl(color) {\n let r = color.r / 255;\n let g = color.g / 255;\n let b = color.b / 255;\n let max = Math.max(r, g, b);\n let min = Math.min(r, g, b);\n let h = 0;\n let s = 0;\n let l = (max + min) / 2;\n if (max === min) {\n h = s = 0; // achromatic\n } else {\n let d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = (g - b) / d + (g < b ? 6 : 0);\n break;\n case g:\n h = (b - r) / d + 2;\n break;\n case b:\n h = (r - g) / d + 4;\n break;\n }\n h /= 6;\n }\n return {\n h: h,\n s: s,\n l: l\n };\n}\n/**\r\n * Converts HSV to HSL.\r\n *\r\n * https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_HSL\r\n */\nexport function hsvToHsl(hsv) {\n const l = hsv.v * (1 - hsv.s / 2);\n const s = l === 0 || l === 1 ? 0 : (hsv.v - l) / Math.min(l, 1 - l);\n return {\n h: hsv.h,\n s,\n l,\n a: hsv.a\n };\n}\n/**\r\n * Converts HSL to HSV.\r\n *\r\n * https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV\r\n */\nexport function hslToHsv(hsl) {\n const v = hsl.l + hsl.s * Math.min(hsl.l, 1 - hsl.l);\n const s = v === 0 ? 0 : 2 * (1 - hsl.l / v);\n return {\n h: hsl.h,\n s,\n v,\n a: hsl.a\n };\n}\n/**\r\n * Returns a color that is `percent` brighter than the reference color.\r\n *\r\n * @param color Reference color\r\n * @param percent Brightness percent\r\n * @return Hex code of the new color\r\n */\nexport function lighten(rgb, percent) {\n if (rgb) {\n return {\n r: Math.max(0, Math.min(255, rgb.r + getLightnessStep(rgb.r, percent))),\n g: Math.max(0, Math.min(255, rgb.g + getLightnessStep(rgb.g, percent))),\n b: Math.max(0, Math.min(255, rgb.b + getLightnessStep(rgb.b, percent))),\n a: rgb.a\n };\n } else {\n // TODO is this correct ?\n return rgb;\n }\n}\n;\n/**\r\n * Gets lightness step.\r\n *\r\n * @param value Value\r\n * @param percent Percent\r\n * @return Step\r\n */\nexport function getLightnessStep(value, percent) {\n let base = percent > 0 ? 255 - value : value;\n return Math.round(base * percent);\n}\n/**\r\n * Returns a color that is `percent` brighter than the source `color`.\r\n *\r\n * @param color Source color\r\n * @param percent Brightness percent\r\n * @return New color\r\n */\nexport function brighten(rgb, percent) {\n if (rgb) {\n let base = Math.min(Math.max(rgb.r, rgb.g, rgb.b), 230);\n //let base = Math.max(rgb.r, rgb.g, rgb.b);\n let step = getLightnessStep(base, percent);\n return {\n r: Math.max(0, Math.min(255, Math.round(rgb.r + step))),\n g: Math.max(0, Math.min(255, Math.round(rgb.g + step))),\n b: Math.max(0, Math.min(255, Math.round(rgb.b + step))),\n a: rgb.a\n };\n } else {\n // TODO is this correct ?\n return rgb;\n }\n}\n;\n/**\r\n * Returns brightness step.\r\n *\r\n * @ignore Exclude from docs\r\n * @param value Value\r\n * @param percent Percent\r\n * @return Step\r\n */\nexport function getBrightnessStep(_value, percent) {\n let base = 255; //percent > 0 ? 255 - value : value;\n return Math.round(base * percent);\n}\n/**\r\n * Returns `true` if color is \"light\". Useful indetermining which contrasting\r\n * color to use for elements over this color. E.g.: you would want to use\r\n * black text over light background, and vice versa.\r\n *\r\n * @param color Source color\r\n * @return Light?\r\n */\nexport function isLight(color) {\n return (color.r * 299 + color.g * 587 + color.b * 114) / 1000 >= 128;\n}\n/**\r\n * Returns a new [[iRGB]] object based on `rgb` parameter with specific\r\n * saturation applied.\r\n *\r\n * `saturation` can be in the range of 0 (fully desaturated) to 1 (fully\r\n * saturated).\r\n *\r\n * @param color Base color\r\n * @param saturation Saturation (0-1)\r\n * @return New color\r\n */\nexport function saturate(rgb, saturation) {\n if (rgb === undefined || saturation == 1) {\n return rgb;\n }\n let hsl = rgbToHsl(rgb);\n hsl.s = saturation;\n return hslToRgb(hsl);\n}\n/**\r\n * Returns a color which contrasts more with the source `color`.\r\n *\r\n * @param color Base color\r\n * @param lightAlternative Light option\r\n * @param darkAlternative Dark option\r\n * @return New color\r\n */\nexport function alternativeColor(color, lightAlternative = {\n r: 255,\n g: 255,\n b: 255\n}, darkAlternative = {\n r: 255,\n g: 255,\n b: 255\n}) {\n let light = lightAlternative;\n let dark = darkAlternative;\n if (isLight(darkAlternative)) {\n light = darkAlternative;\n dark = lightAlternative;\n }\n return isLight(color) ? dark : light;\n}\n/**\r\n * @ignore\r\n */\nexport function mergeTags(tags1, tags2) {\n if (!tags1) {\n tags1 = [];\n }\n return [...tags1, ...tags2].filter((value, index, self) => {\n return self.indexOf(value) === index;\n });\n}\n/**\r\n * @ignore\r\n */\nexport function sameBounds(a, b) {\n if (!b) {\n return false;\n }\n if (a.left != b.left) {\n return false;\n }\n if (a.right != b.right) {\n return false;\n }\n if (a.top != b.top) {\n return false;\n }\n if (a.bottom != b.bottom) {\n return false;\n }\n return true;\n}\n","import { range } from \"./Animation\";\nimport * as $utils from \"./Utils\";\nimport * as $type from \"./Type\";\n/**\r\n * @ignore\r\n */\nfunction string2hex(string) {\n //string = cssColorNames[string.toLowerCase()] || string;\n if (string[0] === \"#\") {\n string = string.substr(1);\n }\n if (string.length == 3) {\n string = string[0].repeat(2) + string[1].repeat(2) + string[2].repeat(2);\n }\n return parseInt(string, 16);\n}\n/**\r\n * @ignore\r\n */\nexport function rgba2hex(color) {\n color = color.replace(/[ ]/g, \"\");\n // Init\n let matches = color.match(/^rgb\\(([0-9]*),([0-9]*),([0-9]*)\\)/i);\n // Try rgb() format\n if (matches) {\n matches.push(\"1\");\n } else {\n matches = color.match(/^rgba\\(([0-9]*),([0-9]*),([0-9]*),([.0-9]*)\\)/i);\n if (!matches) {\n return 0x000000;\n }\n }\n let hex = \"\";\n for (let i = 1; i <= 3; i++) {\n let val = parseInt(matches[i]).toString(16);\n if (val.length == 1) {\n val = \"0\" + val;\n }\n hex += val;\n }\n return string2hex(hex);\n}\n/**\r\n * Returns a new [[Color]] object base on input.\r\n *\r\n * Accepts parameters in CSS hex or rgb/rtba strings, or hex numbers.\r\n *\r\n * * `\"#f00\"`\r\n * * `\"#ff0000\"`\r\n * * `\"rgb(255, 0, 0)\"`\r\n * * `\"rgba(255, 0, 0, 1)\"`\r\n * * `0xff0000`\r\n *\r\n * @param input Input color\r\n * @return Color\r\n */\nexport function color(input) {\n return Color.fromAny(input);\n}\n/**\r\n * Wherever color needs to be specified in amCharts 5, `Color` object needs to\r\n * be used.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/colors-gradients-and-patterns/} for more info\r\n * @important\r\n */\nexport class Color {\n constructor(hex) {\n Object.defineProperty(this, \"_hex\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._hex = hex | 0;\n }\n /**\r\n * Color numeric value.\r\n */\n get hex() {\n return this._hex;\n }\n /**\r\n * Value of color's R channel.\r\n * @return R value\r\n */\n get r() {\n return this._hex >>> 16;\n }\n /**\r\n * Value of color's G channel.\r\n * @return G value\r\n */\n get g() {\n return this._hex >> 8 & 0xFF;\n }\n /**\r\n * Value of color's B channel.\r\n * @return B value\r\n */\n get b() {\n return this._hex & 0xFF;\n }\n /**\r\n * Returns color CSS representation in form of `rgba(r, g, b, a)` string.\r\n *\r\n * @param alpha Opacity\r\n * @return CSS string\r\n */\n toCSS(alpha = 1) {\n return \"rgba(\" + this.r + \", \" + this.g + \", \" + this.b + \", \" + alpha + \")\";\n }\n /**\r\n * Returns color CSS representation in form of `#rgb` string.\r\n *\r\n * @return CSS string\r\n */\n toCSSHex() {\n return \"#\" + $utils.padString(this.r.toString(16), 2) + $utils.padString(this.g.toString(16), 2) + $utils.padString(this.b.toString(16), 2);\n }\n /**\r\n * Returns color's HSL info.\r\n * @param alpha Opacity\r\n * @return HSL info\r\n */\n toHSL(alpha = 1) {\n return $utils.rgbToHsl({\n r: this.r,\n g: this.g,\n b: this.b,\n a: alpha\n });\n }\n /**\r\n * Converts HSL values into a new [[Color]] object.\r\n *\r\n * @param h H value\r\n * @param s S value\r\n * @param l L value\r\n * @return Color object\r\n */\n static fromHSL(h, s, l) {\n const rgb = $utils.hslToRgb({\n h: h,\n s: s,\n l: l\n });\n return this.fromRGB(rgb.r, rgb.g, rgb.b);\n }\n toString() {\n return this.toCSSHex();\n }\n /**\r\n * Converts hex number into a new [[Color]] object.\r\n *\r\n * ```TypeScript\r\n * Color.fromHex(0xff0000) // red\r\n * ```\r\n * ```JavaScript\r\n * Color.fromHex(0xff0000) // red\r\n * ```\r\n *\r\n * @param hex Hex color\r\n * @return Color\r\n */\n static fromHex(hex) {\n return new Color(hex);\n }\n /**\r\n * Converts RGB values to a new [[Color]] object.\r\n *\r\n * @param r R value\r\n * @param g G value\r\n * @param b B value\r\n * @return Color\r\n */\n static fromRGB(r, g, b) {\n return new Color((b | 0) + (g << 8) + (r << 16));\n }\n /**\r\n * Converts RGB string to a new [[Color]] object.\r\n *\r\n * ```TypeScript\r\n * Color.fromString(\"#ff0000\") // red\r\n * ```\r\n * ```JavaScript\r\n * Color.fromString(\"#ff0000\") // red\r\n * ```\r\n *\r\n * @param s RGB string\r\n * @return Color\r\n */\n static fromString(s) {\n return new Color(string2hex(s));\n }\n /**\r\n * Converts CSS rgba() syntax to a new [[Color]] object.\r\n *\r\n * ```TypeScript\r\n * Color.fromCSS(\"rgba(255, 0, 0, 1)\") // red\r\n * ```\r\n * ```JavaScript\r\n * Color.fromCSS(\"rgba(255, 0, 0, 1)\") // red\r\n * ```\r\n *\r\n * @param {string} s [description]\r\n * @return {Color} [description]\r\n */\n static fromCSS(s) {\n return new Color(rgba2hex(s));\n }\n /**\r\n * Convert to color from virtually anything.\r\n *\r\n * Will throw an exception if unable to resolve the color.\r\n *\r\n * @param s Source\r\n * @return Color\r\n */\n static fromAny(s) {\n if ($type.isString(s)) {\n if (s[0] == \"#\") {\n return Color.fromString(s);\n } else if (s.substr(0, 3) == \"rgb\") {\n return Color.fromCSS(s);\n }\n } else if ($type.isNumber(s)) {\n return Color.fromHex(s);\n } else if (s instanceof Color) {\n return Color.fromHex(s.hex);\n }\n throw new Error(\"Unknown color syntax: \" + s);\n }\n /**\r\n * Returns a new [[Color]] object based on either `lightAlternative` or\r\n * `darkAlternative` depending on which one is more contrasting with\r\n * the `color`.\r\n *\r\n * @param color Reference color\r\n * @param lightAlternative Light color\r\n * @param darkAlternative Dark color\r\n * @return Alternative color\r\n */\n static alternative(color, lightAlternative, darkAlternative) {\n const rgb = $utils.alternativeColor({\n r: color.r,\n g: color.g,\n b: color.b\n }, lightAlternative ? {\n r: lightAlternative.r,\n g: lightAlternative.g,\n b: lightAlternative.b\n } : undefined, darkAlternative ? {\n r: darkAlternative.r,\n g: darkAlternative.g,\n b: darkAlternative.b\n } : undefined);\n return this.fromRGB(rgb.r, rgb.g, rgb.b);\n }\n /**\r\n * Returns an intermediate Color between two reference colors depending on\r\n * the progress (`diff`) between the two.\r\n *\r\n * @param diff Progress\r\n * @param from Source color\r\n * @param to Target color\r\n * @param mode Interpolation mode\r\n * @return Color\r\n */\n static interpolate(diff, from, to, mode = \"rgb\") {\n if (mode == \"hsl\") {\n const fromHSL = from.toHSL();\n const toHSL = to.toHSL();\n return Color.fromHSL(range(diff, fromHSL.h, toHSL.h), range(diff, fromHSL.s, toHSL.s), range(diff, fromHSL.l, toHSL.l));\n } else {\n return Color.fromRGB(range(diff, from.r, to.r), range(diff, from.g, to.g), range(diff, from.b, to.b));\n }\n }\n /**\r\n * Returns a new [[Color]] lightened by `percent` value.\r\n *\r\n * Use negative value to darken the color.\r\n *\r\n * @param color Source color\r\n * @param percent Percent\r\n * @return New color\r\n */\n static lighten(color, percent) {\n const rgb = $utils.lighten({\n r: color.r,\n g: color.g,\n b: color.b\n }, percent);\n return Color.fromRGB(rgb.r, rgb.g, rgb.b);\n }\n /**\r\n * Returns a new [[Color]] brightened by `percent` value.\r\n *\r\n * Use negative value to dim the color.\r\n *\r\n * @param color Source color\r\n * @param percent Percent\r\n * @return New color\r\n */\n static brighten(color, percent) {\n const rgb = $utils.brighten({\n r: color.r,\n g: color.g,\n b: color.b\n }, percent);\n return Color.fromRGB(rgb.r, rgb.g, rgb.b);\n }\n /**\r\n * Returns a new [[Color]] saturated by `percent` value.\r\n *\r\n * Value range is between `0` (fully desaturated), to `1` (full color).\r\n *\r\n * @param color Source color\r\n * @param percent Percent\r\n * @return New color\r\n */\n static saturate(color, percent) {\n const rgb = $utils.saturate({\n r: color.r,\n g: color.g,\n b: color.b\n }, percent);\n return Color.fromRGB(rgb.r, rgb.g, rgb.b);\n }\n}\n","/**\r\n * Event Dispatcher module is used for registering listeners and dispatching\r\n * events across amCharts system.\r\n */\n/**\r\n * ============================================================================\r\n * IMPORTS\r\n * ============================================================================\r\n * @hidden\r\n */\nimport { Disposer, MultiDisposer } from \"./Disposer\";\nimport * as $array from \"./Array\";\nimport * as $type from \"./Type\";\n/**\r\n * Universal Event Dispatcher.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/} for more info\r\n */\nexport class EventDispatcher {\n /**\r\n * Constructor\r\n */\n constructor() {\n Object.defineProperty(this, \"_listeners\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_killed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_disabled\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_iterating\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_enabled\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._listeners = [];\n this._killed = [];\n this._disabled = {};\n this._iterating = 0;\n this._enabled = true;\n this._disposed = false;\n }\n /**\r\n * Returns if this object has been already disposed.\r\n *\r\n * @return Disposed?\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Dispose (destroy) this object.\r\n */\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n const a = this._listeners;\n this._iterating = 1;\n this._listeners = null;\n this._disabled = null;\n try {\n $array.each(a, x => {\n x.disposer.dispose();\n });\n } finally {\n this._killed = null;\n this._iterating = null;\n }\n }\n }\n /**\r\n * Checks if this particular event dispatcher has any listeners set.\r\n *\r\n * @return Has listeners?\r\n */\n hasListeners() {\n return this._listeners.length !== 0;\n }\n /**\r\n * Checks if this particular event dispatcher has any particular listeners set.\r\n *\r\n * @return Has particular event listeners?\r\n */\n hasListenersByType(type) {\n return $array.any(this._listeners, x => (x.type === null || x.type === type) && !x.killed);\n }\n /**\r\n * Enable dispatching of events if they were previously disabled by\r\n * `disable()`.\r\n */\n enable() {\n this._enabled = true;\n }\n /**\r\n * Disable dispatching of events until re-enabled by `enable()`.\r\n */\n disable() {\n this._enabled = false;\n }\n /**\r\n * Enable dispatching particular event, if it was disabled before by\r\n * `disableType()`.\r\n *\r\n * @param type Event type\r\n */\n enableType(type) {\n delete this._disabled[type];\n }\n /**\r\n * Disable dispatching of events for a certain event type.\r\n *\r\n * Optionally, can set how many dispatches to skip before automatically\r\n * re-enabling the dispatching.\r\n *\r\n * @param type Event type\r\n * @param amount Number of event dispatches to skip\r\n */\n disableType(type, amount = Infinity) {\n this._disabled[type] = amount;\n }\n /**\r\n * Removes listener from dispatcher.\r\n *\r\n * Will throw an exception if such listener does not exists.\r\n *\r\n * @param listener Listener to remove\r\n */\n _removeListener(listener) {\n if (this._iterating === 0) {\n const index = this._listeners.indexOf(listener);\n if (index === -1) {\n throw new Error(\"Invalid state: could not remove listener\");\n }\n this._listeners.splice(index, 1);\n } else {\n this._killed.push(listener);\n }\n }\n /**\r\n * Removes existing listener by certain parameters.\r\n *\r\n * @param once Listener's once setting\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n */\n _removeExistingListener(once, type, callback, context) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n this._eachListener(info => {\n if (info.once === once &&\n // TODO is this correct ?\n info.type === type && (callback === undefined || info.callback === callback) && info.context === context) {\n info.disposer.dispose();\n }\n });\n }\n /**\r\n * Checks if dispatching for particular event type is enabled.\r\n *\r\n * @param type Event type\r\n * @return Enabled?\r\n */\n isEnabled(type) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n // TODO is this check correct ?\n return this._enabled && this._listeners.length > 0 && this.hasListenersByType(type) && this._disabled[type] === undefined;\n }\n /**\r\n * Removes all listeners of a particular event type\r\n *\r\n * @param type Listener's type\r\n */\n removeType(type) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n this._eachListener(info => {\n if (info.type === type) {\n info.disposer.dispose();\n }\n });\n }\n /**\r\n * Checks if there's already a listener with specific parameters.\r\n *\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n * @return Has listener?\r\n */\n has(type, callback, context) {\n const index = $array.findIndex(this._listeners, info => {\n return info.once !== true &&\n // Ignoring \"once\" listeners\n info.type === type && (callback === undefined || info.callback === callback) && info.context === context;\n });\n return index !== -1;\n }\n /**\r\n * Checks whether event of the particular type should be dispatched.\r\n *\r\n * @param type Event type\r\n * @return Dispatch?\r\n */\n _shouldDispatch(type) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n const count = this._disabled[type];\n if (!$type.isNumber(count)) {\n return this._enabled;\n } else {\n if (count <= 1) {\n delete this._disabled[type];\n } else {\n --this._disabled[type];\n }\n return false;\n }\n }\n /**\r\n * [_eachListener description]\r\n *\r\n * All of this extra code is needed when a listener is removed while iterating\r\n *\r\n * @todo Description\r\n * @param fn [description]\r\n */\n _eachListener(fn) {\n ++this._iterating;\n try {\n $array.each(this._listeners, fn);\n } finally {\n --this._iterating;\n // TODO should this be inside or outside the finally ?\n if (this._iterating === 0 && this._killed.length !== 0) {\n // Remove killed listeners\n $array.each(this._killed, killed => {\n this._removeListener(killed);\n });\n this._killed.length = 0;\n }\n }\n }\n /**\r\n * Dispatches an event immediately without waiting for next cycle.\r\n *\r\n * @param type Event type\r\n * @param event Event object\r\n * @todo automatically add in type and target properties if they are missing\r\n */\n dispatch(type, event) {\n if (this._shouldDispatch(type)) {\n // TODO check if it's faster to use an object of listeners rather than a single big array\n // TODO if the function throws, maybe it should keep going ?\n this._eachListener(listener => {\n if (!listener.killed && (listener.type === null || listener.type === type)) {\n listener.dispatch(type, event);\n }\n });\n }\n }\n /**\r\n * Shelves the event to be dispatched within next update cycle.\r\n *\r\n * @param type Event type\r\n * @param event Event object\r\n * @todo automatically add in type and target properties if they are missing\r\n */\n /*public dispatchLater(type: Key, event: T[Key]): void {\r\n if (this._shouldDispatch(type)) {\r\n this._eachListener((listener) => {\r\n // TODO check if it's faster to use an object of listeners rather than a single big array\r\n if (!listener.killed && (listener.type === null || listener.type === type)) {\r\n // TODO if the function throws, maybe it should keep going ?\r\n // TODO dispatch during the update cycle, rather than using whenIdle\r\n $async.whenIdle(() => {\r\n if (!listener.killed) {\r\n listener.dispatch(type, event);\r\n }\r\n });\r\n }\r\n });\r\n }\r\n }*/\n /**\r\n * Creates, catalogs and returns an [[EventListener]].\r\n *\r\n * Event listener can be disposed.\r\n *\r\n * @param once Listener's once setting\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n * @param shouldClone Whether the listener should be copied when the EventDispatcher is copied\r\n * @param dispatch\r\n * @returns An event listener\r\n */\n _on(once, type, callback, context, shouldClone, dispatch) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n this._removeExistingListener(once, type, callback, context);\n const info = {\n type: type,\n callback: callback,\n context: context,\n shouldClone: shouldClone,\n dispatch: dispatch,\n killed: false,\n once: once,\n disposer: new Disposer(() => {\n info.killed = true;\n this._removeListener(info);\n })\n };\n this._listeners.push(info);\n return info;\n }\n /**\r\n * Creates an event listener to be invoked on **any** event.\r\n *\r\n * @param callback Callback function\r\n * @param context Callback context\r\n * @param shouldClone Whether the listener should be copied when the EventDispatcher is copied\r\n * @returns A disposable event listener\r\n */\n onAll(callback, context, shouldClone = true) {\n return this._on(false, null, callback, context, shouldClone, (_type, event) => callback.call(context, event)).disposer;\n }\n /**\r\n * Creates an event listener to be invoked on a specific event type.\r\n *\r\n * ```TypeScript\r\n * button.events.once(\"click\", (ev) => {\r\n * console.log(\"Button clicked\");\r\n * }, this);\r\n * ```\r\n * ```JavaScript\r\n * button.events.once(\"click\", (ev) => {\r\n * console.log(\"Button clicked\");\r\n * }, this);\r\n * ```\r\n *\r\n * The above will invoke our custom event handler whenever series we put\r\n * event on is hidden.\r\n *\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n * @param shouldClone Whether the listener should be copied when the EventDispatcher is copied\r\n * @returns A disposable event listener\r\n */\n on(type, callback, context, shouldClone = true) {\n return this._on(false, type, callback, context, shouldClone, (_type, event) => callback.call(context, event)).disposer;\n }\n /**\r\n * Creates an event listener to be invoked on a specific event type once.\r\n *\r\n * Once the event listener is invoked, it is automatically disposed.\r\n *\r\n * ```TypeScript\r\n * button.events.once(\"click\", (ev) => {\r\n * console.log(\"Button clicked\");\r\n * }, this);\r\n * ```\r\n * ```JavaScript\r\n * button.events.once(\"click\", (ev) => {\r\n * console.log(\"Button clicked\");\r\n * }, this);\r\n * ```\r\n *\r\n * The above will invoke our custom event handler the first time series we\r\n * put event on is hidden.\r\n *\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n * @param shouldClone Whether the listener should be copied when the EventDispatcher is copied\r\n * @returns A disposable event listener\r\n */\n once(type, callback, context, shouldClone = true) {\n const x = this._on(true, type, callback, context, shouldClone, (_type, event) => {\n x.disposer.dispose();\n callback.call(context, event);\n });\n // TODO maybe this should return a different Disposer ?\n return x.disposer;\n }\n /**\r\n * Removes the event listener with specific parameters.\r\n *\r\n * @param type Listener's type\r\n * @param callback Callback function\r\n * @param context Callback context\r\n */\n off(type, callback, context) {\n this._removeExistingListener(false, type, callback, context);\n }\n /**\r\n * Copies all dispatcher parameters, including listeners, from another event\r\n * dispatcher.\r\n *\r\n * @param source Source event dispatcher\r\n * @ignore\r\n */\n copyFrom(source) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n if (source === this) {\n throw new Error(\"Cannot copyFrom the same TargetedEventDispatcher\");\n }\n const disposers = [];\n $array.each(source._listeners, x => {\n // TODO is this correct ?\n if (!x.killed && x.shouldClone) {\n if (x.type === null) {\n disposers.push(this.onAll(x.callback, x.context));\n } else if (x.once) {\n disposers.push(this.once(x.type, x.callback, x.context));\n } else {\n disposers.push(this.on(x.type, x.callback, x.context));\n }\n }\n });\n return new MultiDisposer(disposers);\n }\n}\n/**\r\n * A version of the [[EventDispatcher]] that dispatches events for a specific\r\n * target object.\r\n *\r\n * @ignore\r\n */\nexport class TargetedEventDispatcher extends EventDispatcher {\n /**\r\n * Constructor\r\n *\r\n * @param target Event dispatcher target\r\n */\n constructor(target) {\n super();\n /**\r\n * A target object which is originating events using this dispatcher.\r\n */\n Object.defineProperty(this, \"target\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.target = target;\n }\n /**\r\n * Copies all dispatcher parameters, including listeners, from another event\r\n * dispatcher.\r\n *\r\n * @param source Source event dispatcher\r\n * @ignore\r\n */\n copyFrom(source) {\n if (this._disposed) {\n throw new Error(\"EventDispatcher is disposed\");\n }\n if (source === this) {\n throw new Error(\"Cannot copyFrom the same TargetedEventDispatcher\");\n }\n const disposers = [];\n $array.each(source._listeners, x => {\n // TODO very hacky\n if (x.context === source.target) {\n return;\n }\n // TODO is this correct ?\n if (!x.killed && x.shouldClone) {\n if (x.type === null) {\n disposers.push(this.onAll(x.callback, x.context));\n } else if (x.once) {\n disposers.push(this.once(x.type, x.callback, x.context));\n } else {\n disposers.push(this.on(x.type, x.callback, x.context));\n }\n }\n });\n return new MultiDisposer(disposers);\n }\n}\n","import { __awaiter } from \"tslib\";\nimport { Percent } from \"./Percent\";\nimport { Color } from \"./Color\";\nimport { EventDispatcher } from \"./EventDispatcher\";\nimport * as $object from \"./Object\";\n/**\r\n * @ignore\r\n */\nexport function waitForAnimations(animations) {\n return __awaiter(this, void 0, void 0, function* () {\n if (animations !== undefined) {\n const promises = [];\n $object.each(animations, (_, animation) => {\n promises.push(animation.waitForStop());\n });\n yield Promise.all(promises);\n }\n });\n}\n/**\r\n * @ignore\r\n */\nexport function normalize(value, min, max) {\n if (min === max) {\n return 0;\n } else {\n return Math.min(Math.max((value - min) * (1 / (max - min)), 0), 1);\n }\n}\n/**\r\n * @ignore\r\n */\nexport function range(diff, from, to) {\n return from + diff * (to - from);\n}\n/**\r\n * @ignore\r\n */\nexport function defaultInterpolate(diff, from, to) {\n if (diff >= 1) {\n return to;\n } else {\n return from;\n }\n}\n/**\r\n * @ignore\r\n */\nexport function percentInterpolate(diff, from, to) {\n return new Percent(range(diff, from.percent, to.percent));\n}\n/**\r\n * @ignore\r\n */\nexport function colorInterpolate(diff, from, to) {\n return Color.interpolate(diff, from, to);\n}\n/**\r\n * @ignore\r\n */\nexport function getInterpolate(from, to) {\n if (typeof from === \"number\" && typeof to === \"number\") {\n return range;\n }\n if (from instanceof Percent && to instanceof Percent) {\n return percentInterpolate;\n }\n if (from instanceof Color && to instanceof Color) {\n return colorInterpolate;\n }\n return defaultInterpolate;\n}\nexport var AnimationState = /*#__PURE__*/function (AnimationState) {\n AnimationState[AnimationState[\"Stopped\"] = 0] = \"Stopped\";\n AnimationState[AnimationState[\"Playing\"] = 1] = \"Playing\";\n AnimationState[AnimationState[\"Paused\"] = 2] = \"Paused\";\n return AnimationState;\n}(AnimationState || {});\n/**\r\n * @ignore\r\n */\nexport class AnimationTime {\n constructor(entity, duration) {\n Object.defineProperty(this, \"_entity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_duration\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_playingDuration\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"_startingTime\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"_current\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_from\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_to\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new EventDispatcher()\n });\n Object.defineProperty(this, \"easing\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._entity = entity;\n this._duration = duration;\n }\n _stopEvent() {\n const type = \"stopped\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this\n });\n }\n }\n _runAnimation(currentTime) {\n if (this._playingDuration !== null) {\n if (this._startingTime === null) {\n this._startingTime = currentTime;\n return AnimationState.Playing;\n } else {\n const diff = (currentTime - this._startingTime) / this._playingDuration;\n if (diff >= 1) {\n this._playingDuration = null;\n this._startingTime = null;\n this._from = this._to;\n this._current = this._to;\n this._entity.markDirty();\n this._stopEvent();\n const type = \"ended\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this\n });\n }\n return AnimationState.Stopped;\n } else {\n this._current = range(diff, this._from, this._to);\n this._entity.markDirty();\n const type = \"progress\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this,\n progress: diff\n });\n }\n return AnimationState.Playing;\n }\n }\n } else {\n return AnimationState.Stopped;\n }\n }\n _play() {\n this._from = this._current;\n if (this._playingDuration === null) {\n this._entity._root._addAnimation(this);\n const type = \"started\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this\n });\n }\n } else {\n this._startingTime = null;\n }\n this._playingDuration = Math.abs(this._to - this._from) * this._duration;\n }\n get duration() {\n return this._duration;\n }\n set duration(value) {\n if (this._duration !== value) {\n this._duration = value;\n if (value === 0) {\n this.jumpTo(this._to);\n } else if (this._current !== this._to) {\n this._play();\n }\n }\n }\n get current() {\n if (this.easing) {\n return this.easing(this._current);\n } else {\n return this._current;\n }\n }\n stop() {\n this.jumpTo(this._current);\n }\n jumpTo(value) {\n if (this._current !== value) {\n this._entity.markDirty();\n }\n if (this._playingDuration !== null) {\n this._stopEvent();\n }\n this._playingDuration = null;\n this._startingTime = null;\n this._current = value;\n this._from = value;\n this._to = value;\n }\n tweenTo(value) {\n if (this._current === value || this._duration === 0) {\n this.jumpTo(value);\n } else {\n if (this._to !== value) {\n this._to = value;\n this._play();\n }\n }\n }\n}\n/*export class AnimationValue extends AnimationTime {\r\n public _min: number;\r\n public _max: number;\r\n\r\n constructor(entity: Entity, duration: number, min: number, max: number) {\r\n super(entity, duration);\r\n this._min = min;\r\n this._max = max;\r\n }\r\n\r\n public get min(): number {\r\n return this._min;\r\n }\r\n\r\n public set min(value: number) {\r\n if (this._min !== value) {\r\n this._min = value;\r\n this._entity.markDirty();\r\n }\r\n }\r\n\r\n public get max(): number {\r\n return this._max;\r\n }\r\n\r\n public set max(value: number) {\r\n if (this._max !== value) {\r\n this._max = value;\r\n this._entity.markDirty();\r\n }\r\n }\r\n\r\n public currentValue(): number {\r\n return range(super.currentTime(), this._min, this._max);\r\n }\r\n\r\n public jumpToValue(value: number) {\r\n super.jumpToTime(normalize(value, this._min, this._max));\r\n }\r\n\r\n public tweenToValue(value: number) {\r\n super.tweenToTime(normalize(value, this._min, this._max));\r\n }\r\n}\r\n*/\n","import { EventDispatcher } from \"./EventDispatcher\";\nimport * as $array from \"./Array\";\n/**\r\n * Checks if specific index fits into length.\r\n *\r\n * @param index Index\r\n * @param len Length\r\n * @ignore\r\n */\nfunction checkBounds(index, len) {\n if (!(index >= 0 && index < len)) {\n throw new Error(\"Index out of bounds: \" + index);\n }\n}\n/**\r\n * A List class is used to hold a number of indexed items of the same type.\r\n */\nexport class List {\n /**\r\n * Constructor\r\n *\r\n * @param initial Inital list of values to add to list\r\n */\n constructor(initial = []) {\n /**\r\n * List values.\r\n */\n Object.defineProperty(this, \"_values\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new EventDispatcher()\n });\n this._values = initial;\n }\n /**\r\n * An array of values in the list.\r\n *\r\n * Do not use this property to add values. Rather use dedicated methods, like\r\n * `push()`, `removeIndex()`, etc.\r\n *\r\n * @readonly\r\n * @return List values\r\n */\n get values() {\n return this._values;\n }\n /**\r\n * Checks if list contains specific item reference.\r\n *\r\n * @param item Item to search for\r\n * @return `true` if found, `false` if not found\r\n */\n contains(value) {\n return this._values.indexOf(value) !== -1;\n }\n /**\r\n * Removes specific item from the list.\r\n *\r\n * @param item An item to remove\r\n */\n removeValue(value) {\n let i = 0;\n let length = this._values.length;\n while (i < length) {\n // TODO handle NaN\n if (this._values[i] === value) {\n this.removeIndex(i);\n --length;\n } else {\n ++i;\n }\n }\n }\n /**\r\n * Searches the list for specific item and returns its index.\r\n *\r\n * @param item An item to search for\r\n * @return Index or -1 if not found\r\n */\n indexOf(value) {\n return $array.indexOf(this._values, value);\n }\n /**\r\n * Number of items in list.\r\n *\r\n * @readonly\r\n * @return Number of items\r\n */\n get length() {\n return this._values.length;\n }\n /**\r\n * Checks if there's a value at specific index.\r\n *\r\n * @param index Index\r\n * @return Value exists?\r\n */\n hasIndex(index) {\n return index >= 0 && index < this._values.length;\n }\n /**\r\n * Returns an item at specified index.\r\n *\r\n * @param index Index\r\n * @return List item\r\n */\n getIndex(index) {\n return this._values[index];\n }\n _onPush(newValue) {\n if (this.events.isEnabled(\"push\")) {\n this.events.dispatch(\"push\", {\n type: \"push\",\n target: this,\n newValue\n });\n }\n }\n _onInsertIndex(index, newValue) {\n if (this.events.isEnabled(\"insertIndex\")) {\n this.events.dispatch(\"insertIndex\", {\n type: \"insertIndex\",\n target: this,\n index,\n newValue\n });\n }\n }\n _onSetIndex(index, oldValue, newValue) {\n if (this.events.isEnabled(\"setIndex\")) {\n this.events.dispatch(\"setIndex\", {\n type: \"setIndex\",\n target: this,\n index,\n oldValue,\n newValue\n });\n }\n }\n _onRemoveIndex(index, oldValue) {\n if (this.events.isEnabled(\"removeIndex\")) {\n this.events.dispatch(\"removeIndex\", {\n type: \"removeIndex\",\n target: this,\n index,\n oldValue\n });\n }\n }\n _onMoveIndex(oldIndex, newIndex, value) {\n if (this.events.isEnabled(\"moveIndex\")) {\n this.events.dispatch(\"moveIndex\", {\n type: \"moveIndex\",\n target: this,\n oldIndex,\n newIndex,\n value\n });\n }\n }\n _onClear(oldValues) {\n if (this.events.isEnabled(\"clear\")) {\n this.events.dispatch(\"clear\", {\n type: \"clear\",\n target: this,\n oldValues\n });\n }\n }\n /**\r\n * Sets value at specific index.\r\n *\r\n * If there's already a value at the index, it is overwritten.\r\n *\r\n * @param index Index\r\n * @param value New value\r\n * @return New value\r\n */\n setIndex(index, value) {\n checkBounds(index, this._values.length);\n const oldValue = this._values[index];\n // Do nothing if the old value and the new value are the same\n if (oldValue !== value) {\n this._values[index] = value;\n this._onSetIndex(index, oldValue, value);\n }\n return oldValue;\n }\n /**\r\n * Adds an item to the list at a specific index, which pushes all the other\r\n * items further down the list.\r\n *\r\n * @param index Index\r\n * @param item An item to add\r\n */\n insertIndex(index, value) {\n checkBounds(index, this._values.length + 1);\n $array.insertIndex(this._values, index, value);\n this._onInsertIndex(index, value);\n return value;\n }\n /**\r\n * Swaps indexes of two items in the list.\r\n *\r\n * @param a Item 1\r\n * @param b Item 2\r\n */\n swap(a, b) {\n const len = this._values.length;\n checkBounds(a, len);\n checkBounds(b, len);\n if (a !== b) {\n const value_a = this._values[a];\n const value_b = this._values[b];\n this._values[a] = value_b;\n this._onSetIndex(a, value_a, value_b);\n this._values[b] = value_a;\n this._onSetIndex(b, value_b, value_a);\n }\n }\n /**\r\n * Removes a value at specific index.\r\n *\r\n * @param index Index of value to remove\r\n * @return Removed value\r\n */\n removeIndex(index) {\n checkBounds(index, this._values.length);\n const oldValue = this._values[index];\n $array.removeIndex(this._values, index);\n this._onRemoveIndex(index, oldValue);\n return oldValue;\n }\n /**\r\n * Moves an item to a specific index within the list.\r\n *\r\n * If the index is not specified it will move the item to the end of the\r\n * list.\r\n *\r\n * @param value Item to move\r\n * @param index Index to place item at\r\n */\n moveValue(value, toIndex) {\n // TODO don't do anything if the desired index is the same as the current index\n let index = this.indexOf(value);\n // TODO remove all old values rather than only the first ?\n if (index !== -1) {\n $array.removeIndex(this._values, index);\n if (toIndex == null) {\n const toIndex = this._values.length;\n this._values.push(value);\n this._onMoveIndex(index, toIndex, value);\n } else {\n $array.insertIndex(this._values, toIndex, value);\n this._onMoveIndex(index, toIndex, value);\n }\n } else if (toIndex == null) {\n this._values.push(value);\n this._onPush(value);\n } else {\n $array.insertIndex(this._values, toIndex, value);\n this._onInsertIndex(toIndex, value);\n }\n return value;\n }\n /**\r\n * Adds an item to the end of the list.\r\n *\r\n * @param item An item to add\r\n */\n push(value) {\n this._values.push(value);\n this._onPush(value);\n return value;\n }\n /**\r\n * Adds an item as a first item in the list.\r\n *\r\n * @param item An item to add\r\n */\n unshift(value) {\n this.insertIndex(0, value);\n return value;\n }\n /**\r\n * Adds multiple items to the list.\r\n *\r\n * @param items An Array of items to add\r\n */\n pushAll(values) {\n $array.each(values, value => {\n this.push(value);\n });\n }\n /**\r\n * Copies and adds items from abother list.\r\n *\r\n * @param source A list top copy items from\r\n */\n copyFrom(source) {\n this.pushAll(source._values);\n }\n /**\r\n * Returns the last item from the list, and removes it.\r\n *\r\n * @return Item\r\n */\n pop() {\n let index = this._values.length - 1;\n return index < 0 ? undefined : this.removeIndex(this._values.length - 1);\n }\n /**\r\n * Returns the first item from the list, and removes it.\r\n *\r\n * @return Item\r\n */\n shift() {\n return this._values.length ? this.removeIndex(0) : undefined;\n }\n /**\r\n * Sets multiple items to the list.\r\n *\r\n * All current items are removed.\r\n *\r\n * @param newArray New items\r\n */\n setAll(newArray) {\n const old = this._values;\n this._values = [];\n this._onClear(old);\n $array.each(newArray, value => {\n this._values.push(value);\n this._onPush(value);\n });\n }\n /**\r\n * Removes all items from the list.\r\n */\n clear() {\n this.setAll([]);\n }\n /**\r\n * Returns an ES6 iterator for the list.\r\n */\n *[Symbol.iterator]() {\n const length = this._values.length;\n for (let i = 0; i < length; ++i) {\n yield this._values[i];\n }\n }\n /**\r\n * Calls `f` for each element in the list.\r\n *\r\n * `f` should have at least one parameter defined which will get a current\r\n * item, with optional second argument - index.\r\n */\n each(f) {\n $array.each(this._values, f);\n }\n /**\r\n * Calls `f` for each element in the list, from right to left.\r\n *\r\n * `f` should have at least one parameter defined which will get a current\r\n * item, with optional second argument - index.\r\n */\n eachReverse(f) {\n $array.eachReverse(this._values, f);\n }\n}\n/**\r\n * A version of a [[List]] where the elements are disposed automatically when\r\n * removed from the list, unless `autoDispose` is set to `false`.\r\n */\nexport class ListAutoDispose extends List {\n constructor() {\n super(...arguments);\n /**\r\n * Automatically disposes elements that are removed from the list.\r\n *\r\n * @default true\r\n */\n Object.defineProperty(this, \"autoDispose\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n _onSetIndex(index, oldValue, newValue) {\n if (this.autoDispose) {\n oldValue.dispose();\n }\n super._onSetIndex(index, oldValue, newValue);\n }\n _onRemoveIndex(index, oldValue) {\n if (this.autoDispose) {\n oldValue.dispose();\n }\n super._onRemoveIndex(index, oldValue);\n }\n _onClear(oldValues) {\n if (this.autoDispose) {\n $array.each(oldValues, x => {\n x.dispose();\n });\n }\n super._onClear(oldValues);\n }\n _dispose() {\n if (this.autoDispose) {\n $array.each(this._values, x => {\n x.dispose();\n });\n }\n }\n isDisposed() {\n return this._disposed;\n }\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._dispose();\n }\n }\n}\n/**\r\n * A version of a [[List]] that is able to create new elements as well as\r\n * apply additional settings to newly created items.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/list-templates/} for more info\r\n */\nexport class ListTemplate extends ListAutoDispose {\n constructor(template, make) {\n super();\n Object.defineProperty(this, \"template\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"make\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.template = template;\n this.make = make;\n }\n _dispose() {\n super._dispose();\n if (this.autoDispose) {\n this.template.dispose();\n }\n }\n}\n","import { List } from \"./List\";\nimport * as $array from \"./Array\";\n/**\r\n * A version of [[List]] to hold children of the [[Container]].\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/containers/} for more info\r\n */\nexport class Children extends List {\n constructor(container) {\n super();\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_container\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._container = container;\n this._events = this.events.onAll(change => {\n if (change.type === \"clear\") {\n $array.each(change.oldValues, x => {\n this._onRemoved(x);\n });\n } else if (change.type === \"push\") {\n this._onInserted(change.newValue);\n } else if (change.type === \"setIndex\") {\n this._onRemoved(change.oldValue);\n this._onInserted(change.newValue, change.index);\n } else if (change.type === \"insertIndex\") {\n this._onInserted(change.newValue, change.index);\n } else if (change.type === \"removeIndex\") {\n this._onRemoved(change.oldValue);\n } else if (change.type === \"moveIndex\") {\n this._onRemoved(change.value);\n this._onInserted(change.value, change.newIndex);\n } else {\n throw new Error(\"Unknown IListEvent type\");\n }\n });\n }\n _onInserted(child, index) {\n child._setParent(this._container, true);\n const childrenDisplay = this._container._childrenDisplay;\n if (index === undefined) {\n childrenDisplay.addChild(child._display);\n } else {\n childrenDisplay.addChildAt(child._display, index);\n }\n }\n _onRemoved(child) {\n this._container._childrenDisplay.removeChild(child._display);\n this._container.markDirtyBounds();\n this._container.markDirty();\n }\n /**\r\n * Returns `true` if obejct is disposed.\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Permanently dispose this object.\r\n */\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._events.dispose();\n $array.each(this.values, child => {\n child.dispose();\n });\n }\n }\n}\n","import { isNumber } from \"./Type\";\n/**\r\n * ============================================================================\r\n * CONSTANTS\r\n * ============================================================================\r\n * @hidden\r\n */\nexport const PI = Math.PI;\nexport const HALFPI = PI / 2;\nexport const RADIANS = PI / 180;\nexport const DEGREES = 180 / PI;\n/**\r\n * Rounds the numeric value to whole number or specific precision of set.\r\n *\r\n * @param value Value\r\n * @param precision Precision (number of decimal points)\r\n * @param floor In case value ends with 0.5 and precision is 0, we might need to floor the value instead of ceiling it.\r\n * @return Rounded value\r\n */\nexport function round(value, precision, floor) {\n if (!isNumber(precision) || precision <= 0) {\n let rounded = Math.round(value);\n if (floor) {\n if (rounded - value == 0.5) {\n rounded--;\n }\n }\n return rounded;\n } else {\n let d = Math.pow(10, precision);\n return Math.round(value * d) / d;\n }\n}\n/**\r\n * Ceils the numeric value to whole number or specific precision of set.\r\n *\r\n * @param value Value\r\n * @param precision Precision (number of decimal points)\r\n * @return Rounded value\r\n */\nexport function ceil(value, precision) {\n if (!isNumber(precision) || precision <= 0) {\n return Math.ceil(value);\n } else {\n let d = Math.pow(10, precision);\n return Math.ceil(value * d) / d;\n }\n}\n/**\r\n * [getCubicControlPointA description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n * @param p0 [description]\r\n * @param p1 [description]\r\n * @param p2 [description]\r\n * @param p3 [description]\r\n * @param tensionX [description]\r\n * @param tensionY [description]\r\n * @return [description]\r\n */\nexport function getCubicControlPointA(p0, p1, p2, tensionX, tensionY) {\n return {\n x: (-p0.x + p1.x / tensionX + p2.x) * tensionX,\n y: (-p0.y + p1.y / tensionY + p2.y) * tensionY\n };\n}\n/**\r\n * [getCubicControlPointB description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n * @param p0 [description]\r\n * @param p1 [description]\r\n * @param p2 [description]\r\n * @param p3 [description]\r\n * @param tensionX [description]\r\n * @param tensionY [description]\r\n * @return [description]\r\n */\nexport function getCubicControlPointB(p1, p2, p3, tensionX, tensionY) {\n return {\n x: (p1.x + p2.x / tensionX - p3.x) * tensionX,\n y: (p1.y + p2.y / tensionY - p3.y) * tensionY\n };\n}\nexport function fitToRange(value, min, max) {\n return Math.min(Math.max(value, min), max);\n}\n/**\r\n * Returns sine of an angle specified in degrees.\r\n *\r\n * @param value Value\r\n * @return Sine\r\n */\nexport function sin(angle) {\n return Math.sin(RADIANS * angle);\n}\n/**\r\n * Returns tan of an angle specified in degrees.\r\n *\r\n * @param value Value\r\n * @return Sine\r\n */\nexport function tan(angle) {\n return Math.tan(RADIANS * angle);\n}\n/**\r\n * Returns cosine of an angle specified in degrees.\r\n *\r\n * @param value Value\r\n * @return Cosine\r\n */\nexport function cos(angle) {\n return Math.cos(RADIANS * angle);\n}\n// 0 to 360\nexport function normalizeAngle(value) {\n value = value % 360;\n if (value < 0) {\n value += 360;\n }\n return value;\n}\n// TODO this doesn't work properly for skewing, and it's probably broken for rotation too\nexport function getArcBounds(cx, cy, startAngle, endAngle, radius) {\n let minX = Number.MAX_VALUE;\n let minY = Number.MAX_VALUE;\n let maxX = -Number.MAX_VALUE;\n let maxY = -Number.MAX_VALUE;\n let bpoints = [];\n bpoints.push(getArcPoint(radius, startAngle));\n bpoints.push(getArcPoint(radius, endAngle));\n let fromAngle = Math.min(Math.floor(startAngle / 90) * 90, Math.floor(endAngle / 90) * 90);\n let toAngle = Math.max(Math.ceil(startAngle / 90) * 90, Math.ceil(endAngle / 90) * 90);\n for (let angle = fromAngle; angle <= toAngle; angle += 90) {\n if (angle >= startAngle && angle <= endAngle) {\n bpoints.push(getArcPoint(radius, angle));\n }\n }\n for (let i = 0; i < bpoints.length; i++) {\n let pt = bpoints[i];\n if (pt.x < minX) {\n minX = pt.x;\n }\n if (pt.y < minY) {\n minY = pt.y;\n }\n if (pt.x > maxX) {\n maxX = pt.x;\n }\n if (pt.y > maxY) {\n maxY = pt.y;\n }\n }\n return {\n left: cx + minX,\n top: cy + minY,\n right: cx + maxX,\n bottom: cy + maxY\n };\n}\n/**\r\n * Returns point on arc\r\n *\r\n * @param center point\r\n * @param radius\r\n * @param arc\r\n * @return {boolean}\r\n */\nexport function getArcPoint(radius, arc) {\n return {\n x: radius * cos(arc),\n y: radius * sin(arc)\n };\n}\nexport function mergeBounds(bounds) {\n const len = bounds.length;\n if (len > 0) {\n let bound = bounds[0];\n let left = bound.left;\n let top = bound.top;\n let right = bound.right;\n let bottom = bound.bottom;\n if (len > 1) {\n for (let i = 1; i < len; i++) {\n bound = bounds[i];\n left = Math.min(bound.left, left);\n right = Math.max(bound.right, right);\n top = Math.min(bound.top, top);\n bottom = Math.max(bound.bottom, bottom);\n }\n }\n return {\n left,\n right,\n top,\n bottom\n };\n }\n return {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n };\n}\nexport function fitAngleToRange(value, startAngle, endAngle) {\n if (startAngle > endAngle) {\n let temp = startAngle;\n startAngle = endAngle;\n endAngle = temp;\n }\n value = normalizeAngle(value);\n let count = (startAngle - normalizeAngle(startAngle)) / 360;\n if (value < startAngle) {\n value += 360 * (count + 1);\n }\n let maxEnd = startAngle + (endAngle - startAngle) / 2 + 180;\n let maxStart = startAngle + (endAngle - startAngle) / 2 - 180;\n if (value > endAngle) {\n if (value - 360 > startAngle) {\n value -= 360;\n } else {\n if (value < maxEnd) {\n value = endAngle;\n } else {\n value = startAngle;\n }\n }\n }\n if (value < startAngle) {\n if (value > maxStart) {\n value = startAngle;\n } else {\n value = endAngle;\n }\n }\n return value;\n}\nexport function inBounds(point, bounds) {\n if (point.x >= bounds.left && point.y >= bounds.top && point.x <= bounds.right && point.y <= bounds.bottom) {\n return true;\n }\n return false;\n}\nexport function getAngle(point1, point2) {\n if (!point2) {\n point2 = {\n x: point1.x * 2,\n y: point1.y * 2\n };\n }\n let diffX = point2.x - point1.x;\n let diffY = point2.y - point1.y;\n let angle = Math.atan2(diffY, diffX) * DEGREES;\n if (angle < 0) {\n angle += 360;\n }\n return normalizeAngle(angle);\n}\n/**\r\n * [getPointOnQuadraticCurve description]\r\n *\r\n * @ignore Exclude from docs\r\n * @todo Description\r\n * @param pointA [description]\r\n * @param pointB [description]\r\n * @param controlPoint [description]\r\n * @param position [description]\r\n * @return [description]\r\n */\nexport function getPointOnQuadraticCurve(pointA, pointB, controlPoint, position) {\n let x = (1 - position) * (1 - position) * pointA.x + 2 * (1 - position) * position * controlPoint.x + position * position * pointB.x;\n let y = (1 - position) * (1 - position) * pointA.y + 2 * (1 - position) * position * controlPoint.y + position * position * pointB.y;\n return {\n x: x,\n y: y\n };\n}\nexport function getPointOnLine(pointA, pointB, position) {\n return {\n x: pointA.x + (pointB.x - pointA.x) * position,\n y: pointA.y + (pointB.y - pointA.y) * position\n };\n}\n/**\r\n * Returns the closest value from the array of values to the reference value.\r\n *\r\n * @param values Array of values\r\n * @param value Reference value\r\n * @return Closes value from the array\r\n */\nexport function closest(values, referenceValue) {\n return values.reduce(function (prev, curr) {\n return Math.abs(curr - referenceValue) < Math.abs(prev - referenceValue) ? curr : prev;\n });\n}\n/**\r\n * Returns true if bounds overlap\r\n * @param bounds1 IBounds\r\n * @param bounds2 IBounds\r\n * @returns boolean\r\n */\nexport function boundsOverlap(bounds1, bounds2) {\n const horizontalOverlap = bounds1.left < bounds2.right && bounds1.right > bounds2.left;\n const verticalOverlap = bounds1.top < bounds2.bottom && bounds1.bottom > bounds2.top;\n return horizontalOverlap && verticalOverlap;\n}\n/**\r\n * Generates points of a spiral\r\n * @param cx\r\n * @param cy\r\n * @param radius\r\n * @param radiusY\r\n * @param innerRadius\r\n * @param step\r\n * @param radiusStep\r\n * @param startAngle\r\n * @param endAngle\r\n * @returns IPoint[]\r\n */\nexport function spiralPoints(cx, cy, radius, radiusY, innerRadius, step, radiusStep, startAngle, endAngle) {\n let r = innerRadius + 0.01;\n let angle = startAngle * RADIANS;\n let points = [];\n while (r < radius + radiusStep) {\n let stepSize = step;\n if (stepSize / 2 > r) {\n stepSize = 2 * r;\n }\n angle += 2 * Math.asin(stepSize / 2 / r);\n if (angle * DEGREES > endAngle + (radius - innerRadius) / radiusStep * 360) {\n break;\n }\n let degrees = angle * DEGREES;\n let point = {\n x: cx + r * Math.cos(angle),\n y: cy + r * radiusY / radius * Math.sin(angle)\n };\n points.push(point);\n r = innerRadius + degrees / 360 * radiusStep;\n }\n points.shift();\n return points;\n}\n/**\r\n * Returns true if circles overlap\r\n * @param circle1\r\n * @param circle2\r\n * @returns boolean\r\n */\nexport function circlesOverlap(circle1, circle2) {\n return Math.hypot(circle1.x - circle2.x, circle1.y - circle2.y) <= circle1.radius + circle2.radius;\n}\n","/**\r\n * A collection of easing functions\r\n *\r\n * Parts of this collection are taken from D3.js library (https://d3js.org/)\r\n */\n/**\r\n * ============================================================================\r\n * IMPORTS\r\n * ============================================================================\r\n * @hidden\r\n */\nimport * as $math from \"./Math\";\n/**\r\n * The functions below are from D3.js library (https://d3js.org/)\r\n *\r\n * ----------------------------------------------------------------------------\r\n * Copyright 2017 Mike Bostock\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are met:\r\n *\r\n * 1. Redistributions of source code must retain the above copyright notice,\r\n *\tthis list of conditions and the following disclaimer.\r\n *\r\n * 2. Redistributions in binary form must reproduce the above copyright notice,\r\n *\tthis list of conditions and the following disclaimer in the documentation\r\n *\tand/or other materials provided with the distribution.\r\n *\r\n * 3. Neither the name of the copyright holder nor the names of its\r\n *\tcontributors may be used to endorse or promote products derived from this\r\n *\tsoftware without specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r\n * POSSIBILITY OF SUCH DAMAGE.\r\n * ----------------------------------------------------------------------------\r\n * @hidden\r\n */\n/**\r\n */\nexport function linear(t) {\n return t;\n}\nexport function quad(t) {\n return t * t;\n}\nexport function cubic(t) {\n return t * t * t;\n}\nexport function pow(t, e) {\n return Math.pow(t, e);\n}\nexport function exp(t) {\n return Math.pow(2, 10 * t - 10);\n}\nexport function sine(t) {\n return 1 - Math.cos(t * $math.HALFPI);\n}\nexport function circle(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n/**\r\n * ============================================================================\r\n * TRANSFORMERS\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n */\nexport function yoyo(ease) {\n return function (t) {\n if (t < 0.5) {\n return ease(t * 2.0);\n } else {\n return ease((1.0 - t) * 2.0);\n }\n };\n}\nexport function out(ease) {\n return function (t) {\n return 1.0 - ease(1.0 - t);\n };\n}\nexport function inOut(ease) {\n return function (t) {\n if (t <= 0.5) {\n return ease(t * 2.0) / 2.0;\n } else {\n return 1.0 - ease((1.0 - t) * 2.0) / 2.0;\n }\n };\n}\n/**\r\n * ============================================================================\r\n * BOUNCE\r\n * ============================================================================\r\n * @hidden\r\n */\nlet b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\nexport function bounce(t) {\n return 1 - bounceOut(1 - t);\n}\n/**\r\n * @ignore\r\n */\nfunction bounceOut(t) {\n t = t;\n if (t < b1) {\n return b0 * t * t;\n } else if (t < b3) {\n return b0 * (t -= b2) * t + b4;\n } else if (t < b6) {\n return b0 * (t -= b5) * t + b7;\n } else {\n return b0 * (t -= b8) * t + b9;\n }\n}\n/**\r\n * ============================================================================\r\n * ELASTIC\r\n * ============================================================================\r\n * @hidden\r\n */\n/**\r\n * @ignore\r\n */\nlet tau = 2 * Math.PI;\n/**\r\n * @ignore\r\n */\nlet amplitude = 1;\n/**\r\n * @ignore\r\n */\nlet period = 0.3 / tau;\n/**\r\n * @ignore\r\n */\nlet s = Math.asin(1 / amplitude) * period;\nexport function elastic(t) {\n let v = t;\n return amplitude * Math.pow(2, 10 * --v) * Math.sin((s - v) / period);\n}\n","import * as $object from \"./Object\";\nimport * as $ease from \"./Ease\";\n/**\r\n * An object representing a collection of setting values to apply as required.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/states/} for more info\r\n */\nexport class State {\n constructor(entity, settings) {\n Object.defineProperty(this, \"_entity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_settings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_userSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n this._entity = entity;\n this._settings = settings;\n $object.each(settings, key => {\n this._userSettings[key] = true;\n });\n }\n get(key, fallback) {\n const value = this._settings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n /**\r\n * @ignore\r\n */\n setRaw(key, value) {\n this._settings[key] = value;\n }\n /**\r\n * Sets a setting `value` for the specified `key` to be set when the state\r\n * is applied.\r\n *\r\n * @param key Setting key\r\n * @param value Setting value\r\n * @return Setting value\r\n */\n set(key, value) {\n this._userSettings[key] = true;\n this.setRaw(key, value);\n }\n /**\r\n * Removes a setting value for the specified `key`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n */\n remove(key) {\n delete this._userSettings[key];\n delete this._settings[key];\n }\n /**\r\n * Sets multiple settings at once.\r\n *\r\n * `settings` must be an object with key: value pairs.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param settings Settings\r\n */\n setAll(settings) {\n $object.keys(settings).forEach(key => {\n this.set(key, settings[key]);\n });\n }\n _eachSetting(f) {\n $object.each(this._settings, f);\n }\n /**\r\n * Applies the state to the target element.\r\n *\r\n * All setting values are set immediately.\r\n */\n apply() {\n const seen = {};\n seen[\"stateAnimationEasing\"] = true;\n seen[\"stateAnimationDuration\"] = true;\n const defaultState = this._entity.states.lookup(\"default\");\n this._eachSetting((key, value) => {\n if (!seen[key]) {\n seen[key] = true;\n // save values to default state\n if (this !== defaultState) {\n if (!(key in defaultState._settings)) {\n defaultState._settings[key] = this._entity.get(key);\n }\n }\n this._entity.set(key, value);\n }\n });\n }\n /**\r\n * Applies the state to the target element.\r\n *\r\n * Returns an object representing all [[Animation]] objects created for\r\n * each setting key transition.\r\n *\r\n * @return Animations\r\n */\n applyAnimate(duration) {\n if (duration == null) {\n duration = this._settings.stateAnimationDuration;\n }\n if (duration == null) {\n duration = this.get(\"stateAnimationDuration\", this._entity.get(\"stateAnimationDuration\", 0));\n }\n let easing = this._settings.stateAnimationEasing;\n if (easing == null) {\n easing = this.get(\"stateAnimationEasing\", this._entity.get(\"stateAnimationEasing\", $ease.cubic));\n }\n const defaultState = this._entity.states.lookup(\"default\");\n const seen = {};\n seen[\"stateAnimationEasing\"] = true;\n seen[\"stateAnimationDuration\"] = true;\n const animations = {};\n this._eachSetting((key, value) => {\n if (!seen[key]) {\n seen[key] = true;\n // save values to default state\n if (this != defaultState) {\n if (!(key in defaultState._settings)) {\n defaultState._settings[key] = this._entity.get(key);\n }\n }\n const animation = this._entity.animate({\n key: key,\n to: value,\n duration: duration,\n easing: easing\n });\n if (animation) {\n animations[key] = animation;\n }\n }\n });\n return animations;\n }\n}\n/**\r\n * Collection of [[State]] objects for an element.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/states/} for more info\r\n */\nexport class States {\n constructor(entity) {\n Object.defineProperty(this, \"_states\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_entity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._entity = entity;\n }\n /**\r\n * Checks if a state by `name` exists. Returns it there is one.\r\n *\r\n * @param name State name\r\n * @return State\r\n */\n lookup(name) {\n return this._states[name];\n }\n /**\r\n * Sets supplied `settings` on a state by the `name`.\r\n *\r\n * If such state does not yet exists, it is created.\r\n *\r\n * @param name State name\r\n * @param settings Settings\r\n * @return New State\r\n */\n create(name, settings) {\n const state = this._states[name];\n if (state) {\n state.setAll(settings);\n return state;\n } else {\n const state = new State(this._entity, settings);\n this._states[name] = state;\n return state;\n }\n }\n /**\r\n * Removes the state called `name`.\r\n *\r\n * @param name State name\r\n */\n remove(name) {\n delete this._states[name];\n }\n /**\r\n * Applies a named state to the target element.\r\n *\r\n * @param newState State name\r\n */\n apply(newState) {\n const state = this._states[newState];\n if (state) {\n state.apply();\n }\n this._entity._applyState(newState);\n }\n /**\r\n * Applies a named state to the element.\r\n *\r\n * Returns an object representing all [[Animation]] objects created for\r\n * each setting key transition.\r\n *\r\n * @param newState State name\r\n * @return Animations\r\n */\n applyAnimate(newState, duration) {\n let animations;\n const state = this._states[newState];\n if (state) {\n animations = state.applyAnimate(duration);\n }\n this._entity._applyStateAnimated(newState, duration);\n return animations;\n }\n}\n","/**\r\n * @ignore\r\n */\nexport class Registry {\n constructor() {\n /**\r\n * Currently running version of amCharts.\r\n */\n Object.defineProperty(this, \"version\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"5.10.7\"\n });\n /**\r\n * List of applied licenses.\r\n * @ignore\r\n */\n Object.defineProperty(this, \"licenses\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n /**\r\n * Entities that have their `id` setting set.\r\n */\n Object.defineProperty(this, \"entitiesById\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n /**\r\n * All created [[Root]] elements.\r\n */\n Object.defineProperty(this, \"rootElements\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n }\n}\n/**\r\n * @ignore\r\n */\nexport const registry = new Registry();\n/**\r\n * Adds a license, e.g.:\r\n *\r\n * ```TypeScript\r\n * am5.addLicense(\"xxxxxxxx\");\r\n * ```\r\n * ```JavaScript\r\n * am5.addLicense(\"xxxxxxxx\");\r\n * ```\r\n *\r\n * Multiple licenses can be added to cover for multiple products.\r\n *\r\n * @param license License key\r\n */\nexport function addLicense(license) {\n registry.licenses.push(license);\n}\n/**\r\n * Disposes all [[Root]] elements.\r\n */\nexport function disposeAllRootElements() {\n let root;\n while (root = registry.rootElements.pop()) {\n root.dispose();\n }\n}\n/**\r\n * Finds and returns a `Root` element assigned to a container with `id`.\r\n *\r\n * @param id Container ID\r\n * @return Root\r\n * @since 5.9.2\r\n */\nexport function getRootById(id) {\n let found;\n registry.rootElements.forEach(item => {\n if (item.dom.id == id) {\n found = item;\n }\n });\n return found;\n}\n","/**\r\n * @ignore\r\n */\nexport function compare(left, right) {\n if (left === right) {\n return 0;\n } else if (left < right) {\n return -1;\n } else {\n return 1;\n }\n}\n/**\r\n * @ignore\r\n */\nexport function compareArray(left, right, f) {\n const leftLength = left.length;\n const rightLength = right.length;\n const length = Math.min(leftLength, rightLength);\n for (let i = 0; i < length; ++i) {\n const order = f(left[i], right[i]);\n if (order !== 0) {\n return order;\n }\n }\n return compare(leftLength, rightLength);\n}\n/**\r\n * @ignore\r\n */\nexport function reverse(order) {\n if (order < 0) {\n return 1;\n } else if (order > 0) {\n return -1;\n } else {\n return 0;\n }\n}\n/**\r\n * @ignore\r\n */\nexport function compareNumber(a, b) {\n if (a === b) {\n return 0;\n } else if (a < b) {\n return -1;\n } else {\n return 1;\n }\n}\n","import { Disposer } from \"./Disposer\";\nimport { EventDispatcher } from \"./EventDispatcher\";\nimport { AnimationState, getInterpolate } from \"./Animation\";\nimport { States } from \"./States\";\nimport { registry } from \"../Registry\";\nimport * as $object from \"./Object\";\nimport * as $ease from \"./Ease\";\nimport * as $array from \"./Array\";\nimport * as $order from \"./Order\";\n/**\r\n * Allows to dynamically modify setting value of its target element.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/adapters/} for more info\r\n */\nexport class Adapters {\n constructor(entity) {\n Object.defineProperty(this, \"_entity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_callbacks\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_disabled\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n this._entity = entity;\n }\n /**\r\n * Add a function (`callback`) that will modify value for setting `key`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/adapters/} for more info\r\n */\n add(key, callback) {\n let callbacks = this._callbacks[key];\n if (callbacks === undefined) {\n callbacks = this._callbacks[key] = [];\n }\n callbacks.push(callback);\n this._entity._markDirtyKey(key);\n return new Disposer(() => {\n if ($array.removeFirst(callbacks, callback)) {\n this._entity._markDirtyKey(key);\n }\n });\n }\n /**\r\n * Removes all adapters for the specific key.\r\n *\r\n * @since 5.1.0\r\n */\n remove(key) {\n const callbacks = this._callbacks[key];\n if (callbacks !== undefined) {\n delete this._callbacks[key];\n if (callbacks.length !== 0) {\n this._entity._markDirtyKey(key);\n }\n }\n }\n /**\r\n * Enables (previously disabled) adapters for specific key.\r\n *\r\n * @since 5.1.0\r\n */\n enable(key) {\n if (this._disabled[key]) {\n delete this._disabled[key];\n this._entity._markDirtyKey(key);\n }\n }\n /**\r\n * Disables all adapters for specific key.\r\n *\r\n * @since 5.1.0\r\n */\n disable(key) {\n if (!this._disabled[key]) {\n this._disabled[key] = true;\n this._entity._markDirtyKey(key);\n }\n }\n /**\r\n * @ignore\r\n */\n fold(key, value) {\n if (!this._disabled[key]) {\n const callbacks = this._callbacks[key];\n if (callbacks !== undefined) {\n for (let i = 0, len = callbacks.length; i < len; ++i) {\n value = callbacks[i](value, this._entity, key);\n }\n }\n }\n return value;\n }\n}\n/**\r\n * Animation object.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/animations/} for more info\r\n */\nexport class Animation {\n constructor(animation, from, to, duration, easing, loops, startingTime) {\n Object.defineProperty(this, \"_animation\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_from\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_to\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_duration\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_easing\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_loops\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_interpolate\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_oldTime\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_time\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_stopped\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_playing\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new EventDispatcher()\n });\n this._animation = animation;\n this._from = from;\n this._to = to;\n this._duration = duration;\n this._easing = easing;\n this._loops = loops;\n this._interpolate = getInterpolate(from, to);\n this._oldTime = startingTime;\n }\n get to() {\n return this._to;\n }\n get from() {\n return this._from;\n }\n get playing() {\n return this._playing;\n }\n get stopped() {\n return this._stopped;\n }\n stop() {\n if (!this._stopped) {\n this._stopped = true;\n this._playing = false;\n if (this.events.isEnabled(\"stopped\")) {\n this.events.dispatch(\"stopped\", {\n type: \"stopped\",\n target: this\n });\n }\n }\n }\n pause() {\n this._playing = false;\n this._oldTime = null;\n }\n play() {\n if (!this._stopped && !this._playing) {\n this._playing = true;\n this._animation._startAnimation();\n }\n }\n get percentage() {\n return this._time / this._duration;\n }\n waitForStop() {\n return new Promise((resolve, _reject) => {\n if (this._stopped) {\n resolve();\n } else {\n const listener = () => {\n stopped.dispose();\n resolve();\n };\n const stopped = this.events.on(\"stopped\", listener);\n }\n });\n }\n _checkEnded() {\n if (this._loops > 1) {\n --this._loops;\n return false;\n } else {\n return true;\n }\n }\n _run(currentTime) {\n if (this._oldTime !== null) {\n this._time += currentTime - this._oldTime;\n if (this._time > this._duration) {\n this._time = this._duration;\n }\n }\n this._oldTime = currentTime;\n }\n _reset(currentTime) {\n this._oldTime = currentTime;\n this._time = 0;\n }\n _value(diff) {\n return this._interpolate(this._easing(diff), this._from, this._to);\n }\n}\n/**\r\n * @ignore\r\n */\nlet counter = 0;\n/**\r\n * Base class for [[Entity]] objects that support Settings.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n */\nexport class Settings {\n constructor(settings) {\n /**\r\n * Unique ID.\r\n */\n Object.defineProperty(this, \"uid\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: ++counter\n });\n Object.defineProperty(this, \"_settings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_privateSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_settingEvents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_privateSettingEvents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_prevSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_prevPrivateSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_animatingSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_animatingPrivateSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n // TODO move this into Entity\n Object.defineProperty(this, \"_userProperties\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n /**\r\n * If this is set to `false` then disposing does nothing, it's a no-op.\r\n */\n Object.defineProperty(this, \"enableDispose\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n this._settings = settings;\n }\n _checkDirty() {\n $object.keys(this._settings).forEach(key => {\n this._userProperties[key] = true;\n this._markDirtyKey(key);\n });\n }\n /**\r\n * @ignore\r\n */\n resetUserSettings() {\n this._userProperties = {};\n }\n _runAnimation(currentTime) {\n let state = AnimationState.Stopped;\n if (!this.isDisposed()) {\n let playing = false;\n let paused = false;\n $object.each(this._animatingSettings, (key, animation) => {\n if (animation.stopped) {\n this._stopAnimation(key);\n } else if (animation.playing) {\n animation._run(currentTime);\n const diff = animation.percentage;\n if (diff >= 1) {\n if (animation._checkEnded()) {\n this.set(key, animation._value(1));\n } else {\n playing = true;\n animation._reset(currentTime);\n this._set(key, animation._value(1));\n }\n } else {\n playing = true;\n this._set(key, animation._value(diff));\n }\n } else {\n paused = true;\n }\n });\n $object.each(this._animatingPrivateSettings, (key, animation) => {\n if (animation.stopped) {\n this._stopAnimationPrivate(key);\n } else if (animation.playing) {\n animation._run(currentTime);\n const diff = animation.percentage;\n if (diff >= 1) {\n if (animation._checkEnded()) {\n this.setPrivate(key, animation._value(1));\n } else {\n playing = true;\n animation._reset(currentTime);\n this._setPrivate(key, animation._value(1));\n }\n } else {\n playing = true;\n this._setPrivate(key, animation._value(diff));\n }\n } else {\n paused = true;\n }\n });\n if (playing) {\n state = AnimationState.Playing;\n } else if (paused) {\n state = AnimationState.Paused;\n }\n }\n return state;\n }\n _markDirtyKey(_key) {\n this.markDirty();\n }\n _markDirtyPrivateKey(_key) {\n this.markDirty();\n }\n /**\r\n * Sets a callback function to invoke when specific key of settings changes\r\n * or is set.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/#Settings_value_change} for more info\r\n * @param key Settings key\r\n * @param callback Callback\r\n * @return Disposer for event\r\n */\n on(key, callback) {\n let events = this._settingEvents[key];\n if (events === undefined) {\n events = this._settingEvents[key] = [];\n }\n events.push(callback);\n return new Disposer(() => {\n $array.removeFirst(events, callback);\n if (events.length === 0) {\n delete this._settingEvents[key];\n }\n });\n }\n /**\r\n * Removes a callback for when value of a setting changes.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/#Settings_value_change} for more info\r\n * @param key Private settings key\r\n * @param callback Callback\r\n * @since 5.9.2\r\n */\n off(key, callback) {\n let events = this._settingEvents[key];\n if (events !== undefined && callback !== undefined) {\n $array.removeFirst(events, callback);\n } else {\n delete this._settingEvents[key];\n }\n }\n /**\r\n * Sets a callback function to invoke when specific key of private settings\r\n * changes or is set.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/#Settings_value_change} for more info\r\n * @param key Private settings key\r\n * @param callback Callback\r\n * @return Disposer for event\r\n */\n onPrivate(key, callback) {\n let events = this._privateSettingEvents[key];\n if (events === undefined) {\n events = this._privateSettingEvents[key] = [];\n }\n events.push(callback);\n return new Disposer(() => {\n $array.removeFirst(events, callback);\n if (events.length === 0) {\n delete this._privateSettingEvents[key];\n }\n });\n }\n /**\r\n * Removes a callback for when value of a private setting changes.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/#Settings_value_change} for more info\r\n * @param key Private settings key\r\n * @param callback Callback\r\n * @since 5.9.2\r\n */\n offPrivate(key, callback) {\n let events = this._privateSettingEvents[key];\n if (events !== undefined && callback !== undefined) {\n $array.removeFirst(events, callback);\n } else {\n delete this._privateSettingEvents[key];\n }\n }\n /**\r\n * @ignore\r\n */\n getRaw(key, fallback) {\n const value = this._settings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n /**\r\n * Returns `true` if the setting exists.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Settings key\r\n * @return {boolean} Key exists\r\n */\n has(key) {\n return key in this._settings;\n }\n get(key, fallback) {\n return this.getRaw(key, fallback);\n }\n _sendKeyEvent(key, value) {\n const events = this._settingEvents[key];\n if (events !== undefined) {\n $array.each(events, callback => {\n callback(value, this, key);\n });\n }\n }\n _sendPrivateKeyEvent(key, value) {\n const events = this._privateSettingEvents[key];\n if (events !== undefined) {\n $array.each(events, callback => {\n callback(value, this, key);\n });\n }\n }\n /**\r\n * @ignore\r\n */\n _setRaw(key, old, value) {\n this._prevSettings[key] = old;\n this._sendKeyEvent(key, value);\n }\n /**\r\n * @ignore\r\n */\n setRaw(key, value) {\n const old = this._settings[key];\n this._settings[key] = value;\n if (old !== value) {\n this._setRaw(key, old, value);\n }\n }\n /**\r\n * @ignore\r\n */\n _set(key, value) {\n const old = this._settings[key];\n this._settings[key] = value;\n if (old !== value) {\n this._setRaw(key, old, value);\n this._markDirtyKey(key);\n }\n }\n _stopAnimation(key) {\n const animation = this._animatingSettings[key];\n if (animation) {\n delete this._animatingSettings[key];\n animation.stop();\n }\n }\n /**\r\n * Sets a setting `value` for the specified `key`, and returns the same `value`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n * @param value Setting value\r\n * @return Setting value\r\n */\n set(key, value) {\n this._set(key, value);\n this._stopAnimation(key);\n return value;\n }\n /**\r\n * Removes a setting value for the specified `key`;\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n */\n remove(key) {\n if (key in this._settings) {\n this._prevSettings[key] = this._settings[key];\n delete this._settings[key];\n this._sendKeyEvent(key, undefined);\n this._markDirtyKey(key);\n }\n this._stopAnimation(key);\n }\n /**\r\n * Removes all keys;\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n */\n removeAll() {\n $array.each($object.keys(this._settings), key => {\n this.remove(key);\n });\n }\n /**\r\n * Returns a value of a private setting.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/#Private_settings} for more info\r\n */\n getPrivate(key, fallback) {\n const value = this._privateSettings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n /**\r\n * @ignore\r\n */\n _setPrivateRaw(key, old, value) {\n this._prevPrivateSettings[key] = old;\n this._sendPrivateKeyEvent(key, value);\n }\n /**\r\n * @ignore\r\n */\n setPrivateRaw(key, value) {\n const old = this._privateSettings[key];\n this._privateSettings[key] = value;\n if (old !== value) {\n this._setPrivateRaw(key, old, value);\n }\n }\n /**\r\n * @ignore\r\n */\n _setPrivate(key, value) {\n const old = this._privateSettings[key];\n this._privateSettings[key] = value;\n if (old !== value) {\n this._setPrivateRaw(key, old, value);\n this._markDirtyPrivateKey(key);\n }\n }\n _stopAnimationPrivate(key) {\n const animation = this._animatingPrivateSettings[key];\n if (animation) {\n animation.stop();\n delete this._animatingPrivateSettings[key];\n }\n }\n /**\r\n * @ignore\r\n */\n setPrivate(key, value) {\n this._setPrivate(key, value);\n this._stopAnimationPrivate(key);\n return value;\n }\n /**\r\n * @ignore\r\n */\n removePrivate(key) {\n if (key in this._privateSettings) {\n this._prevPrivateSettings[key] = this._privateSettings[key];\n delete this._privateSettings[key];\n this._markDirtyPrivateKey(key);\n }\n this._stopAnimationPrivate(key);\n }\n /**\r\n * Sets multiple settings at once.\r\n *\r\n * `settings` must be an object with key: value pairs.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param settings Settings\r\n */\n setAll(settings) {\n $object.each(settings, (key, value) => {\n this.set(key, value);\n });\n }\n /**\r\n * Animates setting values from current/start values to new ones.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/animations/#Animating_settings} for more info\r\n * @param options Animation options\r\n * @return Animation object\r\n */\n animate(options) {\n const key = options.key;\n const to = options.to;\n const duration = options.duration || 0;\n const loops = options.loops || 1;\n const from = options.from === undefined ? this.get(key) : options.from;\n const easing = options.easing === undefined ? $ease.linear : options.easing;\n if (duration === 0) {\n this.set(key, to);\n } else {\n if (from === undefined || from === to) {\n this.set(key, to);\n } else {\n this.set(key, from);\n const animation = this._animatingSettings[key] = new Animation(this, from, to, duration, easing, loops, this._animationTime());\n this._startAnimation();\n return animation;\n }\n }\n const animation = new Animation(this, from, to, duration, easing, loops, null);\n animation.stop();\n return animation;\n }\n /**\r\n * @ignore\r\n */\n animatePrivate(options) {\n const key = options.key;\n const to = options.to;\n const duration = options.duration || 0;\n const loops = options.loops || 1;\n const from = options.from === undefined ? this.getPrivate(key) : options.from;\n const easing = options.easing === undefined ? $ease.linear : options.easing;\n if (duration === 0) {\n this.setPrivate(key, to);\n } else {\n if (from === undefined || from === to) {\n this.setPrivate(key, to);\n } else {\n this.setPrivate(key, from);\n const animation = this._animatingPrivateSettings[key] = new Animation(this, from, to, duration, easing, loops, this._animationTime());\n this._startAnimation();\n return animation;\n }\n }\n const animation = new Animation(this, from, to, duration, easing, loops, null);\n animation.stop();\n return animation;\n }\n _dispose() {}\n /**\r\n * Returns `true` if this element is disposed.\r\n *\r\n * @return Disposed\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Disposes this object.\r\n */\n dispose() {\n if (this.enableDispose && !this._disposed) {\n this._disposed = true;\n this._dispose();\n }\n }\n}\n/**\r\n * Base class.\r\n *\r\n * @important\r\n */\nexport class Entity extends Settings {\n /**\r\n * IMPORTANT! Do not instantiate this class via `new Class()` syntax.\r\n *\r\n * Use static method `Class.new()` instead.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#New_element_syntax} for more info\r\n * @ignore\r\n */\n constructor(root, settings, isReal, templates = []) {\n super(settings);\n Object.defineProperty(this, \"_root\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_user_id\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n }); // for testing purposes\n Object.defineProperty(this, \"states\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new States(this)\n });\n Object.defineProperty(this, \"adapters\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Adapters(this)\n });\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._createEvents()\n });\n Object.defineProperty(this, \"_userPrivateProperties\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dirtyPrivate\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_template\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n // Templates for the themes\n Object.defineProperty(this, \"_templates\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n // Internal templates which can be overridden by the user's templates\n Object.defineProperty(this, \"_internalTemplates\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n // Default themes which can be overridden by the user's themes\n Object.defineProperty(this, \"_defaultThemes\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n // Disposers for all of the templates\n Object.defineProperty(this, \"_templateDisposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_disposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n // Whether the template setup function should be run\n Object.defineProperty(this, \"_runSetup\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_disposerProperties\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n if (!isReal) {\n throw new Error(\"You cannot use `new Class()`, instead use `Class.new()`\");\n }\n this._root = root;\n this._internalTemplates = templates;\n if (settings.id) {\n this._registerId(settings.id);\n }\n }\n /**\r\n * Use this method to create an instance of this class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#New_element_syntax} for more info\r\n * @param root Root element\r\n * @param settings Settings\r\n * @param template Template\r\n * @return Instantiated object\r\n */\n static new(root, settings, template) {\n const x = new this(root, settings, true);\n x._template = template;\n x._afterNew();\n return x;\n }\n static _new(root, settings, templates = []) {\n const x = new this(root, settings, true, templates);\n x._afterNew();\n return x;\n }\n _afterNew() {\n this._checkDirty();\n let shouldApply = false;\n const template = this._template;\n if (template) {\n shouldApply = true;\n template._setObjectTemplate(this);\n }\n $array.each(this._internalTemplates, template => {\n shouldApply = true;\n template._setObjectTemplate(this);\n });\n if (shouldApply) {\n this._applyTemplates(false);\n }\n this.states.create(\"default\", {});\n this._setDefaults();\n }\n // This is the same as _afterNew, except it also applies the themes.\n // This should only be used for classes which don't have a parent (because they extend from Entity and not Sprite).\n _afterNewApplyThemes() {\n this._checkDirty();\n const template = this._template;\n if (template) {\n template._setObjectTemplate(this);\n }\n $array.each(this._internalTemplates, template => {\n template._setObjectTemplate(this);\n });\n this.states.create(\"default\", {});\n this._setDefaults();\n this._applyThemes();\n }\n _createEvents() {\n return new EventDispatcher();\n }\n /**\r\n * @ignore\r\n */\n get classNames() {\n return this.constructor.classNames;\n }\n /**\r\n * @ignore\r\n */\n get className() {\n return this.constructor.className;\n }\n _setDefaults() {}\n _setDefaultFn(key, f) {\n const value = this.get(key);\n if (value) {\n return value;\n } else {\n const value = f();\n this.set(key, value);\n return value;\n }\n }\n _setDefault(key, value) {\n if (!this.has(key)) {\n super.set(key, value);\n }\n }\n _setRawDefault(key, value) {\n if (!this.has(key)) {\n super.setRaw(key, value);\n }\n }\n _clearDirty() {\n $object.keys(this._dirty).forEach(key => {\n this._dirty[key] = false;\n });\n $object.keys(this._dirtyPrivate).forEach(key => {\n this._dirtyPrivate[key] = false;\n });\n }\n /**\r\n * @ignore\r\n */\n isDirty(key) {\n return !!this._dirty[key];\n }\n /**\r\n * @ignore\r\n */\n isPrivateDirty(key) {\n return !!this._dirtyPrivate[key];\n }\n _markDirtyKey(key) {\n this._dirty[key] = true;\n super._markDirtyKey(key);\n }\n _markDirtyPrivateKey(key) {\n this._dirtyPrivate[key] = true;\n super._markDirtyKey(key);\n }\n /**\r\n * Checks if element is of certain class (or inherits one).\r\n *\r\n * @param type Class name to check\r\n * @return {boolean} Is of class?\r\n */\n isType(type) {\n return this.classNames.indexOf(type) !== -1;\n }\n _pushPropertyDisposer(key, disposer) {\n let disposers = this._disposerProperties[key];\n if (disposers === undefined) {\n disposers = this._disposerProperties[key] = [];\n }\n disposers.push(disposer);\n return disposer;\n }\n _disposeProperty(key) {\n const disposers = this._disposerProperties[key];\n if (disposers !== undefined) {\n $array.each(disposers, disposer => {\n disposer.dispose();\n });\n delete this._disposerProperties[key];\n }\n }\n /**\r\n * @todo needs description\r\n * @param value Template\r\n */\n set template(value) {\n const template = this._template;\n if (template !== value) {\n this._template = value;\n if (template) {\n template._removeObjectTemplate(this);\n }\n if (value) {\n value._setObjectTemplate(this);\n }\n this._applyTemplates();\n }\n }\n get template() {\n return this._template;\n }\n /**\r\n * @ignore\r\n */\n markDirty() {\n this._root._addDirtyEntity(this);\n }\n _startAnimation() {\n this._root._addAnimation(this);\n }\n _animationTime() {\n return this._root.animationTime;\n }\n _applyState(_name) {}\n _applyStateAnimated(_name, _duration) {}\n get(key, fallback) {\n const value = this.adapters.fold(key, this._settings[key]);\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n /**\r\n * @ignore\r\n */\n isUserSetting(key) {\n return this._userProperties[key] || false;\n }\n /**\r\n * Sets a setting `value` for the specified `key`, and returns the same `value`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n * @param value Setting value\r\n * @return Setting value\r\n */\n set(key, value) {\n this._userProperties[key] = true;\n return super.set(key, value);\n }\n /**\r\n * @ignore\r\n */\n setRaw(key, value) {\n this._userProperties[key] = true;\n super.setRaw(key, value);\n }\n /**\r\n * Sets a setting `value` for the specified `key` only if the value for this key was not set previously using set method, and returns the same `value`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n * @param value Setting value\r\n * @return Setting value\r\n */\n _setSoft(key, value) {\n if (!this._userProperties[key]) {\n return super.set(key, value);\n }\n return value;\n }\n /**\r\n * Removes a setting value for the specified `key`.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/settings/} for more info\r\n * @param key Setting key\r\n */\n remove(key) {\n delete this._userProperties[key];\n this._removeTemplateProperty(key);\n }\n /**\r\n * @ignore\r\n */\n setPrivate(key, value) {\n this._userPrivateProperties[key] = true;\n return super.setPrivate(key, value);\n }\n /**\r\n * @ignore\r\n */\n setPrivateRaw(key, value) {\n this._userPrivateProperties[key] = true;\n super.setPrivateRaw(key, value);\n }\n /**\r\n * @ignore\r\n */\n removePrivate(key) {\n delete this._userPrivateProperties[key];\n this._removeTemplatePrivateProperty(key);\n }\n _setTemplateProperty(template, key, value) {\n if (!this._userProperties[key]) {\n const match = this._findTemplateByKey(key);\n if (template === match) {\n super.set(key, value);\n }\n }\n }\n _setTemplatePrivateProperty(template, key, value) {\n if (!this._userPrivateProperties[key]) {\n const match = this._findTemplateByPrivateKey(key);\n if (template === match) {\n super.setPrivate(key, value);\n }\n }\n }\n _removeTemplateProperty(key) {\n if (!this._userProperties[key]) {\n const match = this._findTemplateByKey(key);\n if (match) {\n // TODO don't stop the animation if the property didn't change\n super.set(key, match._settings[key]);\n } else {\n super.remove(key);\n }\n }\n }\n _removeTemplatePrivateProperty(key) {\n if (!this._userPrivateProperties[key]) {\n const match = this._findTemplateByPrivateKey(key);\n if (match) {\n // TODO don't stop the animation if the property didn't change\n super.setPrivate(key, match._privateSettings[key]);\n } else {\n super.removePrivate(key);\n }\n }\n }\n _walkParents(f) {\n f(this._root._rootContainer);\n f(this);\n }\n // TODO faster version of this method which is specialized to just 1 key\n _applyStateByKey(name) {\n const other = this.states.create(name, {});\n const seen = {};\n this._eachTemplate(template => {\n const state = template.states.lookup(name);\n if (state) {\n state._apply(other, seen);\n }\n });\n $object.each(other._settings, key => {\n if (!seen[key] && !other._userSettings[key]) {\n other.remove(key);\n }\n });\n }\n _applyTemplate(template, state) {\n this._templateDisposers.push(template._apply(this, state));\n $object.each(template._settings, (key, value) => {\n if (!state.settings[key] && !this._userProperties[key]) {\n state.settings[key] = true;\n super.set(key, value);\n }\n });\n $object.each(template._privateSettings, (key, value) => {\n if (!state.privateSettings[key] && !this._userPrivateProperties[key]) {\n state.privateSettings[key] = true;\n super.setPrivate(key, value);\n }\n });\n if (this._runSetup && template.setup) {\n this._runSetup = false;\n template.setup(this);\n }\n }\n /**\r\n * Calls the closure with each template and returns the first template which is true\r\n */\n _findStaticTemplate(f) {\n if (this._template) {\n if (f(this._template)) {\n return this._template;\n }\n }\n }\n _eachTemplate(f) {\n this._findStaticTemplate(template => {\n f(template);\n return false;\n });\n // _internalTemplates is sorted with most specific to the right\n $array.eachReverse(this._internalTemplates, f);\n // _templates is sorted with most specific to the left\n $array.each(this._templates, f);\n }\n _applyTemplates(remove = true) {\n if (remove) {\n this._disposeTemplates();\n }\n const state = {\n settings: {},\n privateSettings: {},\n states: {}\n };\n this._eachTemplate(template => {\n this._applyTemplate(template, state);\n });\n if (remove) {\n $object.each(this._settings, key => {\n if (!this._userProperties[key] && !state.settings[key]) {\n super.remove(key);\n }\n });\n $object.each(this._privateSettings, key => {\n if (!this._userPrivateProperties[key] && !state.privateSettings[key]) {\n super.removePrivate(key);\n }\n });\n }\n }\n _findTemplate(f) {\n const value = this._findStaticTemplate(f);\n if (value === undefined) {\n // _internalTemplates is sorted with most specific to the right\n const value = $array.findReverse(this._internalTemplates, f);\n if (value === undefined) {\n // _templates is sorted with most specific to the left\n return $array.find(this._templates, f);\n } else {\n return value;\n }\n } else {\n return value;\n }\n }\n _findTemplateByKey(key) {\n return this._findTemplate(template => {\n return key in template._settings;\n });\n }\n _findTemplateByPrivateKey(key) {\n return this._findTemplate(template => {\n return key in template._privateSettings;\n });\n }\n _disposeTemplates() {\n $array.each(this._templateDisposers, disposer => {\n disposer.dispose();\n });\n this._templateDisposers.length = 0;\n }\n _removeTemplates() {\n $array.each(this._templates, template => {\n template._removeObjectTemplate(this);\n });\n this._templates.length = 0;\n }\n _applyThemes(force = false) {\n let isConnected = false;\n const defaults = [];\n let themes = [];\n const themeTags = new Set();\n const tags = this.get(\"themeTagsSelf\");\n if (tags) {\n $array.each(tags, tag => {\n themeTags.add(tag);\n });\n }\n this._walkParents(entity => {\n if (entity === this._root._rootContainer) {\n isConnected = true;\n }\n if (entity._defaultThemes.length > 0) {\n defaults.push(entity._defaultThemes);\n }\n const theme = entity.get(\"themes\");\n if (theme) {\n themes.push(theme);\n }\n const tags = entity.get(\"themeTags\");\n if (tags) {\n $array.each(tags, tag => {\n themeTags.add(tag);\n });\n }\n });\n themes = defaults.concat(themes);\n this._removeTemplates();\n if (isConnected || force) {\n $array.eachReverse(this.classNames, name => {\n const allRules = [];\n $array.each(themes, themes => {\n $array.each(themes, theme => {\n const rules = theme._lookupRules(name);\n if (rules) {\n $array.eachReverse(rules, rule => {\n const matches = rule.tags.every(tag => {\n return themeTags.has(tag);\n });\n if (matches) {\n const result = $array.getFirstSortedIndex(allRules, x => {\n const order = $order.compare(rule.tags.length, x.tags.length);\n if (order === 0) {\n return $order.compareArray(rule.tags, x.tags, $order.compare);\n } else {\n return order;\n }\n });\n allRules.splice(result.index, 0, rule);\n }\n });\n }\n });\n });\n $array.each(allRules, rule => {\n this._templates.push(rule.template);\n rule.template._setObjectTemplate(this);\n });\n });\n }\n this._applyTemplates();\n if (isConnected || force) {\n // This causes it to only run the setup function the first time that the themes are applied\n this._runSetup = false;\n }\n return isConnected || force;\n }\n _changed() {}\n _beforeChanged() {\n if (this.isDirty(\"id\")) {\n const id = this.get(\"id\");\n if (id) {\n this._registerId(id);\n }\n const prevId = this._prevSettings.id;\n if (prevId) {\n delete registry.entitiesById[prevId];\n }\n }\n }\n _registerId(id) {\n if (registry.entitiesById[id] && registry.entitiesById[id] !== this) {\n throw new Error(\"An entity with id \\\"\" + id + \"\\\" already exists.\");\n }\n registry.entitiesById[id] = this;\n }\n _afterChanged() {}\n /**\r\n * @ignore\r\n */\n addDisposer(disposer) {\n this._disposers.push(disposer);\n return disposer;\n }\n _dispose() {\n super._dispose();\n const template = this._template;\n if (template) {\n template._removeObjectTemplate(this);\n }\n $array.each(this._internalTemplates, template => {\n template._removeObjectTemplate(this);\n });\n this._removeTemplates();\n this._disposeTemplates();\n this.events.dispose();\n this._disposers.forEach(x => {\n x.dispose();\n });\n $object.each(this._disposerProperties, (_, disposers) => {\n $array.each(disposers, disposer => {\n disposer.dispose();\n });\n });\n const id = this.get(\"id\");\n if (id) {\n delete registry.entitiesById[id];\n }\n }\n /**\r\n * Creates and returns a \"disposable\" timeout.\r\n *\r\n * @param fn Callback\r\n * @param delay Delay in milliseconds\r\n * @return Timeout disposer\r\n */\n setTimeout(fn, delay) {\n const id = setTimeout(() => {\n this.removeDispose(disposer);\n fn();\n }, delay);\n const disposer = new Disposer(() => {\n clearTimeout(id);\n });\n this._disposers.push(disposer);\n return disposer;\n }\n /**\r\n * @ignore\r\n */\n removeDispose(target) {\n if (!this.isDisposed()) {\n let index = $array.indexOf(this._disposers, target);\n if (index > -1) {\n this._disposers.splice(index, 1);\n }\n }\n target.dispose();\n }\n /**\r\n * @ignore\r\n */\n hasTag(tag) {\n return $array.indexOf(this.get(\"themeTags\", []), tag) !== -1;\n }\n /**\r\n * @ignore\r\n */\n addTag(tag) {\n if (!this.hasTag(tag)) {\n const tags = this.get(\"themeTags\", []);\n tags.push(tag);\n this.set(\"themeTags\", tags);\n }\n }\n /**\r\n * @ignore\r\n */\n removeTag(tag) {\n if (this.hasTag(tag)) {\n const tags = this.get(\"themeTags\", []);\n $array.remove(tags, tag);\n this.set(\"themeTags\", tags);\n }\n }\n _t(text, locale, ...rest) {\n return this._root.language.translate(text, locale, ...rest);\n }\n /**\r\n * An instance of [[Root]] object.\r\n *\r\n * @readonly\r\n * @since 5.0.6\r\n * @return Root object\r\n */\n get root() {\n return this._root;\n }\n}\nObject.defineProperty(Entity, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Entity\"\n});\nObject.defineProperty(Entity, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"Entity\"]\n});\n","import { EventDispatcher } from \"./EventDispatcher\";\nimport { Disposer, MultiDisposer } from \"./Disposer\";\nimport * as $array from \"./Array\";\nimport * as $object from \"./Object\";\nimport * as $type from \"./Type\";\nfunction disposeSettings(settings) {\n $object.each(settings, (_key, value) => {\n if ($type.isObject(value) && typeof value.dispose === \"function\") {\n value.enableDispose = true;\n value.dispose();\n }\n });\n}\nexport class TemplateState {\n constructor(name, template, settings) {\n Object.defineProperty(this, \"_settings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_name\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_template\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._name = name;\n this._template = template;\n this._settings = settings;\n }\n _dispose() {\n disposeSettings(this._settings);\n }\n get(key, fallback) {\n const value = this._settings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n set(key, value) {\n this._settings[key] = value;\n // TODO maybe only do this if the value changed ?\n this._template._stateChanged(this._name);\n }\n remove(key) {\n delete this._settings[key];\n // TODO maybe only do this if the value changed ?\n this._template._stateChanged(this._name);\n }\n setAll(settings) {\n $object.keys(settings).forEach(key => {\n this._settings[key] = settings[key];\n });\n this._template._stateChanged(this._name);\n }\n _apply(other, seen) {\n $object.each(this._settings, (key, value) => {\n if (!seen[key] && !other._userSettings[key]) {\n seen[key] = true;\n other.setRaw(key, value);\n }\n });\n }\n}\nexport class TemplateStates {\n constructor(template) {\n Object.defineProperty(this, \"_template\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_states\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n this._template = template;\n }\n _dispose() {\n $object.each(this._states, (_key, state) => {\n state._dispose();\n });\n }\n lookup(name) {\n return this._states[name];\n }\n create(name, settings) {\n const state = this._states[name];\n if (state) {\n state.setAll(settings);\n return state;\n } else {\n const state = new TemplateState(name, this._template, settings);\n this._states[name] = state;\n this._template._stateChanged(name);\n return state;\n }\n }\n remove(name) {\n delete this._states[name];\n this._template._stateChanged(name);\n }\n _apply(entity, state) {\n $object.each(this._states, (key, value) => {\n let seen = state.states[key];\n if (seen == null) {\n seen = state.states[key] = {};\n }\n const other = entity.states.create(key, {});\n value._apply(other, seen);\n });\n }\n}\nexport class TemplateAdapters {\n constructor() {\n Object.defineProperty(this, \"_callbacks\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n }\n add(key, callback) {\n let callbacks = this._callbacks[key];\n if (callbacks === undefined) {\n callbacks = this._callbacks[key] = [];\n }\n callbacks.push(callback);\n return new Disposer(() => {\n $array.removeFirst(callbacks, callback);\n if (callbacks.length === 0) {\n delete this._callbacks[key];\n }\n });\n }\n remove(key) {\n const callbacks = this._callbacks[key];\n if (callbacks !== undefined) {\n delete this._callbacks[key];\n }\n }\n _apply(entity) {\n const disposers = [];\n $object.each(this._callbacks, (key, callbacks) => {\n $array.each(callbacks, callback => {\n disposers.push(entity.adapters.add(key, callback));\n });\n });\n return new MultiDisposer(disposers);\n }\n}\n// TODO maybe extend from Properties ?\nexport class Template {\n constructor(settings, isReal) {\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_settings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_privateSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n // TODO code duplication with Properties\n Object.defineProperty(this, \"_settingEvents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_privateSettingEvents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_entities\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"states\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new TemplateStates(this)\n });\n Object.defineProperty(this, \"adapters\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new TemplateAdapters()\n });\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new EventDispatcher()\n });\n Object.defineProperty(this, \"setup\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n if (!isReal) {\n throw new Error(\"You cannot use `new Class()`, instead use `Class.new()`\");\n }\n this._settings = settings;\n }\n /**\r\n * Use this method to create an instance of this class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#New_element_syntax} for more info\r\n * @param root Root element\r\n * @param settings Settings\r\n * @param template Template\r\n * @return Instantiated object\r\n */\n static new(settings) {\n return new Template(settings, true);\n }\n _dispose() {\n disposeSettings(this._settings);\n disposeSettings(this._privateSettings);\n }\n /**\r\n * Returns `true` if this element is disposed.\r\n *\r\n * @return Disposed\r\n */\n isDisposed() {\n return this._disposed;\n }\n /**\r\n * Disposes this object.\r\n */\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._dispose();\n }\n }\n _checkDisposed() {\n if (this._disposed) {\n throw new Error(\"Template is disposed\");\n }\n }\n /**\r\n * Array of all entities using this template.\r\n */\n get entities() {\n return this._entities;\n }\n get(key, fallback) {\n this._checkDisposed();\n const value = this._settings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n setRaw(key, value) {\n this._checkDisposed();\n this._settings[key] = value;\n }\n set(key, value) {\n this._checkDisposed();\n if (this._settings[key] !== value) {\n this.setRaw(key, value);\n this._entities.forEach(entity => {\n entity._setTemplateProperty(this, key, value);\n });\n }\n }\n remove(key) {\n this._checkDisposed();\n if (key in this._settings) {\n delete this._settings[key];\n this._entities.forEach(entity => {\n entity._removeTemplateProperty(key);\n });\n }\n }\n removeAll() {\n this._checkDisposed();\n $object.each(this._settings, (key, _value) => {\n this.remove(key);\n });\n }\n getPrivate(key, fallback) {\n this._checkDisposed();\n const value = this._privateSettings[key];\n if (value !== undefined) {\n return value;\n } else {\n return fallback;\n }\n }\n setPrivateRaw(key, value) {\n this._checkDisposed();\n this._privateSettings[key] = value;\n return value;\n }\n setPrivate(key, value) {\n this._checkDisposed();\n if (this._privateSettings[key] !== value) {\n this.setPrivateRaw(key, value);\n this._entities.forEach(entity => {\n entity._setTemplatePrivateProperty(this, key, value);\n });\n }\n return value;\n }\n removePrivate(key) {\n this._checkDisposed();\n if (key in this._privateSettings) {\n delete this._privateSettings[key];\n this._entities.forEach(entity => {\n entity._removeTemplatePrivateProperty(key);\n });\n }\n }\n setAll(value) {\n this._checkDisposed();\n $object.each(value, (key, value) => {\n this.set(key, value);\n });\n }\n // TODO code duplication with Properties\n on(key, callback) {\n this._checkDisposed();\n let events = this._settingEvents[key];\n if (events === undefined) {\n events = this._settingEvents[key] = [];\n }\n events.push(callback);\n return new Disposer(() => {\n $array.removeFirst(events, callback);\n if (events.length === 0) {\n delete this._settingEvents[key];\n }\n });\n }\n // TODO code duplication with Properties\n onPrivate(key, callback) {\n this._checkDisposed();\n let events = this._privateSettingEvents[key];\n if (events === undefined) {\n events = this._privateSettingEvents[key] = [];\n }\n events.push(callback);\n return new Disposer(() => {\n $array.removeFirst(events, callback);\n if (events.length === 0) {\n delete this._privateSettingEvents[key];\n }\n });\n }\n _apply(entity, state) {\n this._checkDisposed();\n const disposers = [];\n $object.each(this._settingEvents, (key, events) => {\n $array.each(events, event => {\n disposers.push(entity.on(key, event));\n });\n });\n $object.each(this._privateSettingEvents, (key, events) => {\n $array.each(events, event => {\n disposers.push(entity.onPrivate(key, event));\n });\n });\n this.states._apply(entity, state);\n disposers.push(this.adapters._apply(entity));\n disposers.push(entity.events.copyFrom(this.events));\n return new MultiDisposer(disposers);\n }\n _setObjectTemplate(entity) {\n this._checkDisposed();\n this._entities.push(entity);\n }\n _removeObjectTemplate(entity) {\n //this._checkDisposed();\n $array.remove(this._entities, entity);\n }\n _stateChanged(name) {\n this._checkDisposed();\n this._entities.forEach(entity => {\n entity._applyStateByKey(name);\n });\n }\n}\n","import { __awaiter } from \"tslib\";\nimport { Entity } from \"../util/Entity\";\nimport { Template } from \"../util/Template\";\nimport { Percent } from \"../util/Percent\";\nimport { EventDispatcher } from \"../util/EventDispatcher\";\nimport { MultiDisposer, CounterDisposer } from \"../util/Disposer\";\nimport { waitForAnimations } from \"../util/Animation\";\nimport * as $utils from \"../util/Utils\";\nimport * as $array from \"../util/Array\";\nimport * as $type from \"../util/Type\";\nimport * as $object from \"../util/Object\";\nimport * as $math from \"../util/Math\";\n//import { populateString } from \"../util/PopulateString\";\n/**\r\n * An [[EventDispatcher]] for [[Sprite]].\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/} for more info\r\n */\nclass SpriteEventDispatcher extends EventDispatcher {\n constructor(sprite) {\n super();\n Object.defineProperty(this, \"_sprite\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_rendererDisposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dispatchParents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n this._sprite = sprite;\n }\n _makePointerEvent(key, event) {\n return {\n type: key,\n originalEvent: event.event,\n point: event.point,\n simulated: event.simulated,\n native: event.native,\n target: this._sprite\n };\n }\n _onRenderer(key, dispatch) {\n // TODO: is this OK? it'd be good not to require to set this on each individual element\n this._sprite.set(\"interactive\", true);\n this._sprite._display.interactive = true;\n let events = this._rendererDisposers[key];\n if (events === undefined) {\n const disposer = this._sprite._display.on(key, e => {\n dispatch.call(this, e);\n });\n events = this._rendererDisposers[key] = new CounterDisposer(() => {\n delete this._rendererDisposers[key];\n disposer.dispose();\n });\n }\n return events.increment();\n }\n _on(once, type, callback, context, shouldClone, dispatch) {\n const info = super._on(once, type, callback, context, shouldClone, dispatch);\n const rendererEvent = SpriteEventDispatcher.RENDERER_EVENTS[type];\n if (rendererEvent !== undefined) {\n info.disposer = new MultiDisposer([info.disposer, this._onRenderer(type, rendererEvent)]);\n }\n return info;\n }\n /**\r\n * Will stop any bubbling up of the event to element's parents.\r\n *\r\n * Should be called in an event handler, e.g.:\r\n *\r\n * ```TypeScript\r\n * element.events.on(\"pointerdown\", function(ev) {\r\n * // Do something here and prevent from \"pointerdown\" bubbling up\r\n * // ...\r\n * ev.target.events.stopParentDispatch();\r\n * });\r\n * ```\r\n * ```JavaScript\r\n * element.events.on(\"pointerdown\", function(ev) {\r\n * // Do something here and prevent from \"pointerdown\" bubbling up\r\n * // ...\r\n * ev.target.events.stopParentDispatch();\r\n * });\r\n * ```\r\n */\n stopParentDispatch() {\n this._dispatchParents = false;\n }\n /**\r\n * @ignore\r\n */\n dispatchParents(type, event) {\n const old = this._dispatchParents;\n this._dispatchParents = true;\n try {\n this.dispatch(type, event);\n if (this._dispatchParents && this._sprite.parent) {\n this._sprite.parent.events.dispatchParents(type, event);\n }\n } finally {\n this._dispatchParents = old;\n }\n }\n}\nObject.defineProperty(SpriteEventDispatcher, \"RENDERER_EVENTS\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {\n \"click\": function (event) {\n if (this.isEnabled(\"click\") && !this._sprite.isDragging() && this._sprite._hasDown() && !this._sprite._hasMoved(this._makePointerEvent(\"click\", event))) {\n this.dispatch(\"click\", this._makePointerEvent(\"click\", event));\n }\n },\n \"rightclick\": function (event) {\n if (this.isEnabled(\"rightclick\")) {\n this.dispatch(\"rightclick\", this._makePointerEvent(\"rightclick\", event));\n }\n },\n \"middleclick\": function (event) {\n if (this.isEnabled(\"middleclick\")) {\n this.dispatch(\"middleclick\", this._makePointerEvent(\"middleclick\", event));\n }\n },\n \"dblclick\": function (event) {\n this.dispatchParents(\"dblclick\", this._makePointerEvent(\"dblclick\", event));\n },\n \"pointerover\": function (event) {\n const sprite = this._sprite;\n let dispatch = true;\n if (sprite.getPrivate(\"trustBounds\")) {\n sprite._getBounds();\n const bounds = sprite.globalBounds();\n if (sprite.isType(\"Graphics\")) {\n const strokeWidth = sprite.get(\"strokeWidth\", 1) / 2;\n if (strokeWidth >= 1) {\n bounds.left -= strokeWidth;\n bounds.right += strokeWidth;\n bounds.top -= strokeWidth;\n bounds.bottom += strokeWidth;\n }\n }\n if (!$math.inBounds(event.point, bounds)) {\n dispatch = false;\n sprite._root._renderer.removeHovering(sprite._display);\n }\n }\n if (dispatch && this.isEnabled(\"pointerover\")) {\n this.dispatch(\"pointerover\", this._makePointerEvent(\"pointerover\", event));\n }\n },\n \"pointerout\": function (event) {\n if (this.isEnabled(\"pointerout\")) {\n this.dispatch(\"pointerout\", this._makePointerEvent(\"pointerout\", event));\n }\n },\n \"pointerdown\": function (event) {\n this.dispatchParents(\"pointerdown\", this._makePointerEvent(\"pointerdown\", event));\n },\n \"pointerup\": function (event) {\n if (this.isEnabled(\"pointerup\")) {\n this.dispatch(\"pointerup\", this._makePointerEvent(\"pointerup\", event));\n }\n },\n \"globalpointerup\": function (event) {\n if (this.isEnabled(\"globalpointerup\")) {\n this.dispatch(\"globalpointerup\", this._makePointerEvent(\"globalpointerup\", event));\n }\n },\n \"globalpointermove\": function (event) {\n if (this.isEnabled(\"globalpointermove\")) {\n this.dispatch(\"globalpointermove\", this._makePointerEvent(\"globalpointermove\", event));\n }\n },\n \"wheel\": function (event) {\n this.dispatchParents(\"wheel\", {\n type: \"wheel\",\n target: this._sprite,\n originalEvent: event.event,\n point: event.point\n });\n }\n }\n});\n/**\r\n * A base class for all visual elements.\r\n *\r\n * @important\r\n */\nexport class Sprite extends Entity {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_adjustedLocalBounds\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n }\n });\n Object.defineProperty(this, \"_localBounds\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n }\n });\n Object.defineProperty(this, \"_parent\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_dataItem\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_templateField\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_sizeDirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n // Will be true only when dragging\n Object.defineProperty(this, \"_isDragging\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n // The event when the dragging starts\n Object.defineProperty(this, \"_dragEvent\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n // The position when dragging starts\n Object.defineProperty(this, \"_dragPoint\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_isHidden\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_isShowing\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_isHiding\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_isDown\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_downPoint\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_downPoints\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_toggleDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_dragDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_hoverDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_focusDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipMoveDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipPointerDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_statesHandled\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n _afterNew() {\n this.setPrivateRaw(\"visible\", true);\n super._afterNew();\n }\n /**\r\n * Marks some setting as dirty. Could be used to trigger adapter.\r\n * @param key\r\n */\n markDirtyKey(key) {\n this._markDirtyKey(key);\n }\n _markDirtyKey(key) {\n super._markDirtyKey(key);\n if (key == \"x\" || key == \"y\" || key == \"dx\" || key == \"dy\") {\n this.markDirtyBounds();\n this._addPercentagePositionChildren();\n this.markDirtyPosition();\n }\n }\n _markDirtyPrivateKey(key) {\n super._markDirtyPrivateKey(key);\n if (key == \"x\" || key == \"y\") {\n this.markDirtyPosition();\n }\n }\n _removeTemplateField() {\n if (this._templateField) {\n this._templateField._removeObjectTemplate(this);\n }\n }\n _createEvents() {\n return new SpriteEventDispatcher(this);\n }\n _processTemplateField() {\n let template;\n const field = this.get(\"templateField\");\n if (field) {\n const dataItem = this.dataItem;\n if (dataItem) {\n const context = dataItem.dataContext;\n if (context) {\n template = context[field];\n if (!(template instanceof Template) && template) {\n template = Template.new(template);\n }\n }\n }\n }\n if (this._templateField !== template) {\n this._removeTemplateField();\n this._templateField = template;\n if (template) {\n template._setObjectTemplate(this);\n }\n this._applyTemplates();\n }\n }\n // TODO change this to run before the element is added to the parent, so that way\n // it doesn't need to apply the themes twice\n _setDataItem(dataItem) {\n const oldDataItem = this._dataItem;\n this._dataItem = dataItem;\n this._processTemplateField();\n const eventType = \"dataitemchanged\";\n if (dataItem != oldDataItem) {\n if (this.events.isEnabled(eventType)) {\n this.events.dispatch(eventType, {\n type: eventType,\n target: this,\n oldDataItem: oldDataItem,\n newDataItem: dataItem\n });\n }\n }\n }\n /**\r\n * A [[DataItem]] used for this element.\r\n *\r\n * NOTE: data item is being assigned automatically in most cases where it\r\n * matters. Use this accessor to set data item only if you know what you're\r\n * doing.\r\n *\r\n * @param value Data item\r\n */\n set dataItem(value) {\n this._setDataItem(value);\n }\n /**\r\n * @return DataItem\r\n */\n get dataItem() {\n if (this._dataItem) {\n return this._dataItem;\n } else {\n let parent = this._parent;\n while (parent) {\n if (parent._dataItem) {\n return parent._dataItem;\n } else {\n parent = parent._parent;\n }\n }\n }\n }\n _addPercentageSizeChildren() {\n let parent = this.parent;\n if (parent) {\n if (this.get(\"width\") instanceof Percent || this.get(\"height\") instanceof Percent) {\n $array.pushOne(parent._percentageSizeChildren, this);\n } else {\n $array.removeFirst(parent._percentageSizeChildren, this);\n }\n }\n }\n _addPercentagePositionChildren() {\n let parent = this.parent;\n if (parent) {\n if (this.get(\"x\") instanceof Percent || this.get(\"y\") instanceof Percent) {\n $array.pushOne(parent._percentagePositionChildren, this);\n } else {\n $array.removeFirst(parent._percentagePositionChildren, this);\n }\n }\n }\n /**\r\n * @ignore\r\n */\n markDirtyPosition() {\n this._root._addDirtyPosition(this);\n }\n updatePivotPoint() {\n const bounds = this._localBounds;\n if (bounds) {\n const centerX = this.get(\"centerX\");\n if (centerX != null) {\n this._display.pivot.x = bounds.left + $utils.relativeToValue(centerX, bounds.right - bounds.left);\n }\n const centerY = this.get(\"centerY\");\n if (centerY != null) {\n this._display.pivot.y = bounds.top + $utils.relativeToValue(centerY, bounds.bottom - bounds.top);\n }\n }\n }\n _beforeChanged() {\n super._beforeChanged();\n // handling states in beforeChanged, otherwise states is not applied without animated theme\n this._handleStates();\n if (this.isDirty(\"tooltip\")) {\n const previous = this._prevSettings.tooltip;\n if (previous) {\n previous.dispose();\n }\n }\n if (this.isDirty(\"layer\") || this.isDirty(\"layerMargin\")) {\n this._display.setLayer(this.get(\"layer\"), this.get(\"layerMargin\"));\n this.markDirtyLayer();\n }\n if (this.isDirty(\"tooltipPosition\")) {\n const tooltipMoveDp = this._tooltipMoveDp;\n if (tooltipMoveDp) {\n tooltipMoveDp.dispose();\n this._tooltipMoveDp = undefined;\n }\n const tooltipPointerDp = this._tooltipPointerDp;\n if (tooltipPointerDp) {\n tooltipPointerDp.dispose();\n this._tooltipPointerDp = undefined;\n }\n if (this.get(\"tooltipPosition\") == \"pointer\") {\n if (this.isHover()) {\n this._tooltipMoveDp = this.events.on(\"globalpointermove\", e => {\n this.showTooltip(e.point);\n });\n }\n this._tooltipPointerDp = new MultiDisposer([this.events.on(\"pointerover\", () => {\n this._tooltipMoveDp = this.events.on(\"globalpointermove\", e => {\n this.showTooltip(e.point);\n });\n }), this.events.on(\"pointerout\", () => {\n const tooltipMoveDp = this._tooltipMoveDp;\n if (tooltipMoveDp) {\n tooltipMoveDp.dispose();\n this._tooltipMoveDp = undefined;\n }\n })]);\n }\n }\n }\n _handleStates() {\n if (!this._statesHandled) {\n if (this.isDirty(\"active\")) {\n if (this.get(\"active\")) {\n this.states.applyAnimate(\"active\");\n this.set(\"ariaChecked\", true);\n } else {\n if (!this.isHidden()) {\n this.states.applyAnimate(\"default\");\n }\n this.set(\"ariaChecked\", false);\n }\n this.markDirtyAccessibility();\n }\n if (this.isDirty(\"disabled\")) {\n if (this.get(\"disabled\")) {\n this.states.applyAnimate(\"disabled\");\n this.set(\"ariaChecked\", false);\n } else {\n if (!this.isHidden()) {\n this.states.applyAnimate(\"default\");\n }\n this.set(\"ariaChecked\", true);\n }\n this.markDirtyAccessibility();\n }\n this._statesHandled = true;\n }\n }\n _changed() {\n super._changed();\n const display = this._display;\n const events = this.events;\n if (this.isDirty(\"draggable\")) {\n const draggable = this.get(\"draggable\");\n if (draggable) {\n this.set(\"interactive\", true);\n this._dragDp = new MultiDisposer([events.on(\"pointerdown\", ev => {\n this.dragStart(ev);\n }), events.on(\"globalpointermove\", ev => {\n this.dragMove(ev);\n }), events.on(\"globalpointerup\", ev => {\n this.dragStop(ev);\n })]);\n } else {\n if (this._dragDp) {\n this._dragDp.dispose();\n this._dragDp = undefined;\n }\n }\n display.cancelTouch = draggable ? true : false;\n }\n if (this.isDirty(\"tooltipText\") || this.isDirty(\"tooltipHTML\") || this.isDirty(\"showTooltipOn\")) {\n const tooltipText = this.get(\"tooltipText\");\n const tooltipHTML = this.get(\"tooltipHTML\");\n const showTooltipOn = this.get(\"showTooltipOn\", \"hover\");\n if (this._tooltipDp) {\n this._tooltipDp.dispose();\n this._tooltipDp = undefined;\n }\n if (tooltipText || tooltipHTML) {\n if (showTooltipOn == \"click\") {\n this._tooltipDp = new MultiDisposer([events.on(\"click\", () => {\n this.setTimeout(() => {\n const tooltip = this.getTooltip();\n if (tooltip && !tooltip.isHidden() && tooltip.get(\"tooltipTarget\") === this) {\n this.hideTooltip();\n } else {\n this.showTooltip();\n }\n }, 10);\n }), $utils.addEventListener(document, \"click\", _ev => {\n this.hideTooltip();\n })]);\n this._disposers.push(this._tooltipDp);\n } else if (showTooltipOn == \"always\") {\n // nothing\n } else {\n this._tooltipDp = new MultiDisposer([events.on(\"pointerover\", () => {\n this.showTooltip();\n }), events.on(\"pointerout\", () => {\n this.hideTooltip();\n })]);\n this._disposers.push(this._tooltipDp);\n }\n }\n }\n if (this.isDirty(\"toggleKey\")) {\n let toggleKey = this.get(\"toggleKey\");\n if (toggleKey && toggleKey != \"none\") {\n this._toggleDp = events.on(\"click\", () => {\n if (!this._isDragging) {\n this.set(toggleKey, !this.get(toggleKey));\n }\n });\n } else {\n if (this._toggleDp) {\n this._toggleDp.dispose();\n this._toggleDp = undefined;\n }\n }\n }\n if (this.isDirty(\"opacity\")) {\n display.alpha = Math.max(0, this.get(\"opacity\", 1));\n if (this.get(\"focusable\")) {\n this.markDirtyAccessibility();\n }\n }\n if (this.isDirty(\"rotation\")) {\n this.markDirtyBounds();\n display.angle = this.get(\"rotation\", 0);\n }\n if (this.isDirty(\"scale\")) {\n this.markDirtyBounds();\n display.scale = this.get(\"scale\", 0);\n }\n if (this.isDirty(\"centerX\") || this.isDirty(\"centerY\")) {\n this.markDirtyBounds();\n this.updatePivotPoint();\n }\n if (this.isDirty(\"visible\") || this.isPrivateDirty(\"visible\") || this.isDirty(\"forceHidden\")) {\n if (!this.get(\"visible\") || !this.getPrivate(\"visible\") || this.get(\"forceHidden\")) {\n display.visible = false;\n this.hideTooltip();\n } else {\n display.visible = true;\n }\n this.markDirtyBounds();\n if (this.get(\"focusable\")) {\n this.markDirtyAccessibility();\n }\n }\n if (this.isDirty(\"width\") || this.isDirty(\"height\")) {\n this.markDirtyBounds();\n this._addPercentageSizeChildren();\n const parent = this.parent;\n if (parent) {\n if (this.isDirty(\"width\") && this.get(\"width\") instanceof Percent || this.isDirty(\"height\") && this.get(\"height\") instanceof Percent) {\n parent.markDirty();\n parent._prevWidth = 0;\n }\n }\n this._sizeDirty = true;\n }\n if (this.isDirty(\"maxWidth\") || this.isDirty(\"maxHeight\") || this.isPrivateDirty(\"width\") || this.isPrivateDirty(\"height\") || this.isDirty(\"minWidth\") || this.isDirty(\"minHeight\") || this.isPrivateDirty(\"maxWidth\") || this.isPrivateDirty(\"maxHeight\") || this.isPrivateDirty(\"minWidth\") || this.isPrivateDirty(\"minHeight\") || this.isDirty(\"marginLeft\") || this.isDirty(\"marginTop\") || this.isDirty(\"marginRight\") || this.isDirty(\"marginBottom\")) {\n this.markDirtyBounds();\n this._sizeDirty = true;\n }\n if (this._sizeDirty) {\n this._updateSize();\n }\n if (this.isDirty(\"wheelable\")) {\n const wheelable = this.get(\"wheelable\");\n if (wheelable) {\n this.set(\"interactive\", true);\n }\n display.wheelable = wheelable ? true : false;\n }\n // Accessibility\n if (this.isDirty(\"tabindexOrder\") || this.isDirty(\"focusableGroup\")) {\n if (this.get(\"focusable\")) {\n this._root._registerTabindexOrder(this);\n } else {\n this._root._unregisterTabindexOrder(this);\n }\n }\n if (this.isDirty(\"filter\")) {\n //this.markDirtyBounds();\n display.filter = this.get(\"filter\");\n }\n let filter = this.get(\"filter\", \"\");\n if (this.isDirty(\"blur\")) {\n const blur = this.get(\"blur\", 0);\n if (blur != 0) {\n filter += \" blur(\" + blur + \"px)\";\n }\n }\n if (this.isDirty(\"saturate\")) {\n const saturate = this.get(\"saturate\", 1);\n if (saturate != 1) {\n filter += \" saturate(\" + saturate + \")\";\n }\n }\n if (this.isDirty(\"brightness\")) {\n const brightness = this.get(\"brightness\", 1);\n if (brightness != 1) {\n filter += \" brightness(\" + brightness + \")\";\n }\n }\n if (this.isDirty(\"contrast\")) {\n const contrast = this.get(\"contrast\", 1);\n if (contrast != 1) {\n filter += \" contrast(\" + contrast + \")\";\n }\n }\n if (this.isDirty(\"sepia\")) {\n const sepia = this.get(\"sepia\", 0);\n if (sepia != 0) {\n filter += \" sepia(\" + sepia + \")\";\n }\n }\n if (this.isDirty(\"hue\")) {\n const hue = this.get(\"hue\", 0);\n if (hue != 0) {\n filter += \" hue-rotate(\" + hue + \"deg)\";\n }\n }\n if (this.isDirty(\"invert\")) {\n const invert = this.get(\"invert\", 0);\n if (invert != 0) {\n filter += \" invert(\" + invert + \")\";\n }\n }\n if (filter) {\n display.filter = filter;\n }\n if (this.isDirty(\"cursorOverStyle\")) {\n display.cursorOverStyle = this.get(\"cursorOverStyle\");\n }\n if (this.isDirty(\"hoverOnFocus\")) {\n if (this.get(\"hoverOnFocus\")) {\n this._focusDp = new MultiDisposer([events.on(\"focus\", () => {\n // TODO: proper hover, not just tooltip\n this.showTooltip();\n }), events.on(\"blur\", () => {\n // TODO: proper hover, not just tooltip\n this.hideTooltip();\n })]);\n } else {\n if (this._focusDp) {\n this._focusDp.dispose();\n this._focusDp = undefined;\n }\n }\n }\n if (this.isDirty(\"focusable\")) {\n if (this.get(\"focusable\")) {\n this._root._registerTabindexOrder(this);\n } else {\n this._root._unregisterTabindexOrder(this);\n }\n this.markDirtyAccessibility();\n this._disposers.push(events.on(\"blur\", () => {\n this.setPrivateRaw(\"touchHovering\", false);\n }));\n }\n if (this.isPrivateDirty(\"focusable\")) {\n this.markDirtyAccessibility();\n }\n if (this.isDirty(\"role\") || this.isDirty(\"ariaLive\") || this.isDirty(\"ariaChecked\") || this.isDirty(\"ariaHidden\") || this.isDirty(\"ariaOrientation\") || this.isDirty(\"ariaValueNow\") || this.isDirty(\"ariaValueMin\") || this.isDirty(\"ariaValueMax\") || this.isDirty(\"ariaValueText\") || this.isDirty(\"ariaLabel\") || this.isDirty(\"ariaControls\")) {\n // display.accessibility.ariaLabel = populateString(this, this.get(\"ariaLabel\", \"\"));\n // @todo make sure ariaLabel gets populated in Root\n this.markDirtyAccessibility();\n }\n if (this.isDirty(\"exportable\")) {\n display.exportable = this.get(\"exportable\");\n }\n if (this.isDirty(\"interactive\")) {\n const events = this.events;\n if (this.get(\"interactive\") && !events.isDisposed()) {\n this._hoverDp = new MultiDisposer([events.on(\"click\", ev => {\n if ($utils.isTouchEvent(ev.originalEvent)) {\n if (!this.getPrivate(\"touchHovering\")) {\n this.setTimeout(() => {\n this._handleOver();\n if (this.get(\"tooltipText\") || this.get(\"tooltipHTML\")) {\n this.showTooltip();\n }\n this.setPrivateRaw(\"touchHovering\", true);\n this.events.dispatch(\"pointerover\", {\n type: \"pointerover\",\n target: ev.target,\n originalEvent: ev.originalEvent,\n point: ev.point,\n simulated: ev.simulated\n });\n }, 10);\n }\n }\n }), events.on(\"globalpointerup\", ev => {\n if ($utils.isTouchEvent(ev.originalEvent)) {\n if (this.getPrivate(\"touchHovering\")) {\n this._handleOut();\n if (this.get(\"tooltipText\") || this.get(\"tooltipHTML\")) {\n this.hideTooltip();\n }\n this.setPrivateRaw(\"touchHovering\", false);\n this.events.dispatch(\"pointerout\", {\n type: \"pointerout\",\n target: ev.target,\n originalEvent: ev.originalEvent,\n point: ev.point,\n simulated: ev.simulated\n });\n }\n }\n if (this._isDown) {\n this._handleUp(ev);\n }\n //this._isDown = false;\n }), events.on(\"pointerover\", () => {\n this._handleOver();\n }), events.on(\"pointerout\", () => {\n this._handleOut();\n }), events.on(\"pointerdown\", e => {\n this._handleDown(e);\n })]);\n } else {\n this._display.interactive = false;\n if (this._hoverDp) {\n this._hoverDp.dispose();\n this._hoverDp = undefined;\n }\n }\n }\n if (this.isDirty(\"forceInactive\")) {\n this._display.inactive = this.get(\"forceInactive\", null);\n }\n if (this.get(\"showTooltipOn\") == \"always\" && this._display.visible) {\n this.showTooltip();\n }\n }\n /**\r\n * @ignore\r\n * @todo should this be user-accessible?\r\n */\n dragStart(e) {\n this._dragEvent = e;\n this.events.stopParentDispatch();\n }\n /**\r\n * @ignore\r\n * @todo should this be user-accessible?\r\n */\n dragStop(e) {\n this._dragEvent = undefined;\n this._dragPoint = undefined;\n this.events.stopParentDispatch();\n if (this._isDragging) {\n this._isDragging = false;\n const type = \"dragstop\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this,\n originalEvent: e.originalEvent,\n point: e.point,\n simulated: e.simulated\n });\n }\n }\n }\n _handleOver() {\n if (!this.isHidden()) {\n if (this.get(\"active\") && this.states.lookup(\"hoverActive\")) {\n this.states.applyAnimate(\"hoverActive\");\n } else if (this.get(\"disabled\") && this.states.lookup(\"hoverDisabled\")) {\n this.states.applyAnimate(\"hoverDisabled\");\n } else {\n this.states.applyAnimate(\"hover\");\n }\n if (this.get(\"draggable\") && this._isDown && this.states.lookup(\"down\")) {\n this.states.applyAnimate(\"down\");\n }\n }\n }\n _handleOut() {\n if (!this.isHidden()) {\n if (this.get(\"active\") && this.states.lookup(\"active\")) {\n this.states.applyAnimate(\"active\");\n } else if (this.get(\"disabled\") && this.states.lookup(\"disabled\")) {\n this.states.applyAnimate(\"disabled\");\n } else {\n if (this.states.lookup(\"hover\") || this.states.lookup(\"hoverActive\")) {\n this.states.applyAnimate(\"default\");\n }\n }\n if (this.get(\"draggable\") && this._isDown && this.states.lookup(\"down\")) {\n this.states.applyAnimate(\"down\");\n }\n }\n }\n _handleUp(e) {\n if (!this.isHidden()) {\n if (this.get(\"active\") && this.states.lookup(\"active\")) {\n this.states.applyAnimate(\"active\");\n } else if (this.get(\"disabled\") && this.states.lookup(\"disabled\")) {\n this.states.applyAnimate(\"disabled\");\n } else if (this.states.lookup(\"down\")) {\n if (this.isHover()) {\n this.states.applyAnimate(\"hover\");\n } else {\n this.states.applyAnimate(\"default\");\n }\n }\n // @todo remove this once migrated to _downPoints\n this._downPoint = undefined;\n const pointerId = $utils.getPointerId(e.originalEvent);\n delete this._downPoints[pointerId];\n if ($object.keys(this._downPoints).length == 0) {\n this._isDown = false;\n }\n }\n }\n _hasMoved(e) {\n // @todo remove this once migrated to _downPoints\n // if (this._downPoint) {\n // \tconst x = Math.abs(this._downPoint.x - e.point.x);\n // \tconst y = Math.abs(this._downPoint.y - e.point.y);\n // \treturn (x > 5) || (y > 5);\n // }\n const pointerId = $utils.getPointerId(e.originalEvent);\n const downPoint = this._downPoints[pointerId];\n if (downPoint) {\n const x = Math.abs(downPoint.x - e.point.x);\n const y = Math.abs(downPoint.y - e.point.y);\n return x > 5 || y > 5;\n }\n return false;\n }\n _hasDown() {\n return $object.keys(this._downPoints).length > 0;\n }\n _handleDown(e) {\n const parent = this.parent;\n if (parent && !this.get(\"draggable\")) {\n parent._handleDown(e);\n }\n if (this.get(\"interactive\") && !this.isHidden()) {\n if (this.states.lookup(\"down\")) {\n this.states.applyAnimate(\"down\");\n }\n this._downPoint = {\n x: e.point.x,\n y: e.point.y\n };\n // @todo remove this once migrated to _downPoints\n this._isDown = true;\n const pointerId = $utils.getPointerId(e.originalEvent);\n this._downPoints[pointerId] = {\n x: e.point.x,\n y: e.point.y\n };\n }\n }\n /**\r\n * @ignore\r\n * @todo should this be user-accessible?\r\n */\n dragMove(e) {\n let dragEvent = this._dragEvent;\n if (dragEvent) {\n if (dragEvent.simulated && !e.simulated) {\n return true;\n }\n let angle = 0;\n let parent = this.parent;\n let scale = 1;\n while (parent != null) {\n angle += parent.get(\"rotation\", 0);\n parent = parent.parent;\n if (parent) {\n scale *= parent.get(\"scale\", 1);\n }\n }\n let x = (e.point.x - dragEvent.point.x) / scale;\n let y = (e.point.y - dragEvent.point.y) / scale;\n const events = this.events;\n if (dragEvent.simulated && !this._isDragging) {\n this._isDragging = true;\n this._dragEvent = e;\n this._dragPoint = {\n x: this.x(),\n y: this.y()\n };\n const type = \"dragstart\";\n if (events.isEnabled(type)) {\n events.dispatch(type, {\n type: type,\n target: this,\n originalEvent: e.originalEvent,\n point: e.point,\n simulated: e.simulated\n });\n }\n }\n if (this._isDragging) {\n let dragPoint = this._dragPoint;\n this.set(\"x\", dragPoint.x + x * $math.cos(angle) + y * $math.sin(angle));\n this.set(\"y\", dragPoint.y + y * $math.cos(angle) - x * $math.sin(angle));\n const type = \"dragged\";\n if (events.isEnabled(type)) {\n events.dispatch(type, {\n type: type,\n target: this,\n originalEvent: e.originalEvent,\n point: e.point,\n simulated: e.simulated\n });\n }\n } else {\n if (Math.hypot(x, y) > 5) {\n this._isDragging = true;\n this._dragEvent = e;\n this._dragPoint = {\n x: this.x(),\n y: this.y()\n };\n const type = \"dragstart\";\n if (events.isEnabled(type)) {\n events.dispatch(type, {\n type: type,\n target: this,\n originalEvent: e.originalEvent,\n point: e.point,\n simulated: e.simulated\n });\n }\n }\n }\n }\n }\n _updateSize() {}\n _getBounds() {\n this._localBounds = this._display.getLocalBounds();\n }\n /**\r\n * Returns depth (how deep in the hierachy of the content tree) of this\r\n * element.\r\n *\r\n * @return Depth\r\n */\n depth() {\n let self = this.parent;\n let depth = 0;\n while (true) {\n if (self) {\n ++depth;\n self = self.parent;\n } else {\n return depth;\n }\n }\n }\n /**\r\n * @ignore\r\n */\n markDirtySize() {\n this._sizeDirty = true;\n this.markDirty();\n }\n /**\r\n * @ignore\r\n */\n markDirtyBounds() {\n const display = this._display;\n if (this.get(\"isMeasured\")) {\n this._root._addDirtyBounds(this);\n display.isMeasured = true;\n display.invalidateBounds();\n const parent = this.parent;\n if (parent && this.get(\"position\") != \"absolute\") {\n if (parent.get(\"width\") == null || parent.get(\"height\") == null || parent.get(\"layout\")) {\n parent.markDirtyBounds();\n }\n }\n if (this.get(\"focusable\") && this.isFocus()) {\n this.markDirtyAccessibility();\n }\n }\n }\n /**\r\n * @ignore\r\n */\n markDirtyAccessibility() {\n //if (this._root.focused(this)) {\n this._root._invalidateAccessibility(this);\n //}\n }\n /**\r\n * @ignore\r\n */\n markDirtyLayer() {\n //this._display.markDirtyLayer(this.isDirty(\"opacity\") || this.isDirty(\"visible\")); https://codepen.io/team/amcharts/pen/gOWZPmP <- problems\n this._display.markDirtyLayer(true);\n }\n /**\r\n * @ignore\r\n */\n markDirty() {\n super.markDirty();\n this.markDirtyLayer();\n }\n _updateBounds() {\n const oldBounds = this._adjustedLocalBounds;\n let newBounds;\n // if display.visible == false, it still returns bounds\n if (!this.get(\"visible\") || !this.getPrivate(\"visible\") || this.get(\"forceHidden\")) {\n newBounds = {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n };\n this._localBounds = newBounds;\n this._adjustedLocalBounds = newBounds;\n } else {\n this._getBounds();\n this._fixMinBounds(this._localBounds);\n this.updatePivotPoint();\n this._adjustedLocalBounds = this._display.getAdjustedBounds(this._localBounds);\n newBounds = this._adjustedLocalBounds;\n }\n if (!oldBounds || oldBounds.left !== newBounds.left || oldBounds.top !== newBounds.top || oldBounds.right !== newBounds.right || oldBounds.bottom !== newBounds.bottom) {\n const eventType = \"boundschanged\";\n if (this.events.isEnabled(eventType)) {\n this.events.dispatch(eventType, {\n type: eventType,\n target: this\n });\n }\n if (this.parent) {\n this.parent.markDirty();\n this.parent.markDirtyBounds();\n }\n // Update tooltip position together with the Sprite\n if (this.getPrivate(\"showingTooltip\")) {\n this.showTooltip();\n }\n }\n }\n _fixMinBounds(bounds) {\n let minWidth = this.get(\"minWidth\", this.getPrivate(\"minWidth\"));\n let minHeight = this.get(\"minHeight\", this.getPrivate(\"minHeight\"));\n if ($type.isNumber(minWidth)) {\n if (bounds.right - bounds.left < minWidth) {\n bounds.right = bounds.left + minWidth;\n }\n }\n if ($type.isNumber(minHeight)) {\n if (bounds.bottom - bounds.top < minHeight) {\n bounds.bottom = bounds.top + minHeight;\n }\n }\n let privateWidth = this.getPrivate(\"width\");\n let privateHeight = this.getPrivate(\"height\");\n if ($type.isNumber(privateWidth)) {\n if (privateWidth > 0) {\n bounds.right = bounds.left + privateWidth;\n } else {\n bounds.left = bounds.right + privateWidth;\n }\n }\n if ($type.isNumber(privateHeight)) {\n if (privateHeight > 0) {\n bounds.bottom = bounds.top + privateHeight;\n } else {\n bounds.top = bounds.bottom + privateHeight;\n }\n }\n }\n _removeParent(parent) {\n if (parent) {\n parent.children.removeValue(this);\n $array.removeFirst(parent._percentageSizeChildren, this);\n $array.removeFirst(parent._percentagePositionChildren, this);\n }\n }\n _clearDirty() {\n super._clearDirty();\n this._sizeDirty = false;\n this._statesHandled = false;\n }\n /**\r\n * Simulate hover over element.\r\n */\n hover() {\n if (!this.isDisposed()) {\n this.showTooltip();\n this._handleOver();\n }\n }\n /**\r\n * Simulate unhover over element.\r\n */\n unhover() {\n if (!this.isDisposed()) {\n this.hideTooltip();\n this._handleOut();\n }\n }\n /**\r\n * Shows element's [[Tooltip]].\r\n */\n showTooltip(point) {\n if (!this.isDisposed()) {\n const tooltip = this.getTooltip();\n const tooltipText = this.get(\"tooltipText\");\n const tooltipHTML = this.get(\"tooltipHTML\");\n if ((tooltipText || tooltipHTML) && tooltip) {\n const tooltipPosition = this.get(\"tooltipPosition\");\n const tooltipTarget = this.getPrivate(\"tooltipTarget\", this);\n if (tooltipPosition == \"fixed\" || !point) {\n this._display._setMatrix();\n point = this.toGlobal(tooltipTarget._getTooltipPoint());\n }\n tooltip.set(\"pointTo\", point);\n tooltip.set(\"tooltipTarget\", tooltipTarget);\n if (!tooltip.get(\"x\")) {\n tooltip.set(\"x\", point.x);\n }\n if (!tooltip.get(\"y\")) {\n tooltip.set(\"y\", point.y);\n }\n if (tooltipText) {\n tooltip.label.set(\"text\", tooltipText);\n }\n if (tooltipHTML) {\n tooltip.label.set(\"html\", tooltipHTML);\n }\n const dataItem = this.dataItem;\n if (dataItem) {\n tooltip.label._setDataItem(dataItem);\n }\n if (this.get(\"showTooltipOn\") == \"always\" && (point.x < 0 || point.x > this._root.width() || point.y < 0 || point.y > this._root.height())) {\n this.hideTooltip();\n return;\n }\n tooltip.label.text.markDirtyText();\n const promise = tooltip.show();\n this.setPrivateRaw(\"showingTooltip\", true);\n return promise;\n }\n }\n }\n /**\r\n * Hides element's [[Tooltip]].\r\n */\n hideTooltip() {\n const tooltip = this.getTooltip();\n if (tooltip) {\n if (tooltip.get(\"tooltipTarget\") == this.getPrivate(\"tooltipTarget\", this) || this.get(\"tooltip\") == tooltip) {\n let timeout = tooltip.get(\"keepTargetHover\") && tooltip.get(\"stateAnimationDuration\", 0) == 0 ? 400 : undefined;\n const promise = tooltip.hide(timeout);\n this.setPrivateRaw(\"showingTooltip\", false);\n return promise;\n }\n }\n }\n _getTooltipPoint() {\n const bounds = this._localBounds;\n if (bounds) {\n let x = 0;\n let y = 0;\n if (!this.get(\"isMeasured\")) {\n x = $utils.relativeToValue(this.get(\"tooltipX\", 0), this.width());\n y = $utils.relativeToValue(this.get(\"tooltipY\", 0), this.height());\n } else {\n x = bounds.left + $utils.relativeToValue(this.get(\"tooltipX\", 0), bounds.right - bounds.left);\n y = bounds.top + $utils.relativeToValue(this.get(\"tooltipY\", 0), bounds.bottom - bounds.top);\n }\n return {\n x,\n y\n };\n }\n return {\n x: 0,\n y: 0\n };\n }\n /**\r\n * Returns [[Tooltip]] used for this element.\r\n *\r\n * @return Tooltip\r\n */\n getTooltip() {\n let tooltip = this.get(\"tooltip\");\n if (!tooltip) {\n let parent = this.parent;\n if (parent) {\n return parent.getTooltip();\n }\n } else {\n return tooltip;\n }\n }\n _updatePosition() {\n const parent = this.parent;\n let dx = this.get(\"dx\", 0);\n let dy = this.get(\"dy\", 0);\n let x = this.get(\"x\");\n let _x = this.getPrivate(\"x\");\n let xx = 0;\n let yy = 0;\n const position = this.get(\"position\");\n if (x instanceof Percent) {\n if (parent) {\n x = parent.innerWidth() * x.value + parent.get(\"paddingLeft\", 0);\n } else {\n x = 0;\n }\n }\n if ($type.isNumber(x)) {\n xx = x + dx;\n } else {\n if (_x != null) {\n xx = _x;\n } else if (parent) {\n if (position == \"relative\") {\n xx = parent.get(\"paddingLeft\", 0) + dx;\n }\n }\n }\n let y = this.get(\"y\");\n let _y = this.getPrivate(\"y\");\n if (y instanceof Percent) {\n if (parent) {\n y = parent.innerHeight() * y.value + parent.get(\"paddingTop\", 0);\n } else {\n y = 0;\n }\n }\n if ($type.isNumber(y)) {\n yy = y + dy;\n } else {\n if (_y != null) {\n yy = _y;\n } else if (parent) {\n if (position == \"relative\") {\n yy = parent.get(\"paddingTop\", 0) + dy;\n }\n }\n }\n const display = this._display;\n if (display.x != xx || display.y != yy) {\n display.invalidateBounds();\n display.x = xx;\n display.y = yy;\n const eventType = \"positionchanged\";\n if (this.events.isEnabled(eventType)) {\n this.events.dispatch(eventType, {\n type: eventType,\n target: this\n });\n }\n }\n // Update tooltip position together with the Sprite\n if (this.getPrivate(\"showingTooltip\")) {\n this.showTooltip();\n }\n }\n /**\r\n * Returns element's actual X position in pixels.\r\n *\r\n * @return X (px)\r\n */\n x() {\n let x = this.get(\"x\");\n let _x = this.getPrivate(\"x\");\n const parent = this.parent;\n if (parent) {\n if (x instanceof Percent) {\n return $utils.relativeToValue(x, parent.innerWidth()) + parent.get(\"paddingLeft\", 0);\n } else {\n if (!$type.isNumber(x)) {\n if (_x != null) {\n return _x;\n } else {\n return parent.get(\"paddingLeft\", this._display.x);\n }\n } else {\n return x;\n }\n }\n }\n return this._display.x;\n }\n /**\r\n * Returns element's actual Y position in pixels.\r\n *\r\n * @return Y (px)\r\n */\n y() {\n let _y = this.getPrivate(\"y\");\n if (_y != null) {\n return _y;\n }\n let y = this.get(\"y\");\n const parent = this.parent;\n if (parent) {\n if (y instanceof Percent) {\n return $utils.relativeToValue(y, parent.innerHeight()) + parent.get(\"paddingTop\", 0);\n } else {\n if (!$type.isNumber(y)) {\n if (_y != null) {\n return _y;\n } else {\n return parent.get(\"paddingTop\", this._display.y);\n }\n } else {\n return y;\n }\n }\n }\n return this._display.y;\n }\n _dispose() {\n super._dispose();\n this._display.dispose();\n this._removeTemplateField();\n this._removeParent(this.parent);\n this._root._removeFocusElement(this);\n const tooltip = this.get(\"tooltip\");\n if (tooltip) {\n tooltip.dispose();\n }\n this.markDirty();\n }\n /**\r\n * @ignore\r\n */\n adjustedLocalBounds() {\n this._fixMinBounds(this._adjustedLocalBounds);\n return this._adjustedLocalBounds;\n }\n /**\r\n * Returns local coordinates of the element's bounds.\r\n *\r\n * @ignore\r\n * @return Global bounds\r\n */\n localBounds() {\n return this._localBounds;\n }\n /**\r\n * Returns adjusted local coordinates of the element's bounds.\r\n *\r\n * @ignore\r\n * @return Global bounds\r\n */\n bounds() {\n const bounds = this._adjustedLocalBounds;\n const x = this.x();\n const y = this.y();\n return {\n left: bounds.left + x,\n right: bounds.right + x,\n top: bounds.top + y,\n bottom: bounds.bottom + y\n };\n }\n /**\r\n * Returns global coordinates of the element's bounds.\r\n *\r\n * @ignore\r\n * @return Global bounds\r\n */\n globalBounds() {\n const bounds = this.localBounds();\n const p0 = this.toGlobal({\n x: bounds.left,\n y: bounds.top\n });\n const p1 = this.toGlobal({\n x: bounds.right,\n y: bounds.top\n });\n const p2 = this.toGlobal({\n x: bounds.right,\n y: bounds.bottom\n });\n const p3 = this.toGlobal({\n x: bounds.left,\n y: bounds.bottom\n });\n return {\n left: Math.min(p0.x, p1.x, p2.x, p3.x),\n top: Math.min(p0.y, p1.y, p2.y, p3.y),\n right: Math.max(p0.x, p1.x, p2.x, p3.x),\n bottom: Math.max(p0.y, p1.y, p2.y, p3.y)\n };\n }\n _onShow(_duration) {}\n _onHide(_duration) {}\n /**\r\n * Plays initial reveal animation regardless if element is currently hidden\r\n * or visible.\r\n *\r\n * @param duration Duration of the animation in milliseconds\r\n * @param delay Delay showing of the element by X milliseconds\r\n * @return Promise\r\n */\n appear(duration, delay) {\n return __awaiter(this, void 0, void 0, function* () {\n yield this.hide(0);\n if (delay) {\n return new Promise((success, _error) => {\n this.setTimeout(() => {\n success(this.show(duration));\n }, delay);\n });\n } else {\n return this.show(duration);\n }\n });\n }\n /**\r\n * Shows currently hidden element and returns a `Promise` which completes\r\n * when all showing animations are finished.\r\n *\r\n * ```TypeScript\r\n * series.show().then(function(ev) {\r\n * console.log(\"Series is now fully visible\");\r\n * })\r\n * ```\r\n * ```JavaScript\r\n * series.show().then(function(ev) {\r\n * console.log(\"Series is now fully visible\");\r\n * })\r\n * ```\r\n *\r\n * @return Promise\r\n */\n show(duration) {\n return __awaiter(this, void 0, void 0, function* () {\n if (!this._isShowing) {\n this._isHidden = false;\n this._isShowing = true;\n this._isHiding = false;\n if (this.states.lookup(\"default\").get(\"visible\")) {\n this.set(\"visible\", true);\n }\n this._onShow(duration);\n const animations = this.states.applyAnimate(\"default\", duration);\n yield waitForAnimations(animations);\n this._isShowing = false;\n }\n });\n }\n /**\r\n * Hides the element and returns a `Promise` which completes when all hiding\r\n * animations are finished.\r\n *\r\n * ```TypeScript\r\n * series.hide().then(function(ev) {\r\n * console.log(\"Series finished hiding\");\r\n * })\r\n * ```\r\n * ```JavaScript\r\n * series.hide().then(function(ev) {\r\n * console.log(\"Series finished hiding\");\r\n * })\r\n * ```\r\n *\r\n * @return Promise\r\n */\n hide(duration) {\n return __awaiter(this, void 0, void 0, function* () {\n if (!this._isHiding && !this._isHidden) {\n this._isHiding = true;\n this._isShowing = false;\n let state = this.states.lookup(\"hidden\");\n if (!state) {\n state = this.states.create(\"hidden\", {\n \"opacity\": 0,\n \"visible\": false\n });\n }\n this._isHidden = true;\n this._onHide(duration);\n const animations = this.states.applyAnimate(\"hidden\", duration);\n yield waitForAnimations(animations);\n this._isHiding = false;\n }\n });\n }\n /**\r\n * Returns `true` if this element is currently hidden.\r\n *\r\n * @return Is hidden?\r\n */\n isHidden() {\n return this._isHidden;\n }\n /**\r\n * Returns `true` if this element is currently animating to a default state.\r\n *\r\n * @return Is showing?\r\n */\n isShowing() {\n return this._isShowing;\n }\n /**\r\n * Returns `true` if this element is currently animating to a hidden state.\r\n *\r\n * @return Is hiding?\r\n */\n isHiding() {\n return this._isHiding;\n }\n /**\r\n * Returns `true` if this element is currently hovered by a pointer.\r\n *\r\n * @return Is hovered?\r\n */\n isHover() {\n return this._display.hovering();\n }\n /**\r\n * Returns `true` if this element does currently have focus.\r\n *\r\n * @return Is focused?\r\n */\n isFocus() {\n return this._root.focused(this);\n }\n /**\r\n * Returns `true` if this element is currently being dragged.\r\n *\r\n * @return Is dragged?\r\n */\n isDragging() {\n return this._isDragging;\n }\n /**\r\n * Returns `false` if if either public or private setting `visible` is set\r\n * to `false`, or `forceHidden` is set to `true`.\r\n *\r\n * @return Visible?\r\n */\n isVisible() {\n if (this.get(\"visible\") && this.getPrivate(\"visible\") && !this.get(\"forceHidden\")) {\n return true;\n }\n return false;\n }\n /**\r\n * Same as `isVisible()`, except it checks all ascendants, too.\r\n *\r\n * @since 5.2.7\r\n * @return Visible?\r\n */\n isVisibleDeep() {\n return this._parent ? this._parent.isVisibleDeep() && this.isVisible() : this.isVisible();\n }\n /**\r\n * Returns an actual opacity of the element, taking into account all parents.\r\n *\r\n * @return Opacity\r\n * @since 5.2.11\r\n */\n compositeOpacity() {\n const opacity = this.get(\"opacity\", 1);\n return this._parent ? this._parent.compositeOpacity() * opacity : opacity;\n }\n /**\r\n * Returns an actual scale of the element, taking into account all parents.\r\n *\r\n * @return Opacity\r\n * @since 5.9.2\r\n */\n compositeScale() {\n const scale = this.get(\"scale\", 1);\n return this._parent ? this._parent.compositeScale() * scale : scale;\n }\n /**\r\n * Returns an actual roation of the element, taking into account all parents.\r\n *\r\n * @return Opacity\r\n * @since 5.9.2\r\n */\n compositeRotation() {\n const rotation = this.get(\"rotation\", 0);\n return this._parent ? this._parent.compositeRotation() + rotation : rotation;\n }\n /**\r\n * Returns width of this element in pixels.\r\n *\r\n * @return Width (px)\r\n */\n width() {\n let width = this.get(\"width\");\n let maxWidth = this.get(\"maxWidth\", this.getPrivate(\"maxWidth\"));\n let minWidth = this.get(\"minWidth\", this.getPrivate(\"minWidth\"));\n let privateWidth = this.getPrivate(\"width\");\n let w = 0;\n if ($type.isNumber(privateWidth)) {\n w = privateWidth;\n } else {\n if (width == null) {\n if (this._adjustedLocalBounds) {\n w = this._adjustedLocalBounds.right - this._adjustedLocalBounds.left;\n }\n } else {\n if (width instanceof Percent) {\n const parent = this.parent;\n if (parent) {\n w = parent.innerWidth() * width.value;\n } else {\n w = this._root.width() * width.value;\n }\n } else if ($type.isNumber(width)) {\n w = width;\n }\n }\n }\n if ($type.isNumber(minWidth)) {\n w = Math.max(minWidth, w);\n }\n if ($type.isNumber(maxWidth)) {\n w = Math.min(maxWidth, w);\n }\n return w;\n }\n /**\r\n * Returns maximum allowed width of this element in pixels.\r\n *\r\n * @return Maximum width (px)\r\n */\n maxWidth() {\n let maxWidth = this.get(\"maxWidth\", this.getPrivate(\"maxWidth\"));\n if ($type.isNumber(maxWidth)) {\n return maxWidth;\n } else {\n let width = this.get(\"width\");\n if ($type.isNumber(width)) {\n return width;\n }\n }\n const parent = this.parent;\n if (parent) {\n return parent.innerWidth();\n }\n return this._root.width();\n }\n /**\r\n * Returns maximum allowed height of this element in pixels.\r\n *\r\n * @return Maximum height (px)\r\n */\n maxHeight() {\n let maxHeight = this.get(\"maxHeight\", this.getPrivate(\"maxHeight\"));\n if ($type.isNumber(maxHeight)) {\n return maxHeight;\n } else {\n let height = this.get(\"height\");\n if ($type.isNumber(height)) {\n return height;\n }\n }\n const parent = this.parent;\n if (parent) {\n return parent.innerHeight();\n }\n return this._root.height();\n }\n /**\r\n * Returns height of this element in pixels.\r\n *\r\n * @return Height (px)\r\n */\n height() {\n let height = this.get(\"height\");\n let maxHeight = this.get(\"maxHeight\", this.getPrivate(\"maxHeight\"));\n let minHeight = this.get(\"minHeight\", this.getPrivate(\"minHeight\"));\n let privateHeight = this.getPrivate(\"height\");\n let h = 0;\n if ($type.isNumber(privateHeight)) {\n h = privateHeight;\n } else {\n if (height == null) {\n if (this._adjustedLocalBounds) {\n h = this._adjustedLocalBounds.bottom - this._adjustedLocalBounds.top;\n }\n } else {\n if (height instanceof Percent) {\n const parent = this.parent;\n if (parent) {\n h = parent.innerHeight() * height.value;\n } else {\n h = this._root.height() * height.value;\n }\n } else if ($type.isNumber(height)) {\n h = height;\n }\n }\n }\n if ($type.isNumber(minHeight)) {\n h = Math.max(minHeight, h);\n }\n if ($type.isNumber(maxHeight)) {\n h = Math.min(maxHeight, h);\n }\n return h;\n }\n _findStaticTemplate(f) {\n // templateField overrides template\n if (this._templateField && f(this._templateField)) {\n return this._templateField;\n }\n return super._findStaticTemplate(f);\n }\n _walkParents(f) {\n if (this._parent) {\n this._walkParent(f);\n }\n }\n _walkParent(f) {\n if (this._parent) {\n this._parent._walkParent(f);\n }\n f(this);\n }\n /**\r\n * Parent [[Container]] of this element.\r\n *\r\n * @return Parent container\r\n */\n get parent() {\n return this._parent;\n }\n _setParent(parent, updateChildren = false) {\n const prevParent = this._parent;\n if (parent !== prevParent) {\n this.markDirtyBounds();\n parent.markDirty();\n this._parent = parent;\n if (updateChildren) {\n this._removeParent(prevParent);\n if (parent) {\n this._addPercentageSizeChildren();\n this._addPercentagePositionChildren();\n }\n }\n this.markDirtyPosition();\n this._applyThemes();\n }\n }\n /**\r\n * Returns an instance of [[NumberFormatter]] used in this element.\r\n *\r\n * If this element does not have it set, global one form [[Root]] is used.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/using-formatters/} for more info\r\n * @return NumberFormatter instace\r\n */\n getNumberFormatter() {\n return this.get(\"numberFormatter\", this._root.numberFormatter);\n }\n /**\r\n * Returns an instance of [[DateFormatter]] used in this element.\r\n *\r\n * If this element does not have it set, global one form [[Root]] is used.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/using-formatters/} for more info\r\n * @return DateFormatter instace\r\n */\n getDateFormatter() {\n return this.get(\"dateFormatter\", this._root.dateFormatter);\n }\n /**\r\n * Returns an instance of [[DurationFormatter]] used in this element.\r\n *\r\n * If this element does not have it set, global one form [[Root]] is used.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/using-formatters/} for more info\r\n * @return DurationFormatter instace\r\n */\n getDurationFormatter() {\n return this.get(\"durationFormatter\", this._root.durationFormatter);\n }\n /**\r\n * Converts X/Y coordinate within this element to a global coordinate.\r\n *\r\n * @param point Local coordinate\r\n * @return Global coordinate\r\n */\n toGlobal(point) {\n return this._display.toGlobal(point);\n }\n /**\r\n * Converts global X/Y coordinate to a coordinate within this element.\r\n *\r\n * @param point Global coordinate\r\n * @return Local coordinate\r\n */\n toLocal(point) {\n return this._display.toLocal(point);\n }\n _getDownPoint() {\n const id = this._getDownPointId();\n if (id) {\n return this._downPoints[id];\n }\n }\n _getDownPointId() {\n if (this._downPoints) {\n return $object.keysOrdered(this._downPoints, (a, b) => {\n if (a > b) {\n return 1;\n }\n if (a < b) {\n return -1;\n }\n return 0;\n })[0];\n }\n }\n /**\r\n * Moves sprite to the end of the parent's children array.\r\n *\r\n * Depending on `layout` setting of the parten container, it may effect the\r\n * positioning or overlapping order of the elements.\r\n */\n toFront() {\n const parent = this.parent;\n if (parent) {\n parent.children.moveValue(this, parent.children.length - 1);\n }\n }\n /**\r\n * Moves sprite to the beginning of the parent's children array.\r\n *\r\n * Depending on `layout` setting of the parten container, it may effect the\r\n * positioning or overlapping order of the elements.\r\n */\n toBack() {\n const parent = this.parent;\n if (parent) {\n parent.children.moveValue(this, 0);\n }\n }\n}\nObject.defineProperty(Sprite, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Sprite\"\n});\nObject.defineProperty(Sprite, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Entity.classNames.concat([Sprite.className])\n});\n","import { Entity } from \"../../util/Entity\";\n/**\r\n * Base class for patterns.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/colors-gradients-and-patterns/patterns/} for more info\r\n */\nexport class Pattern extends Entity {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_display\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeGraphics()\n });\n Object.defineProperty(this, \"_backgroundDisplay\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeGraphics()\n });\n Object.defineProperty(this, \"_clear\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_pattern\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n _afterNew() {\n // Applying themes because pattern will not have parent\n super._afterNewApplyThemes();\n }\n get pattern() {\n return this._pattern;\n }\n _draw() {}\n _beforeChanged() {\n super._beforeChanged();\n if (this.isDirty(\"repetition\") || this.isDirty(\"width\") || this.isDirty(\"height\") || this.isDirty(\"rotation\") || this.isDirty(\"strokeWidth\") || this.isDirty(\"strokeDasharray\") || this.isDirty(\"strokeDashoffset\") || this.isDirty(\"colorOpacity\") || this.isDirty(\"fillOpacity\")) {\n this._clear = true;\n }\n this._checkDirtyFill();\n }\n _checkDirtyFill() {\n if (this.isDirty(\"color\") || this.isDirty(\"fill\")) {\n this._clear = true;\n }\n }\n _changed() {\n super._changed();\n if (this._clear) {\n const repetition = this.get(\"repetition\", \"\");\n const width = this.get(\"width\", 100);\n const height = this.get(\"height\", 100);\n const fill = this.get(\"fill\");\n const fillOpacity = this.get(\"fillOpacity\", 1);\n const backgroundDisplay = this._backgroundDisplay;\n const display = this._display;\n display.clear();\n backgroundDisplay.clear();\n if (fill && fillOpacity > 0) {\n backgroundDisplay.beginFill(fill, fillOpacity);\n backgroundDisplay.drawRect(0, 0, width, height);\n backgroundDisplay.endFill();\n }\n display.angle = this.get(\"rotation\", 0);\n //display.pivot = { x: width / 2, y: height / 2 };\n this._draw();\n this._pattern = this._root._renderer.createPattern(display, backgroundDisplay, repetition, width, height);\n }\n this._clear = false;\n }\n}\nObject.defineProperty(Pattern, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Pattern\"\n});\nObject.defineProperty(Pattern, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Entity.classNames.concat([Pattern.className])\n});\n","import { Pattern } from \"./Pattern\";\n/**\r\n * Picture pattern.\r\n *\r\n * @since 5.2.15\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/colors-gradients-and-patterns/patterns/} for more info\r\n */\nexport class PicturePattern extends Pattern {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_image\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n _beforeChanged() {\n super._beforeChanged();\n this._clear = true;\n if (this.isDirty(\"src\")) {\n this._load();\n }\n const canvas = this.get(\"canvas\");\n if (canvas) {\n this.set(\"width\", canvas.width);\n this.set(\"height\", canvas.height);\n }\n }\n _draw() {\n super._draw();\n const image = this._image;\n if (image) {\n const patternWidth = this.get(\"width\", 100);\n const patternHeight = this.get(\"height\", 100);\n // Fit\n const fit = this.get(\"fit\", \"image\");\n let width = 0;\n let height = 0;\n if (fit == \"pattern\") {\n width = patternWidth;\n height = patternHeight;\n } else {\n width = image.width;\n height = image.height;\n if (fit == \"image\") {\n this.set(\"width\", width);\n this.set(\"height\", height);\n }\n }\n // Position\n const centered = this.get(\"centered\", true);\n let x = 0;\n let y = 0;\n if (centered) {\n x = patternWidth / 2 - width / 2;\n y = patternHeight / 2 - height / 2;\n }\n this._display.image(image, width, height, x, y);\n }\n const canvas = this.get(\"canvas\");\n if (canvas) {\n this._display.image(canvas, canvas.width, canvas.height, 0, 0);\n }\n }\n _load() {\n const src = this.get(\"src\");\n if (src) {\n const image = new Image();\n //image.crossOrigin = \"Anonymous\";\n image.src = src;\n image.decode().then(() => {\n this._image = image;\n this._draw();\n if (this.events.isEnabled(\"loaded\")) {\n this.events.dispatch(\"loaded\", {\n type: \"loaded\",\n target: this\n });\n }\n }).catch(_error => {\n // TODO: maybe raise error?\n });\n }\n }\n}\nObject.defineProperty(PicturePattern, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"PicturePattern\"\n});\nObject.defineProperty(PicturePattern, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Pattern.classNames.concat([PicturePattern.className])\n});\n","/**\r\n * From https://github.com/pixijs/pixi.js/blob/3dd0ff9a935f0bc13a09aefff9eb2872f02c51b9/packages/canvas/canvas-renderer/src/utils/mapCanvasBlendModesToPixi.ts#L13\r\n */\nexport var BlendMode = /*#__PURE__*/function (BlendMode) {\n BlendMode[\"ADD\"] = \"lighter\";\n BlendMode[\"COLOR\"] = \"color\";\n BlendMode[\"COLOR_BURN\"] = \"color-burn\";\n BlendMode[\"COLOR_DODGE\"] = \"color-dodge\";\n BlendMode[\"DARKEN\"] = \"darken\";\n BlendMode[\"DIFFERENCE\"] = \"difference\";\n BlendMode[\"DST_OVER\"] = \"destination-over\";\n BlendMode[\"EXCLUSION\"] = \"exclusion\";\n BlendMode[\"HARD_LIGHT\"] = \"hard-light\";\n BlendMode[\"HUE\"] = \"hue\";\n BlendMode[\"LIGHTEN\"] = \"lighten\";\n BlendMode[\"LUMINOSITY\"] = \"luminosity\";\n BlendMode[\"MULTIPLY\"] = \"multiply\";\n BlendMode[\"NORMAL\"] = \"source-over\";\n BlendMode[\"OVERLAY\"] = \"overlay\";\n BlendMode[\"SATURATION\"] = \"saturation\";\n BlendMode[\"SCREEN\"] = \"screen\";\n BlendMode[\"SOFT_LIGHT\"] = \"soft-light\";\n BlendMode[\"SRC_ATOP\"] = \"source-atop\";\n BlendMode[\"XOR\"] = \"xor\";\n return BlendMode;\n}(BlendMode || {});\n\n","import { PicturePattern } from \"../render/patterns/PicturePattern\";\nimport { Sprite } from \"./Sprite\";\nimport { BlendMode } from \"./backend/Renderer\";\nimport * as $type from \"../util/Type\";\nimport * as $array from \"../util/Array\";\nexport const visualSettings = [\"fill\", \"fillOpacity\", \"stroke\", \"strokeWidth\", \"strokeOpacity\", \"fillPattern\", \"strokePattern\", \"fillGradient\", \"strokeGradient\", \"strokeDasharray\", \"strokeDashoffset\", \"shadowBlur\", \"shadowColor\", \"shadowOpacity\", \"shadowOffsetX\", \"shadowOffsetY\", \"blur\", \"sepia\", \"invert\", \"brightness\", \"hue\", \"contrast\", \"saturate\"];\n/**\r\n * Base class used for drawing shapes.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/graphics/} for more info\r\n * @important\r\n */\nexport class Graphics extends Sprite {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_display\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeGraphics()\n });\n Object.defineProperty(this, \"_clear\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n _beforeChanged() {\n super._beforeChanged();\n if (this.isDirty(\"draw\") || this.isDirty(\"svgPath\")) {\n this.markDirtyBounds();\n }\n if (this.isDirty(\"fill\") || this.isDirty(\"stroke\") || this.isDirty(\"visible\") || this.isDirty(\"forceHidden\") || this.isDirty(\"scale\") || this.isDirty(\"fillGradient\") || this.isDirty(\"strokeGradient\") || this.isDirty(\"fillPattern\") || this.isDirty(\"strokePattern\") || this.isDirty(\"fillOpacity\") || this.isDirty(\"strokeOpacity\") || this.isDirty(\"strokeWidth\") || this.isDirty(\"draw\") || this.isDirty(\"blendMode\") || this.isDirty(\"strokeDasharray\") || this.isDirty(\"strokeDashoffset\") || this.isDirty(\"svgPath\") || this.isDirty(\"lineJoin\") || this.isDirty(\"shadowColor\") || this.isDirty(\"shadowBlur\") || this.isDirty(\"shadowOffsetX\") || this.isDirty(\"shadowOffsetY\")) {\n this._clear = true;\n }\n this._display.crisp = this.get(\"crisp\", false);\n if (this.isDirty(\"fillGradient\")) {\n const gradient = this.get(\"fillGradient\");\n if (gradient) {\n this._display.isMeasured = true;\n const gradientTarget = gradient.get(\"target\");\n if (gradientTarget) {\n this._disposers.push(gradientTarget.events.on(\"boundschanged\", () => {\n this._markDirtyKey(\"fill\");\n }));\n this._disposers.push(gradientTarget.events.on(\"positionchanged\", () => {\n this._markDirtyKey(\"fill\");\n }));\n }\n }\n }\n if (this.isDirty(\"strokeGradient\")) {\n const gradient = this.get(\"strokeGradient\");\n if (gradient) {\n this._display.isMeasured = true;\n const gradientTarget = gradient.get(\"target\");\n if (gradientTarget) {\n this._disposers.push(gradientTarget.events.on(\"boundschanged\", () => {\n this._markDirtyKey(\"stroke\");\n }));\n this._disposers.push(gradientTarget.events.on(\"positionchanged\", () => {\n this._markDirtyKey(\"stroke\");\n }));\n }\n }\n }\n }\n _changed() {\n super._changed();\n if (this._clear) {\n this.markDirtyBounds();\n this.markDirtyLayer();\n this._display.clear();\n let strokeDasharray = this.get(\"strokeDasharray\");\n if ($type.isNumber(strokeDasharray)) {\n if (strokeDasharray < 0.5) {\n strokeDasharray = [0];\n } else {\n strokeDasharray = [strokeDasharray];\n }\n }\n this._display.setLineDash(strokeDasharray);\n const strokeDashoffset = this.get(\"strokeDashoffset\");\n if (strokeDashoffset) {\n this._display.setLineDashOffset(strokeDashoffset);\n }\n const blendMode = this.get(\"blendMode\", BlendMode.NORMAL);\n this._display.blendMode = blendMode;\n const draw = this.get(\"draw\");\n if (draw && typeof draw === \"function\") {\n draw(this._display, this);\n }\n const svgPath = this.get(\"svgPath\");\n if (svgPath != null) {\n this._display.svgPath(svgPath);\n }\n }\n }\n _afterChanged() {\n super._afterChanged();\n if (this._clear) {\n const fill = this.get(\"fill\");\n const fillGradient = this.get(\"fillGradient\");\n const fillPattern = this.get(\"fillPattern\");\n const fillOpacity = this.get(\"fillOpacity\");\n const stroke = this.get(\"stroke\");\n const strokeGradient = this.get(\"strokeGradient\");\n const strokePattern = this.get(\"strokePattern\");\n const shadowColor = this.get(\"shadowColor\");\n const shadowBlur = this.get(\"shadowBlur\");\n const shadowOffsetX = this.get(\"shadowOffsetX\");\n const shadowOffsetY = this.get(\"shadowOffsetY\");\n const shadowOpacity = this.get(\"shadowOpacity\");\n if (shadowColor && (shadowBlur || shadowOffsetX || shadowOffsetY)) {\n this._display.shadow(shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY, shadowOpacity);\n }\n if (fill && !fillGradient) {\n this._display.beginFill(fill, fillOpacity);\n this._display.endFill();\n }\n if (fillGradient) {\n if (fill) {\n const stops = fillGradient.get(\"stops\", []);\n if (stops.length) {\n $array.each(stops, stop => {\n if ((!stop.color || stop.colorInherited) && fill) {\n stop.color = fill;\n stop.colorInherited = true;\n }\n if (stop.opacity == null || stop.opacityInherited) {\n stop.opacity = fillOpacity;\n stop.opacityInherited = true;\n }\n });\n }\n }\n const gradient = fillGradient.getFill(this);\n if (gradient) {\n this._display.beginFill(gradient, fillOpacity);\n this._display.endFill();\n }\n }\n if (fillPattern) {\n const pattern = fillPattern.pattern;\n if (pattern) {\n this._display.beginFill(pattern, fillOpacity);\n this._display.endFill();\n if (fillPattern instanceof PicturePattern) {\n fillPattern.events.once(\"loaded\", () => {\n this._clear = true;\n this.markDirty();\n });\n }\n }\n }\n if (stroke || strokeGradient || strokePattern) {\n const strokeOpacity = this.get(\"strokeOpacity\");\n let strokeWidth = this.get(\"strokeWidth\", 1);\n if (this.get(\"nonScalingStroke\")) {\n strokeWidth = strokeWidth / this.get(\"scale\", 1);\n }\n if (this.get(\"crisp\")) {\n strokeWidth /= this._root._renderer.resolution;\n }\n const lineJoin = this.get(\"lineJoin\");\n if (stroke && !strokeGradient) {\n this._display.lineStyle(strokeWidth, stroke, strokeOpacity, lineJoin);\n this._display.endStroke();\n }\n if (strokeGradient) {\n const stops = strokeGradient.get(\"stops\", []);\n if (stops.length) {\n $array.each(stops, stop => {\n if ((!stop.color || stop.colorInherited) && stroke) {\n stop.color = stroke;\n stop.colorInherited = true;\n }\n if (stop.opacity == null || stop.opacityInherited) {\n stop.opacity = strokeOpacity;\n stop.opacityInherited = true;\n }\n });\n }\n const gradient = strokeGradient.getFill(this);\n if (gradient) {\n this._display.lineStyle(strokeWidth, gradient, strokeOpacity, lineJoin);\n this._display.endStroke();\n }\n }\n if (strokePattern) {\n /*\r\n let changed = false;\r\n \r\n if (stroke && (!strokePattern.get(\"color\") || strokePattern.get(\"colorInherited\"))) {\r\n strokePattern.set(\"color\", stroke);\r\n strokePattern.set(\"colorInherited\", true);\r\n changed = true;\r\n }\r\n if (changed) {\r\n // @todo: is this OK?\r\n strokePattern._changed();\r\n }\r\n */\n let pattern = strokePattern.pattern;\n if (pattern) {\n this._display.lineStyle(strokeWidth, pattern, strokeOpacity, lineJoin);\n this._display.endStroke();\n if (strokePattern instanceof PicturePattern) {\n strokePattern.events.once(\"loaded\", () => {\n this._clear = true;\n this.markDirty();\n });\n }\n }\n }\n }\n if (this.getPrivate(\"showingTooltip\")) {\n this.showTooltip();\n }\n }\n this._clear = false;\n }\n}\nObject.defineProperty(Graphics, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Graphics\"\n});\nObject.defineProperty(Graphics, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Sprite.classNames.concat([Graphics.className])\n});\n","import { Graphics } from \"./Graphics\";\n/**\r\n * Draws a rectangle.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/graphics/} for more info\r\n * @important\r\n */\nexport class Rectangle extends Graphics {\n _afterNew() {\n super._afterNew();\n this._display.isMeasured = true;\n this.setPrivateRaw(\"trustBounds\", true);\n }\n _beforeChanged() {\n super._beforeChanged();\n if (this.isDirty(\"width\") || this.isDirty(\"height\") || this.isPrivateDirty(\"width\") || this.isPrivateDirty(\"height\")) {\n this._clear = true;\n }\n }\n _changed() {\n super._changed();\n if (this._clear && !this.get(\"draw\")) {\n this._draw();\n }\n }\n _draw() {\n this._display.drawRect(0, 0, this.width(), this.height());\n }\n _updateSize() {\n this.markDirty();\n this._clear = true;\n }\n}\nObject.defineProperty(Rectangle, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Rectangle\"\n});\nObject.defineProperty(Rectangle, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Graphics.classNames.concat([Rectangle.className])\n});\n","import { Entity } from \"../util/Entity\";\nexport function eachChildren(container, f) {\n if (container.get(\"reverseChildren\", false)) {\n container.children.eachReverse(f);\n } else {\n container.children.each(f);\n }\n}\n/**\r\n * Base class for [[Container]] layouts.\r\n */\nexport class Layout extends Entity {}\nObject.defineProperty(Layout, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Layout\"\n});\nObject.defineProperty(Layout, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Entity.classNames.concat([Layout.className])\n});\n","import { Layout, eachChildren } from \"./Layout\";\nimport * as $type from \"../util/Type\";\nimport { Percent } from \"../util/Percent\";\n/**\r\n * A horizontal children layout for [[Container]].\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/containers/#Layout} for more info\r\n */\nexport class HorizontalLayout extends Layout {\n /**\r\n * @ignore\r\n */\n updateContainer(container) {\n let paddingLeft = container.get(\"paddingLeft\", 0);\n let availableWidth = container.innerWidth();\n let totalPercent = 0;\n eachChildren(container, child => {\n if (child.isVisible()) {\n if (child.get(\"position\") == \"relative\") {\n let childWidth = child.get(\"width\");\n if (childWidth instanceof Percent) {\n totalPercent += childWidth.value;\n let w = availableWidth * childWidth.value;\n let minWidth = child.get(\"minWidth\", child.getPrivate(\"minWidth\", -Infinity));\n if (minWidth > w) {\n availableWidth -= minWidth;\n totalPercent -= childWidth.value;\n }\n let maxWidth = child.get(\"maxWidth\", child.getPrivate(\"maxWidth\", Infinity));\n if (w > maxWidth) {\n availableWidth -= maxWidth;\n totalPercent -= childWidth.value;\n }\n } else {\n if (!$type.isNumber(childWidth)) {\n childWidth = child.width();\n }\n availableWidth -= childWidth + child.get(\"marginLeft\", 0) + child.get(\"marginRight\", 0);\n }\n }\n }\n });\n if (availableWidth <= 0 || availableWidth == Infinity) {\n availableWidth = .1;\n }\n //if (availableWidth > 0) {\n eachChildren(container, child => {\n if (child.isVisible()) {\n if (child.get(\"position\") == \"relative\") {\n let childWidth = child.get(\"width\");\n if (childWidth instanceof Percent) {\n let privateWidth = availableWidth * childWidth.value / totalPercent - child.get(\"marginLeft\", 0) - child.get(\"marginRight\", 0);\n let minWidth = child.get(\"minWidth\", child.getPrivate(\"minWidth\", -Infinity));\n let maxWidth = child.get(\"maxWidth\", child.getPrivate(\"maxWidth\", Infinity));\n privateWidth = Math.min(Math.max(minWidth, privateWidth), maxWidth);\n child.setPrivate(\"width\", privateWidth);\n } else {\n if (child._prevSettings.width instanceof Percent) {\n child.setPrivate(\"width\", undefined);\n }\n }\n }\n }\n });\n //}\n let prevX = paddingLeft;\n eachChildren(container, child => {\n if (child.get(\"position\") == \"relative\") {\n if (child.isVisible()) {\n let bounds = child.adjustedLocalBounds();\n let marginLeft = child.get(\"marginLeft\", 0);\n let marginRight = child.get(\"marginRight\", 0);\n let maxWidth = child.get(\"maxWidth\");\n let left = bounds.left;\n let right = bounds.right;\n if (maxWidth) {\n if (right - left > maxWidth) {\n right = left + maxWidth;\n }\n }\n let x = prevX + marginLeft - left;\n child.setPrivate(\"x\", x);\n prevX = x + right + marginRight;\n } else {\n child.setPrivate(\"x\", undefined);\n }\n }\n });\n }\n}\nObject.defineProperty(HorizontalLayout, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"HorizontalLayout\"\n});\nObject.defineProperty(HorizontalLayout, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Layout.classNames.concat([HorizontalLayout.className])\n});\n","import { Layout, eachChildren } from \"./Layout\";\nimport * as $type from \"../util/Type\";\nimport { Percent } from \"../util/Percent\";\n/**\r\n * A vertical children layout for [[Container]].\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/containers/#Layout} for more info\r\n */\nexport class VerticalLayout extends Layout {\n /**\r\n * @ignore\r\n */\n updateContainer(container) {\n let paddingTop = container.get(\"paddingTop\", 0);\n let availableHeight = container.innerHeight();\n let totalPercent = 0;\n eachChildren(container, child => {\n if (child.isVisible()) {\n if (child.get(\"position\") == \"relative\") {\n let childHeight = child.get(\"height\");\n if (childHeight instanceof Percent) {\n totalPercent += childHeight.value;\n let h = availableHeight * childHeight.value;\n let minHeight = child.get(\"minHeight\", child.getPrivate(\"minHeight\", -Infinity));\n if (minHeight > h) {\n availableHeight -= minHeight;\n totalPercent -= childHeight.value;\n }\n let maxHeight = child.get(\"maxHeight\", child.getPrivate(\"maxHeight\", Infinity));\n if (h > maxHeight) {\n availableHeight -= maxHeight;\n totalPercent -= childHeight.value;\n }\n } else {\n if (!$type.isNumber(childHeight)) {\n childHeight = child.height();\n }\n availableHeight -= childHeight + child.get(\"marginTop\", 0) + child.get(\"marginBottom\", 0);\n }\n }\n }\n });\n if (availableHeight <= 0 || availableHeight == Infinity) {\n availableHeight = .1;\n }\n //if (availableHeight > 0) {\n eachChildren(container, child => {\n if (child.isVisible()) {\n if (child.get(\"position\") == \"relative\") {\n let childHeight = child.get(\"height\");\n if (childHeight instanceof Percent) {\n let privateHeight = availableHeight * childHeight.value / totalPercent - child.get(\"marginTop\", 0) - child.get(\"marginBottom\", 0);\n let minHeight = child.get(\"minHeight\", child.getPrivate(\"minHeight\", -Infinity));\n let maxHeight = child.get(\"maxHeight\", child.getPrivate(\"maxHeight\", Infinity));\n privateHeight = Math.min(Math.max(minHeight, privateHeight), maxHeight);\n child.setPrivate(\"height\", privateHeight);\n } else {\n if (child._prevSettings.height instanceof Percent) {\n child.setPrivate(\"height\", undefined);\n }\n }\n }\n }\n });\n //}\n let prevY = paddingTop;\n eachChildren(container, child => {\n if (child.get(\"position\") == \"relative\") {\n if (child.isVisible()) {\n let bounds = child.adjustedLocalBounds();\n let marginTop = child.get(\"marginTop\", 0);\n let top = bounds.top;\n let bottom = bounds.bottom;\n let maxHeight = child.get(\"maxHeight\");\n if (maxHeight) {\n if (bottom - top > maxHeight) {\n bottom = top + maxHeight;\n }\n }\n let marginBottom = child.get(\"marginBottom\", 0);\n let y = prevY + marginTop - top;\n child.setPrivate(\"y\", y);\n prevY = y + bottom + marginBottom;\n } else {\n child.setPrivate(\"y\", undefined);\n }\n }\n });\n }\n}\nObject.defineProperty(VerticalLayout, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"VerticalLayout\"\n});\nObject.defineProperty(VerticalLayout, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Layout.classNames.concat([VerticalLayout.className])\n});\n","import { Layout, eachChildren } from \"./Layout\";\nimport * as $array from \"../util/Array\";\nimport * as $math from \"../util/Math\";\n/**\r\n * A grid children layout for [[Container]].\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/containers/#Layout} for more info\r\n */\nexport class GridLayout extends Layout {\n _afterNew() {\n this._setRawDefault(\"maxColumns\", Number.MAX_VALUE);\n super._afterNew();\n }\n /**\r\n * @ignore\r\n */\n updateContainer(container) {\n let paddingLeft = container.get(\"paddingLeft\", 0);\n let paddingRight = container.get(\"paddingRight\", 0);\n let paddingTop = container.get(\"paddingTop\", 0);\n let availableWidth = container.maxWidth() - paddingLeft - paddingRight;\n let minCellWidth = availableWidth;\n let maxCellWidth = 1;\n eachChildren(container, child => {\n if (child.get(\"visible\") && child.getPrivate(\"visible\") && !child.get(\"forceHidden\")) {\n if (child.get(\"position\") != \"absolute\") {\n let childWidth = child.width();\n if (childWidth < minCellWidth) {\n minCellWidth = childWidth;\n }\n if (childWidth > maxCellWidth) {\n maxCellWidth = childWidth;\n }\n }\n }\n });\n minCellWidth = $math.fitToRange(minCellWidth, 1, availableWidth);\n maxCellWidth = $math.fitToRange(maxCellWidth, 1, availableWidth);\n let columnCount = 1;\n if (this.get(\"fixedWidthGrid\")) {\n columnCount = availableWidth / maxCellWidth;\n } else {\n columnCount = availableWidth / minCellWidth;\n }\n columnCount = Math.max(1, Math.floor(columnCount));\n columnCount = Math.min(this.get(\"maxColumns\", Number.MAX_VALUE), columnCount);\n let columnWidths = this.getColumnWidths(container, columnCount, maxCellWidth, availableWidth);\n let prevY = paddingTop;\n let column = 0;\n let maxColumnHeight = 0;\n columnCount = columnWidths.length;\n let prevX = paddingLeft;\n eachChildren(container, child => {\n if (child.get(\"position\") == \"relative\" && child.isVisible()) {\n const marginTop = child.get(\"marginTop\", 0);\n const marginBottom = child.get(\"marginBottom\", 0);\n let bounds = child.adjustedLocalBounds();\n let marginLeft = child.get(\"marginLeft\", 0);\n let marginRight = child.get(\"marginRight\", 0);\n let x = prevX + marginLeft - bounds.left;\n let y = prevY + marginTop - bounds.top;\n child.setPrivate(\"x\", x);\n child.setPrivate(\"y\", y);\n prevX += columnWidths[column] + marginRight;\n maxColumnHeight = Math.max(maxColumnHeight, child.height() + marginTop + marginBottom);\n column++;\n if (column >= columnCount) {\n column = 0;\n prevX = paddingLeft;\n prevY += maxColumnHeight;\n }\n }\n });\n }\n /**\r\n * @ignore\r\n */\n getColumnWidths(container, columnCount, maxCellWidth, availableWidth) {\n let totalWidth = 0;\n let columnWidths = [];\n let column = 0;\n eachChildren(container, child => {\n let bounds = child.adjustedLocalBounds();\n if (child.get(\"position\") != \"absolute\" && child.isVisible()) {\n if (this.get(\"fixedWidthGrid\")) {\n columnWidths[column] = maxCellWidth;\n } else {\n columnWidths[column] = Math.max(columnWidths[column] | 0, bounds.right - bounds.left + child.get(\"marginLeft\", 0) + child.get(\"marginRight\", 0));\n }\n if (column < container.children.length - 1) {\n column++;\n if (column == columnCount) {\n column = 0;\n }\n }\n }\n });\n $array.each(columnWidths, w => {\n totalWidth += w;\n });\n if (totalWidth > availableWidth) {\n if (columnCount > 2) {\n columnCount -= 1;\n return this.getColumnWidths(container, columnCount, maxCellWidth, availableWidth);\n } else {\n return [availableWidth];\n }\n }\n return columnWidths;\n }\n}\nObject.defineProperty(GridLayout, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"GridLayout\"\n});\nObject.defineProperty(GridLayout, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Layout.classNames.concat([GridLayout.className])\n});\n","import { Color } from \"./Color\";\nimport * as $type from \"./Type\";\nexport class TextFormatter {\n /**\r\n * Replaces brackets with temporary placeholders.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Input text\r\n * @return Escaped text\r\n */\n static escape(text) {\n return text.replace(/\\[\\[/g, this.prefix + \"1\").replace(/([^\\/\\]]{1})\\]\\]/g, \"$1\" + this.prefix + \"2\").replace(/\\]\\]/g, this.prefix + \"2\").replace(/\\{\\{/g, this.prefix + \"3\").replace(/\\}\\}/g, this.prefix + \"4\").replace(/\\'\\'/g, this.prefix + \"5\");\n }\n /**\r\n * Replaces placeholders back to brackets.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Escaped text\r\n * @return Unescaped text\r\n */\n static unescape(text) {\n return text.replace(new RegExp(this.prefix + \"1\", \"g\"), \"[[\").replace(new RegExp(this.prefix + \"2\", \"g\"), \"]]\").replace(new RegExp(this.prefix + \"3\", \"g\"), \"{{\").replace(new RegExp(this.prefix + \"4\", \"g\"), \"}}\").replace(new RegExp(this.prefix + \"5\", \"g\"), \"''\");\n }\n /**\r\n * Cleans up the text text for leftover double square brackets.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Input text\r\n * @return Cleaned up text\r\n */\n static cleanUp(text) {\n return text.replace(/\\[\\[/g, \"[\").replace(/\\]\\]/g, \"]\").replace(/\\{\\{/g, \"{\").replace(/\\}\\}/g, \"}\").replace(/\\'\\'/g, \"'\");\n }\n /**\r\n * Splits string into chunks. (style blocks, quoted blocks, regular blocks)\r\n *\r\n * If the second parameter `quotedBlocks` is set to `true` this method will\r\n * also single out text blocks enclosed within single quotes that no\r\n * formatting should be applied to, and they should be displayed as is.\r\n *\r\n * Default for the above is `false`, so that you can use single quote in text\r\n * without escaping it.\r\n *\r\n * If enabled, single quotes can be escaped by doubling it - adding two\r\n * single quotes, which will be replaced by a one single quote in the final\r\n * output.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Text to chunk\r\n * @param quotedBlocks Use quoted blocks\r\n * @param noFormatting Formatting blocks will be treated as regular text\r\n * @return Array of string chunks\r\n */\n static chunk(text, quotedBlocks = false, noFormatting = false) {\n // Init result\n let res = [];\n // Replace double (escaped) square spaces and quotes with temporary codes\n text = this.escape(text);\n // Deal with style blocks\n let chunks = quotedBlocks ? text.split(\"'\") : [text];\n for (let i = 0; i < chunks.length; i++) {\n let chunk = chunks[i];\n // Empty?\n if (chunk === \"\") {\n continue;\n }\n if (i % 2 === 0) {\n // Text outside quotes\n // Parse for style blocks which are \"text\" chunks, the rest chunks are\n // \"value\"\n chunk = chunk.replace(/\\]\\[/g, \"]\" + $type.PLACEHOLDER + \"[\");\n chunk = chunk.replace(/\\[\\]/g, \"[ ]\");\n let chunks2 = chunk.split(/[\\[\\]]+/);\n for (let i2 = 0; i2 < chunks2.length; i2++) {\n let chunk2 = this.cleanUp(this.unescape(chunks2[i2]));\n // Placeholder?\n if (chunk2 === $type.PLACEHOLDER) {\n continue;\n }\n // Empty?\n if (chunk2 === \"\") {\n continue;\n }\n // Block or value\n if (i2 % 2 === 0) {\n res.push({\n \"type\": \"value\",\n \"text\": chunk2\n });\n } else {\n res.push({\n \"type\": noFormatting ? \"value\" : \"format\",\n \"text\": \"[\" + chunk2 + \"]\"\n });\n }\n }\n } else {\n // A text within doublequotes\n // All chunks are \"text\"\n let chunks2 = chunk.split(/[\\[\\]]+/);\n for (let i2 = 0; i2 < chunks2.length; i2++) {\n let chunk2 = this.cleanUp(this.unescape(chunks2[i2]));\n // Empty?\n if (chunk2 === \"\") {\n continue;\n }\n // Block or text\n if (i2 % 2 === 0) {\n res.push({\n \"type\": \"text\",\n \"text\": chunk2\n });\n } else if (this.isImage(chunk2)) {\n res.push({\n \"type\": \"image\",\n \"text\": \"[\" + chunk2 + \"]\"\n });\n } else {\n res.push({\n \"type\": \"format\",\n \"text\": \"[\" + chunk2 + \"]\"\n });\n }\n }\n }\n }\n return res;\n }\n /**\r\n * Checks if supplied format contains image information and should be\r\n * formatted as such.\r\n * I.e.: `[img: myImage.png]`\r\n *\r\n * @ignore\r\n * @param text Format\r\n * @return true if it is an image\r\n */\n static isImage(text) {\n return text.match(/img[ ]?:/) ? true : false;\n }\n static getTextStyle(style) {\n // let textStyle: string[] = [];\n // let textFill: string | undefined;\n let format = {};\n if (style == \"\" || style == \"[ ]\") {\n return {};\n }\n // Pre-process quoted text\n const q = style.match(/('[^']*')|(\"[^\"]*\")/gi);\n if (q) {\n for (let i = 0; i < q.length; i++) {\n style = style.replace(q[i], q[i].replace(/['\"]*/g, \"\").replace(/[ ]+/g, \"+\"));\n }\n }\n // Get style parts\n let b = style.match(/([\\w\\-]*:[\\s]?[^;\\s\\]]*)|(\\#[\\w]{1,6})|([\\w\\-]+)|(\\/)/gi);\n // Empty?\n if (!b) {\n return {};\n }\n // Check each part\n for (let i = 0; i < b.length; i++) {\n if (b[i].match(/^(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)$/i)) {\n format.fontWeight = b[i];\n } else if (b[i].match(/^(underline|line-through)$/i)) {\n format.textDecoration = b[i];\n } else if (b[i] == \"/\") {\n // Just closing tag\n // Do nothing\n } else if (!b[i].match(/:/)) {\n // Color\n format.fill = Color.fromString(b[i]);\n } else {\n const p = b[i].replace(\"+\", \" \").split(/:[ ]*/);\n format[p[0]] = p[1];\n //textStyle.push(b[i].replace(/^[a-zA-Z]:[ ]*/, \"\"));\n //b[i] = b[i].replace(/\\+/g, \" \");\n }\n }\n return format;\n }\n}\nObject.defineProperty(TextFormatter, \"prefix\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"__amcharts__\"\n});\n","/** @ignore */ /** */\nimport * as $type from \"./Type\";\nimport * as $utils from \"./Utils\";\nimport { Sprite } from \"../render/Sprite\";\nimport { TextFormatter } from \"./TextFormatter\";\n/**\r\n * @ignore\r\n */\nexport function populateString(target, string) {\n if (string != null) {\n string = \"\" + string;\n string = TextFormatter.escape(string);\n let tags = string.match(/\\{([^}]+)\\}/g);\n let i;\n if (tags) {\n for (i = 0; i < tags.length; i++) {\n let tag = tags[i].replace(/\\{([^}]+)\\}/, \"$1\");\n let value = getTagValue(target, tag, \"\");\n if (value == null) {\n value = \"\";\n }\n string = string.split(tags[i]).join(value);\n }\n }\n string = TextFormatter.unescape(string);\n } else {\n string = \"\";\n }\n // TODO: apply adapter?\n return string;\n}\n/**\r\n * @ignore\r\n */\nfunction getTagValue(target, tagName, format) {\n let value;\n const dataItem = target.dataItem;\n // Parse parts\n let parts = [];\n let reg = /(format[a-zA-Z]*)\\((.*)\\)|([^.]+)/g;\n let matches;\n while (true) {\n matches = reg.exec(tagName);\n if (matches === null) {\n break;\n }\n if (matches[3]) {\n // Simple property\n parts.push({\n prop: matches[3]\n });\n // Check if maybe we should force a formatter on this value\n const dateFields = target.getDateFormatter().get(\"dateFields\", []);\n const numericFields = target.getNumberFormatter().get(\"numericFields\", []);\n const durationFields = target.getDurationFormatter().get(\"durationFields\", []);\n if (dateFields.indexOf(matches[3]) !== -1) {\n parts.push({\n method: \"formatDate\",\n params: []\n });\n } else if (numericFields.indexOf(matches[3]) !== -1) {\n parts.push({\n method: \"formatNumber\",\n params: []\n });\n } else if (durationFields.indexOf(matches[3]) !== -1) {\n parts.push({\n method: \"formatDuration\",\n params: []\n });\n }\n } else {\n // Method\n // Parse parameters\n let params = [];\n if ($utils.trim(matches[2]) != \"\") {\n let reg2 = /'([^']*)'|\"([^\"]*)\"|([0-9\\-]+)/g;\n let matches2;\n while (true) {\n matches2 = reg2.exec(matches[2]);\n if (matches2 === null) {\n break;\n }\n params.push(matches2[1] || matches2[2] || matches2[3]);\n }\n }\n parts.push({\n method: matches[1],\n params: params\n });\n }\n }\n // Check if we can retrieve the value from data item\n if (dataItem) {\n // Check values\n value = getTagValueFromObject(target, parts, dataItem._settings);\n // Check properties\n if (value == null || $type.isObject(value)) {\n // isObject helps to solve problem with date axis, as for example dateX will get dateX from values object and won't get to the dateX date.\n value = getTagValueFromObject(target, parts, dataItem);\n }\n // Check data context\n let dataContext = dataItem.dataContext;\n if (value == null && dataContext) {\n value = getTagValueFromObject(target, parts, dataContext);\n // Maybe it's a literal dot-separated name of the key in dataContext?\n if (value == null) {\n value = getTagValueFromObject(target, [{\n prop: tagName\n }], dataContext);\n }\n // scond data context level sometimes exist (tree map)\n if (value == null && dataContext.dataContext) {\n value = getTagValueFromObject(target, parts, dataContext.dataContext);\n }\n }\n // Check component's data item\n if (value == null && dataItem.component && dataItem.component.dataItem !== dataItem) {\n value = getTagValue(dataItem.component, tagName, format);\n }\n }\n // Check sprite's properties\n if (value == null) {\n value = getTagValueFromObject(target, parts, target);\n }\n // Finally, check the parent\n if (value == null && target.parent) {\n value = getTagValue(target.parent, tagName, format);\n }\n return value;\n}\n/**\r\n * @ignore\r\n */\nfunction getCustomDataValue(target, prop) {\n const customData = target.getPrivate(\"customData\");\n if ($type.isObject(customData)) {\n return customData[prop];\n }\n}\n/**\r\n * @ignore\r\n */\nexport function getTagValueFromObject(target, parts, object, format) {\n let current = object;\n let formatApplied = false;\n for (let i = 0, len = parts.length; i < len; i++) {\n let part = parts[i];\n if (part.prop) {\n // Regular property\n if (current instanceof Sprite) {\n let tmp = current.get(part.prop);\n if (tmp == null) tmp = current.getPrivate(part.prop);\n if (tmp == null) tmp = getCustomDataValue(current, part.prop);\n if (tmp == null) tmp = current[part.prop];\n current = tmp;\n } else if (current.get) {\n let tmp = current.get(part.prop);\n if (tmp == null) tmp = current[part.prop];\n current = tmp;\n } else {\n current = current[part.prop];\n }\n if (current == null) {\n // Not set, return undefined\n return;\n }\n } else {\n // Method\n switch (part.method) {\n case \"formatNumber\":\n let numberValue = $type.toNumber(current);\n if (numberValue != null) {\n current = target.getNumberFormatter().format(numberValue, format || part.params[0] || undefined);\n formatApplied = true;\n }\n break;\n case \"formatDate\":\n let dateValue = $type.toDate(current);\n if (!$type.isDate(dateValue) || $type.isNaN(dateValue.getTime())) {\n // Was not able to get date out of value, quitting and letting\n // calling method try another value\n return;\n }\n if (dateValue != null) {\n current = target.getDateFormatter().format(dateValue, format || part.params[0] || undefined);\n formatApplied = true;\n }\n break;\n case \"formatDuration\":\n let durationValue = $type.toNumber(current);\n if (durationValue != null) {\n current = target.getDurationFormatter().format(durationValue, format || part.params[0] || undefined, part.params[1] || undefined);\n formatApplied = true;\n }\n break;\n case \"urlEncode\":\n case \"encodeURIComponent\":\n current = encodeURIComponent(current);\n break;\n default:\n if (current[part.method]) {\n current[part.method].apply(object, part.params);\n }\n break;\n }\n }\n }\n // Apply default format if it wasn't applied explicitly\n if (!formatApplied) {\n let formatParts = [{\n method: \"\",\n params: format\n }];\n if (format == null) {\n // Format is not set\n // Determine from the type of the value\n if ($type.isNumber(current)) {\n formatParts[0].method = \"formatNumber\";\n formatParts[0].params = \"\";\n } else if ($type.isDate(current)) {\n formatParts[0].method = \"formatDate\";\n formatParts[0].params = \"\";\n }\n } else {\n // Format set\n // Try to determine formatter based on the format\n let formatterType = $utils.getFormat(format);\n // format\n if (formatterType === \"number\") {\n formatParts[0].method = \"formatNumber\";\n } else if (formatterType === \"date\") {\n formatParts[0].method = \"formatDate\";\n } else if (formatterType === \"duration\") {\n formatParts[0].method = \"formatDuration\";\n }\n }\n // Apply format\n if (formatParts[0].method) {\n current = getTagValueFromObject(target, formatParts, current);\n }\n }\n return current;\n}\n","import { Children } from \"../util/Children\";\nimport { Percent } from \"../util/Percent\";\nimport { Sprite } from \"./Sprite\";\nimport { Rectangle } from \"./Rectangle\";\nimport { HorizontalLayout } from \"./HorizontalLayout\";\nimport { VerticalLayout } from \"./VerticalLayout\";\nimport { GridLayout } from \"./GridLayout\";\nimport { populateString } from \"../util/PopulateString\";\nimport * as $array from \"../util/Array\";\nimport * as $type from \"../util/Type\";\nimport * as $utils from \"../util/Utils\";\n/**\r\n * A basic element that can have child elements, maintain their layout, and\r\n * have a background.\r\n *\r\n * It can have any [[Sprite]] element as a child, from very basic shapes, to\r\n * full-fledged charts.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/containers/} for more info\r\n * @important\r\n */\nexport class Container extends Sprite {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_display\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeContainer()\n });\n Object.defineProperty(this, \"_childrenDisplay\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeContainer()\n });\n /**\r\n * List of Container's child elements.\r\n */\n Object.defineProperty(this, \"children\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Children(this)\n });\n Object.defineProperty(this, \"_percentageSizeChildren\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_percentagePositionChildren\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_prevWidth\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_prevHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_contentWidth\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_contentHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_contentMask\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_vsbd0\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_vsbd1\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n _afterNew() {\n super._afterNew();\n this._display.addChild(this._childrenDisplay);\n }\n _dispose() {\n $array.eachReverse(this.allChildren(), child => {\n child.dispose();\n });\n if (this.getPrivate(\"htmlElement\")) {\n this._root._removeHTMLContent(this);\n }\n super._dispose();\n }\n _changed() {\n super._changed();\n if (this.isDirty(\"interactiveChildren\")) {\n this._display.interactiveChildren = this.get(\"interactiveChildren\", false);\n }\n if (this.isDirty(\"layout\")) {\n this._prevWidth = 0;\n this._prevHeight = 0;\n this.markDirtyBounds();\n if (this._prevSettings.layout) {\n this.children.each(child => {\n child.removePrivate(\"x\");\n child.removePrivate(\"y\");\n });\n }\n }\n if (this.isDirty(\"paddingTop\") || this.isDirty(\"paddingBottom\") || this.isDirty(\"paddingLeft\") || this.isDirty(\"paddingRight\")) {\n this.children.each(child => {\n child.markDirtyPosition();\n });\n }\n if (this.isDirty(\"maskContent\")) {\n const childrenDisplay = this._childrenDisplay;\n let contentMask = this._contentMask;\n if (this.get(\"maskContent\")) {\n if (!contentMask) {\n contentMask = Rectangle.new(this._root, {\n x: -.5,\n y: -.5,\n width: this.width() + 1,\n height: this.height() + 1\n });\n this._contentMask = contentMask;\n childrenDisplay.addChildAt(contentMask._display, 0);\n childrenDisplay.mask = contentMask._display;\n }\n } else {\n if (contentMask) {\n childrenDisplay.removeChild(contentMask._display);\n childrenDisplay.mask = null;\n contentMask.dispose();\n this._contentMask = undefined;\n }\n }\n }\n }\n _updateSize() {\n super._updateSize();\n $array.each(this._percentageSizeChildren, child => {\n child._updateSize();\n });\n $array.each(this._percentagePositionChildren, child => {\n child.markDirtyPosition();\n child._updateSize();\n });\n this.updateBackground();\n }\n updateBackground() {\n const background = this.get(\"background\");\n let bounds = this._localBounds;\n if (bounds && !this.isHidden()) {\n let x = bounds.left;\n let y = bounds.top;\n let w = bounds.right - x;\n let h = bounds.bottom - y;\n let maxWidth = this.get(\"maxWidth\");\n let maxHeight = this.get(\"maxHeight\");\n if (maxHeight) {\n if (h > maxHeight) {\n h = maxHeight;\n }\n }\n if (maxWidth) {\n if (w > maxWidth) {\n w = maxWidth;\n }\n }\n let width = this.width();\n let height = this.height();\n if (background) {\n background.setAll({\n width: w,\n height: h,\n x: x,\n y: y\n });\n if (this._display.interactive) {\n background._display.interactive = true;\n }\n }\n const contentMask = this._contentMask;\n if (contentMask) {\n contentMask.setAll({\n width: width + 1,\n height: height + 1\n });\n }\n const verticalScrollbar = this.get(\"verticalScrollbar\");\n if (verticalScrollbar) {\n verticalScrollbar.set(\"height\", height);\n verticalScrollbar.set(\"x\", width - verticalScrollbar.width() - verticalScrollbar.get(\"marginRight\", 0));\n verticalScrollbar.set(\"end\", verticalScrollbar.get(\"start\", 0) + height / this._contentHeight);\n const bg = verticalScrollbar.get(\"background\");\n if (bg) {\n bg.setAll({\n width: verticalScrollbar.width(),\n height: height\n });\n }\n let visible = true;\n if (this._contentHeight <= height) {\n visible = false;\n }\n verticalScrollbar.setPrivate(\"visible\", visible);\n }\n }\n }\n _applyThemes(force = false) {\n if (super._applyThemes(force)) {\n this.eachChildren(child => {\n child._applyThemes(force);\n });\n return true;\n } else {\n return false;\n }\n }\n _applyState(name) {\n super._applyState(name);\n if (this.get(\"setStateOnChildren\")) {\n this.eachChildren(child => {\n child.states.apply(name);\n });\n }\n }\n _applyStateAnimated(name, duration) {\n super._applyStateAnimated(name, duration);\n if (this.get(\"setStateOnChildren\")) {\n this.eachChildren(child => {\n child.states.applyAnimate(name, duration);\n });\n }\n }\n /**\r\n * Returns container's inner width (width without padding) in pixels.\r\n *\r\n * @return Inner width (px)\r\n */\n innerWidth() {\n return this.width() - this.get(\"paddingRight\", 0) - this.get(\"paddingLeft\", 0);\n }\n /**\r\n * Returns container's inner height (height without padding) in pixels.\r\n *\r\n * @return Inner height (px)\r\n */\n innerHeight() {\n return this.height() - this.get(\"paddingTop\", 0) - this.get(\"paddingBottom\", 0);\n }\n _getBounds() {\n if (!this.get(\"html\")) {\n let width = this.get(\"width\");\n let height = this.get(\"height\");\n let pWidth = this.getPrivate(\"width\");\n let pHeight = this.getPrivate(\"height\");\n let bounds = {\n left: 0,\n top: 0,\n right: this.width(),\n bottom: this.height()\n };\n let layout = this.get(\"layout\");\n let horizontal = false;\n let vertical = false;\n if (layout instanceof HorizontalLayout || layout instanceof GridLayout) {\n horizontal = true;\n }\n if (layout instanceof VerticalLayout) {\n vertical = true;\n }\n if ((width != null || pWidth != null) && (height != null || pHeight != null) && !this.get(\"verticalScrollbar\")) {\n // void\n } else {\n let m = Number.MAX_VALUE;\n let l = m;\n let r = -m;\n let t = m;\n let b = -m;\n const paddingLeft = this.get(\"paddingLeft\", 0);\n const paddingTop = this.get(\"paddingTop\", 0);\n const paddingRight = this.get(\"paddingRight\", 0);\n const paddingBottom = this.get(\"paddingBottom\", 0);\n this.children.each(child => {\n if (child.get(\"position\") != \"absolute\" && child.get(\"isMeasured\")) {\n let childBounds = child.adjustedLocalBounds();\n let childX = child.x();\n let childY = child.y();\n let cl = childX + childBounds.left;\n let cr = childX + childBounds.right;\n let ct = childY + childBounds.top;\n let cb = childY + childBounds.bottom;\n if (horizontal) {\n cl -= child.get(\"marginLeft\", 0);\n cr += child.get(\"marginRight\", 0);\n }\n if (vertical) {\n ct -= child.get(\"marginTop\", 0);\n cb += child.get(\"marginBottom\", 0);\n }\n if (cl < l) {\n l = cl;\n }\n if (cr > r) {\n r = cr;\n }\n if (ct < t) {\n t = ct;\n }\n if (cb > b) {\n b = cb;\n }\n }\n });\n if (l == m) {\n l = 0;\n }\n if (r == -m) {\n r = 0;\n }\n if (t == m) {\n t = 0;\n }\n if (b == -m) {\n b = 0;\n }\n bounds.left = l - paddingLeft;\n bounds.top = t - paddingTop;\n bounds.right = r + paddingRight;\n bounds.bottom = b + paddingBottom;\n const minWidth = this.get(\"minWidth\");\n if ($type.isNumber(minWidth) && minWidth > 0) {\n if (bounds.right - bounds.left < minWidth) {\n if (bounds.right >= minWidth) {\n bounds.left = bounds.right - minWidth;\n } else {\n bounds.right = bounds.left + minWidth;\n }\n }\n }\n const minHeight = this.get(\"minHeight\");\n if ($type.isNumber(minHeight) && minHeight > 0) {\n if (bounds.bottom - bounds.top < minHeight) {\n if (bounds.bottom >= minHeight) {\n bounds.top = bounds.bottom - minHeight;\n } else {\n bounds.bottom = bounds.top + minHeight;\n }\n }\n }\n }\n this._contentWidth = bounds.right - bounds.left;\n this._contentHeight = bounds.bottom - bounds.top;\n if ($type.isNumber(width)) {\n bounds.left = 0;\n bounds.right = width;\n }\n if ($type.isNumber(pWidth)) {\n bounds.left = 0;\n bounds.right = pWidth;\n }\n if ($type.isNumber(height)) {\n bounds.top = 0;\n bounds.bottom = height;\n }\n if ($type.isNumber(pHeight)) {\n bounds.top = 0;\n bounds.bottom = pHeight;\n }\n this._localBounds = bounds;\n } else {\n let bounds = this._localBounds;\n if (bounds) {\n this._contentWidth = bounds.right - bounds.left;\n this._contentHeight = bounds.bottom - bounds.top;\n }\n }\n }\n _updateBounds() {\n const layout = this.get(\"layout\");\n if (layout) {\n layout.updateContainer(this);\n }\n super._updateBounds();\n this.updateBackground();\n }\n /**\r\n * @ignore\r\n */\n markDirty() {\n super.markDirty();\n this._root._addDirtyParent(this);\n }\n _prepareChildren() {\n const innerWidth = this.innerWidth();\n const innerHeight = this.innerHeight();\n if (innerWidth != this._prevWidth || innerHeight != this._prevHeight) {\n let layout = this.get(\"layout\");\n let horizontal = false;\n let vertical = false;\n if (layout) {\n if (layout instanceof HorizontalLayout || layout instanceof GridLayout) {\n horizontal = true;\n }\n if (layout instanceof VerticalLayout) {\n vertical = true;\n }\n }\n $array.each(this._percentageSizeChildren, child => {\n if (!horizontal) {\n let width = child.get(\"width\");\n if (width instanceof Percent) {\n child.setPrivate(\"width\", width.value * innerWidth);\n }\n }\n if (!vertical) {\n let height = child.get(\"height\");\n if (height instanceof Percent) {\n child.setPrivate(\"height\", height.value * innerHeight);\n }\n }\n });\n $array.each(this._percentagePositionChildren, child => {\n child.markDirtyPosition();\n child.markDirtyBounds();\n });\n this._prevWidth = innerWidth;\n this._prevHeight = innerHeight;\n this._sizeDirty = true;\n this.updateBackground();\n }\n this._handleStates();\n }\n _updateHTMLContent() {\n const html = this.get(\"html\");\n if (html && html !== \"\") {\n this._root._setHTMLContent(this, populateString(this, this.get(\"html\", \"\")));\n } else {\n this._root._removeHTMLContent(this);\n }\n this._root._positionHTMLElement(this);\n }\n /**\r\n * If scrolling is enabled on the Container (by adding `verticalScrollbar`)\r\n * the Container will scroll in such way so that target element becomes\r\n * visible if its currently outside of view.\r\n *\r\n * @param child Target child\r\n * @since 5.10.5\r\n */\n scrollToChild(child) {\n const verticalScrollbar = this.get(\"verticalScrollbar\");\n if (verticalScrollbar) {\n let y = child.y();\n let h = this.innerHeight();\n let ch = child.height();\n let contentH = this._contentHeight;\n let max = 1 - (h - ch / 2) / contentH;\n if (y + ch * .7 + this._childrenDisplay.y > h || y - ch * .3 + this._childrenDisplay.y < 0) {\n let pos = Math.max(0, Math.min(max, (y - ch / 2) / contentH));\n verticalScrollbar.animate({\n key: \"start\",\n to: pos,\n duration: verticalScrollbar.get(\"animationDuration\", 0),\n easing: verticalScrollbar.get(\"animationEasing\")\n });\n }\n }\n }\n _updateChildren() {\n if (this.isDirty(\"html\")) {\n this._updateHTMLContent();\n }\n if (this.isDirty(\"verticalScrollbar\")) {\n const verticalScrollbar = this.get(\"verticalScrollbar\");\n if (verticalScrollbar) {\n verticalScrollbar._setParent(this);\n verticalScrollbar.startGrip.setPrivate(\"visible\", false);\n verticalScrollbar.endGrip.setPrivate(\"visible\", false);\n this.set(\"maskContent\", true);\n this.set(\"paddingRight\", verticalScrollbar.width() + verticalScrollbar.get(\"marginRight\", 0) + verticalScrollbar.get(\"marginLeft\", 0));\n let background = this.get(\"background\");\n if (!background) {\n background = this.set(\"background\", Rectangle.new(this._root, {\n themeTags: [\"background\"],\n fillOpacity: 0,\n fill: this._root.interfaceColors.get(\"alternativeBackground\")\n }));\n }\n this._vsbd0 = this.events.on(\"wheel\", event => {\n const wheelEvent = event.originalEvent;\n // Ignore wheel event if it is happening on a non-chart element, e.g. if\n // some page element is over the chart.\n if ($utils.isLocalEvent(wheelEvent, this)) {\n wheelEvent.preventDefault();\n } else {\n return;\n }\n let shiftY = wheelEvent.deltaY / 5000;\n const start = verticalScrollbar.get(\"start\", 0);\n const end = verticalScrollbar.get(\"end\", 1);\n if (start + shiftY <= 0) {\n shiftY = -start;\n }\n if (end + shiftY >= 1) {\n shiftY = 1 - end;\n }\n if (start + shiftY >= 0 && end + shiftY <= 1) {\n verticalScrollbar.set(\"start\", start + shiftY);\n verticalScrollbar.set(\"end\", end + shiftY);\n }\n });\n this._disposers.push(this._vsbd0);\n this._vsbd1 = verticalScrollbar.events.on(\"rangechanged\", () => {\n let h = this._contentHeight;\n const childrenDisplay = this._childrenDisplay;\n const contentMask = this._contentMask;\n childrenDisplay.y = -verticalScrollbar.get(\"start\", 0) * h;\n childrenDisplay.markDirtyLayer();\n if (contentMask) {\n contentMask._display.y = -childrenDisplay.y;\n childrenDisplay.mask = contentMask._display;\n }\n });\n this._disposers.push(this._vsbd1);\n this._display.addChild(verticalScrollbar._display);\n } else {\n const previous = this._prevSettings.verticalScrollbar;\n if (previous) {\n this._display.removeChild(previous._display);\n if (this._vsbd0) {\n this._vsbd0.dispose();\n }\n if (this._vsbd1) {\n this._vsbd1.dispose();\n }\n const childrenDisplay = this._childrenDisplay;\n childrenDisplay.y = 0;\n this.setPrivate(\"height\", undefined);\n this.set(\"maskContent\", false);\n this.set(\"paddingRight\", undefined);\n }\n }\n }\n if (this.isDirty(\"background\")) {\n // TODO maybe this should dispose ?\n const previous = this._prevSettings[\"background\"];\n if (previous) {\n this._display.removeChild(previous._display);\n }\n const background = this.get(\"background\");\n if (background instanceof Sprite) {\n background.set(\"isMeasured\", false);\n background._setParent(this);\n this._display.addChildAt(background._display, 0);\n }\n }\n if (this.isDirty(\"mask\")) {\n const mask = this.get(\"mask\");\n const previous = this._prevSettings[\"mask\"];\n if (previous) {\n this._display.removeChild(previous._display);\n if (previous != mask) {\n previous.dispose();\n }\n }\n if (mask) {\n const parent = mask.parent;\n if (parent) {\n parent.children.removeValue(mask);\n }\n mask._setParent(this);\n this._display.addChildAt(mask._display, 0);\n this._childrenDisplay.mask = mask._display;\n }\n }\n }\n _processTemplateField() {\n super._processTemplateField();\n this.children.each(child => {\n child._processTemplateField();\n });\n }\n /**\r\n * @ignore\r\n */\n walkChildren(f) {\n this.children.each(child => {\n if (child instanceof Container) {\n child.walkChildren(f);\n }\n f(child);\n });\n }\n eachChildren(f) {\n const background = this.get(\"background\");\n if (background) {\n f(background);\n }\n const verticalScrollbar = this.get(\"verticalScrollbar\");\n if (verticalScrollbar) {\n f(verticalScrollbar);\n }\n const mask = this.get(\"mask\");\n if (mask) {\n f(mask);\n }\n this.children.values.forEach(child => {\n f(child);\n });\n }\n allChildren() {\n const output = [];\n this.eachChildren(x => {\n output.push(x);\n });\n return output;\n }\n _setDataItem(dataItem) {\n const updated = dataItem !== this._dataItem;\n super._setDataItem(dataItem);\n const html = this.get(\"html\", \"\");\n if (html && html !== \"\" && updated) {\n this._root._setHTMLContent(this, populateString(this, html));\n }\n }\n}\nObject.defineProperty(Container, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Container\"\n});\nObject.defineProperty(Container, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Sprite.classNames.concat([Container.className])\n});\n","import { Sprite } from \"./Sprite\";\nimport { populateString } from \"../util/PopulateString\";\nimport * as $array from \"../util/Array\";\nimport * as $utils from \"../util/Utils\";\nimport { Disposer } from \"../util/Disposer\";\n/**\r\n * @ignore Text is an internal class. Use Label instead.\r\n */\nexport class Text extends Sprite {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"textStyle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeTextStyle()\n });\n Object.defineProperty(this, \"_display\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._root._renderer.makeText(\"\", this.textStyle)\n });\n Object.defineProperty(this, \"_textStyles\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"textAlign\", \"fontFamily\", \"fontSize\", \"fontStyle\", \"fontWeight\", \"fontStyle\", \"fontVariant\", \"textDecoration\", \"shadowColor\", \"shadowBlur\", \"shadowOffsetX\", \"shadowOffsetY\", \"shadowOpacity\",\n // \"leading\",\n // \"letterSpacing\",\n \"lineHeight\", \"baselineRatio\",\n //\"padding\",\n // \"stroke\",\n // \"strokeThickness\",\n // \"trim\",\n // \"wordWrap\",\n \"direction\", \"textBaseline\", \"oversizedBehavior\", \"breakWords\", \"ellipsis\", \"minScale\", \"maxChars\"]\n });\n Object.defineProperty(this, \"_originalScale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n _updateBounds() {\n if (!this.get(\"text\")) {\n let newBounds = {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n };\n this._adjustedLocalBounds = newBounds;\n } else {\n super._updateBounds();\n let fillGradient = this.get(\"fillGradient\");\n if (fillGradient) {\n this._display.style.fill = fillGradient.getFill(this);\n }\n }\n }\n _changed() {\n super._changed();\n this._display.clear();\n let textStyle = this.textStyle;\n if (this.isDirty(\"opacity\")) {\n let opacity = this.get(\"opacity\", 1);\n this._display.alpha = opacity;\n }\n if (this.isDirty(\"text\") || this.isDirty(\"populateText\")) {\n this._display.text = this._getText();\n this.markDirtyBounds();\n if (this.get(\"role\") == \"tooltip\") {\n this._root.updateTooltip(this);\n }\n }\n if (this.isPrivateDirty(\"tooltipElement\")) {\n const tooltipElement = this.getPrivate(\"tooltipElement\");\n if (tooltipElement) {\n this._disposers.push(new Disposer(() => {\n this._root._removeTooltipElement(this);\n }));\n }\n }\n if (this.isDirty(\"width\")) {\n textStyle.wordWrapWidth = this.width();\n this.markDirtyBounds();\n }\n if (this.isDirty(\"oversizedBehavior\")) {\n textStyle.oversizedBehavior = this.get(\"oversizedBehavior\", \"none\");\n this.markDirtyBounds();\n }\n if (this.isDirty(\"breakWords\")) {\n textStyle.breakWords = this.get(\"breakWords\", false);\n this.markDirtyBounds();\n }\n if (this.isDirty(\"ellipsis\")) {\n textStyle.ellipsis = this.get(\"ellipsis\");\n this.markDirtyBounds();\n }\n if (this.isDirty(\"ignoreFormatting\")) {\n textStyle.ignoreFormatting = this.get(\"ignoreFormatting\", false);\n this.markDirtyBounds();\n }\n if (this.isDirty(\"minScale\")) {\n textStyle.minScale = this.get(\"minScale\", 0);\n this.markDirtyBounds();\n }\n if (this.isDirty(\"fill\") || this.isDirty(\"fillGradient\")) {\n const fill = this.get(\"fill\");\n const fillGradient = this.get(\"fillGradient\");\n const fillOpacity = this.get(\"fillOpacity\");\n if (fillGradient) {\n if (fill) {\n const stops = fillGradient.get(\"stops\", []);\n if (stops.length) {\n $array.each(stops, stop => {\n if ((!stop.color || stop.colorInherited) && fill) {\n stop.color = fill;\n stop.colorInherited = true;\n }\n if (stop.opacity == null || stop.opacityInherited) {\n stop.opacity = fillOpacity;\n stop.opacityInherited = true;\n }\n });\n }\n }\n textStyle.fill = fillGradient.getFill(this);\n } else if (fill) {\n textStyle.fill = fill;\n }\n }\n if (this.isDirty(\"fillOpacity\")) {\n let fillOpacity = this.get(\"fillOpacity\", 1);\n if (fillOpacity) {\n textStyle.fillOpacity = fillOpacity;\n }\n }\n if (this.isDirty(\"maxWidth\") || this.isPrivateDirty(\"maxWidth\")) {\n textStyle.maxWidth = this.get(\"maxWidth\", this.getPrivate(\"maxWidth\"));\n this.markDirtyBounds();\n }\n if (this.isDirty(\"maxHeight\") || this.isPrivateDirty(\"maxHeight\")) {\n textStyle.maxHeight = this.get(\"maxHeight\", this.getPrivate(\"maxHeight\"));\n this.markDirtyBounds();\n }\n $array.each(this._textStyles, styleName => {\n if (this._dirty[styleName]) {\n textStyle[styleName] = this.get(styleName);\n this.markDirtyBounds();\n }\n });\n textStyle[\"fontSize\"] = this.get(\"fontSize\");\n textStyle[\"fontFamily\"] = this.get(\"fontFamily\");\n this._display.style = textStyle;\n if (this.isDirty(\"role\") && this.get(\"role\") == \"tooltip\") {\n this._root.updateTooltip(this);\n }\n }\n _getText() {\n let text = this.get(\"text\", \"\");\n if (this.get(\"maxChars\")) {\n text = $utils.truncateTextWithEllipsis(text, this.get(\"maxChars\", 100000000), this.get(\"breakWords\"), this.get(\"ellipsis\"));\n }\n return this.get(\"populateText\") ? populateString(this, text) : text;\n }\n _getAccessibleText() {\n const ariaLabel = this.get(\"ariaLabel\");\n if (ariaLabel !== undefined) {\n return this.get(\"populateText\") ? populateString(this, ariaLabel) : ariaLabel;\n }\n return this._getText();\n }\n /**\r\n * Forces the text to be re-evaluated and re-populated.\r\n */\n markDirtyText() {\n this._display.text = this._getText();\n if (this.get(\"role\") == \"tooltip\") {\n this._root.updateTooltip(this);\n }\n this.markDirtyBounds();\n this.markDirty();\n }\n _setDataItem(dataItem) {\n super._setDataItem(dataItem);\n if (this.get(\"populateText\")) {\n this.markDirtyText();\n }\n }\n getNumberFormatter() {\n if (this.parent) {\n return this.parent.getNumberFormatter();\n } else {\n return super.getNumberFormatter();\n }\n }\n getDateFormatter() {\n if (this.parent) {\n return this.parent.getDateFormatter();\n } else {\n return super.getDateFormatter();\n }\n }\n getDurationFormatter() {\n if (this.parent) {\n return this.parent.getDurationFormatter();\n } else {\n return super.getDurationFormatter();\n }\n }\n}\nObject.defineProperty(Text, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Text\"\n});\nObject.defineProperty(Text, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Sprite.classNames.concat([Text.className])\n});\n","/** @ignore */ /** */\nimport * as $array from \"./Array\";\nimport * as $utils from \"./Utils\";\n/**\r\n * @ignore\r\n */\nclass Native {\n constructor() {\n Object.defineProperty(this, \"_observer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_targets\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n this._observer = new ResizeObserver(entries => {\n $array.each(entries, entry => {\n $array.each(this._targets, x => {\n if (x.target === entry.target) {\n x.callback();\n }\n });\n });\n });\n }\n addTarget(target, callback) {\n this._observer.observe(target, {\n box: \"border-box\"\n });\n this._targets.push({\n target,\n callback\n });\n }\n removeTarget(target) {\n this._observer.unobserve(target);\n $array.keepIf(this._targets, x => {\n return x.target !== target;\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass Raf {\n constructor() {\n Object.defineProperty(this, \"_timer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"_targets\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n }\n addTarget(target, callback) {\n if (this._timer === null) {\n let lastTime = null;\n const loop = () => {\n const currentTime = Date.now();\n if (lastTime === null || currentTime > lastTime + Raf.delay) {\n lastTime = currentTime;\n $array.each(this._targets, x => {\n let newSize = x.target.getBoundingClientRect();\n if (newSize.width !== x.size.width || newSize.height !== x.size.height) {\n x.size = newSize;\n x.callback();\n }\n });\n }\n if (this._targets.length === 0) {\n this._timer = null;\n } else {\n this._timer = requestAnimationFrame(loop);\n }\n };\n this._timer = requestAnimationFrame(loop);\n }\n // We start off with fake bounds so that sensor always kicks in\n let size = {\n width: 0,\n height: 0,\n left: 0,\n right: 0,\n top: 0,\n bottom: 0,\n x: 0,\n y: 0\n };\n this._targets.push({\n target,\n callback,\n size\n });\n }\n removeTarget(target) {\n $array.keepIf(this._targets, x => {\n return x.target !== target;\n });\n if (this._targets.length === 0) {\n if (this._timer !== null) {\n cancelAnimationFrame(this._timer);\n this._timer = null;\n }\n }\n }\n}\nObject.defineProperty(Raf, \"delay\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 200\n});\n/**\r\n * @ignore\r\n */\nlet observer = null;\n/**\r\n * @ignore\r\n */\nfunction makeSensor() {\n if (observer === null) {\n if (typeof ResizeObserver !== \"undefined\") {\n observer = new Native();\n } else {\n observer = new Raf();\n }\n }\n return observer;\n}\n/**\r\n * @ignore\r\n */\nexport class ResizeSensor {\n constructor(element, callback) {\n Object.defineProperty(this, \"_sensor\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_element\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_listener\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_disposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n this._sensor = makeSensor();\n this._element = element;\n // This is needed because we need to know when the window is zoomed\n this._listener = $utils.onZoom(callback);\n this._sensor.addTarget(element, callback);\n }\n isDisposed() {\n return this._disposed;\n }\n dispose() {\n if (!this._disposed) {\n this._disposed = true;\n this._sensor.removeTarget(this._element);\n this._listener.dispose();\n }\n }\n get sensor() {\n return this._sensor;\n }\n}\n","import { Entity } from \"./Entity\";\n/**\r\n * Presets for common UI elements.\r\n */\nexport class InterfaceColors extends Entity {}\nObject.defineProperty(InterfaceColors, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"InterfaceColors\"\n});\nObject.defineProperty(InterfaceColors, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Entity.classNames.concat([InterfaceColors.className])\n});\n","import { Text } from \"../render/Text\";\nimport { p50, p100 } from \"../util/Percent\";\nimport { Container } from \"./Container\";\nimport * as $array from \"../../core/util/Array\";\nimport * as $type from \"../../core/util/Type\";\n/**\r\n * Creates a label with support for in-line styling and data bindings.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/labels/} for more info\r\n */\nexport class Label extends Container {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_text\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_textKeys\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"text\", \"fill\", \"fillGradient\", \"fillOpacity\", \"textAlign\", \"fontFamily\", \"fontSize\", \"fontStyle\", \"fontWeight\", \"fontStyle\", \"fontVariant\", \"textDecoration\", \"shadowColor\", \"shadowBlur\", \"shadowOffsetX\", \"shadowOffsetY\", \"shadowOpacity\",\n // \"leading\",\n // \"letterSpacing\",\n \"lineHeight\", \"baselineRatio\",\n //\"padding\",\n // \"stroke\",\n // \"strokeThickness\",\n // \"trim\",\n // \"wordWrap\",\n \"direction\", \"textBaseline\", \"oversizedBehavior\", \"breakWords\", \"ellipsis\", \"minScale\", \"populateText\", \"role\", \"ignoreFormatting\", \"maxChars\", \"ariaLabel\"]\n });\n }\n /**\r\n * @ignore Text is not to be used directly\r\n */\n get text() {\n return this._text;\n }\n _afterNew() {\n super._afterNew();\n this._makeText();\n $array.each(this._textKeys, property => {\n const propValue = this.get(property);\n if (propValue != undefined) {\n this._text.set(property, propValue);\n }\n });\n if (this.get(\"html\", \"\") !== \"\") {\n this._text.set(\"text\", \"\");\n }\n this.onPrivate(\"maxWidth\", () => {\n this._setMaxDimentions();\n });\n this.onPrivate(\"maxHeight\", () => {\n this._setMaxDimentions();\n });\n }\n _makeText() {\n this._text = this.children.push(Text.new(this._root, {}));\n }\n _updateChildren() {\n super._updateChildren();\n const text = this._text;\n $array.each(this._textKeys, property => {\n this._text.set(property, this.get(property));\n });\n if (this.isDirty(\"maxWidth\") || this.isDirty(\"maxHeight\") || this.isDirty(\"rotation\")) {\n this._setMaxDimentions();\n }\n // Do not show regular text if HTML is used\n if (this.get(\"html\", \"\") !== \"\") {\n text.set(\"text\", \"\");\n } else {\n text.set(\"text\", this.get(\"text\"));\n this._maybeUpdateHTMLColor();\n }\n if (this.isDirty(\"fill\") || this.isDirty(\"fillGradient\")) {\n this._maybeUpdateHTMLColor();\n }\n if (this.isDirty(\"textAlign\") || this.isDirty(\"width\")) {\n const textAlign = this.get(\"textAlign\");\n let x;\n if (this.get(\"width\") != null) {\n if (textAlign == \"right\") {\n x = p100;\n } else if (textAlign == \"center\") {\n x = p50;\n } else {\n x = 0;\n }\n } else {\n if (textAlign == \"left\" || textAlign == \"start\") {\n x = this.get(\"paddingLeft\", 0);\n } else if (textAlign == \"right\" || textAlign == \"end\") {\n x = -this.get(\"paddingRight\", 0);\n }\n }\n text.set(\"x\", x);\n }\n const background = this.get(\"background\");\n if (background) {\n background.setPrivate(\"visible\", text._display.textVisible);\n }\n }\n _maybeUpdateHTMLColor() {\n const htmlElement = this.getPrivate(\"htmlElement\");\n if (htmlElement && this.get(\"fill\")) {\n htmlElement.style.color = this.get(\"fill\").toCSSHex();\n //@todo support gradient\n }\n }\n _setMaxDimentions() {\n const rotation = this.get(\"rotation\");\n const vertical = rotation == 90 || rotation == 270 || rotation == -90;\n const text = this._text;\n const maxWidth = this.get(\"maxWidth\", this.getPrivate(\"maxWidth\", Infinity));\n if ($type.isNumber(maxWidth)) {\n text.set(vertical ? \"maxHeight\" : \"maxWidth\", maxWidth - this.get(\"paddingLeft\", 0) - this.get(\"paddingRight\", 0));\n } else {\n text.set(vertical ? \"maxHeight\" : \"maxWidth\", undefined);\n }\n const maxHeight = this.get(\"maxHeight\", this.getPrivate(\"maxHeight\", Infinity));\n if ($type.isNumber(maxHeight)) {\n text.set(vertical ? \"maxWidth\" : \"maxHeight\", maxHeight - this.get(\"paddingTop\", 0) - this.get(\"paddingBottom\", 0));\n } else {\n text.set(vertical ? \"maxWidth\" : \"maxHeight\", undefined);\n }\n this.root.events.once(\"frameended\", () => {\n text.markDirtyBounds();\n });\n }\n _setDataItem(dataItem) {\n super._setDataItem(dataItem);\n this._markDirtyKey(\"text\");\n const text = this._text;\n if (text.get(\"populateText\")) {\n text.markDirtyText();\n }\n const html = this.get(\"html\");\n if (html && html !== \"\") {\n this._updateHTMLContent();\n }\n }\n /**\r\n * Returns text with populated placeholders and formatting if `populateText` is\r\n * set to `true`.\r\n *\r\n * @return Populated text\r\n */\n getText() {\n return this._text._getText();\n }\n /**\r\n * Returns \"aria-label\" text with populated placeholders and formatting\r\n * if `populateText` is set to `true`.\r\n *\r\n * @return Populated text\r\n */\n getAccessibleText() {\n return this._text._getAccessibleText();\n }\n}\nObject.defineProperty(Label, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Label\"\n});\nObject.defineProperty(Label, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Container.classNames.concat([Label.className])\n});\n","import { Graphics } from \"./Graphics\";\nimport * as $math from \"../util/Math\";\n/**\r\n * Draws a rectangle with a pointer.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/graphics/} for more info\r\n * @important\r\n */\nexport class PointedRectangle extends Graphics {\n _beforeChanged() {\n super._beforeChanged();\n if (this.isDirty(\"pointerBaseWidth\") || this.isDirty(\"cornerRadius\") || this.isDirty(\"pointerLength\") || this.isDirty(\"pointerX\") || this.isDirty(\"pointerY\") || this.isDirty(\"width\") || this.isDirty(\"height\")) {\n this._clear = true;\n }\n }\n _changed() {\n super._changed();\n if (this._clear) {\n this.markDirtyBounds();\n let w = this.width();\n let h = this.height();\n if (w > 0 && h > 0) {\n let cr = this.get(\"cornerRadius\", 8);\n cr = $math.fitToRange(cr, 0, Math.min(w / 2, h / 2));\n let x = this.get(\"pointerX\", 0);\n let y = this.get(\"pointerY\", 0);\n let bwh = this.get(\"pointerBaseWidth\", 15) / 2;\n // corner coordinates\n // top left\n let xtl = 0;\n let ytl = 0;\n // top right\n let xtr = w;\n let ytr = 0;\n // bottom right\n let xbr = w;\n let ybr = h;\n // bottom left\n let xbl = 0;\n let ybl = h;\n // find stem base side: http://$math.stackexchange.com/questions/274712/calculate-on-which-side-of-straign-line-is-dot-located\n // d=(x−x1)(y2−y1)−(y−y1)(x2−x1)\n let d1 = (x - xtl) * (ybr - ytl) - (y - ytl) * (xbr - xtl);\n let d2 = (x - xbl) * (ytr - ybl) - (y - ybl) * (xtr - xbl);\n const display = this._display;\n // top\n display.moveTo(cr, 0);\n if (d1 > 0 && d2 > 0) {\n let stemX = Math.round($math.fitToRange(x, cr + bwh, w - bwh - cr));\n y = $math.fitToRange(y, -Infinity, 0);\n display.lineTo(stemX - bwh, 0);\n display.lineTo(x, y);\n display.lineTo(stemX + bwh, 0);\n }\n display.lineTo(w - cr, 0);\n display.arcTo(w, 0, w, cr, cr);\n // right\n if (d1 > 0 && d2 < 0) {\n let stemY = Math.round($math.fitToRange(y, cr + bwh, h - bwh - cr));\n x = $math.fitToRange(x, w, Infinity);\n display.lineTo(w, cr);\n display.lineTo(w, Math.max(stemY - bwh, cr));\n display.lineTo(x, y);\n display.lineTo(w, stemY + bwh);\n }\n display.lineTo(w, h - cr);\n display.arcTo(w, h, w - cr, h, cr);\n // bottom\n if (d1 < 0 && d2 < 0) {\n let stemX = Math.round($math.fitToRange(x, cr + bwh, w - bwh - cr));\n y = $math.fitToRange(y, h, Infinity);\n display.lineTo(w - cr, h);\n display.lineTo(stemX + bwh, h);\n display.lineTo(x, y);\n display.lineTo(stemX - bwh, h);\n }\n display.lineTo(cr, h);\n display.arcTo(0, h, 0, h - cr, cr);\n // left\n if (d1 < 0 && d2 > 0) {\n let stemY = Math.round($math.fitToRange(y, cr + bwh, h - cr - bwh));\n x = $math.fitToRange(x, -Infinity, 0);\n display.lineTo(0, h - cr);\n display.lineTo(0, stemY + bwh);\n display.lineTo(x, y);\n display.lineTo(0, Math.max(stemY - bwh, cr));\n }\n display.lineTo(0, cr);\n display.arcTo(0, 0, cr, 0, cr);\n display.closePath();\n }\n }\n }\n}\nObject.defineProperty(PointedRectangle, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"PointedRectangle\"\n});\nObject.defineProperty(PointedRectangle, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Graphics.classNames.concat([PointedRectangle.className])\n});\n","import { MultiDisposer } from \"../util/Disposer\";\nimport { Label } from \"../render/Label\";\nimport { PointedRectangle } from \"../render/PointedRectangle\";\nimport { Container } from \"./Container\";\nimport { Percent } from \"../util/Percent\";\nimport { Color } from \"../util/Color\";\nimport * as $math from \"../util/Math\";\nimport * as $array from \"../util/Array\";\nimport * as $utils from \"../util/Utils\";\n/**\r\n * Creates a tooltip.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/tooltips/} for more info\r\n * @important\r\n */\nexport class Tooltip extends Container {\n constructor(root, settings, isReal, templates = []) {\n super(root, settings, isReal, templates);\n Object.defineProperty(this, \"_fx\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_fy\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_label\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_fillDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_strokeDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_labelDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_w\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_h\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_keepHoverDp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_htmlContentHovered\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n _afterNew() {\n this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, [\"tooltip\"]);\n super._afterNew();\n const background = this._setDefaultFn(\"background\", () => {\n return PointedRectangle.new(this._root, {});\n });\n background.set(\"themeTags\", [\"tooltip\", \"background\"]);\n this._label = this.children.push(Label.new(this._root, {}));\n this._disposers.push(this._label.events.on(\"boundschanged\", () => {\n this._updateBackground();\n }));\n this._disposers.push(this.on(\"bounds\", () => {\n this._updateBackground();\n }));\n this._updateTextColor();\n this._root.tooltipContainer.children.push(this);\n this.hide(0);\n this._disposers.push(this.label.onPrivate(\"htmlElement\", htmlElement => {\n if (htmlElement) {\n this._disposers.push($utils.addEventListener(htmlElement, \"pointerover\", _ev => {\n this._htmlContentHovered = true;\n }));\n this._disposers.push($utils.addEventListener(htmlElement, \"pointerout\", _ev => {\n this._htmlContentHovered = false;\n }));\n }\n }));\n this.on(\"visible\", _ev => {\n this._handleReaderAnnouncement();\n });\n this.label.events.on(\"dataitemchanged\", _ev => {\n this._handleReaderAnnouncement();\n });\n this._root._tooltips.push(this);\n }\n _handleReaderAnnouncement() {\n if (this.get(\"readerAnnounce\") && this.isVisibleDeep()) {\n this._root.readerAlert(this.label.getAccessibleText());\n }\n }\n /**\r\n * A [[Label]] element for the tooltip.\r\n *\r\n * @readonly\r\n * @return Label\r\n */\n get label() {\n return this._label;\n }\n /**\r\n * Permanently disposes the tooltip.\r\n */\n _dispose() {\n super._dispose();\n $array.remove(this._root._tooltips, this);\n }\n _updateChildren() {\n super._updateChildren();\n if (this.isDirty(\"pointerOrientation\") || this.isPrivateDirty(\"minWidth\") || this.isPrivateDirty(\"minHeight\")) {\n this.get(\"background\")._markDirtyKey(\"width\");\n }\n const labelText = this.get(\"labelText\");\n if (labelText != null) {\n this.label.set(\"text\", this.get(\"labelText\"));\n }\n const labelHTML = this.get(\"labelHTML\");\n if (labelHTML != null) {\n this.label.set(\"html\", this.get(\"labelHTML\"));\n }\n const labelAriaLabel = this.get(\"labelAriaLabel\");\n if (labelAriaLabel != null) {\n this.label.set(\"ariaLabel\", this.get(\"labelAriaLabel\"));\n }\n }\n _changed() {\n super._changed();\n if (this.isDirty(\"pointTo\") || this.isDirty(\"pointerOrientation\")) {\n // can't compare to previous, as sometimes pointTo is set twice (when pointer moves, so the position won't be udpated)\n this._updateBackground();\n }\n if (this.isDirty(\"tooltipTarget\")) {\n this.updateBackgroundColor();\n }\n if (this.isDirty(\"keepTargetHover\")) {\n const keephover = this.get(\"keepTargetHover\");\n if (keephover) {\n const bg = this.get(\"background\");\n this._keepHoverDp = new MultiDisposer([bg.events.on(\"pointerover\", _ev => {\n let target = this.get(\"tooltipTarget\");\n if (target) {\n if (target.parent && target.parent.getPrivate(\"tooltipTarget\") == target) {\n target = target.parent;\n }\n target.hover();\n }\n }), bg.events.on(\"pointerout\", _ev => {\n let target = this.get(\"tooltipTarget\");\n if (target) {\n if (target.parent && target.parent.getPrivate(\"tooltipTarget\") == target) {\n target = target.parent;\n }\n if (!this._htmlContentHovered) {\n target.unhover();\n }\n }\n })]);\n this.label.onPrivate(\"htmlElement\", htmlElement => {\n if (this._keepHoverDp && htmlElement) {\n this._keepHoverDp.disposers.push($utils.addEventListener(htmlElement, \"pointerleave\", ev => {\n const outEvent = this.root._renderer.getEvent(ev);\n bg.events.dispatch(\"pointerout\", {\n type: \"pointerout\",\n originalEvent: outEvent.event,\n point: outEvent.point,\n simulated: false,\n target: bg\n });\n }));\n }\n });\n } else {\n if (this._keepHoverDp) {\n this._keepHoverDp.dispose();\n this._keepHoverDp = undefined;\n }\n }\n }\n }\n _onShow() {\n super._onShow();\n this.updateBackgroundColor();\n }\n updateBackgroundColor() {\n let tooltipTarget = this.get(\"tooltipTarget\");\n const background = this.get(\"background\");\n let fill;\n let stroke;\n if (tooltipTarget && background) {\n fill = tooltipTarget.get(\"fill\");\n stroke = tooltipTarget.get(\"stroke\");\n if (fill == null) {\n fill = stroke;\n }\n if (this.get(\"getFillFromSprite\")) {\n if (this._fillDp) {\n this._fillDp.dispose();\n }\n if (fill != null) {\n background.set(\"fill\", fill);\n }\n this._fillDp = tooltipTarget.on(\"fill\", fill => {\n if (fill != null) {\n background.set(\"fill\", fill);\n this._updateTextColor(fill);\n }\n });\n this._disposers.push(this._fillDp);\n }\n if (this.get(\"getStrokeFromSprite\")) {\n if (this._strokeDp) {\n this._strokeDp.dispose();\n }\n if (fill != null) {\n background.set(\"stroke\", fill);\n }\n this._strokeDp = tooltipTarget.on(\"fill\", fill => {\n if (fill != null) {\n background.set(\"stroke\", fill);\n }\n });\n this._disposers.push(this._strokeDp);\n }\n if (this.get(\"getLabelFillFromSprite\")) {\n if (this._labelDp) {\n this._labelDp.dispose();\n }\n if (fill != null) {\n this.label.set(\"fill\", fill);\n }\n this._labelDp = tooltipTarget.on(\"fill\", fill => {\n if (fill != null) {\n this.label.set(\"fill\", fill);\n }\n });\n this._disposers.push(this._labelDp);\n }\n }\n this._updateTextColor(fill);\n }\n _updateTextColor(fill) {\n if (this.get(\"autoTextColor\")) {\n if (fill == null) {\n fill = this.get(\"background\").get(\"fill\");\n }\n if (fill == null) {\n fill = this._root.interfaceColors.get(\"background\");\n }\n if (fill instanceof Color) {\n this.label.set(\"fill\", Color.alternative(fill, this._root.interfaceColors.get(\"alternativeText\"), this._root.interfaceColors.get(\"text\")));\n }\n }\n }\n _setDataItem(dataItem) {\n super._setDataItem(dataItem);\n this.label._setDataItem(dataItem);\n }\n _updateBackground() {\n super.updateBackground();\n const parent = this._root.container;\n if (parent) {\n let cw = 0.5;\n let ch = 0.5;\n let centerX = this.get(\"centerX\");\n if (centerX instanceof Percent) {\n cw = centerX.value;\n }\n let centerY = this.get(\"centerY\");\n if (centerY instanceof Percent) {\n ch = centerY.value;\n }\n let parentW = parent.width();\n let parentH = parent.height();\n let tooltipContainer = this.parent;\n let xx = 0;\n let yy = 0;\n if (tooltipContainer) {\n xx = tooltipContainer.x();\n yy = tooltipContainer.y();\n const layerMargin = tooltipContainer.get(\"layerMargin\");\n if (layerMargin) {\n xx += layerMargin.left || 0;\n yy += layerMargin.top || 0;\n parentW += (layerMargin.left || 0) + (layerMargin.right || 0);\n parentH += (layerMargin.top || 0) + (layerMargin.bottom || 0);\n }\n }\n const bounds = this.get(\"bounds\", {\n left: -xx,\n top: -yy,\n right: parentW - xx,\n bottom: parentH - yy\n });\n this._updateBounds();\n let w = this.width();\n let h = this.height();\n // use old w and h,as when tooltip is hidden, these are 0 and unneeded animation happens\n if (w === 0) {\n w = this._w;\n }\n if (h === 0) {\n h = this._h;\n }\n let pointTo = this.get(\"pointTo\", {\n x: parentW / 2,\n y: parentH / 2\n });\n let x = pointTo.x;\n let y = pointTo.y;\n let pointerOrientation = this.get(\"pointerOrientation\");\n let background = this.get(\"background\");\n let pointerLength = 0;\n let bgStrokeSizeY = 0;\n let bgStrokeSizeX = 0;\n if (background instanceof PointedRectangle) {\n pointerLength = background.get(\"pointerLength\", 0);\n bgStrokeSizeY = background.get(\"strokeWidth\", 0) / 2;\n bgStrokeSizeX = bgStrokeSizeY;\n background.set(\"width\", w);\n background.set(\"height\", h);\n }\n let pointerX = 0;\n let pointerY = 0;\n let boundsW = bounds.right - bounds.left;\n let boundsH = bounds.bottom - bounds.top;\n // horizontal\n if (pointerOrientation == \"horizontal\" || pointerOrientation == \"left\" || pointerOrientation == \"right\") {\n bgStrokeSizeY = 0;\n if (pointerOrientation == \"horizontal\") {\n if (x > bounds.left + boundsW / 2) {\n x -= w * (1 - cw) + pointerLength;\n bgStrokeSizeX *= -1;\n } else {\n x += w * cw + pointerLength;\n }\n } else if (pointerOrientation == \"left\") {\n x += w * (1 - cw) + pointerLength;\n } else {\n x -= w * cw + pointerLength;\n bgStrokeSizeX *= -1;\n }\n }\n // vertical pointer\n else {\n bgStrokeSizeX = 0;\n if (pointerOrientation == \"vertical\") {\n if (y > bounds.top + h / 2 + pointerLength) {\n y -= h * (1 - ch) + pointerLength;\n } else {\n y += h * ch + pointerLength;\n bgStrokeSizeY *= -1;\n }\n } else if (pointerOrientation == \"down\") {\n y -= h * (1 - ch) + pointerLength;\n } else {\n y += h * ch + pointerLength;\n bgStrokeSizeY *= -1;\n }\n }\n x = $math.fitToRange(x, bounds.left + w * cw, bounds.left + boundsW - w * (1 - cw)) + bgStrokeSizeX;\n y = $math.fitToRange(y, bounds.top + h * ch, bounds.top + boundsH - h * (1 - ch)) - bgStrokeSizeY;\n pointerX = pointTo.x - x + w * cw + bgStrokeSizeX;\n pointerY = pointTo.y - y + h * ch - bgStrokeSizeY;\n this._fx = x;\n this._fy = y;\n const animationDuration = this.get(\"animationDuration\", 0);\n if (animationDuration > 0 && this.get(\"visible\") && this.get(\"opacity\") > 0.1) {\n const animationEasing = this.get(\"animationEasing\");\n this.animate({\n key: \"x\",\n to: x,\n duration: animationDuration,\n easing: animationEasing\n });\n this.animate({\n key: \"y\",\n to: y,\n duration: animationDuration,\n easing: animationEasing\n });\n } else {\n this.set(\"x\", x);\n this.set(\"y\", y);\n }\n if (background instanceof PointedRectangle) {\n background.set(\"pointerX\", pointerX);\n background.set(\"pointerY\", pointerY);\n }\n if (w > 0) {\n this._w = w;\n }\n if (h > 0) {\n this._h = h;\n }\n }\n }\n}\nObject.defineProperty(Tooltip, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Tooltip\"\n});\nObject.defineProperty(Tooltip, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Container.classNames.concat([Tooltip.className])\n});\n","import { Entity } from \"./Entity\";\nimport { TextFormatter } from \"./TextFormatter\";\nimport * as $object from \"./Object\";\nimport * as $utils from \"./Utils\";\nimport * as $type from \"./Type\";\n/**\r\n * Number formatter\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-numbers/} for more info\r\n * @important\r\n */\nexport class NumberFormatter extends Entity {\n _setDefaults() {\n // Defaults\n this._setDefault(\"negativeBase\", 0);\n this._setDefault(\"numberFormat\", \"#,###.#####\");\n this._setDefault(\"smallNumberThreshold\", 1.00);\n const bns = \"_big_number_suffix_\";\n const sns = \"_small_number_suffix_\";\n const bs = \"_byte_suffix_\";\n this._setDefault(\"bigNumberPrefixes\", [{\n \"number\": 1e+3,\n \"suffix\": this._t(bns + \"3\")\n }, {\n \"number\": 1e+6,\n \"suffix\": this._t(bns + \"6\")\n }, {\n \"number\": 1e+9,\n \"suffix\": this._t(bns + \"9\")\n }, {\n \"number\": 1e+12,\n \"suffix\": this._t(bns + \"12\")\n }, {\n \"number\": 1e+15,\n \"suffix\": this._t(bns + \"15\")\n }, {\n \"number\": 1e+18,\n \"suffix\": this._t(bns + \"18\")\n }, {\n \"number\": 1e+21,\n \"suffix\": this._t(bns + \"21\")\n }, {\n \"number\": 1e+24,\n \"suffix\": this._t(bns + \"24\")\n }]);\n this._setDefault(\"smallNumberPrefixes\", [{\n \"number\": 1e-24,\n \"suffix\": this._t(sns + \"24\")\n }, {\n \"number\": 1e-21,\n \"suffix\": this._t(sns + \"21\")\n }, {\n \"number\": 1e-18,\n \"suffix\": this._t(sns + \"18\")\n }, {\n \"number\": 1e-15,\n \"suffix\": this._t(sns + \"15\")\n }, {\n \"number\": 1e-12,\n \"suffix\": this._t(sns + \"12\")\n }, {\n \"number\": 1e-9,\n \"suffix\": this._t(sns + \"9\")\n }, {\n \"number\": 1e-6,\n \"suffix\": this._t(sns + \"6\")\n }, {\n \"number\": 1e-3,\n \"suffix\": this._t(sns + \"3\")\n }]);\n this._setDefault(\"bytePrefixes\", [{\n \"number\": 1,\n suffix: this._t(bs + \"B\")\n }, {\n \"number\": 1024,\n suffix: this._t(bs + \"KB\")\n }, {\n \"number\": 1048576,\n suffix: this._t(bs + \"MB\")\n }, {\n \"number\": 1073741824,\n suffix: this._t(bs + \"GB\")\n }, {\n \"number\": 1099511627776,\n suffix: this._t(bs + \"TB\")\n }, {\n \"number\": 1125899906842624,\n suffix: this._t(bs + \"PB\")\n }]);\n super._setDefaults();\n }\n _beforeChanged() {\n super._beforeChanged();\n }\n /**\r\n * Formats the number according to specific format.\r\n *\r\n * @param value Value to format\r\n * @param format Format to apply\r\n * @return Formatted number\r\n */\n format(value, format, precision) {\n // no format passed in or \"Number\"\n if (format == null || $type.isString(format) && format.toLowerCase() === \"number\") {\n format = this.get(\"numberFormat\", \"\");\n }\n // Init return value\n let formatted;\n // Cast to number just in case\n // TODO: maybe use better casting\n let source = Number(value);\n // Is it a built-in format or Intl.NumberFormatOptions\n if ($type.isObject(format)) {\n try {\n if (this.get(\"intlLocales\")) {\n return new Intl.NumberFormat(this.get(\"intlLocales\"), format).format(source);\n } else {\n return new Intl.NumberFormat(undefined, format).format(source);\n }\n } catch (e) {\n return \"Invalid\";\n }\n } else {\n // Clean format\n format = $utils.cleanFormat(format);\n // Get format info (it will also deal with parser caching)\n let info = this.parseFormat(format, this._root.language);\n // format and replace the number\n let details;\n if (source > this.get(\"negativeBase\")) {\n details = info.positive;\n } else if (source < this.get(\"negativeBase\")) {\n details = info.negative;\n } else {\n details = info.zero;\n }\n // Adjust precision\n if (precision != null && !details.mod) {\n details = $object.copy(details);\n details.decimals.active = source == 0 ? 0 : precision;\n }\n // Format\n formatted = details.template.split($type.PLACEHOLDER).join(this.applyFormat(source, details));\n }\n if (this.get(\"forceLTR\") === true) {\n formatted = \"‎\" + formatted;\n }\n return formatted;\n }\n /**\r\n * Parses supplied format into structured object which can be used to format\r\n * the number.\r\n *\r\n * @param format Format string, i.e. \"#,###.00\"\r\n * @param language Language\r\n * @ignore\r\n */\n parseFormat(format, language) {\n // Check cache\n // TODO\n // let cached = this.getCache(format);\n // if (cached != null) {\n // \treturn cached;\n // }\n const thousandSeparator = language.translateEmpty(\"_thousandSeparator\");\n const decimalSeparator = language.translateEmpty(\"_decimalSeparator\");\n // init format parse info holder\n let info = {\n \"positive\": {\n \"thousands\": {\n \"active\": -1,\n \"passive\": -1,\n \"interval\": -1,\n \"separator\": thousandSeparator\n },\n \"decimals\": {\n \"active\": -1,\n \"passive\": -1,\n \"separator\": decimalSeparator\n },\n \"template\": \"\",\n \"source\": \"\",\n \"parsed\": false\n },\n \"negative\": {\n \"thousands\": {\n \"active\": -1,\n \"passive\": -1,\n \"interval\": -1,\n \"separator\": thousandSeparator\n },\n \"decimals\": {\n \"active\": -1,\n \"passive\": -1,\n \"separator\": decimalSeparator\n },\n \"template\": \"\",\n \"source\": \"\",\n \"parsed\": false\n },\n \"zero\": {\n \"thousands\": {\n \"active\": -1,\n \"passive\": -1,\n \"interval\": -1,\n \"separator\": thousandSeparator\n },\n \"decimals\": {\n \"active\": -1,\n \"passive\": -1,\n \"separator\": decimalSeparator\n },\n \"template\": \"\",\n \"source\": \"\",\n \"parsed\": false\n }\n };\n // Escape double vertical bars (that mean display one vertical bar)\n format = format.replace(\"||\", $type.PLACEHOLDER2);\n // Split it up and deal with different formats\n let parts = format.split(\"|\");\n info.positive.source = parts[0];\n if (typeof parts[2] === \"undefined\") {\n info.zero = info.positive;\n } else {\n info.zero.source = parts[2];\n }\n if (typeof parts[1] === \"undefined\") {\n info.negative = info.positive;\n } else {\n info.negative.source = parts[1];\n }\n // Parse each\n $object.each(info, (_part, item) => {\n // Already parsed\n if (item.parsed) {\n return;\n }\n // Check cached\n // TODO\n // if (typeof this.getCache(item.source) !== \"undefined\") {\n // \tinfo[part] = this.getCache(item.source);\n // \treturn;\n // }\n // Begin parsing\n let partFormat = item.source;\n // Just \"Number\"?\n if (partFormat.toLowerCase() === \"number\") {\n partFormat = this.get(\"numberFormat\", \"#,###.#####\");\n }\n // Let TextFormatter split into chunks\n let chunks = TextFormatter.chunk(partFormat, true);\n for (let i = 0; i < chunks.length; i++) {\n let chunk = chunks[i];\n // replace back double vertical bar\n chunk.text = chunk.text.replace($type.PLACEHOLDER2, \"|\");\n if (chunk.type === \"value\") {\n // Parse format\n // Look for codes\n let matches = chunk.text.match(/[#0.,]+[ ]?[abespABESP%!]?[abespABESP‰!]?/);\n if (matches) {\n if (matches === null || matches[0] === \"\") {\n // no codes here - assume string\n // nothing to do here\n item.template += chunk.text;\n } else {\n // look for the format modifiers at the end\n let mods = matches[0].match(/[abespABESP%‰!]{2}|[abespABESP%‰]{1}$/);\n if (mods) {\n item.mod = mods[0].toLowerCase();\n item.modSpacing = matches[0].match(/[ ]{1}[abespABESP%‰!]{1}$/) ? true : false;\n }\n // break the format up\n let a = matches[0].split(\".\");\n // Deal with thousands\n if (a[0] === \"\") {\n // No directives for thousands\n // Leave default settings (no formatting)\n } else {\n // Counts\n item.thousands.active = (a[0].match(/0/g) || []).length;\n item.thousands.passive = (a[0].match(/\\#/g) || []).length + item.thousands.active;\n // Separator interval\n let b = a[0].split(\",\");\n if (b.length === 1) {\n // No thousands separators\n // Do nothing\n } else {\n // Use length fo the last chunk as thousands length\n item.thousands.interval = (b.pop() || \"\").length;\n if (item.thousands.interval === 0) {\n item.thousands.interval = -1;\n }\n }\n }\n // Deal with decimals\n if (typeof a[1] === \"undefined\") {\n // No directives for decimals\n // Leave at defaults (no formatting)\n } else {\n // Counts\n item.decimals.active = (a[1].match(/0/g) || []).length;\n item.decimals.passive = (a[1].match(/\\#/g) || []).length + item.decimals.active;\n }\n // Add special code to template\n item.template += chunk.text.split(matches[0]).join($type.PLACEHOLDER);\n }\n }\n } else {\n // Quoted string - take it as it is\n item.template += chunk.text;\n }\n }\n // Apply style formatting\n //item.template = getTextFormatter().format(item.template, this.outputFormat);\n // Save cache\n // TODO\n //this.setCache(item.source, item);\n // Mark this as parsed\n item.parsed = true;\n });\n // Save cache (the whole thing)\n // TODO\n //this.setCache(format, info);\n return info;\n }\n /**\r\n * Applies parsed format to a numeric value.\r\n *\r\n * @param value Value\r\n * @param details Parsed format as returned by parseFormat()\r\n * @return Formatted number\r\n * @ignore\r\n */\n applyFormat(value, details) {\n // Use absolute values\n let negative = value < 0;\n value = Math.abs(value);\n // Recalculate according to modifier\n let prefix = \"\",\n suffix = \"\";\n let mods = details.mod ? details.mod.split(\"\") : [];\n if (mods.indexOf(\"b\") !== -1) {\n let a = this.applyPrefix(value, this.get(\"bytePrefixes\"), mods.indexOf(\"!\") !== -1);\n value = a[0];\n prefix = a[1];\n suffix = a[2];\n if (details.modSpacing) {\n suffix = \" \" + suffix;\n }\n } else if (mods.indexOf(\"a\") !== -1) {\n let a = this.applyPrefix(value, value < this.get(\"smallNumberThreshold\") ? this.get(\"smallNumberPrefixes\") : this.get(\"bigNumberPrefixes\"), mods.indexOf(\"!\") !== -1);\n value = a[0];\n prefix = a[1];\n suffix = a[2];\n if (details.modSpacing) {\n suffix = \" \" + suffix;\n }\n } else if (mods.indexOf(\"p\") !== -1) {\n let ol = Math.min(value.toString().length + 2, 21);\n //value *= 100;\n value = parseFloat(value.toPrecision(ol));\n prefix = this._root.language.translate(\"_percentPrefix\");\n suffix = this._root.language.translate(\"_percentSuffix\");\n if (prefix == \"\" && suffix == \"\") {\n suffix = \"%\";\n }\n } else if (mods.indexOf(\"%\") !== -1) {\n let ol = Math.min(value.toString().length + 2, 21);\n value *= 100;\n value = parseFloat(value.toPrecision(ol));\n suffix = \"%\";\n } else if (mods.indexOf(\"‰\") !== -1) {\n let ol = Math.min(value.toString().length + 3, 21);\n value *= 1000;\n value = parseFloat(value.toPrecision(ol));\n suffix = \"‰\";\n }\n // Round to passive\n if (mods.indexOf(\"e\") !== -1) {\n // convert the value to exponential\n let exp;\n if (details.decimals.passive >= 0) {\n exp = value.toExponential(details.decimals.passive).split(\"e\");\n } else {\n exp = value.toExponential().split(\"e\");\n }\n value = Number(exp[0]);\n suffix = \"e\" + exp[1];\n if (details.modSpacing) {\n suffix = \" \" + suffix;\n }\n } else if (details.decimals.passive === 0) {\n value = Math.round(value);\n } else if (details.decimals.passive > 0) {\n const decimals = $utils.decimalPlaces(value);\n if (decimals > 0) {\n const d = Math.pow(10, details.decimals.passive);\n value = Math.round(parseFloat((value * d).toFixed(decimals))) / d;\n }\n }\n // Init return value\n let res = \"\";\n // Calc integer and decimal parts\n let a = $type.numberToString(value).split(\".\");\n // Format integers\n let ints = a[0];\n // Pad integers to active length\n if (ints.length < details.thousands.active) {\n ints = Array(details.thousands.active - ints.length + 1).join(\"0\") + ints;\n }\n // Insert thousands separators\n if (details.thousands.interval > 0) {\n let ip = [];\n let intsr = ints.split(\"\").reverse().join(\"\");\n for (let i = 0, len = ints.length; i <= len; i += details.thousands.interval) {\n let c = intsr.substr(i, details.thousands.interval).split(\"\").reverse().join(\"\");\n if (c !== \"\") {\n ip.unshift(c);\n }\n }\n ints = ip.join(details.thousands.separator);\n }\n // Add integers\n res += ints;\n // Add decimals\n if (a.length === 1) {\n a.push(\"\");\n }\n let decs = a[1];\n // Fill zeros?\n if (decs.length < details.decimals.active) {\n decs += Array(details.decimals.active - decs.length + 1).join(\"0\");\n }\n if (decs !== \"\") {\n res += details.decimals.separator + decs;\n }\n // Can't have empty return value\n if (res === \"\") {\n res = \"0\";\n }\n // Add minus sign back\n if (value !== 0 && negative && mods.indexOf(\"s\") === -1) {\n res = \"-\" + res;\n }\n // Add suffixes/prefixes\n if (prefix) {\n res = prefix + res;\n }\n if (suffix) {\n res += suffix;\n }\n return res;\n }\n applyPrefix(value, prefixes, force = false) {\n let newvalue = value;\n let prefix = \"\";\n let suffix = \"\";\n let applied = false;\n let k = 1;\n for (let i = 0, len = prefixes.length; i < len; i++) {\n if (prefixes[i].number <= value) {\n if (prefixes[i].number === 0) {\n newvalue = 0;\n } else {\n newvalue = value / prefixes[i].number;\n k = prefixes[i].number;\n }\n prefix = prefixes[i].prefix;\n suffix = prefixes[i].suffix;\n applied = true;\n }\n }\n if (!applied && force && prefixes.length && value != 0) {\n // Prefix was not applied. Use the first prefix.\n newvalue = value / prefixes[0].number;\n prefix = prefixes[0].prefix;\n suffix = prefixes[0].suffix;\n applied = true;\n }\n if (applied) {\n newvalue = parseFloat(newvalue.toPrecision(Math.min(k.toString().length + Math.floor(newvalue).toString().replace(/[^0-9]*/g, \"\").length, 21)));\n }\n return [newvalue, prefix, suffix];\n }\n /**\r\n * Replaces brackets with temporary placeholders.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Input text\r\n * @return Escaped text\r\n */\n escape(text) {\n return text.replace(\"||\", $type.PLACEHOLDER2);\n }\n /**\r\n * Replaces placeholders back to brackets.\r\n *\r\n * @ignore Exclude from docs\r\n * @param text Escaped text\r\n * @return Unescaped text\r\n */\n unescape(text) {\n return text.replace($type.PLACEHOLDER2, \"|\");\n }\n}\n","function parseDate(timezone, date) {\n let year = 0;\n let month = 0;\n let day = 1;\n let hour = 0;\n let minute = 0;\n let second = 0;\n let millisecond = 0;\n let weekday = 0;\n timezone.formatToParts(date).forEach(x => {\n switch (x.type) {\n case \"year\":\n year = +x.value;\n break;\n case \"month\":\n month = +x.value - 1;\n break;\n case \"day\":\n day = +x.value;\n break;\n case \"hour\":\n hour = +x.value;\n break;\n case \"minute\":\n minute = +x.value;\n break;\n case \"second\":\n second = +x.value;\n break;\n case \"fractionalSecond\":\n millisecond = +x.value;\n break;\n case \"weekday\":\n switch (x.value) {\n case \"Sun\":\n weekday = 0;\n break;\n case \"Mon\":\n weekday = 1;\n break;\n case \"Tue\":\n weekday = 2;\n break;\n case \"Wed\":\n weekday = 3;\n break;\n case \"Thu\":\n weekday = 4;\n break;\n case \"Fri\":\n weekday = 5;\n break;\n case \"Sat\":\n weekday = 6;\n break;\n }\n }\n });\n if (hour === 24) {\n hour = 0;\n }\n return {\n year,\n month,\n day,\n hour,\n minute,\n second,\n millisecond,\n weekday\n };\n}\nfunction toUTCDate(timezone, date) {\n const {\n year,\n month,\n day,\n hour,\n minute,\n second,\n millisecond\n } = parseDate(timezone, date);\n return Date.UTC(year, month, day, hour, minute, second, millisecond);\n}\nexport class Timezone {\n constructor(timezone, isReal) {\n Object.defineProperty(this, \"_utc\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_dtf\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"name\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n if (!isReal) {\n throw new Error(\"You cannot use `new Class()`, instead use `Class.new()`\");\n }\n this.name = timezone;\n this._utc = new Intl.DateTimeFormat(\"UTC\", {\n hour12: false,\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n weekday: \"short\",\n fractionalSecondDigits: 3\n });\n this._dtf = new Intl.DateTimeFormat(\"UTC\", {\n hour12: false,\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n weekday: \"short\",\n fractionalSecondDigits: 3\n });\n }\n /**\r\n * Use this method to create an instance of this class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#New_element_syntax} for more info\r\n * @param timezone IANA timezone\r\n * @return Instantiated object\r\n */\n static new(timezone) {\n return new this(timezone, true);\n }\n convertLocal(date) {\n const offset = this.offsetUTC(date);\n const userOffset = date.getTimezoneOffset();\n const output = new Date(date);\n output.setUTCMinutes(output.getUTCMinutes() - (offset - userOffset));\n const newUserOffset = output.getTimezoneOffset();\n if (userOffset != newUserOffset) {\n output.setUTCMinutes(output.getUTCMinutes() + newUserOffset - userOffset);\n }\n return output;\n }\n offsetUTC(date) {\n const utc = toUTCDate(this._utc, date);\n const dtf = toUTCDate(this._dtf, date);\n return (utc - dtf) / 60000;\n }\n parseDate(date) {\n return parseDate(this._dtf, date);\n }\n}\n","import { Entity } from \"./Entity\";\nimport { TextFormatter } from \"./TextFormatter\";\nimport { Timezone } from \"./Timezone\";\nimport * as $type from \"./Type\";\nimport * as $utils from \"./Utils\";\n/**\r\n * Date formatter class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-dates/} for more info\r\n * @important\r\n */\nexport class DateFormatter extends Entity {\n _setDefaults() {\n // Defaults\n this._setDefault(\"capitalize\", true);\n this._setDefault(\"dateFormat\", \"yyyy-MM-dd\");\n super._setDefaults();\n }\n _beforeChanged() {\n super._beforeChanged();\n }\n /**\r\n * Formats a source `Date` object into string format\r\n * @param source inpout date\r\n * @param format Output format\r\n * @param ignoreTimezone Ignore timezone?\r\n * @return Formatted date\r\n */\n format(source, format, ignoreTimezone = false) {\n // Locale?\n // TODO\n // No format passed in or it's empty\n if (typeof format === \"undefined\" || format === \"\") {\n format = this.get(\"dateFormat\", \"yyyy-MM-dd\");\n }\n // Init return value\n let formatted;\n // Cast?\n // TODO: decide if we need to cast\n let date = source;\n // Is it a built-in format or Intl.DateTimeFormat\n if ($type.isObject(format)) {\n try {\n const locales = this.get(\"intlLocales\");\n if (locales) {\n return new Intl.DateTimeFormat(locales, format).format(date);\n } else {\n return new Intl.DateTimeFormat(undefined, format).format(date);\n }\n } catch (e) {\n return \"Invalid\";\n }\n }\n // get format info (it will also deal with parser caching)\n let info = this.parseFormat(format);\n // Should we apply custom time zone?\n const timezone = this._root.timezone;\n if (timezone && !this._root.utc && !ignoreTimezone) {\n date = timezone.convertLocal(date);\n }\n // Check if it's a valid date\n if (!$type.isNumber(date.getTime())) {\n // TODO translation\n //return this._t(\"Invalid date\");\n return \"Invalid date\";\n }\n // Apply format\n formatted = this.applyFormat(date, info, ignoreTimezone);\n // Capitalize\n if (this.get(\"capitalize\")) {\n formatted = formatted.replace(/^.{1}/, formatted.substr(0, 1).toUpperCase());\n }\n // We're done\n return formatted;\n }\n /**\r\n * Applies format to Date.\r\n *\r\n * @param date Date object\r\n * @param info Parsed format information\r\n * @return Formatted date string\r\n */\n applyFormat(date, info, ignoreTimezone = false) {\n // Init return value\n let res = info.template;\n // Get values\n let fullYear,\n month,\n weekday,\n day,\n hours,\n minutes,\n seconds,\n milliseconds,\n timestamp = date.getTime();\n if (this._root.utc && !ignoreTimezone) {\n fullYear = date.getUTCFullYear();\n month = date.getUTCMonth();\n weekday = date.getUTCDay();\n day = date.getUTCDate();\n hours = date.getUTCHours();\n minutes = date.getUTCMinutes();\n seconds = date.getUTCSeconds();\n milliseconds = date.getUTCMilliseconds();\n } else {\n fullYear = date.getFullYear();\n month = date.getMonth();\n weekday = date.getDay();\n day = date.getDate();\n hours = date.getHours();\n minutes = date.getMinutes();\n seconds = date.getSeconds();\n milliseconds = date.getMilliseconds();\n }\n // Go through each part and format/replace it in template\n for (let i = 0, len = info.parts.length; i < len; i++) {\n let value = \"\";\n switch (info.parts[i]) {\n case \"G\":\n value = this._t(fullYear < 0 ? \"_era_bc\" : \"_era_ad\");\n break;\n case \"yyyy\":\n value = Math.abs(fullYear).toString();\n if (fullYear < 0) {\n value += this._t(\"_era_bc\");\n }\n break;\n case \"yyy\":\n case \"yy\":\n case \"y\":\n value = Math.abs(fullYear).toString().substr(-info.parts[i].length);\n if (fullYear < 0) {\n value += this._t(\"_era_bc\");\n }\n break;\n case \"YYYY\":\n case \"YYY\":\n case \"YY\":\n case \"Y\":\n let year = $utils.getWeekYear(date, this._root.utc);\n if (info.parts[i] == \"YYYY\") {\n value = Math.abs(year).toString();\n } else {\n value = Math.abs(year).toString().substr(-info.parts[i].length);\n }\n if (year < 0) {\n value += this._t(\"_era_bc\");\n }\n break;\n case \"u\":\n // @todo\n break;\n case \"q\":\n value = \"\" + Math.ceil((date.getMonth() + 1) / 3);\n break;\n case \"MMMMM\":\n value = this._t(this._getMonth(month)).substr(0, 1);\n break;\n case \"MMMM\":\n value = this._t(this._getMonth(month));\n break;\n case \"MMM\":\n value = this._t(this._getShortMonth(month));\n break;\n case \"MM\":\n value = $utils.padString(month + 1, 2, \"0\");\n break;\n case \"M\":\n value = (month + 1).toString();\n break;\n case \"ww\":\n value = $utils.padString($utils.getWeek(date, this._root.utc), 2, \"0\");\n break;\n case \"w\":\n value = $utils.getWeek(date, this._root.utc).toString();\n break;\n case \"W\":\n value = $utils.getMonthWeek(date, this._root.utc).toString();\n break;\n case \"dd\":\n value = $utils.padString(day, 2, \"0\");\n break;\n case \"d\":\n value = day.toString();\n break;\n case \"DD\":\n case \"DDD\":\n value = $utils.padString($utils.getYearDay(date, this._root.utc).toString(), info.parts[i].length, \"0\");\n break;\n case \"D\":\n value = $utils.getYearDay(date, this._root.utc).toString();\n break;\n case \"F\":\n // @todo\n break;\n case \"g\":\n // @todo\n break;\n case \"t\":\n value = this._root.language.translateFunc(\"_dateOrd\").call(this, day);\n break;\n case \"E\":\n value = (weekday || 7).toString();\n break;\n case \"EE\":\n value = $utils.padString((weekday || 7).toString(), 2, \"0\");\n break;\n case \"EEE\":\n case \"eee\":\n value = this._t(this._getShortWeekday(weekday));\n break;\n case \"EEEE\":\n case \"eeee\":\n value = this._t(this._getWeekday(weekday));\n break;\n case \"EEEEE\":\n case \"eeeee\":\n value = this._t(this._getShortWeekday(weekday)).substr(0, 1);\n break;\n case \"e\":\n case \"ee\":\n value = (weekday - (this._root.locale.firstDayOfWeek || 1) + 1).toString();\n if (info.parts[i] == \"ee\") {\n value = $utils.padString(value, 2, \"0\");\n }\n break;\n case \"a\":\n if (hours >= 12) {\n value = this._t(\"PM\");\n } else {\n value = this._t(\"AM\");\n }\n break;\n case \"aa\":\n if (hours >= 12) {\n value = this._t(\"P.M.\");\n } else {\n value = this._t(\"A.M.\");\n }\n break;\n case \"aaa\":\n if (hours >= 12) {\n value = this._t(\"P\");\n } else {\n value = this._t(\"A\");\n }\n break;\n case \"h\":\n value = $utils.get12Hours(hours).toString();\n break;\n case \"hh\":\n value = $utils.padString($utils.get12Hours(hours), 2, \"0\");\n break;\n case \"H\":\n value = hours.toString();\n break;\n case \"HH\":\n value = $utils.padString(hours, 2, \"0\");\n break;\n case \"K\":\n value = $utils.get12Hours(hours, 0).toString();\n break;\n case \"KK\":\n value = $utils.padString($utils.get12Hours(hours, 0), 2, \"0\");\n break;\n case \"k\":\n value = (hours + 1).toString();\n break;\n case \"kk\":\n value = $utils.padString(hours + 1, 2, \"0\");\n break;\n case \"m\":\n value = minutes.toString();\n break;\n case \"mm\":\n value = $utils.padString(minutes, 2, \"0\");\n break;\n case \"s\":\n value = seconds.toString();\n break;\n case \"ss\":\n value = $utils.padString(seconds, 2, \"0\");\n break;\n case \"S\":\n case \"SS\":\n case \"SSS\":\n value = Math.round(milliseconds / 1000 * Math.pow(10, info.parts[i].length)).toString();\n break;\n case \"x\":\n value = timestamp.toString();\n break;\n case \"n\":\n case \"nn\":\n case \"nnn\":\n value = $utils.padString(milliseconds, info.parts[i].length, \"0\");\n break;\n case \"z\":\n value = $utils.getTimeZone(date, false, false, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined).replace(/[+-]+[0-9]+$/, \"\");\n break;\n case \"zz\":\n value = $utils.getTimeZone(date, true, false, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined);\n break;\n case \"zzz\":\n value = $utils.getTimeZone(date, false, true, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined).replace(/[+-]+[0-9]+$/, \"\");\n break;\n case \"zzzz\":\n value = $utils.getTimeZone(date, true, true, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined);\n break;\n case \"Z\":\n case \"ZZ\":\n let timezone = this._root.utc ? \"UTC\" : this._root.timezone;\n if (timezone instanceof Timezone) {\n timezone = timezone.name;\n }\n const offset = timezone ? $utils.getTimezoneOffset(timezone) : date.getTimezoneOffset();\n let tz = Math.abs(offset) / 60;\n let tzh = Math.floor(tz);\n let tzm = tz * 60 - tzh * 60;\n if (this._root.utc) {\n tzh = 0;\n tzm = 0;\n }\n if (info.parts[i] == \"Z\") {\n value = \"GMT\";\n value += offset > 0 ? \"-\" : \"+\";\n value += $utils.padString(tzh, 2) + \":\" + $utils.padString(tzm, 2);\n } else {\n value = offset > 0 ? \"-\" : \"+\";\n value += $utils.padString(tzh, 2) + $utils.padString(tzm, 2);\n }\n break;\n case \"i\":\n value = date.toISOString();\n break;\n case \"I\":\n value = date.toUTCString();\n break;\n }\n res = res.replace($type.PLACEHOLDER, value);\n }\n return res;\n }\n /**\r\n * Parses format into structured infromation.\r\n *\r\n * @param format Format template\r\n */\n parseFormat(format) {\n // Check cache\n // TODO: implement caching of the parsed format\n // Init format parse info holder\n let info = {\n \"template\": \"\",\n \"parts\": []\n };\n // Let TextFormatter split into chunks\n let chunks = TextFormatter.chunk(format, true);\n for (let i = 0; i < chunks.length; i++) {\n let chunk = chunks[i];\n if (chunk.type === \"value\") {\n // Just \"Date\"?\n if (chunk.text.match(/^date$/i)) {\n let dateFormat = this.get(\"dateFormat\", \"yyyy-MM-dd\");\n if (!$type.isString(dateFormat)) {\n dateFormat = \"yyyy-MM-dd\";\n }\n chunk.text = dateFormat;\n }\n // Find all possible parts\n let matches = chunk.text.match(/G|yyyy|yyy|yy|y|YYYY|YYY|YY|Y|u|q|MMMMM|MMMM|MMM|MM|M|ww|w|W|dd|d|DDD|DD|D|F|g|EEEEE|EEEE|EEE|EE|E|eeeee|eeee|eee|ee|e|aaa|aa|a|hh|h|HH|H|KK|K|kk|k|mm|m|ss|s|SSS|SS|S|A|zzzz|zzz|zz|z|ZZ|Z|t|x|nnn|nn|n|i|I/g);\n // Found?\n if (matches) {\n // Populate template\n for (let x = 0; x < matches.length; x++) {\n info.parts.push(matches[x]);\n chunk.text = chunk.text.replace(matches[x], $type.PLACEHOLDER);\n }\n }\n }\n // Apply to template\n info.template += chunk.text;\n }\n // Save cache\n // TODO\n return info;\n }\n _months() {\n return [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\n }\n _getMonth(index) {\n return this._months()[index];\n }\n _shortMonths() {\n return [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May(short)\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n }\n _getShortMonth(index) {\n return this._shortMonths()[index];\n }\n _weekdays() {\n return [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\n }\n _getWeekday(index) {\n return this._weekdays()[index];\n }\n _shortWeekdays() {\n return [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n }\n _getShortWeekday(index) {\n return this._shortWeekdays()[index];\n }\n parse(source, format) {\n // Is it already a Date\n if (source instanceof Date) {\n return source;\n }\n // Is it a numeric timestamp\n if ($type.isNumber(source)) {\n return new Date(source);\n }\n // Are we parsing a timestamp?\n if (format == \"x\") {\n return new Date(parseInt(source));\n }\n // No? Let's check if it's string, and try converting to it if nec\n if (!$type.isString(source)) {\n source = source.toString();\n }\n // Init return value\n let res;\n // Init RegEx for parsing\n let reg = \"\";\n // Clean format\n format = $utils.cleanFormat(format);\n // Clip format to length of the source string\n format = format.substr(0, source.length);\n // Parse format\n let info = this.parseFormat(format);\n // Init parsed items holder\n let parsedIndexes = {\n \"year\": -1,\n \"year3\": -1,\n \"year2\": -1,\n \"year1\": -1,\n \"month\": -1,\n \"monthShort\": -1,\n \"monthLong\": -1,\n \"weekdayShort\": -1,\n \"weekdayLong\": -1,\n \"day\": -1,\n \"yearDay\": -1,\n \"week\": -1,\n \"hourBase0\": -1,\n \"hour12Base0\": -1,\n \"hourBase1\": -1,\n \"hour12Base1\": -1,\n \"minute\": -1,\n \"second\": -1,\n \"millisecond\": -1,\n \"millisecondDigits\": -1,\n \"am\": -1,\n \"zone\": -1,\n \"timestamp\": -1,\n \"iso\": -1\n };\n // Init values\n let resValues = {\n \"year\": 1970,\n \"month\": 0,\n \"day\": 1,\n \"hour\": 0,\n \"minute\": 0,\n \"second\": 0,\n \"millisecond\": 0,\n \"timestamp\": null,\n \"offset\": 0,\n \"utc\": this._root.utc\n };\n // Index adjuster\n let indexAdjust = 0;\n let index = 0;\n // Iterate through all of the parts\n for (let i = 0; i < info.parts.length; i++) {\n // Set current match index\n index = i + indexAdjust + 1;\n switch (info.parts[i]) {\n case \"yyyy\":\n case \"YYYY\":\n reg += \"([0-9]{4})\";\n parsedIndexes.year = index;\n break;\n case \"yyy\":\n case \"YYY\":\n reg += \"([0-9]{3})\";\n parsedIndexes.year3 = index;\n break;\n case \"yy\":\n case \"YY\":\n reg += \"([0-9]{2})\";\n parsedIndexes.year2 = index;\n break;\n case \"y\":\n case \"Y\":\n reg += \"([0-9]{1})\";\n parsedIndexes.year1 = index;\n break;\n case \"MMMM\":\n reg += \"(\" + this.getStringList(this._months()).join(\"|\") + \")\";\n parsedIndexes.monthLong = index;\n break;\n case \"MMM\":\n reg += \"(\" + this.getStringList(this._shortMonths()).join(\"|\") + \")\";\n parsedIndexes.monthShort = index;\n break;\n case \"MM\":\n case \"M\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.month = index;\n break;\n case \"ww\":\n case \"w\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.week = index;\n break;\n case \"dd\":\n case \"d\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.day = index;\n break;\n case \"DDD\":\n case \"DD\":\n case \"D\":\n reg += \"([0-9]{3}|[0-9]{2}|[0-9]{1})\";\n parsedIndexes.yearDay = index;\n break;\n case \"dddd\":\n reg += \"(\" + this.getStringList(this._weekdays()).join(\"|\") + \")\";\n parsedIndexes.weekdayLong = index;\n break;\n case \"ddd\":\n reg += \"(\" + this.getStringList(this._shortWeekdays()).join(\"|\") + \")\";\n parsedIndexes.weekdayShort = index;\n break;\n case \"aaa\":\n case \"aa\":\n case \"a\":\n // TODO: fix (escape regex)\n reg += \"(\" + this.getStringList([\"AM\", \"PM\", \"A\\.M\\.\", \"P\\.M\\.\", \"A\", \"P\"]).join(\"|\") + \")\";\n parsedIndexes.am = index;\n break;\n case \"hh\":\n case \"h\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.hour12Base1 = index;\n break;\n case \"HH\":\n case \"H\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.hourBase0 = index;\n break;\n case \"KK\":\n case \"K\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.hour12Base0 = index;\n break;\n case \"kk\":\n case \"k\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.hourBase1 = index;\n break;\n case \"mm\":\n case \"m\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.minute = index;\n break;\n case \"ss\":\n case \"s\":\n reg += \"([0-9]{2}|[0-9]{1})\";\n parsedIndexes.second = index;\n break;\n case \"SSS\":\n case \"SS\":\n case \"S\":\n reg += \"([0-9]{3}|[0-9]{2}|[0-9]{1})\";\n parsedIndexes.millisecond = index;\n parsedIndexes.millisecondDigits = info.parts[i].length;\n break;\n case \"nnn\":\n case \"nn\":\n case \"n\":\n reg += \"([0-9]{3}|[0-9]{2}|[0-9]{1})\";\n parsedIndexes.millisecond = index;\n break;\n case \"x\":\n reg += \"([0-9]{1,})\";\n parsedIndexes.timestamp = index;\n break;\n case \"Z\":\n reg += \"GMT([-+]+[0-9]{2}:[0-9]{2})\";\n parsedIndexes.zone = index;\n break;\n case \"ZZ\":\n reg += \"([\\\\-+]+[0-9]{2}[0-9]{2})\";\n parsedIndexes.zone = index;\n break;\n case \"i\":\n reg += \"([0-9]{4})-?([0-9]{2})-?([0-9]{2})T?([0-9]{2}):?([0-9]{2}):?([0-9]{2})\\\\.?([0-9]{0,3})([zZ]|[+\\\\-][0-9]{2}:?[0-9]{2}|$)\";\n parsedIndexes.iso = index;\n indexAdjust += 7;\n break;\n case \"G\":\n case \"YYYY\":\n case \"YYY\":\n case \"YY\":\n case \"Y\":\n case \"MMMMM\":\n case \"W\":\n case \"EEEEE\":\n case \"EEEE\":\n case \"EEE\":\n case \"EE\":\n case \"E\":\n case \"eeeee\":\n case \"eeee\":\n case \"eee\":\n case \"ee\":\n case \"e\":\n case \"zzzz\":\n case \"zzz\":\n case \"zz\":\n case \"z\":\n case \"t\":\n // Ignore\n indexAdjust--;\n break;\n }\n reg += \"[^0-9]*\";\n }\n // Try matching\n let regex = new RegExp(reg);\n let matches = source.match(regex);\n if (matches) {\n // Populate the date object\n // Full year\n if (parsedIndexes.year > -1) {\n resValues.year = parseInt(matches[parsedIndexes.year]);\n }\n // 3-digit year\n if (parsedIndexes.year3 > -1) {\n let val = parseInt(matches[parsedIndexes.year3]);\n val += 1000;\n resValues.year = val;\n }\n // 2-digit year\n if (parsedIndexes.year2 > -1) {\n let val = parseInt(matches[parsedIndexes.year2]);\n if (val > 50) {\n val += 1000;\n } else {\n val += 2000;\n }\n resValues.year = val;\n }\n // 1-digit year\n if (parsedIndexes.year1 > -1) {\n let val = parseInt(matches[parsedIndexes.year1]);\n val = Math.floor(new Date().getFullYear() / 10) * 10 + val;\n resValues.year = val;\n }\n // Full month\n if (parsedIndexes.monthLong > -1) {\n resValues.month = this.resolveMonth(matches[parsedIndexes.monthLong]);\n }\n // Short month\n if (parsedIndexes.monthShort > -1) {\n resValues.month = this.resolveShortMonth(matches[parsedIndexes.monthShort]);\n }\n // Numeric month\n if (parsedIndexes.month > -1) {\n resValues.month = parseInt(matches[parsedIndexes.month]) - 1;\n }\n // Weekday\n // @todo\n // Week\n if (parsedIndexes.week > -1 && parsedIndexes.day === -1) {\n // We parse weeks ONLY if day is not explicitly set\n // TODO: this needs work\n // (but maybe later - I can hardly imagine anyone passing their dates in weeks)\n resValues.month = 0;\n resValues.day = $utils.getDayFromWeek(parseInt(matches[parsedIndexes.week]), resValues.year, 1, this._root.utc);\n }\n // Day\n if (parsedIndexes.day > -1) {\n resValues.day = parseInt(matches[parsedIndexes.day]);\n }\n // Year day\n if (parsedIndexes.yearDay > -1) {\n resValues.month = 0;\n resValues.day = parseInt(matches[parsedIndexes.yearDay]);\n }\n // 24 Hour (0-23)\n if (parsedIndexes.hourBase0 > -1) {\n resValues.hour = parseInt(matches[parsedIndexes.hourBase0]);\n }\n // 24 Hour (1-24)\n if (parsedIndexes.hourBase1 > -1) {\n resValues.hour = parseInt(matches[parsedIndexes.hourBase1]) - 1;\n }\n // 12 Hour (0-11)\n if (parsedIndexes.hour12Base0 > -1) {\n let val = parseInt(matches[parsedIndexes.hour12Base0]);\n if (val == 11) {\n val = 0;\n }\n if (parsedIndexes.am > -1 && !this.isAm(matches[parsedIndexes.am])) {\n val += 12;\n }\n resValues.hour = val;\n }\n // 12 Hour (1-12)\n if (parsedIndexes.hour12Base1 > -1) {\n let val = parseInt(matches[parsedIndexes.hour12Base1]);\n if (val == 12) {\n val = 0;\n }\n if (parsedIndexes.am > -1 && !this.isAm(matches[parsedIndexes.am])) {\n val += 12;\n }\n resValues.hour = val;\n }\n // Minute\n if (parsedIndexes.minute > -1) {\n resValues.minute = parseInt(matches[parsedIndexes.minute]);\n }\n // Second\n if (parsedIndexes.second > -1) {\n resValues.second = parseInt(matches[parsedIndexes.second]);\n }\n // Millisecond\n if (parsedIndexes.millisecond > -1) {\n let val = parseInt(matches[parsedIndexes.millisecond]);\n if (parsedIndexes.millisecondDigits == 2) {\n val *= 10;\n } else if (parsedIndexes.millisecondDigits == 1) {\n val *= 100;\n }\n resValues.millisecond = val;\n }\n // Timestamp\n if (parsedIndexes.timestamp > -1) {\n resValues.timestamp = parseInt(matches[parsedIndexes.timestamp]);\n const ts = new Date(resValues.timestamp);\n resValues.year = ts.getUTCFullYear();\n resValues.month = ts.getUTCMonth();\n resValues.day = ts.getUTCDate();\n resValues.hour = ts.getUTCHours();\n resValues.minute = ts.getUTCMinutes();\n resValues.second = ts.getUTCSeconds();\n resValues.millisecond = ts.getUTCMilliseconds();\n }\n // Adjust time zone\n if (parsedIndexes.zone > -1) {\n resValues.offset = this.resolveTimezoneOffset(new Date(resValues.year, resValues.month, resValues.day), matches[parsedIndexes.zone]);\n }\n // ISO\n if (parsedIndexes.iso > -1) {\n resValues.year = $type.toNumber(matches[parsedIndexes.iso + 0]);\n resValues.month = $type.toNumber(matches[parsedIndexes.iso + 1]) - 1;\n resValues.day = $type.toNumber(matches[parsedIndexes.iso + 2]);\n resValues.hour = $type.toNumber(matches[parsedIndexes.iso + 3]);\n resValues.minute = $type.toNumber(matches[parsedIndexes.iso + 4]);\n resValues.second = $type.toNumber(matches[parsedIndexes.iso + 5]);\n resValues.millisecond = $type.toNumber(matches[parsedIndexes.iso + 6]);\n if (matches[parsedIndexes.iso + 7] == \"Z\" || matches[parsedIndexes.iso + 7] == \"z\") {\n resValues.utc = true;\n } else if (matches[parsedIndexes.iso + 7] != \"\") {\n resValues.offset = this.resolveTimezoneOffset(new Date(resValues.year, resValues.month, resValues.day), matches[parsedIndexes.iso + 7]);\n }\n }\n // Create Date object\n if (resValues.utc) {\n res = new Date(Date.UTC(resValues.year, resValues.month, resValues.day, resValues.hour, resValues.minute, resValues.second, resValues.millisecond));\n } else {\n res = new Date(resValues.year, resValues.month, resValues.day, resValues.hour, resValues.minute + resValues.offset, resValues.second, resValues.millisecond);\n }\n } else {\n // Didn't match anything\n // Let's try dropping it into Date constructor and hope for the best\n res = new Date(source);\n }\n return res;\n }\n resolveTimezoneOffset(date, zone) {\n let value = zone.match(/([+\\-]?)([0-9]{2}):?([0-9]{2})/);\n if (value) {\n let match = zone.match(/([+\\-]?)([0-9]{2}):?([0-9]{2})/);\n let dir = match[1];\n let hour = match[2];\n let minute = match[3];\n let offset = parseInt(hour) * 60 + parseInt(minute);\n // Adjust offset\n // Making it negative does not seem to make sense, but it's right\n // because of how JavaScript calculates GMT offsets\n if (dir == \"+\") {\n offset *= -1;\n }\n // Check the difference in offset\n let originalOffset = (date || new Date()).getTimezoneOffset();\n let diff = offset - originalOffset;\n return diff;\n }\n return 0;\n }\n /**\r\n * Resolves month name (i.e. \"December\") into a month number (11).\r\n *\r\n * @param value Month name\r\n * @return Month number\r\n */\n resolveMonth(value) {\n // Let's try English first\n let month = this._months().indexOf(value);\n if (month > -1) {\n return month;\n }\n // Try the translation\n if (!this._root.language.isDefault()) {\n month = this._root.language.translateAll(this._months()).indexOf(value);\n if (month > -1) {\n return month;\n }\n }\n return 0;\n }\n /**\r\n * Resolves short month name (i.e. \"Dec\") into a month number.\r\n *\r\n * @param value Short month name\r\n * @return Month number\r\n */\n resolveShortMonth(value) {\n // Let's try English first\n let month = this._shortMonths().indexOf(value);\n if (month > -1) {\n return month;\n }\n // Maybe long month (workaround for May)\n month = this._months().indexOf(value);\n if (month > -1) {\n return month;\n }\n // Try the translation\n if (this._root.language && !this._root.language.isDefault()) {\n month = this._root.language.translateAll(this._shortMonths()).indexOf(value);\n if (month > -1) {\n return month;\n }\n }\n return 0;\n }\n /**\r\n * Checks if passed in string represents AM/PM notation in many of its\r\n * versions.\r\n *\r\n * @param value Source string\r\n * @return Is it AM/PM?\r\n */\n isAm(value) {\n let list = this.getStringList([\"AM\", \"A.M.\", \"A\"]);\n return list.indexOf(value.toUpperCase()) > -1;\n }\n /**\r\n * Translates list of strings.\r\n *\r\n * @param list Source strings\r\n * @return Translated strings\r\n */\n getStringList(list) {\n let res = [];\n for (let i = 0; i < list.length; i++) {\n // translate?\n if (this._root.language) {\n res.push($utils.escapeForRgex(this._t(list[i])));\n } else {\n res.push($utils.escapeForRgex(list[i]));\n }\n }\n return res;\n }\n}\n","import { Entity } from \"./Entity\";\nimport { TextFormatter } from \"./TextFormatter\";\nimport * as $object from \"./Object\";\nimport * as $utils from \"./Utils\";\nimport * as $type from \"./Type\";\n/**\r\n * A class used to format numberic values as time duration.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-durations/} for more info\r\n */\nexport class DurationFormatter extends Entity {\n constructor() {\n super(...arguments);\n /**\r\n * Collection of aliases for units.\r\n */\n Object.defineProperty(this, \"_unitAliases\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {\n \"Y\": \"y\",\n \"D\": \"d\",\n \"H\": \"h\",\n \"K\": \"h\",\n \"k\": \"h\",\n \"n\": \"S\"\n }\n });\n }\n _setDefaults() {\n const dmillisecond = \"_duration_millisecond\";\n const dsecond = \"_duration_second\";\n const dminute = \"_duration_minute\";\n const dhour = \"_duration_hour\";\n const dday = \"_duration_day\";\n const dweek = \"_duration_week\";\n const dmonth = \"_duration_month\";\n const dyear = \"_duration_year\";\n const asecond = \"_second\";\n const aminute = \"_minute\";\n const ahour = \"_hour\";\n const aday = \"_day\";\n const aweek = \"_week\";\n const amonth = \"_week\";\n const ayear = \"_year\";\n // Defaults\n this._setDefault(\"negativeBase\", 0);\n this._setDefault(\"baseUnit\", \"second\");\n this._setDefault(\"durationFormats\", {\n \"millisecond\": {\n \"millisecond\": this._t(dmillisecond),\n \"second\": this._t(dmillisecond + asecond),\n \"minute\": this._t(dmillisecond + aminute),\n \"hour\": this._t(dmillisecond + ahour),\n \"day\": this._t(dmillisecond + aday),\n \"week\": this._t(dmillisecond + aweek),\n \"month\": this._t(dmillisecond + amonth),\n \"year\": this._t(dmillisecond + ayear)\n },\n \"second\": {\n \"second\": this._t(dsecond),\n \"minute\": this._t(dsecond + aminute),\n \"hour\": this._t(dsecond + ahour),\n \"day\": this._t(dsecond + aday),\n \"week\": this._t(dsecond + aweek),\n \"month\": this._t(dsecond + amonth),\n \"year\": this._t(dsecond + ayear)\n },\n \"minute\": {\n \"minute\": this._t(dminute),\n \"hour\": this._t(dminute + ahour),\n \"day\": this._t(dminute + aday),\n \"week\": this._t(dminute + aweek),\n \"month\": this._t(dminute + amonth),\n \"year\": this._t(dminute + ayear)\n },\n \"hour\": {\n \"hour\": this._t(dhour),\n \"day\": this._t(dhour + aday),\n \"week\": this._t(dhour + aweek),\n \"month\": this._t(dhour + amonth),\n \"year\": this._t(dhour + ayear)\n },\n \"day\": {\n \"day\": this._t(dday),\n \"week\": this._t(dday + aweek),\n \"month\": this._t(dday + amonth),\n \"year\": this._t(dday + ayear)\n },\n \"week\": {\n \"week\": this._t(dweek),\n \"month\": this._t(dweek + amonth),\n \"year\": this._t(dweek + ayear)\n },\n \"month\": {\n \"month\": this._t(dmonth),\n \"year\": this._t(dmonth + ayear)\n },\n \"year\": {\n \"year\": this._t(dyear)\n }\n });\n super._setDefaults();\n }\n _beforeChanged() {\n super._beforeChanged();\n }\n /**\r\n * Formats the number as duration.\r\n *\r\n * For example `1000` (base unit seconds) would be converted to `16:40` as in\r\n * 16 minutes and 40 seconds.\r\n *\r\n * @param value Value to format\r\n * @param format Format to apply\r\n * @param base Override base unit\r\n * @return Formatted number\r\n */\n format(value, format, base) {\n // no base unit?\n let baseUnit = base || this.get(\"baseUnit\");\n // no format passed in or empty\n if (typeof format === \"undefined\" || format === \"\") {\n if (this.get(\"durationFormat\") != null) {\n format = this.get(\"durationFormat\");\n } else {\n format = this.getFormat($type.toNumber(value), undefined, baseUnit);\n }\n }\n // Clean format\n format = $utils.cleanFormat(format);\n // get format info (it will also deal with parser caching)\n let info = this.parseFormat(format, baseUnit);\n // cast to number just in case\n // TODO: maybe use better casting\n let source = Number(value);\n // format and replace the number\n let details;\n if (source > this.get(\"negativeBase\")) {\n details = info.positive;\n } else if (source < this.get(\"negativeBase\")) {\n details = info.negative;\n } else {\n details = info.zero;\n }\n // Format\n let formatted = this.applyFormat(source, details);\n // Apply color?\n if (details.color !== \"\") {\n formatted = \"[\" + details.color + \"]\" + formatted + \"[/]\";\n }\n return formatted;\n }\n /**\r\n * Parses supplied format into structured object which can be used to format\r\n * the number.\r\n *\r\n * @param format Format string, i.e. \"#,###.00\"\r\n * @param base Override base unit\r\n * @return Parsed information\r\n */\n parseFormat(format, base) {\n // Check cache\n // TODO\n // let cached = this.getCache(format);\n // if (cached != null) {\n // \treturn cached;\n // }\n // no base unit?\n let baseUnit = base || this.get(\"baseUnit\");\n // Initialize duration parsing info\n let info = {\n \"positive\": {\n \"color\": \"\",\n \"template\": \"\",\n \"parts\": [],\n \"source\": \"\",\n \"baseUnit\": baseUnit,\n \"parsed\": false,\n \"absolute\": false\n },\n \"negative\": {\n \"color\": \"\",\n \"template\": \"\",\n \"parts\": [],\n \"source\": \"\",\n \"baseUnit\": baseUnit,\n \"parsed\": false,\n \"absolute\": false\n },\n \"zero\": {\n \"color\": \"\",\n \"template\": \"\",\n \"parts\": [],\n \"source\": \"\",\n \"baseUnit\": baseUnit,\n \"parsed\": false,\n \"absolute\": false\n }\n };\n // Escape double vertical bars (that mean display one vertical bar)\n format = format.replace(\"||\", $type.PLACEHOLDER2);\n // Split it up and deal with different formats\n let parts = format.split(\"|\");\n info.positive.source = parts[0];\n if (typeof parts[2] === \"undefined\") {\n info.zero = info.positive;\n } else {\n info.zero.source = parts[2];\n }\n if (typeof parts[1] === \"undefined\") {\n info.negative = info.positive;\n } else {\n info.negative.source = parts[1];\n }\n // Parse each\n $object.each(info, (_part, item) => {\n // Already parsed\n if (item.parsed) {\n return;\n }\n // Check cached\n // TODO\n // if (typeof this.getCache(item.source) !== \"undefined\") {\n // \tinfo[part] = this.getCache(item.source);\n // \treturn;\n // }\n // Begin parsing\n let partFormat = item.source;\n // Check for [] directives\n let dirs = [];\n dirs = item.source.match(/^\\[([^\\]]*)\\]/);\n if (dirs && dirs.length && dirs[0] !== \"\") {\n partFormat = item.source.substr(dirs[0].length);\n item.color = dirs[1];\n }\n // Let TextFormatter split into chunks\n let chunks = TextFormatter.chunk(partFormat, true);\n for (let i = 0; i < chunks.length; i++) {\n let chunk = chunks[i];\n // replace back double vertical bar\n chunk.text = chunk.text.replace($type.PLACEHOLDER2, \"|\");\n if (chunk.type === \"value\") {\n // Just \"Duration\"?\n // if (chunk.text.toLowerCase() === \"duration\") {\n // \tchunk.text = durationFormat;\n // }\n // Check for \"a\" (absolute) modifier\n if (chunk.text.match(/[yYMdDwhHKkmsSn]+a/)) {\n item.absolute = true;\n chunk.text = chunk.text.replace(/([yYMdDwhHKkmsSn]+)a/, \"$1\");\n }\n // Find all possible parts\n let matches = chunk.text.match(/y+|Y+|M+|d+|D+|w+|h+|H+|K+|k+|m+|s+|S+|n+/g);\n if (matches) {\n // Populate template\n for (let x = 0; x < matches.length; x++) {\n // Is it an alias?\n if (matches[x] == null) {\n matches[x] = this._unitAliases[matches[x]];\n }\n item.parts.push(matches[x]);\n chunk.text = chunk.text.replace(matches[x], $type.PLACEHOLDER);\n }\n }\n }\n // Apply to template\n item.template += chunk.text;\n }\n // Apply style formatting\n //item.template = TextFormatter.format(item.template, this.outputFormat);\n // Save cache\n // TODO\n //this.setCache(item.source, item);\n // Mark this as parsed\n item.parsed = true;\n });\n // Save cache (the whole thing)\n // TODO\n //this.setCache(format, info);\n return info;\n }\n /**\r\n * Applies parsed format to a numeric value.\r\n *\r\n * @param value Value\r\n * @param details Parsed format as returned by {parseFormat}\r\n * @return Formatted duration\r\n */\n applyFormat(value, details) {\n // Use absolute values\n let negative = !details.absolute && value < this.get(\"negativeBase\");\n value = Math.abs(value);\n // Recalculate to milliseconds\n let tstamp = this.toTimeStamp(value, details.baseUnit);\n // Init return value\n let res = details.template;\n // Iterate through duration parts\n for (let i = 0, len = details.parts.length; i < len; i++) {\n // Gather the part\n let part = details.parts[i];\n let unit = this._toTimeUnit(part.substr(0, 1));\n let digits = part.length;\n // Calculate current unit value\n let ints;\n const unitValue = this._getUnitValue(unit);\n if (i < len - 1) {\n ints = Math.floor(tstamp / unitValue);\n } else {\n ints = Math.round(tstamp / unitValue);\n }\n res = res.replace($type.PLACEHOLDER, $utils.padString(ints, digits, \"0\"));\n // Reduce timestamp\n tstamp -= ints * unitValue;\n }\n // Reapply negative sign\n if (negative) {\n res = \"-\" + res;\n }\n return res;\n }\n /**\r\n * Converts numeric value to timestamp in milliseconds.\r\n *\r\n * @param value A source value\r\n * @param baseUnit Base unit the source value is in: \"q\", \"s\", \"i\", \"h\", \"d\", \"w\", \"m\", \"y\"\r\n * @return Value representation as a timestamp in milliseconds\r\n */\n toTimeStamp(value, baseUnit) {\n return value * this._getUnitValue(baseUnit);\n }\n _toTimeUnit(code) {\n switch (code) {\n case \"S\":\n return \"millisecond\";\n case \"s\":\n return \"second\";\n case \"m\":\n return \"minute\";\n case \"h\":\n return \"hour\";\n case \"d\":\n return \"day\";\n case \"w\":\n return \"week\";\n case \"M\":\n return \"month\";\n case \"y\":\n return \"year\";\n }\n ;\n }\n /**\r\n * Returns appropriate default format for the value.\r\n *\r\n * If `maxValue` is sepcified, it will use that value to determine the time\r\n * unit for the format.\r\n *\r\n * For example if your `baseUnit` is `\"second\"` and you pass in `10`, you\r\n * will get `\"10\"`.\r\n *\r\n * However, you might want it to be formatted in the context of bigger scale,\r\n * say 10 minutes (600 seconds). If you pass in `600` as `maxValue`, all\r\n * values, including small ones will use format with minutes, e.g.:\r\n * `00:10`, `00:50`, `12: 30`, etc.\r\n *\r\n * @param value Value to format\r\n * @param maxValue Maximum value to be used to determine format\r\n * @param baseUnit Base unit of the value\r\n * @return Format\r\n */\n getFormat(value, maxValue, baseUnit) {\n // Is format override set?\n if (this.get(\"durationFormat\") != null) {\n return this.get(\"durationFormat\");\n }\n // Get base unit\n if (!baseUnit) {\n baseUnit = this.get(\"baseUnit\");\n }\n if (maxValue != null && value != maxValue) {\n value = Math.abs(value);\n maxValue = Math.abs(maxValue);\n let maxUnit = this.getValueUnit(Math.max(value, maxValue), baseUnit);\n return this.get(\"durationFormats\")[baseUnit][maxUnit];\n } else {\n let unit = this.getValueUnit(value, baseUnit);\n return this.get(\"durationFormats\")[baseUnit][unit];\n }\n }\n /**\r\n * Returns value's closest denominator time unit, e.g 100 seconds is\r\n * `\"minute\"`, while 59 seconds would still be `second`.\r\n *\r\n * @param value Source duration value\r\n * @param baseUnit Base unit\r\n * @return Denominator\r\n */\n getValueUnit(value, baseUnit) {\n // Get base unit\n if (!baseUnit) {\n baseUnit = this.get(\"baseUnit\");\n }\n // Convert to milliseconds\n let currentUnit;\n let ms = this.getMilliseconds(value, baseUnit);\n $object.eachContinue(this._getUnitValues(), (key, val) => {\n if (key == baseUnit || currentUnit) {\n let num = ms / val;\n if (num <= 1) {\n if (!currentUnit) {\n currentUnit = key;\n }\n return false;\n }\n currentUnit = key;\n }\n return true;\n });\n return currentUnit;\n }\n /**\r\n * Converts value to milliseconds according to `baseUnit`.\r\n *\r\n * @param value Source duration value\r\n * @param baseUnit Base unit\r\n * @return Value in milliseconds\r\n */\n getMilliseconds(value, baseUnit) {\n // Get base unit\n if (!baseUnit) {\n baseUnit = this.get(\"baseUnit\");\n }\n return value * this._getUnitValue(baseUnit);\n }\n _getUnitValue(timeUnit) {\n return this._getUnitValues()[timeUnit];\n }\n _getUnitValues() {\n return {\n \"millisecond\": 1,\n \"second\": 1000,\n \"minute\": 60000,\n \"hour\": 3600000,\n \"day\": 86400000,\n \"week\": 604800000,\n \"month\": 2592000000,\n \"year\": 31536000000\n };\n }\n}\n","/**\r\n * amCharts 5 locale\r\n *\r\n * Locale: en\r\n * Language: International English\r\n * Author: Martynas Majeris\r\n *\r\n * Follow instructions in [on this page](https://www.amcharts.com/docs/v5/tutorials/creating-translations/) to make corrections or add new translations.\r\n *\r\n * ---\r\n * Edit but leave the header section above this line. You can remove any\r\n * subsequent comment sections.\r\n * ---\r\n *\r\n * Use this file as a template to create translations. Leave the key part in\r\n * English intact. Fill the value with a translation.\r\n *\r\n * Empty string means no translation, so default \"International English\"\r\n * will be used.\r\n *\r\n * If you need the translation to literally be an empty string, use `null`\r\n * instead.\r\n *\r\n * IMPORTANT:\r\n * When translating make good effort to keep the translation length\r\n * at least the same chartcount as the English, especially for short prompts.\r\n *\r\n * Having significantly longer prompts may distort the actual charts.\r\n *\r\n * NOTE:\r\n * Some prompts - like months or weekdays - come in two versions: full and\r\n * shortened.\r\n *\r\n * If there's no official shortened version of these in your language, and it\r\n * would not be possible to invent such short versions that don't seem weird\r\n * to native speakers of that language, fill those with the same as full\r\n * version.\r\n *\r\n * PLACEHOLDERS:\r\n * Some prompts have placeholders like \"%1\". Those will be replaced by actual\r\n * values during translation and should be retained in the translated prompts.\r\n *\r\n * Placeholder positions may be changed to better suit structure of the\r\n * sentence.\r\n *\r\n * For example \"From %1 to %2\", when actually used will replace \"%1\" with an\r\n * actual value representing range start, and \"%2\" will be replaced by end\r\n * value.\r\n *\r\n * E.g. in a Scrollbar for Value axis \"From %1 to %2\" will become\r\n * \"From 100 to 200\". You may translate \"From\" and \"to\", as well as re-arrange\r\n * the order of the prompt itself, but make sure the \"%1\" and \"%2\" remain, in\r\n * places where they will make sense.\r\n *\r\n * Save the file as language_LOCALE, i.e. `en_GB.ts`, `fr_FR.ts`, etc.\r\n */\nexport default {\n \"firstDayOfWeek\": 1,\n // Number formatting options.\n // \n // Please check with the local standards which separator is accepted to be\n // used for separating decimals, and which for thousands.\n \"_decimalSeparator\": \".\",\n \"_thousandSeparator\": \",\",\n // Position of the percent sign in numbers\n \"_percentPrefix\": null,\n \"_percentSuffix\": \"%\",\n // Suffixes for numbers\n // When formatting numbers, big or small numers might be reformatted to\n // shorter version, by applying a suffix.\n // \n // For example, 1000000 might become \"1m\".\n // Or 1024 might become \"1KB\" if we're formatting byte numbers.\n // \n // This section defines such suffixes for all such cases.\n \"_big_number_suffix_3\": \"k\",\n \"_big_number_suffix_6\": \"M\",\n \"_big_number_suffix_9\": \"G\",\n \"_big_number_suffix_12\": \"T\",\n \"_big_number_suffix_15\": \"P\",\n \"_big_number_suffix_18\": \"E\",\n \"_big_number_suffix_21\": \"Z\",\n \"_big_number_suffix_24\": \"Y\",\n \"_small_number_suffix_3\": \"m\",\n \"_small_number_suffix_6\": \"μ\",\n \"_small_number_suffix_9\": \"n\",\n \"_small_number_suffix_12\": \"p\",\n \"_small_number_suffix_15\": \"f\",\n \"_small_number_suffix_18\": \"a\",\n \"_small_number_suffix_21\": \"z\",\n \"_small_number_suffix_24\": \"y\",\n \"_byte_suffix_B\": \"B\",\n \"_byte_suffix_KB\": \"KB\",\n \"_byte_suffix_MB\": \"MB\",\n \"_byte_suffix_GB\": \"GB\",\n \"_byte_suffix_TB\": \"TB\",\n \"_byte_suffix_PB\": \"PB\",\n // Default date formats for various periods.\n // \n // This should reflect official or de facto formatting universally accepted\n // in the country translation is being made for\n // Available format codes here:\n // https://www.amcharts.com/docs/v5/concepts/formatters/formatting-dates/#Format_codes\n // \n // This will be used when formatting date/time for particular granularity,\n // e.g. \"_date_hour\" will be shown whenever we need to show time as hours.\n // \n // \"date\" is used as in default date format when showing standalone dates.\n \"_date\": \"yyyy-MM-dd\",\n \"_date_millisecond\": \"mm:ss SSS\",\n \"_date_millisecond_full\": \"HH:mm:ss SSS\",\n \"_date_second\": \"HH:mm:ss\",\n \"_date_second_full\": \"HH:mm:ss\",\n \"_date_minute\": \"HH:mm\",\n \"_date_minute_full\": \"HH:mm - MMM dd, yyyy\",\n \"_date_hour\": \"HH:mm\",\n \"_date_hour_full\": \"HH:mm - MMM dd, yyyy\",\n \"_date_day\": \"MMM dd\",\n \"_date_day_full\": \"MMM dd, yyyy\",\n \"_date_week\": \"ww\",\n \"_date_week_full\": \"MMM dd, yyyy\",\n \"_date_month\": \"MMM\",\n \"_date_month_full\": \"MMM, yyyy\",\n \"_date_year\": \"yyyy\",\n // Default duration formats for various base units.\n // \n // This will be used by DurationFormatter to format numeric values into\n // duration.\n // \n // Notice how each duration unit comes in several versions. This is to ensure\n // that each base unit is shown correctly.\n // \n // For example, if we have baseUnit set to \"second\", meaning our duration is\n // in seconds.\n // \n // If we pass in `50` to formatter, it will know that we have just 50 seconds\n // (less than a minute) so it will use format in `\"_duration_second\"` (\"ss\"),\n // and the formatted result will be in like `\"50\"`.\n // \n // If we pass in `70`, which is more than a minute, the formatter will switch\n // to `\"_duration_second_minute\"` (\"mm:ss\"), resulting in \"01:10\" formatted\n // text.\n // \n // Available codes here:\n // https://www.amcharts.com/docs/v4/concepts/formatters/formatting-duration/#Available_Codes\n \"_duration_millisecond\": \"SSS\",\n \"_duration_millisecond_second\": \"ss.SSS\",\n \"_duration_millisecond_minute\": \"mm:ss SSS\",\n \"_duration_millisecond_hour\": \"hh:mm:ss SSS\",\n \"_duration_millisecond_day\": \"d'd' mm:ss SSS\",\n \"_duration_millisecond_week\": \"d'd' mm:ss SSS\",\n \"_duration_millisecond_month\": \"M'm' dd'd' mm:ss SSS\",\n \"_duration_millisecond_year\": \"y'y' MM'm' dd'd' mm:ss SSS\",\n \"_duration_second\": \"ss\",\n \"_duration_second_minute\": \"mm:ss\",\n \"_duration_second_hour\": \"hh:mm:ss\",\n \"_duration_second_day\": \"d'd' hh:mm:ss\",\n \"_duration_second_week\": \"d'd' hh:mm:ss\",\n \"_duration_second_month\": \"M'm' dd'd' hh:mm:ss\",\n \"_duration_second_year\": \"y'y' MM'm' dd'd' hh:mm:ss\",\n \"_duration_minute\": \"mm\",\n \"_duration_minute_hour\": \"hh:mm\",\n \"_duration_minute_day\": \"d'd' hh:mm\",\n \"_duration_minute_week\": \"d'd' hh:mm\",\n \"_duration_minute_month\": \"M'm' dd'd' hh:mm\",\n \"_duration_minute_year\": \"y'y' MM'm' dd'd' hh:mm\",\n \"_duration_hour\": \"hh'h'\",\n \"_duration_hour_day\": \"d'd' hh'h'\",\n \"_duration_hour_week\": \"d'd' hh'h'\",\n \"_duration_hour_month\": \"M'm' dd'd' hh'h'\",\n \"_duration_hour_year\": \"y'y' MM'm' dd'd' hh'h'\",\n \"_duration_day\": \"d'd'\",\n \"_duration_day_week\": \"d'd'\",\n \"_duration_day_month\": \"M'm' dd'd'\",\n \"_duration_day_year\": \"y'y' MM'm' dd'd'\",\n \"_duration_week\": \"w'w'\",\n \"_duration_week_month\": \"w'w'\",\n \"_duration_week_year\": \"w'w'\",\n \"_duration_month\": \"M'm'\",\n \"_duration_month_year\": \"y'y' MM'm'\",\n \"_duration_year\": \"y'y'\",\n // Era translations\n \"_era_ad\": \"AD\",\n \"_era_bc\": \"BC\",\n // Day part, used in 12-hour formats, e.g. 5 P.M.\n // Please note that these come in 3 variants:\n // * one letter (e.g. \"A\")\n // * two letters (e.g. \"AM\")\n // * two letters with dots (e.g. \"A.M.\")\n // \n // All three need to to be translated even if they are all the same. Some\n // users might use one, some the other.\n \"A\": \"\",\n \"P\": \"\",\n \"AM\": \"\",\n \"PM\": \"\",\n \"A.M.\": \"\",\n \"P.M.\": \"\",\n // Date-related stuff.\n // \n // When translating months, if there's a difference, use the form which is\n // best for a full date, e.g. as you would use it in \"2018 January 1\".\n // \n // Note that May is listed twice. This is because in English May is the same\n // in both long and short forms, while in other languages it may not be the\n // case. Translate \"May\" to full word, while \"May(short)\" to shortened\n // version.\n // \n // Should month names and weekdays be capitalized or not?\n // \n // Rule of thumb is this: if the names should always be capitalized,\n // regardless of name position within date (\"January\", \"21st January 2018\",\n // etc.) use capitalized names. Otherwise enter all lowercase.\n // \n // The date formatter will automatically capitalize names if they are the\n // first (or only) word in resulting date.\n \"January\": \"\",\n \"February\": \"\",\n \"March\": \"\",\n \"April\": \"\",\n \"May\": \"\",\n \"June\": \"\",\n \"July\": \"\",\n \"August\": \"\",\n \"September\": \"\",\n \"October\": \"\",\n \"November\": \"\",\n \"December\": \"\",\n \"Jan\": \"\",\n \"Feb\": \"\",\n \"Mar\": \"\",\n \"Apr\": \"\",\n \"May(short)\": \"May\",\n \"Jun\": \"\",\n \"Jul\": \"\",\n \"Aug\": \"\",\n \"Sep\": \"\",\n \"Oct\": \"\",\n \"Nov\": \"\",\n \"Dec\": \"\",\n // Weekdays.\n \"Sunday\": \"\",\n \"Monday\": \"\",\n \"Tuesday\": \"\",\n \"Wednesday\": \"\",\n \"Thursday\": \"\",\n \"Friday\": \"\",\n \"Saturday\": \"\",\n \"Sun\": \"\",\n \"Mon\": \"\",\n \"Tue\": \"\",\n \"Wed\": \"\",\n \"Thu\": \"\",\n \"Fri\": \"\",\n \"Sat\": \"\",\n // Date ordinal function.\n // \n // This is used when adding number ordinal when formatting days in dates.\n // \n // E.g. \"January 1st\", \"February 2nd\".\n // \n // The function accepts day number, and returns a string to be added to the\n // day, like in default English translation, if we pass in 2, we will receive\n // \"nd\" back.\n \"_dateOrd\": function (day) {\n let res = \"th\";\n if (day < 11 || day > 13) {\n switch (day % 10) {\n case 1:\n res = \"st\";\n break;\n case 2:\n res = \"nd\";\n break;\n case 3:\n res = \"rd\";\n break;\n }\n }\n return res;\n },\n // Various chart controls.\n // Shown as a tooltip on zoom out button.\n \"Zoom Out\": \"\",\n // Timeline buttons\n \"Play\": \"\",\n \"Stop\": \"\",\n // Chart's Legend screen reader title.\n \"Legend\": \"\",\n // Legend's item screen reader indicator.\n \"Press ENTER to toggle\": \"\",\n // Shown when the chart is busy loading something.\n \"Loading\": \"\",\n // Shown as the first button in the breadcrumb navigation, e.g.:\n // Home > First level > ...\n \"Home\": \"\",\n // Chart types.\n // Those are used as default screen reader titles for the main chart element\n // unless developer has set some more descriptive title.\n \"Chart\": \"\",\n \"Serial chart\": \"\",\n \"X/Y chart\": \"\",\n \"Pie chart\": \"\",\n \"Gauge chart\": \"\",\n \"Radar chart\": \"\",\n \"Sankey diagram\": \"\",\n \"Flow diagram\": \"\",\n \"Chord diagram\": \"\",\n \"TreeMap chart\": \"\",\n \"Force directed tree\": \"\",\n \"Sliced chart\": \"\",\n // Series types.\n // Used to name series by type for screen readers if they do not have their\n // name set.\n \"Series\": \"\",\n \"Candlestick Series\": \"\",\n \"OHLC Series\": \"\",\n \"Column Series\": \"\",\n \"Line Series\": \"\",\n \"Pie Slice Series\": \"\",\n \"Funnel Series\": \"\",\n \"Pyramid Series\": \"\",\n \"X/Y Series\": \"\",\n // Map-related stuff.\n \"Map\": \"\",\n \"Press ENTER to zoom in\": \"\",\n \"Press ENTER to zoom out\": \"\",\n \"Use arrow keys to zoom in and out\": \"\",\n \"Use plus and minus keys on your keyboard to zoom in and out\": \"\",\n // Export-related stuff.\n // These prompts are used in Export menu labels.\n // \n // \"Export\" is the top-level menu item.\n // \n // \"Image\", \"Data\", \"Print\" as second-level indicating type of export\n // operation.\n // \n // Leave actual format untranslated, unless you absolutely know that they\n // would convey more meaning in some other way.\n \"Export\": \"\",\n \"Image\": \"\",\n \"Data\": \"\",\n \"Print\": \"\",\n \"Press ENTER or use arrow keys to navigate\": \"\",\n \"Press ENTER to open\": \"\",\n \"Press ENTER to print.\": \"\",\n \"Press ENTER to export as %1.\": \"\",\n \"(Press ESC to close this message)\": \"\",\n \"Image Export Complete\": \"\",\n \"Export operation took longer than expected. Something might have gone wrong.\": \"\",\n \"Saved from\": \"\",\n \"PNG\": \"\",\n \"JPG\": \"\",\n \"GIF\": \"\",\n \"SVG\": \"\",\n \"PDF\": \"\",\n \"JSON\": \"\",\n \"CSV\": \"\",\n \"XLSX\": \"\",\n \"HTML\": \"\",\n // Scrollbar-related stuff.\n // \n // Scrollbar is a control which can zoom and pan the axes on the chart.\n // \n // Each scrollbar has two grips: left or right (for horizontal scrollbar) or\n // upper and lower (for vertical one).\n // \n // Prompts change in relation to whether Scrollbar is vertical or horizontal.\n // \n // The final section is used to indicate the current range of selection.\n \"Use TAB to select grip buttons or left and right arrows to change selection\": \"\",\n \"Use left and right arrows to move selection\": \"\",\n \"Use left and right arrows to move left selection\": \"\",\n \"Use left and right arrows to move right selection\": \"\",\n \"Use TAB select grip buttons or up and down arrows to change selection\": \"\",\n \"Use up and down arrows to move selection\": \"\",\n \"Use up and down arrows to move lower selection\": \"\",\n \"Use up and down arrows to move upper selection\": \"\",\n \"From %1 to %2\": \"\",\n \"From %1\": \"\",\n \"To %1\": \"\",\n // Data loader-related.\n \"No parser available for file: %1\": \"\",\n \"Error parsing file: %1\": \"\",\n \"Unable to load file: %1\": \"\",\n \"Invalid date\": \"\",\n // Common actions\n \"Close\": \"\",\n \"Minimize\": \"\"\n};\n","import { Entity } from \"./Entity\";\nimport * as $array from \"./Array\";\nimport * as $object from \"./Object\";\nimport en from \"../../../locales/en\";\n;\n/**\r\n * Add localization functionality.\r\n */\nexport class Language extends Entity {\n _setDefaults() {\n this.setPrivate(\"defaultLocale\", en);\n super._setDefaults();\n }\n /**\r\n * Returns a prompt translation.\r\n *\r\n * @param prompt Prompt to translate\r\n * @param locale Target locale\r\n * @param ...rest Parameters\r\n * @return Translation\r\n */\n translate(prompt, locale, ...rest) {\n // Get langauge\n if (!locale) {\n locale = this._root.locale || this.getPrivate(\"defaultLocale\");\n }\n // Init translation\n let translation = prompt;\n let value = locale[prompt];\n // Try to look for the translation\n if (value === null) {\n translation = \"\";\n } else if (value != null) {\n // It might be an empty string\n if (value) {\n translation = value;\n }\n } else if (locale !== this.getPrivate(\"defaultLocale\")) {\n // Try to look in default language\n return this.translate(prompt, this.getPrivate(\"defaultLocale\"), ...rest);\n }\n // Replace %1, %2, etc params\n if (rest.length) {\n for (let len = rest.length, i = 0; i < len; ++i) {\n translation = translation.split(\"%\" + (i + 1)).join(rest[i]);\n }\n }\n // Return the translation\n return translation;\n }\n /**\r\n * Returns a prompt translation, including custom prompts.\r\n *\r\n * @param prompt Prompt to translate\r\n * @param locale Target locale\r\n * @param ...rest Parameters\r\n * @return Translation\r\n */\n translateAny(prompt, locale, ...rest) {\n return this.translate(prompt, locale, ...rest);\n }\n /**\r\n * Add a custom prompt to locale.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/locales/creating-translations/#Extending_locale_with_custom_prompts}\r\n * @param prompt Source prompt\r\n * @param translation Tanslation\r\n * @param locale Target locale\r\n */\n setTranslationAny(prompt, translation, locale) {\n const localeTarget = locale || this._root.locale;\n localeTarget[prompt] = translation;\n }\n /**\r\n * Add a batch of custom prompts.\r\n *\r\n * @since 5.3.3\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/locales/creating-translations/#Extending_locale_with_custom_prompts}\r\n * @param translations Translations\r\n * @param locale Target locale\r\n */\n setTranslationsAny(translations, locale) {\n $object.each(translations, (key, val) => {\n this.setTranslationAny(key, val, locale);\n });\n }\n translateEmpty(prompt, locale, ...rest) {\n let translation = this.translate(prompt, locale, ...rest);\n return translation == prompt ? \"\" : translation;\n }\n translateFunc(prompt, locale) {\n if (this._root.locale[prompt]) {\n return this._root.locale[prompt];\n }\n // Try to look in default language\n if (locale !== this.getPrivate(\"defaultLocale\")) {\n return this.translateFunc(prompt, this.getPrivate(\"defaultLocale\"));\n }\n // Fail - return empty function\n return () => {\n return \"\";\n };\n }\n /**\r\n * Translates a btach of prompts.\r\n *\r\n * @param list Array of prompts to translate\r\n * @param locale Target locale\r\n * @return Array of translations\r\n */\n translateAll(list, locale) {\n // Translate all items in the list\n if (!this.isDefault()) {\n return $array.map(list, x => this.translate(x, locale));\n } else {\n return list;\n }\n }\n /**\r\n * Returns `true` if the currently selected locale is a default locale.\r\n *\r\n * @return `true` if locale is default; `false` if it is not.\r\n */\n isDefault() {\n return this.getPrivate(\"defaultLocale\") === this._root.locale;\n }\n}\n","import { Template } from \"./util/Template\";\nimport * as $order from \"./util/Order\";\nimport * as $array from \"./util/Array\";\n/**\r\n * A base class for an amCharts theme.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/themes/} for more info\r\n * @important\r\n */\nexport class Theme {\n constructor(root, isReal) {\n Object.defineProperty(this, \"_root\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_rules\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n this._root = root;\n if (!isReal) {\n throw new Error(\"You cannot use `new Class()`, instead use `Class.new()`\");\n }\n }\n /**\r\n * Use this method to create an instance of this class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#New_element_syntax} for more info\r\n * @param root Root element\r\n * @param settings Settings\r\n * @param template Template\r\n * @return Instantiated object\r\n */\n static new(root) {\n const x = new this(root, true);\n x.setupDefaultRules();\n return x;\n }\n setupDefaultRules() {}\n /**\r\n * Looks up the rules for a specific theme class.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/themes/} for more info\r\n * @param themeClass Theme class\r\n * @return Array>\r\n */\n _lookupRules(themeClass) {\n return this._rules[themeClass];\n }\n /**\r\n * Creates a [[Template]] for specific theme class and tags.\r\n *\r\n * NOTE: the difference from `rule()` is that `ruleRaw()` does not do any\r\n * type checks.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/themes/} for more info\r\n * @param themeClass Theme class\r\n * @param themeTags Theme tags\r\n * @return Template\r\n */\n ruleRaw(themeClass, themeTags = []) {\n let rules = this._rules[themeClass];\n if (!rules) {\n rules = this._rules[themeClass] = [];\n }\n themeTags.sort($order.compare);\n const {\n index,\n found\n } = $array.getSortedIndex(rules, x => {\n const order = $order.compare(x.tags.length, themeTags.length);\n if (order === 0) {\n return $order.compareArray(x.tags, themeTags, $order.compare);\n } else {\n return order;\n }\n });\n if (found) {\n return rules[index].template;\n } else {\n const template = Template.new({});\n rules.splice(index, 0, {\n tags: themeTags,\n template\n });\n return template;\n }\n }\n /**\r\n * Creates a [[Template]] for specific theme class and tags.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/themes/} for more info\r\n * @param themeClass Theme class\r\n * @param themeTags Theme tags\r\n * @return Template\r\n */\n rule(themeClass, themeTags = []) {\n return this.ruleRaw(themeClass, themeTags);\n }\n}\n","import { Theme } from \"../core/Theme\";\nimport { p100, p50 } from \"../core/util/Percent\";\nimport { Color } from \"../core/util/Color\";\nimport { GridLayout } from \"../core/render/GridLayout\";\nimport * as $ease from \"../core/util/Ease\";\n/**\r\n * @ignore\r\n */\nexport function setColor(rule, key, ic, name) {\n // TODO this shouldn't use get, figure out a better way\n rule.set(key, ic.get(name));\n ic.on(name, value => {\n rule.set(key, value);\n });\n}\n/**\r\n * @ignore\r\n */\nexport class DefaultTheme extends Theme {\n setupDefaultRules() {\n super.setupDefaultRules();\n const language = this._root.language;\n const ic = this._root.interfaceColors;\n const horizontalLayout = this._root.horizontalLayout;\n const verticalLayout = this._root.verticalLayout;\n const r = this.rule.bind(this);\n /**\r\n * ========================================================================\r\n * core\r\n * ========================================================================\r\n */\n r(\"InterfaceColors\").setAll({\n stroke: Color.fromHex(0xe5e5e5),\n fill: Color.fromHex(0xf3f3f3),\n primaryButton: Color.fromHex(0x6794dc),\n primaryButtonHover: Color.fromHex(0x6771dc),\n primaryButtonDown: Color.fromHex(0x68dc76),\n primaryButtonActive: Color.fromHex(0x68dc76),\n primaryButtonDisabled: Color.fromHex(0xdadada),\n primaryButtonTextDisabled: Color.fromHex(0xffffff),\n primaryButtonText: Color.fromHex(0xffffff),\n primaryButtonStroke: Color.fromHex(0xffffff),\n secondaryButton: Color.fromHex(0xd9d9d9),\n secondaryButtonHover: Color.fromHex(0xa3a3a3),\n secondaryButtonDown: Color.fromHex(0x8d8d8d),\n secondaryButtonActive: Color.fromHex(0xe6e6e6),\n secondaryButtonText: Color.fromHex(0x000000),\n secondaryButtonStroke: Color.fromHex(0xffffff),\n grid: Color.fromHex(0x000000),\n background: Color.fromHex(0xffffff),\n alternativeBackground: Color.fromHex(0x000000),\n text: Color.fromHex(0x000000),\n alternativeText: Color.fromHex(0xffffff),\n disabled: Color.fromHex(0xadadad),\n positive: Color.fromHex(0x50b300),\n negative: Color.fromHex(0xb30000)\n });\n {\n const rule = r(\"ColorSet\");\n rule.setAll({\n passOptions: {\n hue: 0.05,\n saturation: 0,\n lightness: 0\n },\n colors: [Color.fromHex(0x67b7dc)],\n step: 1,\n //baseColor: Color.fromRGB(103, 183, 220),\n //count: 20,\n reuse: false,\n startIndex: 0\n });\n rule.setPrivate(\"currentStep\", 0);\n rule.setPrivate(\"currentPass\", 0);\n }\n r(\"Entity\").setAll({\n stateAnimationDuration: 0,\n stateAnimationEasing: $ease.out($ease.cubic)\n });\n r(\"Component\").setAll({\n interpolationDuration: 0,\n interpolationEasing: $ease.out($ease.cubic)\n });\n r(\"Sprite\").setAll({\n visible: true,\n scale: 1,\n opacity: 1,\n rotation: 0,\n position: \"relative\",\n tooltipX: p50,\n tooltipY: p50,\n tooltipPosition: \"fixed\",\n isMeasured: true\n });\n r(\"Sprite\").states.create(\"default\", {\n \"visible\": true,\n opacity: 1\n });\n r(\"Container\").setAll({\n interactiveChildren: true,\n setStateOnChildren: false\n });\n r(\"Graphics\").setAll({\n strokeWidth: 1\n });\n r(\"Chart\").setAll({\n width: p100,\n height: p100,\n interactiveChildren: false\n });\n r(\"ZoomableContainer\").setAll({\n width: p100,\n height: p100,\n wheelable: true,\n pinchZoom: true,\n maxZoomLevel: 32,\n minZoomLevel: 1,\n zoomStep: 2,\n animationEasing: $ease.out($ease.cubic),\n animationDuration: 600,\n maxPanOut: 0.4\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: alignment\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Sprite\", [\"horizontal\", \"center\"]).setAll({\n centerX: p50,\n x: p50\n });\n r(\"Sprite\", [\"vertical\", \"center\"]).setAll({\n centerY: p50,\n y: p50\n });\n r(\"Container\", [\"horizontal\", \"layout\"]).setAll({\n layout: horizontalLayout\n });\n r(\"Container\", [\"vertical\", \"layout\"]).setAll({\n layout: verticalLayout\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: patterns\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Pattern\").setAll({\n repetition: \"repeat\",\n width: 50,\n height: 50,\n rotation: 0,\n fillOpacity: 1\n });\n r(\"LinePattern\").setAll({\n gap: 6,\n colorOpacity: 1,\n width: 49,\n height: 49\n });\n r(\"RectanglePattern\").setAll({\n gap: 6,\n checkered: false,\n centered: true,\n maxWidth: 5,\n maxHeight: 5,\n width: 48,\n height: 48,\n strokeWidth: 0\n });\n r(\"CirclePattern\").setAll({\n gap: 5,\n checkered: false,\n centered: false,\n radius: 3,\n strokeWidth: 0,\n width: 45,\n height: 45\n });\n r(\"GrainPattern\").setAll({\n width: 200,\n height: 200,\n colors: [Color.fromHex(0x000000)],\n size: 1,\n horizontalGap: 0,\n verticalGap: 0,\n density: 1,\n minOpacity: 0,\n maxOpacity: 0.2\n });\n {\n const rule = r(\"PatternSet\");\n rule.setAll({\n step: 1\n });\n setColor(rule, \"color\", ic, \"stroke\");\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * core: gradients\r\n * ------------------------------------------------------------------------\r\n */\n r(\"LinearGradient\").setAll({\n rotation: 90\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Legend\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Legend\").setAll({\n fillField: \"fill\",\n strokeField: \"stroke\",\n nameField: \"name\",\n layout: GridLayout.new(this._root, {}),\n layer: 30,\n clickTarget: \"itemContainer\"\n });\n // Class: Container\n r(\"Container\", [\"legend\", \"item\", \"itemcontainer\"]).setAll({\n paddingLeft: 5,\n paddingRight: 5,\n paddingBottom: 5,\n paddingTop: 5,\n layout: horizontalLayout,\n setStateOnChildren: true,\n interactiveChildren: false,\n ariaChecked: true,\n focusable: true,\n ariaLabel: language.translate(\"Press ENTER to toggle\"),\n role: \"checkbox\"\n });\n {\n const rule = r(\"Rectangle\", [\"legend\", \"item\", \"background\"]);\n rule.setAll({\n fillOpacity: 0\n });\n setColor(rule, \"fill\", ic, \"background\");\n }\n r(\"Container\", [\"legend\", \"marker\"]).setAll({\n setStateOnChildren: true,\n centerY: p50,\n paddingLeft: 0,\n paddingRight: 0,\n paddingBottom: 0,\n paddingTop: 0,\n width: 18,\n height: 18\n });\n r(\"RoundedRectangle\", [\"legend\", \"marker\", \"rectangle\"]).setAll({\n width: p100,\n height: p100,\n cornerRadiusBL: 3,\n cornerRadiusTL: 3,\n cornerRadiusBR: 3,\n cornerRadiusTR: 3\n });\n {\n const rule = r(\"RoundedRectangle\", [\"legend\", \"marker\", \"rectangle\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"disabled\");\n setColor(rule, \"stroke\", ic, \"disabled\");\n }\n r(\"Label\", [\"legend\", \"label\"]).setAll({\n centerY: p50,\n marginLeft: 5,\n paddingRight: 0,\n paddingLeft: 0,\n paddingTop: 0,\n paddingBottom: 0,\n populateText: true\n });\n {\n const rule = r(\"Label\", [\"legend\", \"label\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"disabled\");\n }\n r(\"Label\", [\"legend\", \"value\", \"label\"]).setAll({\n centerY: p50,\n marginLeft: 5,\n paddingRight: 0,\n paddingLeft: 0,\n paddingTop: 0,\n paddingBottom: 0,\n width: 50,\n centerX: p100,\n populateText: true\n });\n {\n const rule = r(\"Label\", [\"legend\", \"value\", \"label\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"disabled\");\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * core: HeatLegend\r\n * ------------------------------------------------------------------------\r\n */\n r(\"HeatLegend\").setAll({\n stepCount: 1\n });\n r(\"RoundedRectangle\", [\"heatlegend\", \"marker\"]).setAll({\n cornerRadiusTR: 0,\n cornerRadiusBR: 0,\n cornerRadiusTL: 0,\n cornerRadiusBL: 0\n });\n r(\"RoundedRectangle\", [\"vertical\", \"heatlegend\", \"marker\"]).setAll({\n height: p100,\n width: 15\n });\n r(\"RoundedRectangle\", [\"horizontal\", \"heatlegend\", \"marker\"]).setAll({\n width: p100,\n height: 15\n });\n r(\"HeatLegend\", [\"vertical\"]).setAll({\n height: p100\n });\n r(\"HeatLegend\", [\"horizontal\"]).setAll({\n width: p100\n });\n r(\"Label\", [\"heatlegend\", \"start\"]).setAll({\n paddingLeft: 5,\n paddingRight: 5,\n paddingTop: 5,\n paddingBottom: 5\n });\n r(\"Label\", [\"heatlegend\", \"end\"]).setAll({\n paddingLeft: 5,\n paddingRight: 5,\n paddingTop: 5,\n paddingBottom: 5\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Labels\r\n * ------------------------------------------------------------------------\r\n */\n {\n const rule = r(\"Label\");\n rule.setAll({\n paddingTop: 8,\n paddingBottom: 8,\n paddingLeft: 10,\n paddingRight: 10,\n fontFamily: \"-apple-system, BlinkMacSystemFont, \\\"Segoe UI\\\", Roboto, Helvetica, Arial, sans-serif, \\\"Apple Color Emoji\\\", \\\"Segoe UI Emoji\\\", \\\"Segoe UI Symbol\\\"\",\n fontSize: \"1em\",\n populateText: false\n });\n setColor(rule, \"fill\", ic, \"text\");\n }\n r(\"RadialLabel\").setAll({\n textType: \"regular\",\n centerY: p50,\n centerX: p50,\n inside: false,\n radius: 0,\n baseRadius: p100,\n orientation: \"auto\",\n textAlign: \"center\"\n });\n r(\"EditableLabel\").setAll({\n editOn: \"click\",\n //setStateOnChildren: true,\n themeTags: [\"editablelabel\"],\n multiLine: true\n });\n r(\"RoundedRectangle\", [\"editablelabel\", \"background\"]).setAll({\n fillOpacity: 0,\n fill: Color.fromHex(0x000000),\n cornerRadiusBL: 3,\n cornerRadiusBR: 3,\n cornerRadiusTL: 3,\n cornerRadiusTR: 3,\n strokeOpacity: 0,\n stroke: Color.fromHex(0x000000)\n });\n {\n r(\"RoundedRectangle\", [\"editablelabel\", \"background\"]).states.create(\"active\", {\n strokeOpacity: 0.2\n });\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Elements and shapes\r\n * ------------------------------------------------------------------------\r\n */\n r(\"RoundedRectangle\").setAll({\n cornerRadiusTL: 8,\n cornerRadiusBL: 8,\n cornerRadiusTR: 8,\n cornerRadiusBR: 8\n });\n r(\"PointedRectangle\").setAll({\n pointerBaseWidth: 15,\n pointerLength: 10,\n cornerRadius: 8\n });\n r(\"Slice\").setAll({\n shiftRadius: 0,\n dRadius: 0,\n dInnerRadius: 0\n });\n {\n const rule = r(\"Tick\");\n rule.setAll({\n strokeOpacity: .15,\n isMeasured: false,\n length: 4.5,\n position: \"absolute\",\n crisp: true\n });\n setColor(rule, \"stroke\", ic, \"grid\");\n }\n r(\"Bullet\").setAll({\n locationX: 0.5,\n locationY: 0.5\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Tooltip\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Tooltip\").setAll({\n position: \"absolute\",\n getFillFromSprite: true,\n getStrokeFromSprite: false,\n autoTextColor: true,\n paddingTop: 9,\n paddingBottom: 8,\n paddingLeft: 10,\n paddingRight: 10,\n marginBottom: 5,\n pointerOrientation: \"vertical\",\n centerX: p50,\n centerY: p50,\n animationEasing: $ease.out($ease.cubic),\n exportable: false\n //layer: 100\n });\n r(\"Polygon\").setAll({\n animationEasing: $ease.out($ease.cubic)\n });\n {\n const rule = r(\"PointedRectangle\", [\"tooltip\", \"background\"]);\n rule.setAll({\n strokeOpacity: 0.9,\n cornerRadius: 4,\n pointerLength: 4,\n pointerBaseWidth: 8,\n fillOpacity: 0.9,\n stroke: Color.fromHex(0xffffff)\n });\n }\n {\n const rule = r(\"Label\", [\"tooltip\"]);\n rule.setAll({\n role: \"tooltip\",\n populateText: true,\n paddingRight: 0,\n paddingTop: 0,\n paddingLeft: 0,\n paddingBottom: 0\n });\n setColor(rule, \"fill\", ic, \"alternativeText\");\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Button\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Button\").setAll({\n paddingTop: 8,\n paddingBottom: 8,\n paddingLeft: 10,\n paddingRight: 10,\n interactive: true,\n layout: horizontalLayout,\n interactiveChildren: false,\n setStateOnChildren: true,\n focusable: true\n });\n r(\"Button\").states.create(\"hover\", {});\n r(\"Button\").states.create(\"down\", {\n stateAnimationDuration: 0\n });\n r(\"Button\").states.create(\"active\", {});\n r(\"Button\").states.create(\"disabled\", {\n forceInactive: true\n });\n {\n const rule = r(\"RoundedRectangle\", [\"button\", \"background\"]);\n setColor(rule, \"fill\", ic, \"primaryButton\");\n setColor(rule, \"stroke\", ic, \"primaryButtonStroke\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"button\", \"background\"]).states.create(\"hover\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonHover\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"button\", \"background\"]).states.create(\"down\", {\n stateAnimationDuration: 0\n });\n setColor(rule, \"fill\", ic, \"primaryButtonDown\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"button\", \"background\"]).states.create(\"active\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonActive\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"button\", \"background\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonDisabled\");\n }\n {\n const rule = r(\"Graphics\", [\"button\", \"icon\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonTextDisabled\");\n }\n {\n const rule = r(\"Label\", [\"button\"]).states.create(\"disabled\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonTextDisabled\");\n }\n {\n const rule = r(\"Graphics\", [\"button\", \"icon\"]);\n rule.setAll({\n forceInactive: true\n });\n setColor(rule, \"stroke\", ic, \"primaryButtonText\");\n }\n {\n const rule = r(\"Label\", [\"button\"]);\n setColor(rule, \"fill\", ic, \"primaryButtonText\");\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * charts/xy: ZoomOutButton\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Button\", [\"zoom\"]).setAll({\n paddingTop: 18,\n paddingBottom: 18,\n paddingLeft: 12,\n paddingRight: 12,\n centerX: 46,\n centerY: -10,\n y: 0,\n x: p100,\n role: \"button\",\n ariaLabel: language.translate(\"Zoom Out\"),\n layer: 30\n });\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"button\", \"zoom\"]);\n rule.setAll({\n cornerRadiusBL: 40,\n cornerRadiusBR: 40,\n cornerRadiusTL: 40,\n cornerRadiusTR: 40\n });\n setColor(rule, \"fill\", ic, \"primaryButton\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"button\", \"zoom\"]).states.create(\"hover\", {});\n setColor(rule, \"fill\", ic, \"primaryButtonHover\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"button\", \"zoom\"]).states.create(\"down\", {\n stateAnimationDuration: 0\n });\n setColor(rule, \"fill\", ic, \"primaryButtonDown\");\n }\n {\n const rule = r(\"Graphics\", [\"icon\", \"button\", \"zoom\"]);\n rule.setAll({\n crisp: true,\n strokeOpacity: 0.7,\n draw: display => {\n display.moveTo(0, 0);\n display.lineTo(12, 0);\n }\n });\n setColor(rule, \"stroke\", ic, \"primaryButtonText\");\n }\n /**\r\n * ------------------------------------------------------------------------\r\n * core: ResizeButton\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Button\", [\"resize\"]).setAll({\n paddingTop: 9,\n paddingBottom: 9,\n paddingLeft: 13,\n paddingRight: 13,\n draggable: true,\n centerX: p50,\n centerY: p50,\n position: \"absolute\",\n role: \"slider\",\n ariaValueMin: \"0\",\n ariaValueMax: \"100\",\n ariaLabel: language.translate(\"Use up and down arrows to move selection\")\n });\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"resize\", \"button\"]);\n rule.setAll({\n cornerRadiusBL: 40,\n cornerRadiusBR: 40,\n cornerRadiusTL: 40,\n cornerRadiusTR: 40\n });\n setColor(rule, \"fill\", ic, \"secondaryButton\");\n setColor(rule, \"stroke\", ic, \"secondaryButtonStroke\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"resize\", \"button\"]).states.create(\"hover\", {});\n setColor(rule, \"fill\", ic, \"secondaryButtonHover\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"background\", \"resize\", \"button\"]).states.create(\"down\", {\n stateAnimationDuration: 0\n });\n setColor(rule, \"fill\", ic, \"secondaryButtonDown\");\n }\n {\n const rule = r(\"Graphics\", [\"resize\", \"button\", \"icon\"]);\n rule.setAll({\n interactive: false,\n crisp: true,\n strokeOpacity: 0.5,\n draw: display => {\n display.moveTo(0, 0.5);\n display.lineTo(0, 12.5);\n display.moveTo(4, 0.5);\n display.lineTo(4, 12.5);\n }\n });\n setColor(rule, \"stroke\", ic, \"secondaryButtonText\");\n }\n r(\"Button\", [\"resize\", \"vertical\"]).setAll({\n rotation: 90,\n cursorOverStyle: \"ns-resize\"\n });\n r(\"Button\", [\"resize\", \"horizontal\"]).setAll({\n cursorOverStyle: \"ew-resize\"\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: PlayButton\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Button\", [\"play\"]).setAll({\n paddingTop: 13,\n paddingBottom: 13,\n paddingLeft: 14,\n paddingRight: 14,\n ariaLabel: language.translate(\"Play\"),\n toggleKey: \"active\"\n });\n {\n const rule = r(\"RoundedRectangle\", [\"play\", \"background\"]);\n rule.setAll({\n strokeOpacity: 0.5,\n cornerRadiusBL: 100,\n cornerRadiusBR: 100,\n cornerRadiusTL: 100,\n cornerRadiusTR: 100\n });\n setColor(rule, \"fill\", ic, \"primaryButton\");\n }\n {\n const rule = r(\"Graphics\", [\"play\", \"icon\"]);\n rule.setAll({\n stateAnimationDuration: 0,\n dx: 1,\n draw: display => {\n display.moveTo(0, -5);\n display.lineTo(8, 0);\n display.lineTo(0, 5);\n display.lineTo(0, -5);\n }\n });\n setColor(rule, \"fill\", ic, \"primaryButtonText\");\n }\n r(\"Graphics\", [\"play\", \"icon\"]).states.create(\"default\", {\n stateAnimationDuration: 0\n });\n r(\"Graphics\", [\"play\", \"icon\"]).states.create(\"active\", {\n stateAnimationDuration: 0,\n draw: display => {\n display.moveTo(-4, -5);\n display.lineTo(-1, -5);\n display.lineTo(-1, 5);\n display.lineTo(-4, 5);\n display.lineTo(-4, -5);\n display.moveTo(4, -5);\n display.lineTo(1, -5);\n display.lineTo(1, 5);\n display.lineTo(4, 5);\n display.lineTo(4, -5);\n }\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: SwitchButton\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Button\", [\"switch\"]).setAll({\n paddingTop: 4,\n paddingBottom: 4,\n paddingLeft: 4,\n paddingRight: 4,\n ariaLabel: language.translate(\"Press ENTER to toggle\"),\n toggleKey: \"active\",\n width: 40,\n height: 24,\n layout: null\n });\n {\n const rule = r(\"RoundedRectangle\", [\"switch\", \"background\"]);\n rule.setAll({\n strokeOpacity: 0.5,\n cornerRadiusBL: 100,\n cornerRadiusBR: 100,\n cornerRadiusTL: 100,\n cornerRadiusTR: 100\n });\n setColor(rule, \"fill\", ic, \"primaryButton\");\n }\n {\n const rule = r(\"Circle\", [\"switch\", \"icon\"]);\n rule.setAll({\n radius: 8,\n centerY: 0,\n centerX: 0,\n dx: 0\n });\n setColor(rule, \"fill\", ic, \"primaryButtonText\");\n }\n r(\"Graphics\", [\"switch\", \"icon\"]).states.create(\"active\", {\n dx: 16\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * core: Scrollbar\r\n * ------------------------------------------------------------------------\r\n */\n r(\"Scrollbar\").setAll({\n start: 0,\n end: 1,\n layer: 30,\n animationEasing: $ease.out($ease.cubic)\n });\n r(\"Scrollbar\", [\"vertical\"]).setAll({\n marginRight: 13,\n marginLeft: 13,\n minWidth: 12,\n height: p100\n });\n r(\"Scrollbar\", [\"horizontal\"]).setAll({\n marginTop: 13,\n marginBottom: 13,\n minHeight: 12,\n width: p100\n });\n this.rule(\"Button\", [\"scrollbar\"]).setAll({\n exportable: false\n });\n {\n const rule = r(\"RoundedRectangle\", [\"scrollbar\", \"main\", \"background\"]);\n rule.setAll({\n cornerRadiusTL: 8,\n cornerRadiusBL: 8,\n cornerRadiusTR: 8,\n cornerRadiusBR: 8,\n fillOpacity: 0.8\n });\n setColor(rule, \"fill\", ic, \"fill\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"scrollbar\", \"thumb\"]);\n rule.setAll({\n role: \"slider\",\n ariaLive: \"polite\",\n position: \"absolute\",\n draggable: true\n });\n setColor(rule, \"fill\", ic, \"secondaryButton\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"scrollbar\", \"thumb\"]).states.create(\"hover\", {});\n setColor(rule, \"fill\", ic, \"secondaryButtonHover\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"scrollbar\", \"thumb\"]).states.create(\"down\", {\n stateAnimationDuration: 0\n });\n setColor(rule, \"fill\", ic, \"secondaryButtonDown\");\n }\n r(\"RoundedRectangle\", [\"scrollbar\", \"thumb\", \"vertical\"]).setAll({\n x: p50,\n width: p100,\n centerX: p50,\n ariaLabel: language.translate(\"Use up and down arrows to move selection\")\n });\n r(\"RoundedRectangle\", [\"scrollbar\", \"thumb\", \"horizontal\"]).setAll({\n y: p50,\n centerY: p50,\n height: p100,\n ariaLabel: language.translate(\"Use left and right arrows to move selection\")\n });\n // @todo: is this needed? used to be \"ContentScrollbar\"\n // r(\"Scrollbar\", [\"content?\"]).setAll({\n // \tmarginRight: 0,\n // \tmarginLeft: 5,\n // \tlayer: 5\n // });\n /**\r\n * ========================================================================\r\n * charts/xy\r\n * ========================================================================\r\n *\r\n * This needs to be in DefaultTheme because it's the only theme that is\r\n * automatically applied to Root, and tooltips different ancestors\r\n * than actual charts using them.\r\n */\n {\n const rule = r(\"PointedRectangle\", [\"axis\", \"tooltip\", \"background\"]);\n rule.setAll({\n cornerRadius: 0\n });\n setColor(rule, \"fill\", ic, \"alternativeBackground\");\n }\n r(\"Label\", [\"axis\", \"tooltip\"]).setAll({\n role: undefined\n });\n r(\"Label\", [\"axis\", \"tooltip\", \"y\"]).setAll({\n textAlign: \"right\"\n });\n r(\"Label\", [\"axis\", \"tooltip\", \"y\", \"opposite\"]).setAll({\n textAlign: \"left\"\n });\n r(\"Label\", [\"axis\", \"tooltip\", \"x\"]).setAll({\n textAlign: \"center\"\n });\n r(\"Tooltip\", [\"categoryaxis\"]).setAll({\n labelText: \"{category}\"\n });\n /**\r\n * ------------------------------------------------------------------------\r\n * Shapes\r\n * ------------------------------------------------------------------------\r\n */\n // Class: Graphics\n r(\"Star\").setAll({\n spikes: 5,\n innerRadius: 5,\n radius: 10\n });\n // STOCK\n r(\"Tooltip\", [\"stock\"]).setAll({\n paddingTop: 6,\n paddingBottom: 5,\n paddingLeft: 7,\n paddingRight: 7\n });\n r(\"PointedRectangle\", [\"tooltip\", \"stock\", \"axis\"]).setAll({\n pointerLength: 0,\n pointerBaseWidth: 0,\n cornerRadius: 3\n });\n r(\"Label\", [\"tooltip\", \"stock\"]).setAll({\n fontSize: \"0.8em\"\n });\n // resizer\n r(\"SpriteResizer\").setAll({\n rotationStep: 10,\n isMeasured: false\n });\n {\n const rule = r(\"Container\", [\"resizer\", \"grip\"]);\n rule.states.create(\"hover\", {});\n }\n {\n const rule = r(\"RoundedRectangle\", [\"resizer\", \"grip\"]);\n rule.setAll({\n strokeOpacity: 0.7,\n strokeWidth: 1,\n fillOpacity: 1,\n width: 12,\n height: 12\n });\n setColor(rule, \"fill\", ic, \"background\");\n setColor(rule, \"stroke\", ic, \"alternativeBackground\");\n }\n {\n const rule = r(\"RoundedRectangle\", [\"resizer\", \"grip\", \"outline\"]);\n rule.setAll({\n strokeOpacity: 0,\n fillOpacity: 0,\n width: 20,\n height: 20\n });\n rule.states.create(\"hover\", {\n fillOpacity: 0.3\n });\n setColor(rule, \"fill\", ic, \"alternativeBackground\");\n }\n r(\"RoundedRectangle\", [\"resizer\", \"grip\", \"left\"]).setAll({\n cornerRadiusBL: 0,\n cornerRadiusBR: 0,\n cornerRadiusTL: 0,\n cornerRadiusTR: 0\n });\n r(\"RoundedRectangle\", [\"resizer\", \"grip\", \"right\"]).setAll({\n cornerRadiusBL: 0,\n cornerRadiusBR: 0,\n cornerRadiusTL: 0,\n cornerRadiusTR: 0\n });\n {\n const rule = r(\"Rectangle\", [\"resizer\", \"rectangle\"]);\n rule.setAll({\n strokeDasharray: [2, 2],\n strokeOpacity: 0.5,\n strokeWidth: 1\n });\n setColor(rule, \"stroke\", ic, \"alternativeBackground\");\n }\n r(\"Graphics\", [\"button\", \"plus\", \"icon\"]).setAll({\n x: p50,\n y: p50,\n draw: display => {\n display.moveTo(-4, 0);\n display.lineTo(4, 0);\n display.moveTo(0, -4);\n display.lineTo(0, 4);\n }\n });\n r(\"Graphics\", [\"button\", \"minus\", \"icon\"]).setAll({\n x: p50,\n y: p50,\n draw: display => {\n display.moveTo(-4, 0);\n display.lineTo(4, 0);\n }\n });\n r(\"Graphics\", [\"button\", \"home\", \"icon\"]).setAll({\n x: p50,\n y: p50,\n svgPath: \"M 8 -1 L 6 -1 L 6 7 L 2 7 L 2 1 L -2 1 L -2 7 L -6 7 L -6 -1 L -8 -1 L 0 -9 L 8 -1 Z M 8 -1\"\n });\n r(\"Button\", [\"zoomtools\"]).setAll({\n marginTop: 1,\n marginBottom: 2\n });\n r(\"ZoomTools\").setAll({\n x: p100,\n centerX: p100,\n y: p100,\n centerY: p100,\n paddingRight: 10,\n paddingBottom: 10\n });\n }\n}\n","/**\r\n * Modified from Pixi:\r\n *\r\n * The MIT License\r\n *\r\n * Copyright (c) 2013-2017 Mathew Groves, Chad Engler\r\n *\r\n * Permission is hereby granted, free of charge, to any person obtaining a copy\r\n * of this software and associated documentation files (the \"Software\"), to deal\r\n * in the Software without restriction, including without limitation the rights\r\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the Software is\r\n * furnished to do so, subject to the following conditions:\r\n *\r\n * The above copyright notice and this permission notice shall be included in\r\n * all copies or substantial portions of the Software.\r\n *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n * THE SOFTWARE.\r\n */\n/**\r\n * @ignore\r\n */\nexport class Matrix {\n constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {\n Object.defineProperty(this, \"a\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"b\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"c\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"d\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tx\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"ty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.a = a;\n this.b = b;\n this.c = c;\n this.d = d;\n this.tx = tx;\n this.ty = ty;\n }\n /**\r\n * Sets the matrix based on all the available properties\r\n */\n setTransform(x, y, pivotX, pivotY, rotation, scale = 1) {\n this.a = Math.cos(rotation) * scale;\n this.b = Math.sin(rotation) * scale;\n this.c = -Math.sin(rotation) * scale;\n this.d = Math.cos(rotation) * scale;\n this.tx = x - (pivotX * this.a + pivotY * this.c);\n this.ty = y - (pivotX * this.b + pivotY * this.d);\n }\n /**\r\n * Get a new position with the current transformation applied.\r\n * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering)\r\n */\n apply(origin) {\n return {\n x: this.a * origin.x + this.c * origin.y + this.tx,\n y: this.b * origin.x + this.d * origin.y + this.ty\n };\n }\n /**\r\n * Get a new position with the inverse of the current transformation applied.\r\n * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input)\r\n */\n applyInverse(origin) {\n const id = 1 / (this.a * this.d + this.c * -this.b);\n return {\n x: this.d * id * origin.x + -this.c * id * origin.y + (this.ty * this.c - this.tx * this.d) * id,\n y: this.a * id * origin.y + -this.b * id * origin.x + (-this.ty * this.a + this.tx * this.b) * id\n };\n }\n /**\r\n * Appends the given Matrix to this Matrix.\r\n */\n append(matrix) {\n const a1 = this.a;\n const b1 = this.b;\n const c1 = this.c;\n const d1 = this.d;\n this.a = matrix.a * a1 + matrix.b * c1;\n this.b = matrix.a * b1 + matrix.b * d1;\n this.c = matrix.c * a1 + matrix.d * c1;\n this.d = matrix.c * b1 + matrix.d * d1;\n this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx;\n this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty;\n }\n /**\r\n * Prepends the given Matrix to this Matrix.\r\n */\n prepend(matrix) {\n const tx1 = this.tx;\n if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) {\n const a1 = this.a;\n const c1 = this.c;\n this.a = a1 * matrix.a + this.b * matrix.c;\n this.b = a1 * matrix.b + this.b * matrix.d;\n this.c = c1 * matrix.a + this.d * matrix.c;\n this.d = c1 * matrix.b + this.d * matrix.d;\n }\n this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx;\n this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty;\n }\n /**\r\n * Copies the other matrix's properties into this matrix\r\n */\n copyFrom(matrix) {\n this.a = matrix.a;\n this.b = matrix.b;\n this.c = matrix.c;\n this.d = matrix.d;\n this.tx = matrix.tx;\n this.ty = matrix.ty;\n }\n}\n","var _slicedToArray = function () {\n function sliceIterator(arr, i) {\n var _arr = [];\n var _n = true;\n var _d = false;\n var _e = undefined;\n try {\n for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i[\"return\"]) _i[\"return\"]();\n } finally {\n if (_d) throw _e;\n }\n }\n return _arr;\n }\n return function (arr, i) {\n if (Array.isArray(arr)) {\n return arr;\n } else if (Symbol.iterator in Object(arr)) {\n return sliceIterator(arr, i);\n } else {\n throw new TypeError(\"Invalid attempt to destructure non-iterable instance\");\n }\n };\n}();\nvar TAU = Math.PI * 2;\nvar mapToEllipse = function mapToEllipse(_ref, rx, ry, cosphi, sinphi, centerx, centery) {\n var x = _ref.x,\n y = _ref.y;\n x *= rx;\n y *= ry;\n var xp = cosphi * x - sinphi * y;\n var yp = sinphi * x + cosphi * y;\n return {\n x: xp + centerx,\n y: yp + centery\n };\n};\nvar approxUnitArc = function approxUnitArc(ang1, ang2) {\n // If 90 degree circular arc, use a constant\n // as derived from http://spencermortensen.com/articles/bezier-circle\n var a = ang2 === 1.5707963267948966 ? 0.551915024494 : ang2 === -1.5707963267948966 ? -0.551915024494 : 4 / 3 * Math.tan(ang2 / 4);\n var x1 = Math.cos(ang1);\n var y1 = Math.sin(ang1);\n var x2 = Math.cos(ang1 + ang2);\n var y2 = Math.sin(ang1 + ang2);\n return [{\n x: x1 - y1 * a,\n y: y1 + x1 * a\n }, {\n x: x2 + y2 * a,\n y: y2 - x2 * a\n }, {\n x: x2,\n y: y2\n }];\n};\nvar vectorAngle = function vectorAngle(ux, uy, vx, vy) {\n var sign = ux * vy - uy * vx < 0 ? -1 : 1;\n var dot = ux * vx + uy * vy;\n if (dot > 1) {\n dot = 1;\n }\n if (dot < -1) {\n dot = -1;\n }\n return sign * Math.acos(dot);\n};\nvar getArcCenter = function getArcCenter(px, py, cx, cy, rx, ry, largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp) {\n var rxsq = Math.pow(rx, 2);\n var rysq = Math.pow(ry, 2);\n var pxpsq = Math.pow(pxp, 2);\n var pypsq = Math.pow(pyp, 2);\n var radicant = rxsq * rysq - rxsq * pypsq - rysq * pxpsq;\n if (radicant < 0) {\n radicant = 0;\n }\n radicant /= rxsq * pypsq + rysq * pxpsq;\n radicant = Math.sqrt(radicant) * (largeArcFlag === sweepFlag ? -1 : 1);\n var centerxp = radicant * rx / ry * pyp;\n var centeryp = radicant * -ry / rx * pxp;\n var centerx = cosphi * centerxp - sinphi * centeryp + (px + cx) / 2;\n var centery = sinphi * centerxp + cosphi * centeryp + (py + cy) / 2;\n var vx1 = (pxp - centerxp) / rx;\n var vy1 = (pyp - centeryp) / ry;\n var vx2 = (-pxp - centerxp) / rx;\n var vy2 = (-pyp - centeryp) / ry;\n var ang1 = vectorAngle(1, 0, vx1, vy1);\n var ang2 = vectorAngle(vx1, vy1, vx2, vy2);\n if (sweepFlag === 0 && ang2 > 0) {\n ang2 -= TAU;\n }\n if (sweepFlag === 1 && ang2 < 0) {\n ang2 += TAU;\n }\n return [centerx, centery, ang1, ang2];\n};\nvar arcToBezier = function arcToBezier(_ref2) {\n var px = _ref2.px,\n py = _ref2.py,\n cx = _ref2.cx,\n cy = _ref2.cy,\n rx = _ref2.rx,\n ry = _ref2.ry,\n _ref2$xAxisRotation = _ref2.xAxisRotation,\n xAxisRotation = _ref2$xAxisRotation === undefined ? 0 : _ref2$xAxisRotation,\n _ref2$largeArcFlag = _ref2.largeArcFlag,\n largeArcFlag = _ref2$largeArcFlag === undefined ? 0 : _ref2$largeArcFlag,\n _ref2$sweepFlag = _ref2.sweepFlag,\n sweepFlag = _ref2$sweepFlag === undefined ? 0 : _ref2$sweepFlag;\n var curves = [];\n if (rx === 0 || ry === 0) {\n return [];\n }\n var sinphi = Math.sin(xAxisRotation * TAU / 360);\n var cosphi = Math.cos(xAxisRotation * TAU / 360);\n var pxp = cosphi * (px - cx) / 2 + sinphi * (py - cy) / 2;\n var pyp = -sinphi * (px - cx) / 2 + cosphi * (py - cy) / 2;\n if (pxp === 0 && pyp === 0) {\n return [];\n }\n rx = Math.abs(rx);\n ry = Math.abs(ry);\n var lambda = Math.pow(pxp, 2) / Math.pow(rx, 2) + Math.pow(pyp, 2) / Math.pow(ry, 2);\n if (lambda > 1) {\n rx *= Math.sqrt(lambda);\n ry *= Math.sqrt(lambda);\n }\n var _getArcCenter = getArcCenter(px, py, cx, cy, rx, ry, largeArcFlag, sweepFlag, sinphi, cosphi, pxp, pyp),\n _getArcCenter2 = _slicedToArray(_getArcCenter, 4),\n centerx = _getArcCenter2[0],\n centery = _getArcCenter2[1],\n ang1 = _getArcCenter2[2],\n ang2 = _getArcCenter2[3];\n\n // If 'ang2' == 90.0000000001, then `ratio` will evaluate to\n // 1.0000000001. This causes `segments` to be greater than one, which is an\n // unecessary split, and adds extra points to the bezier curve. To alleviate\n // this issue, we round to 1.0 when the ratio is close to 1.0.\n\n var ratio = Math.abs(ang2) / (TAU / 4);\n if (Math.abs(1.0 - ratio) < 0.0000001) {\n ratio = 1.0;\n }\n var segments = Math.max(Math.ceil(ratio), 1);\n ang2 /= segments;\n for (var i = 0; i < segments; i++) {\n curves.push(approxUnitArc(ang1, ang2));\n ang1 += ang2;\n }\n return curves.map(function (curve) {\n var _mapToEllipse = mapToEllipse(curve[0], rx, ry, cosphi, sinphi, centerx, centery),\n x1 = _mapToEllipse.x,\n y1 = _mapToEllipse.y;\n var _mapToEllipse2 = mapToEllipse(curve[1], rx, ry, cosphi, sinphi, centerx, centery),\n x2 = _mapToEllipse2.x,\n y2 = _mapToEllipse2.y;\n var _mapToEllipse3 = mapToEllipse(curve[2], rx, ry, cosphi, sinphi, centerx, centery),\n x = _mapToEllipse3.x,\n y = _mapToEllipse3.y;\n return {\n x1: x1,\n y1: y1,\n x2: x2,\n y2: y2,\n x: x,\n y: y\n };\n });\n};\nexport default arcToBezier;","/** @ignore */ /** */\nimport { BlendMode } from \"./Renderer\";\nimport { Color } from \"../../util/Color\";\nimport { Matrix } from \"../../util/Matrix\";\nimport { Percent, percent } from \"../../util/Percent\";\n//import { Throttler } from \"../../util/Throttler\";\nimport { ArrayDisposer, Disposer, DisposerClass, CounterDisposer, MultiDisposer } from \"../../util/Disposer\";\nimport { TextFormatter } from \"../../util/TextFormatter\";\nimport * as $utils from \"../../util/Utils\";\nimport * as $array from \"../../util/Array\";\nimport * as $object from \"../../util/Object\";\nimport * as $type from \"../../util/Type\";\nimport * as $math from \"../../util/Math\";\nimport arcToBezier from 'svg-arc-to-cubic-bezier';\n/**\r\n * @ignore\r\n */\nfunction checkArgs(name, actual, expected) {\n if (actual !== expected) {\n throw new Error(\"Required \" + expected + \" arguments for \" + name + \" but got \" + actual);\n }\n}\n/**\r\n * @ignore\r\n */\nfunction checkMinArgs(name, actual, expected) {\n if (actual < expected) {\n throw new Error(\"Required at least \" + expected + \" arguments for \" + name + \" but got \" + actual);\n }\n}\n/**\r\n * @ignore\r\n */\nfunction checkEvenArgs(name, actual, expected) {\n checkMinArgs(name, actual, expected);\n if (actual % expected !== 0) {\n throw new Error(\"Arguments for \" + name + \" must be in pairs of \" + expected);\n }\n}\n/**\r\n * @ignore\r\n * This splits the flag so that way 0017 will be processed as 0 0 17\r\n *\r\n * This is important for weird paths like `M17 5A1 1 0 0017 30 1 1 0 0017 5`\r\n */\nfunction splitArcFlags(args) {\n for (let i = 0; i < args.length; i += 7) {\n let index = i + 3;\n let flag = args[index];\n if (flag.length > 1) {\n const a = /^([01])([01])(.*)$/.exec(flag);\n if (a !== null) {\n args.splice(index, 0, a[1]);\n ++index;\n args.splice(index, 0, a[2]);\n ++index;\n if (a[3].length > 0) {\n args[index] = a[3];\n } else {\n args.splice(index, 1);\n }\n }\n }\n ++index;\n flag = args[index];\n if (flag.length > 1) {\n const a = /^([01])(.+)$/.exec(flag);\n if (a !== null) {\n args.splice(index, 0, a[1]);\n ++index;\n args[index] = a[2];\n }\n }\n }\n}\n/**\r\n * @ignore\r\n */\nfunction assertBinary(value) {\n if (value === 0 || value === 1) {\n return value;\n } else {\n throw new Error(\"Flag must be 0 or 1\");\n }\n}\n// 1 -> 0xffffff * (2 / 2)\n// 2 -> 0xffffff * (1 / 2)\n//\n// 3 -> 0xffffff * (3 / 4)\n// 4 -> 0xffffff * (1 / 4)\n//\n// 5 -> 0xffffff * (7 / 8)\n// 6 -> 0xffffff * (5 / 8)\n// 7 -> 0xffffff * (3 / 8)\n// 8 -> 0xffffff * (1 / 8)\n//\n// 9 -> 0xffffff * (15 / 16)\n// 10 -> 0xffffff * (13 / 16)\n// 11 -> 0xffffff * (11 / 16)\n// 12 -> 0xffffff * (9 / 16)\n// 13 -> 0xffffff * (7 / 16)\n// 14 -> 0xffffff * (5 / 16)\n// 15 -> 0xffffff * (3 / 16)\n// 16 -> 0xffffff * (1 / 16)\n// @todo remove this old color distribution algo if the new one pans out\n/*function distributeId(id: number): number {\r\n if (id === 1) {\r\n return 0x000001;\r\n\r\n } else {\r\n // Finds the closest power of 2\r\n const base = Math.pow(2, Math.ceil(Math.log(id) / Math.log(2)));\r\n\r\n // Translates the id into an odd fraction index\r\n const index = ((base - id) * 2) + 1;\r\n\r\n // TODO is Math.round correct ?\r\n return Math.round(0xffffff * (index / base));\r\n }\r\n}*/\n/**\r\n * Function by smeans:\r\n * https://lowcode.life/generating-unique-contrasting-colors-in-javascript/\r\n * @ignore\r\n */\nfunction distributeId(id) {\n const rgb = [0, 0, 0];\n for (let i = 0; i < 24; i++) {\n rgb[i % 3] <<= 1;\n rgb[i % 3] |= id & 0x01;\n id >>= 1;\n }\n return (rgb[0] | 0) + (rgb[1] << 8) + (rgb[2] << 16);\n}\n/**\r\n * @ignore\r\n */\nfunction eachTargets(hitTarget, f) {\n for (;;) {\n if (hitTarget.interactive) {\n if (!f(hitTarget)) {\n break;\n }\n }\n if (hitTarget._parent) {\n hitTarget = hitTarget._parent;\n } else {\n break;\n }\n }\n}\n// TODO feature detection for mouse/touch/pointer\n/**\r\n * @ignore\r\n */\nfunction onPointerEvent(element, name, f) {\n return $utils.addEventListener(element, $utils.getRendererEvent(name), event => {\n const target = $utils.getEventTarget(event);\n let touches = event.touches;\n if (touches) {\n if (touches.length == 0) {\n touches = event.changedTouches;\n }\n f($array.copy(touches), target);\n } else {\n f([event], target);\n }\n });\n}\n/**\r\n * @ignore\r\n */\nfunction isTainted(image) {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n const context = canvas.getContext(\"2d\", {\n willReadFrequently: true\n });\n context.drawImage(image, 0, 0, 1, 1);\n try {\n context.getImageData(0, 0, 1, 1);\n return false;\n } catch (err) {\n console.warn(\"Image \\\"\" + image.src + \"\\\" is loaded from different host and is not covered by CORS policy. For more information about the implications read here: https://www.amcharts.com/docs/v5/concepts/cors\");\n return true;\n }\n}\n/**\r\n * This is needed to workaround a bug in iOS which causes it to not GC canvas elements.\r\n *\r\n * @ignore\r\n */\nfunction clearCanvas(view) {\n view.width = 0;\n view.height = 0;\n view.style.width = \"0px\";\n view.style.height = \"0px\";\n}\n/**\r\n * Aligns the coordinate to the pixel, so it renders crisp\r\n *\r\n * @ignore\r\n */\nfunction crisp(x) {\n return Math.floor(x) + .5;\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasPivot {\n constructor() {\n Object.defineProperty(this, \"_x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n }\n get x() {\n return this._x;\n }\n get y() {\n return this._y;\n }\n set x(value) {\n this._x = value;\n }\n set y(value) {\n this._y = value;\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasDisplayObject extends DisposerClass {\n constructor(renderer) {\n super();\n Object.defineProperty(this, \"_layer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"mask\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"visible\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"exportable\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"interactive\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"inactive\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"wheelable\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"cancelTouch\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"isMeasured\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"buttonMode\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"alpha\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n Object.defineProperty(this, \"compoundAlpha\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n Object.defineProperty(this, \"angle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"scale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"crisp\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"pivot\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new CanvasPivot()\n });\n Object.defineProperty(this, \"filter\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"cursorOverStyle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_replacedCursorStyle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_localMatrix\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Matrix()\n });\n Object.defineProperty(this, \"_matrix\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Matrix()\n });\n // TODO can this be replaced with _localMatrix ?\n Object.defineProperty(this, \"_uMatrix\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Matrix()\n });\n Object.defineProperty(this, \"_renderer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_parent\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_localBounds\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_bounds\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_colorId\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._renderer = renderer;\n }\n subStatus(status) {\n return {\n inactive: this.inactive == null ? status.inactive : this.inactive,\n layer: this._layer || status.layer\n };\n }\n _dispose() {\n this._renderer._removeObject(this);\n this.getLayer().dirty = true;\n }\n getCanvas() {\n return this.getLayer().view;\n }\n getLayer() {\n let self = this;\n for (;;) {\n if (self._layer) {\n return self._layer;\n } else if (self._parent) {\n self = self._parent;\n } else {\n return this._renderer.defaultLayer;\n }\n }\n }\n setLayer(order, margin) {\n if (order == null) {\n this._layer = undefined;\n } else {\n const visible = true;\n this._layer = this._renderer.getLayer(order, visible);\n this._layer.visible = visible;\n this._layer.margin = margin;\n if (margin) {\n $utils.setInteractive(this._layer.view, false);\n }\n this._renderer._ghostLayer.setMargin(this._renderer.layers);\n if (this._parent) {\n this._parent.registerChildLayer(this._layer);\n }\n this._layer.dirty = true;\n this._renderer.resizeLayer(this._layer);\n this._renderer.resizeGhost();\n }\n }\n markDirtyLayer() {\n this.getLayer().dirty = true;\n }\n clear() {\n this.invalidateBounds();\n }\n invalidateBounds() {\n this._localBounds = undefined;\n }\n _addBounds(_bounds) {}\n _getColorId() {\n if (this._colorId === undefined) {\n this._colorId = this._renderer.paintId(this);\n }\n return this._colorId;\n }\n _isInteractive(status) {\n return !status.inactive && (this.interactive || this._renderer._forceInteractive > 0);\n }\n _isInteractiveMask(status) {\n return this._isInteractive(status);\n }\n contains(child) {\n for (;;) {\n if (child === this) {\n return true;\n } else if (child._parent) {\n child = child._parent;\n } else {\n return false;\n }\n }\n }\n toGlobal(point) {\n return this._matrix.apply(point);\n }\n toLocal(point) {\n return this._matrix.applyInverse(point);\n }\n getLocalMatrix() {\n this._uMatrix.setTransform(0, 0, this.pivot.x, this.pivot.y, this.angle * Math.PI / 180, this.scale);\n return this._uMatrix;\n }\n getLocalBounds() {\n if (!this._localBounds) {\n const bn = 10000000;\n this._localBounds = {\n left: bn,\n top: bn,\n right: -bn,\n bottom: -bn\n };\n this._addBounds(this._localBounds);\n }\n return this._localBounds;\n }\n getAdjustedBounds(bounds) {\n this._setMatrix();\n const matrix = this.getLocalMatrix();\n const p0 = matrix.apply({\n x: bounds.left,\n y: bounds.top\n });\n const p1 = matrix.apply({\n x: bounds.right,\n y: bounds.top\n });\n const p2 = matrix.apply({\n x: bounds.right,\n y: bounds.bottom\n });\n const p3 = matrix.apply({\n x: bounds.left,\n y: bounds.bottom\n });\n return {\n left: Math.min(p0.x, p1.x, p2.x, p3.x),\n top: Math.min(p0.y, p1.y, p2.y, p3.y),\n right: Math.max(p0.x, p1.x, p2.x, p3.x),\n bottom: Math.max(p0.y, p1.y, p2.y, p3.y)\n };\n }\n on(key, callback, context) {\n if (this.interactive) {\n return this._renderer._addEvent(this, key, callback, context);\n } else {\n return new Disposer(() => {});\n }\n }\n _setMatrix() {\n // TODO only calculate this if it has actually changed\n this._localMatrix.setTransform(this.x, this.y, this.pivot.x, this.pivot.y,\n // Converts degrees to radians\n this.angle * Math.PI / 180, this.scale);\n this._matrix.copyFrom(this._localMatrix);\n if (this._parent) {\n // TODO only calculate this if it has actually changed\n this._matrix.prepend(this._parent._matrix);\n }\n }\n _transform(context, resolution) {\n const m = this._matrix;\n let tx = m.tx * resolution;\n let ty = m.ty * resolution;\n if (this.crisp) {\n tx = crisp(tx);\n ty = crisp(ty);\n }\n context.setTransform(m.a * resolution, m.b * resolution, m.c * resolution, m.d * resolution, tx, ty);\n }\n _transformMargin(context, resolution, margin) {\n const m = this._matrix;\n context.setTransform(m.a * resolution, m.b * resolution, m.c * resolution, m.d * resolution, (m.tx + margin.left) * resolution, (m.ty + margin.top) * resolution);\n }\n _transformLayer(context, resolution, layer) {\n if (layer.margin) {\n this._transformMargin(context, layer.scale || resolution, layer.margin);\n } else {\n this._transform(context, layer.scale || resolution);\n }\n }\n render(status) {\n if (this.visible && (this.exportable !== false || !this._renderer._omitTainted)) {\n this._setMatrix();\n const subStatus = this.subStatus(status);\n const resolution = this._renderer.resolution;\n const layers = this._renderer.layers;\n const ghostLayer = this._renderer._ghostLayer;\n const ghostContext = ghostLayer.context;\n const mask = this.mask;\n if (mask) {\n mask._setMatrix();\n }\n // TODO improve this\n $array.each(layers, layer => {\n if (layer) {\n const context = layer.context;\n context.save();\n // We must apply the mask before we transform the element\n if (mask) {\n mask._transformLayer(context, resolution, layer);\n mask._runPath(context);\n context.clip();\n }\n context.globalAlpha = this.compoundAlpha * this.alpha;\n this._transformLayer(context, resolution, layer);\n if (this.filter) {\n context.filter = this.filter;\n }\n }\n });\n ghostContext.save();\n // We must apply the mask before we transform the element\n if (mask && this._isInteractiveMask(subStatus)) {\n mask._transformMargin(ghostContext, resolution, ghostLayer.margin);\n mask._runPath(ghostContext);\n ghostContext.clip();\n }\n this._transformMargin(ghostContext, resolution, ghostLayer.margin);\n this._render(subStatus);\n ghostContext.restore();\n $array.each(layers, layer => {\n if (layer) {\n layer.context.restore();\n }\n });\n }\n }\n _render(status) {\n if (this.exportable === false) {\n status.layer.tainted = true;\n }\n }\n hovering() {\n return this._renderer._hovering.has(this);\n }\n dragging() {\n return this._renderer._dragging.some(x => x.value === this);\n }\n shouldCancelTouch() {\n const renderer = this._renderer;\n if (renderer.tapToActivate && !renderer._touchActive) {\n return false;\n }\n if (this.cancelTouch) {\n return true;\n } else if (this._parent) {\n return this._parent.shouldCancelTouch();\n }\n return false;\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasContainer extends CanvasDisplayObject {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"interactiveChildren\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_childLayers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_children\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n }\n _isInteractiveMask(status) {\n return this.interactiveChildren || super._isInteractiveMask(status);\n }\n addChild(child) {\n child._parent = this;\n this._children.push(child);\n if (child._layer) {\n this.registerChildLayer(child._layer);\n }\n }\n addChildAt(child, index) {\n child._parent = this;\n this._children.splice(index, 0, child);\n if (child._layer) {\n this.registerChildLayer(child._layer);\n }\n }\n removeChild(child) {\n child._parent = undefined;\n $array.removeFirst(this._children, child);\n }\n _render(status) {\n super._render(status);\n const renderer = this._renderer;\n if (this.interactive && this.interactiveChildren) {\n ++renderer._forceInteractive;\n }\n $array.each(this._children, child => {\n child.compoundAlpha = this.compoundAlpha * this.alpha;\n child.render(status);\n });\n if (this.interactive && this.interactiveChildren) {\n --renderer._forceInteractive;\n }\n }\n registerChildLayer(layer) {\n if (!this._childLayers) {\n this._childLayers = [];\n }\n $array.pushOne(this._childLayers, layer);\n if (this._parent) {\n this._parent.registerChildLayer(layer);\n }\n }\n markDirtyLayer(deep = false) {\n super.markDirtyLayer();\n if (deep && this._childLayers) {\n $array.each(this._childLayers, layer => layer.dirty = true);\n }\n }\n _dispose() {\n super._dispose();\n if (this._childLayers) {\n $array.each(this._childLayers, layer => {\n layer.dirty = true;\n });\n }\n }\n}\n/**\r\n * @ignore\r\n */\nfunction setPoint(bounds, point) {\n bounds.left = Math.min(bounds.left, point.x);\n bounds.top = Math.min(bounds.top, point.y);\n bounds.right = Math.max(bounds.right, point.x);\n bounds.bottom = Math.max(bounds.bottom, point.y);\n}\n/**\r\n * @ignore\r\n */\nclass Op {\n colorize(_context, _forceColor) {}\n colorizeGhost(context, forceColor) {\n this.colorize(context, forceColor);\n }\n path(_context) {}\n pathGhost(context) {\n this.path(context);\n }\n addBounds(_bounds) {}\n}\n/**\r\n * @ignore\r\n */\nclass BeginPath extends Op {\n colorize(context, _forceColor) {\n context.beginPath();\n }\n}\n/**\r\n * @ignore\r\n */\nclass BeginFill extends Op {\n constructor(color) {\n super();\n Object.defineProperty(this, \"color\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: color\n });\n }\n colorize(context, forceColor) {\n if (forceColor !== undefined) {\n context.fillStyle = forceColor;\n } else {\n context.fillStyle = this.color;\n }\n }\n}\n/**\r\n * @ignore\r\n */\nclass EndFill extends Op {\n constructor(clearShadow) {\n super();\n Object.defineProperty(this, \"clearShadow\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: clearShadow\n });\n }\n colorize(context, _forceColor) {\n context.fill();\n if (this.clearShadow) {\n context.shadowColor = \"\";\n context.shadowBlur = 0;\n context.shadowOffsetX = 0;\n context.shadowOffsetY = 0;\n }\n }\n}\n/**\r\n * @ignore\r\n */\nclass EndStroke extends Op {\n colorize(context, _forceColor) {\n context.stroke();\n }\n}\n/**\r\n * @ignore\r\n */\nclass LineStyle extends Op {\n constructor(width, color, lineJoin) {\n super();\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: width\n });\n Object.defineProperty(this, \"color\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: color\n });\n Object.defineProperty(this, \"lineJoin\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: lineJoin\n });\n }\n colorize(context, forceColor) {\n if (forceColor !== undefined) {\n context.strokeStyle = forceColor;\n } else {\n context.strokeStyle = this.color;\n }\n context.lineWidth = this.width;\n if (this.lineJoin) {\n context.lineJoin = this.lineJoin;\n }\n }\n}\n/**\r\n * @ignore\r\n */\nclass LineDash extends Op {\n constructor(dash) {\n super();\n Object.defineProperty(this, \"dash\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: dash\n });\n }\n colorize(context, _forceColor) {\n context.setLineDash(this.dash);\n }\n}\n/**\r\n * @ignore\r\n */\nclass LineDashOffset extends Op {\n constructor(dashOffset) {\n super();\n Object.defineProperty(this, \"dashOffset\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: dashOffset\n });\n }\n colorize(context, _forceColor) {\n context.lineDashOffset = this.dashOffset;\n }\n}\n/**\r\n * @ignore\r\n */\nclass DrawRect extends Op {\n constructor(x, y, width, height) {\n super();\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: width\n });\n Object.defineProperty(this, \"height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: height\n });\n }\n path(context) {\n context.rect(this.x, this.y, this.width, this.height);\n }\n addBounds(bounds) {\n const l = this.x;\n const t = this.y;\n const r = l + this.width;\n const b = t + this.height;\n setPoint(bounds, {\n x: l,\n y: t\n });\n setPoint(bounds, {\n x: r,\n y: t\n });\n setPoint(bounds, {\n x: l,\n y: b\n });\n setPoint(bounds, {\n x: r,\n y: b\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass DrawCircle extends Op {\n constructor(x, y, radius) {\n super();\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n Object.defineProperty(this, \"radius\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: radius\n });\n }\n path(context) {\n context.moveTo(this.x + this.radius, this.y);\n context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);\n }\n // TODO handle skewing and rotation\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.x - this.radius,\n y: this.y - this.radius\n });\n setPoint(bounds, {\n x: this.x + this.radius,\n y: this.y + this.radius\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass DrawEllipse extends Op {\n constructor(x, y, radiusX, radiusY) {\n super();\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n Object.defineProperty(this, \"radiusX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: radiusX\n });\n Object.defineProperty(this, \"radiusY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: radiusY\n });\n }\n path(context) {\n context.ellipse(0, 0, this.radiusX, this.radiusY, 0, 0, Math.PI * 2);\n }\n // TODO handle skewing and rotation\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.x - this.radiusX,\n y: this.y - this.radiusY\n });\n setPoint(bounds, {\n x: this.x + this.radiusX,\n y: this.y + this.radiusY\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass Arc extends Op {\n constructor(cx, cy, radius, startAngle, endAngle, anticlockwise) {\n super();\n Object.defineProperty(this, \"cx\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cx\n });\n Object.defineProperty(this, \"cy\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cy\n });\n Object.defineProperty(this, \"radius\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: radius\n });\n Object.defineProperty(this, \"startAngle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: startAngle\n });\n Object.defineProperty(this, \"endAngle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: endAngle\n });\n Object.defineProperty(this, \"anticlockwise\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: anticlockwise\n });\n }\n path(context) {\n if (this.radius > 0) {\n context.arc(this.cx, this.cy, this.radius, this.startAngle, this.endAngle, this.anticlockwise);\n }\n }\n addBounds(bounds) {\n let arcBounds = $math.getArcBounds(this.cx, this.cy, this.startAngle * $math.DEGREES, this.endAngle * $math.DEGREES, this.radius);\n setPoint(bounds, {\n x: arcBounds.left,\n y: arcBounds.top\n });\n setPoint(bounds, {\n x: arcBounds.right,\n y: arcBounds.bottom\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass ArcTo extends Op {\n constructor(x1, y1, x2, y2, radius) {\n super();\n Object.defineProperty(this, \"x1\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x1\n });\n Object.defineProperty(this, \"y1\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y1\n });\n Object.defineProperty(this, \"x2\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x2\n });\n Object.defineProperty(this, \"y2\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y2\n });\n Object.defineProperty(this, \"radius\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: radius\n });\n }\n path(context) {\n if (this.radius > 0) {\n context.arcTo(this.x1, this.y1, this.x2, this.y2, this.radius);\n }\n }\n // TODO: add points\n addBounds(_bounds) {\n /*\r\n // not finished\r\n https://math.stackexchange.com/questions/1781438/finding-the-center-of-a-circle-given-two-points-and-a-radius-algebraically\r\n if (prevPoint) {\r\n let x1 = prevPoint.x;\r\n let y1 = prevPoint.y;\r\n let x2 = this.x2;\r\n let y2 = this.y2;\r\n let r = this.radius;\r\n let xa = (x2 - x1) / 2;\r\n let ya = (y2 - y1) / 2;\r\n let x0 = x1 + xa;\r\n let y0 = y1 + ya;\r\n let a = Math.hypot(xa, ya);\r\n let b = Math.sqrt(r * r - a * a);\r\n let cx = x0 + b * ya / a;\r\n let cy = y0 - b * xa / a;\r\n console.log(cx, cy);\r\n }*/\n }\n}\n/**\r\n * @ignore\r\n */\nclass LineTo extends Op {\n constructor(x, y) {\n super();\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n }\n path(context) {\n context.lineTo(this.x, this.y);\n }\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.x,\n y: this.y\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass MoveTo extends Op {\n constructor(x, y) {\n super();\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n }\n path(context) {\n context.moveTo(this.x, this.y);\n }\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.x,\n y: this.y\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass ClosePath extends Op {\n path(context) {\n context.closePath();\n }\n}\n/**\r\n * @ignore\r\n */\nclass BezierCurveTo extends Op {\n constructor(cpX, cpY, cpX2, cpY2, toX, toY) {\n super();\n Object.defineProperty(this, \"cpX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpX\n });\n Object.defineProperty(this, \"cpY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpY\n });\n Object.defineProperty(this, \"cpX2\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpX2\n });\n Object.defineProperty(this, \"cpY2\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpY2\n });\n Object.defineProperty(this, \"toX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: toX\n });\n Object.defineProperty(this, \"toY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: toY\n });\n }\n path(context) {\n context.bezierCurveTo(this.cpX, this.cpY, this.cpX2, this.cpY2, this.toX, this.toY);\n }\n // TODO: OK?\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.cpX,\n y: this.cpY\n });\n setPoint(bounds, {\n x: this.cpX2,\n y: this.cpY2\n });\n setPoint(bounds, {\n x: this.toX,\n y: this.toY\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass QuadraticCurveTo extends Op {\n constructor(cpX, cpY, toX, toY) {\n super();\n Object.defineProperty(this, \"cpX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpX\n });\n Object.defineProperty(this, \"cpY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: cpY\n });\n Object.defineProperty(this, \"toX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: toX\n });\n Object.defineProperty(this, \"toY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: toY\n });\n }\n path(context) {\n context.quadraticCurveTo(this.cpX, this.cpY, this.toX, this.toY);\n }\n // TODO: OK?\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.cpX,\n y: this.cpY\n });\n setPoint(bounds, {\n x: this.toX,\n y: this.toY\n });\n }\n}\n/**\r\n * @ignore\r\n */\nclass Shadow extends Op {\n constructor(color, blur, offsetX, offsetY, opacity) {\n super();\n Object.defineProperty(this, \"color\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: color\n });\n Object.defineProperty(this, \"blur\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: blur\n });\n Object.defineProperty(this, \"offsetX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: offsetX\n });\n Object.defineProperty(this, \"offsetY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: offsetY\n });\n Object.defineProperty(this, \"opacity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: opacity\n });\n }\n colorize(context, _forceColor) {\n if (this.opacity) {\n context.fillStyle = this.color;\n }\n context.shadowColor = this.color;\n context.shadowBlur = this.blur;\n context.shadowOffsetX = this.offsetX;\n context.shadowOffsetY = this.offsetY;\n }\n colorizeGhost(_context, _forceColor) {}\n}\n/**\r\n * @ignore\r\n */\nclass GraphicsImage extends Op {\n constructor(image, width, height, x, y) {\n super();\n Object.defineProperty(this, \"image\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: image\n });\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: width\n });\n Object.defineProperty(this, \"height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: height\n });\n Object.defineProperty(this, \"x\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: x\n });\n Object.defineProperty(this, \"y\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: y\n });\n }\n path(context) {\n context.drawImage(this.image, this.x, this.y, this.width, this.height);\n }\n // TODO: OK?\n addBounds(bounds) {\n setPoint(bounds, {\n x: this.x,\n y: this.y\n });\n setPoint(bounds, {\n x: this.width,\n y: this.height\n });\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasGraphics extends CanvasDisplayObject {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_operations\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"blendMode\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: BlendMode.NORMAL\n });\n Object.defineProperty(this, \"_hasShadows\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_fillAlpha\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_strokeAlpha\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n clear() {\n super.clear();\n this._operations.length = 0;\n }\n _pushOp(op) {\n this._operations.push(op);\n }\n beginFill(color, alpha = 1) {\n this._fillAlpha = alpha;\n if (color) {\n if (color instanceof Color) {\n this._pushOp(new BeginFill(color.toCSS(alpha)));\n } else {\n this.isMeasured = true;\n this._pushOp(new BeginFill(color));\n }\n } else {\n this._pushOp(new BeginFill(\"rgba(0, 0, 0, \" + alpha + \")\"));\n }\n }\n endFill() {\n this._pushOp(new EndFill(this._hasShadows));\n }\n endStroke() {\n this._pushOp(new EndStroke());\n }\n beginPath() {\n this._pushOp(new BeginPath());\n }\n lineStyle(width = 0, color, alpha = 1, lineJoin) {\n this._strokeAlpha = alpha;\n if (color) {\n if (color instanceof Color) {\n this._pushOp(new LineStyle(width, color.toCSS(alpha), lineJoin));\n } else {\n this._pushOp(new LineStyle(width, color, lineJoin));\n }\n } else {\n this._pushOp(new LineStyle(width, \"rgba(0, 0, 0, \" + alpha + \")\", lineJoin));\n }\n }\n setLineDash(dash) {\n this._pushOp(new LineDash(dash ? dash : []));\n }\n setLineDashOffset(dashOffset = 0) {\n this._pushOp(new LineDashOffset(dashOffset));\n }\n drawRect(x, y, width, height) {\n this._pushOp(new DrawRect(x, y, width, height));\n }\n drawCircle(x, y, radius) {\n this._pushOp(new DrawCircle(x, y, radius));\n }\n drawEllipse(x, y, radiusX, radiusY) {\n this._pushOp(new DrawEllipse(x, y, radiusX, radiusY));\n }\n arc(cx, cy, radius, startAngle, endAngle, anticlockwise = false) {\n this._pushOp(new Arc(cx, cy, radius, startAngle, endAngle, anticlockwise));\n }\n arcTo(x1, y1, x2, y2, radius) {\n this._pushOp(new ArcTo(x1, y1, x2, y2, radius));\n }\n lineTo(x, y) {\n this._pushOp(new LineTo(x, y));\n }\n moveTo(x, y) {\n this._pushOp(new MoveTo(x, y));\n }\n bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY) {\n this._pushOp(new BezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY));\n }\n quadraticCurveTo(cpX, cpY, toX, toY) {\n this._pushOp(new QuadraticCurveTo(cpX, cpY, toX, toY));\n }\n closePath() {\n this._pushOp(new ClosePath());\n }\n shadow(color, blur = 0, offsetX = 0, offsetY = 0, opacity) {\n this._hasShadows = true;\n this._pushOp(new Shadow(opacity ? color.toCSS(opacity) : color.toCSS(this._fillAlpha || this._strokeAlpha), blur, offsetX, offsetY));\n }\n image(image, width, height, x, y) {\n this._pushOp(new GraphicsImage(image, width, height, x, y));\n }\n // https://svgwg.org/svg2-draft/paths.html#DProperty\n // TODO better error checking\n svgPath(path) {\n let x = 0;\n let y = 0;\n let cpx = null;\n let cpy = null;\n let qcpx = null;\n let qcpy = null;\n const SEGMENTS_REGEXP = /([MmZzLlHhVvCcSsQqTtAa])([^MmZzLlHhVvCcSsQqTtAa]*)/g;\n const ARGS_REGEXP = /[\\u0009\\u0020\\u000A\\u000C\\u000D]*([\\+\\-]?[0-9]*\\.?[0-9]+(?:[eE][\\+\\-]?[0-9]+)?)[\\u0009\\u0020\\u000A\\u000C\\u000D]*,?/g;\n let match;\n while ((match = SEGMENTS_REGEXP.exec(path)) !== null) {\n const name = match[1];\n const rest = match[2];\n const args = [];\n while ((match = ARGS_REGEXP.exec(rest)) !== null) {\n args.push(match[1]);\n }\n // Reset control point\n if (name !== \"S\" && name !== \"s\" && name !== \"C\" && name !== \"c\") {\n cpx = null;\n cpy = null;\n }\n // Reset control point\n if (name !== \"Q\" && name !== \"q\" && name !== \"T\" && name !== \"t\") {\n qcpx = null;\n qcpy = null;\n }\n switch (name) {\n case \"M\":\n checkEvenArgs(name, args.length, 2);\n x = +args[0];\n y = +args[1];\n this.moveTo(x, y);\n for (let i = 2; i < args.length; i += 2) {\n x = +args[i];\n y = +args[i + 1];\n this.lineTo(x, y);\n }\n break;\n case \"m\":\n checkEvenArgs(name, args.length, 2);\n x += +args[0];\n y += +args[1];\n this.moveTo(x, y);\n for (let i = 2; i < args.length; i += 2) {\n x += +args[i];\n y += +args[i + 1];\n this.lineTo(x, y);\n }\n break;\n case \"L\":\n checkEvenArgs(name, args.length, 2);\n for (let i = 0; i < args.length; i += 2) {\n x = +args[i];\n y = +args[i + 1];\n this.lineTo(x, y);\n }\n break;\n case \"l\":\n checkEvenArgs(name, args.length, 2);\n for (let i = 0; i < args.length; i += 2) {\n x += +args[i];\n y += +args[i + 1];\n this.lineTo(x, y);\n }\n break;\n case \"H\":\n checkMinArgs(name, args.length, 1);\n for (let i = 0; i < args.length; ++i) {\n x = +args[i];\n this.lineTo(x, y);\n }\n break;\n case \"h\":\n checkMinArgs(name, args.length, 1);\n for (let i = 0; i < args.length; ++i) {\n x += +args[i];\n this.lineTo(x, y);\n }\n break;\n case \"V\":\n checkMinArgs(name, args.length, 1);\n for (let i = 0; i < args.length; ++i) {\n y = +args[i];\n this.lineTo(x, y);\n }\n break;\n case \"v\":\n checkMinArgs(name, args.length, 1);\n for (let i = 0; i < args.length; ++i) {\n y += +args[i];\n this.lineTo(x, y);\n }\n break;\n case \"C\":\n checkEvenArgs(name, args.length, 6);\n for (let i = 0; i < args.length; i += 6) {\n const x1 = +args[i];\n const y1 = +args[i + 1];\n cpx = +args[i + 2];\n cpy = +args[i + 3];\n x = +args[i + 4];\n y = +args[i + 5];\n this.bezierCurveTo(x1, y1, cpx, cpy, x, y);\n }\n break;\n case \"c\":\n checkEvenArgs(name, args.length, 6);\n for (let i = 0; i < args.length; i += 6) {\n const x1 = +args[i] + x;\n const y1 = +args[i + 1] + y;\n cpx = +args[i + 2] + x;\n cpy = +args[i + 3] + y;\n x += +args[i + 4];\n y += +args[i + 5];\n this.bezierCurveTo(x1, y1, cpx, cpy, x, y);\n }\n break;\n case \"S\":\n checkEvenArgs(name, args.length, 4);\n if (cpx === null || cpy === null) {\n cpx = x;\n cpy = y;\n }\n for (let i = 0; i < args.length; i += 4) {\n const x1 = 2 * x - cpx;\n const y1 = 2 * y - cpy;\n cpx = +args[i];\n cpy = +args[i + 1];\n x = +args[i + 2];\n y = +args[i + 3];\n this.bezierCurveTo(x1, y1, cpx, cpy, x, y);\n }\n break;\n case \"s\":\n checkEvenArgs(name, args.length, 4);\n if (cpx === null || cpy === null) {\n cpx = x;\n cpy = y;\n }\n for (let i = 0; i < args.length; i += 4) {\n const x1 = 2 * x - cpx;\n const y1 = 2 * y - cpy;\n cpx = +args[i] + x;\n cpy = +args[i + 1] + y;\n x += +args[i + 2];\n y += +args[i + 3];\n this.bezierCurveTo(x1, y1, cpx, cpy, x, y);\n }\n break;\n case \"Q\":\n checkEvenArgs(name, args.length, 4);\n for (let i = 0; i < args.length; i += 4) {\n qcpx = +args[i];\n qcpy = +args[i + 1];\n x = +args[i + 2];\n y = +args[i + 3];\n this.quadraticCurveTo(qcpx, qcpy, x, y);\n }\n break;\n case \"q\":\n checkEvenArgs(name, args.length, 4);\n for (let i = 0; i < args.length; i += 4) {\n qcpx = +args[i] + x;\n qcpy = +args[i + 1] + y;\n x += +args[i + 2];\n y += +args[i + 3];\n this.quadraticCurveTo(qcpx, qcpy, x, y);\n }\n break;\n case \"T\":\n checkEvenArgs(name, args.length, 2);\n if (qcpx === null || qcpy === null) {\n qcpx = x;\n qcpy = y;\n }\n for (let i = 0; i < args.length; i += 2) {\n qcpx = 2 * x - qcpx;\n qcpy = 2 * y - qcpy;\n x = +args[i];\n y = +args[i + 1];\n this.quadraticCurveTo(qcpx, qcpy, x, y);\n }\n break;\n case \"t\":\n checkEvenArgs(name, args.length, 2);\n if (qcpx === null || qcpy === null) {\n qcpx = x;\n qcpy = y;\n }\n for (let i = 0; i < args.length; i += 2) {\n qcpx = 2 * x - qcpx;\n qcpy = 2 * y - qcpy;\n x += +args[i];\n y += +args[i + 1];\n this.quadraticCurveTo(qcpx, qcpy, x, y);\n }\n break;\n case \"A\":\n case \"a\":\n const relative = name === \"a\";\n splitArcFlags(args);\n checkEvenArgs(name, args.length, 7);\n for (let i = 0; i < args.length; i += 7) {\n let cx = +args[i + 5];\n let cy = +args[i + 6];\n if (relative) {\n cx += x;\n cy += y;\n }\n const bs = arcToBezier({\n px: x,\n py: y,\n rx: +args[i],\n ry: +args[i + 1],\n xAxisRotation: +args[i + 2],\n largeArcFlag: assertBinary(+args[i + 3]),\n sweepFlag: assertBinary(+args[i + 4]),\n cx,\n cy\n });\n $array.each(bs, b => {\n this.bezierCurveTo(b.x1, b.y1, b.x2, b.y2, b.x, b.y);\n x = b.x;\n y = b.y;\n });\n }\n break;\n case \"Z\":\n case \"z\":\n checkArgs(name, args.length, 0);\n this.closePath();\n break;\n }\n }\n }\n _runPath(context) {\n context.beginPath();\n $array.each(this._operations, op => {\n op.path(context);\n });\n }\n _render(status) {\n super._render(status);\n const layerDirty = status.layer.dirty;\n const interactive = this._isInteractive(status);\n if (layerDirty || interactive) {\n const context = status.layer.context;\n const ghostContext = this._renderer._ghostLayer.context;\n if (layerDirty) {\n context.globalCompositeOperation = this.blendMode;\n context.beginPath();\n }\n let color;\n if (interactive) {\n ghostContext.beginPath();\n color = this._getColorId();\n }\n $array.each(this._operations, op => {\n if (layerDirty) {\n op.path(context);\n op.colorize(context, undefined);\n }\n if (interactive) {\n op.pathGhost(ghostContext);\n op.colorizeGhost(ghostContext, color);\n }\n });\n }\n }\n renderDetached(context) {\n if (this.visible) {\n this._setMatrix();\n context.save();\n // We must apply the mask before we transform the element\n const mask = this.mask;\n if (mask) {\n mask._setMatrix();\n mask._transform(context, 1);\n mask._runPath(context);\n context.clip();\n }\n // TODO handle compoundAlpha somehow ?\n context.globalAlpha = this.compoundAlpha * this.alpha;\n this._transform(context, 1);\n if (this.filter) {\n context.filter = this.filter;\n }\n context.globalCompositeOperation = this.blendMode;\n context.beginPath();\n $array.each(this._operations, op => {\n op.path(context);\n op.colorize(context, undefined);\n });\n context.restore();\n }\n }\n _addBounds(bounds) {\n if (this.visible && this.isMeasured) {\n $array.each(this._operations, op => {\n op.addBounds(bounds);\n });\n }\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasText extends CanvasDisplayObject {\n constructor(renderer, text, style) {\n super(renderer);\n Object.defineProperty(this, \"text\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"style\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"resolution\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n Object.defineProperty(this, \"textVisible\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_textInfo\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_originalScale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n this.text = text;\n this.style = style;\n }\n invalidateBounds() {\n super.invalidateBounds();\n this._textInfo = undefined;\n }\n _shared(context) {\n if (this.style.textAlign) {\n context.textAlign = this.style.textAlign;\n }\n if (this.style.direction) {\n context.direction = this.style.direction;\n }\n if (this.style.textBaseline) {\n context.textBaseline = this.style.textBaseline;\n }\n }\n _prerender(status, ignoreGhost = false, ignoreFontWeight = false) {\n super._render(status);\n const context = status.layer.context;\n const ghostContext = this._renderer._ghostLayer.context;\n // Font style\n const style = this.style;\n let fontStyle = this._getFontStyle(undefined, ignoreFontWeight);\n context.font = fontStyle;\n if (this._isInteractive(status) && !ignoreGhost) {\n ghostContext.font = fontStyle;\n }\n // Other parameters\n if (style.fill) {\n if (style.fill instanceof Color) {\n context.fillStyle = style.fill.toCSS(style.fillOpacity != undefined ? style.fillOpacity : 1);\n } else {\n context.fillStyle = style.fill;\n }\n }\n if (style.shadowColor) {\n status.layer.context.shadowColor = style.shadowColor.toCSS(style.shadowOpacity || 1);\n }\n if (style.shadowBlur) {\n status.layer.context.shadowBlur = style.shadowBlur;\n }\n if (style.shadowOffsetX) {\n status.layer.context.shadowOffsetX = style.shadowOffsetX;\n }\n if (style.shadowOffsetY) {\n status.layer.context.shadowOffsetY = style.shadowOffsetY;\n }\n this._shared(context);\n if (this._isInteractive(status) && !ignoreGhost) {\n ghostContext.fillStyle = this._getColorId();\n this._shared(ghostContext);\n }\n }\n _getFontStyle(style2, ignoreFontWeight = false) {\n // Process defaults\n const style = this.style;\n let fontStyle = [];\n if (style2 && style2.fontVariant) {\n fontStyle.push(style2.fontVariant);\n } else if (style.fontVariant) {\n fontStyle.push(style.fontVariant);\n }\n if (!ignoreFontWeight) {\n if (style2 && style2.fontWeight) {\n fontStyle.push(style2.fontWeight);\n } else if (style.fontWeight) {\n fontStyle.push(style.fontWeight);\n }\n }\n if (style2 && style2.fontStyle) {\n fontStyle.push(style2.fontStyle);\n } else if (style.fontStyle) {\n fontStyle.push(style.fontStyle);\n }\n if (style2 && style2.fontSize) {\n if ($type.isNumber(style2.fontSize)) {\n style2.fontSize = style2.fontSize + \"px\";\n }\n fontStyle.push(style2.fontSize);\n } else if (style.fontSize) {\n if ($type.isNumber(style.fontSize)) {\n style.fontSize = style.fontSize + \"px\";\n }\n fontStyle.push(style.fontSize);\n }\n if (style2 && style2.fontFamily) {\n fontStyle.push(style2.fontFamily);\n } else if (style.fontFamily) {\n fontStyle.push(style.fontFamily);\n } else if (fontStyle.length) {\n fontStyle.push(\"Arial\");\n }\n return fontStyle.join(\" \");\n }\n _render(status) {\n // We need measurements in order to properly position text for alignment\n if (!this._textInfo) {\n this._measure(status);\n }\n if (this.textVisible) {\n const interactive = this._isInteractive(status);\n const context = status.layer.context;\n const layerDirty = status.layer.dirty;\n const ghostContext = this._renderer._ghostLayer.context;\n context.save();\n ghostContext.save();\n this._prerender(status);\n // const lines = this.text.toString().replace(/\\r/g, \"\").split(/\\n/);\n // const x = this._localBounds && (this._localBounds.left < 0) ? Math.abs(this._localBounds.left) : 0;\n // Process text info produced by _measure()\n $array.each(this._textInfo, (line, _index) => {\n $array.each(line.textChunks, (chunk, _index) => {\n // Set style\n if (chunk.style) {\n context.save();\n ghostContext.save();\n context.font = chunk.style;\n if (this._isInteractive(status)) {\n ghostContext.font = chunk.style;\n }\n }\n if (chunk.fill) {\n context.save();\n context.fillStyle = chunk.fill.toCSS();\n // Color does not affect ghostContext so we not set it\n }\n // Draw text\n if (layerDirty) {\n context.fillText(chunk.text, chunk.offsetX, line.offsetY + chunk.offsetY);\n }\n // Draw underline\n if (chunk.textDecoration == \"underline\" || chunk.textDecoration == \"line-through\") {\n let thickness = 1;\n let offset = 1;\n let fontSize = chunk.height;\n const oversizedBehavior = this.style.oversizedBehavior || \"\";\n if ([\"truncate\", \"wrap\", \"wrap-no-break\"].indexOf(oversizedBehavior) > -1) {\n // Measure actual width of the text so the line fits\n const metrics = this._measureText(chunk.text, context);\n chunk.width = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;\n }\n let offsetX = chunk.offsetX;\n switch (this.style.textAlign) {\n case \"right\":\n case \"end\":\n offsetX -= chunk.width;\n break;\n case \"center\":\n offsetX -= chunk.width / 2;\n break;\n }\n if (chunk.style) {\n const format = TextFormatter.getTextStyle(chunk.style);\n switch (format.fontWeight) {\n case \"bolder\":\n case \"bold\":\n case \"700\":\n case \"800\":\n case \"900\":\n thickness = 2;\n break;\n }\n }\n if (fontSize) {\n offset = fontSize / 20;\n }\n let y;\n if (chunk.textDecoration == \"line-through\") {\n y = thickness + line.offsetY + chunk.offsetY - chunk.height / 2;\n } else {\n y = thickness + offset * 1.5 + line.offsetY + chunk.offsetY;\n }\n context.save();\n context.beginPath();\n if (chunk.fill) {\n context.strokeStyle = chunk.fill.toCSS();\n } else if (this.style.fill && this.style.fill instanceof Color) {\n context.strokeStyle = this.style.fill.toCSS();\n }\n context.lineWidth = thickness * offset;\n context.moveTo(offsetX, y);\n context.lineTo(offsetX + chunk.width, y);\n context.stroke();\n context.restore();\n }\n if (interactive && this.interactive) {\n // Draw text in ghost canvas ONLY if it is set as interactive\n // explicitly. This way we avoid hit test anomalies caused by anti\n // aliasing of text.\n ghostContext.fillText(chunk.text, chunk.offsetX, line.offsetY + chunk.offsetY);\n }\n if (chunk.fill) {\n context.restore();\n // Color does not affect ghostContext so we not set it\n }\n // Reset style\n if (chunk.style) {\n context.restore();\n ghostContext.restore();\n }\n });\n });\n context.restore();\n ghostContext.restore();\n }\n }\n _addBounds(bounds) {\n if (this.visible && this.isMeasured) {\n //if (this._textVisible) {\n const x = this._measure({\n inactive: this.inactive,\n layer: this.getLayer()\n });\n setPoint(bounds, {\n x: x.left,\n y: x.top\n });\n setPoint(bounds, {\n x: x.right,\n y: x.bottom\n });\n //}\n }\n }\n _ignoreFontWeight() {\n return /apple/i.test(navigator.vendor);\n }\n _measure(status) {\n const context = status.layer.context;\n const ghostContext = this._renderer._ghostLayer.context;\n const rtl = this.style.direction == \"rtl\";\n // Reset text info\n this._textInfo = [];\n // Init\n const oversizedBehavior = this.style.oversizedBehavior;\n const maxWidth = this.style.maxWidth;\n const truncate = $type.isNumber(maxWidth) && oversizedBehavior == \"truncate\";\n const wrap = $type.isNumber(maxWidth) && (oversizedBehavior == \"wrap\" || oversizedBehavior == \"wrap-no-break\");\n // Pre-render\n context.save();\n ghostContext.save();\n this._prerender(status, true, this._ignoreFontWeight());\n // Get default font metrix\n const refText = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 \";\n // Split up text into lines\n const lines = this.text.toString().replace(/\\r/g, \"\").split(/\\n/);\n let styleRestored = true;\n let minX = 0;\n let maxX = 0;\n // Iterate through the lines\n let offsetY = 0;\n let currentStyle;\n $array.each(lines, (line, _index) => {\n // Split up line into format/value chunks\n let chunks;\n if (line == \"\") {\n chunks = [{\n type: \"value\",\n text: \"\"\n }];\n } else {\n chunks = TextFormatter.chunk(line, false, this.style.ignoreFormatting);\n }\n while (chunks.length > 0) {\n // Init line object\n let lineInfo = {\n offsetY: offsetY,\n ascent: 0,\n width: 0,\n height: 0,\n left: 0,\n right: 0,\n textChunks: []\n };\n // Measure reference text\n const metrics = this._measureText(refText, context);\n const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n lineInfo.height = height;\n lineInfo.ascent = metrics.actualBoundingBoxAscent;\n let currentFormat;\n let currentDecoration = this.style.textDecoration;\n let currentFill;\n let currentChunkWidth;\n let skipFurtherText = false;\n let firstTextChunk = true;\n let leftoverChunks = [];\n let currentVerticalAlign;\n //let offsetX = 0;\n //let chunk;\n //while(chunk = chunks.shift()) {\n $array.eachContinue(chunks, (chunk, index) => {\n // Format chunk\n if (chunk.type == \"format\") {\n if (chunk.text == \"[/]\") {\n if (!styleRestored) {\n context.restore();\n ghostContext.restore();\n styleRestored = true;\n }\n currentFill = undefined;\n currentStyle = undefined;\n currentChunkWidth = undefined;\n currentDecoration = this.style.textDecoration;\n currentVerticalAlign = undefined;\n currentFormat = chunk.text;\n } else {\n if (!styleRestored) {\n context.restore();\n ghostContext.restore();\n }\n let format = TextFormatter.getTextStyle(chunk.text);\n const fontStyle = this._getFontStyle(format);\n context.save();\n ghostContext.save();\n context.font = fontStyle;\n currentStyle = fontStyle;\n currentFormat = chunk.text;\n if (format.textDecoration) {\n currentDecoration = format.textDecoration;\n }\n if (format.fill) {\n currentFill = format.fill;\n }\n if (format.width) {\n currentChunkWidth = $type.toNumber(format.width);\n }\n if (format.verticalAlign) {\n currentVerticalAlign = format.verticalAlign;\n }\n styleRestored = false;\n // Measure reference text after change of format\n const metrics = this._measureText(refText, context);\n const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n if (height > lineInfo.height) {\n lineInfo.height = height;\n }\n if (metrics.actualBoundingBoxAscent > lineInfo.ascent) {\n lineInfo.ascent = metrics.actualBoundingBoxAscent;\n }\n }\n }\n // Text chunk\n else if (chunk.type == \"value\" && !skipFurtherText) {\n // Measure\n const metrics = this._measureText(chunk.text, context);\n let chunkWidth = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;\n // Check for fit\n if (truncate) {\n // Break words?\n let breakWords = firstTextChunk || this.style.breakWords || false;\n // Measure ellipsis and check if it fits\n const ellipsis = this.style.ellipsis || \"\";\n const ellipsisMetrics = this._measureText(ellipsis, context);\n const ellipsisWidth = ellipsisMetrics.actualBoundingBoxLeft + ellipsisMetrics.actualBoundingBoxRight;\n // Check fit\n if (lineInfo.width + chunkWidth > maxWidth) {\n const excessWidth = maxWidth - lineInfo.width - ellipsisWidth;\n chunk.text = this._truncateText(context, chunk.text, excessWidth, breakWords);\n chunk.text += ellipsis;\n skipFurtherText = true;\n }\n } else if (wrap) {\n // Check fit\n if (lineInfo.width + chunkWidth > maxWidth) {\n const excessWidth = maxWidth - lineInfo.width;\n const tmpText = this._truncateText(context, chunk.text, excessWidth, false, firstTextChunk && this.style.oversizedBehavior != \"wrap-no-break\");\n if (tmpText == \"\") {\n // Unable to fit a single letter - hide the whole label\n this.textVisible = true;\n return false;\n }\n //skipFurtherText = true;\n //Add remaining chunks for the next line\n leftoverChunks = chunks.slice(index + 1);\n //Add remaining text of current chunk if it was forced-cut\n if ($utils.trim(tmpText) != $utils.trim(chunk.text)) {\n leftoverChunks.unshift({\n type: \"value\",\n text: chunk.text.substr(tmpText.length)\n });\n if (currentFormat) {\n leftoverChunks.unshift({\n type: \"format\",\n text: currentFormat\n });\n }\n }\n // Set current chunk (truncated)\n chunk.text = $utils.trim(tmpText);\n chunks = [];\n skipFurtherText = true;\n }\n }\n // Chunk width?\n let leftBoundMod = 1;\n let rightBoundMod = 1;\n if (currentStyle && currentChunkWidth && currentChunkWidth > chunkWidth) {\n // increase horizontal bounding boxes accordingly\n const boundsMod = chunkWidth / currentChunkWidth;\n switch (this.style.textAlign) {\n case \"right\":\n case \"end\":\n leftBoundMod = boundsMod;\n break;\n case \"center\":\n leftBoundMod = boundsMod;\n rightBoundMod = boundsMod;\n break;\n default:\n rightBoundMod = boundsMod;\n }\n chunkWidth = currentChunkWidth;\n }\n const chunkHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n if (chunkHeight > lineInfo.height) {\n lineInfo.height = chunkHeight;\n }\n if (metrics.actualBoundingBoxAscent > lineInfo.ascent) {\n lineInfo.ascent = metrics.actualBoundingBoxAscent;\n }\n lineInfo.width += chunkWidth;\n lineInfo.left += metrics.actualBoundingBoxLeft / leftBoundMod;\n lineInfo.right += metrics.actualBoundingBoxRight / rightBoundMod;\n lineInfo.textChunks.push({\n style: currentStyle,\n fill: currentFill,\n text: chunk.text,\n width: chunkWidth,\n height: chunkHeight,\n left: metrics.actualBoundingBoxLeft,\n right: metrics.actualBoundingBoxRight,\n ascent: metrics.actualBoundingBoxAscent,\n offsetX: 0,\n offsetY: 0,\n textDecoration: currentDecoration,\n verticalAlign: currentVerticalAlign\n });\n //offsetX += chunkWidth;\n firstTextChunk = false;\n }\n if (leftoverChunks) {\n //return false;\n }\n return true;\n //}\n });\n if (this.style.lineHeight instanceof Percent) {\n lineInfo.height *= this.style.lineHeight.value;\n lineInfo.ascent *= this.style.lineHeight.value;\n } else {\n lineInfo.height *= this.style.lineHeight || 1.2;\n lineInfo.ascent *= this.style.lineHeight || 1.2;\n }\n if (minX < lineInfo.left) {\n minX = lineInfo.left;\n }\n if (maxX < lineInfo.right) {\n maxX = lineInfo.right;\n }\n this._textInfo.push(lineInfo);\n //lineInfo.offsetY += lineInfo.ascent;\n offsetY += lineInfo.height;\n // Reset chunks so that it can proceed to the next line\n chunks = leftoverChunks || [];\n }\n });\n if (!styleRestored) {\n context.restore();\n ghostContext.restore();\n }\n // Adjust chunk internal offsets\n $array.each(this._textInfo, (lineInfo, _index) => {\n let currentChunkOffset = 0;\n $array.each(lineInfo.textChunks, chunk => {\n chunk.offsetX = currentChunkOffset + chunk.left - lineInfo.left;\n chunk.offsetY += lineInfo.height - lineInfo.height * (this.style.baselineRatio || 0.19);\n currentChunkOffset += chunk.width;\n if (chunk.verticalAlign) {\n switch (chunk.verticalAlign) {\n case \"super\":\n chunk.offsetY -= lineInfo.height / 2 - chunk.height / 2;\n break;\n case \"sub\":\n chunk.offsetY += chunk.height / 2;\n break;\n }\n }\n });\n });\n const bounds = {\n left: rtl ? -maxX : -minX,\n top: 0,\n right: rtl ? minX : maxX,\n bottom: offsetY\n };\n // We need to fit?\n if (oversizedBehavior !== \"none\") {\n const ratio = this._fitRatio(bounds);\n if (ratio < 1) {\n if (oversizedBehavior == \"fit\") {\n if ($type.isNumber(this.style.minScale) && ratio < this.style.minScale) {\n this.textVisible = false;\n bounds.left = 0;\n bounds.top = 0;\n bounds.right = 0;\n bounds.bottom = 0;\n } else {\n if (!this._originalScale || this._originalScale == 1) {\n this._originalScale = this.scale;\n }\n this.scale = ratio;\n this.textVisible = true;\n }\n } else if (oversizedBehavior == \"hide\") {\n this.textVisible = false;\n bounds.left = 0;\n bounds.top = 0;\n bounds.right = 0;\n bounds.bottom = 0;\n } else {\n switch (this.style.textAlign) {\n case \"right\":\n case \"end\":\n bounds.left = rtl ? maxWidth : -maxWidth;\n bounds.right = 0;\n break;\n case \"center\":\n bounds.left = -maxWidth / 2;\n bounds.right = maxWidth / 2;\n break;\n default:\n bounds.left = 0;\n bounds.right = rtl ? -maxWidth : maxWidth;\n }\n this.scale = this._originalScale || 1;\n this._originalScale = undefined;\n this.textVisible = true;\n }\n } else {\n this.scale = this._originalScale || 1;\n this._originalScale = undefined;\n this.textVisible = true;\n }\n }\n context.restore();\n ghostContext.restore();\n return bounds;\n }\n _fitRatio(bounds) {\n const maxW = this.style.maxWidth;\n const maxH = this.style.maxHeight;\n if (!$type.isNumber(maxW) && !$type.isNumber(maxH)) {\n return 1;\n }\n const w = bounds.right - bounds.left;\n const h = bounds.bottom - bounds.top;\n return Math.min(maxW / w || 1, maxH / h || 1);\n }\n _truncateText(context, text, maxWidth, breakWords = false, fallbackBreakWords = true) {\n let width;\n do {\n if (breakWords) {\n text = text.slice(0, -1);\n } else {\n let tmp = text.replace(/[^,;:!?\\\\\\/\\s​]+[,;:!?\\\\\\/\\s​]*$/g, \"\");\n if ((tmp == \"\" || tmp === text) && fallbackBreakWords) {\n breakWords = true;\n } else if (tmp == \"\") {\n return text;\n } else {\n text = tmp;\n }\n }\n const metrics = this._measureText(text, context);\n width = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight;\n } while (width > maxWidth && text != \"\");\n return text;\n }\n _measureText(text, context) {\n let metrics = context.measureText(text);\n let fakeMetrics = {};\n if (metrics.actualBoundingBoxAscent == null) {\n const div = document.createElement(\"div\");\n div.innerText = text;\n div.style.visibility = \"hidden\";\n div.style.position = \"absolute\";\n div.style.top = \"-1000000px;\";\n div.style.fontFamily = this.style.fontFamily || \"\";\n div.style.fontSize = this.style.fontSize + \"\";\n document.body.appendChild(div);\n const bbox = div.getBoundingClientRect();\n document.body.removeChild(div);\n const h = bbox.height;\n const w = metrics.width;\n let left = 0;\n let right = w;\n fakeMetrics = {\n actualBoundingBoxAscent: h,\n actualBoundingBoxDescent: 0,\n actualBoundingBoxLeft: left,\n actualBoundingBoxRight: right,\n fontBoundingBoxAscent: h,\n fontBoundingBoxDescent: 0,\n width: w\n };\n //return fake;\n } else {\n fakeMetrics = {\n actualBoundingBoxAscent: metrics.actualBoundingBoxAscent,\n actualBoundingBoxDescent: metrics.actualBoundingBoxDescent,\n actualBoundingBoxLeft: metrics.actualBoundingBoxLeft,\n actualBoundingBoxRight: metrics.actualBoundingBoxRight,\n fontBoundingBoxAscent: metrics.actualBoundingBoxAscent,\n fontBoundingBoxDescent: metrics.actualBoundingBoxDescent,\n width: metrics.width\n };\n }\n const w = metrics.width;\n switch (this.style.textAlign) {\n case \"right\":\n case \"end\":\n fakeMetrics.actualBoundingBoxLeft = w;\n fakeMetrics.actualBoundingBoxRight = 0;\n break;\n case \"center\":\n fakeMetrics.actualBoundingBoxLeft = w / 2;\n fakeMetrics.actualBoundingBoxRight = w / 2;\n break;\n default:\n fakeMetrics.actualBoundingBoxLeft = 0;\n fakeMetrics.actualBoundingBoxRight = w;\n }\n return fakeMetrics;\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasTextStyle {\n constructor() {\n //public wordWrapWidth: number = 100;\n Object.defineProperty(this, \"fill\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fillOpacity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"textAlign\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fontFamily\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fontSize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fontWeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fontStyle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"fontVariant\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"textDecoration\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowColor\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowBlur\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOffsetX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOffsetY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOpacity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n // leading?: number;\n // letterSpacing?: number;\n Object.defineProperty(this, \"lineHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: percent(120)\n });\n Object.defineProperty(this, \"baselineRatio\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0.19\n });\n // padding?: number;\n // stroke?: number;\n // strokeThickness?: number;\n // trim?: number;\n // wordWrap?: boolean;\n Object.defineProperty(this, \"direction\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"textBaseline\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"oversizedBehavior\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"none\"\n });\n Object.defineProperty(this, \"breakWords\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"ellipsis\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"…\"\n });\n Object.defineProperty(this, \"maxWidth\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"maxHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"minScale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"ignoreFormatting\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasRadialText extends CanvasText {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"textType\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"circular\"\n });\n Object.defineProperty(this, \"radius\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"startAngle\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"inside\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"orientation\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"auto\"\n });\n Object.defineProperty(this, \"kerning\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_textReversed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n _render(status) {\n switch (this.textType) {\n case \"circular\":\n this._renderCircular(status);\n break;\n default:\n super._render(status);\n break;\n }\n }\n _renderCircular(status) {\n if (this.textVisible) {\n this._prerender(status);\n const interactive = this._isInteractive(status);\n const context = status.layer.context;\n const layerDirty = status.layer.dirty;\n const ghostContext = this._renderer._ghostLayer.context;\n // Savepoint\n context.save();\n if (interactive) {\n ghostContext.save();\n }\n // We need measurements in order to properly position text for alignment\n if (!this._textInfo) {\n this._measure(status);\n }\n // Init\n let radius = this.radius || 0;\n let startAngle = this.startAngle || 0;\n let deltaAngle = 0;\n let orientation = this.orientation;\n let inward = orientation == \"auto\" ? \"auto\" : orientation == \"inward\";\n const inside = this.inside;\n const align = this.style.textAlign || \"left\";\n const kerning = this.kerning || 0;\n let clockwise = align == \"left\" ? 1 : -1;\n const shouldReverse = !this._textReversed;\n // Check if we need to invert the whole stuff\n if (inward == \"auto\") {\n // Calc max angle so we know whether we need to flip it\n let maxAngle = 0;\n let midAngle = 0;\n $array.each(this._textInfo, (line, _index) => {\n const deltaAngle = startAngle + line.width / (radius - line.height) / 2 * -clockwise;\n if (deltaAngle > maxAngle) {\n maxAngle = deltaAngle;\n }\n });\n if (align == \"left\") {\n midAngle = (maxAngle + deltaAngle / 2) * $math.DEGREES;\n } else if (align == \"right\") {\n midAngle = (maxAngle - deltaAngle / 2) * $math.DEGREES;\n } else {\n midAngle = startAngle * $math.DEGREES;\n }\n midAngle = $math.normalizeAngle(midAngle);\n inward = midAngle >= 270 || midAngle <= 90;\n }\n if (inward == true && shouldReverse) {\n this._textInfo.reverse();\n this._textReversed = true;\n }\n // if ((inward == false && align == \"left\") || (inward == true && align == \"right\")) {\n // \tclockwise *= -1;\n // }\n // Process text info produced by _measure()\n $array.each(this._textInfo, (line, _index) => {\n const textHeight = line.height;\n // Adjust radius (for `inside = false`)\n // Radius adjustment for `inside = false` is below the line calculation\n if (!inside) {\n radius += textHeight;\n }\n // Reverse letters if we're painting them counter-clockwise\n if ((clockwise == -1 && inward || clockwise == 1 && !inward) && shouldReverse) {\n line.textChunks.reverse();\n }\n // Init angles\n let lineStartAngle = startAngle;\n deltaAngle = 0;\n // Adjust for center-align\n if (align == \"center\") {\n lineStartAngle += line.width / (radius - textHeight) / 2 * -clockwise;\n deltaAngle = lineStartAngle - startAngle;\n }\n // if (inward == \"auto\") {\n // \tlet midAngle;\n // \tif (align == \"left\") {\n // \t\tmidAngle = (lineStartAngle + deltaAngle / 2) * $math.DEGREES;\n // \t}\n // \telse if () {\n // \t\tmidAngle = (lineStartAngle - deltaAngle / 2) * $math.DEGREES;\n // \t}\n // \tinward = (midAngle >= 270) || (midAngle <= 90);\n // }\n // Rotate letters if they are facing outward\n lineStartAngle += Math.PI * (inward ? 0 : 1); // Rotate 180 if outward\n // Savepoint\n context.save();\n if (interactive) {\n ghostContext.save();\n }\n // Assume starting angle\n context.rotate(lineStartAngle);\n if (interactive) {\n ghostContext.rotate(lineStartAngle);\n }\n let angleShift = 0;\n $array.each(line.textChunks, (chunk, _index) => {\n // Draw the letter\n const char = chunk.text;\n const charWidth = chunk.width;\n // Rotate half a letter\n angleShift = charWidth / 2 / (radius - textHeight) * clockwise;\n context.rotate(angleShift);\n if (interactive) {\n ghostContext.rotate(angleShift);\n }\n // Set style\n if (chunk.style) {\n context.save();\n ghostContext.save();\n context.font = chunk.style;\n if (interactive) {\n ghostContext.font = chunk.style;\n }\n }\n if (chunk.fill) {\n context.save();\n context.fillStyle = chunk.fill.toCSS();\n // Color does not affect ghostContext so we not set it\n }\n // Center letters\n context.textBaseline = \"middle\";\n context.textAlign = \"center\";\n if (interactive) {\n ghostContext.textBaseline = \"middle\";\n ghostContext.textAlign = \"center\";\n }\n // Plop the letter\n if (layerDirty) {\n context.fillText(char, 0, (inward ? 1 : -1) * (0 - radius + textHeight / 2));\n }\n if (interactive) {\n ghostContext.fillText(char, 0, (inward ? 1 : -1) * (0 - radius + textHeight / 2));\n }\n if (chunk.fill) {\n context.restore();\n // Color does not affect ghostContext so we not set it\n }\n // Reset style\n if (chunk.style) {\n context.restore();\n ghostContext.restore();\n }\n // Rotate half a letter and add spacing\n angleShift = (charWidth / 2 + kerning) / (radius - textHeight) * clockwise;\n context.rotate(angleShift);\n if (interactive) {\n ghostContext.rotate(angleShift);\n }\n });\n // Restore angle\n context.restore();\n if (interactive) {\n ghostContext.restore();\n }\n // Adjust radius (for `inside = true`)\n if (inside) {\n radius -= textHeight;\n }\n });\n // Restore\n context.restore();\n if (interactive) {\n ghostContext.restore();\n }\n }\n }\n _measure(status) {\n switch (this.textType) {\n case \"circular\":\n return this._measureCircular(status);\n default:\n return super._measure(status);\n }\n }\n _measureCircular(status) {\n const context = status.layer.context;\n const ghostContext = this._renderer._ghostLayer.context;\n const rtl = this.style.direction == \"rtl\";\n const oversizedBehavior = this.style.oversizedBehavior;\n const maxWidth = this.style.maxWidth;\n const truncate = $type.isNumber(maxWidth) && oversizedBehavior == \"truncate\";\n const ellipsis = this.style.ellipsis || \"\";\n let ellipsisMetrics;\n //const wrap = $type.isNumber(maxWidth) && (oversizedBehavior == \"wrap\" || oversizedBehavior == \"wrap-no-break\");\n // Reset text info\n this.textVisible = true;\n this._textInfo = [];\n this._textReversed = false;\n // Pre-render\n context.save();\n ghostContext.save();\n this._prerender(status, true);\n // Split up text into lines\n const lines = this.text.toString().replace(/\\r/g, \"\").split(/\\n/);\n let styleRestored = true;\n let totalWidth = 0;\n // Iterate through the lines\n let offsetY = 0;\n $array.each(lines, (line, _index) => {\n // Split up line into format/value chunks\n let chunks = TextFormatter.chunk(line, false, this.style.ignoreFormatting);\n // Init line object\n let lineInfo = {\n offsetY: offsetY,\n ascent: 0,\n width: 0,\n height: 0,\n left: 0,\n right: 0,\n textChunks: []\n };\n let currentStyle;\n let currentFill;\n let currentChunkWidth;\n //while(chunk = chunks.shift()) {\n $array.each(chunks, (chunk, _index) => {\n // Format chunk\n if (chunk.type == \"format\") {\n if (chunk.text == \"[/]\") {\n if (!styleRestored) {\n context.restore();\n ghostContext.restore();\n styleRestored = true;\n }\n currentFill = undefined;\n currentStyle = undefined;\n currentChunkWidth = undefined;\n } else {\n let format = TextFormatter.getTextStyle(chunk.text);\n const fontStyle = this._getFontStyle(format);\n context.save();\n ghostContext.save();\n context.font = fontStyle;\n currentStyle = fontStyle;\n if (format.fill) {\n currentFill = format.fill;\n }\n if (format.width) {\n currentChunkWidth = $type.toNumber(format.width);\n }\n styleRestored = false;\n }\n if (truncate) {\n ellipsisMetrics = this._measureText(ellipsis, context);\n }\n }\n // Text format\n else if (chunk.type == \"value\") {\n // Measure each letter\n let chars = chunk.text.match(/./ug) || [];\n if (rtl) {\n chars = $utils.splitString(chunk.text);\n chars.reverse();\n }\n for (let i = 0; i < chars.length; i++) {\n const char = chars[i];\n // Measure\n const metrics = this._measureText(char, context);\n let chunkWidth = metrics.width;\n // Chunk width?\n if (currentStyle && currentChunkWidth && currentChunkWidth > chunkWidth) {\n chunkWidth = currentChunkWidth;\n }\n const chunkHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n if (chunkHeight > lineInfo.height) {\n lineInfo.height = chunkHeight;\n }\n if (metrics.actualBoundingBoxAscent > lineInfo.ascent) {\n lineInfo.ascent = metrics.actualBoundingBoxAscent;\n }\n totalWidth += chunkWidth;\n // Handle oversized behavior\n if (truncate) {\n // Measure ellipsis and check if it fits\n if (!ellipsisMetrics) {\n ellipsisMetrics = this._measureText(ellipsis, context);\n }\n const ellipsisWidth = ellipsisMetrics.actualBoundingBoxLeft + ellipsisMetrics.actualBoundingBoxRight;\n //totalWidth += ellipsisWidth;\n if (totalWidth + ellipsisWidth > maxWidth) {\n if (lineInfo.textChunks.length == 1) {\n this.textVisible = false;\n } else {\n lineInfo.width += ellipsisWidth;\n lineInfo.left += ellipsisMetrics.actualBoundingBoxLeft;\n lineInfo.right += ellipsisMetrics.actualBoundingBoxRight;\n lineInfo.textChunks.push({\n style: currentStyle,\n fill: currentFill,\n text: ellipsis,\n width: ellipsisWidth,\n height: chunkHeight + ellipsisMetrics.actualBoundingBoxDescent,\n left: ellipsisMetrics.actualBoundingBoxLeft,\n right: ellipsisMetrics.actualBoundingBoxRight,\n ascent: ellipsisMetrics.actualBoundingBoxAscent,\n offsetX: 0,\n offsetY: chunkHeight,\n textDecoration: undefined\n });\n }\n break;\n }\n }\n lineInfo.width += chunkWidth;\n lineInfo.left += metrics.actualBoundingBoxLeft;\n lineInfo.right += metrics.actualBoundingBoxRight;\n lineInfo.textChunks.push({\n style: currentStyle,\n fill: currentFill,\n text: char,\n width: chunkWidth,\n height: chunkHeight + metrics.actualBoundingBoxDescent,\n left: metrics.actualBoundingBoxLeft,\n right: metrics.actualBoundingBoxRight,\n ascent: metrics.actualBoundingBoxAscent,\n offsetX: 0,\n offsetY: chunkHeight,\n textDecoration: undefined\n });\n if (rtl) {\n // @todo still needed?\n //break;\n }\n }\n }\n });\n if (this.style.lineHeight instanceof Percent) {\n lineInfo.height *= this.style.lineHeight.value;\n } else {\n lineInfo.height *= this.style.lineHeight || 1.2;\n }\n this._textInfo.push(lineInfo);\n //lineInfo.offsetY += lineInfo.ascent;\n offsetY += lineInfo.height;\n });\n if (!styleRestored) {\n context.restore();\n ghostContext.restore();\n }\n if (oversizedBehavior == \"hide\" && totalWidth > maxWidth) {\n this.textVisible = false;\n }\n // Adjust chunk internal offsets\n $array.each(this._textInfo, lineInfo => {\n $array.each(lineInfo.textChunks, chunk => {\n chunk.offsetY += Math.round((lineInfo.height - chunk.height + (lineInfo.ascent - chunk.ascent)) / 2);\n });\n });\n context.restore();\n ghostContext.restore();\n return {\n left: 0,\n top: 0,\n right: 0,\n bottom: 0\n };\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasImage extends CanvasDisplayObject {\n constructor(renderer, image) {\n super(renderer);\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"image\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tainted\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowColor\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowBlur\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOffsetX\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOffsetY\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"shadowOpacity\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_imageMask\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.image = image;\n }\n _dispose() {\n super._dispose();\n if (this._imageMask) {\n clearCanvas(this._imageMask);\n }\n }\n getLocalBounds() {\n if (!this._localBounds) {\n let w = 0;\n let h = 0;\n if (this.width) {\n w = this.width;\n }\n if (this.height) {\n h = this.height;\n }\n this._localBounds = {\n left: 0,\n top: 0,\n right: w,\n bottom: h\n };\n this._addBounds(this._localBounds);\n }\n return this._localBounds;\n }\n _render(status) {\n super._render(status);\n if (this.image) {\n if (this.tainted === undefined) {\n this.tainted = isTainted(this.image);\n status.layer.tainted = true;\n }\n if (this.tainted && this._renderer._omitTainted) {\n return;\n }\n if (status.layer.dirty) {\n if (this.shadowColor) {\n status.layer.context.shadowColor = this.shadowColor.toCSS(this.shadowOpacity || 1);\n }\n if (this.shadowBlur) {\n status.layer.context.shadowBlur = this.shadowBlur;\n }\n if (this.shadowOffsetX) {\n status.layer.context.shadowOffsetX = this.shadowOffsetX;\n }\n if (this.shadowOffsetY) {\n status.layer.context.shadowOffsetY = this.shadowOffsetY;\n }\n // TODO should this round ?\n const width = this.width || this.image.naturalWidth;\n const height = this.height || this.image.naturalHeight;\n status.layer.context.drawImage(this.image, 0, 0, width, height);\n }\n if (this.interactive && this._isInteractive(status)) {\n const mask = this._getMask(this.image);\n this._renderer._ghostLayer.context.drawImage(mask, 0, 0);\n }\n }\n }\n clear() {\n super.clear();\n this.image = undefined;\n this._imageMask = undefined;\n }\n _getMask(image) {\n if (this._imageMask === undefined) {\n // TODO should this round ?\n const width = this.width || image.naturalWidth;\n const height = this.height || image.naturalHeight;\n // We need to create a second canvas because destination-in clears out the entire canvas\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const context = canvas.getContext(\"2d\");\n context.imageSmoothingEnabled = false;\n context.fillStyle = this._getColorId();\n context.fillRect(0, 0, width, height);\n if (!isTainted(image)) {\n context.globalCompositeOperation = \"destination-in\";\n context.drawImage(image, 0, 0, width, height);\n }\n this._imageMask = canvas;\n }\n return this._imageMask;\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasRendererEvent {\n constructor(event, originalPoint, point, bbox) {\n Object.defineProperty(this, \"event\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: event\n });\n Object.defineProperty(this, \"originalPoint\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: originalPoint\n });\n Object.defineProperty(this, \"point\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: point\n });\n Object.defineProperty(this, \"bbox\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: bbox\n });\n Object.defineProperty(this, \"id\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"simulated\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"native\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n if ($utils.supports(\"touchevents\") && event instanceof Touch) {\n this.id = event.identifier;\n } else {\n this.id = null;\n }\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasRenderer extends ArrayDisposer {\n constructor(resolution) {\n super();\n Object.defineProperty(this, \"view\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: document.createElement(\"div\")\n });\n Object.defineProperty(this, \"_layerDom\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: document.createElement(\"div\")\n });\n Object.defineProperty(this, \"layers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_dirtyLayers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"defaultLayer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this.getLayer(0)\n });\n Object.defineProperty(this, \"_ghostLayer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new GhostLayer()\n });\n Object.defineProperty(this, \"_patternCanvas\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: document.createElement(\"canvas\")\n });\n Object.defineProperty(this, \"_patternContext\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._patternCanvas.getContext(\"2d\")\n });\n Object.defineProperty(this, \"_realWidth\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_realHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_calculatedWidth\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_calculatedHeight\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"resolution\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"interactionsEnabled\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_listeners\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_colorId\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_colorMap\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_forceInteractive\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_omitTainted\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n // TODO this should store the Id as well\n Object.defineProperty(this, \"_hovering\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new Set()\n });\n Object.defineProperty(this, \"_dragging\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_mousedown\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_lastPointerMoveEvent\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tapToActivate\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"tapToActivateTimeout\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 3000\n });\n Object.defineProperty(this, \"_touchActive\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_touchActiveTimeout\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n if (resolution == null) {\n this.resolution = window.devicePixelRatio;\n } else {\n this.resolution = resolution;\n }\n this.view.style.position = \"absolute\";\n this.view.setAttribute(\"aria-hidden\", \"true\");\n this.view.appendChild(this._layerDom);\n this._disposers.push(new Disposer(() => {\n $object.each(this._events, (_key, events) => {\n events.disposer.dispose();\n });\n $array.each(this.layers, layer => {\n clearCanvas(layer.view);\n if (layer.exportableView) {\n clearCanvas(layer.exportableView);\n }\n });\n clearCanvas(this._ghostLayer.view);\n clearCanvas(this._patternCanvas);\n }));\n /*\r\n this._disposers.push($utils.addEventListener(this._ghostLayer.view, \"click\", (originalEvent: MouseEvent) => {\r\n const event = this.getEvent(originalEvent);\r\n const target = this._getHitTarget(event.originalPoint, event.bbox);\r\n console.debug(target);\r\n }));\r\n */\n // Monitor for possible pixel ratio changes (when page is zoomed)\n this._disposers.push($utils.onZoom(() => {\n if (resolution == null) {\n this.resolution = window.devicePixelRatio;\n }\n }));\n // We need this in order top prevent default touch gestures when dragging\n // draggable elements\n if ($utils.supports(\"touchevents\")) {\n const listener = ev => {\n if (this._dragging.length !== 0) {\n $array.eachContinue(this._dragging, item => {\n if (item.value.shouldCancelTouch()) {\n ev.preventDefault();\n return false;\n }\n return true;\n });\n }\n // If touch down happends, delay touch out\n if (this._touchActiveTimeout) {\n this._delayTouchDeactivate();\n }\n };\n this._disposers.push($utils.addEventListener(window, \"touchstart\", listener, {\n passive: false\n }));\n this._disposers.push($utils.addEventListener(this.view, \"touchstart\", listener, {\n passive: false\n }));\n this._disposers.push($utils.addEventListener(this.view, \"touchmove\", () => {\n // If touch is moving, delay touch out\n if (this._touchActiveTimeout) {\n this._delayTouchDeactivate();\n }\n }, {\n passive: true\n }));\n this._disposers.push($utils.addEventListener(window, \"click\", _ev => {\n this._touchActive = false;\n }, {\n passive: true\n }));\n this._disposers.push($utils.addEventListener(this.view, \"click\", _ev => {\n window.setTimeout(() => {\n this._touchActive = true;\n this._delayTouchDeactivate();\n }, 100);\n }, {\n passive: true\n }));\n }\n // Prevent scrolling of the window when hovering on \"wheelable\" object\n if ($utils.supports(\"wheelevents\")) {\n this._disposers.push($utils.addEventListener(this.view, \"wheel\", ev => {\n let prevent = false;\n this._hovering.forEach(obj => {\n if (obj.wheelable) {\n prevent = true;\n return false;\n }\n });\n if (prevent) {\n ev.preventDefault();\n }\n }, {\n passive: false\n }));\n }\n }\n /*protected _mouseMoveThrottler: Throttler = new Throttler(() => {\r\n this._dispatchGlobalMousemove(this._lastPointerMoveEvent.event, this._lastPointerMoveEvent.native);\r\n });\r\n */\n resetImageArray() {\n this._ghostLayer.imageArray = undefined;\n }\n _delayTouchDeactivate() {\n if (this._touchActiveTimeout) {\n clearTimeout(this._touchActiveTimeout);\n }\n if (this.tapToActivateTimeout > 0) {\n this._touchActiveTimeout = window.setTimeout(() => {\n this._touchActive = false;\n }, this.tapToActivateTimeout);\n }\n }\n get debugGhostView() {\n return !!this._ghostLayer.view.parentNode;\n }\n set debugGhostView(value) {\n if (value) {\n if (!this._ghostLayer.view.parentNode) {\n this.view.appendChild(this._ghostLayer.view);\n }\n } else {\n if (this._ghostLayer.view.parentNode) {\n this._ghostLayer.view.parentNode.removeChild(this._ghostLayer.view);\n }\n }\n }\n createLinearGradient(x1, y1, x2, y2) {\n return this.defaultLayer.context.createLinearGradient(x1, y1, x2, y2);\n }\n createRadialGradient(x1, y1, radius1, x2, y2, radius2) {\n return this.defaultLayer.context.createRadialGradient(x1, y1, radius1, x2, y2, radius2);\n }\n createPattern(graphics, background, repetition, width, height) {\n // const patternCanvas = document.createElement(\"canvas\");\n // const patternContext = patternCanvas.getContext(\"2d\")!;\n // patternCanvas.width = width;\n // patternCanvas.height = height;\n // if (fill) {\n // \tpatternContext.fillStyle = fill.toCSS();\n // \tpatternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);\n // }\n // const layer = {\n // \tview: patternCanvas,\n // \tcontext: patternContext,\n // \tvisible: true,\n // \torder: 0,\n // \twidth: width,\n // \theight: height,\n // \tdirty: true\n // };\n // // patternContext.arc(0, 0, 50, 0, .5 * Math.PI);\n // // patternContext.stroke();\n // image.targetLayer = layer;\n // image.render(layer);\n //this._layerDom.appendChild(patternCanvas);\n this._patternCanvas.width = width;\n this._patternCanvas.height = height;\n this._patternContext.clearRect(0, 0, width, height);\n // patternCanvas.style.width = width * this.resolution + \"px\";\n // patternCanvas.style.height = height * this.resolution + \"px\";\n background.renderDetached(this._patternContext);\n graphics.renderDetached(this._patternContext);\n return this._patternContext.createPattern(this._patternCanvas, repetition);\n }\n makeContainer() {\n return new CanvasContainer(this);\n }\n makeGraphics() {\n return new CanvasGraphics(this);\n }\n makeText(text, style) {\n return new CanvasText(this, text, style);\n }\n makeTextStyle() {\n return new CanvasTextStyle();\n }\n makeRadialText(text, style) {\n return new CanvasRadialText(this, text, style);\n }\n makePicture(image) {\n return new CanvasImage(this, image);\n }\n resizeLayer(layer) {\n layer.resize(this._calculatedWidth, this._calculatedHeight, this._calculatedWidth, this._calculatedHeight, this.resolution);\n }\n resizeGhost() {\n this._ghostLayer.resize(this._calculatedWidth, this._calculatedHeight, this._calculatedWidth, this._calculatedHeight, this.resolution);\n }\n resize(realWidth, realHeight, calculatedWidth, calculatedHeight) {\n this._realWidth = realWidth;\n this._realHeight = realHeight;\n this._calculatedWidth = calculatedWidth;\n this._calculatedHeight = calculatedHeight;\n $array.each(this.layers, layer => {\n if (layer) {\n layer.dirty = true;\n this.resizeLayer(layer);\n }\n });\n this.resizeGhost();\n this.view.style.width = calculatedWidth + \"px\";\n this.view.style.height = calculatedHeight + \"px\";\n }\n createDetachedLayer(willReadFrequently = false) {\n const view = document.createElement(\"canvas\");\n const context = view.getContext(\"2d\", {\n willReadFrequently: willReadFrequently\n });\n const layer = new CanvasLayer(view, context);\n view.style.position = \"absolute\";\n view.style.top = \"0px\";\n view.style.left = \"0px\";\n return layer;\n }\n getLayerByOrder(order) {\n const layers = this.layers;\n const length = layers.length;\n for (let i = 0; i < length; i++) {\n const layer = layers[i];\n if (layer.order == order) {\n return layer;\n }\n }\n }\n getLayer(order, visible = true) {\n let existingLayer = this.getLayerByOrder(order);\n if (existingLayer) {\n return existingLayer;\n }\n const layer = this.createDetachedLayer(order == 99);\n layer.order = order;\n layer.visible = visible;\n layer.view.className = \"am5-layer-\" + order;\n if (layer.visible) {\n this.resizeLayer(layer);\n }\n const layers = this.layers;\n layers.push(layer);\n layers.sort((a, b) => {\n if (a.order > b.order) {\n return 1;\n } else if (a.order < b.order) {\n return -1;\n } else {\n return 0;\n }\n });\n const length = layers.length;\n const layerIndex = $array.indexOf(layers, layer);\n let next;\n for (let i = layerIndex + 1; i < length; i++) {\n if (layers[i].visible) {\n next = layers[i];\n break;\n }\n }\n if (layer.visible) {\n if (next === undefined) {\n this._layerDom.appendChild(layer.view);\n } else {\n this._layerDom.insertBefore(layer.view, next.view);\n }\n }\n return layer;\n }\n render(root) {\n this._dirtyLayers.length = 0;\n $array.each(this.layers, layer => {\n if (layer) {\n if (layer.dirty && layer.visible) {\n this._dirtyLayers.push(layer);\n layer.clear();\n }\n }\n });\n this._ghostLayer.clear();\n root.render({\n inactive: null,\n layer: this.defaultLayer\n });\n this._ghostLayer.context.restore();\n //setTimeout(() => {\n // Remove this after the Chrome bug is fixed:\n // https://bugs.chromium.org/p/chromium/issues/detail?id=1279394\n $array.each(this.layers, layer => {\n if (layer) {\n const context = layer.context;\n context.beginPath();\n context.moveTo(0, 0);\n context.stroke();\n }\n });\n $array.each(this._dirtyLayers, layer => {\n layer.context.restore();\n layer.dirty = false;\n });\n //}, 100)\n if (this._hovering.size && this._lastPointerMoveEvent) {\n const {\n events,\n target,\n native\n } = this._lastPointerMoveEvent;\n //this._mouseMoveThrottler.run();\n $array.each(events, event => {\n this._dispatchGlobalMousemove(event, target, native);\n });\n }\n }\n paintId(obj) {\n const id = distributeId(++this._colorId);\n const color = Color.fromHex(id).toCSS();\n this._colorMap[color] = obj;\n return color;\n }\n _removeObject(obj) {\n if (obj._colorId !== undefined) {\n delete this._colorMap[obj._colorId];\n }\n }\n // protected _identifyObjectByColor(colorId: number): CanvasDisplayObject | undefined {\n // \treturn this._colorMap[colorId];\n // }\n _adjustBoundingBox(bbox) {\n const margin = this._ghostLayer.margin;\n return new DOMRect(-margin.left, -margin.top, bbox.width + margin.left + margin.right, bbox.height + margin.top + margin.bottom);\n }\n getEvent(originalEvent, adjustPoint = true) {\n const bbox = this.view.getBoundingClientRect();\n const x = originalEvent.clientX || 0;\n const y = originalEvent.clientY || 0;\n const widthScale = this._calculatedWidth / this._realWidth;\n const heightScale = this._calculatedHeight / this._realHeight;\n const originalPoint = {\n x: x - bbox.left,\n y: y - bbox.top\n };\n const point = {\n x: (x - (adjustPoint ? bbox.left : 0)) * widthScale,\n y: (y - (adjustPoint ? bbox.top : 0)) * heightScale\n };\n return new CanvasRendererEvent(originalEvent, originalPoint, point, this._adjustBoundingBox(bbox));\n }\n _getHitTarget(point, bbox, target) {\n if (bbox.width === 0 || bbox.height === 0 || point.x < bbox.left || point.x > bbox.right || point.y < bbox.top || point.y > bbox.bottom) {\n return;\n }\n if (!target || !this._layerDom.contains(target)) {\n return;\n }\n const pixel = this._ghostLayer.getImageData(point, bbox);\n if (pixel.data[0] === 0 && pixel.data[1] === 0 && pixel.data[2] === 0) {\n return false;\n }\n const colorId = Color.fromRGB(pixel.data[0], pixel.data[1], pixel.data[2]).toCSS();\n const hit = this._colorMap[colorId];\n return hit;\n }\n getObjectAtPoint(point) {\n const data = this._ghostLayer.getImageArray(point);\n if (data[0] === 0 && data[1] === 0 && data[2] === 0) {\n return undefined;\n }\n const colorId = Color.fromRGB(data[0], data[1], data[2]).toCSS();\n const hit = this._colorMap[colorId];\n return hit;\n }\n _withEvents(key, f) {\n const events = this._events[key];\n if (events !== undefined) {\n events.dispatching = true;\n try {\n f(events);\n } finally {\n events.dispatching = false;\n if (events.cleanup) {\n events.cleanup = false;\n $array.keepIf(events.callbacks, callback => {\n return !callback.disposed;\n });\n if (events.callbacks.length === 0) {\n events.disposer.dispose();\n delete this._events[key];\n }\n }\n }\n }\n }\n _dispatchEventAll(key, event) {\n if (!this.interactionsEnabled) {\n return;\n }\n this._withEvents(key, events => {\n $array.each(events.callbacks, callback => {\n if (!callback.disposed) {\n callback.callback.call(callback.context, event);\n }\n });\n });\n }\n _dispatchEvent(key, target, event) {\n if (!this.interactionsEnabled) {\n return false;\n }\n let dispatched = false;\n this._withEvents(key, events => {\n $array.each(events.callbacks, callback => {\n if (!callback.disposed && callback.object === target) {\n callback.callback.call(callback.context, event);\n dispatched = true;\n }\n });\n });\n return dispatched;\n }\n _dispatchMousedown(originalEvent, originalTarget) {\n const button = originalEvent.button;\n if (button != 0 && button != 2 && button != 1 && button !== undefined) {\n // Ignore non-primary mouse buttons\n return;\n }\n const event = this.getEvent(originalEvent);\n const target = this._getHitTarget(event.originalPoint, event.bbox, originalTarget);\n if (target) {\n const id = event.id;\n let dragged = false;\n eachTargets(target, obj => {\n const info = {\n id: id,\n value: obj\n };\n this._mousedown.push(info);\n if (!dragged && this._dispatchEvent(\"pointerdown\", obj, event)) {\n // Only dispatch the first element which matches\n dragged = true;\n const has = this._dragging.some(x => {\n return x.value === obj && x.id === id;\n });\n if (!has) {\n this._dragging.push(info);\n }\n }\n return true;\n });\n }\n }\n _dispatchGlobalMousemove(originalEvent, originalTarget, native) {\n const event = this.getEvent(originalEvent);\n const target = this._getHitTarget(event.originalPoint, event.bbox, originalTarget);\n event.native = native;\n if (target) {\n this._hovering.forEach(obj => {\n if (!obj.contains(target)) {\n this._hovering.delete(obj);\n if (obj.cursorOverStyle) {\n $utils.setStyle(document.body, \"cursor\", obj._replacedCursorStyle);\n }\n this._dispatchEvent(\"pointerout\", obj, event);\n }\n });\n if (event.native) {\n eachTargets(target, obj => {\n if (!this._hovering.has(obj)) {\n this._hovering.add(obj);\n if (obj.cursorOverStyle) {\n obj._replacedCursorStyle = $utils.getStyle(document.body, \"cursor\");\n $utils.setStyle(document.body, \"cursor\", obj.cursorOverStyle);\n }\n this._dispatchEvent(\"pointerover\", obj, event);\n }\n return true;\n });\n }\n //} else if (target === false) {\n } else {\n this._hovering.forEach(obj => {\n if (obj.cursorOverStyle) {\n $utils.setStyle(document.body, \"cursor\", obj._replacedCursorStyle);\n }\n this._dispatchEvent(\"pointerout\", obj, event);\n });\n this._hovering.clear();\n }\n this._dispatchEventAll(\"globalpointermove\", event);\n }\n removeHovering(graphics) {\n this._hovering.delete(graphics);\n if (graphics.cursorOverStyle) {\n $utils.setStyle(document.body, \"cursor\", graphics._replacedCursorStyle);\n }\n }\n _dispatchGlobalMouseup(originalEvent, native) {\n const event = this.getEvent(originalEvent);\n event.native = native;\n //const target = this._getHitTarget(event.originalPoint);\n this._dispatchEventAll(\"globalpointerup\", event);\n }\n _dispatchDragMove(originalEvent) {\n if (this._dragging.length !== 0) {\n const event = this.getEvent(originalEvent);\n const id = event.id;\n this._dragging.forEach(obj => {\n if (obj.id === id) {\n this._dispatchEvent(\"pointermove\", obj.value, event);\n }\n });\n }\n }\n _dispatchDragEnd(originalEvent, originalTarget) {\n const button = originalEvent.button;\n let clickevent;\n if (button == 0 || button === undefined) {\n clickevent = \"click\";\n } else if (button == 2) {\n clickevent = \"rightclick\";\n } else if (button == 1) {\n clickevent = \"middleclick\";\n } else {\n // Ignore non-primary mouse buttons\n return;\n }\n const event = this.getEvent(originalEvent);\n const id = event.id;\n if (this._mousedown.length !== 0) {\n const target = this._getHitTarget(event.originalPoint, event.bbox, originalTarget);\n if (target) {\n this._mousedown.forEach(obj => {\n if (obj.id === id && obj.value.contains(target)) {\n this._dispatchEvent(clickevent, obj.value, event);\n }\n });\n }\n this._mousedown.length = 0;\n }\n if (this._dragging.length !== 0) {\n this._dragging.forEach(obj => {\n if (obj.id === id) {\n this._dispatchEvent(\"pointerup\", obj.value, event);\n }\n });\n this._dragging.length = 0;\n }\n }\n _dispatchDoubleClick(originalEvent, originalTarget) {\n const event = this.getEvent(originalEvent);\n const target = this._getHitTarget(event.originalPoint, event.bbox, originalTarget);\n if (target) {\n eachTargets(target, obj => {\n if (this._dispatchEvent(\"dblclick\", obj, event)) {\n return false;\n } else {\n return true;\n }\n });\n }\n }\n _dispatchWheel(originalEvent, originalTarget) {\n const event = this.getEvent(originalEvent);\n const target = this._getHitTarget(event.originalPoint, event.bbox, originalTarget);\n if (target) {\n eachTargets(target, obj => {\n if (this._dispatchEvent(\"wheel\", obj, event)) {\n return false;\n } else {\n return true;\n }\n });\n }\n }\n _makeSharedEvent(key, f) {\n if (this._listeners[key] === undefined) {\n const listener = f();\n this._listeners[key] = new CounterDisposer(() => {\n delete this._listeners[key];\n listener.dispose();\n });\n }\n return this._listeners[key].increment();\n }\n _onPointerEvent(name, f) {\n let native = false;\n let timer = null;\n function clear() {\n timer = null;\n native = false;\n }\n return new MultiDisposer([new Disposer(() => {\n if (timer !== null) {\n clearTimeout(timer);\n }\n clear();\n }), $utils.addEventListener(this.view, $utils.getRendererEvent(name), _ => {\n native = true;\n if (timer !== null) {\n clearTimeout(timer);\n }\n timer = window.setTimeout(clear, 0);\n }), onPointerEvent(window, name, (ev, target) => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n f(ev, target, native);\n native = false;\n })]);\n }\n // This ensures that only a single DOM event is added (e.g. only a single mousemove event listener)\n _initEvent(key) {\n switch (key) {\n case \"globalpointermove\":\n case \"pointerover\":\n case \"pointerout\":\n return this._makeSharedEvent(\"pointermove\", () => {\n const listener = (events, target, native) => {\n this._lastPointerMoveEvent = {\n events,\n target,\n native\n };\n $array.each(events, event => {\n this._dispatchGlobalMousemove(event, target, native);\n });\n };\n return new MultiDisposer([this._onPointerEvent(\"pointerdown\", listener), this._onPointerEvent(\"pointermove\", listener)]);\n });\n case \"globalpointerup\":\n return this._makeSharedEvent(\"pointerup\", () => {\n const mouseup = this._onPointerEvent(\"pointerup\", (events, target, native) => {\n $array.each(events, event => {\n this._dispatchGlobalMouseup(event, native);\n });\n this._lastPointerMoveEvent = {\n events,\n target,\n native\n };\n });\n const pointercancel = this._onPointerEvent(\"pointercancel\", (events, target, native) => {\n $array.each(events, event => {\n this._dispatchGlobalMouseup(event, native);\n });\n this._lastPointerMoveEvent = {\n events,\n target,\n native\n };\n });\n return new Disposer(() => {\n mouseup.dispose();\n pointercancel.dispose();\n });\n });\n case \"click\":\n case \"rightclick\":\n case \"middleclick\":\n case \"pointerdown\":\n /*\r\n return this._makeSharedEvent(\"pointerdown\", () => {\r\n return this._onPointerEvent(\"pointerdown\", (event, target, native) => {\r\n this._lastPointerMoveEvent = { event, target, native };\r\n this._dispatchMousedown(event)\r\n });\r\n });\r\n */\n case \"pointermove\":\n case \"pointerup\":\n return this._makeSharedEvent(\"pointerdown\", () => {\n //const throttler = new Throttler();\n const mousedown = this._onPointerEvent(\"pointerdown\", (events, target) => {\n $array.each(events, ev => {\n this._dispatchMousedown(ev, target);\n });\n });\n // TODO handle throttling properly for multitouch\n const mousemove = this._onPointerEvent(\"pointermove\", ev => {\n //throttler.throttle(() => {\n $array.each(ev, ev => {\n this._dispatchDragMove(ev);\n });\n //});\n });\n const mouseup = this._onPointerEvent(\"pointerup\", (ev, target) => {\n $array.each(ev, ev => {\n this._dispatchDragEnd(ev, target);\n });\n });\n const pointercancel = this._onPointerEvent(\"pointercancel\", (ev, target) => {\n $array.each(ev, ev => {\n this._dispatchDragEnd(ev, target);\n });\n });\n return new Disposer(() => {\n mousedown.dispose();\n mousemove.dispose();\n mouseup.dispose();\n pointercancel.dispose();\n });\n });\n case \"dblclick\":\n return this._makeSharedEvent(\"dblclick\", () => {\n return this._onPointerEvent(\"dblclick\", (ev, target) => {\n $array.each(ev, ev => {\n this._dispatchDoubleClick(ev, target);\n });\n });\n });\n case \"wheel\":\n return this._makeSharedEvent(\"wheel\", () => {\n return $utils.addEventListener(this.view, $utils.getRendererEvent(\"wheel\"), event => {\n this._dispatchWheel(event, $utils.getEventTarget(event));\n }, {\n passive: false\n });\n });\n }\n }\n _addEvent(object, key, callback, context) {\n let events = this._events[key];\n if (events === undefined) {\n events = this._events[key] = {\n disposer: this._initEvent(key),\n callbacks: [],\n dispatching: false,\n cleanup: false\n };\n }\n const listener = {\n object,\n context,\n callback,\n disposed: false\n };\n events.callbacks.push(listener);\n return new Disposer(() => {\n listener.disposed = true;\n if (events.dispatching) {\n events.cleanup = true;\n } else {\n $array.removeFirst(events.callbacks, listener);\n if (events.callbacks.length === 0) {\n events.disposer.dispose();\n delete this._events[key];\n }\n }\n });\n }\n getCanvas(root, options) {\n // Make sure everything is rendered\n this.render(root);\n if (!options) {\n options = {};\n }\n let scale = this.resolution;\n let canvasWidth = Math.floor(this._calculatedWidth * this.resolution);\n let canvasHeight = Math.floor(this._calculatedHeight * this.resolution);\n // Check if we need to scale\n if (options.minWidth && options.minWidth > canvasWidth) {\n let minScale = options.minWidth / canvasWidth;\n if (minScale > scale) {\n scale = minScale * this.resolution;\n }\n }\n if (options.minHeight && options.minHeight > canvasHeight) {\n let minScale = options.minHeight / canvasHeight;\n if (minScale > scale) {\n scale = minScale * this.resolution;\n }\n }\n if (options.maxWidth && options.maxWidth < canvasWidth) {\n let maxScale = options.maxWidth / canvasWidth;\n if (maxScale < scale) {\n scale = maxScale * this.resolution;\n }\n }\n if (options.maxHeight && options.maxHeight > canvasHeight) {\n let maxScale = options.maxHeight / canvasHeight;\n if (maxScale < scale) {\n scale = maxScale * this.resolution;\n }\n }\n // Check if we need to compensate for pixel ratio\n if (options.maintainPixelRatio) {\n scale /= this.resolution;\n }\n // Init list canvases to remove from DOM after export\n const canvases = [];\n // Set up new canvas for export\n let forceRender = false;\n const canvas = document.createElement(\"canvas\");\n if (scale != this.resolution) {\n forceRender = true;\n canvasWidth = canvasWidth * scale / this.resolution;\n canvasHeight = canvasHeight * scale / this.resolution;\n }\n canvas.width = canvasWidth;\n canvas.height = canvasHeight;\n // Add to DOM so it inherits CSS\n canvas.style.position = \"fixed\";\n canvas.style.top = \"-10000px\";\n this.view.appendChild(canvas);\n canvases.push(canvas);\n // Context\n const context = canvas.getContext(\"2d\");\n let width = 0;\n let height = 0;\n let needRerender = false;\n $array.each(this.layers, layer => {\n if (layer && layer.visible) {\n if (layer.tainted || forceRender) {\n needRerender = true;\n layer.exportableView = layer.view;\n layer.exportableContext = layer.context;\n layer.view = document.createElement(\"canvas\");\n // Add to DOM so it inherits CSS\n layer.view.style.position = \"fixed\";\n layer.view.style.top = \"-10000px\";\n this.view.appendChild(layer.view);\n canvases.push(layer.view);\n let extraX = 0;\n let extraY = 0;\n if (layer.margin) {\n extraX += layer.margin.left || 0 + layer.margin.right || 0;\n extraY += layer.margin.top || 0 + layer.margin.bottom || 0;\n }\n layer.view.width = canvasWidth + extraX;\n layer.view.height = canvasHeight + extraY;\n layer.context = layer.view.getContext(\"2d\");\n layer.dirty = true;\n layer.scale = scale;\n }\n }\n });\n if (needRerender) {\n this._omitTainted = true;\n this.render(root);\n this._omitTainted = false;\n }\n $array.each(this.layers, layer => {\n if (layer && layer.visible) {\n // Layer is fine. Just plop it into our target canvas\n let x = 0;\n let y = 0;\n if (layer.margin) {\n x = -(layer.margin.left || 0) * this.resolution;\n y = -(layer.margin.top || 0) * this.resolution;\n }\n context.drawImage(layer.view, x, y);\n // Restore layer original canvas\n if (layer.exportableView) {\n layer.view = layer.exportableView;\n layer.exportableView = undefined;\n }\n if (layer.exportableContext) {\n layer.context = layer.exportableContext;\n layer.exportableContext = undefined;\n }\n if (width < layer.view.clientWidth) {\n width = layer.view.clientWidth;\n }\n if (height < layer.view.clientHeight) {\n height = layer.view.clientHeight;\n }\n layer.scale = undefined;\n }\n });\n canvas.style.width = width + \"px\";\n canvas.style.height = height + \"px\";\n $array.each(canvases, canvas => {\n canvas.style.position = \"\";\n canvas.style.top = \"\";\n this.view.removeChild(canvas);\n });\n return canvas;\n }\n}\nclass GhostLayer {\n constructor() {\n Object.defineProperty(this, \"view\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"context\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"margin\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {\n left: 0,\n right: 0,\n top: 0,\n bottom: 0\n }\n });\n Object.defineProperty(this, \"_resolution\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 1\n });\n Object.defineProperty(this, \"_width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"imageArray\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.view = document.createElement(\"canvas\");\n this.context = this.view.getContext(\"2d\", {\n alpha: false,\n willReadFrequently: true\n });\n this.context.imageSmoothingEnabled = false;\n this.view.style.position = \"absolute\";\n this.view.style.top = \"0px\";\n this.view.style.left = \"0px\";\n }\n resize(canvasWidth, canvasHeight, domWidth, domHeight, resolution) {\n this._resolution = resolution;\n canvasWidth += this.margin.left + this.margin.right;\n canvasHeight += this.margin.top + this.margin.bottom;\n // TODO this should take into account calculateSize\n domWidth += this.margin.left + this.margin.right;\n domHeight += this.margin.top + this.margin.bottom;\n this.view.style.left = -this.margin.left + \"px\";\n this.view.style.top = -this.margin.top + \"px\";\n this._width = Math.floor(canvasWidth * resolution);\n this._height = Math.floor(canvasHeight * resolution);\n this.view.width = this._width;\n this.view.style.width = domWidth + \"px\";\n this.view.height = this._height;\n this.view.style.height = domHeight + \"px\";\n }\n getImageData(point, bbox) {\n return this.context.getImageData(\n // TODO should this round ?\n Math.round((point.x - bbox.left) / bbox.width * this._width), Math.round((point.y - bbox.top) / bbox.height * this._height), 1, 1);\n }\n getImageArray(point) {\n if (!this.imageArray) {\n this.imageArray = this.context.getImageData(0, 0, this._width, this._height).data;\n }\n const data = this.imageArray;\n const x = Math.round(point.x * this._resolution);\n const y = Math.round(point.y * this._resolution);\n const i = (y * this._width + x) * 4;\n return [data[i], data[i + 1], data[i + 2], data[i + 3]];\n }\n setMargin(layers) {\n this.margin.left = 0;\n this.margin.right = 0;\n this.margin.top = 0;\n this.margin.bottom = 0;\n $array.each(layers, layer => {\n if (layer.margin) {\n this.margin.left = Math.max(this.margin.left, layer.margin.left);\n this.margin.right = Math.max(this.margin.right, layer.margin.right);\n this.margin.top = Math.max(this.margin.top, layer.margin.top);\n this.margin.bottom = Math.max(this.margin.bottom, layer.margin.bottom);\n }\n });\n }\n clear() {\n this.context.save();\n this.context.fillStyle = '#000';\n this.context.fillRect(0, 0, this._width, this._height);\n }\n}\n/**\r\n * @ignore\r\n */\nexport class CanvasLayer {\n constructor(view, context) {\n Object.defineProperty(this, \"view\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"context\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"tainted\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"margin\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"order\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"visible\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"scale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"dirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"exportableView\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"exportableContext\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_width\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n Object.defineProperty(this, \"_height\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n this.view = view;\n this.context = context;\n }\n resize(canvasWidth, canvasHeight, domWidth, domHeight, resolution) {\n // TODO should this take into account calculateSize ?\n if (this.width != null) {\n canvasWidth = this.width;\n domWidth = this.width;\n }\n // TODO should this take into account calculateSize ?\n if (this.height != null) {\n canvasHeight = this.height;\n domHeight = this.height;\n }\n if (this.margin) {\n canvasWidth += this.margin.left + this.margin.right;\n canvasHeight += this.margin.top + this.margin.bottom;\n // TODO this should take into account calculateSize\n domWidth += this.margin.left + this.margin.right;\n domHeight += this.margin.top + this.margin.bottom;\n this.view.style.left = -this.margin.left + \"px\";\n this.view.style.top = -this.margin.top + \"px\";\n } else {\n this.view.style.left = \"0px\";\n this.view.style.top = \"0px\";\n }\n this._width = Math.floor(canvasWidth * resolution);\n this._height = Math.floor(canvasHeight * resolution);\n this.view.width = this._width;\n this.view.style.width = domWidth + \"px\";\n this.view.height = this._height;\n this.view.style.height = domHeight + \"px\";\n }\n clear() {\n this.context.save();\n this.context.clearRect(0, 0, this._width, this._height);\n }\n}\n","import { AnimationState } from \"./util/Animation\";\nimport { Container } from \"./render/Container\";\nimport { Text } from \"./render/Text\";\nimport { HorizontalLayout } from \"./render/HorizontalLayout\";\nimport { VerticalLayout } from \"./render/VerticalLayout\";\nimport { GridLayout } from \"./render/GridLayout\";\nimport { Disposer } from \"./util/Disposer\";\nimport { ResizeSensor } from \"./util/ResizeSensor\";\nimport { InterfaceColors } from \"./util/InterfaceColors\";\nimport { Graphics } from \"./render/Graphics\";\nimport { Rectangle } from \"./render/Rectangle\";\nimport { Tooltip } from \"./render/Tooltip\";\nimport { NumberFormatter } from \"./util/NumberFormatter\";\nimport { DateFormatter } from \"./util/DateFormatter\";\nimport { DurationFormatter } from \"./util/DurationFormatter\";\nimport { Language } from \"./util/Language\";\nimport { EventDispatcher } from \"./util/EventDispatcher\";\nimport { DefaultTheme } from \"../themes/DefaultTheme\";\nimport { CanvasRenderer } from \"./render/backend/CanvasRenderer\";\nimport { p100, percent, isPercent, Percent } from \"./util/Percent\";\nimport { color } from \"./util/Color\";\nimport { populateString } from \"./util/PopulateString\";\nimport { registry } from \"./Registry\";\nimport * as $order from \"./util/Order\";\nimport * as $array from \"./util/Array\";\nimport * as $object from \"./util/Object\";\nimport * as $utils from \"./util/Utils\";\nimport * as $type from \"./util/Type\";\nimport en from \"../../locales/en\";\nfunction rAF(fps, callback) {\n if (fps == null) {\n requestAnimationFrame(callback);\n } else {\n setTimeout(() => {\n requestAnimationFrame(callback);\n }, 1000 / fps);\n }\n}\n// TODO implement Disposer\n/**\r\n * Root element of the chart.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/#Root_element} for more info\r\n */\nexport class Root {\n constructor(id, settings = {}, isReal) {\n /**\r\n * A reference to original chart container (div element).\r\n */\n Object.defineProperty(this, \"dom\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_inner\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_settings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_isDirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_isDirtyParents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_isDirtyAnimation\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_dirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dirtyParents\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dirtyBounds\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_dirtyPositions\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: {}\n });\n Object.defineProperty(this, \"_ticker\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"_tickers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_updateTick\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n /**\r\n * Root's event dispatcher.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/events/} for more info\r\n */\n Object.defineProperty(this, \"events\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new EventDispatcher()\n });\n /**\r\n * @ignore\r\n * @todo needs description\r\n */\n Object.defineProperty(this, \"animationTime\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: null\n });\n Object.defineProperty(this, \"_animations\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_renderer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_rootContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Main content container.\r\n */\n Object.defineProperty(this, \"container\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * A [[Container]] used to display tooltips in.\r\n */\n Object.defineProperty(this, \"tooltipContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipContainerSettings\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltip\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n // Locale-related\n /**\r\n * @ignore\r\n */\n Object.defineProperty(this, \"language\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Language.new(this, {})\n });\n /**\r\n * Locale used by the chart.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/locales/}\r\n */\n Object.defineProperty(this, \"locale\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: en\n });\n // Date-time related\n /**\r\n * Use UTC when formatting date/time.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-dates/#utc-and-time-zones} for more info\r\n */\n Object.defineProperty(this, \"utc\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n /**\r\n * If set, will format date/time in specific time zone.\r\n *\r\n * The value should be named time zone, e.g.:\r\n * `\"America/Vancouver\"`, `\"Australia/Sydney\"`, `\"UTC\"`.\r\n *\r\n * NOTE: Using time zone feature may noticeable affect performance of the\r\n * chart, especially with large data sets, since every single date will need\r\n * to be recalculated.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/root-element/#time-zone} for more info\r\n * @since 5.1.0\r\n */\n Object.defineProperty(this, \"timezone\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * The maximum FPS that the Root will run at.\r\n *\r\n * If `undefined` it will run at the highest FPS.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/root-element/#Performance} for more info\r\n */\n Object.defineProperty(this, \"fps\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Number formatter.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-numbers/} for more info\r\n */\n Object.defineProperty(this, \"numberFormatter\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: NumberFormatter.new(this, {})\n });\n /**\r\n * Date/time formatter.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-dates/} for more info\r\n */\n Object.defineProperty(this, \"dateFormatter\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: DateFormatter.new(this, {})\n });\n /**\r\n * Duration formatter.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/formatters/formatting-dates/} for more info\r\n */\n Object.defineProperty(this, \"durationFormatter\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: DurationFormatter.new(this, {})\n });\n // Accessibility\n /**\r\n * Global tab index for using for the whole chart\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/accessibility/} for more info\r\n */\n Object.defineProperty(this, \"tabindex\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: 0\n });\n //@todo maybe make this better\n Object.defineProperty(this, \"_tabindexes\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_a11yD\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_focusElementDirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_focusElementContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_focusedSprite\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_isShift\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_keyboardDragPoint\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipElementContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_readerAlertElement\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_logo\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltipDiv\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Used for dynamically-created CSS and JavaScript with strict source policies.\r\n */\n Object.defineProperty(this, \"nonce\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * Special color set to be used for various controls.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/colors-gradients-and-patterns/#Interface_colors} for more info\r\n */\n Object.defineProperty(this, \"interfaceColors\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * An instance of vertical layout object that can be used to set `layout` setting\r\n * of a [[Container]].\r\n *\r\n * @default VerticalLayout.new()\r\n */\n Object.defineProperty(this, \"verticalLayout\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: VerticalLayout.new(this, {})\n });\n /**\r\n * An instance of horizontal layout object that can be used to set `layout` setting\r\n * of a [[Container]].\r\n *\r\n * @default HorizontalLayout.new()\r\n */\n Object.defineProperty(this, \"horizontalLayout\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: HorizontalLayout.new(this, {})\n });\n /**\r\n * An instance of grid layout object that can be used to set `layout` setting\r\n * of a [[Container]].\r\n *\r\n * @default VerticalLayout.new()\r\n */\n Object.defineProperty(this, \"gridLayout\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: GridLayout.new(this, {})\n });\n Object.defineProperty(this, \"_paused\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n /**\r\n * Indicates whether chart should resized automatically when parent container\r\n * width and/or height changes.\r\n *\r\n * If disabled (`autoResize = false`) you can make the chart resize manually\r\n * by calling root element's `resize()` method.\r\n */\n Object.defineProperty(this, \"autoResize\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: true\n });\n Object.defineProperty(this, \"_fontHash\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"\"\n });\n Object.defineProperty(this, \"_isDisposed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_disposers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_resizeSensorDisposer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_tooltips\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_htmlElementContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_htmlEnabledContainers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n if (!isReal) {\n throw new Error(\"You cannot use `new Class()`, instead use `Class.new()`\");\n }\n this._settings = settings;\n if (settings.accessible == false) {\n this._a11yD = true;\n }\n if (settings.useSafeResolution == null) {\n settings.useSafeResolution = true;\n }\n let resolution;\n if (settings.useSafeResolution) {\n resolution = $utils.getSafeResolution();\n }\n this._renderer = new CanvasRenderer(resolution);\n let dom;\n if (id instanceof HTMLElement) {\n dom = id;\n } else {\n dom = document.getElementById(id);\n }\n $array.each(registry.rootElements, root => {\n if (root.dom === dom) {\n throw new Error(\"You cannot have multiple Roots on the same DOM node\");\n }\n });\n this.interfaceColors = InterfaceColors.new(this, {});\n if (dom === null) {\n throw new Error(\"Could not find HTML element with id `\" + id + \"`\");\n }\n this.dom = dom;\n let inner = document.createElement(\"div\");\n inner.style.position = \"relative\";\n inner.style.width = \"100%\";\n inner.style.height = \"100%\";\n dom.appendChild(inner);\n const tooltipContainerBounds = settings.tooltipContainerBounds;\n if (tooltipContainerBounds) {\n this._tooltipContainerSettings = tooltipContainerBounds;\n }\n this._inner = inner;\n this._updateComputedStyles();\n registry.rootElements.push(this);\n }\n static new(id, settings) {\n const root = new Root(id, settings, true);\n root._init();\n return root;\n }\n moveDOM(id) {\n let dom;\n if (id instanceof HTMLElement) {\n dom = id;\n } else {\n dom = document.getElementById(id);\n }\n if (dom) {\n while (this.dom.childNodes.length > 0) {\n dom.appendChild(this.dom.childNodes[0]);\n }\n this.dom = dom;\n this._initResizeSensor();\n this.resize();\n }\n }\n _handleLogo() {\n if (this._logo) {\n const w = this.dom.offsetWidth;\n const h = this.dom.offsetHeight;\n if (w <= 150 || h <= 60) {\n this._logo.hide();\n } else {\n this._logo.show();\n }\n }\n }\n _showBranding() {\n if (!this._logo) {\n const logo = this.tooltipContainer.children.push(Container.new(this, {\n interactive: true,\n interactiveChildren: false,\n position: \"absolute\",\n setStateOnChildren: true,\n paddingTop: 9,\n paddingRight: 9,\n paddingBottom: 9,\n paddingLeft: 9,\n scale: .6,\n y: percent(100),\n centerY: p100,\n tooltipText: \"Created using amCharts 5\",\n tooltipX: p100,\n cursorOverStyle: \"pointer\",\n background: Rectangle.new(this, {\n fill: color(0x474758),\n fillOpacity: 0,\n tooltipY: 5\n })\n }));\n const tooltip = Tooltip.new(this, {\n pointerOrientation: \"horizontal\",\n paddingTop: 4,\n paddingRight: 7,\n paddingBottom: 4,\n paddingLeft: 7\n });\n tooltip.label.setAll({\n fontSize: 12\n });\n tooltip.get(\"background\").setAll({\n fill: this.interfaceColors.get(\"background\"),\n stroke: this.interfaceColors.get(\"grid\"),\n strokeOpacity: 0.3\n });\n logo.set(\"tooltip\", tooltip);\n logo.events.on(\"click\", () => {\n window.open(\"https://www.amcharts.com/\", \"_blank\");\n });\n logo.states.create(\"hover\", {});\n const m = logo.children.push(Graphics.new(this, {\n stroke: color(0xcccccc),\n strokeWidth: 3,\n svgPath: \"M5 25 L13 25h13.6c3.4 0 6 0 10.3-4.3s5.2-12 8.6-12c3.4 0 4.3 8.6 7.7 8.6M83.4 25H79.8c-3.4 0-6 0-10.3-4.3s-5.2-12-8.6-12-4.3 8.6-7.7 8.6\"\n }));\n m.states.create(\"hover\", {\n stroke: color(0x3CABFF)\n });\n const a = logo.children.push(Graphics.new(this, {\n stroke: color(0x888888),\n strokeWidth: 3,\n svgPath: \"M83.4 25h-31C37 25 39.5 4.4 28.4 4.4S18.9 24.2 4.3 25H0\"\n }));\n a.states.create(\"hover\", {\n stroke: color(0x474758)\n });\n //logo.set(\"tooltip\", this._tooltip);\n //logo.setPrivate(\"tooltipTarget\", logo.get(\"background\"));\n this._logo = logo;\n this._handleLogo();\n }\n }\n _getRealSize() {\n return this.dom.getBoundingClientRect();\n }\n _getCalculatedSize(rect) {\n if (this._settings.calculateSize) {\n return this._settings.calculateSize(rect);\n } else {\n return {\n width: rect.width,\n height: rect.height\n };\n }\n }\n _init() {\n const settings = this._settings;\n if (settings.accessible !== false) {\n if (settings.focusable) {\n this._inner.setAttribute(\"focusable\", \"true\");\n this._inner.setAttribute(\"tabindex\", this.tabindex + \"\");\n }\n if (settings.ariaLabel) {\n this._inner.setAttribute(\"aria-label\", settings.ariaLabel);\n }\n if (settings.role) {\n this._inner.setAttribute(\"role\", settings.role);\n }\n }\n const renderer = this._renderer;\n const rect = this._getRealSize();\n const size = this._getCalculatedSize(rect);\n const width = Math.floor(size.width);\n const height = Math.floor(size.height);\n const realWidth = Math.floor(rect.width);\n const realHeight = Math.floor(rect.height);\n const rootContainer = Container.new(this, {\n visible: true,\n width: width,\n height: height\n });\n this._rootContainer = rootContainer;\n this._rootContainer._defaultThemes.push(DefaultTheme.new(this));\n const container = rootContainer.children.push(Container.new(this, {\n visible: true,\n width: p100,\n height: p100\n }));\n this.container = container;\n renderer.resize(realWidth, realHeight, width, height);\n //@todo: better appendChild - refer\n this._inner.appendChild(renderer.view);\n // TODO: TMP TMP TMP for testing only, remove\n //renderer.debugGhostView = true;\n this._initResizeSensor();\n // HTML content holder\n const htmlElementContainer = document.createElement(\"div\");\n this._htmlElementContainer = htmlElementContainer;\n htmlElementContainer.className = \"am5-html-container\";\n htmlElementContainer.style.position = \"absolute\";\n htmlElementContainer.style.pointerEvents = \"none\";\n if (!this._tooltipContainerSettings) {\n htmlElementContainer.style.overflow = \"hidden\";\n }\n this._inner.appendChild(htmlElementContainer);\n if (this._a11yD !== true) {\n // Create element which is used to make announcements to screen reader\n const readerAlertElement = document.createElement(\"div\");\n readerAlertElement.className = \"am5-reader-container\";\n readerAlertElement.setAttribute(\"role\", \"alert\");\n // readerAlertElement.style.zIndex = \"-100000\";\n // readerAlertElement.style.opacity = \"0\";\n // readerAlertElement.style.top = \"0\";\n readerAlertElement.style.position = \"absolute\";\n readerAlertElement.style.width = \"1px\";\n readerAlertElement.style.height = \"1px\";\n readerAlertElement.style.overflow = \"hidden\";\n readerAlertElement.style.clip = \"rect(1px, 1px, 1px, 1px)\";\n this._readerAlertElement = readerAlertElement;\n this._inner.appendChild(this._readerAlertElement);\n const focusElementContainer = document.createElement(\"div\");\n focusElementContainer.className = \"am5-focus-container\";\n focusElementContainer.style.position = \"absolute\";\n focusElementContainer.style.pointerEvents = \"none\";\n focusElementContainer.style.top = \"0px\";\n focusElementContainer.style.left = \"0px\";\n focusElementContainer.style.overflow = \"hidden\";\n focusElementContainer.style.width = width + \"px\";\n focusElementContainer.style.height = height + \"px\";\n focusElementContainer.setAttribute(\"role\", \"graphics-document\");\n $utils.setInteractive(focusElementContainer, false);\n this._focusElementContainer = focusElementContainer;\n this._inner.appendChild(this._focusElementContainer);\n const tooltipElementContainer = document.createElement(\"div\");\n this._tooltipElementContainer = tooltipElementContainer;\n tooltipElementContainer.className = \"am5-tooltip-container\";\n this._inner.appendChild(tooltipElementContainer);\n // Add keyboard events for accessibility, e.g. simulating drag with arrow\n // keys and click with ENTER\n if ($utils.supports(\"keyboardevents\")) {\n this._disposers.push($utils.addEventListener(window, \"keydown\", ev => {\n const eventKey = $utils.getEventKey(ev);\n if (eventKey == \"Shift\") {\n this._isShift = true;\n } else if (eventKey == \"Tab\") {\n this._isShift = ev.shiftKey;\n }\n }));\n this._disposers.push($utils.addEventListener(window, \"keyup\", ev => {\n const eventKey = $utils.getEventKey(ev);\n if (eventKey == \"Shift\") {\n this._isShift = false;\n }\n }));\n this._disposers.push($utils.addEventListener(focusElementContainer, \"click\", () => {\n // Some screen readers convert ENTER (and some SPACE) press whil on\n // focused element to a \"click\" event, preventing actual \"keydown\"\n // event from firing. We're using this \"click\" event to still\n // generate internal click events.\n const focusedSprite = this._focusedSprite;\n if (focusedSprite) {\n const downEvent = renderer.getEvent(new MouseEvent(\"click\"));\n focusedSprite.events.dispatch(\"click\", {\n type: \"click\",\n originalEvent: downEvent.event,\n point: downEvent.point,\n simulated: true,\n target: focusedSprite\n });\n }\n }));\n this._disposers.push($utils.addEventListener(focusElementContainer, \"keydown\", ev => {\n const focusedSprite = this._focusedSprite;\n if (focusedSprite) {\n if (ev.key == \"Escape\") {\n // ESC pressed - lose current focus\n $utils.blur();\n this._focusedSprite = undefined;\n }\n let dragOffsetX = 0;\n let dragOffsetY = 0;\n // TODO: figure out if using bogus MouseEvent is fine, or it will\n // fail on some platforms\n const eventKey = $utils.getEventKey(ev);\n switch (eventKey) {\n case \"Enter\":\n case \" \":\n if (eventKey == \" \" && focusedSprite.get(\"role\") != \"checkbox\") {\n return;\n }\n ev.preventDefault();\n const downEvent = renderer.getEvent(new MouseEvent(\"mouse\"));\n focusedSprite.events.dispatch(\"click\", {\n type: \"click\",\n originalEvent: downEvent.event,\n point: downEvent.point,\n simulated: true,\n target: focusedSprite\n });\n return;\n case \"ArrowLeft\":\n dragOffsetX = -6;\n break;\n case \"ArrowRight\":\n dragOffsetX = 6;\n break;\n case \"ArrowUp\":\n dragOffsetY = -6;\n break;\n case \"ArrowDown\":\n dragOffsetY = 6;\n break;\n default:\n return;\n }\n if (dragOffsetX != 0 || dragOffsetY != 0) {\n ev.preventDefault();\n if (!focusedSprite.isDragging()) {\n // Start dragging\n this._keyboardDragPoint = {\n x: 0,\n y: 0\n };\n const downEvent = renderer.getEvent(new MouseEvent(\"mousedown\", {\n clientX: 0,\n clientY: 0\n }));\n downEvent.point = {\n x: 0,\n y: 0\n };\n if (focusedSprite.events.isEnabled(\"pointerdown\")) {\n focusedSprite.events.dispatch(\"pointerdown\", {\n type: \"pointerdown\",\n originalEvent: downEvent.event,\n point: downEvent.point,\n simulated: true,\n target: focusedSprite\n });\n }\n } else {\n // Move focus marker\n //this._positionFocusElement(focusedSprite);\n }\n // Move incrementally\n const dragPoint = this._keyboardDragPoint;\n dragPoint.x += dragOffsetX;\n dragPoint.y += dragOffsetY;\n const moveEvent = renderer.getEvent(new MouseEvent(\"mousemove\", {\n clientX: dragPoint.x,\n clientY: dragPoint.y\n }), false);\n if (focusedSprite.events.isEnabled(\"globalpointermove\")) {\n focusedSprite.events.dispatch(\"globalpointermove\", {\n type: \"globalpointermove\",\n originalEvent: moveEvent.event,\n point: moveEvent.point,\n simulated: true,\n target: focusedSprite\n });\n }\n }\n }\n }));\n this._disposers.push($utils.addEventListener(focusElementContainer, \"keyup\", ev => {\n if (this._focusedSprite) {\n const focusedSprite = this._focusedSprite;\n const eventKey = $utils.getEventKey(ev);\n switch (eventKey) {\n case \"ArrowLeft\":\n case \"ArrowRight\":\n case \"ArrowTop\":\n case \"ArrowDown\":\n if (focusedSprite.isDragging()) {\n // Simulate drag stop\n const dragPoint = this._keyboardDragPoint;\n const upEvent = renderer.getEvent(new MouseEvent(\"mouseup\", {\n clientX: dragPoint.x,\n clientY: dragPoint.y\n }));\n if (focusedSprite.events.isEnabled(\"globalpointerup\")) {\n focusedSprite.events.dispatch(\"globalpointerup\", {\n type: \"globalpointerup\",\n originalEvent: upEvent.event,\n point: upEvent.point,\n simulated: true,\n target: focusedSprite\n });\n }\n //this._positionFocusElement(focusedSprite);\n this._keyboardDragPoint = undefined;\n // @todo dispatch mouseup event instead of calling dragStop?\n // this._dispatchEvent(\"globalpointerup\", target, upEvent);\n return;\n } else if (focusedSprite.get(\"focusableGroup\")) {\n // Find next item in focusable group\n const group = focusedSprite.get(\"focusableGroup\");\n const items = this._tabindexes.filter(item => {\n return item.get(\"focusableGroup\") == group && item.getPrivate(\"focusable\") !== false && item.isVisibleDeep() ? true : false;\n });\n let index = items.indexOf(focusedSprite);\n const lastIndex = items.length - 1;\n index += eventKey == \"ArrowRight\" || eventKey == \"ArrowDown\" ? 1 : -1;\n if (index < 0) {\n index = lastIndex;\n } else if (index > lastIndex) {\n index = 0;\n }\n $utils.focus(items[index].getPrivate(\"focusElement\").dom);\n }\n break;\n }\n }\n }));\n }\n }\n this._startTicker();\n this.setThemes([]);\n this._addTooltip();\n if (!this._hasLicense()) {\n this._showBranding();\n }\n }\n _initResizeSensor() {\n if (this._resizeSensorDisposer) {\n this._resizeSensorDisposer.dispose();\n }\n this._resizeSensorDisposer = new ResizeSensor(this.dom, () => {\n if (this.autoResize) {\n this.resize();\n }\n });\n this._disposers.push(this._resizeSensorDisposer);\n }\n /**\r\n * If automatic resizing of char is disabled (`root.autoResize = false`), it\r\n * can be resized manually by calling this method.\r\n */\n resize() {\n const rect = this._getRealSize();\n const size = this._getCalculatedSize(rect);\n const w = Math.floor(size.width);\n const h = Math.floor(size.height);\n if (w > 0 && h > 0) {\n const realWidth = Math.floor(rect.width);\n const realHeight = Math.floor(rect.height);\n const htmlElementContainer = this._htmlElementContainer;\n htmlElementContainer.style.width = w + \"px\";\n htmlElementContainer.style.height = h + \"px\";\n if (this._a11yD !== true) {\n const focusElementContainer = this._focusElementContainer;\n focusElementContainer.style.width = w + \"px\";\n focusElementContainer.style.height = h + \"px\";\n }\n this._renderer.resize(realWidth, realHeight, w, h);\n const rootContainer = this._rootContainer;\n rootContainer.setPrivate(\"width\", w);\n rootContainer.setPrivate(\"height\", h);\n this._render();\n this._handleLogo();\n }\n }\n _render() {\n this._renderer.render(this._rootContainer._display);\n if (this._focusElementDirty) {\n this._updateCurrentFocus();\n this._focusElementDirty = false;\n }\n }\n _runTickers(currentTime) {\n $array.each(this._tickers, f => {\n f(currentTime);\n });\n }\n _runAnimations(currentTime) {\n let running = 0;\n $array.keepIf(this._animations, animation => {\n const state = animation._runAnimation(currentTime);\n if (state === AnimationState.Stopped) {\n return false;\n } else if (state === AnimationState.Playing) {\n ++running;\n return true;\n } else {\n return true;\n }\n });\n this._isDirtyAnimation = false;\n return running === 0;\n }\n _runDirties() {\n //console.log(\"tick **************************************************************\");\n let allParents = {};\n while (this._isDirtyParents) {\n // This must be before calling _prepareChildren\n this._isDirtyParents = false;\n $object.keys(this._dirtyParents).forEach(key => {\n const parent = this._dirtyParents[key];\n delete this._dirtyParents[key];\n if (!parent.isDisposed()) {\n allParents[parent.uid] = parent;\n parent._prepareChildren();\n }\n });\n }\n $object.keys(allParents).forEach(key => {\n allParents[key]._updateChildren();\n });\n const objects = [];\n //\t\tconsole.log(\"_beforeChanged\")\n $object.keys(this._dirty).forEach(key => {\n const entity = this._dirty[key];\n if (entity.isDisposed()) {\n delete this._dirty[entity.uid];\n } else {\n objects.push(entity);\n entity._beforeChanged();\n }\n });\n //\t\tconsole.log(\"_changed\")\n objects.forEach(entity => {\n entity._changed();\n delete this._dirty[entity.uid];\n entity._clearDirty();\n });\n this._isDirty = false;\n const depths = {};\n const bounds = [];\n $object.keys(this._dirtyBounds).forEach(key => {\n const entity = this._dirtyBounds[key];\n delete this._dirtyBounds[key];\n if (!entity.isDisposed()) {\n depths[entity.uid] = entity.depth();\n bounds.push(entity);\n }\n });\n this._positionHTMLElements();\n // High depth -> low depth\n bounds.sort((x, y) => {\n return $order.compare(depths[y.uid], depths[x.uid]);\n });\n //\t\tconsole.log(\"_updateBounds\")\n bounds.forEach(entity => {\n entity._updateBounds();\n });\n //\t\tconsole.log(\"_updatePosition\")\n const dirtyPositions = this._dirtyPositions;\n $object.keys(dirtyPositions).forEach(key => {\n const sprite = dirtyPositions[key];\n delete dirtyPositions[key];\n if (!sprite.isDisposed()) {\n sprite._updatePosition();\n }\n });\n //\t\tconsole.log(\"_afterChanged\")\n objects.forEach(entity => {\n entity._afterChanged();\n });\n }\n _renderFrame(currentTime) {\n if (this._updateTick) {\n if (this.events.isEnabled(\"framestarted\")) {\n this.events.dispatch(\"framestarted\", {\n type: \"framestarted\",\n target: this,\n timestamp: currentTime\n });\n }\n this._checkComputedStyles();\n this._runTickers(currentTime);\n const animationDone = this._runAnimations(currentTime);\n this._runDirties();\n this._render();\n this._renderer.resetImageArray();\n this._positionHTMLElements();\n if (this.events.isEnabled(\"frameended\")) {\n this.events.dispatch(\"frameended\", {\n type: \"frameended\",\n target: this,\n timestamp: currentTime\n });\n }\n return this._tickers.length === 0 && animationDone && !this._isDirtyAnimation && !this._isDirty;\n } else {\n return true;\n }\n }\n _runTicker(currentTime, now) {\n if (!this.isDisposed()) {\n this.animationTime = currentTime;\n const done = this._renderFrame(currentTime);\n // No more work to do\n if (done) {\n this._ticker = null;\n this.animationTime = null;\n } else {\n if (!this._paused) {\n if (now) {\n this._ticker;\n } else {\n rAF(this.fps, this._ticker);\n }\n }\n }\n }\n }\n _runTickerNow(timeout = 10000) {\n if (!this.isDisposed()) {\n const endTime = performance.now() + timeout;\n for (;;) {\n const currentTime = performance.now();\n if (currentTime >= endTime) {\n this.animationTime = null;\n break;\n }\n this.animationTime = currentTime;\n const done = this._renderFrame(currentTime);\n if (done) {\n this.animationTime = null;\n break;\n }\n }\n }\n }\n _startTicker() {\n if (this._ticker === null) {\n this.animationTime = null;\n this._ticker = currentTime => {\n this._runTicker(currentTime);\n };\n rAF(this.fps, this._ticker);\n }\n }\n /**\r\n * Returns whether the root is updating or not.\r\n */\n get updateTick() {\n return this._updateTick;\n }\n /**\r\n * Enables or disables the root updating.\r\n */\n set updateTick(value) {\n this._updateTick = value;\n if (value) {\n this._startTicker();\n }\n }\n _addDirtyEntity(entity) {\n this._isDirty = true;\n if (this._dirty[entity.uid] === undefined) {\n this._dirty[entity.uid] = entity;\n }\n this._startTicker();\n }\n _addDirtyParent(parent) {\n this._isDirty = true;\n this._isDirtyParents = true;\n if (this._dirtyParents[parent.uid] === undefined) {\n this._dirtyParents[parent.uid] = parent;\n }\n this._startTicker();\n }\n _addDirtyBounds(entity) {\n this._isDirty = true;\n if (this._dirtyBounds[entity.uid] === undefined) {\n this._dirtyBounds[entity.uid] = entity;\n }\n this._startTicker();\n }\n _addDirtyPosition(sprite) {\n this._isDirty = true;\n if (this._dirtyPositions[sprite.uid] === undefined) {\n this._dirtyPositions[sprite.uid] = sprite;\n }\n this._startTicker();\n }\n _addAnimation(animation) {\n this._isDirtyAnimation = true;\n // TODO use numeric id instead\n if (this._animations.indexOf(animation) === -1) {\n this._animations.push(animation);\n }\n this._startTicker();\n }\n _markDirty() {\n this._isDirty = true;\n }\n _markDirtyRedraw() {\n this.events.once(\"frameended\", () => {\n this._isDirty = true;\n this._startTicker();\n });\n }\n eachFrame(f) {\n this._tickers.push(f);\n this._startTicker();\n return new Disposer(() => {\n $array.removeFirst(this._tickers, f);\n });\n }\n markDirtyGlobal(container) {\n if (!container) {\n container = this.container;\n }\n container.walkChildren(child => {\n if (child instanceof Container) {\n this.markDirtyGlobal(child);\n }\n child.markDirty();\n child.markDirtyBounds();\n });\n }\n /**\r\n * Returns width of the target container, in pixels.\r\n *\r\n * @return Width\r\n */\n width() {\n // TODO make this more efficient, maybe just return the renderer's width ?\n return Math.floor(this._getCalculatedSize(this._getRealSize()).width);\n }\n /**\r\n * Returns height of the target container, in pixels.\r\n *\r\n * @return Height\r\n */\n height() {\n // TODO make this more efficient, maybe just return the renderer's height ?\n return Math.floor(this._getCalculatedSize(this._getRealSize()).height);\n }\n /**\r\n * Disposes root and all the content in it.\r\n */\n dispose() {\n if (!this._isDisposed) {\n this._isDisposed = true;\n this._rootContainer.dispose();\n this._renderer.dispose();\n this.horizontalLayout.dispose();\n this.verticalLayout.dispose();\n this.interfaceColors.dispose();\n $array.each(this._disposers, x => {\n x.dispose();\n });\n if (this._inner) {\n $utils.removeElement(this._inner);\n }\n $array.remove(registry.rootElements, this);\n }\n }\n /**\r\n * Returns `true` if root element is disposed.\r\n *\r\n * @return Disposed?\r\n */\n isDisposed() {\n return this._isDisposed;\n }\n /**\r\n * Triggers screen reader read out a message.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/accessibility/} for more info\r\n * @param text Alert text\r\n */\n readerAlert(text) {\n if (this._a11yD !== true) {\n this._readerAlertElement.innerHTML = $utils.stripTags(text);\n }\n }\n /**\r\n * Sets themes to be used for the chart.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/themes/} for more info\r\n * @param themes A list of themes\r\n */\n setThemes(themes) {\n this._rootContainer.set(\"themes\", themes);\n // otherwise new themes are not applied\n const tooltipContainer = this.tooltipContainer;\n if (tooltipContainer) {\n tooltipContainer._applyThemes();\n }\n // @todo review this\n const interfaceColors = this.interfaceColors;\n if (interfaceColors) {\n interfaceColors._applyThemes();\n }\n }\n _addTooltip() {\n if (!this.tooltipContainer) {\n const tooltipContainerSettings = this._tooltipContainerSettings;\n const tooltipContainer = this._rootContainer.children.push(Container.new(this, {\n position: \"absolute\",\n isMeasured: false,\n width: p100,\n height: p100,\n layer: tooltipContainerSettings ? 35 : 30,\n layerMargin: tooltipContainerSettings ? tooltipContainerSettings : undefined\n }));\n this.tooltipContainer = tooltipContainer;\n const tooltip = Tooltip.new(this, {});\n this.container.set(\"tooltip\", tooltip);\n tooltip.hide(0);\n this._tooltip = tooltip;\n }\n }\n /**\r\n * Accesibility\r\n */\n _registerTabindexOrder(target) {\n if (this._a11yD == true) {\n return;\n }\n if (target.get(\"focusable\")) {\n $array.pushOne(this._tabindexes, target);\n } else {\n $array.remove(this._tabindexes, target);\n }\n this._invalidateTabindexes();\n }\n _unregisterTabindexOrder(target) {\n if (this._a11yD == true) {\n return;\n }\n $array.remove(this._tabindexes, target);\n this._invalidateTabindexes();\n }\n _invalidateTabindexes() {\n if (this._a11yD == true) {\n return;\n }\n this._tabindexes.sort((a, b) => {\n const aindex = a.get(\"tabindexOrder\", 0);\n const bindex = b.get(\"tabindexOrder\", 0);\n if (aindex == bindex) {\n return 0;\n } else if (aindex > bindex) {\n return 1;\n } else {\n return -1;\n }\n });\n const groups = [];\n $array.each(this._tabindexes, (item, index) => {\n if (!item.getPrivate(\"focusElement\")) {\n this._makeFocusElement(index, item);\n } else {\n this._moveFocusElement(index, item);\n }\n const group = item.get(\"focusableGroup\");\n if (group && item.getPrivate(\"focusable\") !== false) {\n if (groups.indexOf(group) !== -1) {\n // Non-first element in the group, make it not directly focusable\n item.getPrivate(\"focusElement\").dom.setAttribute(\"tabindex\", \"-1\");\n } else {\n groups.push(group);\n }\n }\n });\n }\n _updateCurrentFocus() {\n if (this._a11yD == true) {\n return;\n }\n if (this._focusedSprite) {\n this._decorateFocusElement(this._focusedSprite);\n this._positionFocusElement(this._focusedSprite);\n }\n }\n _decorateFocusElement(target, focusElement) {\n if (this._a11yD == true) {\n return;\n }\n // Decorate with proper accessibility attributes\n if (!focusElement) {\n focusElement = target.getPrivate(\"focusElement\").dom;\n }\n if (!focusElement) {\n return;\n }\n const role = target.get(\"role\");\n if (role) {\n focusElement.setAttribute(\"role\", role);\n } else {\n focusElement.removeAttribute(\"role\");\n }\n const ariaLabel = target.get(\"ariaLabel\");\n if (ariaLabel) {\n const label = populateString(target, ariaLabel);\n focusElement.setAttribute(\"aria-label\", label);\n } else {\n focusElement.removeAttribute(\"aria-label\");\n }\n const ariaLive = target.get(\"ariaLive\");\n if (ariaLive) {\n focusElement.setAttribute(\"aria-live\", ariaLive);\n } else {\n focusElement.removeAttribute(\"aria-live\");\n }\n const ariaChecked = target.get(\"ariaChecked\");\n if (ariaChecked != null && role && [\"checkbox\", \"option\", \"radio\", \"menuitemcheckbox\", \"menuitemradio\", \"treeitem\"].indexOf(role) !== -1) {\n focusElement.setAttribute(\"aria-checked\", ariaChecked ? \"true\" : \"false\");\n } else {\n focusElement.removeAttribute(\"aria-checked\");\n }\n const ariaCurrent = target.get(\"ariaCurrent\");\n if (ariaCurrent != null) {\n focusElement.setAttribute(\"aria-current\", ariaCurrent);\n } else {\n focusElement.removeAttribute(\"aria-current\");\n }\n const ariaSelected = target.get(\"ariaSelected\");\n if (ariaSelected != null && role && [\"gridcell\", \"option\", \"row\", \"tab\", \"columnheader\", \"rowheader\", \"treeitem\"].indexOf(role) !== -1) {\n focusElement.setAttribute(\"aria-selected\", ariaSelected ? \"true\" : \"false\");\n } else {\n focusElement.removeAttribute(\"aria-selected\");\n }\n if (target.get(\"ariaHidden\")) {\n focusElement.setAttribute(\"aria-hidden\", \"true\");\n } else {\n focusElement.removeAttribute(\"aria-hidden\");\n }\n const ariaOrientation = target.get(\"ariaOrientation\");\n if (ariaOrientation) {\n focusElement.setAttribute(\"aria-orientation\", ariaOrientation);\n } else {\n focusElement.removeAttribute(\"aria-orientation\");\n }\n const ariaValueNow = target.get(\"ariaValueNow\");\n if (ariaValueNow) {\n focusElement.setAttribute(\"aria-valuenow\", ariaValueNow);\n } else {\n focusElement.removeAttribute(\"aria-valuenow\");\n }\n const ariaValueMin = target.get(\"ariaValueMin\");\n if (ariaValueMin) {\n focusElement.setAttribute(\"aria-valuemin\", ariaValueMin);\n } else {\n focusElement.removeAttribute(\"aria-valuemin\");\n }\n const ariaValueMax = target.get(\"ariaValueMax\");\n if (ariaValueMax) {\n focusElement.setAttribute(\"aria-valuemax\", ariaValueMax);\n } else {\n focusElement.removeAttribute(\"aria-valuemax\");\n }\n const ariaValueText = target.get(\"ariaValueText\");\n if (ariaValueText) {\n focusElement.setAttribute(\"aria-valuetext\", ariaValueText);\n } else {\n focusElement.removeAttribute(\"aria-valuetext\");\n }\n const ariaControls = target.get(\"ariaControls\");\n if (ariaControls) {\n focusElement.setAttribute(\"aria-controls\", ariaControls);\n } else {\n focusElement.removeAttribute(\"aria-controls\");\n }\n if (target.get(\"visible\") && target.get(\"opacity\") !== 0 && target.get(\"role\") != \"tooltip\" && !target.isHidden() && target.getPrivate(\"focusable\") !== false) {\n if (focusElement.getAttribute(\"tabindex\") != \"-1\") {\n focusElement.setAttribute(\"tabindex\", \"\" + this.tabindex);\n }\n focusElement.removeAttribute(\"aria-hidden\");\n } else {\n focusElement.removeAttribute(\"tabindex\");\n focusElement.setAttribute(\"aria-hidden\", \"true\");\n }\n }\n _makeFocusElement(index, target) {\n if (target.getPrivate(\"focusElement\") || this._a11yD == true) {\n return;\n }\n // Init\n const focusElement = document.createElement(\"div\");\n if (target.get(\"role\") != \"tooltip\") {\n focusElement.tabIndex = this.tabindex;\n }\n focusElement.style.position = \"absolute\";\n $utils.setInteractive(focusElement, false);\n const disposers = [];\n target.setPrivate(\"focusElement\", {\n dom: focusElement,\n disposers\n });\n this._decorateFocusElement(target);\n disposers.push($utils.addEventListener(focusElement, \"focus\", ev => {\n this._handleFocus(ev);\n }));\n disposers.push($utils.addEventListener(focusElement, \"blur\", ev => {\n this._handleBlur(ev);\n }));\n this._moveFocusElement(index, target);\n }\n _removeFocusElement(target) {\n if (this._a11yD == true) {\n return;\n }\n $array.remove(this._tabindexes, target);\n const focusElement = target.getPrivate(\"focusElement\");\n if (focusElement) {\n const container = this._focusElementContainer;\n container.removeChild(focusElement.dom);\n $array.each(focusElement.disposers, x => {\n x.dispose();\n });\n }\n }\n _hideFocusElement(target) {\n if (this._a11yD == true) {\n return;\n }\n const focusElement = target.getPrivate(\"focusElement\");\n focusElement.dom.style.display = \"none\";\n }\n _moveFocusElement(index, target) {\n if (this._a11yD == true) {\n return;\n }\n // Get container\n const container = this._focusElementContainer;\n const focusElement = target.getPrivate(\"focusElement\").dom;\n if (focusElement === this._focusElementContainer.children[index]) {\n // Nothing to do\n return;\n }\n const next = this._focusElementContainer.children[index + 1];\n if (next) {\n container.insertBefore(focusElement, next);\n } else {\n container.append(focusElement);\n }\n }\n _positionFocusElement(target) {\n if (this._a11yD == true || target == undefined) {\n return;\n }\n const bounds = target.globalBounds();\n let width = bounds.right == bounds.left ? target.width() : bounds.right - bounds.left;\n let height = bounds.top == bounds.bottom ? target.height() : bounds.bottom - bounds.top;\n const padding = this._settings.focusPadding !== undefined ? this._settings.focusPadding : 2;\n let x = bounds.left - padding;\n let y = bounds.top - padding;\n if (width < 0) {\n x += width;\n width = Math.abs(width);\n }\n if (height < 0) {\n y += height;\n height = Math.abs(height);\n }\n const focusElement = target.getPrivate(\"focusElement\").dom;\n focusElement.style.top = y + \"px\";\n focusElement.style.left = x + \"px\";\n focusElement.style.width = width + padding * 2 + \"px\";\n focusElement.style.height = height + padding * 2 + \"px\";\n }\n _getSpriteByFocusElement(target) {\n let found;\n $array.eachContinue(this._tabindexes, (item, _index) => {\n if (item.getPrivate(\"focusElement\").dom === target) {\n found = item;\n return false;\n }\n return true;\n });\n return found;\n }\n _handleFocus(ev) {\n if (this._a11yD == true) {\n return;\n }\n // Get element\n //const focused = this._tabindexes[index];\n const focused = this._getSpriteByFocusElement(ev.target);\n if (!focused) {\n return;\n }\n // Jump over hidden elements\n if (!focused.isVisibleDeep()) {\n this._focusNext(ev.target, this._isShift ? -1 : 1);\n return;\n }\n // Size and position\n this._positionFocusElement(focused);\n //this._decorateFocusElement(focused);\n this._focusedSprite = focused;\n if (focused.events.isEnabled(\"focus\")) {\n focused.events.dispatch(\"focus\", {\n type: \"focus\",\n originalEvent: ev,\n target: focused\n });\n }\n }\n _focusNext(el, direction) {\n if (this._a11yD == true) {\n return;\n }\n const focusableElements = Array.from(document.querySelectorAll(['a[href]', 'area[href]', 'button:not([disabled])', 'details', 'input:not([disabled])', 'iframe:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[contentEditable=\"\"]', '[contentEditable=\"true\"]', '[contentEditable=\"TRUE\"]', '[tabindex]:not([tabindex^=\"-\"])'\n //':not([disabled])'\n ].join(',')));\n let index = focusableElements.indexOf(el) + direction;\n if (index < 0) {\n index = focusableElements.length - 1;\n } else if (index >= focusableElements.length) {\n index = 0;\n }\n focusableElements[index].focus();\n }\n _handleBlur(ev) {\n if (this._a11yD == true) {\n return;\n }\n const focused = this._focusedSprite;\n if (focused && !focused.isDisposed() && focused.events.isEnabled(\"blur\")) {\n focused.events.dispatch(\"blur\", {\n type: \"blur\",\n originalEvent: ev,\n target: focused\n });\n }\n this._focusedSprite = undefined;\n }\n /**\r\n * @ignore\r\n */\n updateTooltip(target) {\n if (this._a11yD == true) {\n return;\n }\n const text = $utils.stripTags(target._getText());\n let tooltipElement = target.getPrivate(\"tooltipElement\");\n if (target.get(\"role\") == \"tooltip\" && text != \"\") {\n if (!tooltipElement) {\n tooltipElement = this._makeTooltipElement(target);\n }\n if (tooltipElement.innerHTML != text) {\n tooltipElement.innerHTML = text;\n }\n tooltipElement.setAttribute(\"aria-hidden\", target.isVisibleDeep() ? \"false\" : \"true\");\n } else if (tooltipElement) {\n tooltipElement.remove();\n target.removePrivate(\"tooltipElement\");\n }\n }\n _makeTooltipElement(target) {\n const container = this._tooltipElementContainer;\n const tooltipElement = document.createElement(\"div\");\n tooltipElement.style.position = \"absolute\";\n tooltipElement.style.width = \"1px\";\n tooltipElement.style.height = \"1px\";\n tooltipElement.style.overflow = \"hidden\";\n tooltipElement.style.clip = \"rect(1px, 1px, 1px, 1px)\";\n $utils.setInteractive(tooltipElement, false);\n this._decorateFocusElement(target, tooltipElement);\n container.append(tooltipElement);\n target.setPrivate(\"tooltipElement\", tooltipElement);\n return tooltipElement;\n }\n _removeTooltipElement(target) {\n if (this._a11yD == true) {\n return;\n }\n const tooltipElement = target.getPrivate(\"tooltipElement\");\n if (tooltipElement) {\n const parent = tooltipElement.parentElement;\n if (parent) {\n parent.removeChild(tooltipElement);\n }\n }\n }\n _invalidateAccessibility(target) {\n if (this._a11yD == true) {\n return;\n }\n this._focusElementDirty = true;\n const focusElement = target.getPrivate(\"focusElement\");\n if (target.get(\"focusable\")) {\n if (focusElement) {\n this._decorateFocusElement(target);\n this._positionFocusElement(target);\n }\n // else {\n // \tthis._renderer._makeFocusElement(0, this);\n // }\n } else if (focusElement) {\n this._removeFocusElement(target);\n }\n //this.updateCurrentFocus();\n }\n /**\r\n * Returns `true` if `target` is currently focused.\r\n *\r\n * @param target Target\r\n * @return Focused?\r\n */\n focused(target) {\n return this._focusedSprite === target;\n }\n /**\r\n * Converts document coordinates to coordinates withing root element.\r\n *\r\n * @param point Document point\r\n * @return Root point\r\n */\n documentPointToRoot(point) {\n const rect = this._getRealSize();\n const size = this._getCalculatedSize(rect);\n const scaleWidth = size.width / rect.width;\n const scaleHeight = size.height / rect.height;\n return {\n x: (point.x - rect.left) * scaleWidth,\n y: (point.y - rect.top) * scaleHeight\n };\n }\n /**\r\n * Converts root coordinates to document\r\n *\r\n * @param point Document point\r\n * @return Root point\r\n */\n rootPointToDocument(point) {\n const rect = this._getRealSize();\n const size = this._getCalculatedSize(rect);\n const scaleWidth = size.width / rect.width;\n const scaleHeight = size.height / rect.height;\n return {\n x: point.x / scaleWidth + rect.left,\n y: point.y / scaleHeight + rect.top\n };\n }\n /**\r\n * @ignore\r\n */\n addDisposer(disposer) {\n this._disposers.push(disposer);\n return disposer;\n }\n _updateComputedStyles() {\n const styles = window.getComputedStyle(this.dom);\n let fontHash = \"\";\n $object.each(styles, (key, val) => {\n if ($type.isString(key) && key.match(/^font/)) {\n fontHash += val;\n }\n });\n const changed = fontHash != this._fontHash;\n if (changed) {\n this._fontHash = fontHash;\n }\n return changed;\n }\n _checkComputedStyles() {\n if (this._updateComputedStyles()) {\n this._invalidateLabelBounds(this.container);\n }\n }\n _invalidateLabelBounds(target) {\n if (target instanceof Container) {\n target.children.each(child => {\n this._invalidateLabelBounds(child);\n });\n } else if (target instanceof Text) {\n target.markDirtyBounds();\n }\n }\n /**\r\n * To all the clever heads out there. Yes, we did not make any attempts to\r\n * scramble this.\r\n *\r\n * This is a part of a tool meant for our users to manage their commercial\r\n * licenses for removal of amCharts branding from charts.\r\n *\r\n * The only legit way to do so is to purchase a commercial license for amCharts:\r\n * https://www.amcharts.com/online-store/\r\n *\r\n * Removing or altering this code, or disabling amCharts branding in any other\r\n * way is against the license and thus illegal.\r\n */\n _hasLicense() {\n for (let i = 0; i < registry.licenses.length; i++) {\n if (registry.licenses[i].match(/^AM5C.{5,}/i)) {\n return true;\n }\n }\n return false;\n }\n _licenseApplied() {\n if (this._logo) {\n this._logo.set(\"forceHidden\", true);\n }\n }\n /**\r\n * @ignore\r\n */\n get debugGhostView() {\n return this._renderer.debugGhostView;\n }\n /**\r\n * @ignore\r\n */\n set debugGhostView(value) {\n this._renderer.debugGhostView = value;\n }\n /**\r\n * Set this to `true` if you need chart to require first a tap onto it before\r\n * touch gesture related functionality like zoom/pan is turned on.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/root-element/#Touch_related_options} for more info\r\n * @default false\r\n * @since 5.2.9\r\n * @param value Needs a tap to activate touch functions\r\n */\n set tapToActivate(value) {\n this._renderer.tapToActivate = value;\n }\n /**\r\n * @return Needs a tap to activate touch functions\r\n */\n get tapToActivate() {\n return this._renderer.tapToActivate;\n }\n /**\r\n * If `tapToActivate` is set to `true`, this setting will determine number\r\n * of milliseconds the chart will stay \"active\", before releasing the\r\n * controls back to the page.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/getting-started/root-element/#Touch_related_options} for more info\r\n * @default 3000\r\n * @since 5.2.9\r\n * @param value Timeout\r\n */\n set tapToActivateTimeout(value) {\n this._renderer.tapToActivateTimeout = value;\n }\n /**\r\n * @return Timeout\r\n */\n get tapToActivateTimeout() {\n return this._renderer.tapToActivateTimeout;\n }\n _makeHTMLElement(target) {\n // Get container\n const container = this._htmlElementContainer;\n // Init\n const htmlElement = document.createElement(\"div\");\n target.setPrivate(\"htmlElement\", htmlElement);\n //htmlElement.tabIndex = this.tabindex;\n htmlElement.style.position = \"absolute\";\n htmlElement.style.overflow = \"auto\";\n htmlElement.style.boxSizing = \"border-box\";\n $utils.setInteractive(htmlElement, target.get(\"interactive\", false));\n // Translate events\n if (target.events.isEnabled(\"click\")) {\n $utils.setInteractive(htmlElement, true);\n this._disposers.push($utils.addEventListener(htmlElement, \"click\", ev => {\n const downEvent = this._renderer.getEvent(ev);\n target.events.dispatch(\"click\", {\n type: \"click\",\n originalEvent: downEvent.event,\n point: downEvent.point,\n simulated: false,\n target: target\n });\n }));\n }\n this._positionHTMLElement(target);\n container.append(htmlElement);\n $array.pushOne(this._htmlEnabledContainers, target);\n return htmlElement;\n }\n _positionHTMLElements() {\n $array.each(this._htmlEnabledContainers, target => {\n this._positionHTMLElement(target);\n });\n }\n _positionHTMLElement(target) {\n const htmlElement = target.getPrivate(\"htmlElement\");\n if (htmlElement) {\n // Translate settings\n const visualSettings = [\"paddingTop\", \"paddingRight\", \"paddingBottom\", \"paddingLeft\", \"minWidth\", \"minHeight\", \"maxWidth\", \"maxHeight\"];\n $array.each(visualSettings, setting => {\n const value = target.get(setting);\n if (value) {\n htmlElement.style[setting] = value + \"px\";\n } else {\n htmlElement.style[setting] = \"\";\n }\n });\n // Init and reset scale / rotation\n const scale = target.compositeScale() || 1;\n const rotation = target.compositeRotation() || 0;\n htmlElement.style.transform = \"\";\n htmlElement.style.transformOrigin = \"\";\n // Deal with opacity\n const opacity = target.compositeOpacity();\n setTimeout(() => {\n htmlElement.style.opacity = opacity + \"\";\n }, 10);\n const visible = target.isVisibleDeep();\n if (visible) {\n htmlElement.style.display = \"block\";\n }\n // Deal with position\n // const bounds = target.globalBounds();\n // htmlElement.style.top = (bounds.top) + \"px\";\n // htmlElement.style.left = (bounds.left) + \"px\";\n let pos = {\n x: target.x(),\n y: target.y()\n };\n if (target.parent) {\n pos = target.parent.toGlobal(pos);\n }\n htmlElement.style.top = pos.y + \"px\";\n htmlElement.style.left = pos.x + \"px\";\n // Use width/height if those are set\n const width = target.get(\"width\");\n const height = target.get(\"height\");\n let w = 0;\n let h = 0;\n if (width) {\n w = target.width();\n }\n if (height) {\n h = target.height();\n }\n if (!width || !height) {\n htmlElement.style.position = \"fixed\";\n htmlElement.style.width = \"\";\n htmlElement.style.height = \"\";\n const bbox = htmlElement.getBoundingClientRect();\n htmlElement.style.position = \"absolute\";\n if (!width) w = bbox.width;\n if (!height) h = bbox.height;\n let lw = w / scale;\n let lh = h / scale;\n let cx = target.get(\"centerX\", 0);\n let cy = target.get(\"centerY\", 0);\n let ll = 0;\n let lr = 0;\n let lt = 0;\n let lb = 0;\n if (cx instanceof Percent) {\n ll = -cx.value * lw;\n lr = (1 - cx.value) * lw;\n } else {\n ll = -cx;\n lr = lw - cx;\n }\n if (cy instanceof Percent) {\n lt = -cy.value * lh;\n lb = (1 - cy.value) * lh;\n } else {\n lt = -cy;\n lb = lh - cy;\n }\n target._localBounds = {\n left: ll,\n right: lr,\n top: lt,\n bottom: lb\n };\n let previousBounds = target._adjustedLocalBounds;\n let newBounds = target._display.getAdjustedBounds(target._localBounds);\n target._adjustedLocalBounds = newBounds;\n // compare each value of the bounds\n if (previousBounds.left !== newBounds.left || previousBounds.right !== newBounds.right || previousBounds.top !== newBounds.top || previousBounds.bottom !== newBounds.bottom) {\n target.markDirtyBounds();\n }\n }\n if (w > 0) {\n htmlElement.style.minWidth = w + \"px\";\n }\n if (h > 0) {\n htmlElement.style.minHeight = h + \"px\";\n }\n // Hide or show\n if (!visible || opacity == 0) {\n htmlElement.style.display = \"none\";\n }\n // Center position\n const x = target.get(\"centerX\", 0);\n const originX = isPercent(x) ? x.percent + \"%\" : x + \"px\";\n const y = target.get(\"centerY\", 0);\n const originY = isPercent(y) ? y.percent + \"%\" : y + \"px\";\n if (x || y) {\n htmlElement.style.transform = \"translate(-\" + originX + \", -\" + originY + \")\" + htmlElement.style.transform;\n }\n // Deal with scale\n if (scale != 1) {\n htmlElement.style.transform += \"scale(\" + scale + \")\";\n }\n if (rotation != 0) {\n htmlElement.style.transform += \" rotate(\" + rotation + \"deg)\";\n }\n if (htmlElement.style.transform != \"\") {\n htmlElement.style.transformOrigin = originX + \" \" + originY;\n //htmlElement.style.transformOrigin = \"0% 0%\";\n }\n }\n }\n _setHTMLContent(target, html) {\n let htmlElement = target.getPrivate(\"htmlElement\");\n if (!htmlElement) {\n htmlElement = this._makeHTMLElement(target);\n }\n if (htmlElement.innerHTML != html) {\n htmlElement.innerHTML = html;\n }\n }\n _removeHTMLContent(target) {\n let htmlElement = target.getPrivate(\"htmlElement\");\n if (htmlElement) {\n this._htmlElementContainer.removeChild(htmlElement);\n target.removePrivate(\"htmlElement\");\n }\n $array.remove(this._htmlEnabledContainers, target);\n }\n}\n","import * as $type from \"../util/Type\";\nimport * as $math from \"../util/Math\";\nimport * as $utils from \"../util/Utils\";\nimport { Rectangle } from \"./Rectangle\";\n/**\r\n * Draws a rectangle with rounded corners.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/graphics/} for more info\r\n * @important\r\n */\nexport class RoundedRectangle extends Rectangle {\n _beforeChanged() {\n super._beforeChanged();\n if (this.isDirty(\"cornerRadiusTL\") || this.isDirty(\"cornerRadiusTR\") || this.isDirty(\"cornerRadiusBR\") || this.isDirty(\"cornerRadiusBL\")) {\n this._clear = true;\n }\n }\n _draw() {\n let width = this.width();\n let height = this.height();\n let w = width;\n let h = height;\n let wSign = w / Math.abs(width);\n let hSign = h / Math.abs(height);\n if ($type.isNumber(w) && $type.isNumber(h)) {\n let minSide = Math.min(w, h) / 2;\n let crtl = $utils.relativeToValue(this.get(\"cornerRadiusTL\", 8), minSide);\n let crtr = $utils.relativeToValue(this.get(\"cornerRadiusTR\", 8), minSide);\n let crbr = $utils.relativeToValue(this.get(\"cornerRadiusBR\", 8), minSide);\n let crbl = $utils.relativeToValue(this.get(\"cornerRadiusBL\", 8), minSide);\n let maxcr = Math.min(Math.abs(w / 2), Math.abs(h / 2));\n crtl = $math.fitToRange(crtl, 0, maxcr);\n crtr = $math.fitToRange(crtr, 0, maxcr);\n crbr = $math.fitToRange(crbr, 0, maxcr);\n crbl = $math.fitToRange(crbl, 0, maxcr);\n const display = this._display;\n display.moveTo(crtl * wSign, 0);\n display.lineTo(w - crtr * wSign, 0);\n if (crtr > 0) {\n display.arcTo(w, 0, w, crtr * hSign, crtr);\n }\n display.lineTo(w, h - crbr * hSign);\n if (crbr > 0) {\n display.arcTo(w, h, w - crbr * wSign, h, crbr);\n }\n display.lineTo(crbl * wSign, h);\n if (crbl > 0) {\n display.arcTo(0, h, 0, h - crbl * hSign, crbl);\n }\n display.lineTo(0, crtl * hSign);\n if (crtl > 0) {\n display.arcTo(0, 0, crtl * wSign, 0, crtl);\n }\n display.closePath();\n }\n }\n}\nObject.defineProperty(RoundedRectangle, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"RoundedRectangle\"\n});\nObject.defineProperty(RoundedRectangle, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Rectangle.classNames.concat([RoundedRectangle.className])\n});\n","import { RoundedRectangle } from \"../render/RoundedRectangle\";\nimport { Container } from \"./Container\";\nimport * as $utils from \"../../core/util/Utils\";\n/**\r\n * Draws an interactive button.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/buttons/} for more info\r\n * @important\r\n */\nexport class Button extends Container {\n _afterNew() {\n this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, [\"button\"]);\n super._afterNew();\n if (!this._settings.background) {\n this.set(\"background\", RoundedRectangle.new(this._root, {\n themeTags: $utils.mergeTags(this._settings.themeTags, [\"background\"])\n }));\n }\n this.setPrivate(\"trustBounds\", true);\n }\n _prepareChildren() {\n super._prepareChildren();\n if (this.isDirty(\"icon\")) {\n const previous = this._prevSettings.icon;\n const icon = this.get(\"icon\");\n if (icon !== previous) {\n this._disposeProperty(\"icon\");\n if (previous) {\n previous.dispose();\n }\n if (icon) {\n this.children.push(icon);\n }\n this._prevSettings.icon = icon;\n }\n }\n if (this.isDirty(\"label\")) {\n const previous = this._prevSettings.label;\n const label = this.get(\"label\");\n if (label !== previous) {\n this._disposeProperty(\"label\");\n if (previous) {\n previous.dispose();\n }\n if (label) {\n this.children.push(label);\n }\n this._prevSettings.label = label;\n }\n }\n }\n}\nObject.defineProperty(Button, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Button\"\n});\nObject.defineProperty(Button, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Container.classNames.concat([Button.className])\n});\n","import { List } from \"./List\";\n/**\r\n * A [[List]] that holds components data.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/data/} for more info\r\n */\nexport class ListData extends List {\n constructor() {\n super(...arguments);\n /**\r\n * An optional processor for data.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/data/#Pre_processing_data} for more info\r\n */\n Object.defineProperty(this, \"processor\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n }\n /**\r\n * @ignore\r\n */\n incrementRef() {}\n /**\r\n * @ignore\r\n */\n decrementRef() {}\n _onPush(newValue) {\n if (this.processor) {\n this.processor.processRow(newValue);\n }\n super._onPush(newValue);\n }\n _onInsertIndex(index, newValue) {\n if (this.processor) {\n this.processor.processRow(newValue);\n }\n super._onInsertIndex(index, newValue);\n }\n _onSetIndex(index, oldValue, newValue) {\n if (this.processor) {\n this.processor.processRow(newValue);\n }\n super._onSetIndex(index, oldValue, newValue);\n }\n}\n/**\r\n * @deprecated\r\n * @todo remove\r\n */\nexport class JsonData {\n constructor(value) {\n Object.defineProperty(this, \"processor\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_value\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this._value = value;\n }\n incrementRef() {}\n decrementRef() {}\n}\n","import { __awaiter } from \"tslib\";\nimport { Settings } from \"../util/Entity\";\nimport { Container } from \"./Container\";\nimport { ListData } from \"../util/Data\";\nimport * as $array from \"../util/Array\";\nimport * as $object from \"../util/Object\";\n/**\r\n * A base element that holds data bit (data item) for any [[Component]].\r\n */\nexport class DataItem extends Settings {\n constructor(component, dataContext, settings) {\n super(settings);\n /**\r\n * A data item's owener [[Component]].\r\n */\n Object.defineProperty(this, \"component\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * A reference to actual item in source data this item is based on.\r\n */\n Object.defineProperty(this, \"dataContext\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * @todo requires description\r\n */\n Object.defineProperty(this, \"bullets\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * A set of \"open\" values.\r\n */\n Object.defineProperty(this, \"open\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * A set of \"close\" values.\r\n */\n Object.defineProperty(this, \"close\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n this.dataContext = dataContext;\n this.component = component;\n this._settings.visible = true;\n this._checkDirty();\n }\n /**\r\n * @ignore\r\n */\n markDirty() {\n this.component.markDirtyValues(this);\n }\n _startAnimation() {\n this.component._root._addAnimation(this);\n }\n _animationTime() {\n return this.component._root.animationTime;\n }\n _dispose() {\n if (this.component) {\n this.component.disposeDataItem(this);\n }\n super._dispose();\n }\n /**\r\n * Shows a data item that's currently hidden.\r\n */\n show(duration) {\n this.setRaw(\"visible\", true);\n if (this.component) {\n this.component.showDataItem(this, duration);\n }\n }\n /**\r\n * Hides a data item that's currently visible.\r\n */\n hide(duration) {\n this.setRaw(\"visible\", false);\n if (this.component) {\n this.component.hideDataItem(this, duration);\n }\n }\n isHidden() {\n return !this.get(\"visible\");\n }\n}\n/**\r\n * A base class for elements that make use of data.\r\n */\nexport class Component extends Container {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_data\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new ListData()\n });\n Object.defineProperty(this, \"_dataItems\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"_mainDataItems\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this._dataItems\n });\n Object.defineProperty(this, \"valueFields\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: []\n });\n Object.defineProperty(this, \"fields\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: [\"id\"]\n });\n Object.defineProperty(this, \"_valueFields\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_valueFieldsF\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_fields\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_fieldsF\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_valuesDirty\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_dataChanged\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_dataGrouped\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n /**\r\n * Indicates if the component has already been initialized.\r\n */\n Object.defineProperty(this, \"inited\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n }\n /**\r\n * Component's data.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/data/} for more info\r\n */\n set data(data) {\n data.incrementRef();\n this._data.decrementRef();\n this._data = data;\n }\n /**\r\n * @return Data\r\n */\n get data() {\n return this._data;\n }\n _dispose() {\n super._dispose();\n this._data.decrementRef();\n }\n _onDataClear() {}\n _afterNew() {\n super._afterNew();\n this._data.incrementRef();\n this._updateFields();\n this._disposers.push(this.data.events.onAll(change => {\n const dataItems = this._mainDataItems;\n this.markDirtyValues();\n this._markDirtyGroup();\n this._dataChanged = true;\n if (change.type === \"clear\") {\n $array.each(dataItems, dataItem => {\n dataItem.dispose();\n });\n dataItems.length = 0;\n this._onDataClear();\n } else if (change.type === \"push\") {\n const dataItem = new DataItem(this, change.newValue, this._makeDataItem(change.newValue));\n dataItems.push(dataItem);\n this.processDataItem(dataItem);\n } else if (change.type === \"setIndex\") {\n const dataItem = dataItems[change.index];\n const properties = this._makeDataItem(change.newValue);\n if (dataItem.bullets && dataItem.bullets.length == 0) {\n dataItem.bullets = undefined;\n }\n $object.keys(properties).forEach(key => {\n dataItem.animate({\n key: key,\n to: properties[key],\n duration: this.get(\"interpolationDuration\", 0),\n easing: this.get(\"interpolationEasing\")\n });\n });\n dataItem.dataContext = change.newValue;\n } else if (change.type === \"insertIndex\") {\n const dataItem = new DataItem(this, change.newValue, this._makeDataItem(change.newValue));\n dataItems.splice(change.index, 0, dataItem);\n this.processDataItem(dataItem);\n } else if (change.type === \"removeIndex\") {\n const dataItem = dataItems[change.index];\n dataItem.dispose();\n dataItems.splice(change.index, 1);\n } else if (change.type === \"moveIndex\") {\n const dataItem = dataItems[change.oldIndex];\n dataItems.splice(change.oldIndex, 1);\n dataItems.splice(change.newIndex, 0, dataItem);\n } else {\n throw new Error(\"Unknown IStreamEvent type\");\n }\n this._afterDataChange();\n }));\n }\n _updateFields() {\n if (this.valueFields) {\n this._valueFields = [];\n this._valueFieldsF = {};\n $array.each(this.valueFields, key => {\n const field = this.get(key + \"Field\");\n if (field) {\n this._valueFields.push(key);\n this._valueFieldsF[key] = {\n fieldKey: key + \"Field\",\n workingKey: key + \"Working\"\n };\n }\n });\n }\n if (this.fields) {\n this._fields = [];\n this._fieldsF = {};\n $array.each(this.fields, key => {\n const field = this.get(key + \"Field\");\n if (field) {\n this._fields.push(key);\n this._fieldsF[key] = key + \"Field\";\n }\n });\n }\n }\n /**\r\n * A list of component's data items.\r\n *\r\n * @return Data items\r\n */\n get dataItems() {\n return this._dataItems;\n }\n processDataItem(_dataItem) {}\n _makeDataItem(data) {\n //const output: this[\"_dataItemSettings\"] = {};\n const output = {}; // temporary to solve error\n if (this._valueFields) {\n $array.each(this._valueFields, key => {\n const field = this.get(this._valueFieldsF[key].fieldKey);\n output[key] = data[field];\n output[this._valueFieldsF[key].workingKey] = output[key];\n });\n }\n if (this._fields) {\n $array.each(this._fields, key => {\n const field = this.get(this._fieldsF[key]);\n output[key] = data[field];\n });\n }\n return output;\n }\n /**\r\n * Creates a new data item and processes it.\r\n *\r\n * @param data Data item settings\r\n * @param dataContext Data context\r\n * @return New data item\r\n */\n makeDataItem(data, dataContext) {\n let dataItem = new DataItem(this, dataContext, data);\n this.processDataItem(dataItem);\n return dataItem;\n }\n /**\r\n * Adds new explicit data item to series.\r\n *\r\n * @param data Data item settings\r\n * @param dataContext Data context\r\n * @return New data item\r\n */\n pushDataItem(data, dataContext) {\n const dataItem = this.makeDataItem(data, dataContext);\n this._mainDataItems.push(dataItem);\n return dataItem;\n }\n /**\r\n * @ignore\r\n */\n disposeDataItem(_dataItem) {}\n /**\r\n * Shows component's data item.\r\n *\r\n * @param dataItem Data item\r\n * @param _duration Animation duration in milliseconds\r\n * @return Promise\r\n */\n showDataItem(dataItem, _duration) {\n return __awaiter(this, void 0, void 0, function* () {\n dataItem.set(\"visible\", true);\n });\n }\n /**\r\n * Hides component's data item.\r\n *\r\n * @param dataItem Data item\r\n * @param _duration Animation duration in milliseconds\r\n * @return Promise\r\n */\n hideDataItem(dataItem, _duration) {\n return __awaiter(this, void 0, void 0, function* () {\n dataItem.set(\"visible\", false);\n });\n }\n _clearDirty() {\n super._clearDirty();\n this._valuesDirty = false;\n }\n _afterDataChange() {}\n _afterChanged() {\n super._afterChanged();\n if (this._dataChanged) {\n const type = \"datavalidated\";\n if (this.events.isEnabled(type)) {\n this.events.dispatch(type, {\n type: type,\n target: this\n });\n }\n this._dataChanged = false;\n }\n this.inited = true;\n }\n /**\r\n * Forces a repaint of the element which relies on data.\r\n *\r\n * @since 5.0.21\r\n */\n markDirtyValues(_dataItem) {\n this.markDirty();\n this._valuesDirty = true;\n }\n _markDirtyGroup() {\n this._dataGrouped = false;\n }\n /**\r\n * @ignore\r\n */\n markDirtySize() {\n this._sizeDirty = true;\n this.markDirty();\n }\n}\nObject.defineProperty(Component, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Component\"\n});\nObject.defineProperty(Component, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Container.classNames.concat([Component.className])\n});\n","/**\r\n * ============================================================================\r\n * IMPORTS\r\n * ============================================================================\r\n * @hidden\r\n */\nimport * as $type from \"./Type\";\nimport * as $utils from \"./Utils\";\n/**\r\n * Returns a `Promise` which can be used to execute code after number of\r\n * milliseconds.\r\n *\r\n * @param ms Sleep duration in ms\r\n * @return Promise\r\n */\nexport function sleep(ms) {\n return new Promise((resolve, _reject) => {\n setTimeout(resolve, ms);\n });\n}\n/**\r\n * Maps time period names to their numeric representations in milliseconds.\r\n *\r\n * @ignore Exclude from docs\r\n */\nexport let timeUnitDurations = {\n millisecond: 1,\n second: 1000,\n minute: 60000,\n hour: 3600000,\n day: 86400000,\n week: 604800000,\n month: 365.242 / 12 * 86400000,\n year: 31536000000\n};\n/**\r\n * Returns the next time unit that goes after source `unit`.\r\n *\r\n * E.g. \"hour\" is the next unit after \"minute\", etc.\r\n *\r\n * @ignore Exclude from docs\r\n * @param unit Source time unit\r\n * @return Next time unit\r\n */\nexport function getNextUnit(unit) {\n switch (unit) {\n case \"year\":\n return;\n case \"month\":\n return \"year\";\n case \"week\":\n return \"month\";\n case \"day\":\n return \"month\";\n // not a mistake\n case \"hour\":\n return \"day\";\n case \"minute\":\n return \"hour\";\n case \"second\":\n return \"minute\";\n case \"millisecond\":\n return \"second\";\n }\n}\n/**\r\n * Returns number of milliseconds in the `count` of time `unit`.\r\n *\r\n * Available units: \"millisecond\", \"second\", \"minute\", \"hour\", \"day\", \"week\",\r\n * \"month\", and \"year\".\r\n *\r\n * @param unit Time unit\r\n * @param count Number of units\r\n * @return Milliseconds\r\n */\nexport function getDuration(unit, count) {\n if (count == null) {\n count = 1;\n }\n return timeUnitDurations[unit] * count;\n}\n/**\r\n * @ignore\r\n */\nexport function getIntervalDuration(interval) {\n if (interval) {\n return timeUnitDurations[interval.timeUnit] * interval.count;\n }\n return 0;\n}\nexport function getDateIntervalDuration(interval, date, firstDateOfWeek, utc, timezone) {\n const unit = interval.timeUnit;\n const count = interval.count;\n if (unit == \"hour\" || unit == \"minute\" || unit == \"second\" || unit == \"millisecond\") {\n return timeUnitDurations[interval.timeUnit] * interval.count;\n } else {\n const firstTime = round(new Date(date.getTime()), unit, count, firstDateOfWeek, utc, undefined, timezone).getTime();\n let lastTime = firstTime + count * getDuration(unit) * 1.05;\n lastTime = round(new Date(lastTime), unit, 1, firstDateOfWeek, utc, undefined, timezone).getTime();\n return lastTime - firstTime;\n }\n}\n/**\r\n * Returns current `Date` object.\r\n *\r\n * @return Current date\r\n */\nexport function now() {\n return new Date();\n}\n/**\r\n * Returns current timestamp.\r\n *\r\n * @return Current timestamp\r\n */\nexport function getTime() {\n return now().getTime();\n}\n/**\r\n * Returns a copy of the `Date` object.\r\n *\r\n * @param date Source date\r\n * @return Copy\r\n */\nexport function copy(date) {\n return new Date(date.getTime()); // todo: check if this is ok. new Date(date) used to strip milliseconds on FF in v3\n}\n/**\r\n * Checks if the `unit` part of two `Date` objects do not match. Two dates\r\n * represent a \"range\" of time, rather the same time date.\r\n *\r\n * @param timeOne timestamp\r\n * @param timeTwo timestamp\r\n * @param unit Time unit to check\r\n * @return Range?\r\n */\nexport function checkChange(timeOne, timeTwo, unit, utc, timezone) {\n // quick\n if (timeTwo - timeOne > getDuration(unit, 1.2)) {\n return true;\n }\n let dateOne = new Date(timeOne);\n let dateTwo = new Date(timeTwo);\n if (timezone) {\n dateOne = timezone.convertLocal(dateOne);\n dateTwo = timezone.convertLocal(dateTwo);\n }\n let timeZoneOffset1 = 0;\n let timeZoneOffset2 = 0;\n if (!utc && unit != \"millisecond\") {\n timeZoneOffset1 = dateOne.getTimezoneOffset();\n dateOne.setUTCMinutes(dateOne.getUTCMinutes() - timeZoneOffset1);\n timeZoneOffset2 = dateTwo.getTimezoneOffset();\n dateTwo.setUTCMinutes(dateTwo.getUTCMinutes() - timeZoneOffset2);\n }\n let changed = false;\n switch (unit) {\n case \"year\":\n if (dateOne.getUTCFullYear() != dateTwo.getUTCFullYear()) {\n changed = true;\n }\n break;\n case \"month\":\n if (dateOne.getUTCFullYear() != dateTwo.getUTCFullYear()) {\n changed = true;\n } else if (dateOne.getUTCMonth() != dateTwo.getUTCMonth()) {\n changed = true;\n }\n break;\n case \"day\":\n if (dateOne.getUTCMonth() != dateTwo.getUTCMonth()) {\n changed = true;\n } else if (dateOne.getUTCDate() != dateTwo.getUTCDate()) {\n changed = true;\n }\n break;\n case \"hour\":\n if (dateOne.getUTCHours() != dateTwo.getUTCHours()) {\n changed = true;\n }\n break;\n case \"minute\":\n if (dateOne.getUTCMinutes() != dateTwo.getUTCMinutes()) {\n changed = true;\n }\n break;\n case \"second\":\n if (dateOne.getUTCSeconds() != dateTwo.getUTCSeconds()) {\n changed = true;\n }\n break;\n case \"millisecond\":\n if (dateOne.getTime() != dateTwo.getTime()) {\n changed = true;\n }\n break;\n }\n if (changed) {\n return changed;\n }\n let nextUnit = getNextUnit(unit);\n if (nextUnit) {\n return checkChange(timeOne, timeTwo, nextUnit, utc, timezone);\n } else {\n return false;\n }\n}\n/**\r\n * Adds `count` of time `unit` to the source date. Returns a modified `Date` object.\r\n *\r\n * @param date Source date\r\n * @param unit Time unit\r\n * @param count Number of units to add\r\n * @return Modified date\r\n */\nexport function add(date, unit, count, utc, timezone) {\n let timeZoneOffset = 0;\n if (!utc && unit != \"millisecond\") {\n timeZoneOffset = date.getTimezoneOffset();\n if (timezone) {\n timeZoneOffset -= timezone.offsetUTC(date);\n }\n date.setUTCMinutes(date.getUTCMinutes() - timeZoneOffset);\n }\n switch (unit) {\n case \"day\":\n let day = date.getUTCDate();\n date.setUTCDate(day + count);\n break;\n case \"second\":\n let seconds = date.getUTCSeconds();\n date.setUTCSeconds(seconds + count);\n break;\n case \"millisecond\":\n let milliseconds = date.getUTCMilliseconds();\n date.setUTCMilliseconds(milliseconds + count);\n break;\n case \"hour\":\n let hours = date.getUTCHours();\n date.setUTCHours(hours + count);\n break;\n case \"minute\":\n let minutes = date.getUTCMinutes();\n date.setUTCMinutes(minutes + count);\n break;\n case \"year\":\n let year = date.getUTCFullYear();\n date.setUTCFullYear(year + count);\n break;\n case \"month\":\n const endDays = date.getUTCDate();\n const startDays = new Date(date.getUTCFullYear(), date.getUTCMonth(), 0).getUTCDate();\n let month = date.getUTCMonth();\n if (endDays > startDays) {\n date.setUTCMonth(month + count, startDays);\n } else {\n date.setUTCMonth(month + count);\n }\n break;\n case \"week\":\n let wday = date.getUTCDate();\n date.setUTCDate(wday + count * 7);\n break;\n }\n if (!utc && unit != \"millisecond\") {\n date.setUTCMinutes(date.getUTCMinutes() + timeZoneOffset);\n if (unit == \"day\" || unit == \"week\" || unit == \"month\" || unit == \"year\") {\n let newTimeZoneOffset = date.getTimezoneOffset();\n if (timezone) {\n newTimeZoneOffset += timezone.offsetUTC(date);\n }\n if (newTimeZoneOffset != timeZoneOffset) {\n let diff = newTimeZoneOffset - timeZoneOffset;\n date.setUTCMinutes(date.getUTCMinutes() + diff);\n // solves issues if new time falls back to old time zone\n if (date.getTimezoneOffset() != newTimeZoneOffset) {\n date.setUTCMinutes(date.getUTCMinutes() - diff);\n }\n }\n }\n }\n return date;\n}\n/**\r\n * @ignore\r\n */\nexport function roun(time, unit, count, root, firstTime) {\n let firstDate;\n if (firstTime != null) {\n firstDate = new Date(firstTime);\n }\n return round(new Date(time), unit, count, root.locale.firstDayOfWeek, root.utc, firstDate, root.timezone).getTime();\n}\n/**\r\n * \"Rounds\" the date to specific time unit.\r\n *\r\n * @param date Source date\r\n * @param unit Time unit\r\n * @param count Number of units to round to\r\n * @param firstDateOfWeek First day of week\r\n * @param utc Use UTC timezone\r\n * @param firstDate First date to round to\r\n * @param roundMinutes Minutes to round to (some timezones use non-whole hour)\r\n * @param timezone Use specific named timezone when rounding\r\n * @return New date\r\n */\nexport function round(date, unit, count, firstDateOfWeek, utc, firstDate, timezone) {\n if (!timezone || utc) {\n let timeZoneOffset = 0;\n if (!utc && unit != \"millisecond\") {\n timeZoneOffset = date.getTimezoneOffset();\n date.setUTCMinutes(date.getUTCMinutes() - timeZoneOffset);\n }\n switch (unit) {\n case \"day\":\n let day = date.getUTCDate();\n if (count > 1) {\n //\tday = Math.floor(day / count) * count;\n if (firstDate) {\n firstDate = round(firstDate, \"day\", 1);\n let difference = date.getTime() - firstDate.getTime();\n let unitCount = Math.floor(difference / getDuration(\"day\") / count);\n let duration = getDuration(\"day\", unitCount * count);\n date.setTime(firstDate.getTime() + duration - timeZoneOffset * getDuration(\"minute\"));\n }\n } else {\n date.setUTCDate(day);\n }\n date.setUTCHours(0, 0, 0, 0);\n break;\n case \"second\":\n let seconds = date.getUTCSeconds();\n if (count > 1) {\n seconds = Math.floor(seconds / count) * count;\n }\n date.setUTCSeconds(seconds, 0);\n break;\n case \"millisecond\":\n if (count == 1) {\n return date; // much better for perf!\n }\n let milliseconds = date.getUTCMilliseconds();\n milliseconds = Math.floor(milliseconds / count) * count;\n date.setUTCMilliseconds(milliseconds);\n break;\n case \"hour\":\n let hours = date.getUTCHours();\n if (count > 1) {\n hours = Math.floor(hours / count) * count;\n }\n date.setUTCHours(hours, 0, 0, 0);\n break;\n case \"minute\":\n let minutes = date.getUTCMinutes();\n if (count > 1) {\n minutes = Math.floor(minutes / count) * count;\n }\n date.setUTCMinutes(minutes, 0, 0);\n break;\n case \"month\":\n let month = date.getUTCMonth();\n if (count > 1) {\n month = Math.floor(month / count) * count;\n }\n date.setUTCMonth(month, 1);\n date.setUTCHours(0, 0, 0, 0);\n break;\n case \"year\":\n let year = date.getUTCFullYear();\n if (count > 1) {\n year = Math.floor(year / count) * count;\n }\n date.setUTCFullYear(year, 0, 1);\n date.setUTCHours(0, 0, 0, 0);\n break;\n case \"week\":\n if (count > 1) {\n if (firstDate) {\n firstDate = round(firstDate, \"week\", 1);\n let difference = date.getTime() - firstDate.getTime();\n let unitCount = Math.floor(difference / getDuration(\"week\") / count);\n let duration = getDuration(\"week\", unitCount * count);\n date.setTime(firstDate.getTime() + duration - timeZoneOffset * getDuration(\"minute\"));\n }\n }\n let wday = date.getUTCDate();\n let weekDay = date.getUTCDay();\n if (!$type.isNumber(firstDateOfWeek)) {\n firstDateOfWeek = 1;\n }\n if (weekDay >= firstDateOfWeek) {\n wday = wday - weekDay + firstDateOfWeek;\n } else {\n wday = wday - (7 + weekDay) + firstDateOfWeek;\n }\n date.setUTCDate(wday);\n date.setUTCHours(0, 0, 0, 0);\n break;\n }\n if (!utc && unit != \"millisecond\") {\n date.setUTCMinutes(date.getUTCMinutes() + timeZoneOffset);\n if (unit == \"day\" || unit == \"week\" || unit == \"month\" || unit == \"year\") {\n let newTimeZoneOffset = date.getTimezoneOffset();\n if (newTimeZoneOffset != timeZoneOffset) {\n let diff = newTimeZoneOffset - timeZoneOffset;\n date.setUTCMinutes(date.getUTCMinutes() + diff);\n }\n }\n }\n return date;\n } else {\n if (isNaN(date.getTime())) {\n return date;\n }\n let tzoffset = timezone.offsetUTC(date);\n let timeZoneOffset = date.getTimezoneOffset();\n let parsedDate = timezone.parseDate(date);\n let year = parsedDate.year;\n let month = parsedDate.month;\n let day = parsedDate.day;\n let hour = parsedDate.hour;\n let minute = parsedDate.minute;\n let second = parsedDate.second;\n let millisecond = parsedDate.millisecond;\n let weekday = parsedDate.weekday;\n let offsetDif = tzoffset - timeZoneOffset;\n switch (unit) {\n case \"day\":\n if (count > 1 && firstDate) {\n firstDate = round(firstDate, \"day\", 1, firstDateOfWeek, utc, undefined, timezone);\n let difference = date.getTime() - firstDate.getTime();\n let unitCount = Math.floor(difference / getDuration(\"day\") / count);\n let duration = getDuration(\"day\", unitCount * count);\n date.setTime(firstDate.getTime() + duration);\n parsedDate = timezone.parseDate(date);\n year = parsedDate.year;\n month = parsedDate.month;\n day = parsedDate.day;\n }\n hour = 0;\n minute = offsetDif;\n second = 0;\n millisecond = 0;\n break;\n case \"second\":\n minute += offsetDif;\n if (count > 1) {\n second = Math.floor(second / count) * count;\n }\n millisecond = 0;\n break;\n case \"millisecond\":\n minute += offsetDif;\n if (count > 1) {\n millisecond = Math.floor(millisecond / count) * count;\n }\n break;\n case \"hour\":\n if (count > 1) {\n hour = Math.floor(hour / count) * count;\n }\n minute = offsetDif;\n second = 0;\n millisecond = 0;\n break;\n case \"minute\":\n if (count > 1) {\n minute = Math.floor(minute / count) * count;\n }\n minute += offsetDif;\n second = 0;\n millisecond = 0;\n break;\n case \"month\":\n if (count > 1) {\n month = Math.floor(month / count) * count;\n }\n day = 1;\n hour = 0;\n minute = offsetDif;\n second = 0;\n millisecond = 0;\n break;\n case \"year\":\n if (count > 1) {\n year = Math.floor(year / count) * count;\n }\n month = 0;\n day = 1;\n hour = 0;\n minute = offsetDif;\n second = 0;\n millisecond = 0;\n break;\n case \"week\":\n if (!$type.isNumber(firstDateOfWeek)) {\n firstDateOfWeek = 1;\n }\n if (weekday >= firstDateOfWeek) {\n day = day - weekday + firstDateOfWeek;\n } else {\n day = day - (7 + weekday) + firstDateOfWeek;\n }\n hour = 0;\n minute = offsetDif;\n second = 0;\n millisecond = 0;\n break;\n }\n date = new Date(year, month, day, hour, minute, second, millisecond);\n let newTimeZoneOffset = date.getTimezoneOffset();\n let newTzoffset = timezone.offsetUTC(date);\n let newDiff = newTzoffset - newTimeZoneOffset;\n if (newDiff != offsetDif) {\n date.setTime(date.getTime() + (newDiff - offsetDif) * 60000);\n }\n return date;\n }\n}\n/**\r\n * @ignore\r\n */\nexport function chooseInterval(index, duration, gridCount, intervals) {\n let gridInterval = intervals[index];\n let intervalDuration = getIntervalDuration(gridInterval);\n let lastIndex = intervals.length - 1;\n if (index >= lastIndex) {\n return Object.assign({}, intervals[lastIndex]);\n }\n let count = Math.ceil(duration / intervalDuration);\n if (duration < intervalDuration && index > 0) {\n return Object.assign({}, intervals[index - 1]);\n }\n if (count <= gridCount) {\n return Object.assign({}, intervals[index]);\n } else {\n if (index + 1 < intervals.length) {\n return chooseInterval(index + 1, duration, gridCount, intervals);\n } else {\n return Object.assign({}, intervals[index]);\n }\n }\n}\n/**\r\n * @ignore\r\n */\nexport function getUnitValue(date, unit) {\n switch (unit) {\n case \"day\":\n return date.getDate();\n case \"second\":\n return date.getSeconds();\n case \"millisecond\":\n return date.getMilliseconds();\n case \"hour\":\n return date.getHours();\n case \"minute\":\n return date.getMinutes();\n case \"month\":\n return date.getMonth();\n case \"year\":\n return date.getFullYear();\n case \"week\":\n return $utils.getWeek(date);\n }\n}\n","import { __awaiter } from \"tslib\";\nimport { Component } from \"../../core/render/Component\";\nimport { List } from \"../../core/util/List\";\nimport { Color } from \"../../core/util/Color\";\nimport { percentInterpolate } from \"../../core/util/Animation\";\nimport { Percent } from \"../../core/util/Percent\";\nimport { p100 } from \"../../core/util/Percent\";\nimport { Container } from \"../../core/render/Container\";\nimport { Label } from \"../../core/render/Label\";\n//import { Animations } from \"../../core/util/Animation\";\nimport * as $array from \"../../core/util/Array\";\nimport * as $type from \"../../core/util/Type\";\nimport * as $time from \"../../core/util/Time\";\n/**\r\n * A base class for all series.\r\n */\nexport class Series extends Component {\n constructor() {\n super(...arguments);\n Object.defineProperty(this, \"_aggregatesCalculated\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_selectionAggregatesCalculated\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_dataProcessed\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: false\n });\n Object.defineProperty(this, \"_psi\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n Object.defineProperty(this, \"_pei\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * A chart series belongs to.\r\n */\n Object.defineProperty(this, \"chart\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: void 0\n });\n /**\r\n * List of bullets to use for the series.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/bullets/} for more info\r\n */\n Object.defineProperty(this, \"bullets\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: new List()\n });\n /**\r\n * A [[Container]] series' bullets are stored in.\r\n *\r\n * @default Container.new()\r\n */\n Object.defineProperty(this, \"bulletsContainer\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Container.new(this._root, {\n width: p100,\n height: p100,\n position: \"absolute\"\n })\n });\n }\n _afterNew() {\n this.valueFields.push(\"value\", \"customValue\");\n super._afterNew();\n this.setPrivate(\"customData\", {});\n this._disposers.push(this.bullets.events.onAll(change => {\n if (change.type === \"clear\") {\n this._handleBullets(this.dataItems);\n } else if (change.type === \"push\") {\n this._handleBullets(this.dataItems);\n } else if (change.type === \"setIndex\") {\n this._handleBullets(this.dataItems);\n } else if (change.type === \"insertIndex\") {\n this._handleBullets(this.dataItems);\n } else if (change.type === \"removeIndex\") {\n this._handleBullets(this.dataItems);\n } else if (change.type === \"moveIndex\") {\n this._handleBullets(this.dataItems);\n } else {\n throw new Error(\"Unknown IListEvent type\");\n }\n }));\n }\n _dispose() {\n this.bulletsContainer.dispose(); // can be in a different parent\n super._dispose();\n }\n startIndex() {\n let len = this.dataItems.length;\n return Math.min(this.getPrivate(\"startIndex\", 0), len);\n }\n endIndex() {\n let len = this.dataItems.length;\n return Math.min(this.getPrivate(\"endIndex\", len), len);\n }\n _handleBullets(dataItems) {\n $array.each(dataItems, dataItem => {\n const bullets = dataItem.bullets;\n if (bullets) {\n $array.each(bullets, bullet => {\n bullet.dispose();\n });\n dataItem.bullets = undefined;\n }\n });\n this.markDirtyValues();\n }\n /**\r\n * Looks up and returns a data item by its ID.\r\n *\r\n * @param id ID\r\n * @return Data item\r\n */\n getDataItemById(id) {\n return $array.find(this.dataItems, dataItem => {\n return dataItem.get(\"id\") == id;\n });\n }\n _makeBullets(dataItem) {\n if (this._shouldMakeBullet(dataItem)) {\n dataItem.bullets = [];\n this.bullets.each(bulletFunction => {\n this._makeBullet(dataItem, bulletFunction);\n });\n }\n }\n _shouldMakeBullet(_dataItem) {\n return true;\n }\n _makeBullet(dataItem, bulletFunction, index) {\n const bullet = bulletFunction(this._root, this, dataItem);\n if (bullet) {\n bullet._index = index;\n this._makeBulletReal(dataItem, bullet);\n }\n return bullet;\n }\n _makeBulletReal(dataItem, bullet) {\n let sprite = bullet.get(\"sprite\");\n if (sprite) {\n sprite._setDataItem(dataItem);\n sprite.setRaw(\"position\", \"absolute\");\n this.bulletsContainer.children.push(sprite);\n }\n bullet.series = this;\n dataItem.bullets.push(bullet);\n }\n /**\r\n * Adds bullet directly to a data item.\r\n *\r\n * Please note: method accepts [[Bullet]] instance as a paramter, not a\r\n * reference to a function.\r\n *\r\n * You should add Bullet instance, not a method like you do it on series.\r\n *\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/common-elements/bullets/#Adding_directly_to_data_item} for more info\r\n * @since 5.6.0\r\n *\r\n * @param dataItem Target data item\r\n * @param bullet Bullet instance\r\n */\n addBullet(dataItem, bullet) {\n if (!dataItem.bullets) {\n dataItem.bullets = [];\n }\n if (bullet) {\n this._makeBulletReal(dataItem, bullet);\n }\n }\n _clearDirty() {\n super._clearDirty();\n this._aggregatesCalculated = false;\n this._selectionAggregatesCalculated = false;\n }\n _prepareChildren() {\n super._prepareChildren();\n let startIndex = this.startIndex();\n let endIndex = this.endIndex();\n if (this.isDirty(\"name\")) {\n this.updateLegendValue();\n }\n if (this.isDirty(\"heatRules\")) {\n this._valuesDirty = true;\n }\n if (this.isPrivateDirty(\"baseValueSeries\")) {\n const baseValueSeries = this.getPrivate(\"baseValueSeries\");\n if (baseValueSeries) {\n this._disposers.push(baseValueSeries.onPrivate(\"startIndex\", () => {\n this.markDirtyValues();\n }));\n }\n }\n const calculateAggregates = this.get(\"calculateAggregates\");\n if (calculateAggregates) {\n if (this._valuesDirty && !this._dataProcessed) {\n if (!this._aggregatesCalculated) {\n this._calculateAggregates(0, this.dataItems.length);\n this._aggregatesCalculated = true;\n if (startIndex != 0) {\n this._psi = undefined;\n }\n }\n }\n if ((this._psi != startIndex || this._pei != endIndex || this.isPrivateDirty(\"adjustedStartIndex\")) && !this._selectionAggregatesCalculated) {\n if (startIndex === 0 && endIndex === this.dataItems.length && this._aggregatesCalculated) {\n // void\n } else {\n this._calculateAggregates(startIndex, endIndex);\n }\n this._selectionAggregatesCalculated = true;\n }\n }\n if (this.isDirty(\"tooltip\")) {\n let tooltip = this.get(\"tooltip\");\n if (tooltip) {\n tooltip.hide(0);\n tooltip.set(\"tooltipTarget\", this);\n }\n }\n if (this.isDirty(\"fill\") || this.isDirty(\"stroke\")) {\n let markerRectangle;\n const legendDataItem = this.get(\"legendDataItem\");\n if (legendDataItem) {\n markerRectangle = legendDataItem.get(\"markerRectangle\");\n if (markerRectangle) {\n if (this.isVisible()) {\n if (this.isDirty(\"stroke\")) {\n let stroke = this.get(\"stroke\");\n markerRectangle.set(\"stroke\", stroke);\n }\n if (this.isDirty(\"fill\")) {\n let fill = this.get(\"fill\");\n markerRectangle.set(\"fill\", fill);\n }\n }\n }\n }\n this.updateLegendMarker(undefined);\n }\n if (this.bullets.length > 0) {\n let startIndex = this.startIndex();\n let endIndex = this.endIndex();\n if (endIndex < this.dataItems.length) {\n endIndex++;\n }\n for (let i = startIndex; i < endIndex; i++) {\n let dataItem = this.dataItems[i];\n if (!dataItem.bullets) {\n this._makeBullets(dataItem);\n }\n }\n }\n }\n /**\r\n * @ignore\r\n */\n _adjustStartIndex(index) {\n return index;\n }\n _calculateAggregates(startIndex, endIndex) {\n let fields = this._valueFields;\n if (!fields) {\n throw new Error(\"No value fields are set for the series.\");\n }\n const sum = {};\n const absSum = {};\n const count = {};\n const low = {};\n const high = {};\n const open = {};\n const close = {};\n const average = {};\n const previous = {};\n $array.each(fields, key => {\n sum[key] = 0;\n absSum[key] = 0;\n count[key] = 0;\n });\n $array.each(fields, key => {\n let change = key + \"Change\";\n let changePercent = key + \"ChangePercent\";\n let changePrevious = key + \"ChangePrevious\";\n let changePreviousPercent = key + \"ChangePreviousPercent\";\n let changeSelection = key + \"ChangeSelection\";\n let changeSelectionPercent = key + \"ChangeSelectionPercent\";\n let openKey = \"valueY\";\n if (key == \"valueX\" || key == \"openValueX\" || key == \"lowValueX\" || key == \"highValueX\") {\n openKey = \"valueX\";\n }\n const baseValueSeries = this.getPrivate(\"baseValueSeries\");\n const adjustedStartIndex = this.getPrivate(\"adjustedStartIndex\", startIndex);\n for (let i = adjustedStartIndex; i < endIndex; i++) {\n const dataItem = this.dataItems[i];\n if (dataItem) {\n let value = dataItem.get(key);\n if (value != null) {\n count[key]++;\n sum[key] += value;\n absSum[key] += Math.abs(value);\n average[key] = sum[key] / count[key];\n if (low[key] > value || low[key] == null) {\n low[key] = value;\n }\n if (high[key] < value || high[key] == null) {\n high[key] = value;\n }\n close[key] = value;\n if (open[key] == null) {\n open[key] = value;\n previous[key] = value;\n if (baseValueSeries) {\n open[openKey] = baseValueSeries._getBase(openKey);\n }\n }\n if (startIndex === 0) {\n dataItem.setRaw(change, value - open[openKey]);\n dataItem.setRaw(changePercent, (value - open[openKey]) / open[openKey] * 100);\n }\n dataItem.setRaw(changePrevious, value - previous[openKey]);\n dataItem.setRaw(changePreviousPercent, (value - previous[openKey]) / previous[openKey] * 100);\n dataItem.setRaw(changeSelection, value - open[openKey]);\n dataItem.setRaw(changeSelectionPercent, (value - open[openKey]) / open[openKey] * 100);\n previous[key] = value;\n }\n }\n }\n if (endIndex < this.dataItems.length - 1) {\n const dataItem = this.dataItems[endIndex];\n if (dataItem) {\n let value = dataItem.get(key);\n dataItem.setRaw(changePrevious, value - previous[openKey]);\n dataItem.setRaw(changePreviousPercent, (value - previous[openKey]) / previous[openKey] * 100);\n dataItem.setRaw(changeSelection, value - open[openKey]);\n dataItem.setRaw(changeSelectionPercent, (value - open[openKey]) / open[openKey] * 100);\n }\n }\n if (startIndex > 0) {\n startIndex--;\n }\n delete previous[key];\n for (let i = startIndex; i < adjustedStartIndex; i++) {\n const dataItem = this.dataItems[i];\n if (dataItem) {\n let value = dataItem.get(key);\n if (previous[key] == null) {\n previous[key] = value;\n }\n if (value != null) {\n dataItem.setRaw(changePrevious, value - previous[openKey]);\n dataItem.setRaw(changePreviousPercent, (value - previous[openKey]) / previous[openKey] * 100);\n dataItem.setRaw(changeSelection, value - open[openKey]);\n dataItem.setRaw(changeSelectionPercent, (value - open[openKey]) / open[openKey] * 100);\n previous[key] = value;\n }\n }\n }\n });\n $array.each(fields, key => {\n this.setPrivate(key + \"AverageSelection\", average[key]);\n this.setPrivate(key + \"CountSelection\", count[key]);\n this.setPrivate(key + \"SumSelection\", sum[key]);\n this.setPrivate(key + \"AbsoluteSumSelection\", absSum[key]);\n this.setPrivate(key + \"LowSelection\", low[key]);\n this.setPrivate(key + \"HighSelection\", high[key]);\n this.setPrivate(key + \"OpenSelection\", open[key]);\n this.setPrivate(key + \"CloseSelection\", close[key]);\n });\n if (startIndex === 0 && endIndex === this.dataItems.length) {\n $array.each(fields, key => {\n this.setPrivate(key + \"Average\", average[key]);\n this.setPrivate(key + \"Count\", count[key]);\n this.setPrivate(key + \"Sum\", sum[key]);\n this.setPrivate(key + \"AbsoluteSum\", absSum[key]);\n this.setPrivate(key + \"Low\", low[key]);\n this.setPrivate(key + \"High\", high[key]);\n this.setPrivate(key + \"Open\", open[key]);\n this.setPrivate(key + \"Close\", close[key]);\n });\n }\n }\n _updateChildren() {\n super._updateChildren();\n this._psi = this.startIndex();\n this._pei = this.endIndex();\n if (this.isDirty(\"visible\")) {\n this.bulletsContainer.set(\"visible\", this.get(\"visible\"));\n }\n // Apply heat rules\n const rules = this.get(\"heatRules\");\n if (this._valuesDirty && rules && rules.length > 0) {\n $array.each(rules, rule => {\n const minValue = rule.minValue || this.getPrivate(rule.dataField + \"Low\") || 0;\n const maxValue = rule.maxValue || this.getPrivate(rule.dataField + \"High\") || 0;\n $array.each(rule.target._entities, target => {\n const value = target.dataItem.get(rule.dataField);\n if (!$type.isNumber(value)) {\n if (rule.neutral) {\n target.set(rule.key, rule.neutral);\n }\n const states = target.states;\n if (states) {\n const defaultState = states.lookup(\"default\");\n if (defaultState && rule.neutral) {\n defaultState.set(rule.key, rule.neutral);\n }\n }\n if (!rule.customFunction) {\n return;\n }\n }\n if (rule.customFunction) {\n rule.customFunction.call(this, target, minValue, maxValue, value);\n } else {\n let percent;\n if (rule.logarithmic) {\n percent = (Math.log(value) * Math.LOG10E - Math.log(minValue) * Math.LOG10E) / (Math.log(maxValue) * Math.LOG10E - Math.log(minValue) * Math.LOG10E);\n } else {\n percent = (value - minValue) / (maxValue - minValue);\n }\n if ($type.isNumber(value) && (!$type.isNumber(percent) || Math.abs(percent) == Infinity)) {\n percent = 0.5;\n }\n // fixes problems if all values are the same\n let propertyValue;\n if ($type.isNumber(rule.min)) {\n propertyValue = rule.min + (rule.max - rule.min) * percent;\n } else if (rule.min instanceof Color) {\n propertyValue = Color.interpolate(percent, rule.min, rule.max);\n } else if (rule.min instanceof Percent) {\n propertyValue = percentInterpolate(percent, rule.min, rule.max);\n }\n target.set(rule.key, propertyValue);\n const states = target.states;\n if (states) {\n const defaultState = states.lookup(\"default\");\n if (defaultState) {\n defaultState.set(rule.key, propertyValue);\n }\n }\n }\n });\n });\n }\n if (this.get(\"visible\")) {\n let count = this.dataItems.length;\n let startIndex = this.startIndex();\n let endIndex = this.endIndex();\n if (endIndex < count) {\n endIndex++;\n }\n if (startIndex > 0) {\n startIndex--;\n }\n for (let i = 0; i < startIndex; i++) {\n this._hideBullets(this.dataItems[i]);\n }\n for (let i = startIndex; i < endIndex; i++) {\n this._positionBullets(this.dataItems[i]);\n }\n for (let i = endIndex; i < count; i++) {\n this._hideBullets(this.dataItems[i]);\n }\n }\n }\n _positionBullets(dataItem) {\n if (dataItem.bullets) {\n $array.each(dataItem.bullets, bullet => {\n this._positionBullet(bullet);\n const sprite = bullet.get(\"sprite\");\n if (bullet.get(\"dynamic\")) {\n if (sprite) {\n sprite._markDirtyKey(\"fill\");\n sprite.markDirtySize();\n }\n if (sprite instanceof Container) {\n sprite.walkChildren(child => {\n child._markDirtyKey(\"fill\");\n child.markDirtySize();\n if (child instanceof Label) {\n child.text.markDirtyText();\n }\n });\n }\n }\n if (sprite instanceof Label && sprite.get(\"populateText\")) {\n sprite.text.markDirtyText();\n }\n });\n }\n }\n _hideBullets(dataItem) {\n if (dataItem.bullets) {\n $array.each(dataItem.bullets, bullet => {\n let sprite = bullet.get(\"sprite\");\n if (sprite) {\n sprite.setPrivate(\"visible\", false);\n }\n });\n }\n }\n _positionBullet(_bullet) {}\n _placeBulletsContainer(chart) {\n chart.bulletsContainer.children.moveValue(this.bulletsContainer);\n }\n _removeBulletsContainer() {\n const bulletsContainer = this.bulletsContainer;\n if (bulletsContainer.parent) {\n bulletsContainer.parent.children.removeValue(bulletsContainer);\n }\n }\n /**\r\n * @ignore\r\n */\n disposeDataItem(dataItem) {\n //super.disposeDataItem(dataItem); // does nothing\n const bullets = dataItem.bullets;\n if (bullets) {\n $array.each(bullets, bullet => {\n bullet.dispose();\n });\n }\n }\n _getItemReaderLabel() {\n return \"\";\n }\n /**\r\n * Shows series's data item.\r\n *\r\n * @param dataItem Data item\r\n * @param duration Animation duration in milliseconds\r\n * @return Promise\r\n */\n showDataItem(dataItem, duration) {\n const _super = Object.create(null, {\n showDataItem: {\n get: () => super.showDataItem\n }\n });\n return __awaiter(this, void 0, void 0, function* () {\n const promises = [_super.showDataItem.call(this, dataItem, duration)];\n const bullets = dataItem.bullets;\n if (bullets) {\n $array.each(bullets, bullet => {\n const sprite = bullet.get(\"sprite\");\n if (sprite) {\n promises.push(sprite.show(duration));\n }\n });\n }\n yield Promise.all(promises);\n });\n }\n /**\r\n * Hides series's data item.\r\n *\r\n * @param dataItem Data item\r\n * @param duration Animation duration in milliseconds\r\n * @return Promise\r\n */\n hideDataItem(dataItem, duration) {\n const _super = Object.create(null, {\n hideDataItem: {\n get: () => super.hideDataItem\n }\n });\n return __awaiter(this, void 0, void 0, function* () {\n const promises = [_super.hideDataItem.call(this, dataItem, duration)];\n const bullets = dataItem.bullets;\n if (bullets) {\n $array.each(bullets, bullet => {\n const sprite = bullet.get(\"sprite\");\n if (sprite) {\n promises.push(sprite.hide(duration));\n }\n });\n }\n yield Promise.all(promises);\n });\n }\n _sequencedShowHide(show, duration) {\n return __awaiter(this, void 0, void 0, function* () {\n if (this.get(\"sequencedInterpolation\")) {\n if (!$type.isNumber(duration)) {\n duration = this.get(\"interpolationDuration\", 0);\n }\n if (duration > 0) {\n const startIndex = this.startIndex();\n const endIndex = this.endIndex();\n yield Promise.all($array.map(this.dataItems, (dataItem, i) => __awaiter(this, void 0, void 0, function* () {\n let realDuration = duration || 0;\n if (i < startIndex - 10 || i > endIndex + 10) {\n realDuration = 0;\n }\n //let delay = this.get(\"sequencedDelay\", 0) * i + realDuration * (i - startIndex) / (endIndex - startIndex);\n let delay = this.get(\"sequencedDelay\", 0) + realDuration / (endIndex - startIndex);\n yield $time.sleep(delay * (i - startIndex));\n if (show) {\n yield this.showDataItem(dataItem, realDuration);\n } else {\n yield this.hideDataItem(dataItem, realDuration);\n }\n })));\n } else {\n yield Promise.all($array.map(this.dataItems, dataItem => {\n if (show) {\n return this.showDataItem(dataItem, 0);\n } else {\n return this.hideDataItem(dataItem, 0);\n }\n }));\n }\n }\n });\n }\n /**\r\n * @ignore\r\n */\n updateLegendValue(dataItem) {\n if (dataItem) {\n const legendDataItem = dataItem.get(\"legendDataItem\");\n if (legendDataItem) {\n const valueLabel = legendDataItem.get(\"valueLabel\");\n if (valueLabel) {\n const text = valueLabel.text;\n let txt = \"\";\n valueLabel._setDataItem(dataItem);\n txt = this.get(\"legendValueText\", text.get(\"text\", \"\"));\n valueLabel.set(\"text\", txt);\n text.markDirtyText();\n }\n const label = legendDataItem.get(\"label\");\n if (label) {\n const text = label.text;\n let txt = \"\";\n label._setDataItem(dataItem);\n txt = this.get(\"legendLabelText\", text.get(\"text\", \"\"));\n label.set(\"text\", txt);\n text.markDirtyText();\n }\n }\n }\n }\n /**\r\n * @ignore\r\n */\n updateLegendMarker(_dataItem) {}\n _onHide() {\n super._onHide();\n const tooltip = this.getTooltip();\n if (tooltip) {\n tooltip.hide();\n }\n }\n /**\r\n * @ignore\r\n */\n hoverDataItem(_dataItem) {}\n /**\r\n * @ignore\r\n */\n unhoverDataItem(_dataItem) {}\n /**\r\n * @ignore\r\n */\n _getBase(key) {\n const dataItem = this.dataItems[this.startIndex()];\n if (dataItem) {\n return dataItem.get(key);\n }\n return 0;\n }\n}\nObject.defineProperty(Series, \"className\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: \"Series\"\n});\nObject.defineProperty(Series, \"classNames\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: Component.classNames.concat([Series.className])\n});\n","import { Series } from \"./Series\";\nimport { Container } from \"../../core/render/Container\";\nimport { Label } from \"../../core/render/Label\";\nimport { RoundedRectangle } from \"../../core/render/RoundedRectangle\";\nimport { Template } from \"../../core/util/Template\";\nimport { ListTemplate } from \"../../core/util/List\";\nimport * as $utils from \"../../core/util/Utils\";\n/**\r\n * A universal legend control.\r\n *\r\n * @important\r\n * @see {@link https://www.amcharts.com/docs/v5/concepts/legend/} for more info\r\n */\nexport class Legend extends Series {\n constructor() {\n super(...arguments);\n /**\r\n * List of all [[Container]] elements for legend items.\r\n *\r\n * @default new ListTemplate\r\n */\n Object.defineProperty(this, \"itemContainers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this.addDisposer(new ListTemplate(Template.new({}), () => Container._new(this._root, {\n themeTags: $utils.mergeTags(this.itemContainers.template.get(\"themeTags\", []), [\"legend\", \"item\"]),\n themeTagsSelf: $utils.mergeTags(this.itemContainers.template.get(\"themeTagsSelf\", []), [\"itemcontainer\"]),\n background: RoundedRectangle.new(this._root, {\n themeTags: $utils.mergeTags(this.itemContainers.template.get(\"themeTags\", []), [\"legend\", \"item\", \"background\"]),\n themeTagsSelf: $utils.mergeTags(this.itemContainers.template.get(\"themeTagsSelf\", []), [\"itemcontainer\"])\n })\n }, [this.itemContainers.template])))\n });\n /**\r\n * List of legend marker elements.\r\n *\r\n * @default new ListTemplate\r\n */\n Object.defineProperty(this, \"markers\", {\n enumerable: true,\n configurable: true,\n writable: true,\n value: this.addDisposer(new ListTemplate(Template.new({}), () => Container._new(this._root, {\n themeTags: $utils.mergeTags(this.markers.template.get(\"themeTags\", []), [\"legend\", \"marker\"])\n }, [this.markers.template])))\n });\n /**\r\n * List of legend label elements.\r\n *\r\n * @default new ListTemplate