ember-legacy-class-transform
Version:
The default blueprint for ember-cli addons.
251 lines • 6.78 kB
JavaScript
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;
}