UNPKG

ember-legacy-class-transform

Version:
251 lines 6.78 kB
export const CONSTANT = 0; export const INITIAL = 1; export const VOLATILE = NaN; export class RevisionTag { validate(snapshot) { return this.value() === snapshot; } } RevisionTag.id = 0; const VALUE = []; const VALIDATE = []; export class TagWrapper { constructor(type, inner) { this.type = type; this.inner = inner; } value() { let func = VALUE[this.type]; return func(this.inner); } validate(snapshot) { let func = VALIDATE[this.type]; return func(this.inner, snapshot); } } function register(Type) { let type = VALUE.length; VALUE.push(tag => tag.value()); VALIDATE.push((tag, snapshot) => tag.validate(snapshot)); Type.id = type; } /// // CONSTANT: 0 VALUE.push(() => CONSTANT); VALIDATE.push((_tag, snapshot) => snapshot === CONSTANT); export const CONSTANT_TAG = new TagWrapper(0, null); // VOLATILE: 1 VALUE.push(() => VOLATILE); VALIDATE.push((_tag, snapshot) => snapshot === VOLATILE); export const VOLATILE_TAG = new TagWrapper(1, null); // CURRENT: 2 VALUE.push(() => $REVISION); VALIDATE.push((_tag, snapshot) => snapshot === $REVISION); export const CURRENT_TAG = new TagWrapper(2, null); /// let $REVISION = INITIAL; export class DirtyableTag extends RevisionTag { static create(revision = $REVISION) { return new TagWrapper(this.id, new DirtyableTag(revision)); } constructor(revision = $REVISION) { super(); this.revision = revision; } value() { return this.revision; } dirty() { this.revision = ++$REVISION; } } register(DirtyableTag); export function combineTagged(tagged) { let optimized = []; for (let i = 0, l = tagged.length; i < l; i++) { let tag = tagged[i].tag; if (tag === VOLATILE_TAG) return VOLATILE_TAG; if (tag === CONSTANT_TAG) continue; optimized.push(tag); } return _combine(optimized); } export function combineSlice(slice) { let optimized = []; let node = slice.head(); while (node !== null) { let tag = node.tag; if (tag === VOLATILE_TAG) return VOLATILE_TAG; if (tag !== CONSTANT_TAG) optimized.push(tag); node = slice.nextNode(node); } return _combine(optimized); } export function combine(tags) { let optimized = []; for (let i = 0, l = tags.length; i < l; i++) { let tag = tags[i]; if (tag === VOLATILE_TAG) return VOLATILE_TAG; if (tag === CONSTANT_TAG) continue; optimized.push(tag); } return _combine(optimized); } function _combine(tags) { switch (tags.length) { case 0: return CONSTANT_TAG; case 1: return tags[0]; case 2: return TagsPair.create(tags[0], tags[1]); default: return TagsCombinator.create(tags); } ; } export class CachedTag extends RevisionTag { constructor() { super(...arguments); this.lastChecked = null; this.lastValue = null; } value() { let { lastChecked, lastValue } = this; if (lastChecked !== $REVISION) { this.lastChecked = $REVISION; this.lastValue = lastValue = this.compute(); } return this.lastValue; } invalidate() { this.lastChecked = null; } } class TagsPair extends CachedTag { static create(first, second) { return new TagWrapper(this.id, new TagsPair(first, second)); } constructor(first, second) { super(); this.first = first; this.second = second; } compute() { return Math.max(this.first.value(), this.second.value()); } } register(TagsPair); class TagsCombinator extends CachedTag { static create(tags) { return new TagWrapper(this.id, new TagsCombinator(tags)); } constructor(tags) { super(); this.tags = tags; } compute() { let { tags } = this; let max = -1; for (let i = 0; i < tags.length; i++) { let value = tags[i].value(); max = Math.max(value, max); } return max; } } register(TagsCombinator); export class UpdatableTag extends CachedTag { static create(tag) { return new TagWrapper(this.id, new UpdatableTag(tag)); } constructor(tag) { super(); this.tag = tag; this.lastUpdated = INITIAL; } compute() { return Math.max(this.lastUpdated, this.tag.value()); } update(tag) { if (tag !== this.tag) { this.tag = tag; this.lastUpdated = $REVISION; this.invalidate(); } } } register(UpdatableTag); export class CachedReference { constructor() { this.lastRevision = null; this.lastValue = null; } value() { let { tag, lastRevision, lastValue } = this; if (!lastRevision || !tag.validate(lastRevision)) { lastValue = this.lastValue = this.compute(); this.lastRevision = tag.value(); } return lastValue; } invalidate() { this.lastRevision = null; } } class MapperReference extends CachedReference { constructor(reference, mapper) { super(); this.tag = reference.tag; this.reference = reference; this.mapper = mapper; } compute() { let { reference, mapper } = this; return mapper(reference.value()); } } export function map(reference, mapper) { return new MapperReference(reference, mapper); } ////////// export class ReferenceCache { constructor(reference) { this.lastValue = null; this.lastRevision = null; this.initialized = false; this.tag = reference.tag; this.reference = reference; } peek() { if (!this.initialized) { return this.initialize(); } return this.lastValue; } revalidate() { if (!this.initialized) { return this.initialize(); } let { reference, lastRevision } = this; let tag = reference.tag; if (tag.validate(lastRevision)) return NOT_MODIFIED; this.lastRevision = tag.value(); let { lastValue } = this; let value = reference.value(); if (value === lastValue) return NOT_MODIFIED; this.lastValue = value; return value; } initialize() { let { reference } = this; let value = this.lastValue = reference.value(); this.lastRevision = reference.tag.value(); this.initialized = true; return value; } } const NOT_MODIFIED = "adb3b78e-3d22-4e4b-877a-6317c2c5c145"; export function isModified(value) { return value !== NOT_MODIFIED; }