@endorphinjs/template-runtime
Advanced tools
Comparing version 0.1.16 to 0.1.17
@@ -850,3 +850,2 @@ 'use strict'; | ||
dispose: null, | ||
prev: null, | ||
get, | ||
@@ -857,3 +856,6 @@ body, | ||
updated: 0, | ||
rendered: obj(), | ||
rendered: null, | ||
needReorder: false, | ||
localScope: createScope(host), | ||
order: [], | ||
used: null, | ||
@@ -894,10 +896,4 @@ start: null, | ||
block.updated = 0; | ||
block.needReorder = false; | ||
// Do some tricks for better Virtual Scroll lists: | ||
// for example, when we transition from [1, 2, 3] list to [2, 3, 4], | ||
// default algorithm will cause 2 and 3 to be moved before 1, which leads to | ||
// lots of unnecessary DOM operations. By manually controlling `.ptr`, we can | ||
// keyed iterator to update 2 and 3 (if required), add 4 and remove 1 | ||
injector.ptr = null; | ||
const collection = block.get(host, block.scope); | ||
@@ -911,2 +907,3 @@ if (collection && typeof collection.forEach === 'function') { | ||
for (let i = 0, items = rendered[p]; i < items.length; i++) { | ||
block.updated = 1; | ||
disposeBlock(items[i]); | ||
@@ -916,2 +913,11 @@ } | ||
if (block.needReorder) { | ||
reorder(block); | ||
} | ||
if (block.index) { | ||
exitScope(host); | ||
} | ||
block.order.length = 0; | ||
block.rendered = block.used; | ||
@@ -921,2 +927,11 @@ } | ||
/** | ||
* @param {IteratorItemBlock} expected | ||
* @param {KeyIteratorBlock} owner | ||
* @returns {IteratorItemBlock | null} | ||
*/ | ||
function getItem(expected, owner) { | ||
return expected.owner === owner ? expected : null; | ||
} | ||
/** | ||
* @this {KeyIteratorBlock} | ||
@@ -927,57 +942,111 @@ * @param {*} value | ||
function iterator$1(value, key) { | ||
const { host, injector, index, rendered, used, body } = this; | ||
const localScope = { index, key, value }; | ||
const id = this.keyExpr(value, createScope(host, localScope)); | ||
let entry = id in rendered ? rendered[id].shift() : null; | ||
const { host, injector, index, rendered } = this; | ||
const id = getId(this, index, key, value); | ||
let entry = rendered && id in rendered ? rendered[id].shift() : null; | ||
if (entry) { | ||
// Update existing block | ||
if (injector.ptr) { | ||
move(injector, entry, injector.ptr); | ||
const scope = prepareScope(entry ? entry.scope : obj(this.localScope), index, key, value); | ||
setScope(host, scope); | ||
if (!entry) { | ||
entry = injector.ctx = createItem(this, scope); | ||
injector.ptr = entry.start; | ||
entry.update = this.body(host, injector, scope); | ||
this.updated = 1; | ||
} else if (entry.update) { | ||
if (entry.start.prev !== injector.ptr) { | ||
this.needReorder = true; | ||
} | ||
if (entry.update) { | ||
setScope(host, assign(entry.scope, localScope)); | ||
if (run(entry, entry.update, entry.scope)) { | ||
this.updated = 1; | ||
} | ||
exitScope(host); | ||
if (entry.update(host, injector, scope)) { | ||
this.updated = 1; | ||
} | ||
} else { | ||
// Create & render new block | ||
if (!injector.ptr) { | ||
injector.ptr = this.start; | ||
} | ||
} | ||
/** @type {IteratorItemBlock} */ | ||
entry = injectBlock(injector, { | ||
$$block: true, | ||
host, | ||
injector, | ||
scope: enterScope(host, localScope), | ||
dispose: null, | ||
update: undefined, | ||
owner: this, | ||
start: null, | ||
end: null | ||
}); | ||
markUsed(this, id, entry); | ||
injector.ptr = entry.end; | ||
this.index++; | ||
} | ||
entry.update = run(entry, body, entry.scope); | ||
this.updated = 1; | ||
exitScope(host); | ||
/** | ||
* @param {KeyIteratorBlock} block | ||
*/ | ||
function reorder(block) { | ||
const { injector, order } = block; | ||
let actualPrev, actualNext; | ||
let expectedPrev, expectedNext; | ||
for (let i = 0, maxIx = order.length - 1, item; i <= maxIx; i++) { | ||
item = order[i]; | ||
expectedPrev = i > 0 ? order[i - 1] : null; | ||
expectedNext = i < maxIx ? order[i + 1] : null; | ||
actualPrev = getItem(item.start.prev.value, block); | ||
actualNext = getItem(item.end.next.value, block); | ||
if (expectedPrev !== actualPrev && expectedNext !== actualNext) { | ||
// Blocks must be reordered | ||
move(injector, item, expectedPrev ? expectedPrev.end : block.start); | ||
} | ||
} | ||
} | ||
// Mark block as used. | ||
/** | ||
* @param {*} scope | ||
* @param {number} index | ||
* @param {*} key | ||
* @param {*} value | ||
*/ | ||
function prepareScope(scope, index, key, value) { | ||
scope.index = index; | ||
scope.key = key; | ||
scope.value = value; | ||
return scope; | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @param {string} id | ||
* @param {IteratorItemBlock} block | ||
*/ | ||
function markUsed(iterator, id, block) { | ||
const { used } = iterator; | ||
// We allow multiple items key in case of poorly prepared data. | ||
if (id in used) { | ||
used[id].push(entry); | ||
used[id].push(block); | ||
} else { | ||
used[id] = [entry]; | ||
used[id] = [block]; | ||
} | ||
injector.ptr = entry.end; | ||
this.index++; | ||
iterator.order.push(block); | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @param {number} index | ||
* @param {*} key | ||
* @param {*} value | ||
* @return {string} | ||
*/ | ||
function getId(iterator, index, key, value) { | ||
return iterator.keyExpr(value, prepareScope(iterator.localScope, index, key, value)); | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @returns {IteratorItemBlock} | ||
*/ | ||
function createItem(iterator, scope) { | ||
return injectBlock(iterator.injector, { | ||
$$block: true, | ||
host: iterator.host, | ||
injector: iterator.injector, | ||
scope, | ||
dispose: null, | ||
update: undefined, | ||
owner: iterator, | ||
start: null, | ||
end: null | ||
}); | ||
} | ||
/** | ||
* Sets value of attribute `name` to `value` | ||
@@ -984,0 +1053,0 @@ * @param {Injector} injector |
@@ -846,3 +846,2 @@ /** | ||
dispose: null, | ||
prev: null, | ||
get, | ||
@@ -853,3 +852,6 @@ body, | ||
updated: 0, | ||
rendered: obj(), | ||
rendered: null, | ||
needReorder: false, | ||
localScope: createScope(host), | ||
order: [], | ||
used: null, | ||
@@ -890,10 +892,4 @@ start: null, | ||
block.updated = 0; | ||
block.needReorder = false; | ||
// Do some tricks for better Virtual Scroll lists: | ||
// for example, when we transition from [1, 2, 3] list to [2, 3, 4], | ||
// default algorithm will cause 2 and 3 to be moved before 1, which leads to | ||
// lots of unnecessary DOM operations. By manually controlling `.ptr`, we can | ||
// keyed iterator to update 2 and 3 (if required), add 4 and remove 1 | ||
injector.ptr = null; | ||
const collection = block.get(host, block.scope); | ||
@@ -907,2 +903,3 @@ if (collection && typeof collection.forEach === 'function') { | ||
for (let i = 0, items = rendered[p]; i < items.length; i++) { | ||
block.updated = 1; | ||
disposeBlock(items[i]); | ||
@@ -912,2 +909,11 @@ } | ||
if (block.needReorder) { | ||
reorder(block); | ||
} | ||
if (block.index) { | ||
exitScope(host); | ||
} | ||
block.order.length = 0; | ||
block.rendered = block.used; | ||
@@ -917,2 +923,11 @@ } | ||
/** | ||
* @param {IteratorItemBlock} expected | ||
* @param {KeyIteratorBlock} owner | ||
* @returns {IteratorItemBlock | null} | ||
*/ | ||
function getItem(expected, owner) { | ||
return expected.owner === owner ? expected : null; | ||
} | ||
/** | ||
* @this {KeyIteratorBlock} | ||
@@ -923,57 +938,111 @@ * @param {*} value | ||
function iterator$1(value, key) { | ||
const { host, injector, index, rendered, used, body } = this; | ||
const localScope = { index, key, value }; | ||
const id = this.keyExpr(value, createScope(host, localScope)); | ||
let entry = id in rendered ? rendered[id].shift() : null; | ||
const { host, injector, index, rendered } = this; | ||
const id = getId(this, index, key, value); | ||
let entry = rendered && id in rendered ? rendered[id].shift() : null; | ||
if (entry) { | ||
// Update existing block | ||
if (injector.ptr) { | ||
move(injector, entry, injector.ptr); | ||
const scope = prepareScope(entry ? entry.scope : obj(this.localScope), index, key, value); | ||
setScope(host, scope); | ||
if (!entry) { | ||
entry = injector.ctx = createItem(this, scope); | ||
injector.ptr = entry.start; | ||
entry.update = this.body(host, injector, scope); | ||
this.updated = 1; | ||
} else if (entry.update) { | ||
if (entry.start.prev !== injector.ptr) { | ||
this.needReorder = true; | ||
} | ||
if (entry.update) { | ||
setScope(host, assign(entry.scope, localScope)); | ||
if (run(entry, entry.update, entry.scope)) { | ||
this.updated = 1; | ||
} | ||
exitScope(host); | ||
if (entry.update(host, injector, scope)) { | ||
this.updated = 1; | ||
} | ||
} else { | ||
// Create & render new block | ||
if (!injector.ptr) { | ||
injector.ptr = this.start; | ||
} | ||
} | ||
/** @type {IteratorItemBlock} */ | ||
entry = injectBlock(injector, { | ||
$$block: true, | ||
host, | ||
injector, | ||
scope: enterScope(host, localScope), | ||
dispose: null, | ||
update: undefined, | ||
owner: this, | ||
start: null, | ||
end: null | ||
}); | ||
markUsed(this, id, entry); | ||
injector.ptr = entry.end; | ||
this.index++; | ||
} | ||
entry.update = run(entry, body, entry.scope); | ||
this.updated = 1; | ||
exitScope(host); | ||
/** | ||
* @param {KeyIteratorBlock} block | ||
*/ | ||
function reorder(block) { | ||
const { injector, order } = block; | ||
let actualPrev, actualNext; | ||
let expectedPrev, expectedNext; | ||
for (let i = 0, maxIx = order.length - 1, item; i <= maxIx; i++) { | ||
item = order[i]; | ||
expectedPrev = i > 0 ? order[i - 1] : null; | ||
expectedNext = i < maxIx ? order[i + 1] : null; | ||
actualPrev = getItem(item.start.prev.value, block); | ||
actualNext = getItem(item.end.next.value, block); | ||
if (expectedPrev !== actualPrev && expectedNext !== actualNext) { | ||
// Blocks must be reordered | ||
move(injector, item, expectedPrev ? expectedPrev.end : block.start); | ||
} | ||
} | ||
} | ||
// Mark block as used. | ||
/** | ||
* @param {*} scope | ||
* @param {number} index | ||
* @param {*} key | ||
* @param {*} value | ||
*/ | ||
function prepareScope(scope, index, key, value) { | ||
scope.index = index; | ||
scope.key = key; | ||
scope.value = value; | ||
return scope; | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @param {string} id | ||
* @param {IteratorItemBlock} block | ||
*/ | ||
function markUsed(iterator, id, block) { | ||
const { used } = iterator; | ||
// We allow multiple items key in case of poorly prepared data. | ||
if (id in used) { | ||
used[id].push(entry); | ||
used[id].push(block); | ||
} else { | ||
used[id] = [entry]; | ||
used[id] = [block]; | ||
} | ||
injector.ptr = entry.end; | ||
this.index++; | ||
iterator.order.push(block); | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @param {number} index | ||
* @param {*} key | ||
* @param {*} value | ||
* @return {string} | ||
*/ | ||
function getId(iterator, index, key, value) { | ||
return iterator.keyExpr(value, prepareScope(iterator.localScope, index, key, value)); | ||
} | ||
/** | ||
* @param {KeyIteratorBlock} iterator | ||
* @returns {IteratorItemBlock} | ||
*/ | ||
function createItem(iterator, scope) { | ||
return injectBlock(iterator.injector, { | ||
$$block: true, | ||
host: iterator.host, | ||
injector: iterator.injector, | ||
scope, | ||
dispose: null, | ||
update: undefined, | ||
owner: iterator, | ||
start: null, | ||
end: null | ||
}); | ||
} | ||
/** | ||
* Sets value of attribute `name` to `value` | ||
@@ -980,0 +1049,0 @@ * @param {Injector} injector |
{ | ||
"name": "@endorphinjs/template-runtime", | ||
"version": "0.1.16", | ||
"version": "0.1.17", | ||
"description": "EndorphinJS template runtime, embedded with template bundles", | ||
@@ -5,0 +5,0 @@ "main": "./dist/runtime.cjs.js", |
@@ -403,2 +403,9 @@ import { Store } from './lib/store'; | ||
} | null; | ||
order: IteratorItemBlock[]; | ||
needReorder: boolean; | ||
localScope: { | ||
index: number, | ||
key: any, | ||
value: any | ||
} | ||
} | ||
@@ -405,0 +412,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
334718
9
4406