w2p 51849 - display entities as list results

This commit is contained in:
Art Lowel
2018-05-30 15:22:43 +02:00
parent f1b35577b2
commit e5fc17da76
34 changed files with 297 additions and 108 deletions

View File

@@ -23,7 +23,6 @@ import { ItemPageFieldsComponent } from './simple/entity-types/item/item-page-fi
import { OrgUnitPageFieldsComponent } from './simple/entity-types/orgunit/orgunit-page-fields.component';
import { PersonPageFieldsComponent } from './simple/entity-types/person/person-page-fields.component';
import { ProjectPageFieldsComponent } from './simple/entity-types/project/project-page-fields.component';
import { EntityTypeSwitcherComponent } from './simple/entity-types/switcher/entity-type-switcher.component';
@NgModule({
imports: [
@@ -46,7 +45,6 @@ import { EntityTypeSwitcherComponent } from './simple/entity-types/switcher/enti
FileSectionComponent,
CollectionsComponent,
FullFileSectionComponent,
EntityTypeSwitcherComponent,
ItemPageFieldsComponent,
ProjectPageFieldsComponent,
OrgUnitPageFieldsComponent,

View File

@@ -5,7 +5,7 @@ import {
rendersEntityType
} from '../../../../shared/entities/entity-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/entity-type-switcher.component';
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
@rendersEntityType('Item', ElementViewMode.Full)
@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full)

View File

@@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/entity-type-switcher.component';
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
@rendersEntityType('OrgUnit', ElementViewMode.Full)
@Component({

View File

@@ -11,7 +11,7 @@ import { getRemoteDataPayload } from '../../../../core/shared/operators';
import { hasValue } from '../../../../shared/empty.util';
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/entity-type-switcher.component';
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
const compareArraysUsing = <T>(mapFn: (t: T) => any) =>
(a: T[], b: T[]): boolean => {
@@ -63,7 +63,6 @@ const relationsToItems = (thisId: string, ids: ItemDataService) =>
distinctUntilChanged(compareArraysUsingIds()),
);
@rendersEntityType('Person', ElementViewMode.Full)
@Component({
selector: 'ds-person-page-fields',

View File

@@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/entity-type-switcher.component';
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
@rendersEntityType('Project', ElementViewMode.Full)
@Component({

View File

@@ -1 +0,0 @@
@import '../../../../../styles/variables.scss';

View File

@@ -1,36 +0,0 @@
import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core';
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
import { Item } from '../../../../core/shared/item.model';
import { getComponentByEntityType } from '../../../../shared/entities/entity-type-decorator';
import { rendersDSOType } from '../../../../shared/object-collection/shared/dso-element-decorator';
import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model';
import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode';
export const ITEM: InjectionToken<string> = new InjectionToken<string>('item');
@Component({
selector: 'ds-entity-type-switcher',
styleUrls: ['./entity-type-switcher.component.scss'],
templateUrl: './entity-type-switcher.component.html'
})
export class EntityTypeSwitcherComponent implements OnInit {
@Input() item: Item;
@Input() viewMode: ElementViewMode;
objectInjector: Injector;
constructor(private injector: Injector) {
}
ngOnInit(): void {
this.objectInjector = Injector.create({
providers: [{ provide: ITEM, useFactory: () => this.item, deps:[] }],
parent: this.injector
});
}
getComponent(): string {
const type = this.item.findMetadata('relationship.type');
return getComponentByEntityType(type, this.viewMode);
}
}

View File

@@ -1,7 +1,7 @@
<div class="container" *ngVar="(itemRDObs | async) as itemRD">
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
<div *ngIf="itemRD?.payload as item">
<ds-entity-type-switcher [item]="item" [viewMode]="ElementViewMode.Full"></ds-entity-type-switcher>
<ds-entity-type-switcher [object]="item" [viewMode]="ElementViewMode.Full"></ds-entity-type-switcher>
</div>
</div>
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>

View File

@@ -1,5 +1,6 @@
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { Metadatum } from '../core/shared/metadatum.model';
import { hasNoValue, isEmpty } from '../shared/empty.util';
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
export class SearchResult<T extends DSpaceObject> implements ListableObject {

View File

@@ -0,0 +1,44 @@
import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core';
import { SearchResult } from '../../../+search-page/search-result.model';
import { Item } from '../../../core/shared/item.model';
import { hasValue } from '../../empty.util';
import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model';
import { getComponentByEntityType } from '../entity-type-decorator';
import { ElementViewMode } from '../../view-mode';
export const ITEM: InjectionToken<string> = new InjectionToken<string>('item');
@Component({
selector: 'ds-entity-type-switcher',
styleUrls: ['./entity-type-switcher.component.scss'],
templateUrl: './entity-type-switcher.component.html'
})
export class EntityTypeSwitcherComponent implements OnInit {
@Input() object: Item | SearchResult<Item>;
@Input() viewMode: ElementViewMode;
objectInjector: Injector;
constructor(private injector: Injector) {
}
ngOnInit(): void {
this.objectInjector = Injector.create({
providers: [{ provide: ITEM, useFactory: () => this.object, deps:[] }],
parent: this.injector
});
}
getComponent(): string {
let item: Item;
if (hasValue((this.object as any).dspaceObject)) {
const searchResult = this.object as ItemSearchResult;
item = searchResult.dspaceObject;
} else {
item = this.object as Item;
}
const type = item.findMetadata('relationship.type');
return getComponentByEntityType(type, this.viewMode);
}
}

View File

@@ -0,0 +1 @@
<ds-entity-type-switcher [object]="object" [viewMode]="ElementViewMode.SetElement"></ds-entity-type-switcher>

View File

@@ -0,0 +1 @@
@import '../../../../styles/variables';

View File

@@ -1,4 +1,4 @@
import { ItemListElementComponent } from './item-list-element.component';
import { EntityListElementComponent } from './entity-list-element.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
@@ -6,8 +6,8 @@ import { TruncatePipe } from '../../utils/truncate.pipe';
import { Item } from '../../../core/shared/item.model';
import { Observable } from 'rxjs/Observable';
let itemListElementComponent: ItemListElementComponent;
let fixture: ComponentFixture<ItemListElementComponent>;
let itemListElementComponent: EntityListElementComponent;
let fixture: ComponentFixture<EntityListElementComponent>;
const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), {
bitstreams: Observable.of({}),
@@ -38,22 +38,22 @@ const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), {
}]
});
describe('ItemListElementComponent', () => {
describe('EntityListElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ItemListElementComponent , TruncatePipe],
declarations: [ EntityListElementComponent , TruncatePipe],
providers: [
{ provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}}
],
schemas: [ NO_ERRORS_SCHEMA ]
}).overrideComponent(ItemListElementComponent, {
}).overrideComponent(EntityListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(ItemListElementComponent);
fixture = TestBed.createComponent(EntityListElementComponent);
itemListElementComponent = fixture.componentInstance;
}));

View File

@@ -1,15 +1,18 @@
import { Component, Input, Inject } from '@angular/core';
import { Component } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import * as viewMode from '../../../shared/view-mode';
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { SetViewMode } from '../../view-mode';
@Component({
selector: 'ds-item-list-element',
styleUrls: ['./item-list-element.component.scss'],
templateUrl: './item-list-element.component.html'
selector: 'ds-entity-list-element',
styleUrls: ['./entity-list-element.component.scss'],
templateUrl: './entity-list-element.component.html'
})
@renderElementsFor(Item, SetViewMode.List)
export class ItemListElementComponent extends AbstractListableElementComponent<Item> {}
export class EntityListElementComponent extends AbstractListableElementComponent<Item> {
ElementViewMode = viewMode.ElementViewMode;
}

View File

@@ -0,0 +1,74 @@
import { Component, Inject } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Item } from '../../../../core/shared/item.model';
import { Metadatum } from '../../../../core/shared/metadatum.model';
import { hasNoValue, hasValue, isEmpty } from '../../../empty.util';
import { ITEM } from '../../../entities/switcher/entity-type-switcher.component';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import { TruncatableService } from '../../../truncatable/truncatable.service';
// TODO lot of overlap with SearchResultListElementComponent => refactor!
@Component({
selector: 'ds-entity-search-result',
template: ''
})
export class EntitySearchResultComponent {
item: Item;
searchResult: ItemSearchResult;
constructor(
private truncatableService: TruncatableService,
@Inject(ITEM) public object: Item | ItemSearchResult,
) {
if (hasValue((this.object as any).dspaceObject)) {
this.searchResult = this.object as ItemSearchResult;
this.item = this.searchResult.dspaceObject;
} else {
this.searchResult = {
dspaceObject: this.object as Item,
hitHighlights: []
};
this.item = this.object as Item;
}
}
getValues(keys: string[]): string[] {
const results: string[] = new Array<string>();
this.searchResult.hitHighlights.forEach(
(md: Metadatum) => {
if (keys.indexOf(md.key) > -1) {
results.push(md.value);
}
}
);
if (isEmpty(results)) {
this.item.filterMetadata(keys).forEach(
(md: Metadatum) => {
results.push(md.value);
}
);
}
return results;
}
getFirstValue(key: string): string {
let result: string;
this.searchResult.hitHighlights.some(
(md: Metadatum) => {
if (key === md.key) {
result = md.value;
return true;
}
}
);
if (hasNoValue(result)) {
result = this.item.findMetadata(key);
}
return result;
}
isCollapsed(): Observable<boolean> {
return this.truncatableService.isCollapsed(this.item.id);
}
}

View File

@@ -0,0 +1,24 @@
<ds-truncatable [id]="item.id" *ngIf="item !== undefined && item !== null">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="getFirstValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
(<span *ngIf="item.findMetadata('dc.publisher')" class="item-list-publisher"
[innerHTML]="getFirstValue('dc.publisher')">, </span><span
*ngIf="item.findMetadata('dc.date.issued')" class="item-list-date"
[innerHTML]="getFirstValue('dc.date.issued')"></span>)
<span *ngIf="item.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
class="item-list-authors">
<span *ngFor="let author of getValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
<div *ngIf="item.findMetadata('dc.description.abstract')" class="item-list-abstract">
<ds-truncatable-part [id]="item.id" [minLines]="3"><span
[innerHTML]="getFirstValue('dc.description.abstract')"></span>
</ds-truncatable-part>
</div>
</ds-truncatable>

View File

@@ -0,0 +1 @@
@import '../../../../../../styles/variables';

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import { DEFAULT_ENTITY_TYPE, rendersEntityType } from '../../../../entities/entity-type-decorator';
import { ElementViewMode } from '../../../../view-mode';
import { EntitySearchResultComponent } from '../entity-search-result-component';
@rendersEntityType('Item', ElementViewMode.SetElement)
@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.SetElement)
@Component({
selector: 'ds-item-list-element',
styleUrls: ['./item-list-element.component.scss'],
templateUrl: './item-list-element.component.html'
})
export class ItemListElementComponent extends EntitySearchResultComponent {
}

View File

@@ -0,0 +1,16 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="getFirstValue('orgunit.identifier.name')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.filterMetadata(['orgunit.identifier.description']).length > 0"
class="item-list-authors">
<ds-truncatable-part [id]="item.id" [minLines]="3"><span
[innerHTML]="getFirstValue('orgunit.identifier.description')"></span>
</ds-truncatable-part>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1 @@
@import '../../../../../../styles/variables';

View File

@@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
import { ElementViewMode } from '../../../../view-mode';
import { EntitySearchResultComponent } from '../entity-search-result-component';
@rendersEntityType('OrgUnit', ElementViewMode.SetElement)
@Component({
selector: 'ds-orgunit-list-element',
styleUrls: ['./orgunit-list-element.component.scss'],
templateUrl: './orgunit-list-element.component.html'
})
export class OrgUnitListElementComponent extends EntitySearchResultComponent {
}

View File

@@ -0,0 +1,16 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="getFirstValue('dc.contributor.author')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.filterMetadata(['person.identifier.jobtitle']).length > 0"
class="item-list-authors">
<span *ngFor="let value of getValues(['person.identifier.jobtitle']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1 @@
@import '../../../../../../styles/variables';

View File

@@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
import { ElementViewMode } from '../../../../view-mode';
import { EntitySearchResultComponent } from '../entity-search-result-component';
@rendersEntityType('Person', ElementViewMode.SetElement)
@Component({
selector: 'ds-person-list-element',
styleUrls: ['./person-list-element.component.scss'],
templateUrl: './person-list-element.component.html'
})
export class PersonListElementComponent extends EntitySearchResultComponent {
}

View File

@@ -0,0 +1,16 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="getFirstValue('project.identifier.name')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.filterMetadata(['project.identifier.status']).length > 0"
class="item-list-authors">
<span *ngFor="let value of getValues(['project.identifier.status']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1 @@
@import '../../../../../../styles/variables';

View File

@@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
import { ElementViewMode } from '../../../../view-mode';
import { EntitySearchResultComponent } from '../entity-search-result-component';
@rendersEntityType('Project', ElementViewMode.SetElement)
@Component({
selector: 'ds-project-list-element',
styleUrls: ['./project-list-element.component.scss'],
templateUrl: './project-list-element.component.html'
})
export class ProjectListElementComponent extends EntitySearchResultComponent {
}

View File

@@ -1,18 +0,0 @@
<a [routerLink]="['/items/' + object.id]" class="lead">
{{object.findMetadata("dc.title")}}
</a>
<div>
<span class="text-muted">
<span *ngIf="object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
class="item-list-authors">
<span *ngFor="let authorMd of object.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{authorMd.value}}
<span *ngIf="!last">; </span>
</span>
</span>
(<span *ngIf="object.findMetadata('dc.publisher')" class="item-list-publisher">{{object.findMetadata("dc.publisher")}}, </span><span
*ngIf="object.findMetadata('dc.date.issued')" class="item-list-date">{{object.findMetadata("dc.date.issued")}}</span>)
</span>
<div *ngIf="object.findMetadata('dc.description.abstract')" class="item-list-abstract">
{{object.findMetadata("dc.description.abstract") | dsTruncate:[200] }}
</div>
</div>

View File

@@ -1,24 +1 @@
<ds-truncatable [id]="dso.id">
<a
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="getFirstValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
(<span *ngIf="dso.findMetadata('dc.publisher')" class="item-list-publisher"
[innerHTML]="getFirstValue('dc.publisher')">, </span><span
*ngIf="dso.findMetadata('dc.date.issued')" class="item-list-date"
[innerHTML]="getFirstValue('dc.date.issued')"></span>)
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']).length > 0"
class="item-list-authors">
<span *ngFor="let author of getValues(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">
<span [innerHTML]="author"><span [innerHTML]="author"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
<div *ngIf="dso.findMetadata('dc.description.abstract')" class="item-list-abstract">
<ds-truncatable-part [id]="dso.id" [minLines]="3"><span
[innerHTML]="getFirstValue('dc.description.abstract')"></span>
</ds-truncatable-part>
</div>
</ds-truncatable>
<ds-entity-type-switcher [object]="object" [viewMode]="ElementViewMode.SetElement"></ds-entity-type-switcher>

View File

@@ -1,12 +1,12 @@
import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { focusBackground } from '../../../animations/focus';
import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Item } from '../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import * as viewMode from '../../../../shared/view-mode';
import { SetViewMode } from '../../../view-mode';
import { ListableObject } from '../../../object-collection/shared/listable-object.model';
import { focusBackground } from '../../../animations/focus';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
@Component({
selector: 'ds-item-search-result-list-element',
@@ -18,4 +18,5 @@ import { focusBackground } from '../../../animations/focus';
@renderElementsFor(ItemSearchResult, SetViewMode.List)
export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
ElementViewMode = viewMode.ElementViewMode;
}

View File

@@ -8,6 +8,12 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { NgxPaginationModule } from 'ngx-pagination';
import { EntityTypeSwitcherComponent } from './entities/switcher/entity-type-switcher.component';
import { EntitySearchResultComponent } from './object-list/item-list-element/entity-types/entity-search-result-component';
import { ItemListElementComponent } from './object-list/item-list-element/entity-types/item/item-list-element.component';
import { OrgUnitListElementComponent } from './object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component';
import { PersonListElementComponent } from './object-list/item-list-element/entity-types/person/person-list-element.component';
import { ProjectListElementComponent } from './object-list/item-list-element/entity-types/project/project-list-element.component';
import { EnumKeysPipe } from './utils/enum-keys-pipe';
import { FileSizePipe } from './utils/file-size-pipe';
@@ -15,7 +21,7 @@ import { SafeUrlPipe } from './utils/safe-url-pipe';
import { CollectionListElementComponent } from './object-list/collection-list-element/collection-list-element.component';
import { CommunityListElementComponent } from './object-list/community-list-element/community-list-element.component';
import { ItemListElementComponent } from './object-list/item-list-element/item-list-element.component';
import { EntityListElementComponent } from './object-list/item-list-element/entity-list-element.component';
import { SearchResultListElementComponent } from './object-list/search-result-list-element/search-result-list-element.component';
import { WrapperListElementComponent } from './object-list/wrapper-list-element/wrapper-list-element.component';
import { ObjectListComponent } from './object-list/object-list.component';
@@ -89,11 +95,13 @@ const COMPONENTS = [
ViewModeSwitchComponent,
TruncatableComponent,
TruncatablePartComponent,
EntitySearchResultComponent,
EntityTypeSwitcherComponent
];
const ENTRY_COMPONENTS = [
// put shared entry components (components that are created dynamically) here
ItemListElementComponent,
EntityListElementComponent,
CollectionListElementComponent,
CommunityListElementComponent,
SearchResultListElementComponent,
@@ -101,6 +109,10 @@ const ENTRY_COMPONENTS = [
CollectionGridElementComponent,
CommunityGridElementComponent,
SearchResultGridElementComponent,
ItemListElementComponent,
PersonListElementComponent,
OrgUnitListElementComponent,
ProjectListElementComponent
];
const PROVIDERS = [

View File

@@ -5,7 +5,7 @@ export enum SetViewMode {
export enum ElementViewMode {
Full,
setElement
SetElement
}
export type ViewMode = SetViewMode | ElementViewMode;