UNPKG

bibcite

Version:
168 lines (147 loc) 4.54 kB
import { Citation } from "./citation"; type KeyGroup = { index: number; citations: Citation[] }; function docPosComp(a: HTMLElement, b: HTMLElement) { switch (a.compareDocumentPosition(b)) { case Node.DOCUMENT_POSITION_FOLLOWING: return -1; case Node.DOCUMENT_POSITION_PRECEDING: return 1; default: return 0; } } export class CitationKeyUse { _used_keys: Map<string, KeyGroup>; _length: number = 0; constructor(used_keys: Map<string, KeyGroup>) { this._used_keys = used_keys; } add(ci: Citation): { need_ref_update: boolean } { if (this._used_keys.has(ci.key)) { const entry = this._used_keys.get(ci.key); entry.citations.push(ci); // add citations to entry ci.bibIndex = entry.index; return { need_ref_update: false }; } else { this._length += 1; const entry = { index: this._length, citations: [ci], }; this._used_keys.set(ci.key, entry); ci.bibIndex = entry.index; return { need_ref_update: true }; } } remove(ci: Citation): { index: number; need_ref_update: boolean } { const ci_list = this._used_keys.get(ci.key).citations; if (ci_list.length <= 1) { this._used_keys.delete(ci.key); return { index: 0, need_ref_update: true }; } else { const idx = ci_list.indexOf(ci); ci_list.splice(idx, 1); // remove citation from list return { index: idx, need_ref_update: false }; } } has(key: string) { return this._used_keys.has(key); } get citations(): Citation[] { return Array.from(this._used_keys.values()) .map((keyGroup) => keyGroup.citations) .flat(1); } get() { return this._used_keys; } } export class BibSortedCitationKeyUse extends CitationKeyUse { _key_order: Map<string, number>; constructor( used_keys: Map<string, KeyGroup>, key_order: Map<string, number> ) { super(used_keys); this._key_order = key_order; this.sort_used_keys(); } sort_used_keys() { const sorted = [...this._used_keys].sort( ([key1, _1], [key2, _2]) => this._key_order.get(key1) - this._key_order.get(key2) ); sorted.forEach(([_, entry], idx) => { entry.index = idx; entry.citations.forEach((ci) => (ci.bibIndex = idx)); }); this._used_keys = new Map(sorted); } add(ci: Citation) { const result = super.add(ci); if (result.need_ref_update) { this.sort_used_keys(); } return result; } remove(ci: Citation) { const result = super.remove(ci); if (result.need_ref_update) { this.sort_used_keys(); } return result; } } export class InsertionSortedCitationKeyUse extends CitationKeyUse { _safe_to_append_key = (_: Citation) => true; // no order issues at first constructor(used_keys: Map<string, KeyGroup>) { super(used_keys); this.sort_used_keys(); } sort_used_keys() { // sort lists of citations for every key this._used_keys.forEach((entry) => entry.citations.sort(docPosComp).at(0)); const sorted = [...this._used_keys].sort( ([_key1, keyGroup1], [_key2, keyGroup2]) => docPosComp( // compare document position of first citation (i.e. .at(0)) keyGroup1.citations.at(0), keyGroup2.citations.at(0) ) ); sorted.forEach(([_, entry], idx) => (entry.index = idx)); this._length = sorted.length; if (this._length == 0) { this._safe_to_append_key = (_: Citation) => true; } else { this._safe_to_append_key = (other) => docPosComp(sorted.at(-1)[1].citations.at(0), other) < 0; } this._used_keys = new Map(sorted); // tell citations new index this._used_keys.forEach((entry) => entry.citations.forEach((c) => (c.bibIndex = entry.index)) ); } add(ci: Citation) { const update = super.add(ci).need_ref_update; if (update) { if (this._safe_to_append_key(ci)) { // a new reference was added, it was safe to append, so ci was the the // first usage of it and it came after all other references. The next // new reference needs to come after to be safe to append. this._safe_to_append_key = (other) => docPosComp(ci, other) < 0; } else { this.sort_used_keys(); } } return { need_ref_update: update }; } remove(ci: Citation) { const idx = super.remove(ci).index; if (idx == 0) { this.sort_used_keys(); } return { index: idx, need_ref_update: idx == 0 }; } }