})
export class EgAccessKeyInfoComponent extends EgDialogComponent {
- constructor(
+ constructor(
private modal: NgbModal, // required for passing to parent
private keyService: EgAccessKeyService) {
super(modal);
// E.g. "navbar"
@Input() keyCtx: string;
- constructor(
+ constructor(
private elm: ElementRef,
private keyService: EgAccessKeyService
) { }
ngOnInit() {
if (!this.keySpec) {
- console.warn("EgAccessKey no keySpec provided");
+ console.warn('EgAccessKey no keySpec provided');
return;
}
key: keySpec,
desc: this.keyDesc,
ctx: this.keyCtx,
- action: () => {this.elm.nativeElement.click()}
+ action: () => this.elm.nativeElement.click()
});
- })
+ });
}
}
import {Injectable, EventEmitter, HostListener} from '@angular/core';
export interface EgAccessKeyAssignment {
- key: string, // keyboard command
- desc: string, // human-friendly description
- ctx: string, // template context
- action: Function // handler function
-};
+ key: string; // keyboard command
+ desc: string; // human-friendly description
+ ctx: string; // template context
+ action: Function; // handler function
+}
@Injectable()
export class EgAccessKeyService {
compressKeys(evt: KeyboardEvent): string {
let s = '';
- if (evt.ctrlKey || evt.metaKey) s += 'ctrl+';
- if (evt.altKey) s += 'alt+';
+ if (evt.ctrlKey || evt.metaKey) { s += 'ctrl+'; }
+ if (evt.altKey) { s += 'alt+'; }
s += String.fromCharCode(evt.keyCode).toLowerCase();
return s;
* Checks for a key assignment and fires the assigned action.
*/
fire(evt: KeyboardEvent): void {
- let keySpec = this.compressKeys(evt);
- for (let i in this.assignments) { // for-loop to exit early
- if (keySpec == this.assignments[i].key) {
- let assign = this.assignments[i];
+ const keySpec = this.compressKeys(evt);
+ for (const i in this.assignments) { // for-loop to exit early
+ if (keySpec === this.assignments[i].key) {
+ const assign = this.assignments[i];
console.debug(`EgAccessKey assignment found for ${assign.key}`);
// Allow the current digest cycle to complete before
// firing the access key action.
@Injectable()
export class EgCatalogService {
- ccvmMap: {[ccvm:string] : EgIdlObject[]} = {};
- cmfMap: {[cmf:string] : EgIdlObject} = {};
+ ccvmMap: {[ccvm: string]: EgIdlObject[]} = {};
+ cmfMap: {[cmf: string]: EgIdlObject} = {};
// Keep a reference to the most recently retrieved facet data,
// since facet data is consistent across a given search.
search(ctx: CatalogSearchContext): Promise<void> {
ctx.searchState = CatalogSearchState.SEARCHING;
- var fullQuery = ctx.compileSearch();
+ const fullQuery = ctx.compileSearch();
console.debug(`search query: ${fullQuery}`);
let method = 'open-ils.search.biblio.multiclass.query';
- if (ctx.isStaff) method += '.staff';
+ if (ctx.isStaff) {
+ method += '.staff';
+ }
return new Promise((resolve, reject) => {
this.net.request(
ctx.searchState = CatalogSearchState.COMPLETE;
resolve();
});
- })
+ });
}
applyResultData(ctx: CatalogSearchContext, result: any): void {
result.records = [];
// If this is a new search, reset the result IDs collection.
- if (this.lastFacetKey != result.facet_key) ctx.resultIds = [];
+ if (this.lastFacetKey !== result.facet_key) {
+ ctx.resultIds = [];
+ }
- result.ids.forEach((blob, idx) => {ctx.addResultId(blob[0], idx)});
+ result.ids.forEach((blob, idx) => ctx.addResultId(blob[0], idx));
}
fetchBibSummaries(ctx: CatalogSearchContext): Promise<any> {
- let promises = [];
- let depth = ctx.global ?
+ const promises = [];
+ const depth = ctx.global ?
ctx.org.root().ou_type().depth() :
ctx.searchOrg.ou_type().depth();
fetchFacets(ctx: CatalogSearchContext): Promise<void> {
- if (!ctx.result)
+ if (!ctx.result) {
return Promise.reject('Cannot fetch facets without results');
+ }
- if (this.lastFacetKey == ctx.result.facet_key) {
+ if (this.lastFacetKey === ctx.result.facet_key) {
ctx.result.facetData = this.lastFacetData;
return Promise.resolve();
}
'open-ils.search.facet_cache.retrieve',
ctx.result.facet_key
).subscribe(facets => {
- let facetData = {};
+ const facetData = {};
Object.keys(facets).forEach(cmfId => {
- let facetHash = facets[cmfId];
- let cmf = this.cmfMap[cmfId];
+ const facetHash = facets[cmfId];
+ const cmf = this.cmfMap[cmfId];
- let cmfData = [];
+ const cmfData = [];
Object.keys(facetHash).forEach(value => {
- let count = facetHash[value];
+ const count = facetHash[value];
cmfData.push({value : value, count : count});
});
- if (!facetData[cmf.field_class()])
+ if (!facetData[cmf.field_class()]) {
facetData[cmf.field_class()] = {};
+ }
facetData[cmf.field_class()][cmf.name()] = {
cmfLabel : cmf.label(),
valueList : cmfData.sort((a, b) => {
- if (a.count > b.count) return -1;
- if (a.count < b.count) return 1;
+ if (a.count > b.count) { return -1; }
+ if (a.count < b.count) { return 1; }
// secondary alpha sort on display value
return a.value < b.value ? -1 : 1;
})
this.lastFacetData = ctx.result.facetData = facetData;
resolve();
});
- })
+ });
}
fetchCcvms(): Promise<void> {
- if (Object.keys(this.ccvmMap).length)
+ if (Object.keys(this.ccvmMap).length) {
return Promise.resolve();
+ }
return new Promise((resolve, reject) => {
this.pcrud.search('ccvm',
).subscribe(list => {
this.compileCcvms(list);
resolve();
- })
+ });
});
}
- compileCcvms(ccvms : EgIdlObject[]): void {
+ compileCcvms(ccvms: EgIdlObject[]): void {
ccvms.forEach(ccvm => {
- if (!this.ccvmMap[ccvm.ctype()])
+ if (!this.ccvmMap[ccvm.ctype()]) {
this.ccvmMap[ccvm.ctype()] = [];
+ }
this.ccvmMap[ccvm.ctype()].push(ccvm);
});
fetchCmfs(): Promise<void> {
// At the moment, we only need facet CMFs.
- if (Object.keys(this.cmfMap).length)
+ if (Object.keys(this.cmfMap).length) {
return Promise.resolve();
+ }
return new Promise((resolve, reject) => {
this.pcrud.search('cmf',
cmfs.forEach(c => this.cmfMap[c.id()] = c);
resolve();
}
- )
+ );
});
}
orgId: orgId,
depth: depth
}).then(xmlDoc => {
- let summary = this.translateBibSummary(xmlDoc);
+ const summary = this.translateBibSummary(xmlDoc);
summary.id = bibId;
resolve(summary);
});
*/
translateBibSummary(xmlDoc: XMLDocument): any { // TODO: bib summary interface
- let response = {
+ const response = {
copyCounts : [],
ccvms : {}
};
- let resolver:any = (prefix: string): string => {
+ const resolver: any = (prefix: string): string => {
return NAMESPACE_MAPS[prefix] || null;
};
Object.keys(MODS_XPATH_AUTO).forEach(key => {
- let result = xmlDoc.evaluate(MODS_XPATH_AUTO[key], xmlDoc,
+ const res = xmlDoc.evaluate(MODS_XPATH_AUTO[key], xmlDoc,
resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
- let node = result.singleNodeValue;
- if (node) response[key] = node.textContent;
+ const modsNode = res.singleNodeValue;
+ if (modsNode) {
+ response[key] = modsNode.textContent;
+ }
});
let result = xmlDoc.evaluate(MODS_XPATH.extern, xmlDoc,
resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
- let node:any = result.singleNodeValue;
+ let node: any = result.singleNodeValue;
if (node) {
- let attrs = node.attributes;
- for(let i = attrs.length - 1; i >= 0; i--) {
+ const attrs = node.attributes;
+ for (let i = attrs.length - 1; i >= 0; i--) {
response[attrs[i].name] = attrs[i].value;
}
}
result = xmlDoc.evaluate(MODS_XPATH.attributes, xmlDoc,
resolver, XPathResult.ANY_TYPE, null);
- while(node = result.iterateNext()) {
+ while (node = result.iterateNext()) {
response.ccvms[node.getAttribute('name')] = {
code : node.textContent,
label : node.getAttribute('coded-value')
- }
+ };
}
result = xmlDoc.evaluate(MODS_XPATH.copyCounts, xmlDoc,
resolver, XPathResult.ANY_TYPE, null);
- while(node = result.iterateNext()) {
- let counts = {};
+ while (node = result.iterateNext()) {
+ const counts = {};
['type', 'depth', 'org_unit', 'transcendant',
'available', 'count', 'unshadow'].forEach(field => {
counts[field] = node.getAttribute(field);
response.copyCounts.push(counts);
}
- //console.log(response);
+ // console.log(response);
return response;
}
}
equals(filter: FacetFilter): boolean {
return (
- this.facetClass == filter.facetClass &&
- this.facetName == filter.facetName &&
- this.facetValue == filter.facetValue
+ this.facetClass === filter.facetClass &&
+ this.facetName === filter.facetName &&
+ this.facetValue === filter.facetValue
);
}
}
export class CatalogSearchContext {
// Search options and filters
- available: boolean = false;
- global: boolean = false;
+ available = false;
+ global = false;
sort: string;
fieldClass: string[];
query: string[];
matchOp: string[];
format: string;
searchOrg: EgIdlObject;
- ccvmFilters: {[ccvmCode:string] : string[]};
+ ccvmFilters: {[ccvmCode: string]: string[]};
facetFilters: FacetFilter[];
isStaff: boolean;
// List of result IDs for the current page of data.
currentResultIds(): number[] {
- let ids = [];
- for (
- let idx = this.pager.offset;
- idx < Math.min(
- this.pager.offset + this.pager.limit,
- this.pager.resultCount
- );
- idx++
- ) {ids.push(this.resultIds[idx])}
+ const ids = [];
+ const max = Math.min(
+ this.pager.offset + this.pager.limit,
+ this.pager.resultCount
+ );
+ for (let idx = this.pager.offset; idx < max; idx++) {
+ ids.push(this.resultIds[idx]);
+ }
return ids;
}
// Return the index of the requested record
indexForResult(id: number): number {
for (let i = 0; i < this.resultIds.length; i++) {
- if (this.resultIds[i] == id)
+ if (this.resultIds[i] === id) {
return i;
+ }
}
return null;
}
this.joinOp = [''];
this.ccvmFilters = {};
this.facetFilters = [];
- this.result= {};
+ this.result = {};
this.resultIds = [];
this.searchState = CatalogSearchState.PENDING;
}
isSearchable(): boolean {
return this.query.length
- && this.query[0] != ''
- && this.searchOrg != null;
+ && this.query[0] !== ''
+ && this.searchOrg !== null;
}
compileSearch(): string {
- let str: string = '';
+ let str = '';
- if (this.available) str += '#available';
+ if (this.available) {
+ str += '#available';
+ }
if (this.sort) {
// e.g. title, title.descending
- let parts = this.sort.split(/\./);
- if (parts[1]) str += ' #descending';
+ const parts = this.sort.split(/\./);
+ if (parts[1]) { str += ' #descending'; }
str += ' sort(' + parts[0] + ')';
}
// -------
// Compile boolean sub-query components
- if (str.length) str += ' ';
- let qcount = this.query.length;
+ if (str.length) { str += ' '; }
+ const qcount = this.query.length;
// if we multiple boolean query components, wrap them in parens.
- if (qcount > 1) str += '(';
+ if (qcount > 1) { str += '('; }
this.query.forEach((q, idx) => {
- str += this.compileBoolQuerySet(idx)
+ str += this.compileBoolQuerySet(idx);
});
- if (qcount > 1) str += ')';
+ if (qcount > 1) { str += ')'; }
// -------
if (this.format) {
str += ' site(' + this.searchOrg.shortname() + ')';
Object.keys(this.ccvmFilters).forEach(field => {
- if (this.ccvmFilters[field][0] != '')
+ if (this.ccvmFilters[field][0] !== '') {
str += ' ' + field + '(' + this.ccvmFilters[field] + ')';
+ }
});
this.facetFilters.forEach(f => {
}
addQuotes(query: string): string {
- if (query.match(/ /))
- return '"' + query + '"'
+ if (query.match(/ /)) {
+ return '"' + query + '"';
+ }
return query;
- };
+ }
compileBoolQuerySet(idx: number): string {
let query = this.query[idx];
- let joinOp = this.joinOp[idx];
- let matchOp = this.matchOp[idx];
- let fieldClass = this.fieldClass[idx];
+ const joinOp = this.joinOp[idx];
+ const matchOp = this.matchOp[idx];
+ const fieldClass = this.fieldClass[idx];
let str = '';
- if (!query) return str;
+ if (!query) { return str; }
- if (idx > 0) str += ' ' + joinOp + ' ';
+ if (idx > 0) { str += ' ' + joinOp + ' '; }
str += '(';
- if (fieldClass) str += fieldClass + ':';
+ if (fieldClass) { str += fieldClass + ':'; }
- switch(matchOp) {
+ switch (matchOp) {
case 'phrase':
query = this.addQuotes(this.stripQuotes(query));
break;
hasFacet(facet: FacetFilter): boolean {
return Boolean(
- this.facetFilters.filter(
- f => {return f.equals(facet)})[0]
+ this.facetFilters.filter(f => f.equals(facet))[0]
);
}
removeFacet(facet: FacetFilter): void {
- this.facetFilters = this.facetFilters.filter(
- f => { return !f.equals(facet); });
+ this.facetFilters = this.facetFilters.filter(f => !f.equals(facet));
}
addFacet(facet: FacetFilter): void {
- if (!this.hasFacet(facet))
+ if (!this.hasFacet(facet)) {
this.facetFilters.push(facet);
+ }
}
toggleFacet(facet: FacetFilter): void {
format: string; // mods32, marxml, ...
orgId?: number; // org unit ID
depth?: number; // org unit depth
-};
+}
@Injectable()
export class EgUnapiService {
constructor(private org: EgOrgService) {}
createUrl(params: EgUnapiParams): string {
- let depth = params.depth || 0;
- let org = params.orgId ? this.org.get(params.orgId) : this.org.root();
+ const depth = params.depth || 0;
+ const org = params.orgId ? this.org.get(params.orgId) : this.org.root();
return `${UNAPI_PATH}${params.target}/${params.id}${params.extras}/` +
`${org.shortname()}/${depth}&format=${params.format}`;
getAsXmlDocument(params: EgUnapiParams): Promise<XMLDocument> {
// XReq creates an XML document for us. Seems like the right
// tool for the job.
- let url = this.createUrl(params);
+ const url = this.createUrl(params);
return new Promise((resolve, reject) => {
- var xhttp = new XMLHttpRequest();
- xhttp.onreadystatechange = function() {
- if (this.readyState == 4) {
- if (this.status == 200) {
+ const xhttp = new XMLHttpRequest();
+ xhttp.onreadystatechange = function() { // no () => {} !
+ if (this.readyState === 4) {
+ if (this.status === 200) {
resolve(xhttp.responseXML);
} else {
reject(`UNAPI request failed for ${url}`);
}
}
- }
- xhttp.open("GET", url, true);
+ };
+ xhttp.open('GET', url, true);
xhttp.send();
});
}
* Utility class for manage paged information.
*/
export class Pager {
- offset: number = 0;
+ offset = 0;
limit: number = null;
resultCount: number;
onChange$: EventEmitter<number>;
}
isFirstPage(): boolean {
- return this.offset == 0;
+ return this.offset === 0;
}
isLastPage(): boolean {
- return this.currentPage() == this.pageCount();
+ return this.currentPage() === this.pageCount();
}
currentPage(): number {
- return Math.floor(this.offset / this.limit) + 1
+ return Math.floor(this.offset / this.limit) + 1;
}
increment(): void {
}
toFirst() {
- if (!this.isFirstPage())
+ if (!this.isFirstPage()) {
this.setPage(1);
+ }
}
toLast() {
- if (!this.isLastPage())
+ if (!this.isLastPage()) {
this.setPage(this.pageCount());
+ }
}
setPage(page: number): void {
}
pageCount(): number {
- if (this.resultCount === null) return -1;
+ if (this.resultCount === null) { return -1; }
let pages = this.resultCount / this.limit;
- if (Math.floor(pages) < pages)
+ if (Math.floor(pages) < pages) {
pages = Math.floor(pages) + 1;
+ }
return pages;
}
pageList(): number[] {
- let list = [];
- for(let i = 1; i <= this.pageCount(); i++)
+ const list = [];
+ for (let i = 1; i <= this.pageCount(); i++) {
list.push(i);
+ }
return list;
}