import { AppModule, AppStates, AppModes } from '../../app.module' import { ElementBase, ElementStatus, ElementModerationState } from './element-base.class' export { ElementStatus, ElementModerationState } from './element-base.class' import { Marker } from '../../components/map/marker.component' import { ElementComponent } from '../../components/element/element.component' import { OptionValue, CategoryValue, Option, Category, Contribution, VoteReport, Stamp } from '../classes' import { capitalize, formatLabel } from '../../utils/string-helpers' import { App } from '../../gogocarto' import { MapFeatureComponent } from '../../components/map/map-feature.component' declare let $, Map // Standard fields managed by default info bar template const CORE_FIELDS = [ 'id', 'name', 'description', 'descriptionMore', 'address', 'telephone', 'email', 'website', 'urls', 'tags', 'vimeoId', 'images', 'sourceKey', 'updatedAt', 'createdAt', 'status', 'moderationState', 'searchScore', ] export class Element extends ElementBase { private marker_: Marker = null private component_: ElementComponent = null private mapFeature_: MapFeatureComponent = null colorOptionId: any mainIcon: string mainIconColorOptionId: string | number markerShape: string | any markerSize: number private isInitialized_ = false iconsToDisplay: OptionValue[] = [] distance: number // for elements module algorithms isDisplayed = false isShownAlone = false isFavorite = false needToBeUpdatedWhenShown = true constructor(elementJson: any) { super(elementJson) } updateWithJson(elementJson) { super.updateWithJson(elementJson) this.createOptionsTree() this.update(true) } initialize() { this.marker_ = new Marker(this) if (this.geoJSONFeature) { this.mapFeature_ = new MapFeatureComponent(this) } this.isInitialized_ = true } update($force = false) { // console.log("marker update needToBeUpdated", this.needToBeUpdatedWhenShown, "force", $force); if (this.needToBeUpdatedWhenShown || App.mode == AppModes.List || $force) { const optionValuesToUpdate = this.getCurrOptionsValues().filter((ov) => !!ov && !ov.option.isHidden) for (const optionValue of optionValuesToUpdate) App.elementOptionValuesModule.updateOptionValueColor(this, optionValue) const sortedOptionsValues = optionValuesToUpdate.sort((a, b) => { if (a.isFilledByFilters < b.isFilledByFilters) return 1 else if (a.isFilledByFilters > b.isFilledByFilters) return -1 if (a.option.useIconForMarker < b.option.useIconForMarker) return 1 else if (a.option.useIconForMarker > b.option.useIconForMarker) return -1 else return 0 // getCurrOptionsValues are already sorted depending on categories hierachy }) const filledOV = sortedOptionsValues.filter((ov) => ov.isFilledByFilters) const colorOV = filledOV.find((ov) => ov.colorOptionId) this.colorOptionId = colorOV ? colorOV.colorOptionId : null const markerShapeOV = filledOV.find((ov) => ov.option.markerShape) this.markerShape = markerShapeOV ? markerShapeOV.option.markerShape : App.config.marker.defaultShape const markerSizeOV = filledOV.find((ov) => ov.option.markerSize) this.markerSize = markerSizeOV ? markerSizeOV.option.markerSize : App.config.marker.defaultSize if (App.config.marker.customColorOptionId) this.colorOptionId = App.config.marker.customColorOptionId(App, this, this.colorOptionId) if (App.config.marker.customShape) this.markerShape = App.config.marker.customShape(App, this, this.markerShape) if (App.config.marker.customSize) this.markerSize = App.config.marker.customSize(App, this, this.markerSize) if (!this.markerSize) this.markerSize = 1 this.iconsToDisplay = sortedOptionsValues.filter((ov) => ov.option.icon && ov.option.useIconForMarker) if (this.iconsToDisplay.length > 0) { this.mainIcon = this.iconsToDisplay[0].option.icon this.mainIconColorOptionId = this.iconsToDisplay[0].colorOptionId } else { this.mainIcon = App.config.marker.defaultIcon this.mainIconColorOptionId = this.colorOptionId } if (this.displayAsPending()) { this.mainIcon = 'gogo-icon-attention' this.mainIconColorOptionId = App.config.colors.pending } if (App.config.marker.customMainIcon) this.mainIcon = App.config.marker.customMainIcon(App, this, this.mainIcon) if (this.marker) this.marker.update() if (this.feature) this.feature.update() this.needToBeUpdatedWhenShown = false } if (this.isDisplayedOnElementInfoBar()) this.showBigSize() } showBigSize() { if (this.marker) this.marker.showBigSize() if (this.feature) this.feature.showBigSize() } showNormalSize($force = false) { if (!$force && this.isDisplayedOnElementInfoBar()) return if (this.marker) this.marker.showNormalSize() if (this.feature) this.feature.showNormalSize() } isDisplayedOnElementInfoBar() { return App.infoBarComponent.getCurrElementId() == this.id } updateDistance() { this.distance = null if (App.geocoder.getLocation()) this.distance = App.mapComponent.distanceFromLocationTo(this.position) else this.distance = App.boundsModule.extendedBounds ? App.boundsModule.extendedBounds.getCenter().distanceTo(this.position) / 1000 : null // Making the distance more realistic multiplying this.distance = this.distance ? Math.round(1.2 * this.distance) : null } getCurrOptionsValues(): OptionValue[] { if (App.config.menu.showOnePanePerMainOption) { let result = this.sortedOptionsValues.filter((ov) => ov && ov.option.mainOwnerId == App.currMainId) if (result.length == 0) result = [this.optionsValues.find((ov) => ov.optionId == App.currMainId)] return result } else { return this.sortedOptionsValues.filter((optionValue) => !!optionValue) } } // getCurrDeepestOptionsValues(): OptionValue[] { // const currOptionValues = this.getCurrOptionsValues(); // let idsToRemove = []; // for (const ov of currOptionValues) idsToRemove = idsToRemove.concat(ov.option_.parentOptionIds); // const deepestOv = currOptionValues.filter((oV) => idsToRemove.indexOf(oV.option_.id) == -1); // // group by owner // const groupedByParentOvs = {}; // for (const ov of deepestOv) { // const parentName = ov.option.parentOptionName; // if (parentName in groupedByParentOvs) groupedByParentOvs[parentName].push(ov); // else groupedByParentOvs[parentName] = [ov]; // } // let deepestOrderedOv = []; // for (const parent in groupedByParentOvs) { // deepestOrderedOv = deepestOrderedOv.concat(groupedByParentOvs[parent]); // } // return deepestOrderedOv; // } getCurrMainOptionValue(): OptionValue { return this.optionsValues.find((optionValue) => optionValue.option.id == App.currMainId) } getCategoriesIds(): number[] { return this.getCurrOptionsValues() .map((optionValue) => optionValue.categoryOwner.id) .filter((value, index, self) => self.indexOf(value) === index) } getOptionsIdsInCategorieId(categoryId): number[] { return this.getCurrOptionsValues() .filter((optionValue) => optionValue.option.ownerId == categoryId) .map((optionValue) => optionValue.optionId) } displayStamps(): Stamp[] { return App.stampModule.getAllowedStampForElement(this) } isPending() { return this.status == ElementStatus.PendingAdd || this.status == ElementStatus.PendingModification } displayAsPending() { return this.isPending() && App.config.isFeatureAvailable('pending') } isDeleted() { return this.status <= ElementStatus.AdminRefused && this.status != ElementStatus.AggregatedElement } needsModeration() { return this.moderationState != ElementModerationState.NotNeeded } isAggregate() { return this.status == ElementStatus.Aggregate } isAggregated() { return this.status == ElementStatus.AggregatedElement } isFullyEditable() { if (this.isAggregate()) return false if (this.isAggregated()) return true return this.status >= ElementStatus.PendingModification } get marker(): Marker { return this.marker_ } get component() { return this.component_ || (this.component_ = new ElementComponent(this)) } get isInitialized() { return this.isInitialized_ } get feature(): MapFeatureComponent { return this.mapFeature_ } toDisplay() { const result = { id: this.id, name: this.formatProp('name'), address: this.formatProp('address'), originalAddress: this.address, distance: this.distance, taxonomy: this.gogoTaxonomy(), groups: this.categoriesValues, status: this.status, isPending: this.isPending(), isDeleted: this.isDeleted(), images: this.images, files: this.files, needsModeration: this.needsModeration(), moderationState: this.moderationState, formatedOpenHours: this.formatedOpenHours, pendingContribution: this.pendingContribution, contributions: this.contributions, reports: this.reports, votes: this.votes, colorOptionId: this.colorOptionId, dateToDisplay: this.dateToDisplay, aggregatedElements: this.aggregatedElements, mainIcon: this.mainIcon, mainIconColorOptionId: this.mainIconColorOptionId, isAggregate: this.isAggregate(), isAggregated: this.isAggregated(), isFullyEditable: this.isFullyEditable(), } const customData = [] Object.entries(this.data).forEach(([key, value]) => { if (!(key in result)) result[key] = this.formatProp(key) if ( value && typeof value != 'object' && CORE_FIELDS.indexOf(key) == -1 && !['osm_type', 'osm_timestamp', 'osm_version', 'osm_url'].includes(key) ) { value = result[key] if (Array.isArray(value)) value = value.join(', ') customData.push(`${formatLabel(key)} ${value}`) } }) result['customData'] = customData return result } formatProp(propertyName) { return App.elementFormaterModule.getProperty(this, propertyName) } }