mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Fixes #817 - Support dynamic root elements
[Optimizations] Simplified bookkeeping for component tree A component stack is no longer used Small API improvements Improved how component boundaries are managed Assign keys to all HTML elements and custom tags for better diffing Checking in progress Just build the src when calculating size
This commit is contained in:
parent
8f19884eae
commit
2be98636ea
@ -38,10 +38,10 @@ var minifiers = {
|
|||||||
|
|
||||||
const out = gcc.compile(options);
|
const out = gcc.compile(options);
|
||||||
|
|
||||||
// if (out.errors && out.errors.length) {
|
if (out.errors && out.errors.length) {
|
||||||
// console.error(out.errors);
|
console.error(out.errors);
|
||||||
// throw new Error(`Minification failed for ${file}`);
|
throw new Error(`Minification failed for ${file}`);
|
||||||
// }
|
}
|
||||||
return out.compiledCode;
|
return out.compiledCode;
|
||||||
},
|
},
|
||||||
uglify: function minifyUglifyJS(src, file) {
|
uglify: function minifyUglifyJS(src, file) {
|
||||||
@ -139,4 +139,4 @@ promiseChain.then(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('Minification complete.');
|
console.log('Minification complete.');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
"build-react": "npm run bundle-react --silent && node minify.js react",
|
"build-react": "npm run bundle-react --silent && node minify.js react",
|
||||||
"build-inferno": "npm run bundle-inferno --silent && node minify.js inferno",
|
"build-inferno": "npm run bundle-inferno --silent && node minify.js inferno",
|
||||||
"bundle": "mkdir -p build/bundles && npm run bundle-marko && npm run bundle-react && npm run bundle-vue && npm run bundle-preact && npm run bundle-inferno",
|
"bundle": "mkdir -p build/bundles && npm run bundle-marko && npm run bundle-react && npm run bundle-vue && npm run bundle-preact && npm run bundle-inferno",
|
||||||
"bundle-marko": "node ../../scripts/build && NODE_ENV=production rollup -c marko/rollup.config.js",
|
"bundle-marko": "node ../../scripts/build src && NODE_ENV=production rollup -c marko/rollup.config.js",
|
||||||
"bundle-react": "NODE_ENV=production rollup -c react/rollup.config.js",
|
"bundle-react": "NODE_ENV=production rollup -c react/rollup.config.js",
|
||||||
"bundle-preact": "NODE_ENV=production rollup -c preact/rollup.config.js",
|
"bundle-preact": "NODE_ENV=production rollup -c preact/rollup.config.js",
|
||||||
"bundle-vue": "NODE_ENV=production rollup -c vue/rollup.config.js",
|
"bundle-vue": "NODE_ENV=production rollup -c vue/rollup.config.js",
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/build.js",
|
"build": "node scripts/build.js",
|
||||||
|
"build-src": "node scripts/build.js src",
|
||||||
"prepublish": "npm run build",
|
"prepublish": "npm run build",
|
||||||
"test": "npm run jshint -s && npm run mocha -s && npm run test-components -s && npm run test-components-deprecated -s",
|
"test": "npm run jshint -s && npm run mocha -s && npm run test-components -s && npm run test-components-deprecated -s",
|
||||||
"test-ci": "npm run test-generate-coverage && npm run build && NODE_ENV=production npm run test",
|
"test-ci": "npm run test-generate-coverage && npm run build && NODE_ENV=production npm run test",
|
||||||
|
|||||||
@ -16,34 +16,44 @@ const babelOptions = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
buildDir('src', 'dist', {
|
|
||||||
babelExclude: [
|
|
||||||
'/taglibs/async/client-reorder-runtime.min.js'
|
|
||||||
],
|
|
||||||
babelOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
var buildConstants = {
|
var target = process.argv[2];
|
||||||
isDebug: false
|
|
||||||
};
|
var shouldBuildSrc = true;
|
||||||
|
var shouldBuildTest = true;
|
||||||
|
|
||||||
|
if (target === 'src') {
|
||||||
|
shouldBuildTest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldBuildSrc) {
|
||||||
|
buildDir('src', 'dist', {
|
||||||
|
babelExclude: [
|
||||||
|
'/taglibs/async/client-reorder-runtime.min.js'
|
||||||
|
],
|
||||||
|
babelOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(__dirname, '../dist/build.json'),
|
path.join(__dirname, '../dist/build.json'),
|
||||||
JSON.stringify({ isDebug: false }, null, 4),
|
JSON.stringify({ isDebug: false }, null, 4),
|
||||||
{ encoding: 'utf8' });
|
{ encoding: 'utf8' });
|
||||||
|
|
||||||
buildDir('test', 'test-dist', {
|
if (shouldBuildTest) {
|
||||||
babelExclude: [
|
buildDir('test', 'test-dist', {
|
||||||
'*expected*.*',
|
babelExclude: [
|
||||||
'input.js*'
|
'*expected*.*',
|
||||||
],
|
'input.js*'
|
||||||
exclude: [
|
],
|
||||||
'/generated',
|
exclude: [
|
||||||
'*.marko.js',
|
'/generated',
|
||||||
'*.skip',
|
'*.marko.js',
|
||||||
'*.generated.*',
|
'*.skip',
|
||||||
'*actual*.*',
|
'*.generated.*',
|
||||||
'actualized-expected.html*'
|
'*actual*.*',
|
||||||
],
|
'actualized-expected.html*'
|
||||||
babelOptions
|
],
|
||||||
});
|
babelOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -130,15 +130,22 @@ class Builder {
|
|||||||
return new MemberExpression({object, property, computed});
|
return new MemberExpression({object, property, computed});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates code that joins all of the arguments using `+` (BinaryExpression)
|
||||||
|
*
|
||||||
|
* @param {Array} args If the args object is not an array then `arguments` is used
|
||||||
|
* @return {Node} The resulting Node
|
||||||
|
*/
|
||||||
concat(args) {
|
concat(args) {
|
||||||
var prev;
|
var prev;
|
||||||
let operator = '+';
|
let operator = '+';
|
||||||
|
args = Array.isArray(args) ? args : Array.prototype.slice.call(arguments, 0);
|
||||||
|
|
||||||
for (var i=1; i<arguments.length; i++) {
|
for (var i=1; i<args.length; i++) {
|
||||||
var left;
|
var left;
|
||||||
var right = makeNode(arguments[i]);
|
var right = makeNode(args[i]);
|
||||||
if (i === 1) {
|
if (i === 1) {
|
||||||
left = makeNode(arguments[i-1]);
|
left = makeNode(args[i-1]);
|
||||||
} else {
|
} else {
|
||||||
left = prev;
|
left = prev;
|
||||||
}
|
}
|
||||||
@ -153,15 +160,15 @@ class Builder {
|
|||||||
return new ConditionalExpression({test, consequent, alternate});
|
return new ConditionalExpression({test, consequent, alternate});
|
||||||
}
|
}
|
||||||
|
|
||||||
containerNode(type, generateCode) {
|
containerNode(type, codeGenerator) {
|
||||||
if (typeof type === 'function') {
|
if (typeof type === 'function') {
|
||||||
generateCode = arguments[0];
|
codeGenerator = arguments[0];
|
||||||
type = 'ContainerNode';
|
type = 'ContainerNode';
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = new ContainerNode(type);
|
var node = new ContainerNode(type);
|
||||||
if (generateCode) {
|
if (codeGenerator) {
|
||||||
node.setCodeGenerator(generateCode);
|
node.setCodeGenerator(codeGenerator);
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,8 +136,8 @@ class CompileContext extends EventEmitter {
|
|||||||
this.compilerType = this.options.compilerType || 'marko';
|
this.compilerType = this.options.compilerType || 'marko';
|
||||||
this.compilerVersion = this.options.compilerVersion || markoPkgVersion;
|
this.compilerVersion = this.options.compilerVersion || markoPkgVersion;
|
||||||
this.writeVersionComment = writeVersionComment !== 'undefined' ? writeVersionComment : true;
|
this.writeVersionComment = writeVersionComment !== 'undefined' ? writeVersionComment : true;
|
||||||
this.ignoreUnrecognizedTags = this.options.ignoreUnrecognizedTags || false;
|
this.ignoreUnrecognizedTags = this.options.ignoreUnrecognizedTags === true;
|
||||||
this.escapeAtTags = this.options.escapeAtTags || false;
|
this.escapeAtTags = this.options.escapeAtTags === true;
|
||||||
|
|
||||||
this._vars = {};
|
this._vars = {};
|
||||||
this._uniqueVars = new UniqueVars();
|
this._uniqueVars = new UniqueVars();
|
||||||
@ -168,6 +168,7 @@ class CompileContext extends EventEmitter {
|
|||||||
this._imports = {};
|
this._imports = {};
|
||||||
this._fingerprint = undefined;
|
this._fingerprint = undefined;
|
||||||
this._optimizers = undefined;
|
this._optimizers = undefined;
|
||||||
|
this.isComponent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInline(isInline) {
|
setInline(isInline) {
|
||||||
|
|||||||
@ -257,6 +257,14 @@ class Parser {
|
|||||||
attrDef.argument = attr.argument.value;
|
attrDef.argument = attr.argument.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var attrName = attr.name;
|
||||||
|
|
||||||
|
if (attrName) {
|
||||||
|
if (attrName === 'for-key' || attrName === 'for-ref' || attrName === 'w-for' || attrName.endsWith(':key')) {
|
||||||
|
context.data.hasLegacyForKey = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parsedAttributes.push(attrDef);
|
parsedAttributes.push(attrDef);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,9 +24,9 @@ class DocumentType extends Node {
|
|||||||
toJSON() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
value: this.value
|
value: this.documentType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = DocumentType;
|
module.exports = DocumentType;
|
||||||
|
|||||||
@ -23,18 +23,29 @@ class HtmlComment extends Node {
|
|||||||
var comment = this.comment;
|
var comment = this.comment;
|
||||||
var builder = codegen.builder;
|
var builder = codegen.builder;
|
||||||
|
|
||||||
|
if (Array.isArray(comment)) {
|
||||||
|
comment = builder.concat(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentArgs = [comment];
|
||||||
|
|
||||||
return builder.functionCall(
|
return builder.functionCall(
|
||||||
builder.memberExpression(
|
builder.memberExpression(
|
||||||
builder.identifierOut(),
|
builder.identifierOut(),
|
||||||
builder.identifier('comment')),
|
builder.identifier('comment')),
|
||||||
[
|
commentArgs);
|
||||||
comment
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
walk(walker) {
|
walk(walker) {
|
||||||
this.comment = walker.walk(this.comment);
|
this.comment = walker.walk(this.comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPropertyValue(name, value) {
|
||||||
|
if (!this._properties) {
|
||||||
|
this._properties = {};
|
||||||
|
}
|
||||||
|
this._properties[name] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlComment;
|
module.exports = HtmlComment;
|
||||||
|
|||||||
@ -62,11 +62,21 @@ class HtmlElement extends Node {
|
|||||||
this.selfClosed = def.selfClosed;
|
this.selfClosed = def.selfClosed;
|
||||||
this.dynamicAttributes = undefined;
|
this.dynamicAttributes = undefined;
|
||||||
this.bodyOnlyIf = undefined;
|
this.bodyOnlyIf = undefined;
|
||||||
|
this.runtimeFlags = 0; // Runtime flags are used to flag VDOM nodes with important information (flags are OR'd together)
|
||||||
|
this.key = undefined;
|
||||||
|
|
||||||
this.on('beforeGenerateCode', beforeGenerateCode);
|
this.on('beforeGenerateCode', beforeGenerateCode);
|
||||||
this.on('afterGenerateCode', afterGenerateCode);
|
this.on('afterGenerateCode', afterGenerateCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRuntimeFlag(value) {
|
||||||
|
this.runtimeFlags |= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
setKey(key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
generateHTMLCode(codegen) {
|
generateHTMLCode(codegen) {
|
||||||
if (codegen.context.isMacro(this.tagName)) {
|
if (codegen.context.isMacro(this.tagName)) {
|
||||||
// At code generation time, if node tag corresponds to a registered macro
|
// At code generation time, if node tag corresponds to a registered macro
|
||||||
|
|||||||
@ -3,9 +3,21 @@
|
|||||||
const Node = require('../../Node');
|
const Node = require('../../Node');
|
||||||
const vdomUtil = require('../../../util/vdom');
|
const vdomUtil = require('../../../util/vdom');
|
||||||
|
|
||||||
var FLAG_IS_SVG = 1;
|
const FLAG_IS_SVG = 1;
|
||||||
var FLAG_IS_TEXTAREA = 2;
|
const FLAG_IS_TEXTAREA = 2;
|
||||||
var FLAG_SIMPLE_ATTRS = 4;
|
const FLAG_SIMPLE_ATTRS = 4;
|
||||||
|
// const FLAG_PRESERVE = 8;
|
||||||
|
// const FLAG_COMPONENT_START_NODE = 16;
|
||||||
|
// const FLAG_COMPONENT_END_NODE = 32;
|
||||||
|
|
||||||
|
let CREATE_ARGS_COUNT = 0;
|
||||||
|
const INDEX_TAG_NAME = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_ATTRS = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_KEY = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_COMPONENT = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_CHILD_COUNT = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_FLAGS = CREATE_ARGS_COUNT++;
|
||||||
|
const INDEX_PROPS = CREATE_ARGS_COUNT++;
|
||||||
|
|
||||||
function finalizeCreateArgs(createArgs, builder) {
|
function finalizeCreateArgs(createArgs, builder) {
|
||||||
var length = createArgs.length;
|
var length = createArgs.length;
|
||||||
@ -17,7 +29,7 @@ function finalizeCreateArgs(createArgs, builder) {
|
|||||||
lastArg = arg;
|
lastArg = arg;
|
||||||
} else {
|
} else {
|
||||||
if (lastArg != null) {
|
if (lastArg != null) {
|
||||||
if (i === 3) {
|
if (i === INDEX_FLAGS) {
|
||||||
// Use a literal 0 for the flags
|
// Use a literal 0 for the flags
|
||||||
createArgs[i] = builder.literal(0);
|
createArgs[i] = builder.literal(0);
|
||||||
} else {
|
} else {
|
||||||
@ -46,6 +58,21 @@ const SIMPLE_ATTRS = {
|
|||||||
'id': true
|
'id': true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isStaticProperties(properties) {
|
||||||
|
for (var k in properties) {
|
||||||
|
var v = properties[k];
|
||||||
|
if (v.type !== 'Literal') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof v.value === 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class HtmlElementVDOM extends Node {
|
class HtmlElementVDOM extends Node {
|
||||||
constructor(def) {
|
constructor(def) {
|
||||||
super('HtmlElementVDOM');
|
super('HtmlElementVDOM');
|
||||||
@ -57,6 +84,8 @@ class HtmlElementVDOM extends Node {
|
|||||||
this.properties = def.properties;
|
this.properties = def.properties;
|
||||||
this.body = def.body;
|
this.body = def.body;
|
||||||
this.dynamicAttributes = def.dynamicAttributes;
|
this.dynamicAttributes = def.dynamicAttributes;
|
||||||
|
this.key = def.key;
|
||||||
|
this.runtimeFlags = def.runtimeFlags;
|
||||||
|
|
||||||
this.isSVG = false;
|
this.isSVG = false;
|
||||||
this.isTextArea = false;
|
this.isTextArea = false;
|
||||||
@ -69,7 +98,6 @@ class HtmlElementVDOM extends Node {
|
|||||||
this.createElementId = undefined;
|
this.createElementId = undefined;
|
||||||
this.attributesArg = undefined;
|
this.attributesArg = undefined;
|
||||||
this.propertiesArg = undefined;
|
this.propertiesArg = undefined;
|
||||||
this.nextConstId = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateCode(codegen) {
|
generateCode(codegen) {
|
||||||
@ -186,6 +214,8 @@ class HtmlElementVDOM extends Node {
|
|||||||
|
|
||||||
this.attributesArg = attributesArg;
|
this.attributesArg = attributesArg;
|
||||||
|
|
||||||
|
this.properties = builder.literal(this.properties || {});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,25 +230,34 @@ class HtmlElementVDOM extends Node {
|
|||||||
|
|
||||||
let body = this.body;
|
let body = this.body;
|
||||||
let attributesArg = this.attributesArg;
|
let attributesArg = this.attributesArg;
|
||||||
let nextConstId = this.nextConstId;
|
|
||||||
let tagName = this.tagName;
|
let tagName = this.tagName;
|
||||||
|
|
||||||
|
let key = this.key;
|
||||||
|
|
||||||
let childCount = body && body.length;
|
let childCount = body && body.length;
|
||||||
|
|
||||||
let createArgs = new Array(5); // tagName, attributes, childCount, const ID, flags
|
let createArgs = new Array(CREATE_ARGS_COUNT);
|
||||||
|
|
||||||
createArgs[0] = tagName;
|
createArgs[INDEX_TAG_NAME] = tagName;
|
||||||
|
|
||||||
if (attributesArg) {
|
if (attributesArg) {
|
||||||
createArgs[1] = attributesArg;
|
createArgs[INDEX_ATTRS] = attributesArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
createArgs[INDEX_KEY] = key;
|
||||||
|
|
||||||
|
if (!this.isStatic) {
|
||||||
|
createArgs[INDEX_COMPONENT] = builder.identifier('component');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (childCount != null) {
|
if (childCount != null) {
|
||||||
createArgs[2] = builder.literal(childCount);
|
createArgs[INDEX_CHILD_COUNT] = builder.literal(childCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var flags = 0;
|
var flags = 0;
|
||||||
|
|
||||||
if (this.isSVG) {
|
if (this.isSVG) {
|
||||||
@ -233,19 +272,21 @@ class HtmlElementVDOM extends Node {
|
|||||||
flags |= FLAG_SIMPLE_ATTRS;
|
flags |= FLAG_SIMPLE_ATTRS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.runtimeFlags) {
|
||||||
|
flags |= this.runtimeFlags;
|
||||||
|
}
|
||||||
|
|
||||||
if (flags) {
|
if (flags) {
|
||||||
createArgs[3] = builder.literal(flags);
|
createArgs[INDEX_FLAGS] = builder.literal(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextConstId) {
|
let properties = this.properties;
|
||||||
if (!this.properties) {
|
|
||||||
this.properties = {};
|
|
||||||
}
|
|
||||||
this.properties.c = nextConstId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.properties) {
|
if (this.properties && properties.type === 'Literal' && Object.keys(properties.value).length === 0) {
|
||||||
createArgs[4] = builder.literal(this.properties);
|
properties = null;
|
||||||
|
}
|
||||||
|
if (properties) {
|
||||||
|
createArgs[INDEX_PROPS] = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing undefined arguments and convert non-trailing
|
// Remove trailing undefined arguments and convert non-trailing
|
||||||
@ -290,6 +331,21 @@ class HtmlElementVDOM extends Node {
|
|||||||
writer.decIndent();
|
writer.decIndent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setConstId(value) {
|
||||||
|
this.properties.value.i = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalizeProperties(context) {
|
||||||
|
if (this.properties.type === 'Literal' && isStaticProperties(this.properties.value)) {
|
||||||
|
if (Object.keys(this.properties.value).length === 0) {
|
||||||
|
this.properties = null;
|
||||||
|
} else {
|
||||||
|
this.properties = context.addStaticVar('props', this.properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlElementVDOM;
|
module.exports = HtmlElementVDOM;
|
||||||
|
|||||||
@ -38,11 +38,16 @@ module.exports = function(node, codegen, vdomUtil) {
|
|||||||
var attributes = codegen.generateCode(node.getAttributes());
|
var attributes = codegen.generateCode(node.getAttributes());
|
||||||
var properties = codegen.generateCode(node.getProperties());
|
var properties = codegen.generateCode(node.getProperties());
|
||||||
var dynamicAttributes = codegen.generateCode(node.dynamicAttributes);
|
var dynamicAttributes = codegen.generateCode(node.dynamicAttributes);
|
||||||
|
var key = node.key;
|
||||||
|
var runtimeFlags = node.runtimeFlags;
|
||||||
|
var nextConstId = node.nextConstId;
|
||||||
|
|
||||||
var builder = codegen.builder;
|
var builder = codegen.builder;
|
||||||
|
|
||||||
|
var isKeyStatic = vdomUtil.isStaticValue(key);
|
||||||
var isAttrsStatic = checkAttributesStatic(attributes);
|
var isAttrsStatic = checkAttributesStatic(attributes);
|
||||||
var isPropsStatic = checkPropertiesStatic(properties, vdomUtil);
|
var isPropsStatic = checkPropertiesStatic(properties, vdomUtil);
|
||||||
var isStatic = isAttrsStatic && isPropsStatic && node.isLiteralTagName();
|
var isStatic = isKeyStatic && isAttrsStatic && isPropsStatic && node.isLiteralTagName();
|
||||||
var isHtmlOnly = true;
|
var isHtmlOnly = true;
|
||||||
|
|
||||||
if (body && body.length) {
|
if (body && body.length) {
|
||||||
@ -71,6 +76,7 @@ module.exports = function(node, codegen, vdomUtil) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var htmlElVDOM = new HtmlElementVDOM({
|
var htmlElVDOM = new HtmlElementVDOM({
|
||||||
|
key,
|
||||||
tagName,
|
tagName,
|
||||||
attributes,
|
attributes,
|
||||||
properties,
|
properties,
|
||||||
@ -78,7 +84,9 @@ module.exports = function(node, codegen, vdomUtil) {
|
|||||||
isStatic,
|
isStatic,
|
||||||
isAttrsStatic,
|
isAttrsStatic,
|
||||||
isHtmlOnly,
|
isHtmlOnly,
|
||||||
dynamicAttributes
|
dynamicAttributes,
|
||||||
|
nextConstId,
|
||||||
|
runtimeFlags
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -125,6 +125,13 @@ class Node {
|
|||||||
this.body.appendChild(node);
|
this.body.appendChild(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendChildren(nodes) {
|
||||||
|
ok(this.body, 'Node does not support child nodes: ' + this);
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
this.body.appendChild(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
insertBefore(newNode, referenceNode) {
|
insertBefore(newNode, referenceNode) {
|
||||||
ok(this.body, 'Node does not support child nodes: ' + this);
|
ok(this.body, 'Node does not support child nodes: ' + this);
|
||||||
this.body.insertBefore(newNode, referenceNode);
|
this.body.insertBefore(newNode, referenceNode);
|
||||||
|
|||||||
@ -53,19 +53,23 @@ class OptimizerContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NodeVDOM extends Node {
|
class NodeVDOM extends Node {
|
||||||
constructor(variableIdentifier) {
|
constructor(variableIdentifier, isComponent) {
|
||||||
super('NodeVDOM');
|
super('NodeVDOM');
|
||||||
this.variableIdentifier = variableIdentifier;
|
this.variableIdentifier = variableIdentifier;
|
||||||
|
this.isComponent = isComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCode(writer) {
|
writeCode(writer) {
|
||||||
var builder = writer.builder;
|
var builder = writer.builder;
|
||||||
|
var funcCallArgs = [ this.variableIdentifier ];
|
||||||
|
|
||||||
|
if (this.isComponent) {
|
||||||
|
funcCallArgs.push(builder.identifier('component'));
|
||||||
|
}
|
||||||
|
|
||||||
let funcCall = builder.functionCall(
|
let funcCall = builder.functionCall(
|
||||||
builder.identifier('n'),
|
builder.identifier('n'),
|
||||||
[
|
funcCallArgs);
|
||||||
this.variableIdentifier
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (this.isChild) {
|
if (this.isChild) {
|
||||||
writer.write('.');
|
writer.write('.');
|
||||||
@ -89,16 +93,16 @@ function generateNodesForArray(nodes, context, options) {
|
|||||||
function generateStaticNode(node) {
|
function generateStaticNode(node) {
|
||||||
if (node.type === 'HtmlElementVDOM') {
|
if (node.type === 'HtmlElementVDOM') {
|
||||||
node.createElementId = context.helper('createElement');
|
node.createElementId = context.helper('createElement');
|
||||||
|
|
||||||
|
node.setConstId(builder.functionCall(optimizerContext.nextConstIdFunc, []));
|
||||||
}/* else {
|
}/* else {
|
||||||
node.createTextId = context.importModule('marko_createText', 'marko/vdom/createText');
|
node.createTextId = context.importModule('marko_createText', 'marko/vdom/createText');
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
node.nextConstId = builder.functionCall(optimizerContext.nextConstIdFunc, []);
|
|
||||||
|
|
||||||
node.isStaticRoot = true;
|
node.isStaticRoot = true;
|
||||||
let staticNodeId = context.addStaticVar('marko_node' + (optimizerContext.nextNodeId++), node);
|
let staticNodeId = context.addStaticVar('marko_node' + (optimizerContext.nextNodeId++), node);
|
||||||
|
|
||||||
return new NodeVDOM(staticNodeId);
|
return new NodeVDOM(staticNodeId, context.isComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleStaticAttributes(node) {
|
function handleStaticAttributes(node) {
|
||||||
@ -131,6 +135,7 @@ function generateNodesForArray(nodes, context, options) {
|
|||||||
finalNodes.push(node);
|
finalNodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.finalizeProperties(context);
|
||||||
} else {
|
} else {
|
||||||
finalNodes.push(node);
|
finalNodes.push(node);
|
||||||
}
|
}
|
||||||
@ -157,4 +162,4 @@ class VDOMOptimizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = VDOMOptimizer;
|
module.exports = VDOMOptimizer;
|
||||||
|
|||||||
@ -7,53 +7,27 @@ var getComponentsContext = require('./ComponentsContext').___getComponentsContex
|
|||||||
var componentsUtil = require('./util');
|
var componentsUtil = require('./util');
|
||||||
var componentLookup = componentsUtil.___componentLookup;
|
var componentLookup = componentsUtil.___componentLookup;
|
||||||
var emitLifecycleEvent = componentsUtil.___emitLifecycleEvent;
|
var emitLifecycleEvent = componentsUtil.___emitLifecycleEvent;
|
||||||
var destroyComponentForEl = componentsUtil.___destroyComponentForEl;
|
var destroyNodeRecursive = componentsUtil.___destroyNodeRecursive;
|
||||||
var destroyElRecursive = componentsUtil.___destroyElRecursive;
|
|
||||||
var getElementById = componentsUtil.___getElementById;
|
|
||||||
var EventEmitter = require('events-light');
|
var EventEmitter = require('events-light');
|
||||||
var RenderResult = require('../runtime/RenderResult');
|
var RenderResult = require('../runtime/RenderResult');
|
||||||
var SubscriptionTracker = require('listener-tracker');
|
var SubscriptionTracker = require('listener-tracker');
|
||||||
var inherit = require('raptor-util/inherit');
|
var inherit = require('raptor-util/inherit');
|
||||||
var updateManager = require('./update-manager');
|
var updateManager = require('./update-manager');
|
||||||
var morphdom = require('../morphdom');
|
var morphdom = require('../morphdom');
|
||||||
var eventDelegation = require('./event-delegation');
|
|
||||||
|
|
||||||
var slice = Array.prototype.slice;
|
var slice = Array.prototype.slice;
|
||||||
|
|
||||||
var MORPHDOM_SKIP = true;
|
|
||||||
|
|
||||||
var COMPONENT_SUBSCRIBE_TO_OPTIONS;
|
var COMPONENT_SUBSCRIBE_TO_OPTIONS;
|
||||||
var NON_COMPONENT_SUBSCRIBE_TO_OPTIONS = {
|
var NON_COMPONENT_SUBSCRIBE_TO_OPTIONS = {
|
||||||
addDestroyListener: false
|
addDestroyListener: false
|
||||||
};
|
};
|
||||||
|
|
||||||
function outNoop() { /* jshint -W040 */ return this; }
|
|
||||||
|
|
||||||
var emit = EventEmitter.prototype.emit;
|
var emit = EventEmitter.prototype.emit;
|
||||||
|
|
||||||
function removeListener(removeEventListenerHandle) {
|
function removeListener(removeEventListenerHandle) {
|
||||||
removeEventListenerHandle();
|
removeEventListenerHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCompatibleComponent(globalComponentsContext, el) {
|
|
||||||
var component = el._w;
|
|
||||||
while(component) {
|
|
||||||
var id = component.id;
|
|
||||||
var newComponentDef = globalComponentsContext.___componentsById[id];
|
|
||||||
if (newComponentDef && component.___type == newComponentDef.___component.___type) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootFor = component.___rootFor;
|
|
||||||
if (rootFor) {
|
|
||||||
component = rootFor;
|
|
||||||
} else {
|
|
||||||
component.___destroyShallow();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCustomEventWithMethodListener(component, targetMethodName, args, extraArgs) {
|
function handleCustomEventWithMethodListener(component, targetMethodName, args, extraArgs) {
|
||||||
// Remove the "eventType" argument
|
// Remove the "eventType" argument
|
||||||
args.push(component);
|
args.push(component);
|
||||||
@ -72,16 +46,12 @@ function handleCustomEventWithMethodListener(component, targetMethodName, args,
|
|||||||
targetMethod.apply(targetComponent, args);
|
targetMethod.apply(targetComponent, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElIdHelper(component, componentElId, index) {
|
function resolveKeyHelper(key, index) {
|
||||||
var id = component.id;
|
return index ? key + '_' + index : key;
|
||||||
|
}
|
||||||
|
|
||||||
var elId = componentElId != null ? id + '-' + componentElId : id;
|
function resolveComponentIdHelper(component, key, index) {
|
||||||
|
return component.id + '-' + resolveKeyHelper(key, index);
|
||||||
if (index != null) {
|
|
||||||
elId += '[' + index + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
return elId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,46 +128,10 @@ function checkInputChanged(existingComponent, oldInput, newInput) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onNodeDiscarded(node) {
|
function getNodes(component) {
|
||||||
if (node.nodeType === 1) {
|
var nodes = [];
|
||||||
destroyComponentForEl(node);
|
component.___forEachNode(nodes.push.bind(nodes));
|
||||||
}
|
return nodes;
|
||||||
}
|
|
||||||
|
|
||||||
function onBeforeNodeDiscarded(node) {
|
|
||||||
return eventDelegation.___handleNodeDetach(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBeforeElUpdated(fromEl, key, globalComponentsContext) {
|
|
||||||
if (key) {
|
|
||||||
var preserved = globalComponentsContext.___preserved[key];
|
|
||||||
|
|
||||||
if (preserved === true) {
|
|
||||||
// Don't morph elements that are associated with components that are being
|
|
||||||
// reused or elements that are being preserved. For components being reused,
|
|
||||||
// the morphing will take place when the reused component updates.
|
|
||||||
return MORPHDOM_SKIP;
|
|
||||||
} else {
|
|
||||||
// We may need to destroy a Component associated with the current element
|
|
||||||
// if a new UI component was rendered to the same element and the types
|
|
||||||
// do not match
|
|
||||||
checkCompatibleComponent(globalComponentsContext, fromEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBeforeElChildrenUpdated(el, key, globalComponentsContext) {
|
|
||||||
if (key) {
|
|
||||||
var preserved = globalComponentsContext.___preservedBodies[key];
|
|
||||||
if (preserved === true) {
|
|
||||||
// Don't morph the children since they are preserved
|
|
||||||
return MORPHDOM_SKIP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onNodeAdded(node, globalComponentsContext) {
|
|
||||||
eventDelegation.___handleNodeAttach(node, globalComponentsContext.___out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var componentProto;
|
var componentProto;
|
||||||
@ -210,9 +144,9 @@ var componentProto;
|
|||||||
function Component(id) {
|
function Component(id) {
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.el = null;
|
|
||||||
this.___state = null;
|
this.___state = null;
|
||||||
this.___roots = null;
|
this.___startNode = null;
|
||||||
|
this.___endNode = null;
|
||||||
this.___subscriptions = null;
|
this.___subscriptions = null;
|
||||||
this.___domEventListenerHandles = null;
|
this.___domEventListenerHandles = null;
|
||||||
this.___bubblingDomEvents = null; // Used to keep track of bubbling DOM events for components rendered on the server
|
this.___bubblingDomEvents = null; // Used to keep track of bubbling DOM events for components rendered on the server
|
||||||
@ -229,6 +163,8 @@ function Component(id) {
|
|||||||
this.___settingInput = false;
|
this.___settingInput = false;
|
||||||
|
|
||||||
this.___document = undefined;
|
this.___document = undefined;
|
||||||
|
|
||||||
|
this.___keyedElements = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.prototype = componentProto = {
|
Component.prototype = componentProto = {
|
||||||
@ -264,63 +200,57 @@ Component.prototype = componentProto = {
|
|||||||
return emit.apply(this, arguments);
|
return emit.apply(this, arguments);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getElId: function (componentElId, index) {
|
getElId: function (key, index) {
|
||||||
return getElIdHelper(this, componentElId, index);
|
return resolveComponentIdHelper(this, key, index);
|
||||||
},
|
},
|
||||||
getEl: function (componentElId, index) {
|
getEl: function (key, index) {
|
||||||
var doc = this.___document;
|
if (key) {
|
||||||
|
return this.___keyedElements[resolveKeyHelper(key, index)];
|
||||||
if (componentElId != null) {
|
|
||||||
return getElementById(doc, getElIdHelper(this, componentElId, index));
|
|
||||||
} else {
|
} else {
|
||||||
return this.el || getElementById(doc, getElIdHelper(this));
|
return this.___startNode;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getEls: function(id) {
|
getEls: function(key) {
|
||||||
|
key = key + '[]';
|
||||||
|
|
||||||
var els = [];
|
var els = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var el;
|
var el;
|
||||||
while((el = this.getEl(id, i))) {
|
while((el = this.getEl(key, i))) {
|
||||||
els.push(el);
|
els.push(el);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return els;
|
return els;
|
||||||
},
|
},
|
||||||
getComponent: function(id, index) {
|
getComponent: function(key, index) {
|
||||||
return componentLookup[getElIdHelper(this, id, index)];
|
return componentLookup[resolveComponentIdHelper(this, key, index)];
|
||||||
},
|
},
|
||||||
getComponents: function(id) {
|
getComponents: function(key) {
|
||||||
|
key = key + '[]';
|
||||||
|
|
||||||
var components = [];
|
var components = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var component;
|
var component;
|
||||||
while((component = componentLookup[getElIdHelper(this, id, i)])) {
|
while((component = componentLookup[resolveComponentIdHelper(this, key, i)])) {
|
||||||
components.push(component);
|
components.push(component);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return components;
|
return components;
|
||||||
},
|
},
|
||||||
destroy: function() {
|
destroy: function(onBeforeNodeDiscarded) {
|
||||||
if (this.___destroyed) {
|
if (this.___destroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var els = this.els;
|
var nodes = getNodes(this);
|
||||||
|
|
||||||
this.___destroyShallow();
|
this.___destroyShallow();
|
||||||
|
|
||||||
var rootComponents = this.___rootComponents;
|
nodes.forEach(function(node) {
|
||||||
if (rootComponents) {
|
destroyNodeRecursive(node);
|
||||||
rootComponents.forEach(function(rootComponent) {
|
|
||||||
rootComponent.___destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
els.forEach(function(el) {
|
if (!onBeforeNodeDiscarded || onBeforeNodeDiscarded(node) != false) {
|
||||||
destroyElRecursive(el);
|
node.parentNode.removeChild(node);
|
||||||
|
|
||||||
var parentNode = el.parentNode;
|
|
||||||
if (parentNode) {
|
|
||||||
parentNode.removeChild(el);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -333,7 +263,9 @@ Component.prototype = componentProto = {
|
|||||||
emitLifecycleEvent(this, 'destroy');
|
emitLifecycleEvent(this, 'destroy');
|
||||||
this.___destroyed = true;
|
this.___destroyed = true;
|
||||||
|
|
||||||
this.el = null;
|
this.___startNode.___markoComponent = undefined;
|
||||||
|
|
||||||
|
this.___startNode = this.___endNode = null;
|
||||||
|
|
||||||
// Unsubscribe from all DOM events
|
// Unsubscribe from all DOM events
|
||||||
this.___removeDOMEventListeners();
|
this.___removeDOMEventListeners();
|
||||||
@ -452,6 +384,7 @@ Component.prototype = componentProto = {
|
|||||||
|
|
||||||
___queueUpdate: function() {
|
___queueUpdate: function() {
|
||||||
if (!this.___updateQueued) {
|
if (!this.___updateQueued) {
|
||||||
|
this.___updateQueued = true;
|
||||||
updateManager.___queueComponentUpdate(this);
|
updateManager.___queueComponentUpdate(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -512,7 +445,10 @@ Component.prototype = componentProto = {
|
|||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
throw TypeError();
|
throw TypeError();
|
||||||
}
|
}
|
||||||
var fromEls = self.___getRootEls({});
|
|
||||||
|
var startNode = this.___startNode;
|
||||||
|
var endNodeNextSibling = this.___endNode.nextSibling;
|
||||||
|
|
||||||
var doc = self.___document;
|
var doc = self.___document;
|
||||||
var input = this.___renderInput || this.___input;
|
var input = this.___renderInput || this.___input;
|
||||||
var globalData = this.___global;
|
var globalData = this.___global;
|
||||||
@ -523,18 +459,6 @@ Component.prototype = componentProto = {
|
|||||||
out.sync();
|
out.sync();
|
||||||
out.___document = self.___document;
|
out.___document = self.___document;
|
||||||
|
|
||||||
if (isRerenderInBrowser === true) {
|
|
||||||
out.e =
|
|
||||||
out.be =
|
|
||||||
out.ee =
|
|
||||||
out.t =
|
|
||||||
out.h =
|
|
||||||
out.w =
|
|
||||||
out.write =
|
|
||||||
out.html =
|
|
||||||
outNoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
var componentsContext = getComponentsContext(out);
|
var componentsContext = getComponentsContext(out);
|
||||||
var globalComponentsContext = componentsContext.___globalContext;
|
var globalComponentsContext = componentsContext.___globalContext;
|
||||||
globalComponentsContext.___rerenderComponent = self;
|
globalComponentsContext.___rerenderComponent = self;
|
||||||
@ -544,68 +468,40 @@ Component.prototype = componentProto = {
|
|||||||
|
|
||||||
var result = new RenderResult(out);
|
var result = new RenderResult(out);
|
||||||
|
|
||||||
if (isRerenderInBrowser !== true) {
|
var targetNode = out.___getOutput();
|
||||||
var targetNode = out.___getOutput();
|
|
||||||
|
|
||||||
var fromEl;
|
morphdom(
|
||||||
|
startNode.parentNode,
|
||||||
var targetEl = targetNode.___firstChild;
|
startNode,
|
||||||
while (targetEl) {
|
endNodeNextSibling,
|
||||||
var nodeName = targetEl.___nodeName;
|
targetNode,
|
||||||
|
doc,
|
||||||
if (nodeName === 'HTML') {
|
componentsContext);
|
||||||
fromEl = document.documentElement;
|
|
||||||
} else if (nodeName === 'BODY') {
|
|
||||||
fromEl = document.body;
|
|
||||||
} else if (nodeName === 'HEAD') {
|
|
||||||
fromEl = document.head;
|
|
||||||
} else {
|
|
||||||
fromEl = fromEls[targetEl.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromEl) {
|
|
||||||
morphdom(
|
|
||||||
fromEl,
|
|
||||||
targetEl,
|
|
||||||
globalComponentsContext,
|
|
||||||
onNodeAdded,
|
|
||||||
onBeforeElUpdated,
|
|
||||||
onBeforeNodeDiscarded,
|
|
||||||
onNodeDiscarded,
|
|
||||||
onBeforeElChildrenUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
targetEl = targetEl.___nextSibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.afterInsert(doc);
|
result.afterInsert(doc);
|
||||||
|
|
||||||
out.emit('___componentsInitialized');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.___reset();
|
this.___reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
___getRootEls: function(rootEls) {
|
___detach: function() {
|
||||||
var i, len;
|
var fragment = this.___document.createDocumentFragment();
|
||||||
|
this.___forEachNode(fragment.appendChild.bind(fragment));
|
||||||
|
return fragment;
|
||||||
|
},
|
||||||
|
|
||||||
var componentEls = this.els;
|
___forEachNode: function(callback) {
|
||||||
|
var currentNode = this.___startNode;
|
||||||
|
var endNode = this.___endNode;
|
||||||
|
|
||||||
for (i=0, len=componentEls.length; i<len; i++) {
|
for(;;) {
|
||||||
var componentEl = componentEls[i];
|
var nextSibling = currentNode.nextSibling;
|
||||||
rootEls[componentEl.id] = componentEl;
|
callback(currentNode);
|
||||||
}
|
if (currentNode == endNode) {
|
||||||
|
break;
|
||||||
var rootComponents = this.___rootComponents;
|
|
||||||
if (rootComponents) {
|
|
||||||
for (i=0, len=rootComponents.length; i<len; i++) {
|
|
||||||
var rootComponent = rootComponents[i];
|
|
||||||
rootComponent.___getRootEls(rootEls);
|
|
||||||
}
|
}
|
||||||
|
currentNode = nextSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootEls;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
___removeDOMEventListeners: function() {
|
___removeDOMEventListeners: function() {
|
||||||
@ -632,6 +528,14 @@ Component.prototype = componentProto = {
|
|||||||
|
|
||||||
finalCustomEvents[eventType] = [targetMethodName, extraArgs];
|
finalCustomEvents[eventType] = [targetMethodName, extraArgs];
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get el() {
|
||||||
|
return this.___startNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
get els() {
|
||||||
|
return getNodes(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -649,17 +553,7 @@ componentProto.___destroy = componentProto.destroy;
|
|||||||
domInsert(
|
domInsert(
|
||||||
componentProto,
|
componentProto,
|
||||||
function getEl(component) {
|
function getEl(component) {
|
||||||
var els = this.els;
|
return component.___detach();
|
||||||
var elCount = els.length;
|
|
||||||
if (elCount > 1) {
|
|
||||||
var fragment = component.___document.createDocumentFragment();
|
|
||||||
els.forEach(function(el) {
|
|
||||||
fragment.appendChild(el);
|
|
||||||
});
|
|
||||||
return fragment;
|
|
||||||
} else {
|
|
||||||
return els[0];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
function afterInsert(component) {
|
function afterInsert(component) {
|
||||||
return component;
|
return component;
|
||||||
|
|||||||
@ -3,48 +3,50 @@ var repeatedRegExp = /\[\]$/;
|
|||||||
var componentUtil = require('./util');
|
var componentUtil = require('./util');
|
||||||
var attachBubblingEvent = componentUtil.___attachBubblingEvent;
|
var attachBubblingEvent = componentUtil.___attachBubblingEvent;
|
||||||
var extend = require('raptor-util/extend');
|
var extend = require('raptor-util/extend');
|
||||||
|
var KeySequence = require('./KeySequence');
|
||||||
|
|
||||||
|
/*
|
||||||
|
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
var FLAG_HAS_BODY_EL = 2;
|
||||||
|
var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ComponentDef is used to hold the metadata collected at runtime for
|
* A ComponentDef is used to hold the metadata collected at runtime for
|
||||||
* a single component and this information is used to instantiate the component
|
* a single component and this information is used to instantiate the component
|
||||||
* later (after the rendered HTML has been added to the DOM)
|
* later (after the rendered HTML has been added to the DOM)
|
||||||
*/
|
*/
|
||||||
function ComponentDef(component, componentId, globalComponentsContext, componentStack, componentStackLen) {
|
function ComponentDef(component, componentId, globalComponentsContext) {
|
||||||
this.___globalComponentsContext = globalComponentsContext; // The AsyncWriter that this component is associated with
|
this.___globalComponentsContext = globalComponentsContext; // The AsyncWriter that this component is associated with
|
||||||
this.___componentStack = componentStack;
|
|
||||||
this.___componentStackLen = componentStackLen;
|
|
||||||
this.___component = component;
|
this.___component = component;
|
||||||
this.id = componentId;
|
this.id = componentId;
|
||||||
|
|
||||||
this.___roots = null; // IDs of root elements if there are multiple root elements
|
|
||||||
this.___children = null; // An array of nested ComponentDef instances
|
|
||||||
this.___domEvents = undefined; // An array of DOM events that need to be added (in sets of three)
|
this.___domEvents = undefined; // An array of DOM events that need to be added (in sets of three)
|
||||||
|
|
||||||
this.___isExisting = false;
|
this.___isExisting = false;
|
||||||
|
|
||||||
this.___willRerenderInBrowser = false;
|
this.___renderBoundary = false;
|
||||||
|
this.___flags = 0;
|
||||||
|
|
||||||
this.___nextIdIndex = 0; // The unique integer to use for the next scoped ID
|
this.___nextIdIndex = 0; // The unique integer to use for the next scoped ID
|
||||||
|
|
||||||
|
this.___keySequence = null;
|
||||||
|
|
||||||
|
this.___preservedDOMNodes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ComponentDef.prototype = {
|
ComponentDef.prototype = {
|
||||||
___end: function() {
|
|
||||||
this.___componentStack.length = this.___componentStackLen;
|
___nextKey: function(key) {
|
||||||
|
var keySequence = this.___keySequence || (this.___keySequence = new KeySequence());
|
||||||
|
return keySequence.___nextKey(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
___preserveDOMNode: function(key, bodyOnly) {
|
||||||
* Register a nested component for this component. We maintain a tree of components
|
var lookup = this.___preservedDOMNodes || (this.___preservedDOMNodes = {});
|
||||||
* so that we can instantiate nested components before their parents.
|
lookup[key] = bodyOnly ? 2 : 1;
|
||||||
*/
|
|
||||||
___addChild: function (componentDef) {
|
|
||||||
var children = this.___children;
|
|
||||||
|
|
||||||
if (children) {
|
|
||||||
children.push(componentDef);
|
|
||||||
} else {
|
|
||||||
this.___children = [componentDef];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This helper method generates a unique and fully qualified DOM element ID
|
* This helper method generates a unique and fully qualified DOM element ID
|
||||||
* that is unique within the scope of the current component. This method prefixes
|
* that is unique within the scope of the current component. This method prefixes
|
||||||
@ -88,15 +90,15 @@ ComponentDef.prototype = {
|
|||||||
* Returns the next auto generated unique ID for a nested DOM element or nested DOM component
|
* Returns the next auto generated unique ID for a nested DOM element or nested DOM component
|
||||||
*/
|
*/
|
||||||
___nextComponentId: function() {
|
___nextComponentId: function() {
|
||||||
var id = this.id;
|
return this.id + '-c' + (this.___nextIdIndex++);
|
||||||
|
|
||||||
return id === null ?
|
|
||||||
this.___globalComponentsContext.___nextComponentId(this.___out) :
|
|
||||||
id + '-c' + (this.___nextIdIndex++);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
d: function(handlerMethodName, extraArgs) {
|
d: function(handlerMethodName, extraArgs) {
|
||||||
return attachBubblingEvent(this, handlerMethodName, extraArgs);
|
return attachBubblingEvent(this, handlerMethodName, extraArgs);
|
||||||
|
},
|
||||||
|
|
||||||
|
get ___type() {
|
||||||
|
return this.___component.___type;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,10 +148,11 @@ ComponentDef.___deserialize = function(o, types, globals, registry) {
|
|||||||
component.___global = globals;
|
component.___global = globals;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
id: id,
|
||||||
___component: component,
|
___component: component,
|
||||||
___roots: extra.r,
|
___boundary: extra.r,
|
||||||
___domEvents: extra.d,
|
___domEvents: extra.d,
|
||||||
___willRerenderInBrowser: extra._ === 1
|
___flags: extra.f || 0
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,117 +1,47 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
var GlobalComponentsContext = require('./GlobalComponentsContext');
|
||||||
|
|
||||||
var ComponentDef = require('./ComponentDef');
|
function ComponentsContext(out, parentComponentsContext) {
|
||||||
var componentsUtil = require('./util');
|
var globalComponentsContext;
|
||||||
|
var componentDef;
|
||||||
|
var components;
|
||||||
|
|
||||||
var beginComponent = require('./beginComponent');
|
if (parentComponentsContext) {
|
||||||
|
components = parentComponentsContext.___components;
|
||||||
|
globalComponentsContext = parentComponentsContext.___globalContext;
|
||||||
|
componentDef = parentComponentsContext.___componentDef;
|
||||||
|
} else {
|
||||||
|
globalComponentsContext = out.global.___components;
|
||||||
|
if (globalComponentsContext === undefined) {
|
||||||
|
out.global.___components = globalComponentsContext = new GlobalComponentsContext(out);
|
||||||
|
}
|
||||||
|
components = [];
|
||||||
|
}
|
||||||
|
|
||||||
var EMPTY_OBJECT = {};
|
this.___globalContext = globalComponentsContext;
|
||||||
|
this.___components = components;
|
||||||
function GlobalComponentsContext(out) {
|
|
||||||
this.___roots = [];
|
|
||||||
this.___preserved = EMPTY_OBJECT;
|
|
||||||
this.___preservedBodies = EMPTY_OBJECT;
|
|
||||||
this.___componentsById = {};
|
|
||||||
this.___out = out;
|
this.___out = out;
|
||||||
this.___rerenderComponent = undefined;
|
this.___componentDef = componentDef;
|
||||||
this.___nextIdLookup = null;
|
|
||||||
this.___nextComponentId = componentsUtil.___nextComponentIdProvider(out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalComponentsContext.prototype = {
|
ComponentsContext.prototype = {
|
||||||
___initComponents: function(doc) {
|
___initComponents: function(doc) {
|
||||||
var topLevelComponentDefs = null;
|
var componentDefs = this.___components;
|
||||||
|
|
||||||
this.___roots.forEach(function(root) {
|
ComponentsContext.___initClientRendered(componentDefs, doc);
|
||||||
var children = root.___children;
|
|
||||||
if (children) {
|
|
||||||
// NOTE: ComponentsContext.___initClientRendered is provided by
|
|
||||||
// index-browser.js to avoid a circular dependency
|
|
||||||
ComponentsContext.___initClientRendered(children, doc);
|
|
||||||
if (topLevelComponentDefs === null) {
|
|
||||||
topLevelComponentDefs = children;
|
|
||||||
} else {
|
|
||||||
topLevelComponentDefs = topLevelComponentDefs.concat(children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.___roots = null;
|
this.___out.emit('___componentsInitialized');
|
||||||
|
|
||||||
// Reset things stored in global since global is retained for
|
// Reset things stored in global since global is retained for
|
||||||
// future renders
|
// future renders
|
||||||
this.___out.global.___components = undefined;
|
this.___out.global.___components = undefined;
|
||||||
|
|
||||||
return topLevelComponentDefs;
|
return componentDefs;
|
||||||
},
|
},
|
||||||
___preserveDOMNode: function(elId, bodyOnly) {
|
|
||||||
var preserved = bodyOnly === true ? this.___preservedBodies : this.___preserved;
|
|
||||||
if (preserved === EMPTY_OBJECT) {
|
|
||||||
if (bodyOnly === true) {
|
|
||||||
preserved = this.___preservedBodies = {};
|
|
||||||
} else {
|
|
||||||
preserved = this.___preserved = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
preserved[elId] = true;
|
|
||||||
},
|
|
||||||
___nextRepeatedId: function(parentId, id) {
|
|
||||||
var nextIdLookup = this.___nextIdLookup || (this.___nextIdLookup = {});
|
|
||||||
|
|
||||||
var indexLookupKey = parentId + '-' + id;
|
|
||||||
var currentIndex = nextIdLookup[indexLookupKey];
|
|
||||||
if (currentIndex == null) {
|
|
||||||
currentIndex = nextIdLookup[indexLookupKey] = 0;
|
|
||||||
} else {
|
|
||||||
currentIndex = ++nextIdLookup[indexLookupKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
return indexLookupKey.slice(0, -2) + '[' + currentIndex + ']';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ComponentsContext(out, parentComponentsContext, shouldAddGlobalRoot) {
|
|
||||||
var root;
|
|
||||||
|
|
||||||
var globalComponentsContext;
|
|
||||||
|
|
||||||
if (parentComponentsContext === undefined) {
|
|
||||||
globalComponentsContext = out.global.___components;
|
|
||||||
if (globalComponentsContext === undefined) {
|
|
||||||
out.global.___components = globalComponentsContext = new GlobalComponentsContext(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
root = new ComponentDef(null, null, globalComponentsContext);
|
|
||||||
|
|
||||||
if (shouldAddGlobalRoot !== false) {
|
|
||||||
globalComponentsContext.___roots.push(root);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
globalComponentsContext = parentComponentsContext.___globalContext;
|
|
||||||
var parentComponentStack = parentComponentsContext.___componentStack;
|
|
||||||
root = parentComponentStack[parentComponentStack.length-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.___globalContext = globalComponentsContext;
|
|
||||||
this.___out = out;
|
|
||||||
this.___componentStack = [root];
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentsContext.prototype = {
|
|
||||||
___createNestedComponentsContext: function(nestedOut) {
|
|
||||||
return new ComponentsContext(nestedOut, this);
|
|
||||||
},
|
|
||||||
___beginComponent: beginComponent,
|
|
||||||
|
|
||||||
___nextComponentId: function() {
|
|
||||||
var componentStack = this.___componentStack;
|
|
||||||
var parentComponentDef = componentStack[componentStack.length - 1];
|
|
||||||
return parentComponentDef.___nextComponentId();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getComponentsContext(out) {
|
function getComponentsContext(out) {
|
||||||
return out.data.___components || (out.data.___components = new ComponentsContext(out));
|
return out.___components || (out.___components = new ComponentsContext(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = exports = ComponentsContext;
|
module.exports = exports = ComponentsContext;
|
||||||
|
|||||||
18
src/components/GlobalComponentsContext.js
Normal file
18
src/components/GlobalComponentsContext.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
var nextComponentIdProvider = require('./util').___nextComponentIdProvider;
|
||||||
|
var KeySequence = require('./KeySequence');
|
||||||
|
|
||||||
|
function GlobalComponentsContext(out) {
|
||||||
|
this.___preservedEls = {};
|
||||||
|
this.___preservedElBodies = {};
|
||||||
|
this.___renderedComponentsById = {};
|
||||||
|
this.___rerenderComponent = undefined;
|
||||||
|
this.___nextComponentId = nextComponentIdProvider(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalComponentsContext.prototype = {
|
||||||
|
___createKeySequence: function() {
|
||||||
|
return new KeySequence();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GlobalComponentsContext;
|
||||||
27
src/components/KeySequence.js
Normal file
27
src/components/KeySequence.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
function KeySequence() {
|
||||||
|
this.___lookup = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
KeySequence.prototype = {
|
||||||
|
___nextKey: function(key) {
|
||||||
|
// var len = key.length;
|
||||||
|
// var lastChar = key[len-1];
|
||||||
|
// if (lastChar === ']') {
|
||||||
|
// key = key.substring(0, len-2);
|
||||||
|
// }
|
||||||
|
var lookup = this.___lookup;
|
||||||
|
|
||||||
|
var currentIndex = lookup[key]++;
|
||||||
|
if (!currentIndex) {
|
||||||
|
lookup[key] = 1;
|
||||||
|
currentIndex = 0;
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
return key + '_' + currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = KeySequence;
|
||||||
@ -2,14 +2,15 @@ var eventDelegation = require('./event-delegation');
|
|||||||
var delegateEvent = eventDelegation.___delegateEvent;
|
var delegateEvent = eventDelegation.___delegateEvent;
|
||||||
var getEventFromEl = eventDelegation.___getEventFromEl;
|
var getEventFromEl = eventDelegation.___getEventFromEl;
|
||||||
|
|
||||||
var componentsUtil = require('./util');
|
// var componentsUtil = require('./util');
|
||||||
var destroyElRecursive = componentsUtil.___destroyElRecursive;
|
// var destroyNodeRecursive = componentsUtil.___destroyNodeRecursive;
|
||||||
var destroyComponentForEl = componentsUtil.___destroyComponentForEl;
|
// var destroyComponentForNode = componentsUtil.___destroyComponentForNode;
|
||||||
|
|
||||||
function handleNodeAttach(node, out) {
|
function handleNodeAttach(node, componentsContext) {
|
||||||
if (node.nodeType === 1) {
|
if (node.nodeType === 1) {
|
||||||
var target = getEventFromEl(node, 'onattach');
|
var target = getEventFromEl(node, 'onattach');
|
||||||
if (target) {
|
if (target) {
|
||||||
|
var out = componentsContext.___out;
|
||||||
var data = out.data;
|
var data = out.data;
|
||||||
|
|
||||||
var attachTargets = data.___attachTargets;
|
var attachTargets = data.___attachTargets;
|
||||||
@ -39,8 +40,6 @@ function handleNodeDetach(node) {
|
|||||||
delegateEvent(node, target, {
|
delegateEvent(node, target, {
|
||||||
preventDefault: function() {
|
preventDefault: function() {
|
||||||
allowDetach = false;
|
allowDetach = false;
|
||||||
destroyComponentForEl(node);
|
|
||||||
destroyElRecursive(node);
|
|
||||||
},
|
},
|
||||||
detach: function() {
|
detach: function() {
|
||||||
var parentNode = node.parentNode;
|
var parentNode = node.parentNode;
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
var ComponentDef = require('./ComponentDef');
|
var ComponentDef = require('./ComponentDef');
|
||||||
|
|
||||||
module.exports = function(component, isSplitComponent) {
|
module.exports = function beginComponent(componentsContext, component, isSplitComponen, parentComponentDeft) {
|
||||||
var componentStack = this.___componentStack;
|
|
||||||
var origLength = componentStack.length;
|
|
||||||
var parentComponentDef = componentStack[origLength - 1];
|
|
||||||
|
|
||||||
var componentId = component.id;
|
var componentId = component.id;
|
||||||
|
|
||||||
var componentDef = new ComponentDef(component, componentId, this.___globalContext, componentStack, origLength);
|
var globalContext = componentsContext.___globalContext;
|
||||||
parentComponentDef.___addChild(componentDef);
|
var componentDef = componentsContext.___componentDef = new ComponentDef(component, componentId, globalContext);
|
||||||
this.___globalContext.___componentsById[componentId] = componentDef;
|
globalContext.___renderedComponentsById[componentId] = true;
|
||||||
|
componentsContext.___components.push(componentDef);
|
||||||
componentStack.push(componentDef);
|
|
||||||
|
|
||||||
|
var out = componentsContext.___out;
|
||||||
|
out.bc(component);
|
||||||
return componentDef;
|
return componentDef;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,8 +3,13 @@
|
|||||||
const ComponentDef = require('./ComponentDef');
|
const ComponentDef = require('./ComponentDef');
|
||||||
const hasRenderBodyKey = Symbol.for("hasRenderBody");
|
const hasRenderBodyKey = Symbol.for("hasRenderBody");
|
||||||
|
|
||||||
|
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
// var FLAG_HAS_BODY_EL = 2;
|
||||||
|
// var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
|
||||||
function isInputSerializable(component) {
|
function isInputSerializable(component) {
|
||||||
var input = component.___input;
|
var input = component.___input;
|
||||||
|
|
||||||
if (!input) {
|
if (!input) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -16,29 +21,33 @@ function isInputSerializable(component) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function beginComponent(component, isSplitComponent) {
|
module.exports = function beginComponent(componentsContext, component, isSplitComponent, parentComponentDef) {
|
||||||
var componentStack = this.___componentStack;
|
var globalContext = componentsContext.___globalContext;
|
||||||
var origLength = componentStack.length;
|
|
||||||
var parentComponentDef = componentStack[origLength - 1];
|
|
||||||
|
|
||||||
var componentId = component.id;
|
var componentId = component.id;
|
||||||
|
|
||||||
var componentDef = new ComponentDef(component, componentId, this.___globalContext, componentStack, origLength);
|
var componentDef = componentsContext.___componentDef = new ComponentDef(component, componentId, globalContext);
|
||||||
|
|
||||||
// On the server
|
// On the server
|
||||||
if (parentComponentDef.___willRerenderInBrowser === true) {
|
if (parentComponentDef && (parentComponentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER)) {
|
||||||
componentDef.___willRerenderInBrowser = true;
|
componentDef.___flags |= FLAG_WILL_RERENDER_IN_BROWSER;
|
||||||
} else {
|
return componentDef;
|
||||||
parentComponentDef.___addChild(componentDef);
|
|
||||||
if (isSplitComponent === false &&
|
|
||||||
this.___out.global.noBrowserRerender !== true &&
|
|
||||||
isInputSerializable(component)) {
|
|
||||||
|
|
||||||
componentDef.___willRerenderInBrowser = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentStack.push(componentDef);
|
componentsContext.___components.push(componentDef);
|
||||||
|
|
||||||
|
let out = componentsContext.___out;
|
||||||
|
|
||||||
|
componentDef.___renderBoundary = true;
|
||||||
|
|
||||||
|
if (isSplitComponent === false &&
|
||||||
|
out.global.noBrowserRerender !== true &&
|
||||||
|
isInputSerializable(component)) {
|
||||||
|
componentDef.___flags |= FLAG_WILL_RERENDER_IN_BROWSER;
|
||||||
|
out.w('<!--M#' + componentId + '-->');
|
||||||
|
} else {
|
||||||
|
out.w('<!--M^' + componentId + '-->');
|
||||||
|
}
|
||||||
|
|
||||||
return componentDef;
|
return componentDef;
|
||||||
};
|
};
|
||||||
|
|||||||
5
src/components/endComponent-browser.js
Normal file
5
src/components/endComponent-browser.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function endComponent(out) {
|
||||||
|
out.ee(); // endElement() (also works for VComponent nodes pushed on to the stack)
|
||||||
|
};
|
||||||
7
src/components/endComponent.js
Normal file
7
src/components/endComponent.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function endComponent(out, componentDef) {
|
||||||
|
if (componentDef.___renderBoundary) {
|
||||||
|
out.w('<!--M$' + componentDef.id + '-->');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -3,12 +3,34 @@
|
|||||||
var warp10 = require('warp10');
|
var warp10 = require('warp10');
|
||||||
var escapeEndingScriptTagRegExp = /<\//g;
|
var escapeEndingScriptTagRegExp = /<\//g;
|
||||||
|
|
||||||
function flattenHelper(components, flattened, typesArray, typesLookup) {
|
|
||||||
for (var i = 0, len = components.length; i < len; i++) {
|
|
||||||
|
function getRenderedComponents(out, shouldIncludeAll) {
|
||||||
|
var componentsContext = out.___components;
|
||||||
|
if (componentsContext === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('componentsContext:', componentsContext);
|
||||||
|
|
||||||
|
var components = componentsContext.___components;
|
||||||
|
if (components.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('components:', components.map((componentDef) => {
|
||||||
|
// return { id: componentDef.id, type: componentDef.type};
|
||||||
|
// }));
|
||||||
|
|
||||||
|
var componentsFinal = [];
|
||||||
|
var typesLookup = {};
|
||||||
|
var typesArray = [];
|
||||||
|
|
||||||
|
for (var i = components.length - 1; i >= 0; i--) {
|
||||||
var componentDef = components[i];
|
var componentDef = components[i];
|
||||||
var id = componentDef.id;
|
var id = componentDef.id;
|
||||||
var component = componentDef.___component;
|
var component = componentDef.___component;
|
||||||
var rerenderInBrowser = componentDef.___willRerenderInBrowser;
|
var flags = componentDef.___flags;
|
||||||
var state = component.state;
|
var state = component.state;
|
||||||
var input = component.input;
|
var input = component.input;
|
||||||
var typeName = component.typeName;
|
var typeName = component.typeName;
|
||||||
@ -36,14 +58,6 @@ function flattenHelper(components, flattened, typesArray, typesLookup) {
|
|||||||
typesLookup[typeName] = typeIndex;
|
typesLookup[typeName] = typeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = componentDef.___children;
|
|
||||||
|
|
||||||
if (children !== null) {
|
|
||||||
// Depth-first search (children should be initialized before parent)
|
|
||||||
flattenHelper(children, flattened, typesArray, typesLookup);
|
|
||||||
componentDef.___children = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasProps = false;
|
var hasProps = false;
|
||||||
|
|
||||||
let componentKeys = Object.keys(component);
|
let componentKeys = Object.keys(component);
|
||||||
@ -81,71 +95,23 @@ function flattenHelper(components, flattened, typesArray, typesLookup) {
|
|||||||
b: bubblingDomEvents,
|
b: bubblingDomEvents,
|
||||||
d: componentDef.___domEvents,
|
d: componentDef.___domEvents,
|
||||||
e: customEvents,
|
e: customEvents,
|
||||||
|
f: flags ? flags : undefined,
|
||||||
p: customEvents && scope, // Only serialize scope if we need to attach custom events
|
p: customEvents && scope, // Only serialize scope if we need to attach custom events
|
||||||
r: componentDef.___roots,
|
r: componentDef.___boundary,
|
||||||
s: state,
|
s: state,
|
||||||
u: undefinedPropNames,
|
u: undefinedPropNames,
|
||||||
w: hasProps ? component : undefined,
|
w: hasProps ? component : undefined
|
||||||
_: rerenderInBrowser ? 1 : undefined
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flattened.push([
|
componentsFinal.push([
|
||||||
id, // 0 = id
|
id, // 0 = id
|
||||||
typeIndex, // 1 = type
|
typeIndex, // 1 = type
|
||||||
input, // 2 = input
|
input, // 2 = input
|
||||||
extra // 3
|
extra // 3
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getRenderedComponents(out, shouldIncludeAll) {
|
return {w: componentsFinal, t: typesArray};
|
||||||
var componentDefs;
|
|
||||||
var globalComponentsContext;
|
|
||||||
var outGlobal = out.global;
|
|
||||||
|
|
||||||
if (shouldIncludeAll === true) {
|
|
||||||
globalComponentsContext = outGlobal.___components;
|
|
||||||
|
|
||||||
if (globalComponentsContext === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let componentsContext = out.data.___components;
|
|
||||||
if (componentsContext === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
let rootComponentDef = componentsContext.___componentStack[0];
|
|
||||||
componentDefs = rootComponentDef.___children;
|
|
||||||
|
|
||||||
if (componentDefs === null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
rootComponentDef.___children = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var flattened = [];
|
|
||||||
var typesLookup = {};
|
|
||||||
var typesArray = [];
|
|
||||||
|
|
||||||
if (shouldIncludeAll === true) {
|
|
||||||
let roots = globalComponentsContext.___roots;
|
|
||||||
for (let i=0, len=roots.length; i<len; i++) {
|
|
||||||
let root = roots[i];
|
|
||||||
let children = root.___children;
|
|
||||||
if (children !== null) {
|
|
||||||
flattenHelper(children, flattened, typesArray, typesLookup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flattenHelper(componentDefs, flattened, typesArray, typesLookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flattened.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {w: flattened, t: typesArray};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeInitComponentsCode(out, shouldIncludeAll) {
|
function writeInitComponentsCode(out, shouldIncludeAll) {
|
||||||
|
|||||||
@ -5,10 +5,56 @@ var win = window;
|
|||||||
var defaultDocument = document;
|
var defaultDocument = document;
|
||||||
var componentsUtil = require('./util');
|
var componentsUtil = require('./util');
|
||||||
var componentLookup = componentsUtil.___componentLookup;
|
var componentLookup = componentsUtil.___componentLookup;
|
||||||
var getElementById = componentsUtil.___getElementById;
|
|
||||||
var ComponentDef = require('./ComponentDef');
|
var ComponentDef = require('./ComponentDef');
|
||||||
var registry = require('./registry');
|
var registry = require('./registry');
|
||||||
var serverRenderedGlobals = {};
|
var serverRenderedGlobals = {};
|
||||||
|
var serverComponentStartNodes = {};
|
||||||
|
var serverComponentEndNodes = {};
|
||||||
|
var keyedElementsByComponentId = {};
|
||||||
|
|
||||||
|
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
var FLAG_HAS_BODY_EL = 2;
|
||||||
|
var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
|
||||||
|
function indexServerComponentBoundaries(node) {
|
||||||
|
var componentId;
|
||||||
|
|
||||||
|
node = node.firstChild;
|
||||||
|
while(node) {
|
||||||
|
if (node.nodeType === 8) { // Comment node
|
||||||
|
var commentValue = node.nodeValue;
|
||||||
|
if (commentValue[0] === 'M') {
|
||||||
|
componentId = commentValue.substring(2);
|
||||||
|
switch(commentValue[1]) {
|
||||||
|
case '$':
|
||||||
|
serverComponentEndNodes[componentId] = node;
|
||||||
|
break;
|
||||||
|
case '#': // This component will rerender in the browser, no need to index children
|
||||||
|
serverComponentStartNodes[componentId] = node;
|
||||||
|
var endValue = 'M$' + componentId;
|
||||||
|
while((node = node.nextSibling) && node.nodeValue !== endValue) {}
|
||||||
|
continue;
|
||||||
|
case '^':
|
||||||
|
serverComponentStartNodes[componentId] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.nodeType === 1) { // HTML element node
|
||||||
|
var markoKey = node.getAttribute('data-marko-key');
|
||||||
|
if (markoKey) {
|
||||||
|
var separatorIndex = markoKey.indexOf(' ');
|
||||||
|
componentId = markoKey.substring(separatorIndex+1);
|
||||||
|
markoKey = markoKey.substring(0, separatorIndex);
|
||||||
|
var keyedElements = keyedElementsByComponentId[componentId] || (keyedElementsByComponentId[componentId] = {});
|
||||||
|
keyedElements[markoKey] = node;
|
||||||
|
}
|
||||||
|
indexServerComponentBoundaries(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function invokeComponentEventHandler(component, targetMethodName, args) {
|
function invokeComponentEventHandler(component, targetMethodName, args) {
|
||||||
var method = component[targetMethodName];
|
var method = component[targetMethodName];
|
||||||
@ -51,44 +97,9 @@ function initComponent(componentDef, doc) {
|
|||||||
var isExisting = componentDef.___isExisting;
|
var isExisting = componentDef.___isExisting;
|
||||||
var id = component.id;
|
var id = component.id;
|
||||||
|
|
||||||
var rootIds = componentDef.___roots;
|
componentLookup[id] = component;
|
||||||
|
|
||||||
if (rootIds) {
|
if (componentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER) {
|
||||||
var rootComponents;
|
|
||||||
|
|
||||||
var els = [];
|
|
||||||
|
|
||||||
rootIds.forEach(function(rootId) {
|
|
||||||
var nestedId = id + '-' + rootId;
|
|
||||||
var rootComponent = componentLookup[nestedId];
|
|
||||||
if (rootComponent) {
|
|
||||||
rootComponent.___rootFor = component;
|
|
||||||
if (rootComponents) {
|
|
||||||
rootComponents.push(rootComponent);
|
|
||||||
} else {
|
|
||||||
rootComponents = component.___rootComponents = [rootComponent];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var rootEl = getElementById(doc, nestedId);
|
|
||||||
if (rootEl) {
|
|
||||||
rootEl._w = component;
|
|
||||||
els.push(rootEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
component.el = els[0];
|
|
||||||
component.els = els;
|
|
||||||
componentLookup[id] = component;
|
|
||||||
} else if (!isExisting) {
|
|
||||||
var el = getElementById(doc, id);
|
|
||||||
el._w = component;
|
|
||||||
component.el = el;
|
|
||||||
component.els = [el];
|
|
||||||
componentLookup[id] = component;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (componentDef.___willRerenderInBrowser) {
|
|
||||||
component.___rerender(true);
|
component.___rerender(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -106,7 +117,7 @@ function initComponent(componentDef, doc) {
|
|||||||
|
|
||||||
var eventType = domEventArgs[0];
|
var eventType = domEventArgs[0];
|
||||||
var targetMethodName = domEventArgs[1];
|
var targetMethodName = domEventArgs[1];
|
||||||
var eventEl = getElementById(doc, domEventArgs[2]);
|
var eventEl = component.___keyedElements[domEventArgs[2]];
|
||||||
var extraArgs = domEventArgs[3];
|
var extraArgs = domEventArgs[3];
|
||||||
|
|
||||||
addDOMEventListeners(component, eventEl, eventType, targetMethodName, extraArgs, eventListenerHandles);
|
addDOMEventListeners(component, eventEl, eventType, targetMethodName, extraArgs, eventListenerHandles);
|
||||||
@ -139,13 +150,8 @@ function initClientRendered(componentDefs, doc) {
|
|||||||
eventDelegation.___init(doc);
|
eventDelegation.___init(doc);
|
||||||
|
|
||||||
doc = doc || defaultDocument;
|
doc = doc || defaultDocument;
|
||||||
for (var i=0,len=componentDefs.length; i<len; i++) {
|
for (var i=componentDefs.length-1; i>=0; i--) {
|
||||||
var componentDef = componentDefs[i];
|
var componentDef = componentDefs[i];
|
||||||
|
|
||||||
if (componentDef.___children) {
|
|
||||||
initClientRendered(componentDef.___children, doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
initComponent(
|
initComponent(
|
||||||
componentDef,
|
componentDef,
|
||||||
doc);
|
doc);
|
||||||
@ -172,9 +178,12 @@ function initServerRendered(renderedComponents, doc) {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc = doc || defaultDocument;
|
||||||
|
|
||||||
// Ensure that event handlers to handle delegating events are
|
// Ensure that event handlers to handle delegating events are
|
||||||
// always attached before initializing any components
|
// always attached before initializing any components
|
||||||
eventDelegation.___init(doc || defaultDocument);
|
eventDelegation.___init(doc);
|
||||||
|
|
||||||
renderedComponents = warp10Finalize(renderedComponents);
|
renderedComponents = warp10Finalize(renderedComponents);
|
||||||
|
|
||||||
@ -188,6 +197,65 @@ function initServerRendered(renderedComponents, doc) {
|
|||||||
|
|
||||||
componentDefs.forEach(function(componentDef) {
|
componentDefs.forEach(function(componentDef) {
|
||||||
componentDef = ComponentDef.___deserialize(componentDef, typesArray, serverRenderedGlobals, registry);
|
componentDef = ComponentDef.___deserialize(componentDef, typesArray, serverRenderedGlobals, registry);
|
||||||
|
var componentId = componentDef.id;
|
||||||
|
var component = componentDef.___component;
|
||||||
|
|
||||||
|
var startNode;
|
||||||
|
var endNode;
|
||||||
|
var flags = componentDef.___flags;
|
||||||
|
if ((flags & 6) === 6) {
|
||||||
|
startNode = document.head;
|
||||||
|
endNode = document.body;
|
||||||
|
} else if (flags & FLAG_HAS_BODY_EL) {
|
||||||
|
startNode = endNode = document.body;
|
||||||
|
} else if (flags & FLAG_HAS_HEAD_EL) {
|
||||||
|
startNode = endNode = document.head;
|
||||||
|
} else {
|
||||||
|
var startNodeComment = serverComponentStartNodes[componentId];
|
||||||
|
if (!startNodeComment) {
|
||||||
|
indexServerComponentBoundaries(doc);
|
||||||
|
startNodeComment = serverComponentStartNodes[componentId];
|
||||||
|
}
|
||||||
|
var endNodeComment = serverComponentEndNodes[componentId];
|
||||||
|
|
||||||
|
startNode = startNodeComment.nextSibling;
|
||||||
|
|
||||||
|
if (startNode === endNodeComment) {
|
||||||
|
// Component has no output nodes so just mount to the start comment node
|
||||||
|
// and we will remove the end comment node
|
||||||
|
startNode = endNode = startNodeComment;
|
||||||
|
} else {
|
||||||
|
startNodeComment.parentNode.removeChild(startNodeComment);
|
||||||
|
|
||||||
|
if (startNode.parentNode === document) {
|
||||||
|
endNode = startNode = document.documentElement;
|
||||||
|
} else {
|
||||||
|
// Remove the start and end comment nodes and use the inner nodes
|
||||||
|
// as the boundary
|
||||||
|
endNode = endNodeComment.previousSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endNodeComment) {
|
||||||
|
endNodeComment.parentNode.removeChild(endNodeComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component.___keyedElements = keyedElementsByComponentId[componentId] || {};
|
||||||
|
component.___startNode = startNode;
|
||||||
|
component.___endNode = endNode;
|
||||||
|
|
||||||
|
delete keyedElementsByComponentId[componentId];
|
||||||
|
|
||||||
|
// Mark the start node so that we know we need to skip past this
|
||||||
|
// node when matching up children
|
||||||
|
startNode.___startNode = true;
|
||||||
|
|
||||||
|
// Mark the end node so that when we attempt to find boundaries
|
||||||
|
// for nested UI components we don't accidentally go outside the boundary
|
||||||
|
// of the parent component
|
||||||
|
endNode.___endNode = true;
|
||||||
|
|
||||||
initComponent(componentDef, doc || defaultDocument);
|
initComponent(componentDef, doc || defaultDocument);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/components/jquery.js
vendored
6
src/components/jquery.js
vendored
@ -23,11 +23,11 @@ exports.patchComponent = function(jQuery) {
|
|||||||
var match = idRegExp.exec(arg);
|
var match = idRegExp.exec(arg);
|
||||||
//Reset the search to 0 so the next call to exec will start from the beginning for the new string
|
//Reset the search to 0 so the next call to exec will start from the beginning for the new string
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
var componentElId = match[1];
|
var key = match[1];
|
||||||
if (match[2] == null) {
|
if (match[2] == null) {
|
||||||
return jQuery(self.getEl(componentElId));
|
return jQuery(self.getEl(key));
|
||||||
} else {
|
} else {
|
||||||
return jQuery('#' + self.getElId(componentElId) + match[2]);
|
return jQuery(match[2].trim(), self.getEl(key));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var rootEl = self.getEl();
|
var rootEl = self.getEl();
|
||||||
|
|||||||
@ -4,8 +4,9 @@ var componentLookup = componentsUtil.___componentLookup;
|
|||||||
var registry = require('../registry');
|
var registry = require('../registry');
|
||||||
var modernRenderer = require('../renderer');
|
var modernRenderer = require('../renderer');
|
||||||
var resolveComponentKey = modernRenderer.___resolveComponentKey;
|
var resolveComponentKey = modernRenderer.___resolveComponentKey;
|
||||||
var preserveComponentEls = modernRenderer.___preserveComponentEls;
|
|
||||||
var handleBeginAsync = modernRenderer.___handleBeginAsync;
|
var handleBeginAsync = modernRenderer.___handleBeginAsync;
|
||||||
|
var beginComponent = require('../beginComponent');
|
||||||
|
var endComponent = require('../endComponent');
|
||||||
|
|
||||||
var WIDGETS_BEGIN_ASYNC_ADDED_KEY = '$wa';
|
var WIDGETS_BEGIN_ASYNC_ADDED_KEY = '$wa';
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
var typeName = componentProps.type;
|
var typeName = componentProps.type;
|
||||||
var roots = componentProps.roots;
|
var roots = componentProps.roots;
|
||||||
var assignedId = componentProps.id;
|
var assignedId = componentProps.id;
|
||||||
|
var isSplit = componentProps.split === true;
|
||||||
|
|
||||||
return function renderer(input, out, renderingLogic) {
|
return function renderer(input, out, renderingLogic) {
|
||||||
var outGlobal = out.global;
|
var outGlobal = out.global;
|
||||||
@ -44,39 +46,46 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
var globalComponentsContext = componentsContext.___globalContext;
|
var globalComponentsContext = componentsContext.___globalContext;
|
||||||
|
|
||||||
var component = globalComponentsContext.___rerenderComponent;
|
var component = globalComponentsContext.___rerenderComponent;
|
||||||
var fakeComponent;
|
|
||||||
var isRerender = component !== undefined;
|
var isRerender = component !== undefined;
|
||||||
var id = assignedId;
|
var id = assignedId;
|
||||||
var isExisting;
|
var isExisting;
|
||||||
var customEvents;
|
var customEvents;
|
||||||
var scope;
|
var scope;
|
||||||
|
var parentComponentDef;
|
||||||
|
|
||||||
if (component) {
|
if (component) {
|
||||||
id = component.id;
|
id = component.id;
|
||||||
isExisting = true;
|
isExisting = true;
|
||||||
globalComponentsContext.___rerenderComponent = null;
|
globalComponentsContext.___rerenderComponent = null;
|
||||||
} else {
|
} else {
|
||||||
|
parentComponentDef = componentsContext.___componentDef;
|
||||||
var componentArgs = out.___componentArgs;
|
var componentArgs = out.___componentArgs;
|
||||||
|
|
||||||
if (componentArgs) {
|
if (componentArgs) {
|
||||||
|
scope = parentComponentDef.id;
|
||||||
out.___componentArgs = null;
|
out.___componentArgs = null;
|
||||||
scope = componentArgs[0];
|
|
||||||
|
|
||||||
if (scope) {
|
var key;
|
||||||
scope = scope.id;
|
|
||||||
|
if (typeof componentArgs === 'string') {
|
||||||
|
key = componentArgs;
|
||||||
|
} else {
|
||||||
|
key = componentArgs[0];
|
||||||
|
customEvents = componentArgs[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var ref = componentArgs[1];
|
if (key != null) {
|
||||||
if (ref != null) {
|
key = key.toString();
|
||||||
ref = ref.toString();
|
|
||||||
}
|
}
|
||||||
id = id || resolveComponentKey(globalComponentsContext, ref, scope);
|
id = id || resolveComponentKey(globalComponentsContext, key, parentComponentDef);
|
||||||
customEvents = componentArgs[2];
|
} else if (parentComponentDef) {
|
||||||
|
id = parentComponentDef.___nextComponentId();
|
||||||
|
} else {
|
||||||
|
id = globalComponentsContext.___nextComponentId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id = id || componentsContext.___nextComponentId();
|
|
||||||
|
|
||||||
if (registry.___isServer && typeName) {
|
if (registry.___isServer && typeName) {
|
||||||
component = { id:id, typeName:typeName };
|
component = { id:id, typeName:typeName };
|
||||||
} else {
|
} else {
|
||||||
@ -101,6 +110,10 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (component) {
|
||||||
|
component.___updateQueued = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
if (getWidgetConfig) {
|
if (getWidgetConfig) {
|
||||||
// If getWidgetConfig() was implemented then use that to
|
// If getWidgetConfig() was implemented then use that to
|
||||||
@ -151,14 +164,23 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
component.___setCustomEvents(customEvents, scope);
|
component.___setCustomEvents(customEvents, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
preserveComponentEls(component, out, globalComponentsContext);
|
// We put a placeholder element in the output stream to ensure that the existing
|
||||||
|
// DOM node is matched up correctly when using morphdom. We flag the VElement
|
||||||
|
// node to track that it is a preserve marker
|
||||||
|
out.___preserveComponent(component);
|
||||||
|
globalComponentsContext.___renderedComponentsById[id] = true;
|
||||||
|
component.___reset(); // The component is no longer dirty so reset internal flags
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isFakeComponent = false;
|
||||||
|
|
||||||
if (!component) {
|
if (!component) {
|
||||||
fakeComponent = {
|
isFakeComponent = true;
|
||||||
id: id
|
component = {
|
||||||
|
id: id,
|
||||||
|
___keyedElements: {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
componentState = component.___rawState || componentState;
|
componentState = component.___rawState || componentState;
|
||||||
@ -168,17 +190,22 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
getTemplateData(componentState, input, out) :
|
getTemplateData(componentState, input, out) :
|
||||||
componentState || input || {};
|
componentState || input || {};
|
||||||
|
|
||||||
var componentDef = componentsContext.___beginComponent(component || fakeComponent);
|
var componentDef = beginComponent(componentsContext, component, isSplit, parentComponentDef);
|
||||||
|
|
||||||
|
// This is a hack, but we have to swap out the component instance stored with this node
|
||||||
|
var vComponentNode = out.___parent;
|
||||||
|
|
||||||
componentDef.___roots = roots;
|
componentDef.___roots = roots;
|
||||||
componentDef.___component = fakeComponent ? null : component;
|
componentDef.___component = isFakeComponent ? null : component;
|
||||||
componentDef.___isExisting = isExisting;
|
componentDef.___isExisting = isExisting;
|
||||||
componentDef.b = componentBody;
|
componentDef.b = componentBody;
|
||||||
componentDef.c = function(widgetConfig) {
|
componentDef.c = function(widgetConfig) {
|
||||||
component.$c = widgetConfig;
|
component.$c = widgetConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDef.t = function(typeName) {
|
componentDef.t = function(typeName) {
|
||||||
if (typeName) {
|
if (typeName) {
|
||||||
this.___component = component = registry.___createComponent(typeName, fakeComponent.id);
|
vComponentNode.___component = this.___component = component = registry.___createComponent(typeName, component.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,7 +215,7 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
|
|
||||||
// Render the template associated with the component using the final template
|
// Render the template associated with the component using the final template
|
||||||
// data that we constructed
|
// data that we constructed
|
||||||
templateRenderFunc(templateInput, out, componentDef, componentDef);
|
templateRenderFunc(templateInput, out, componentDef, componentDef, component);
|
||||||
|
|
||||||
if (customEvents && componentDef.___component) {
|
if (customEvents && componentDef.___component) {
|
||||||
if (registry.___isServer) {
|
if (registry.___isServer) {
|
||||||
@ -199,7 +226,8 @@ function createRendererFunc(templateRenderFunc, componentProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDef.___end();
|
endComponent(out, componentDef);
|
||||||
|
componentsContext.___componentDef = parentComponentDef;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"browser": {
|
"browser": {
|
||||||
"./beginComponent.js": "./beginComponent-browser.js",
|
"./beginComponent.js": "./beginComponent-browser.js",
|
||||||
|
"./endComponent.js": "./endComponent-browser.js",
|
||||||
"./helpers.js": "./helpers-browser.js",
|
"./helpers.js": "./helpers-browser.js",
|
||||||
"./index.js": "./index-browser.js",
|
"./index.js": "./index-browser.js",
|
||||||
"./init-components.js": "./init-components-browser.js",
|
"./init-components.js": "./init-components-browser.js",
|
||||||
|
|||||||
@ -4,50 +4,26 @@ var emitLifecycleEvent = componentsUtil.___emitLifecycleEvent;
|
|||||||
|
|
||||||
var ComponentsContext = require('./ComponentsContext');
|
var ComponentsContext = require('./ComponentsContext');
|
||||||
var getComponentsContext = ComponentsContext.___getComponentsContext;
|
var getComponentsContext = ComponentsContext.___getComponentsContext;
|
||||||
var repeatedRegExp = /\[\]$/;
|
|
||||||
var registry = require('./registry');
|
var registry = require('./registry');
|
||||||
var copyProps = require('raptor-util/copyProps');
|
var copyProps = require('raptor-util/copyProps');
|
||||||
var isServer = componentsUtil.___isServer === true;
|
var isServer = componentsUtil.___isServer === true;
|
||||||
|
var beginComponent = require('./beginComponent');
|
||||||
|
var endComponent = require('./endComponent');
|
||||||
|
|
||||||
var COMPONENT_BEGIN_ASYNC_ADDED_KEY = '$wa';
|
var COMPONENT_BEGIN_ASYNC_ADDED_KEY = '$wa';
|
||||||
|
|
||||||
function resolveComponentKey(globalComponentsContext, key, scope) {
|
function resolveComponentKey(globalComponentsContext, key, parentComponentDef) {
|
||||||
if (key[0] == '#') {
|
if (key[0] === '#') {
|
||||||
return key.substring(1);
|
return key.substring(1);
|
||||||
} else {
|
} else {
|
||||||
var resolvedId;
|
return parentComponentDef.id + '-' + parentComponentDef.___nextKey(key);
|
||||||
|
|
||||||
if (repeatedRegExp.test(key)) {
|
|
||||||
resolvedId = globalComponentsContext.___nextRepeatedId(scope, key);
|
|
||||||
} else {
|
|
||||||
resolvedId = scope + '-' + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolvedId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function preserveComponentEls(existingComponent, out, globalComponentsContext) {
|
|
||||||
var rootEls = existingComponent.___getRootEls({});
|
|
||||||
|
|
||||||
for (var elId in rootEls) {
|
|
||||||
var el = rootEls[elId];
|
|
||||||
|
|
||||||
// We put a placeholder element in the output stream to ensure that the existing
|
|
||||||
// DOM node is matched up correctly when using morphdom.
|
|
||||||
out.element(el.tagName, { id: elId });
|
|
||||||
|
|
||||||
globalComponentsContext.___preserveDOMNode(elId); // Mark the element as being preserved (for morphdom)
|
|
||||||
}
|
|
||||||
|
|
||||||
existingComponent.___reset(); // The component is no longer dirty so reset internal flags
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBeginAsync(event) {
|
function handleBeginAsync(event) {
|
||||||
var parentOut = event.parentOut;
|
var parentOut = event.parentOut;
|
||||||
var asyncOut = event.out;
|
var asyncOut = event.out;
|
||||||
var componentsContext = parentOut.data.___components;
|
var componentsContext = parentOut.___components;
|
||||||
|
|
||||||
if (componentsContext !== undefined) {
|
if (componentsContext !== undefined) {
|
||||||
// All of the components in this async block should be
|
// All of the components in this async block should be
|
||||||
@ -58,8 +34,7 @@ function handleBeginAsync(event) {
|
|||||||
// stack (to begin with). This will result in top-level components
|
// stack (to begin with). This will result in top-level components
|
||||||
// of the async block being added as children of the component in the
|
// of the async block being added as children of the component in the
|
||||||
// parent block.
|
// parent block.
|
||||||
var nestedComponentsContext = componentsContext.___createNestedComponentsContext(asyncOut);
|
asyncOut.___components = new ComponentsContext(asyncOut, componentsContext);
|
||||||
asyncOut.data.___components = nestedComponentsContext;
|
|
||||||
}
|
}
|
||||||
// Carry along the component arguments
|
// Carry along the component arguments
|
||||||
asyncOut.___componentArgs = parentOut.___componentArgs;
|
asyncOut.___componentArgs = parentOut.___componentArgs;
|
||||||
@ -69,8 +44,6 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
|
|||||||
renderingLogic = renderingLogic || {};
|
renderingLogic = renderingLogic || {};
|
||||||
var onInput = renderingLogic.onInput;
|
var onInput = renderingLogic.onInput;
|
||||||
var typeName = componentProps.type;
|
var typeName = componentProps.type;
|
||||||
var roots = componentProps.roots;
|
|
||||||
var assignedId = componentProps.id;
|
|
||||||
var isSplit = componentProps.split === true;
|
var isSplit = componentProps.split === true;
|
||||||
var shouldApplySplitMixins = isSplit;
|
var shouldApplySplitMixins = isSplit;
|
||||||
|
|
||||||
@ -89,38 +62,46 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
|
|||||||
|
|
||||||
var component = globalComponentsContext.___rerenderComponent;
|
var component = globalComponentsContext.___rerenderComponent;
|
||||||
var isRerender = component !== undefined;
|
var isRerender = component !== undefined;
|
||||||
var id = assignedId;
|
var id;
|
||||||
var isExisting;
|
var isExisting;
|
||||||
var customEvents;
|
var customEvents;
|
||||||
var scope;
|
var scope;
|
||||||
|
var parentComponentDef;
|
||||||
|
|
||||||
if (component) {
|
if (component) {
|
||||||
id = component.id;
|
id = component.id;
|
||||||
isExisting = true;
|
isExisting = true;
|
||||||
globalComponentsContext.___rerenderComponent = null;
|
globalComponentsContext.___rerenderComponent = null;
|
||||||
} else {
|
} else {
|
||||||
|
parentComponentDef = componentsContext.___componentDef;
|
||||||
var componentArgs = out.___componentArgs;
|
var componentArgs = out.___componentArgs;
|
||||||
|
|
||||||
if (componentArgs) {
|
if (componentArgs) {
|
||||||
|
// console.log('componentArgs:', componentArgs);
|
||||||
|
scope = parentComponentDef.id;
|
||||||
out.___componentArgs = null;
|
out.___componentArgs = null;
|
||||||
|
|
||||||
scope = componentArgs[0];
|
var key;
|
||||||
|
|
||||||
if (scope) {
|
if (typeof componentArgs === 'string') {
|
||||||
scope = scope.id;
|
key = componentArgs;
|
||||||
|
} else {
|
||||||
|
key = componentArgs[0];
|
||||||
|
customEvents = componentArgs[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = componentArgs[1];
|
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
key = key.toString();
|
key = key.toString();
|
||||||
|
id = resolveComponentKey(globalComponentsContext, key, parentComponentDef);
|
||||||
|
} else {
|
||||||
|
id = parentComponentDef.___nextComponentId();
|
||||||
}
|
}
|
||||||
id = id || resolveComponentKey(globalComponentsContext, key, scope);
|
} else if (parentComponentDef) {
|
||||||
customEvents = componentArgs[2];
|
id = parentComponentDef.___nextComponentId();
|
||||||
|
} else {
|
||||||
|
id = globalComponentsContext.___nextComponentId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id = id || componentsContext.___nextComponentId();
|
|
||||||
|
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
component = registry.___createComponent(
|
component = registry.___createComponent(
|
||||||
renderingLogic,
|
renderingLogic,
|
||||||
@ -134,12 +115,10 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
|
|||||||
component.___updatedInput = undefined; // We don't want ___updatedInput to be serialized to the browser
|
component.___updatedInput = undefined; // We don't want ___updatedInput to be serialized to the browser
|
||||||
} else {
|
} else {
|
||||||
if (!component) {
|
if (!component) {
|
||||||
if (isRerender) {
|
if (isRerender && (component = componentLookup[id]) && component.___type !== typeName) {
|
||||||
// Look in in the DOM to see if a component with the same ID and type already exists.
|
// Destroy the existing component since
|
||||||
component = componentLookup[id];
|
component.destroy();
|
||||||
if (component && component.___type !== typeName) {
|
component = undefined;
|
||||||
component = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component) {
|
if (component) {
|
||||||
@ -178,7 +157,12 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
|
|||||||
|
|
||||||
if (isExisting === true) {
|
if (isExisting === true) {
|
||||||
if (component.___isDirty === false || component.shouldUpdate(input, component.___state) === false) {
|
if (component.___isDirty === false || component.shouldUpdate(input, component.___state) === false) {
|
||||||
preserveComponentEls(component, out, globalComponentsContext);
|
// We put a placeholder element in the output stream to ensure that the existing
|
||||||
|
// DOM node is matched up correctly when using morphdom. We flag the VElement
|
||||||
|
// node to track that it is a preserve marker
|
||||||
|
out.___preserveComponent(component);
|
||||||
|
globalComponentsContext.___renderedComponentsById[id] = true;
|
||||||
|
component.___reset(); // The component is no longer dirty so reset internal flags
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,15 +173,17 @@ function createRendererFunc(templateRenderFunc, componentProps, renderingLogic)
|
|||||||
emitLifecycleEvent(component, 'render', out);
|
emitLifecycleEvent(component, 'render', out);
|
||||||
}
|
}
|
||||||
|
|
||||||
var componentDef = componentsContext.___beginComponent(component, isSplit);
|
var componentDef =
|
||||||
componentDef.___roots = roots;
|
beginComponent(componentsContext, component, isSplit, parentComponentDef);
|
||||||
|
|
||||||
componentDef.___isExisting = isExisting;
|
componentDef.___isExisting = isExisting;
|
||||||
|
|
||||||
// Render the template associated with the component using the final template
|
// Render the template associated with the component using the final template
|
||||||
// data that we constructed
|
// data that we constructed
|
||||||
templateRenderFunc(input, out, componentDef, component, component.___rawState);
|
templateRenderFunc(input, out, componentDef, component, component.___rawState);
|
||||||
|
|
||||||
componentDef.___end();
|
endComponent(out, componentDef);
|
||||||
|
componentsContext.___componentDef = parentComponentDef;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,5 +191,4 @@ module.exports = createRendererFunc;
|
|||||||
|
|
||||||
// exports used by the legacy renderer
|
// exports used by the legacy renderer
|
||||||
createRendererFunc.___resolveComponentKey = resolveComponentKey;
|
createRendererFunc.___resolveComponentKey = resolveComponentKey;
|
||||||
createRendererFunc.___preserveComponentEls = preserveComponentEls;
|
|
||||||
createRendererFunc.___handleBeginAsync = handleBeginAsync;
|
createRendererFunc.___handleBeginAsync = handleBeginAsync;
|
||||||
|
|||||||
@ -2,23 +2,15 @@
|
|||||||
class ComponentArgs {
|
class ComponentArgs {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.id = null;
|
this.key = null;
|
||||||
this.customEvents = null;
|
this.customEvents = null;
|
||||||
this.empty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setId(id) {
|
setKey(key) {
|
||||||
this.empty = false;
|
this.key = key;
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addCustomEvent(eventType, targetMethod, extraArgs) {
|
addCustomEvent(eventType, targetMethod, extraArgs) {
|
||||||
this.empty = false;
|
|
||||||
|
|
||||||
if (!this.customEvents) {
|
if (!this.customEvents) {
|
||||||
this.customEvents = [];
|
this.customEvents = [];
|
||||||
}
|
}
|
||||||
@ -27,7 +19,7 @@ class ComponentArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compile(transformHelper) {
|
compile(transformHelper) {
|
||||||
if (this.empty) {
|
if (!this.key && !this.customEvents) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,29 +27,14 @@ class ComponentArgs {
|
|||||||
|
|
||||||
var builder = transformHelper.builder;
|
var builder = transformHelper.builder;
|
||||||
|
|
||||||
var id = this.id;
|
var args;
|
||||||
var customEvents = this.customEvents;
|
|
||||||
|
|
||||||
// Make sure the nested component has access to the ID of the containing
|
if (this.key && this.customEvents) {
|
||||||
// component if it is needed
|
args = builder.literal([ this.key, builder.literal(this.customEvents) ]);
|
||||||
var shouldProvideScope = id || customEvents;
|
} else if (this.customEvents) {
|
||||||
|
args = builder.literal([ builder.literalNull(), builder.literal(this.customEvents) ]);
|
||||||
var args = [];
|
|
||||||
|
|
||||||
if (shouldProvideScope) {
|
|
||||||
args.push(builder.identifier('__component'));
|
|
||||||
} else {
|
} else {
|
||||||
args.push(builder.literalNull());
|
args = this.key;
|
||||||
}
|
|
||||||
|
|
||||||
if (id != null) {
|
|
||||||
args.push(id);
|
|
||||||
} else {
|
|
||||||
args.push(builder.literalNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customEvents) {
|
|
||||||
args.push(builder.literal(customEvents));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.type === 'CustomTag') {
|
if (el.type === 'CustomTag') {
|
||||||
@ -66,7 +43,7 @@ class ComponentArgs {
|
|||||||
el.generateRenderTagCode = function(codegen, tagVar, tagArgs) {
|
el.generateRenderTagCode = function(codegen, tagVar, tagArgs) {
|
||||||
tagArgs = [tagVar].concat(tagArgs);
|
tagArgs = [tagVar].concat(tagArgs);
|
||||||
|
|
||||||
tagArgs.push(builder.literal(args));
|
tagArgs.push(args);
|
||||||
|
|
||||||
return codegen.builder.functionCall(
|
return codegen.builder.functionCall(
|
||||||
renderComponentHelper,
|
renderComponentHelper,
|
||||||
@ -75,7 +52,7 @@ class ComponentArgs {
|
|||||||
} else {
|
} else {
|
||||||
el.onBeforeGenerateCode((event) => {
|
el.onBeforeGenerateCode((event) => {
|
||||||
let funcTarget = builder.memberExpression(builder.identifierOut(), builder.identifier('c'));
|
let funcTarget = builder.memberExpression(builder.identifierOut(), builder.identifier('c'));
|
||||||
let funcArgs = [builder.literal(args)];
|
let funcArgs = [args];
|
||||||
|
|
||||||
event.insertCode(builder.functionCall(funcTarget, funcArgs));
|
event.insertCode(builder.functionCall(funcTarget, funcArgs));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,7 +12,11 @@ module.exports = function assignComponentId(isRepeated) {
|
|||||||
var context = this.context;
|
var context = this.context;
|
||||||
var builder = this.builder;
|
var builder = this.builder;
|
||||||
|
|
||||||
let componentRef;
|
if (el.noOutput || (el.tagDef && el.tagDef.noOutput)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let assignedKey;
|
||||||
var nestedIdExpression;
|
var nestedIdExpression;
|
||||||
var idExpression;
|
var idExpression;
|
||||||
|
|
||||||
@ -27,6 +31,9 @@ module.exports = function assignComponentId(isRepeated) {
|
|||||||
this.getMarkoComponentsRequirePath('marko/components/taglib/helpers/getCurrentComponent'));
|
this.getMarkoComponentsRequirePath('marko/components/taglib/helpers/getCurrentComponent'));
|
||||||
|
|
||||||
context.addVar('__component', builder.functionCall(getCurrentComponentVar, [builder.identifierOut()]));
|
context.addVar('__component', builder.functionCall(getCurrentComponentVar, [builder.identifierOut()]));
|
||||||
|
context.addVar('component', builder.memberExpression(
|
||||||
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('___component')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,72 +49,80 @@ module.exports = function assignComponentId(isRepeated) {
|
|||||||
// 3) The HTML does not have an "id" or "ref" attribute. We must add
|
// 3) The HTML does not have an "id" or "ref" attribute. We must add
|
||||||
// an "id" attribute with a unique ID.
|
// an "id" attribute with a unique ID.
|
||||||
|
|
||||||
var isCustomTag = el.type !== 'HtmlElement';
|
var isHtmlElement = el.type === 'HtmlElement';
|
||||||
|
var isCustomTag = el.type === 'CustomTag';
|
||||||
|
|
||||||
if (el.hasAttribute('key')) {
|
// LEGACY -- Remove in Marko 5.0
|
||||||
componentRef = el.getAttributeValue('key');
|
if (!isCustomTag && el.tagName === 'invoke') {
|
||||||
el.removeAttribute('key');
|
isCustomTag = true;
|
||||||
} else if (el.hasAttribute('ref')) {
|
}
|
||||||
context.deprecate('The "ref" attribute is deprecated. Please use "key" instead.');
|
|
||||||
componentRef = el.getAttributeValue('ref');
|
if (!isCustomTag && !isHtmlElement) {
|
||||||
el.removeAttribute('ref');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.hasAttribute('w-id')) {
|
if (el.hasAttribute('w-id')) {
|
||||||
context.deprecate('The "w-id" attribute is deprecated. Please use "key" instead.');
|
context.deprecate('The "w-id" attribute is deprecated. Please use "key" instead.');
|
||||||
|
|
||||||
if (componentRef) {
|
if (el.hasAttribute('key')) {
|
||||||
this.addError('The "w-id" attribute cannot be used in conjuction with the "ref" or "key" attributes.');
|
this.addError('The "w-id" attribute cannot be used in conjunction with the "key" attributes.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentRef = el.getAttributeValue('w-id');
|
if (el.hasAttribute('ref')) {
|
||||||
|
this.addError('The "w-id" attribute cannot be used in conjunction with the "ref" attributes.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedKey = el.getAttributeValue('w-id');
|
||||||
|
|
||||||
el.removeAttribute('w-id');
|
el.removeAttribute('w-id');
|
||||||
|
} else if (el.hasAttribute('key')) {
|
||||||
|
assignedKey = el.getAttributeValue('key');
|
||||||
|
el.removeAttribute('key');
|
||||||
|
} else if (el.hasAttribute('ref')) {
|
||||||
|
context.deprecate('The "ref" attribute is deprecated. Please use "key" instead.');
|
||||||
|
assignedKey = el.getAttributeValue('ref');
|
||||||
|
el.removeAttribute('ref');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentRef) {
|
if (assignedKey) {
|
||||||
idExpression = this.buildComponentElIdFunctionCall(componentRef);
|
nestedIdExpression = assignedKey;
|
||||||
|
|
||||||
nestedIdExpression = componentRef;
|
|
||||||
|
|
||||||
if (isCustomTag) {
|
if (isCustomTag) {
|
||||||
|
idExpression = this.buildComponentElIdFunctionCall(assignedKey);
|
||||||
// The element is a custom tag
|
// The element is a custom tag
|
||||||
this.getComponentArgs().setId(nestedIdExpression);
|
this.getComponentArgs().setKey(nestedIdExpression);
|
||||||
} else {
|
} else {
|
||||||
if (el.hasAttribute('id')) {
|
idExpression = assignedKey;
|
||||||
this.addError('The "ref", "key", and "w-id" attributes cannot be used in conjuction with the "id" attribute.');
|
if (context.data.hasLegacyForKey && el.data.userAssignedKey !== false) {
|
||||||
return;
|
el.setAttributeValue('id', this.buildComponentElIdFunctionCall(assignedKey));
|
||||||
}
|
}
|
||||||
el.setAttributeValue('id', idExpression);
|
|
||||||
}
|
|
||||||
} else if (el.hasAttribute('id')) {
|
|
||||||
idExpression = el.getAttributeValue('id');
|
|
||||||
|
|
||||||
if (el.isFlagSet('hasComponentBind')) {
|
if (context.isServerTarget()) {
|
||||||
// We have to attach a listener to the root element of the component
|
var markoKeyAttrVar = context.importModule('marko_keyAttr',
|
||||||
// We will use an empty string as an indicator that it is the root component
|
this.getMarkoComponentsRequirePath('marko/components/taglib/helpers/markoKeyAttr'));
|
||||||
// element.
|
|
||||||
nestedIdExpression = builder.literal('');
|
el.setAttributeValue('data-marko-key', builder.functionCall(markoKeyAttrVar, [
|
||||||
} else {
|
idExpression,
|
||||||
// Convert the raw String to a JavaScript expression. we need to prefix
|
builder.identifier('__component')
|
||||||
// with '#' to make it clear this is a fully resolved element ID
|
]));
|
||||||
nestedIdExpression = builder.concat(
|
}
|
||||||
builder.literal('#'),
|
|
||||||
idExpression);
|
el.setKey(assignedKey);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Case 3 - We need to add a unique "id" attribute
|
// Case 3 - We need to add a unique "id" attribute
|
||||||
let uniqueElId = this.nextUniqueId();
|
let uniqueElId = this.nextUniqueId();
|
||||||
|
|
||||||
nestedIdExpression = isRepeated ? builder.literal(uniqueElId + '[]') : builder.literal(uniqueElId);
|
nestedIdExpression = isRepeated ? builder.literal(uniqueElId + '[]') : builder.literal(uniqueElId.toString());
|
||||||
|
|
||||||
idExpression = this.buildComponentElIdFunctionCall(nestedIdExpression);
|
idExpression = builder.literal(uniqueElId.toString());
|
||||||
|
|
||||||
if (isCustomTag) {
|
if (isCustomTag) {
|
||||||
this.getComponentArgs().setId(nestedIdExpression);
|
this.getComponentArgs().setKey(nestedIdExpression);
|
||||||
} else {
|
} else {
|
||||||
el.setAttributeValue('id', idExpression);
|
el.setKey(idExpression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,13 +138,17 @@ module.exports = function assignComponentId(isRepeated) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let uniqueElId = transformHelper.nextUniqueId();
|
let uniqueElId = transformHelper.nextUniqueId();
|
||||||
let idVarName = '__componentId' + uniqueElId;
|
let idVarName = '__key' + uniqueElId;
|
||||||
let idVar = builder.identifier(idVarName);
|
let idVar = builder.identifier(idVarName);
|
||||||
|
|
||||||
this.idVarNode = builder.vars([
|
this.idVarNode = builder.vars([
|
||||||
{
|
{
|
||||||
id: idVarName,
|
id: idVarName,
|
||||||
init: idExpression
|
init: builder.functionCall(
|
||||||
|
builder.memberExpression(
|
||||||
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('___nextKey')),
|
||||||
|
[ idExpression ])
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -140,9 +159,9 @@ module.exports = function assignComponentId(isRepeated) {
|
|||||||
idVar);
|
idVar);
|
||||||
|
|
||||||
if (isCustomTag) {
|
if (isCustomTag) {
|
||||||
transformHelper.getComponentArgs().setId(nestedIdExpression);
|
transformHelper.getComponentArgs().setKey(nestedIdExpression);
|
||||||
} else {
|
} else {
|
||||||
el.setAttributeValue('id', idExpression);
|
el.setKey(idExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.idVarNode;
|
return this.idVarNode;
|
||||||
|
|||||||
158
src/components/taglib/TransformHelper/convertToComponent.js
Normal file
158
src/components/taglib/TransformHelper/convertToComponent.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
'use strict';
|
||||||
|
const generateRegisterComponentCode = require('../util/generateRegisterComponentCode');
|
||||||
|
|
||||||
|
// var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
var FLAG_HAS_BODY_EL = 2;
|
||||||
|
var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
|
||||||
|
module.exports = function handleComponentBind(options) {
|
||||||
|
if (this.firstBind) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.firstBind = true;
|
||||||
|
|
||||||
|
let context = this.context;
|
||||||
|
let builder = this.builder;
|
||||||
|
|
||||||
|
let isLegacyComponent = this.isLegacyComponent = options.isLegacyComponent === true;
|
||||||
|
let componentModule = options.componentModule;
|
||||||
|
let rendererModule = options.rendererModule;
|
||||||
|
let componentProps = options.componentProps || {};
|
||||||
|
let rootNodes = options.rootNodes;
|
||||||
|
let isLegacyInnerBind = options.isLegacyInnerBind;
|
||||||
|
|
||||||
|
var isSplit = false;
|
||||||
|
|
||||||
|
if ((rendererModule && rendererModule !== componentModule) ||
|
||||||
|
(!rendererModule && componentModule)) {
|
||||||
|
componentProps.split = isSplit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentModule) {
|
||||||
|
let componentTypeNode;
|
||||||
|
let dependencyModule = isLegacyComponent || isSplit ? componentModule : this.getTemplateModule();
|
||||||
|
|
||||||
|
if (dependencyModule.requirePath) {
|
||||||
|
context.addDependency({ type:'require', path: dependencyModule.requirePath });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSplit) {
|
||||||
|
context.addDependency({ type:'require', path: context.markoModulePrefix + 'components' });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentTypeNode = context.addStaticVar(
|
||||||
|
'marko_componentType',
|
||||||
|
generateRegisterComponentCode(componentModule, this, isSplit));
|
||||||
|
|
||||||
|
componentProps.type = componentTypeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLegacyInnerBind) {
|
||||||
|
let el = rootNodes[0];
|
||||||
|
el.setAttributeValue('id',
|
||||||
|
builder.memberExpression(
|
||||||
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('id')));
|
||||||
|
|
||||||
|
// TODO Deprecation warning for inner binds
|
||||||
|
let componentNode = context.createNodeForEl('_component', {
|
||||||
|
props: builder.literal(componentProps)
|
||||||
|
});
|
||||||
|
el.wrapWith(componentNode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rootNodes.forEach((rootNode) => {
|
||||||
|
if (rootNode.type === 'HtmlElement') {
|
||||||
|
if (rootNode.tagName === 'body') {
|
||||||
|
flags |= FLAG_HAS_BODY_EL;
|
||||||
|
} else if (rootNode.tagName === 'head') {
|
||||||
|
flags |= FLAG_HAS_HEAD_EL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (flags) {
|
||||||
|
context.root.appendChild(
|
||||||
|
builder.assignment(
|
||||||
|
builder.memberExpression(
|
||||||
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('___flags')),
|
||||||
|
builder.literal(flags),
|
||||||
|
'|='));
|
||||||
|
}
|
||||||
|
|
||||||
|
let markoComponentVar;
|
||||||
|
|
||||||
|
if (rendererModule) {
|
||||||
|
if (rendererModule.inlineId) {
|
||||||
|
markoComponentVar = rendererModule.inlineId;
|
||||||
|
} else {
|
||||||
|
markoComponentVar = context.addStaticVar('marko_component', builder.require(builder.literal(rendererModule.requirePath)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setHasBoundComponentForTemplate();
|
||||||
|
|
||||||
|
var rendererHelper = isLegacyComponent ?
|
||||||
|
this.context.helper('rendererLegacy') :
|
||||||
|
this.context.helper('renderer');
|
||||||
|
|
||||||
|
var defineComponentHelper;
|
||||||
|
|
||||||
|
if (!isSplit && !isLegacyComponent) {
|
||||||
|
defineComponentHelper = this.context.helper('defineComponent');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.on('beforeGenerateCode:TemplateRoot', function(eventArgs) {
|
||||||
|
eventArgs.node.addRenderFunctionParam(builder.identifier('__component'));
|
||||||
|
|
||||||
|
if (isLegacyComponent) {
|
||||||
|
eventArgs.node.addRenderFunctionParam(builder.identifier('widget'));
|
||||||
|
eventArgs.node.addRenderFunctionParam(builder.identifier('component'));
|
||||||
|
} else {
|
||||||
|
eventArgs.node.addRenderFunctionParam(builder.identifier('component'));
|
||||||
|
eventArgs.node.addRenderFunctionParam(builder.identifier('state'));
|
||||||
|
}
|
||||||
|
|
||||||
|
eventArgs.node.generateAssignRenderCode = (eventArgs) => {
|
||||||
|
const nodes = [];
|
||||||
|
const templateVar = eventArgs.templateVar;
|
||||||
|
const templateRendererMember = eventArgs.templateRendererMember;
|
||||||
|
const renderFunctionVar = eventArgs.renderFunctionVar;
|
||||||
|
|
||||||
|
const createRendererArgs = [
|
||||||
|
renderFunctionVar,
|
||||||
|
builder.literal(componentProps)
|
||||||
|
];
|
||||||
|
|
||||||
|
if (markoComponentVar) {
|
||||||
|
createRendererArgs.push(markoComponentVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.push(builder.assignment(
|
||||||
|
templateRendererMember,
|
||||||
|
builder.functionCall(
|
||||||
|
rendererHelper,
|
||||||
|
createRendererArgs)));
|
||||||
|
|
||||||
|
if (!isSplit && !isLegacyComponent) {
|
||||||
|
nodes.push(builder.assignment(
|
||||||
|
builder.memberExpression(templateVar, builder.identifier('Component')),
|
||||||
|
builder.functionCall(
|
||||||
|
defineComponentHelper,
|
||||||
|
[
|
||||||
|
markoComponentVar,
|
||||||
|
templateRendererMember
|
||||||
|
])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,287 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const resolveFrom = require('resolve-from');
|
|
||||||
const generateRegisterComponentCode = require('../util/generateRegisterComponentCode');
|
|
||||||
|
|
||||||
function legacyGetDefaultComponentModule(dirname) {
|
|
||||||
var filename;
|
|
||||||
var legacy = true;
|
|
||||||
|
|
||||||
if ((filename = resolveFrom(dirname, './widget'))) {
|
|
||||||
return {
|
|
||||||
filename,
|
|
||||||
requirePath: './widget',
|
|
||||||
legacy
|
|
||||||
};
|
|
||||||
} else if ((filename = resolveFrom(dirname, './component'))) {
|
|
||||||
return {
|
|
||||||
filename,
|
|
||||||
requirePath: './component',
|
|
||||||
legacy
|
|
||||||
};
|
|
||||||
} else if ((filename = resolveFrom(dirname, './'))) {
|
|
||||||
return {
|
|
||||||
filename,
|
|
||||||
requirePath: './',
|
|
||||||
legacy
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkIsInnerBind(el) {
|
|
||||||
var curNode = el;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (curNode.data.hasBoundComponent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
curNode = curNode.parentNode;
|
|
||||||
|
|
||||||
if (!curNode) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function handleComponentBind() {
|
|
||||||
let el = this.el;
|
|
||||||
let context = this.context;
|
|
||||||
let builder = this.builder;
|
|
||||||
|
|
||||||
let componentModule;
|
|
||||||
let rendererModulePath;
|
|
||||||
let rendererModule = this.getRendererModule();
|
|
||||||
let isLegacyComponent = false;
|
|
||||||
|
|
||||||
if (el.hasAttribute('w-bind')) {
|
|
||||||
let bindAttr = el.getAttribute('w-bind');
|
|
||||||
|
|
||||||
context.deprecate('Legacy components using w-bind and defineRenderer/defineComponent or defineComponent are deprecated. See: https://github.com/marko-js/marko/issues/421');
|
|
||||||
this.isLegacyComponent = isLegacyComponent = true;
|
|
||||||
|
|
||||||
// Remove the w-bind attribute since we don't want it showing up in the output DOM
|
|
||||||
el.removeAttribute('w-bind');
|
|
||||||
|
|
||||||
// Read the value for the w-bind attribute. This will be an AST node for the parsed JavaScript
|
|
||||||
let bindAttrValue = bindAttr.value;
|
|
||||||
|
|
||||||
const hasWidgetTypes = context.isFlagSet('hasWidgetTypes');
|
|
||||||
|
|
||||||
if (hasWidgetTypes) {
|
|
||||||
context.deprecate('The <widget-types> tag is deprecated. Please remove it. See: https://github.com/marko-js/marko/issues/514');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bindAttrValue == null) {
|
|
||||||
componentModule = legacyGetDefaultComponentModule(this.dirname);
|
|
||||||
if (!componentModule) {
|
|
||||||
this.addError('No corresponding JavaScript module found in the same directory (either "component.js" or "index.js").');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (bindAttr.isLiteralValue()) {
|
|
||||||
if (typeof bindAttr.literalValue !== 'string') {
|
|
||||||
this.addError('The value for the "w-bind" attribute should be a string. Actual: ' + componentModule);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let requirePath = bindAttr.literalValue;
|
|
||||||
let filename = resolveFrom(this.dirname, requirePath);
|
|
||||||
|
|
||||||
if (!filename) {
|
|
||||||
this.addError('Target file not found: ' + requirePath + ' (from: ' + this.dirname + ')');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentModule = {
|
|
||||||
legacy: true,
|
|
||||||
filename,
|
|
||||||
requirePath
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// This is a dynamic expression. The <widget-types> should have been found.
|
|
||||||
if (!hasWidgetTypes) {
|
|
||||||
this.addError('The <widget-types> tag must be used to declare components when the value of the "w-bind" attribute is a dynamic expression.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
el.insertSiblingBefore(
|
|
||||||
builder.functionCall(
|
|
||||||
builder.memberExpression(builder.identifier('__component'), builder.identifier('t')),
|
|
||||||
[
|
|
||||||
builder.memberExpression(
|
|
||||||
builder.identifier('marko_componentTypes'),
|
|
||||||
bindAttrValue,
|
|
||||||
true /* computed */)
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
} else if (el.isFlagSet('hasComponentBind')) {
|
|
||||||
componentModule = this.getComponentModule();
|
|
||||||
rendererModulePath = this.getRendererModule();
|
|
||||||
|
|
||||||
|
|
||||||
if (context.isFlagSet('hasWidgetTypes')) {
|
|
||||||
context.addError('The <widget-types> tag is no longer supported. See: https://github.com/marko-js/marko/issues/514');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setHasBoundComponentForTemplate();
|
|
||||||
|
|
||||||
let isInnerBind = checkIsInnerBind(el.parentNode);
|
|
||||||
|
|
||||||
el.data.hasBoundComponent = true;
|
|
||||||
|
|
||||||
// A component is bound to the el...
|
|
||||||
|
|
||||||
var componentProps = isInnerBind ? {} : this.getComponentProps();
|
|
||||||
let transformHelper = this;
|
|
||||||
|
|
||||||
var isSplit = false;
|
|
||||||
|
|
||||||
if ((rendererModule && rendererModule !== componentModule) ||
|
|
||||||
(!rendererModule && componentModule)) {
|
|
||||||
componentProps.split = isSplit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (componentModule) {
|
|
||||||
let componentTypeNode;
|
|
||||||
let dependencyModule = isLegacyComponent || isSplit ? componentModule : this.getTemplateModule();
|
|
||||||
|
|
||||||
if (dependencyModule.requirePath) {
|
|
||||||
context.addDependency({ type:'require', path: dependencyModule.requirePath });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSplit) {
|
|
||||||
context.addDependency({ type:'require', path: context.markoModulePrefix + 'components' });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentTypeNode = context.addStaticVar(
|
|
||||||
'marko_componentType',
|
|
||||||
generateRegisterComponentCode(componentModule, this, isSplit));
|
|
||||||
|
|
||||||
componentProps.type = componentTypeNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (el.hasAttribute('w-config')) {
|
|
||||||
el.insertSiblingBefore(
|
|
||||||
builder.functionCall(
|
|
||||||
builder.memberExpression(builder.identifier('__component'), builder.identifier('c')),
|
|
||||||
[
|
|
||||||
el.getAttributeValue('w-config')
|
|
||||||
]));
|
|
||||||
|
|
||||||
el.removeAttribute('w-config');
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = el.getAttributeValue('id');
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
componentProps.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
let markoComponentVar;
|
|
||||||
|
|
||||||
if (rendererModule) {
|
|
||||||
if (rendererModule.inlineId) {
|
|
||||||
markoComponentVar = rendererModule.inlineId;
|
|
||||||
} else {
|
|
||||||
markoComponentVar = context.addStaticVar('marko_component', builder.require(builder.literal(rendererModule.requirePath)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInnerBind) {
|
|
||||||
el.setAttributeValue('id',
|
|
||||||
builder.memberExpression(
|
|
||||||
builder.identifier('__component'),
|
|
||||||
builder.identifier('id')));
|
|
||||||
|
|
||||||
// TODO Deprecation warning for inner binds
|
|
||||||
let componentNode = context.createNodeForEl('_component', {
|
|
||||||
props: builder.literal(componentProps)
|
|
||||||
});
|
|
||||||
el.wrapWith(componentNode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.firstBind) {
|
|
||||||
|
|
||||||
var rendererHelper = isLegacyComponent ?
|
|
||||||
transformHelper.context.helper('rendererLegacy') :
|
|
||||||
transformHelper.context.helper('renderer');
|
|
||||||
|
|
||||||
var defineComponentHelper;
|
|
||||||
|
|
||||||
if (!isSplit && !isLegacyComponent) {
|
|
||||||
defineComponentHelper = transformHelper.context.helper('defineComponent');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.on('beforeGenerateCode:TemplateRoot', function(eventArgs) {
|
|
||||||
eventArgs.node.addRenderFunctionParam(builder.identifier('__component'));
|
|
||||||
|
|
||||||
if (isLegacyComponent) {
|
|
||||||
eventArgs.node.addRenderFunctionParam(builder.identifier('widget'));
|
|
||||||
} else {
|
|
||||||
eventArgs.node.addRenderFunctionParam(builder.identifier('component'));
|
|
||||||
eventArgs.node.addRenderFunctionParam(builder.identifier('state'));
|
|
||||||
}
|
|
||||||
|
|
||||||
eventArgs.node.generateAssignRenderCode = function(eventArgs) {
|
|
||||||
let nodes = [];
|
|
||||||
let templateVar = eventArgs.templateVar;
|
|
||||||
let templateRendererMember = eventArgs.templateRendererMember;
|
|
||||||
let renderFunctionVar = eventArgs.renderFunctionVar;
|
|
||||||
|
|
||||||
let createRendererArgs = [
|
|
||||||
renderFunctionVar,
|
|
||||||
builder.literal(componentProps)
|
|
||||||
];
|
|
||||||
|
|
||||||
if (markoComponentVar) {
|
|
||||||
createRendererArgs.push(markoComponentVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes.push(builder.assignment(
|
|
||||||
templateRendererMember,
|
|
||||||
builder.functionCall(
|
|
||||||
rendererHelper,
|
|
||||||
createRendererArgs)));
|
|
||||||
|
|
||||||
if (!isSplit && !isLegacyComponent) {
|
|
||||||
nodes.push(builder.assignment(
|
|
||||||
builder.memberExpression(templateVar, builder.identifier('Component')),
|
|
||||||
builder.functionCall(
|
|
||||||
defineComponentHelper,
|
|
||||||
[
|
|
||||||
markoComponentVar,
|
|
||||||
templateRendererMember
|
|
||||||
])));
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (el.hasAttribute('key')) {
|
|
||||||
if (!componentProps.roots) {
|
|
||||||
componentProps.roots = [];
|
|
||||||
}
|
|
||||||
var key = el.getAttributeValue('key');
|
|
||||||
componentProps.roots.push(key);
|
|
||||||
} else if (el.hasAttribute('ref')) {
|
|
||||||
if (!componentProps.roots) {
|
|
||||||
componentProps.roots = [];
|
|
||||||
}
|
|
||||||
var ref = el.getAttributeValue('ref');
|
|
||||||
componentProps.roots.push(ref);
|
|
||||||
} else {
|
|
||||||
el.setAttributeValue('id',
|
|
||||||
builder.memberExpression(
|
|
||||||
builder.identifier('__component'),
|
|
||||||
builder.identifier('id')));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const keySuffix = ':key';
|
|
||||||
|
|
||||||
module.exports = function handleComponentKeyAttrs() {
|
|
||||||
let el = this.el;
|
|
||||||
let context = this.context;
|
|
||||||
|
|
||||||
const filePosition = el.pos ? context.getPosInfo(el.pos) : context.filename;
|
|
||||||
|
|
||||||
// BEGIN support for deprecated for attributes
|
|
||||||
|
|
||||||
let deprecatedForAttributes = ['for-ref', 'for-key', 'w-for'];
|
|
||||||
|
|
||||||
deprecatedForAttributes.forEach(attributeName => {
|
|
||||||
if (el.hasAttribute(attributeName)) {
|
|
||||||
context.deprecate(`The "${attributeName}" tag is deprecated. Please use "for:key" instead.`);
|
|
||||||
|
|
||||||
let incompatibleAttributes = ['for', 'for:key']
|
|
||||||
.concat(deprecatedForAttributes.filter(a => a != attributeName))
|
|
||||||
.filter(a => el.hasAttribute(a));
|
|
||||||
|
|
||||||
if (incompatibleAttributes.length) {
|
|
||||||
this.addError(`The "${attributeName}" tag cannot be used with "${incompatibleAttributes.join('" or "')}". (${filePosition})`);
|
|
||||||
} else {
|
|
||||||
el.setAttributeValue('for:key', el.getAttributeValue(attributeName));
|
|
||||||
}
|
|
||||||
|
|
||||||
el.removeAttribute(attributeName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// END support for deprecated for attributes
|
|
||||||
|
|
||||||
el.attributes.forEach(attribute => {
|
|
||||||
const attributeName = attribute.name;
|
|
||||||
|
|
||||||
if (attributeName && attributeName !== keySuffix && attributeName.endsWith(keySuffix)) {
|
|
||||||
const unfixedName = attributeName.replace(keySuffix, '');
|
|
||||||
|
|
||||||
el.removeAttribute(attributeName);
|
|
||||||
if (el.hasAttribute(unfixedName)) {
|
|
||||||
this.addError(`The "${attributeName}" attribute cannot be used in conjuction with the "${unfixedName}" attribute. (${filePosition})`);
|
|
||||||
} else {
|
|
||||||
el.setAttributeValue(
|
|
||||||
unfixedName,
|
|
||||||
this.buildComponentElIdFunctionCall(attribute.value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -15,12 +15,17 @@ function addPreserve(transformHelper, bodyOnly, condition) {
|
|||||||
preserveAttrs['if'] = condition;
|
preserveAttrs['if'] = condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
let componentIdInfo = transformHelper.assignComponentId(true /* repeated */);
|
let componentIdInfo = transformHelper.assignComponentId();
|
||||||
let idVarNode = componentIdInfo.idVarNode ? null : componentIdInfo.createIdVarNode();
|
let idVarNode = componentIdInfo.idVarNode ? null : componentIdInfo.createIdVarNode();
|
||||||
|
|
||||||
preserveAttrs.id = transformHelper.getIdExpression();
|
if (el.type === 'HtmlElement') {
|
||||||
|
preserveAttrs.key = transformHelper.getIdExpression();
|
||||||
|
} else {
|
||||||
|
preserveAttrs.cid = transformHelper.getIdExpression();
|
||||||
|
}
|
||||||
|
|
||||||
let preserveNode = context.createNodeForEl('w-preserve', preserveAttrs);
|
|
||||||
|
let preserveNode = context.createNodeForEl('_preserve', preserveAttrs);
|
||||||
let idVarNodeTarget;
|
let idVarNodeTarget;
|
||||||
|
|
||||||
if (bodyOnly) {
|
if (bodyOnly) {
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var includeTagForComponents = require.resolve('../include-tag');
|
|
||||||
|
|
||||||
module.exports = function(includeNode) {
|
|
||||||
var context = this.context;
|
|
||||||
|
|
||||||
if (!this.hasBoundComponentForTemplate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentNode = includeNode.parentNode;
|
|
||||||
|
|
||||||
if (!parentNode.hasAttribute) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parentNode._normalizeChildTextNodes(context, true /* force trim */);
|
|
||||||
|
|
||||||
if (parentNode.childCount === 1) {
|
|
||||||
if (includeNode.hasAttribute('key') || includeNode.hasAttribute('ref')) {
|
|
||||||
this.assignComponentId();
|
|
||||||
}
|
|
||||||
|
|
||||||
let parentTransformHelper = this.getTransformHelper(parentNode);
|
|
||||||
|
|
||||||
if (includeNode.data.bodySlot) {
|
|
||||||
parentTransformHelper.assignComponentId(false /* not repeated */);
|
|
||||||
var componentProps = this.getComponentProps();
|
|
||||||
componentProps.body = parentTransformHelper.getNestedIdExpression();
|
|
||||||
} else {
|
|
||||||
let componentIdInfo = parentTransformHelper.assignComponentId(true /* repeated */);
|
|
||||||
if (!componentIdInfo.idVarNode) {
|
|
||||||
let idVarNode = componentIdInfo.createIdVarNode();
|
|
||||||
parentNode.onBeforeGenerateCode((event) => {
|
|
||||||
event.insertCode(idVarNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
includeNode.setRendererPath(includeTagForComponents);
|
|
||||||
|
|
||||||
includeNode.onBeforeGenerateCode(function() {
|
|
||||||
includeNode.addProp('_elId', parentTransformHelper.getIdExpression());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// includeNode.generateCodeForDynamicInclude = (options, codegen) => {
|
|
||||||
// var target = options.target;
|
|
||||||
// var data = options.data;
|
|
||||||
//
|
|
||||||
// if (!data) {
|
|
||||||
// data = builder.literal(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let includeVar = context.importModule('marko_component_include', this.getMarkoComponentsRequirePath('marko/components/taglib/helpers/include'));
|
|
||||||
//
|
|
||||||
// let includeArgs = [
|
|
||||||
// target,
|
|
||||||
// builder.identifierOut(),
|
|
||||||
// data
|
|
||||||
// ];
|
|
||||||
//
|
|
||||||
// if (parentTransformHelper) {
|
|
||||||
// includeArgs = includeArgs.concat([
|
|
||||||
// parentTransformHelper.getIdExpression(),
|
|
||||||
//
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return builder.functionCall(includeVar, includeArgs);
|
|
||||||
// };
|
|
||||||
};
|
|
||||||
155
src/components/taglib/TransformHelper/handleLegacyBind.js
Normal file
155
src/components/taglib/TransformHelper/handleLegacyBind.js
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
'use strict';
|
||||||
|
const resolveFrom = require('resolve-from');
|
||||||
|
|
||||||
|
function legacyGetDefaultComponentModule(dirname) {
|
||||||
|
var filename;
|
||||||
|
var legacy = true;
|
||||||
|
|
||||||
|
if ((filename = resolveFrom(dirname, './widget'))) {
|
||||||
|
return {
|
||||||
|
filename,
|
||||||
|
requirePath: './widget',
|
||||||
|
legacy
|
||||||
|
};
|
||||||
|
} else if ((filename = resolveFrom(dirname, './component'))) {
|
||||||
|
return {
|
||||||
|
filename,
|
||||||
|
requirePath: './component',
|
||||||
|
legacy
|
||||||
|
};
|
||||||
|
} else if ((filename = resolveFrom(dirname, './'))) {
|
||||||
|
return {
|
||||||
|
filename,
|
||||||
|
requirePath: './',
|
||||||
|
legacy
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIsInnerBind(el) {
|
||||||
|
var curNode = el;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (curNode.data.hasBoundComponent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
curNode = curNode.parentNode;
|
||||||
|
|
||||||
|
if (!curNode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function handleLegacyBind() {
|
||||||
|
let el = this.el;
|
||||||
|
let context = this.context;
|
||||||
|
let builder = this.builder;
|
||||||
|
|
||||||
|
let componentModule;
|
||||||
|
let rendererModule;
|
||||||
|
|
||||||
|
let isLegacyComponent = false;
|
||||||
|
|
||||||
|
if (el.hasAttribute('w-bind')) {
|
||||||
|
let bindAttr = el.getAttribute('w-bind');
|
||||||
|
|
||||||
|
context.deprecate('Legacy components using w-bind and defineRenderer/defineComponent or defineComponent are deprecated. See: https://github.com/marko-js/marko/issues/421');
|
||||||
|
this.isLegacyComponent = isLegacyComponent = true;
|
||||||
|
|
||||||
|
// Remove the w-bind attribute since we don't want it showing up in the output DOM
|
||||||
|
el.removeAttribute('w-bind');
|
||||||
|
|
||||||
|
// Read the value for the w-bind attribute. This will be an AST node for the parsed JavaScript
|
||||||
|
let bindAttrValue = bindAttr.value;
|
||||||
|
|
||||||
|
const hasWidgetTypes = context.isFlagSet('hasWidgetTypes');
|
||||||
|
|
||||||
|
if (hasWidgetTypes) {
|
||||||
|
context.deprecate('The <widget-types> tag is deprecated. Please remove it. See: https://github.com/marko-js/marko/issues/514');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindAttrValue == null) {
|
||||||
|
componentModule = legacyGetDefaultComponentModule(this.dirname);
|
||||||
|
if (!componentModule) {
|
||||||
|
this.addError('No corresponding JavaScript module found in the same directory (either "component.js" or "index.js").');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (bindAttr.isLiteralValue()) {
|
||||||
|
if (typeof bindAttr.literalValue !== 'string') {
|
||||||
|
this.addError('The value for the "w-bind" attribute should be a string. Actual: ' + componentModule);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let requirePath = bindAttr.literalValue;
|
||||||
|
let filename = resolveFrom(this.dirname, requirePath);
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
this.addError('Target file not found: ' + requirePath + ' (from: ' + this.dirname + ')');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentModule = {
|
||||||
|
legacy: true,
|
||||||
|
filename,
|
||||||
|
requirePath
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// This is a dynamic expression. The <widget-types> should have been found.
|
||||||
|
if (!hasWidgetTypes) {
|
||||||
|
this.addError('The <widget-types> tag must be used to declare components when the value of the "w-bind" attribute is a dynamic expression.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.insertSiblingBefore(
|
||||||
|
builder.functionCall(
|
||||||
|
builder.memberExpression(builder.identifier('__component'), builder.identifier('t')),
|
||||||
|
[
|
||||||
|
builder.memberExpression(
|
||||||
|
builder.identifier('marko_componentTypes'),
|
||||||
|
bindAttrValue,
|
||||||
|
true /* computed */)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isLegacyInnerBind = checkIsInnerBind(el.parentNode);
|
||||||
|
|
||||||
|
el.data.hasBoundComponent = true;
|
||||||
|
|
||||||
|
// A component is bound to the el...
|
||||||
|
|
||||||
|
if (el.hasAttribute('w-config')) {
|
||||||
|
el.insertSiblingBefore(
|
||||||
|
builder.functionCall(
|
||||||
|
builder.memberExpression(builder.identifier('__component'), builder.identifier('c')),
|
||||||
|
[
|
||||||
|
el.getAttributeValue('w-config')
|
||||||
|
]));
|
||||||
|
|
||||||
|
el.removeAttribute('w-config');
|
||||||
|
}
|
||||||
|
|
||||||
|
let componentProps = {};
|
||||||
|
|
||||||
|
let id = el.getAttributeValue('id');
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
componentProps.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.convertToComponent({
|
||||||
|
isLegacyInnerBind,
|
||||||
|
componentModule,
|
||||||
|
rendererModule,
|
||||||
|
isLegacyComponent: true,
|
||||||
|
rootNodes: [el]
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var path = require('path');
|
let path = require('path');
|
||||||
var getComponentFiles = require('./getComponentFiles');
|
let getComponentFiles = require('./getComponentFiles');
|
||||||
|
|
||||||
const esprima = require('esprima');
|
const esprima = require('esprima');
|
||||||
const escodegen = require('escodegen');
|
const escodegen = require('escodegen');
|
||||||
@ -12,16 +12,16 @@ function handleStyleElement(styleEl, transformHelper) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attrs = styleEl.attributes;
|
let attrs = styleEl.attributes;
|
||||||
|
|
||||||
var styleCode;
|
let styleCode;
|
||||||
var lang = 'css';
|
let lang = 'css';
|
||||||
|
|
||||||
var hasStyleBlock = false;
|
let hasStyleBlock = false;
|
||||||
|
|
||||||
for (var i=attrs.length-1; i>=0; i--) {
|
for (let i=attrs.length-1; i>=0; i--) {
|
||||||
var attr = attrs[i];
|
let attr = attrs[i];
|
||||||
var name = attr.name;
|
let name = attr.name;
|
||||||
if (name.startsWith('{')) {
|
if (name.startsWith('{')) {
|
||||||
hasStyleBlock = true;
|
hasStyleBlock = true;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ function handleStyleElement(styleEl, transformHelper) {
|
|||||||
styleEl.setFlag(FLAG_COMPONENT_STYLE);
|
styleEl.setFlag(FLAG_COMPONENT_STYLE);
|
||||||
|
|
||||||
if (styleCode) {
|
if (styleCode) {
|
||||||
var context = transformHelper.context;
|
let context = transformHelper.context;
|
||||||
context.addDependency({
|
context.addDependency({
|
||||||
type: lang,
|
type: lang,
|
||||||
code: styleCode,
|
code: styleCode,
|
||||||
@ -95,15 +95,15 @@ function classToObject(cls, el, transformHelper) {
|
|||||||
|
|
||||||
function handleClassDeclaration(classEl, transformHelper) {
|
function handleClassDeclaration(classEl, transformHelper) {
|
||||||
let tree;
|
let tree;
|
||||||
var wrappedSrc = '('+classEl.tagString+'\n)';
|
let wrappedSrc = '('+classEl.tagString+'\n)';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tree = esprima.parse(wrappedSrc);
|
tree = esprima.parse(wrappedSrc);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
var message = 'Unable to parse JavaScript for component class. ' + err;
|
let message = 'Unable to parse JavaScript for component class. ' + err;
|
||||||
|
|
||||||
if (err.index != null) {
|
if (err.index != null) {
|
||||||
var errorIndex = err.index;
|
let errorIndex = err.index;
|
||||||
// message += '\n' + err.description;
|
// message += '\n' + err.description;
|
||||||
if (errorIndex != null && errorIndex >= 0) {
|
if (errorIndex != null && errorIndex >= 0) {
|
||||||
transformHelper.context.addError({
|
transformHelper.context.addError({
|
||||||
@ -127,36 +127,28 @@ function handleClassDeclaration(classEl, transformHelper) {
|
|||||||
let object = classToObject(expression, classEl, transformHelper);
|
let object = classToObject(expression, classEl, transformHelper);
|
||||||
let componentVar = transformHelper.context.addStaticVar('marko_component', escodegen.generate(object));
|
let componentVar = transformHelper.context.addStaticVar('marko_component', escodegen.generate(object));
|
||||||
|
|
||||||
if (transformHelper.getRendererModule() != null) {
|
let moduleInfo = {
|
||||||
transformHelper.context.addError(classEl, 'The component has both an inline component `class` and a separate `component.js`. This is not allowed. See: https://github.com/marko-js/marko/wiki/Error:-Component-inline-and-external');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var moduleInfo = {
|
|
||||||
inlineId: componentVar,
|
inlineId: componentVar,
|
||||||
filename: transformHelper.filename,
|
filename: transformHelper.filename,
|
||||||
requirePath: './' + path.basename(transformHelper.filename)
|
requirePath: './' + path.basename(transformHelper.filename)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (transformHelper.getComponentModule() == null) {
|
|
||||||
transformHelper.setComponentModule(moduleInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
transformHelper.setRendererModule(moduleInfo);
|
|
||||||
|
|
||||||
classEl.detach();
|
classEl.detach();
|
||||||
|
|
||||||
|
return moduleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function handleRootNodes() {
|
module.exports = function handleRootNodes() {
|
||||||
var context = this.context;
|
let context = this.context;
|
||||||
var builder = this.builder;
|
|
||||||
|
|
||||||
var componentFiles = getComponentFiles(context.filename);
|
let componentFiles = getComponentFiles(context.filename);
|
||||||
if (!componentFiles) {
|
if (!componentFiles) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dirname = context.dirname;
|
let componentModule;
|
||||||
|
let rendererModule;
|
||||||
|
let dirname = context.dirname;
|
||||||
|
|
||||||
if (componentFiles.package) {
|
if (componentFiles.package) {
|
||||||
context.addDependency('package: ./' + componentFiles.package);
|
context.addDependency('package: ./' + componentFiles.package);
|
||||||
@ -174,67 +166,78 @@ module.exports = function handleRootNodes() {
|
|||||||
requirePath: './'+file.slice(0, file.lastIndexOf('.'))
|
requirePath: './'+file.slice(0, file.lastIndexOf('.'))
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setComponentModule(moduleInfo);
|
componentModule = rendererModule = moduleInfo;
|
||||||
this.setRendererModule(moduleInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentFiles.browserFile) {
|
if (componentFiles.browserFile) {
|
||||||
let file = componentFiles.browserFile;
|
let file = componentFiles.browserFile;
|
||||||
this.setComponentModule({
|
|
||||||
|
componentModule = {
|
||||||
filename: path.join(dirname, file),
|
filename: path.join(dirname, file),
|
||||||
requirePath: './' + file.slice(
|
requirePath: './' + file.slice(
|
||||||
0, file.lastIndexOf('.'))
|
0, file.lastIndexOf('.'))
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var templateRoot = this.el;
|
let templateRoot = this.el;
|
||||||
|
|
||||||
var rootNodes = [];
|
let rootNodes = [];
|
||||||
var hasLegacyExplicitBind = false;
|
let hasIdCount = 0;
|
||||||
var hasIdCount = 0;
|
let nodeWithAssignedId;
|
||||||
var nodeWithAssignedId;
|
let assignedId;
|
||||||
var assignedId;
|
let transformHelper = this;
|
||||||
var transformHelper = this;
|
|
||||||
|
|
||||||
let walker = context.createWalker({
|
let walker = context.createWalker({
|
||||||
enter(node) {
|
enter(node) {
|
||||||
let tagName = node.tagName && node.tagName.toLowerCase();
|
let tagName = node.tagName && node.tagName.toLowerCase();
|
||||||
let tag = node.tagName && context.taglibLookup.getTag(node.tagName);
|
|
||||||
|
|
||||||
if (node.type === 'TemplateRoot' || !node.type) {
|
if (node.type === 'TemplateRoot' || !node.type) {
|
||||||
// Don't worry about the TemplateRoot or a Container node
|
// Don't worry about the TemplateRoot or an Container node
|
||||||
// But continue into the node to look at its children for root elements
|
|
||||||
} else if (tag && tag.noOutput) {
|
|
||||||
walker.skip();
|
|
||||||
} else if (node.type === 'HtmlElement') {
|
} else if (node.type === 'HtmlElement') {
|
||||||
if (node.hasAttribute('w-bind')) {
|
if (node.hasAttribute('id')) {
|
||||||
transformHelper.setHasBoundComponentForTemplate();
|
hasIdCount++;
|
||||||
hasLegacyExplicitBind = true;
|
nodeWithAssignedId = node;
|
||||||
} else {
|
assignedId = node.getAttributeValue('id');
|
||||||
if (node.hasAttribute('id')) {
|
}
|
||||||
hasIdCount++;
|
|
||||||
nodeWithAssignedId = node;
|
|
||||||
assignedId = node.getAttributeValue('id');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagName === 'style') {
|
if (tagName === 'style') {
|
||||||
handleStyleElement(node, transformHelper);
|
handleStyleElement(node, transformHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.isFlagSet(FLAG_COMPONENT_STYLE)) {
|
if (!node.isFlagSet(FLAG_COMPONENT_STYLE)) {
|
||||||
rootNodes.push(node);
|
rootNodes.push(node);
|
||||||
}
|
}
|
||||||
|
walker.skip();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (node.type === 'CustomTag') {
|
||||||
|
let tag = context.taglibLookup.getTag(node.tagName);
|
||||||
|
|
||||||
|
if (!tag.noOutput) {
|
||||||
|
rootNodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
walker.skip();
|
walker.skip();
|
||||||
return;
|
return;
|
||||||
} else if (node.type === 'CustomTag') {
|
} else if (node.type === 'Scriptlet') {
|
||||||
rootNodes.push(node);
|
|
||||||
walker.skip();
|
walker.skip();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (tagName === 'class') {
|
if (tagName === 'class') {
|
||||||
handleClassDeclaration(node, transformHelper);
|
let classComponentModule = handleClassDeclaration(node, transformHelper);
|
||||||
|
|
||||||
|
if (rendererModule != null) {
|
||||||
|
transformHelper.context.addError(node, 'The component has both an inline component `class` and a separate `component.js`. This is not allowed. See: https://github.com/marko-js/marko/wiki/Error:-Component-inline-and-external');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (componentModule == null) {
|
||||||
|
componentModule = classComponentModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
rendererModule = classComponentModule;
|
||||||
|
} else if (!node.noOutput) {
|
||||||
|
rootNodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
walker.skip();
|
walker.skip();
|
||||||
@ -245,43 +248,29 @@ module.exports = function handleRootNodes() {
|
|||||||
|
|
||||||
walker.walk(templateRoot);
|
walker.walk(templateRoot);
|
||||||
|
|
||||||
if (hasLegacyExplicitBind) {
|
if (!componentModule) {
|
||||||
//There is an explicit bind so nothing to do
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.hasBoundComponentForTemplate()) {
|
if (context.isFlagSet('hasWidgetTypes')) {
|
||||||
return;
|
context.addError('The <widget-types> tag is no longer supported. See: https://github.com/marko-js/marko/issues/514');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
templateRoot._normalizeChildTextNodes(context, true);
|
||||||
|
|
||||||
|
// After normalizing the text nodes to remove whitespace we may have detached
|
||||||
|
// some of the root text nodes so remove those from our list
|
||||||
|
rootNodes = rootNodes.filter((rootNode) => {
|
||||||
|
return rootNode.isDetached() !== true;
|
||||||
|
});
|
||||||
|
|
||||||
if (rootNodes.length === 0) {
|
if (rootNodes.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootNodes.length > 1 && hasIdCount > 0) {
|
this.convertToComponent({
|
||||||
// We can only bind a component to multiple top-level elements if we can assign
|
rootNodes,
|
||||||
// all of the IDs
|
componentModule,
|
||||||
return;
|
rendererModule
|
||||||
}
|
|
||||||
|
|
||||||
transformHelper.setHasBoundComponentForTemplate();
|
|
||||||
|
|
||||||
var nextKey = 0;
|
|
||||||
|
|
||||||
rootNodes.forEach((curNode, i) => {
|
|
||||||
let id = curNode.getAttributeValue('id');
|
|
||||||
|
|
||||||
if (id && id.type !== 'Literal') {
|
|
||||||
context.addError('Root HTML element should not have a dynamic `id` attribute. See: https://github.com/marko-js/marko/wiki/Error:-Dynamic-root-HTML-element-id-attribute');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
curNode.setFlag('hasComponentBind');
|
|
||||||
|
|
||||||
if (!curNode.hasAttribute('key') && !curNode.hasAttribute('ref')) {
|
|
||||||
if (curNode.type === 'CustomTag' || rootNodes.length > 1) {
|
|
||||||
curNode.setAttributeValue('key', builder.literal('_r' + (nextKey++)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
75
src/components/taglib/TransformHelper/handleScopedAttrs.js
Normal file
75
src/components/taglib/TransformHelper/handleScopedAttrs.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const deprecatedKeySuffix = ':key';
|
||||||
|
const scopedSuffix = ':scoped';
|
||||||
|
const deprecatedAttrs = {
|
||||||
|
'for-ref': true,
|
||||||
|
'for-key': true,
|
||||||
|
'w-for': true
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function handleComponentKeyAttrs() {
|
||||||
|
let el = this.el;
|
||||||
|
let context = this.context;
|
||||||
|
let builder = this.builder;
|
||||||
|
const filePosition = el.pos ? context.getPosInfo(el.pos) : context.filename;
|
||||||
|
|
||||||
|
var attrs = el.attributes.concat([]);
|
||||||
|
|
||||||
|
attrs.forEach(attribute => {
|
||||||
|
const attributeName = attribute.name;
|
||||||
|
if (!attributeName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fixedAttributeName = attributeName;
|
||||||
|
|
||||||
|
// BEGIN support for deprecated for attributes
|
||||||
|
if (deprecatedAttrs[attributeName]) {
|
||||||
|
context.deprecate(`The "${attributeName}" attribute is deprecated. Please use "for:key" instead.`);
|
||||||
|
|
||||||
|
let incompatibleAttributes = ['for', 'for:key']
|
||||||
|
.concat(Object.keys(deprecatedAttrs).filter(a => a != attributeName))
|
||||||
|
.filter(a => el.hasAttribute(a));
|
||||||
|
|
||||||
|
if (incompatibleAttributes.length) {
|
||||||
|
this.addError(`The "${attributeName}" attribute cannot be used in conjunction with "${incompatibleAttributes.join('" or "')}". (${filePosition})`);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
fixedAttributeName = 'for:scoped';
|
||||||
|
}
|
||||||
|
} else if (attributeName !== deprecatedKeySuffix && attributeName.endsWith(deprecatedKeySuffix)) {
|
||||||
|
context.deprecate(`The "${attributeName}" attribute is deprecated. Please use "for:scoped" instead.`);
|
||||||
|
fixedAttributeName = attributeName.slice(0, 0-deprecatedKeySuffix.length) + ':scoped';
|
||||||
|
}
|
||||||
|
// END support for deprecated for attributes
|
||||||
|
|
||||||
|
if (fixedAttributeName !== scopedSuffix && fixedAttributeName.endsWith(scopedSuffix)) {
|
||||||
|
el.removeAttribute(attributeName);
|
||||||
|
|
||||||
|
let finalAttributeName = fixedAttributeName.slice(0, 0-scopedSuffix.length);
|
||||||
|
|
||||||
|
if (el.hasAttribute(finalAttributeName)) {
|
||||||
|
this.addError(`The "${attributeName}" attribute cannot be used in conjunction with the "${finalAttributeName}" attribute. (${filePosition})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uniqueElId = this.nextUniqueId();
|
||||||
|
let idVarName = 'marko_' + finalAttributeName + '_key' + uniqueElId;
|
||||||
|
|
||||||
|
|
||||||
|
let varNode = builder.var(idVarName, attribute.value);
|
||||||
|
|
||||||
|
el.insertSiblingBefore(varNode);
|
||||||
|
let varIdNode = builder.identifier(idVarName);
|
||||||
|
|
||||||
|
el.setAttributeValue(finalAttributeName, this.buildComponentElIdFunctionCall(varIdNode));
|
||||||
|
|
||||||
|
if (!el.hasAttribute('key') && !el.hasAttribute('w-id') && !el.hasAttribute('ref')) {
|
||||||
|
// The scoped attribute should be suitable for a key
|
||||||
|
el.setAttributeValue('key', varIdNode);
|
||||||
|
el.data.userAssignedKey = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -24,25 +24,11 @@ class TransformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHasBoundComponentForTemplate() {
|
setHasBoundComponentForTemplate() {
|
||||||
|
this.context.isComponent = true;
|
||||||
|
|
||||||
this.context.data[HAS_COMPONENT_KEY] = true;
|
this.context.data[HAS_COMPONENT_KEY] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setComponentModule(value) {
|
|
||||||
this.context.data.componentModule = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getComponentModule() {
|
|
||||||
return this.context.data.componentModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRendererModule(value) {
|
|
||||||
this.context.data.rendererModule = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRendererModule() {
|
|
||||||
return this.context.data.rendererModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTemplateModule() {
|
getTemplateModule() {
|
||||||
return {
|
return {
|
||||||
requirePath:this.context.getRequirePath(this.filename)
|
requirePath:this.context.getRequirePath(this.filename)
|
||||||
@ -55,15 +41,6 @@ class TransformHelper {
|
|||||||
this.context.data[WIDGET_PROPS_KEY] != null;
|
this.context.data[WIDGET_PROPS_KEY] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentProps() {
|
|
||||||
var componentProps = this.context.data[WIDGET_PROPS_KEY];
|
|
||||||
if (!componentProps) {
|
|
||||||
this.firstBind = true;
|
|
||||||
componentProps = this.context.data[WIDGET_PROPS_KEY] = {};
|
|
||||||
}
|
|
||||||
return componentProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
addError(message, code) {
|
addError(message, code) {
|
||||||
this.context.addError(this.el, message, code);
|
this.context.addError(this.el, message, code);
|
||||||
}
|
}
|
||||||
@ -81,11 +58,6 @@ class TransformHelper {
|
|||||||
return (this.context.data.componentNextElId++);
|
return (this.context.data.componentNextElId++);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNestedIdExpression() {
|
|
||||||
this.assignComponentId();
|
|
||||||
return this.getComponentIdInfo().nestedIdExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIdExpression() {
|
getIdExpression() {
|
||||||
this.assignComponentId();
|
this.assignComponentId();
|
||||||
return this.getComponentIdInfo().idExpression;
|
return this.getComponentIdInfo().idExpression;
|
||||||
@ -129,11 +101,19 @@ class TransformHelper {
|
|||||||
buildComponentElIdFunctionCall(id) {
|
buildComponentElIdFunctionCall(id) {
|
||||||
var builder = this.builder;
|
var builder = this.builder;
|
||||||
|
|
||||||
var componentElId = builder.memberExpression(
|
if (id.type === 'Literal' && id.value === '') {
|
||||||
builder.identifier('__component'),
|
let componentElId = builder.memberExpression(
|
||||||
builder.identifier('elId'));
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('id'));
|
||||||
|
|
||||||
return builder.functionCall(componentElId, arguments.length === 0 ? [] : [ id ]);
|
return componentElId;
|
||||||
|
} else {
|
||||||
|
let componentElId = builder.memberExpression(
|
||||||
|
builder.identifier('__component'),
|
||||||
|
builder.identifier('elId'));
|
||||||
|
|
||||||
|
return builder.functionCall(componentElId, arguments.length === 0 ? [] : [ id ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTransformHelper(el) {
|
getTransformHelper(el) {
|
||||||
@ -142,12 +122,12 @@ class TransformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransformHelper.prototype.assignComponentId = require('./assignComponentId');
|
TransformHelper.prototype.assignComponentId = require('./assignComponentId');
|
||||||
|
TransformHelper.prototype.convertToComponent = require('./convertToComponent');
|
||||||
TransformHelper.prototype.handleRootNodes = require('./handleRootNodes');
|
TransformHelper.prototype.handleRootNodes = require('./handleRootNodes');
|
||||||
TransformHelper.prototype.handleIncludeNode = require('./handleIncludeNode');
|
|
||||||
TransformHelper.prototype.handleComponentEvents = require('./handleComponentEvents');
|
TransformHelper.prototype.handleComponentEvents = require('./handleComponentEvents');
|
||||||
TransformHelper.prototype.handleComponentPreserve = require('./handleComponentPreserve');
|
TransformHelper.prototype.handleComponentPreserve = require('./handleComponentPreserve');
|
||||||
TransformHelper.prototype.handleComponentPreserveAttrs = require('./handleComponentPreserveAttrs');
|
TransformHelper.prototype.handleComponentPreserveAttrs = require('./handleComponentPreserveAttrs');
|
||||||
TransformHelper.prototype.handleComponentBind = require('./handleComponentBind');
|
TransformHelper.prototype.handleScopedAttrs = require('./handleScopedAttrs');
|
||||||
TransformHelper.prototype.handleComponentKeyAttrs = require('./handleComponentKeyAttrs');
|
TransformHelper.prototype.handleLegacyBind = require('./handleLegacyBind');
|
||||||
|
|
||||||
module.exports = TransformHelper;
|
module.exports = TransformHelper;
|
||||||
|
|||||||
@ -23,10 +23,15 @@ module.exports = function transform(el, context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (el.hasAttribute('w-body')) {
|
if (el.hasAttribute('w-body')) {
|
||||||
|
// This is a legacy code block and should be removed in Marko v5
|
||||||
context.deprecate('The "w-body" attribute is deprecated. Please use "<include(...)" instead. See: https://github.com/marko-js/marko/issues/492');
|
context.deprecate('The "w-body" attribute is deprecated. Please use "<include(...)" instead. See: https://github.com/marko-js/marko/issues/492');
|
||||||
let builder = context.builder;
|
let builder = context.builder;
|
||||||
let bodyValue = el.getAttributeValue('w-body');
|
let bodyValue = el.getAttributeValue('w-body');
|
||||||
el.removeAttribute('w-body');
|
el.removeAttribute('w-body');
|
||||||
|
el.addAttribute({ // The old behavior is that the body content would be preserved if no new body content was provided
|
||||||
|
name: 'no-update-body-if',
|
||||||
|
argument: '!__component.b'//builder.negate(builder.memberExpression(builder.identifier('__component'), builder.identifier('b')))
|
||||||
|
});
|
||||||
|
|
||||||
let includeNode = context.createNodeForEl('include');
|
let includeNode = context.createNodeForEl('include');
|
||||||
|
|
||||||
@ -44,11 +49,6 @@ module.exports = function transform(el, context) {
|
|||||||
|
|
||||||
if (el.tagName === 'widget-types') {
|
if (el.tagName === 'widget-types') {
|
||||||
context.setFlag('hasWidgetTypes');
|
context.setFlag('hasWidgetTypes');
|
||||||
} else if (el.tagName === 'include') {
|
|
||||||
transformHelper.handleComponentEvents();
|
|
||||||
transformHelper.handleIncludeNode(el);
|
|
||||||
transformHelper.getComponentArgs().compile(transformHelper);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.hasAttribute('w-el-id')) {
|
if (el.hasAttribute('w-el-id')) {
|
||||||
@ -56,9 +56,8 @@ module.exports = function transform(el, context) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.isFlagSet('hasComponentBind') || el.hasAttribute('w-bind')) {
|
if (el.hasAttribute('w-bind')) {
|
||||||
el.setFlag('hasComponentBind');
|
transformHelper.handleLegacyBind();
|
||||||
transformHelper.handleComponentBind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/* New preserve attributes */
|
if (/* New preserve attributes */
|
||||||
@ -74,10 +73,11 @@ module.exports = function transform(el, context) {
|
|||||||
transformHelper.handleComponentPreserve();
|
transformHelper.handleComponentPreserve();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.hasAttribute('key') || el.hasAttribute('ref') || el.hasAttribute('w-id')) {
|
// Handle *:key properties (and deprecated w-for/for-key/for-ref)
|
||||||
if (!tagDefinitionHasOverridingKeyAttribute(el, context)) {
|
transformHelper.handleScopedAttrs();
|
||||||
transformHelper.assignComponentId();
|
|
||||||
}
|
if ((el.hasAttribute('w-id') || el.hasAttribute('ref') || el.hasAttribute('key') || transformHelper.hasBoundComponentForTemplate()) && !tagDefinitionHasOverridingKeyAttribute(el, context)) {
|
||||||
|
transformHelper.assignComponentId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.hasAttribute('w-body')) {
|
if (el.hasAttribute('w-body')) {
|
||||||
@ -90,9 +90,6 @@ module.exports = function transform(el, context) {
|
|||||||
// Handle w-on* properties
|
// Handle w-on* properties
|
||||||
transformHelper.handleComponentEvents();
|
transformHelper.handleComponentEvents();
|
||||||
|
|
||||||
// Handle *:key properties (and deprecated w-for/for-key/for-ref)
|
|
||||||
transformHelper.handleComponentKeyAttrs();
|
|
||||||
|
|
||||||
// If we need to pass any information to a nested component then
|
// If we need to pass any information to a nested component then
|
||||||
// we start that information in the "out" so that it can be picked
|
// we start that information in the "out" so that it can be picked
|
||||||
// up later by the nested component. We call this "component args" and
|
// up later by the nested component. We call this "component args" and
|
||||||
|
|||||||
@ -5,13 +5,11 @@
|
|||||||
* @return {ComponentDef} The ComponentDef instance
|
* @return {ComponentDef} The ComponentDef instance
|
||||||
*/
|
*/
|
||||||
module.exports = function getCurrentComponent(out) {
|
module.exports = function getCurrentComponent(out) {
|
||||||
var componentsContext = out.data.___components;
|
var componentsContext = out.___components;
|
||||||
var componentStack;
|
|
||||||
var len;
|
|
||||||
|
|
||||||
if (!componentsContext || (len = (componentStack = componentsContext.___componentStack).length) < 2) {
|
if (!componentsContext) {
|
||||||
throw Error('No component found');
|
throw Error('No component found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return componentStack[len - 1];
|
return componentsContext.___componentDef;
|
||||||
};
|
};
|
||||||
|
|||||||
9
src/components/taglib/helpers/markoKeyAttr.js
Normal file
9
src/components/taglib/helpers/markoKeyAttr.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
// var FLAG_HAS_BODY_EL = 2;
|
||||||
|
// var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
|
||||||
|
module.exports = function markoKeyAttr(key, componentDef) {
|
||||||
|
if ((componentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER) === 0) {
|
||||||
|
return key + ' ' + componentDef.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,19 +0,0 @@
|
|||||||
var normalInclude = require('../../taglibs/core/include-tag').___doInclude;
|
|
||||||
var componentsUtil = require('../util');
|
|
||||||
var getElementById = componentsUtil.___getElementById;
|
|
||||||
|
|
||||||
var getComponentsContext = require('../ComponentsContext').___getComponentsContext;
|
|
||||||
|
|
||||||
module.exports = function include(input, out) {
|
|
||||||
if (!normalInclude(input, out)) {
|
|
||||||
var elId = input._elId;
|
|
||||||
|
|
||||||
// There's no body content so let's see if we should reuse
|
|
||||||
// the existing body content in the DOM
|
|
||||||
var existingEl = getElementById(out.___document, elId);
|
|
||||||
if (existingEl) {
|
|
||||||
var componentsContext = getComponentsContext(out);
|
|
||||||
componentsContext.___globalContext.___preserveDOMNode(elId, true /* body only */);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
var normalInclude = require('../../taglibs/core/include-tag');
|
|
||||||
|
|
||||||
module.exports = function include(input, out) {
|
|
||||||
normalInclude(input, out);
|
|
||||||
};
|
|
||||||
@ -9,7 +9,7 @@ var ComponentsContext = require('../ComponentsContext');
|
|||||||
function handleAwaitBeforeRender(eventArgs) {
|
function handleAwaitBeforeRender(eventArgs) {
|
||||||
if (eventArgs.clientReorder) {
|
if (eventArgs.clientReorder) {
|
||||||
var asyncFragmentOut = eventArgs.out;
|
var asyncFragmentOut = eventArgs.out;
|
||||||
asyncFragmentOut.data.___components = new ComponentsContext(asyncFragmentOut, undefined, false);
|
asyncFragmentOut.___components = new ComponentsContext(asyncFragmentOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -166,13 +166,13 @@
|
|||||||
"no-output": true,
|
"no-output": true,
|
||||||
"@immediate": "boolean"
|
"@immediate": "boolean"
|
||||||
},
|
},
|
||||||
"<w-preserve>": {
|
"<_preserve>": {
|
||||||
"renderer": "./preserve-tag.js",
|
"renderer": "./preserve-tag.js",
|
||||||
"@id": "string",
|
"@key": "string",
|
||||||
"@if": "expression",
|
"@cid": "string",
|
||||||
"@body-only": "expression",
|
"@if": "boolean",
|
||||||
"autocomplete": [],
|
"@body-only": "boolean",
|
||||||
"deprecated": true
|
"autocomplete": []
|
||||||
},
|
},
|
||||||
"<no-update>": {
|
"<no-update>": {
|
||||||
"renderer": "./preserve-tag.js",
|
"renderer": "./preserve-tag.js",
|
||||||
|
|||||||
@ -1,34 +1,46 @@
|
|||||||
var getElementById = require('../util').___getElementById;
|
var componentsUtil = require('../util');
|
||||||
|
var componentLookup = componentsUtil.___componentLookup;
|
||||||
|
|
||||||
module.exports = function render(input, out) {
|
module.exports = function render(input, out) {
|
||||||
var globalComponentsContext = out.global.___components;
|
var componentsContext = out.___components;
|
||||||
|
|
||||||
if (globalComponentsContext && globalComponentsContext.___isRerenderInBrowser !== true && globalComponentsContext.___rerenderComponent !== undefined) {
|
|
||||||
var id = input.id;
|
|
||||||
|
|
||||||
|
if (componentsContext) {
|
||||||
// See if the DOM node with the given ID already exists.
|
// See if the DOM node with the given ID already exists.
|
||||||
// If so, then reuse the existing DOM node instead of re-rendering
|
// If so, then reuse the existing DOM node instead of re-rendering
|
||||||
// the children. We have to put a placeholder node that will get
|
// the children. We have to put a placeholder node that will get
|
||||||
// replaced out if we find that the DOM node has already been rendered
|
// replaced out if we find that the DOM node has already been rendered
|
||||||
if (!('if' in input) || input['if']) {
|
if (!('if' in input) || input['if']) {
|
||||||
var existingEl = getElementById(out.___document, id);
|
var component = componentsContext.___componentDef.___component;
|
||||||
if (existingEl) {
|
var globalComponentsContext = componentsContext.___globalContext;
|
||||||
var bodyOnly = input.bodyOnly === true;
|
var key = input.key;
|
||||||
// Don't actually render anything since the element is already in the DOM,
|
var componentId;
|
||||||
// but keep track that the node is being preserved so that we can ignore
|
|
||||||
// it while transforming the old DOM
|
|
||||||
|
|
||||||
if (!bodyOnly) {
|
if (key) {
|
||||||
var tagName = existingEl.tagName;
|
if (component.___keyedElements[key]) {
|
||||||
// If we are preserving the entire DOM node (not just the body)
|
var bodyOnly = input.bodyOnly === true;
|
||||||
// then that means that we have need to render a placeholder to
|
// Don't actually render anything since the element is already in the DOM,
|
||||||
// mark the target location. We can then replace the placeholder
|
// but keep track that the node is being preserved so that we can ignore
|
||||||
// node with the existing DOM node
|
// it while transforming the old DOM
|
||||||
out.element(tagName, { id: id });
|
if (bodyOnly) {
|
||||||
|
globalComponentsContext.___preservedElBodies[key] = true;
|
||||||
|
} else {
|
||||||
|
// If we are preserving the entire DOM node (not just the body)
|
||||||
|
// then that means that we have need to render a placeholder to
|
||||||
|
// mark the target location. We can then replace the placeholder
|
||||||
|
// node with the existing DOM node
|
||||||
|
out.element('', null, key, null, 0, 8 /* FLAG_PRESERVE */);
|
||||||
|
globalComponentsContext.___preservedEls[key] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if ((componentId = input.cid)) {
|
||||||
|
var existingComponent = componentLookup[componentId];
|
||||||
|
if (existingComponent) {
|
||||||
|
out.___preserveComponent(existingComponent);
|
||||||
|
globalComponentsContext.___renderedComponentsById[componentId] = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
globalComponentsContext.___preserveDOMNode(id, bodyOnly);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
var extend = require('raptor-util/extend');
|
|
||||||
|
|
||||||
var markoGlobal = extend(window.$MG, {
|
var markoUID = window.$MUID || (window.$MUID = { i: 0 });
|
||||||
uid: 0
|
var runtimeId = markoUID.i++;
|
||||||
});
|
|
||||||
|
|
||||||
window.$MG = markoGlobal;
|
|
||||||
|
|
||||||
var runtimeId = markoGlobal.uid++;
|
|
||||||
|
|
||||||
var componentLookup = {};
|
var componentLookup = {};
|
||||||
|
|
||||||
@ -17,18 +11,7 @@ function getComponentForEl(el, doc) {
|
|||||||
if (el) {
|
if (el) {
|
||||||
var node = typeof el == 'string' ? (doc || defaultDocument).getElementById(el) : el;
|
var node = typeof el == 'string' ? (doc || defaultDocument).getElementById(el) : el;
|
||||||
if (node) {
|
if (node) {
|
||||||
var component = node._w;
|
return node.___markoComponent;
|
||||||
|
|
||||||
while(component) {
|
|
||||||
var rootFor = component.___rootFor;
|
|
||||||
if (rootFor) {
|
|
||||||
component = rootFor;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,26 +52,26 @@ function emitLifecycleEvent(component, eventType, eventArg1, eventArg2) {
|
|||||||
component.emit(eventType, eventArg1, eventArg2);
|
component.emit(eventType, eventArg1, eventArg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyComponentForEl(el) {
|
function destroyComponentForNode(node) {
|
||||||
var componentToDestroy = el._w;
|
var componentToDestroy = node.___markoComponent;
|
||||||
if (componentToDestroy) {
|
if (componentToDestroy) {
|
||||||
componentToDestroy.___destroyShallow();
|
componentToDestroy.___destroyShallow();
|
||||||
el._w = null;
|
|
||||||
|
|
||||||
while ((componentToDestroy = componentToDestroy.___rootFor)) {
|
|
||||||
componentToDestroy.___rootFor = null;
|
|
||||||
componentToDestroy.___destroyShallow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function destroyElRecursive(el) {
|
function destroyNodeRecursive(node, component) {
|
||||||
var curChild = el.firstChild;
|
if (node.nodeType === 1) {
|
||||||
while(curChild) {
|
var key;
|
||||||
if (curChild.nodeType === 1) {
|
|
||||||
destroyComponentForEl(curChild);
|
if (component && (key = node.___markoKey)) {
|
||||||
destroyElRecursive(curChild);
|
delete component.___keyedElements[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
var curChild = node.firstChild;
|
||||||
|
while(curChild) {
|
||||||
|
destroyComponentForNode(curChild);
|
||||||
|
destroyNodeRecursive(curChild, component);
|
||||||
|
curChild = curChild.nextSibling;
|
||||||
}
|
}
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,52 +80,36 @@ function nextComponentId() {
|
|||||||
// marko runtimes. This allows multiple instances of marko to be
|
// marko runtimes. This allows multiple instances of marko to be
|
||||||
// loaded in the same window and they should all place nice
|
// loaded in the same window and they should all place nice
|
||||||
// together
|
// together
|
||||||
return 'b' + ((markoGlobal.uid)++);
|
return 'b' + (markoUID.i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextComponentIdProvider(out) {
|
function nextComponentIdProvider() {
|
||||||
return nextComponentId;
|
return nextComponentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElementById(doc, id) {
|
|
||||||
return doc.getElementById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
|
function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
|
||||||
if (handlerMethodName) {
|
if (handlerMethodName) {
|
||||||
var id = componentDef.id;
|
var componentId = componentDef.id;
|
||||||
if (extraArgs) {
|
if (extraArgs) {
|
||||||
var isRerenderInBrowser = componentDef.___globalComponentsContext.___isRerenderInBrowser;
|
return [handlerMethodName, componentId, extraArgs];
|
||||||
|
|
||||||
if (isRerenderInBrowser === true) {
|
|
||||||
// If we are bootstrapping a page rendered on the server
|
|
||||||
// we need to put the actual event args on the UI component
|
|
||||||
// since we will not actually be updating the DOM
|
|
||||||
var component = componentDef.___component;
|
|
||||||
|
|
||||||
var bubblingDomEvents = component.___bubblingDomEvents ||
|
|
||||||
( component.___bubblingDomEvents = [] );
|
|
||||||
|
|
||||||
bubblingDomEvents.push(extraArgs);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return [handlerMethodName, id, extraArgs];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return [handlerMethodName, id];
|
return [handlerMethodName, componentId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMarkoPropsFromEl(el) {
|
function getMarkoPropsFromEl(el) {
|
||||||
var virtualProps = el._vprops;
|
var vElement = el.___markoVElement;
|
||||||
if (virtualProps === undefined) {
|
var virtualProps;
|
||||||
virtualProps = el.getAttribute('data-marko');
|
|
||||||
if (virtualProps) {
|
if (vElement) {
|
||||||
virtualProps = JSON.parse(virtualProps);
|
virtualProps = vElement.___properties;
|
||||||
|
} else {
|
||||||
|
virtualProps = el.___markoVProps;
|
||||||
|
if (!virtualProps) {
|
||||||
|
virtualProps = el.getAttribute('data-marko');
|
||||||
|
el.___markoVProps = virtualProps = virtualProps ? JSON.parse(virtualProps) : EMPTY_OBJECT;
|
||||||
}
|
}
|
||||||
el._vprops = virtualProps = virtualProps || EMPTY_OBJECT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return virtualProps;
|
return virtualProps;
|
||||||
@ -152,9 +119,8 @@ exports.___runtimeId = runtimeId;
|
|||||||
exports.___componentLookup = componentLookup;
|
exports.___componentLookup = componentLookup;
|
||||||
exports.___getComponentForEl = getComponentForEl;
|
exports.___getComponentForEl = getComponentForEl;
|
||||||
exports.___emitLifecycleEvent = emitLifecycleEvent;
|
exports.___emitLifecycleEvent = emitLifecycleEvent;
|
||||||
exports.___destroyComponentForEl = destroyComponentForEl;
|
exports.___destroyComponentForNode = destroyComponentForNode;
|
||||||
exports.___destroyElRecursive = destroyElRecursive;
|
exports.___destroyNodeRecursive = destroyNodeRecursive;
|
||||||
exports.___nextComponentIdProvider = nextComponentIdProvider;
|
exports.___nextComponentIdProvider = nextComponentIdProvider;
|
||||||
exports.___getElementById = getElementById;
|
|
||||||
exports.___attachBubblingEvent = attachBubblingEvent;
|
exports.___attachBubblingEvent = attachBubblingEvent;
|
||||||
exports.___getMarkoPropsFromEl = getMarkoPropsFromEl;
|
exports.___getMarkoPropsFromEl = getMarkoPropsFromEl;
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
var FLAG_WILL_RERENDER_IN_BROWSER = 1;
|
||||||
|
// var FLAG_HAS_BODY_EL = 2;
|
||||||
|
// var FLAG_HAS_HEAD_EL = 4;
|
||||||
|
|
||||||
function nextComponentIdProvider(out) {
|
function nextComponentIdProvider(out) {
|
||||||
var prefix = out.global.componentIdPrefix || 's'; // "s" is for server (we use "b" for the browser)
|
var prefix = out.global.componentIdPrefix || 's'; // "s" is for server (we use "b" for the browser)
|
||||||
var nextId = 0;
|
var nextId = 0;
|
||||||
@ -21,7 +25,7 @@ function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
|
|||||||
// where the extra args will be found when the UI component is
|
// where the extra args will be found when the UI component is
|
||||||
// rerendered in the browser
|
// rerendered in the browser
|
||||||
|
|
||||||
if (componentDef.___willRerenderInBrowser === false) {
|
if (componentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER) {
|
||||||
if (eventIndex === 0) {
|
if (eventIndex === 0) {
|
||||||
component.___bubblingDomEvents = [extraArgs];
|
component.___bubblingDomEvents = [extraArgs];
|
||||||
} else {
|
} else {
|
||||||
@ -40,3 +44,5 @@ function attachBubblingEvent(componentDef, handlerMethodName, extraArgs) {
|
|||||||
exports.___nextComponentIdProvider = nextComponentIdProvider;
|
exports.___nextComponentIdProvider = nextComponentIdProvider;
|
||||||
exports.___isServer = true;
|
exports.___isServer = true;
|
||||||
exports.___attachBubblingEvent = attachBubblingEvent;
|
exports.___attachBubblingEvent = attachBubblingEvent;
|
||||||
|
exports.___destroyComponentForNode = function noop() {};
|
||||||
|
exports.___destroyNodeRecursive = function noop() {};
|
||||||
|
|||||||
@ -1,325 +1,539 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var defaultDoc = typeof document == 'undefined' ? undefined : document;
|
|
||||||
var specialElHandlers = require('./specialElHandlers');
|
var specialElHandlers = require('./specialElHandlers');
|
||||||
|
var componentsUtil = require('../components/util');
|
||||||
var morphAttrs = require('../runtime/vdom/VElement').___morphAttrs;
|
var existingComponentLookup = componentsUtil.___componentLookup;
|
||||||
|
var destroyComponentForNode = componentsUtil.___destroyComponentForNode;
|
||||||
|
var destroyNodeRecursive = componentsUtil.___destroyNodeRecursive;
|
||||||
|
var VElement = require('../runtime/vdom/vdom').___VElement;
|
||||||
|
var virtualizeElement = VElement.___virtualize;
|
||||||
|
var morphAttrs = VElement.___morphAttrs;
|
||||||
|
var eventDelegation = require('../components/event-delegation');
|
||||||
|
|
||||||
var ELEMENT_NODE = 1;
|
var ELEMENT_NODE = 1;
|
||||||
var TEXT_NODE = 3;
|
var TEXT_NODE = 3;
|
||||||
var COMMENT_NODE = 8;
|
var COMMENT_NODE = 8;
|
||||||
|
var COMPONENT_NODE = 2;
|
||||||
|
|
||||||
|
// var FLAG_IS_SVG = 1;
|
||||||
|
// var FLAG_IS_TEXTAREA = 2;
|
||||||
|
// var FLAG_SIMPLE_ATTRS = 4;
|
||||||
|
var FLAG_PRESERVE = 8;
|
||||||
|
|
||||||
function compareNodeNames(fromEl, toEl) {
|
function compareNodeNames(fromEl, toEl) {
|
||||||
return fromEl.nodeName === toEl.___nodeName;
|
return fromEl.___nodeName === toEl.___nodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onBeforeNodeDiscarded(node) {
|
||||||
|
return eventDelegation.___handleNodeDetach(node);
|
||||||
|
}
|
||||||
|
|
||||||
function getElementById(doc, id) {
|
function onNodeAdded(node, componentsContext) {
|
||||||
return doc.getElementById(id);
|
if (node.nodeType === 1) {
|
||||||
|
eventDelegation.___handleNodeAttach(node, componentsContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertBefore(node, referenceNode, parentNode) {
|
||||||
|
return parentNode.insertBefore(node, referenceNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertAfter(node, referenceNode, parentNode) {
|
||||||
|
return parentNode.insertBefore(node, referenceNode && referenceNode.nextSibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
function morphdom(
|
function morphdom(
|
||||||
fromNode,
|
parentNode,
|
||||||
|
startNode,
|
||||||
|
endNode,
|
||||||
toNode,
|
toNode,
|
||||||
context,
|
doc,
|
||||||
onNodeAdded,
|
componentsContext
|
||||||
onBeforeElUpdated,
|
|
||||||
onBeforeNodeDiscarded,
|
|
||||||
onNodeDiscarded,
|
|
||||||
onBeforeElChildrenUpdated
|
|
||||||
) {
|
) {
|
||||||
|
var globalComponentsContext;
|
||||||
|
var isRerenderInBrowser = false;
|
||||||
|
|
||||||
var doc = fromNode.ownerDocument || defaultDoc;
|
if (componentsContext) {
|
||||||
|
globalComponentsContext = componentsContext.___globalContext;
|
||||||
// This object is used as a lookup to quickly find all keyed elements in the original DOM tree.
|
isRerenderInBrowser = globalComponentsContext.___isRerenderInBrowser;
|
||||||
var removalList = [];
|
|
||||||
var foundKeys = {};
|
|
||||||
|
|
||||||
function walkDiscardedChildNodes(node) {
|
|
||||||
onNodeDiscarded(node);
|
|
||||||
var curChild = node.firstChild;
|
|
||||||
|
|
||||||
while (curChild) {
|
|
||||||
walkDiscardedChildNodes(curChild);
|
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createMarkerComment(referenceNode, parentNode) {
|
||||||
|
return doc.createComment('$marko');
|
||||||
|
}
|
||||||
|
|
||||||
function addVirtualNode(vEl, parentEl) {
|
function insertVirtualNodeBefore(vNode, key, referenceEl, parentEl, component, keySequence) {
|
||||||
var realEl = vEl.___actualize(doc);
|
var realNode = vNode.___actualize(doc);
|
||||||
|
insertBefore(realNode, referenceEl, parentEl);
|
||||||
|
|
||||||
if (parentEl) {
|
if (vNode.___nodeType === ELEMENT_NODE) {
|
||||||
parentEl.appendChild(realEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
onNodeAdded(realEl, context);
|
|
||||||
|
|
||||||
var vCurChild = vEl.___firstChild;
|
|
||||||
while (vCurChild) {
|
|
||||||
var realCurChild = null;
|
|
||||||
|
|
||||||
var key = vCurChild.id;
|
|
||||||
if (key) {
|
if (key) {
|
||||||
var unmatchedFromEl = getElementById(doc, key);
|
realNode.___markoKey = key;
|
||||||
if (unmatchedFromEl && compareNodeNames(vCurChild, unmatchedFromEl)) {
|
(component = vNode.___component || component).___keyedElements[key] = realNode;
|
||||||
morphEl(unmatchedFromEl, vCurChild, false);
|
|
||||||
realEl.appendChild(realCurChild = unmatchedFromEl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!realCurChild) {
|
morphChildren(realNode, null, null, vNode, component, keySequence);
|
||||||
addVirtualNode(vCurChild, realEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
vCurChild = vCurChild.___nextSibling;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vEl.___nodeType === 1) {
|
onNodeAdded(realNode, componentsContext);
|
||||||
var elHandler = specialElHandlers[vEl.nodeName];
|
|
||||||
if (elHandler !== undefined) {
|
|
||||||
elHandler(realEl, vEl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return realEl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function morphEl(fromEl, toEl, childrenOnly) {
|
function insertVirtualComponentBefore(vComponent, referenceNode, referenceNodeParentEl) {
|
||||||
var toElKey = toEl.id;
|
var component = vComponent.___component;
|
||||||
var nodeName = toEl.___nodeName;
|
component.___startNode = component.___endNode = insertBefore(createMarkerComment(), referenceNode, referenceNodeParentEl);
|
||||||
|
morphComponent(referenceNodeParentEl, component, vComponent);
|
||||||
|
}
|
||||||
|
|
||||||
if (childrenOnly === false) {
|
function resolveComponentEndNode(startNode, vChild, parentNode) {
|
||||||
if (toElKey) {
|
var endNode = startNode;
|
||||||
// If an element with an ID is being morphed then it is will be in the final
|
|
||||||
// DOM so clear it out of the saved elements collection
|
// We track text nodes because multiple adjacent VText nodes should
|
||||||
foundKeys[toElKey] = true;
|
// be treated as a single VText node for purposes of pairing with HTML
|
||||||
|
// that was rendered on the server since browsers will only see
|
||||||
|
// a single text node
|
||||||
|
var isPrevText = vChild.___nodeType === TEXT_NODE;
|
||||||
|
|
||||||
|
while((vChild = vChild.___nextSibling)) {
|
||||||
|
var nextRealNode = endNode.nextSibling;
|
||||||
|
|
||||||
|
// We stop when there are no more corresponding real nodes or when
|
||||||
|
// we reach the end boundary for our UI component
|
||||||
|
if (!nextRealNode || nextRealNode.___endNode) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
var isText = vChild.___nodeType === TEXT_NODE;
|
||||||
|
if (isText && isPrevText) {
|
||||||
|
// Pretend like we didn't see this VText node since it
|
||||||
|
// the previous vnode was also a VText node
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endNode = nextRealNode;
|
||||||
|
isPrevText = isText;
|
||||||
|
}
|
||||||
|
|
||||||
var constId = toEl.___constId;
|
return endNode;
|
||||||
if (constId !== undefined) {
|
}
|
||||||
var otherProps = fromEl._vprops;
|
|
||||||
if (otherProps !== undefined && constId === otherProps.c) {
|
function morphComponent(parentFromNode, component, vComponent) {
|
||||||
return;
|
// We create a key sequence to generate unique keys since a key
|
||||||
|
// can be repeated
|
||||||
|
var keySequence = globalComponentsContext.___createKeySequence();
|
||||||
|
|
||||||
|
var startNode = component.___startNode;
|
||||||
|
var endNode = component.___endNode;
|
||||||
|
startNode.___markoComponent = undefined;
|
||||||
|
endNode.___endNode = undefined;
|
||||||
|
|
||||||
|
var beforeChild = startNode.previousSibling;
|
||||||
|
var afterChild = endNode.nextSibling;
|
||||||
|
|
||||||
|
morphChildren(parentFromNode, startNode, afterChild, vComponent, component, keySequence);
|
||||||
|
|
||||||
|
endNode = undefined;
|
||||||
|
|
||||||
|
if (beforeChild) {
|
||||||
|
startNode = beforeChild.nextSibling;
|
||||||
|
if (!startNode || startNode === afterChild) {
|
||||||
|
startNode = endNode = insertAfter(createMarkerComment(), beforeChild, parentFromNode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startNode = parentFromNode.firstChild;
|
||||||
|
if (!startNode) {
|
||||||
|
startNode = endNode = insertAfter(createMarkerComment(), beforeChild, parentFromNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endNode) {
|
||||||
|
if (afterChild) {
|
||||||
|
endNode = afterChild.previousSibling;
|
||||||
|
} else {
|
||||||
|
endNode = parentFromNode.lastChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't use a detached node as the component boundary and
|
||||||
|
// we can't use a node that is already the boundary node for another component
|
||||||
|
if (startNode.___markoDetached !== undefined || startNode.___markoComponent) {
|
||||||
|
startNode = insertBefore(createMarkerComment(), startNode, parentFromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endNode.___markoDetached !== undefined || endNode.___endNode) {
|
||||||
|
endNode = insertAfter(createMarkerComment(), endNode, parentFromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
startNode.___markoComponent = component;
|
||||||
|
endNode.___endNode = true;
|
||||||
|
|
||||||
|
component.___startNode = startNode;
|
||||||
|
component.___endNode = endNode;
|
||||||
|
|
||||||
|
return afterChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
var detachedNodes = [];
|
||||||
|
|
||||||
|
function detachNode(node, parentNode, component) {
|
||||||
|
if (node.nodeType === ELEMENT_NODE) {
|
||||||
|
detachedNodes.push(node);
|
||||||
|
node.___markoDetached = component || true;
|
||||||
|
} else {
|
||||||
|
destroyNodeRecursive(node);
|
||||||
|
parentNode.removeChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyComponent(component) {
|
||||||
|
component.destroy(onBeforeNodeDiscarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
function morphChildren(parentFromNode, startNode, endNode, toNode, component, keySequence) {
|
||||||
|
var curFromNodeChild = startNode;
|
||||||
|
var curToNodeChild = toNode.___firstChild;
|
||||||
|
|
||||||
|
var curToNodeKey;
|
||||||
|
var curFromNodeKey;
|
||||||
|
var curToNodeType;
|
||||||
|
|
||||||
|
var fromNextSibling;
|
||||||
|
var toNextSibling;
|
||||||
|
var matchingFromEl;
|
||||||
|
var matchingFromComponent;
|
||||||
|
var toComponent;
|
||||||
|
var curVFromNodeChild;
|
||||||
|
var fromComponent;
|
||||||
|
|
||||||
|
outer: while (curToNodeChild) {
|
||||||
|
toNextSibling = curToNodeChild.___nextSibling;
|
||||||
|
curToNodeType = curToNodeChild.___nodeType;
|
||||||
|
|
||||||
|
if (curToNodeType === COMPONENT_NODE) {
|
||||||
|
toComponent = curToNodeChild.___component;
|
||||||
|
|
||||||
|
if ((matchingFromComponent = existingComponentLookup[toComponent.id]) === undefined) {
|
||||||
|
if (isRerenderInBrowser === true) {
|
||||||
|
var firstVChild = curToNodeChild.___firstChild;
|
||||||
|
if (firstVChild) {
|
||||||
|
if (!curFromNodeChild) {
|
||||||
|
curFromNodeChild = insertBefore(createMarkerComment(), null, parentFromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
toComponent.___startNode = curFromNodeChild;
|
||||||
|
toComponent.___endNode = resolveComponentEndNode(curFromNodeChild, firstVChild, parentFromNode);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toComponent.___startNode = toComponent.___endNode = insertBefore(createMarkerComment(), curFromNodeChild, parentFromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
curFromNodeChild = morphComponent(parentFromNode, toComponent, curToNodeChild);
|
||||||
|
} else {
|
||||||
|
insertVirtualComponentBefore(curToNodeChild, curFromNodeChild, parentFromNode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (matchingFromComponent.___startNode !== curFromNodeChild) {
|
||||||
|
if (curFromNodeChild &&
|
||||||
|
(fromComponent = curFromNodeChild.___markoComponent) &&
|
||||||
|
globalComponentsContext.___renderedComponentsById[fromComponent.id] === undefined) {
|
||||||
|
|
||||||
|
// The component associated with the current real DOM node was not rendered
|
||||||
|
// so we should just remove it out of the real DOM by destroying it
|
||||||
|
curFromNodeChild = fromComponent.___endNode.nextSibling;
|
||||||
|
destroyComponent(fromComponent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to move the existing component into
|
||||||
|
// the correct location
|
||||||
|
insertBefore(matchingFromComponent.___detach(), curFromNodeChild, parentFromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curToNodeChild.___preserve) {
|
||||||
|
curFromNodeChild = matchingFromComponent.___endNode.nextSibling;
|
||||||
|
} else {
|
||||||
|
curFromNodeChild = morphComponent(parentFromNode, toComponent, curToNodeChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (onBeforeElUpdated(fromEl, toElKey, context) === true) {
|
curToNodeChild = toNextSibling;
|
||||||
return;
|
continue;
|
||||||
}
|
} else if ((curToNodeKey = curToNodeChild.___key)) {
|
||||||
|
curVFromNodeChild = undefined;
|
||||||
|
curFromNodeKey = undefined;
|
||||||
|
|
||||||
morphAttrs(fromEl, toEl);
|
// We have a keyed element. This is the fast path for matching
|
||||||
}
|
// up elements
|
||||||
|
curToNodeKey = keySequence.___nextKey(curToNodeKey);
|
||||||
|
|
||||||
|
|
||||||
if (onBeforeElChildrenUpdated(fromEl, toElKey, context) === true) {
|
if (curFromNodeChild) {
|
||||||
return;
|
if (curFromNodeChild !== endNode) {
|
||||||
}
|
curFromNodeKey = curFromNodeChild.___markoKey;
|
||||||
|
curVFromNodeChild = curFromNodeChild.___markoVElement;
|
||||||
|
fromNextSibling = curFromNodeChild.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeName !== 'TEXTAREA') {
|
if (curFromNodeKey === curToNodeKey) {
|
||||||
var curToNodeChild = toEl.___firstChild;
|
// Elements line up. Now we just have to make sure they are compatible
|
||||||
var curFromNodeChild = fromEl.firstChild;
|
if ((curToNodeChild.___flags & FLAG_PRESERVE) === 0) {
|
||||||
var curToNodeKey;
|
// We just skip over the fromNode if it is preserved
|
||||||
var curFromNodeKey;
|
|
||||||
|
|
||||||
var fromNextSibling;
|
|
||||||
var toNextSibling;
|
|
||||||
var matchingFromEl;
|
|
||||||
|
|
||||||
outer: while (curToNodeChild) {
|
|
||||||
toNextSibling = curToNodeChild.___nextSibling;
|
|
||||||
curToNodeKey = curToNodeChild.id;
|
|
||||||
|
|
||||||
while (curFromNodeChild) {
|
|
||||||
fromNextSibling = curFromNodeChild.nextSibling;
|
|
||||||
|
|
||||||
curFromNodeKey = curFromNodeChild.id;
|
|
||||||
|
|
||||||
var curFromNodeType = curFromNodeChild.nodeType;
|
|
||||||
|
|
||||||
var isCompatible = undefined;
|
|
||||||
|
|
||||||
if (curFromNodeType === curToNodeChild.___nodeType) {
|
|
||||||
if (curFromNodeType === ELEMENT_NODE) {
|
|
||||||
// Both nodes being compared are Element nodes
|
|
||||||
|
|
||||||
if (curToNodeKey) {
|
|
||||||
// The target node has a key so we want to match it up with the correct element
|
|
||||||
// in the original DOM tree
|
|
||||||
if (curToNodeKey !== curFromNodeKey) {
|
|
||||||
// The current element in the original DOM tree does not have a matching key so
|
|
||||||
// let's check our lookup to see if there is a matching element in the original
|
|
||||||
// DOM tree
|
|
||||||
if ((matchingFromEl = getElementById(doc, curToNodeKey))) {
|
|
||||||
if (curFromNodeChild.nextSibling === matchingFromEl) {
|
|
||||||
// Special case for single element removals. To avoid removing the original
|
|
||||||
// DOM node out of the tree (since that can break CSS transitions, etc.),
|
|
||||||
// we will instead discard the current node and wait until the next
|
|
||||||
// iteration to properly match up the keyed target element with its matching
|
|
||||||
// element in the original tree
|
|
||||||
isCompatible = false;
|
|
||||||
} else {
|
|
||||||
// We found a matching keyed element somewhere in the original DOM tree.
|
|
||||||
// Let's moving the original DOM node into the current position and morph
|
|
||||||
// it.
|
|
||||||
|
|
||||||
// NOTE: We use insertBefore instead of replaceChild because we want to go through
|
|
||||||
// the `removeNode()` function for the node that is being discarded so that
|
|
||||||
// all lifecycle hooks are correctly invoked
|
|
||||||
|
|
||||||
|
|
||||||
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
|
if (compareNodeNames(curToNodeChild, curVFromNodeChild)) {
|
||||||
|
morphEl(curFromNodeChild, curVFromNodeChild, curToNodeChild, component, keySequence);
|
||||||
|
} else {
|
||||||
|
// Remove the old node
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
|
||||||
fromNextSibling = curFromNodeChild.nextSibling;
|
// Incompatible nodes. Just move the target VNode into the DOM at this position
|
||||||
removalList.push(curFromNodeChild);
|
insertVirtualNodeBefore(curToNodeChild, curToNodeKey, curFromNodeChild, parentFromNode, component, keySequence);
|
||||||
|
|
||||||
curFromNodeChild = matchingFromEl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The nodes are not compatible since the "to" node has a key and there
|
|
||||||
// is no matching keyed node in the source tree
|
|
||||||
isCompatible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (curFromNodeKey) {
|
|
||||||
// The original has a key
|
|
||||||
isCompatible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild) === true;
|
|
||||||
|
|
||||||
if (isCompatible === true) {
|
|
||||||
// We found compatible DOM elements so transform
|
|
||||||
// the current "from" node to match the current
|
|
||||||
// target DOM node.
|
|
||||||
morphEl(curFromNodeChild, curToNodeChild, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (curFromNodeType === TEXT_NODE || curFromNodeType === COMMENT_NODE) {
|
|
||||||
// Both nodes being compared are Text or Comment nodes
|
|
||||||
isCompatible = true;
|
|
||||||
// Simply update nodeValue on the original node to
|
|
||||||
// change the text value
|
|
||||||
curFromNodeChild.nodeValue = curToNodeChild.___nodeValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCompatible === true) {
|
|
||||||
// Advance both the "to" child and the "from" child since we found a match
|
|
||||||
curToNodeChild = toNextSibling;
|
|
||||||
curFromNodeChild = fromNextSibling;
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No compatible match so remove the old node from the DOM and continue trying to find a
|
|
||||||
// match in the original DOM. However, we only do this if the from node is not keyed
|
|
||||||
// since it is possible that a keyed node might match up with a node somewhere else in the
|
|
||||||
// target tree and we don't want to discard it just yet since it still might find a
|
|
||||||
// home in the final DOM tree. After everything is done we will remove any keyed nodes
|
|
||||||
// that didn't find a home
|
|
||||||
removalList.push(curFromNodeChild);
|
|
||||||
|
|
||||||
curFromNodeChild = fromNextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got this far then we did not find a candidate match for
|
|
||||||
// our "to node" and we exhausted all of the children "from"
|
|
||||||
// nodes. Therefore, we will just append the current "to" node
|
|
||||||
// to the end
|
|
||||||
if (curToNodeKey && (matchingFromEl = getElementById(doc, curToNodeKey)) && compareNodeNames(matchingFromEl, curToNodeChild)) {
|
|
||||||
fromEl.appendChild(matchingFromEl);
|
|
||||||
morphEl(matchingFromEl, curToNodeChild, false);
|
|
||||||
} else {
|
} else {
|
||||||
addVirtualNode(curToNodeChild, fromEl);
|
if ((matchingFromEl = component.___keyedElements[curToNodeKey]) === undefined) {
|
||||||
|
if (isRerenderInBrowser === true && curFromNodeChild &&
|
||||||
|
curFromNodeChild.nodeType === ELEMENT_NODE &&
|
||||||
|
curFromNodeChild.nodeName === curToNodeChild.___nodeName) {
|
||||||
|
curVFromNodeChild = virtualizeElement(curFromNodeChild);
|
||||||
|
curFromNodeChild.___markoKey = curToNodeKey;
|
||||||
|
morphEl(curFromNodeChild, curVFromNodeChild, curToNodeChild, component, keySequence);
|
||||||
|
curToNodeChild = toNextSibling;
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertVirtualNodeBefore(curToNodeChild, curToNodeKey, curFromNodeChild, parentFromNode, component, keySequence);
|
||||||
|
fromNextSibling = curFromNodeChild;
|
||||||
|
} else {
|
||||||
|
if (matchingFromEl.___markoDetached !== undefined) {
|
||||||
|
matchingFromEl.___markoDetached = undefined;
|
||||||
|
}
|
||||||
|
curVFromNodeChild = matchingFromEl.___markoVElement;
|
||||||
|
|
||||||
|
if (compareNodeNames(curVFromNodeChild, curToNodeChild)) {
|
||||||
|
if (fromNextSibling === matchingFromEl) {
|
||||||
|
// Single element removal:
|
||||||
|
// A <-> A
|
||||||
|
// B <-> C <-- We are here
|
||||||
|
// C D
|
||||||
|
// D
|
||||||
|
//
|
||||||
|
// Single element swap:
|
||||||
|
// A <-> A
|
||||||
|
// B <-> C <-- We are here
|
||||||
|
// C B
|
||||||
|
|
||||||
|
if (toNextSibling && toNextSibling.___key === curFromNodeKey) {
|
||||||
|
// Single element swap
|
||||||
|
|
||||||
|
// We want to stay on the current real DOM node
|
||||||
|
fromNextSibling = curFromNodeChild;
|
||||||
|
|
||||||
|
// But move the matching element into place
|
||||||
|
insertBefore(matchingFromEl, curFromNodeChild, parentFromNode);
|
||||||
|
} else {
|
||||||
|
// Single element removal
|
||||||
|
|
||||||
|
// We need to remove the current real DOM node
|
||||||
|
// and the matching real DOM node will fall into
|
||||||
|
// place. We will continue diffing with next sibling
|
||||||
|
// after the real DOM node that just fell into place
|
||||||
|
fromNextSibling = fromNextSibling.nextSibling;
|
||||||
|
|
||||||
|
if (curFromNodeChild) {
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// A <-> A
|
||||||
|
// B <-> D <-- We are here
|
||||||
|
// C
|
||||||
|
// D
|
||||||
|
|
||||||
|
// We need to move the matching node into place
|
||||||
|
insertAfter(matchingFromEl, curFromNodeChild, parentFromNode);
|
||||||
|
|
||||||
|
if (curFromNodeChild) {
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((curToNodeChild.___flags & FLAG_PRESERVE) === 0) {
|
||||||
|
morphEl(matchingFromEl, curVFromNodeChild, curToNodeChild, component, keySequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
insertVirtualNodeBefore(curToNodeChild, curToNodeKey, curFromNodeChild, parentFromNode, component, keySequence);
|
||||||
|
detachNode(matchingFromEl, parentFromNode, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
curToNodeChild = toNextSibling;
|
curToNodeChild = toNextSibling;
|
||||||
curFromNodeChild = fromNextSibling;
|
curFromNodeChild = fromNextSibling;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have processed all of the "to nodes". If curFromNodeChild is
|
// The know the target node is not a VComponent node and we know
|
||||||
// non-null then we still have some from nodes left over that need
|
// it is also not a preserve node. Let's now match up the HTML
|
||||||
// to be removed
|
// element, text node, comment, etc.
|
||||||
while (curFromNodeChild) {
|
while (curFromNodeChild && curFromNodeChild !== endNode) {
|
||||||
removalList.push(curFromNodeChild);
|
if ((fromComponent = curFromNodeChild.___markoComponent) && fromComponent !== component) {
|
||||||
curFromNodeChild = curFromNodeChild.nextSibling;
|
// The current "to" element is not associated with a component,
|
||||||
|
// but the current "from" element is associated with a component
|
||||||
|
|
||||||
|
// Even if we destroy the current component in the original
|
||||||
|
// DOM or not, we still need to skip over it since it is
|
||||||
|
// not compatible with the current "to" node
|
||||||
|
curFromNodeChild = fromComponent.___endNode.nextSibling;
|
||||||
|
|
||||||
|
if (!globalComponentsContext.___renderedComponentsById[fromComponent.id]) {
|
||||||
|
destroyComponent(fromComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue; // Move to the next "from" node
|
||||||
|
}
|
||||||
|
|
||||||
|
fromNextSibling = curFromNodeChild.nextSibling;
|
||||||
|
|
||||||
|
var curFromNodeType = curFromNodeChild.nodeType;
|
||||||
|
|
||||||
|
var isCompatible = undefined;
|
||||||
|
|
||||||
|
if (curFromNodeType === curToNodeType) {
|
||||||
|
if (curFromNodeType === ELEMENT_NODE) {
|
||||||
|
// Both nodes being compared are Element nodes
|
||||||
|
curVFromNodeChild = curFromNodeChild.___markoVElement;
|
||||||
|
if (curVFromNodeChild === undefined) {
|
||||||
|
if (isRerenderInBrowser === true) {
|
||||||
|
curVFromNodeChild = virtualizeElement(curFromNodeChild);
|
||||||
|
} else {
|
||||||
|
// Skip over nodes that don't look like ours...
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if ((curFromNodeKey = curVFromNodeChild.___key)) {
|
||||||
|
// We have a keyed element here but our target VDOM node
|
||||||
|
// is not keyed so this not doesn't belong
|
||||||
|
isCompatible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCompatible = isCompatible !== false && compareNodeNames(curVFromNodeChild, curToNodeChild) === true;
|
||||||
|
|
||||||
|
if (isCompatible === true) {
|
||||||
|
// We found compatible DOM elements so transform
|
||||||
|
// the current "from" node to match the current
|
||||||
|
// target DOM node.
|
||||||
|
morphEl(curFromNodeChild, curVFromNodeChild, curToNodeChild, component, keySequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (curFromNodeType === TEXT_NODE || curFromNodeType === COMMENT_NODE) {
|
||||||
|
// Both nodes being compared are Text or Comment nodes
|
||||||
|
isCompatible = true;
|
||||||
|
// Simply update nodeValue on the original node to
|
||||||
|
// change the text value
|
||||||
|
curFromNodeChild.nodeValue = curToNodeChild.___nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCompatible === true) {
|
||||||
|
// Advance both the "to" child and the "from" child since we found a match
|
||||||
|
curToNodeChild = toNextSibling;
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curFromNodeKey) {
|
||||||
|
if (globalComponentsContext.___preservedEls[curFromNodeKey] === undefined) {
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
} // END: while (curFromNodeChild)
|
||||||
|
|
||||||
|
// If we got this far then we did not find a candidate match for
|
||||||
|
// our "to node" and we exhausted all of the children "from"
|
||||||
|
// nodes. Therefore, we will just append the current "to" node
|
||||||
|
// to the end
|
||||||
|
insertVirtualNodeBefore(curToNodeChild, curToNodeKey, curFromNodeChild, parentFromNode, component, keySequence);
|
||||||
|
|
||||||
|
curToNodeChild = toNextSibling;
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have processed all of the "to nodes". If curFromNodeChild is
|
||||||
|
// non-null then we still have some from nodes left over that need
|
||||||
|
// to be removed
|
||||||
|
while (curFromNodeChild && (endNode === null || curFromNodeChild !== endNode)) {
|
||||||
|
fromNextSibling = curFromNodeChild.nextSibling;
|
||||||
|
|
||||||
|
if ((fromComponent = curFromNodeChild.___markoComponent)) {
|
||||||
|
if (globalComponentsContext.___renderedComponentsById[fromComponent.id]) {
|
||||||
|
// Skip over this component since it was rendered in the target VDOM
|
||||||
|
// and will be moved into place later
|
||||||
|
curFromNodeChild = fromComponent.___endNode.nextSibling;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detachNode(curFromNodeChild, parentFromNode, component);
|
||||||
|
|
||||||
|
curFromNodeChild = fromNextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function morphEl(fromEl, vFromEl, toEl, component, keySequence) {
|
||||||
|
var toElKey = toEl.___key;
|
||||||
|
var nodeName = toEl.___nodeName;
|
||||||
|
|
||||||
|
component = toEl.___component || component;
|
||||||
|
|
||||||
|
if (isRerenderInBrowser === true && toElKey) {
|
||||||
|
component.___keyedElements[toElKey] = fromEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
var constId = toEl.___constId;
|
||||||
|
if (constId !== undefined && vFromEl.___constId === constId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
morphAttrs(fromEl, vFromEl, toEl);
|
||||||
|
|
||||||
|
if (toElKey && globalComponentsContext.___preservedElBodies[toElKey] === true) {
|
||||||
|
// Don't morph the children since they are preserved
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeName !== 'TEXTAREA') {
|
||||||
|
morphChildren(fromEl, fromEl.firstChild, null, toEl, component, keySequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
var specialElHandler = specialElHandlers[nodeName];
|
var specialElHandler = specialElHandlers[nodeName];
|
||||||
if (specialElHandler) {
|
if (specialElHandler !== undefined) {
|
||||||
specialElHandler(fromEl, toEl);
|
specialElHandler(fromEl, toEl);
|
||||||
}
|
}
|
||||||
} // END: morphEl(...)
|
} // END: morphEl(...)
|
||||||
|
|
||||||
var morphedNode = fromNode;
|
morphChildren(parentNode, startNode, endNode, toNode);
|
||||||
var fromNodeType = morphedNode.nodeType;
|
|
||||||
var toNodeType = toNode.___nodeType;
|
|
||||||
var morphChildrenOnly = false;
|
|
||||||
var shouldMorphEl = true;
|
|
||||||
var newNode;
|
|
||||||
|
|
||||||
// Handle the case where we are given two DOM nodes that are not
|
detachedNodes.forEach(function(node) {
|
||||||
// compatible (e.g. <div> --> <span> or <div> --> TEXT)
|
var detachedFromComponent = node.___markoDetached;
|
||||||
if (fromNodeType == ELEMENT_NODE) {
|
|
||||||
if (toNodeType == ELEMENT_NODE) {
|
|
||||||
if (!compareNodeNames(fromNode, toNode)) {
|
|
||||||
newNode = toNode.___actualize(doc);
|
|
||||||
morphChildrenOnly = true;
|
|
||||||
removalList.push(fromNode);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Going from an element node to a text or comment node
|
|
||||||
removalList.push(fromNode);
|
|
||||||
newNode = toNode.___actualize(doc);
|
|
||||||
shouldMorphEl = false;
|
|
||||||
}
|
|
||||||
} else if (fromNodeType == TEXT_NODE || fromNodeType == COMMENT_NODE) { // Text or comment node
|
|
||||||
if (toNodeType == fromNodeType) {
|
|
||||||
morphedNode.nodeValue = toNode.___nodeValue;
|
|
||||||
return morphedNode;
|
|
||||||
} else {
|
|
||||||
// Text node to something else
|
|
||||||
removalList.push(fromNode);
|
|
||||||
newNode = addVirtualNode(toNode);
|
|
||||||
shouldMorphEl = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldMorphEl === true) {
|
if (detachedFromComponent !== undefined) {
|
||||||
morphEl(newNode || morphedNode, toNode, morphChildrenOnly);
|
node.___markoDetached = undefined;
|
||||||
}
|
|
||||||
|
|
||||||
if (newNode) {
|
destroyComponentForNode(node);
|
||||||
if (fromNode.parentNode) {
|
destroyNodeRecursive(node, detachedFromComponent !== true && detachedFromComponent);
|
||||||
fromNode.parentNode.replaceChild(newNode, fromNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now need to loop over any keyed nodes that might need to be
|
if (onBeforeNodeDiscarded(node) != false) {
|
||||||
// removed. We only do the removal if we know that the keyed node
|
node.parentNode.removeChild(node);
|
||||||
// never found a match. When a keyed node is matched up we remove
|
|
||||||
// it out of fromNodesLookup and we use fromNodesLookup to determine
|
|
||||||
// if a keyed node has been matched up or not
|
|
||||||
for (var i=0, len=removalList.length; i<len; i++) {
|
|
||||||
var node = removalList[i];
|
|
||||||
var key = node.id;
|
|
||||||
if (!key || foundKeys[key] === undefined) {
|
|
||||||
var parentNode = node.parentNode;
|
|
||||||
if (parentNode !== null || node === fromNode) {
|
|
||||||
if (onBeforeNodeDiscarded(node) == false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentNode !== null) {
|
|
||||||
parentNode.removeChild(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
walkDiscardedChildNodes(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return newNode || morphedNode;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = morphdom;
|
module.exports = morphdom;
|
||||||
|
|||||||
@ -9,7 +9,9 @@ function syncBooleanAttrProp(fromEl, toEl, name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
// We use a JavaScript class to benefit from fast property lookup
|
||||||
|
function SpecialElHandlers() {}
|
||||||
|
SpecialElHandlers.prototype = {
|
||||||
/**
|
/**
|
||||||
* Needed for IE. Apparently IE doesn't think that "selected" is an
|
* Needed for IE. Apparently IE doesn't think that "selected" is an
|
||||||
* attribute when reading over the attributes using selectEl.attributes
|
* attribute when reading over the attributes using selectEl.attributes
|
||||||
@ -27,8 +29,8 @@ module.exports = {
|
|||||||
syncBooleanAttrProp(fromEl, toEl, 'checked');
|
syncBooleanAttrProp(fromEl, toEl, 'checked');
|
||||||
syncBooleanAttrProp(fromEl, toEl, 'disabled');
|
syncBooleanAttrProp(fromEl, toEl, 'disabled');
|
||||||
|
|
||||||
if (fromEl.value != toEl.value) {
|
if (fromEl.value != toEl.___value) {
|
||||||
fromEl.value = toEl.value;
|
fromEl.value = toEl.___value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!toEl.___hasAttribute('value')) {
|
if (!toEl.___hasAttribute('value')) {
|
||||||
@ -37,7 +39,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
TEXTAREA: function(fromEl, toEl) {
|
TEXTAREA: function(fromEl, toEl) {
|
||||||
var newValue = toEl.value;
|
var newValue = toEl.___value;
|
||||||
if (fromEl.value != newValue) {
|
if (fromEl.value != newValue) {
|
||||||
fromEl.value = newValue;
|
fromEl.value = newValue;
|
||||||
}
|
}
|
||||||
@ -75,3 +77,5 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = new SpecialElHandlers();
|
||||||
|
|||||||
@ -41,9 +41,9 @@ var proto = RenderResult.prototype = {
|
|||||||
|
|
||||||
afterInsert: function(doc) {
|
afterInsert: function(doc) {
|
||||||
var out = this.___out;
|
var out = this.___out;
|
||||||
var globalComponentsContext = out.global.___components;
|
var componentsContext = out.___components;
|
||||||
if (globalComponentsContext) {
|
if (componentsContext) {
|
||||||
this.___components = globalComponentsContext.___initComponents(doc);
|
this.___components = componentsContext.___initComponents(doc);
|
||||||
} else {
|
} else {
|
||||||
this.___components = null;
|
this.___components = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
var extend = require('raptor-util/extend');
|
var extend = require('raptor-util/extend');
|
||||||
var componentsUtil = require('../components/util');
|
var componentsUtil = require('../components/util');
|
||||||
var destroyComponentForEl = componentsUtil.___destroyComponentForEl;
|
var destroyComponentForNode = componentsUtil.___destroyComponentForNode;
|
||||||
var destroyElRecursive = componentsUtil.___destroyElRecursive;
|
var destroyNodeRecursive = componentsUtil.___destroyNodeRecursive;
|
||||||
|
|
||||||
function resolveEl(el) {
|
function resolveEl(el) {
|
||||||
if (typeof el == 'string') {
|
if (typeof el == 'string') {
|
||||||
@ -15,8 +15,8 @@ function resolveEl(el) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function beforeRemove(referenceEl) {
|
function beforeRemove(referenceEl) {
|
||||||
destroyElRecursive(referenceEl);
|
destroyNodeRecursive(referenceEl);
|
||||||
destroyComponentForEl(referenceEl);
|
destroyComponentForNode(referenceEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(target, getEl, afterInsert) {
|
module.exports = function(target, getEl, afterInsert) {
|
||||||
@ -47,9 +47,7 @@ module.exports = function(target, getEl, afterInsert) {
|
|||||||
var curChild = referenceEl.firstChild;
|
var curChild = referenceEl.firstChild;
|
||||||
while(curChild) {
|
while(curChild) {
|
||||||
var nextSibling = curChild.nextSibling; // Just in case the DOM changes while removing
|
var nextSibling = curChild.nextSibling; // Just in case the DOM changes while removing
|
||||||
if (curChild.nodeType == 1) {
|
beforeRemove(curChild);
|
||||||
beforeRemove(curChild);
|
|
||||||
}
|
|
||||||
curChild = nextSibling;
|
curChild = nextSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,7 @@ function AsyncStream(global, writer, state, shouldBuffer) {
|
|||||||
this._elStack = undefined; // Array
|
this._elStack = undefined; // Array
|
||||||
|
|
||||||
this.___componentArgs = null; // Component args
|
this.___componentArgs = null; // Component args
|
||||||
|
this.___components = null; // ComponentsContext
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStream.DEFAULT_TIMEOUT = 10000;
|
AsyncStream.DEFAULT_TIMEOUT = 10000;
|
||||||
|
|||||||
@ -4,13 +4,14 @@ var VElement = vdom.___VElement;
|
|||||||
var VDocumentFragment = vdom.___VDocumentFragment;
|
var VDocumentFragment = vdom.___VDocumentFragment;
|
||||||
var VComment = vdom.___VComment;
|
var VComment = vdom.___VComment;
|
||||||
var VText = vdom.___VText;
|
var VText = vdom.___VText;
|
||||||
|
var VComponent = vdom.___VComponent;
|
||||||
var virtualizeHTML = vdom.___virtualizeHTML;
|
var virtualizeHTML = vdom.___virtualizeHTML;
|
||||||
var RenderResult = require('../RenderResult');
|
var RenderResult = require('../RenderResult');
|
||||||
var defaultDocument = vdom.___defaultDocument;
|
var defaultDocument = vdom.___defaultDocument;
|
||||||
|
var morphdom = require('../../morphdom');
|
||||||
|
|
||||||
var FLAG_FINISHED = 1;
|
var FLAG_FINISHED = 1;
|
||||||
var FLAG_LAST_FIRED = 2;
|
var FLAG_LAST_FIRED = 2;
|
||||||
|
|
||||||
var EVENT_UPDATE = 'update';
|
var EVENT_UPDATE = 'update';
|
||||||
var EVENT_FINISH = 'finish';
|
var EVENT_FINISH = 'finish';
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ function AsyncVDOMBuilder(globalData, parentNode, state) {
|
|||||||
this.___stack = [parentNode];
|
this.___stack = [parentNode];
|
||||||
this.___sync = false;
|
this.___sync = false;
|
||||||
this.___vnode = undefined;
|
this.___vnode = undefined;
|
||||||
|
this.___components = null;
|
||||||
this.___componentArgs = null; // Component args
|
this.___componentArgs = null; // Component args
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,39 +50,47 @@ var proto = AsyncVDOMBuilder.prototype = {
|
|||||||
___isOut: true,
|
___isOut: true,
|
||||||
___document: defaultDocument,
|
___document: defaultDocument,
|
||||||
|
|
||||||
___elementNode: function(element, childCount, pushToStack) {
|
bc: function(component) {
|
||||||
var parent = this.___parent;
|
var vComponent = new VComponent(component);
|
||||||
if (parent !== undefined) {
|
return this.___beginNode(vComponent, 0, true);
|
||||||
parent.___appendChild(element);
|
},
|
||||||
if (pushToStack === true) {
|
|
||||||
this.___stack.push(element);
|
___preserveComponent: function(component) {
|
||||||
this.___parent = element;
|
var vComponent = new VComponent(component, true);
|
||||||
}
|
this.___beginNode(vComponent, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
___beginNode: function(child, childCount, pushToStack) {
|
||||||
|
this.___parent.___appendChild(child);
|
||||||
|
if (pushToStack === true) {
|
||||||
|
this.___stack.push(child);
|
||||||
|
this.___parent = child;
|
||||||
}
|
}
|
||||||
return childCount === 0 ? this : element;
|
return childCount === 0 ? this : child;
|
||||||
},
|
},
|
||||||
|
|
||||||
element: function(tagName, attrs, childCount, flags, props) {
|
element: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var element = new VElement(tagName, attrs, childCount, flags, props);
|
var element = new VElement(tagName, attrs, key, component, childCount, flags, props);
|
||||||
return this.___elementNode(element, childCount);
|
return this.___beginNode(element, childCount);
|
||||||
},
|
},
|
||||||
|
|
||||||
___elementDynamicTag: function(tagName, attrs, childCount, flags, props) {
|
___elementDynamicTag: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var element = VElement.___createElementDynamicTag(tagName, attrs, childCount, flags, props);
|
var element = VElement.___createElementDynamicTag(tagName, attrs, key, component, childCount, flags, props);
|
||||||
return this.___elementNode(element, childCount);
|
return this.___beginNode(element, childCount);
|
||||||
},
|
},
|
||||||
|
|
||||||
n: function(node) {
|
n: function(node, component) {
|
||||||
// NOTE: We do a shallow clone since we assume the node is being reused
|
// NOTE: We do a shallow clone since we assume the node is being reused
|
||||||
// and a node can only have one parent node.
|
// and a node can only have one parent node.
|
||||||
return this.node(node.___cloneNode());
|
|
||||||
|
node = this.node(node.___cloneNode());
|
||||||
|
node.___component = component;
|
||||||
|
|
||||||
|
return node;
|
||||||
},
|
},
|
||||||
|
|
||||||
node: function(node) {
|
node: function(node) {
|
||||||
var parent = this.___parent;
|
this.___parent.___appendChild(node);
|
||||||
if (parent !== undefined) {
|
|
||||||
parent.___appendChild(node);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -99,15 +109,7 @@ var proto = AsyncVDOMBuilder.prototype = {
|
|||||||
text = text.toString();
|
text = text.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var parent = this.___parent;
|
this.___parent.___appendChild(new VText(text));
|
||||||
if (parent !== undefined) {
|
|
||||||
var lastChild = parent.lastChild;
|
|
||||||
if (lastChild && lastChild.___Text) {
|
|
||||||
lastChild.___nodeValue += text;
|
|
||||||
} else {
|
|
||||||
parent.___appendChild(new VText(text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -124,15 +126,15 @@ var proto = AsyncVDOMBuilder.prototype = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
beginElement: function(tagName, attrs, childCount, flags, props) {
|
beginElement: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var element = new VElement(tagName, attrs, childCount, flags, props);
|
var element = new VElement(tagName, attrs, key, component, childCount, flags, props);
|
||||||
this.___elementNode(element, childCount, true);
|
this.___beginNode(element, childCount, true);
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
___beginElementDynamicTag: function(tagName, attrs, childCount, flags, props) {
|
___beginElementDynamicTag: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var element = VElement.___createElementDynamicTag(tagName, attrs, childCount, flags, props);
|
var element = VElement.___createElementDynamicTag(tagName, attrs, key, component, childCount, flags, props);
|
||||||
this.___elementNode(element, childCount, true);
|
this.___beginNode(element, childCount, true);
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -304,23 +306,30 @@ var proto = AsyncVDOMBuilder.prototype = {
|
|||||||
var node = this.___vnode;
|
var node = this.___vnode;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
var vdomTree = this.___getOutput();
|
var vdomTree = this.___getOutput();
|
||||||
|
// Create the root document fragment node
|
||||||
node = this.___vnode = vdomTree.actualize(doc || this.___document || document);
|
doc = doc || this.___document || document;
|
||||||
|
this.___vnode = node = vdomTree.___actualize(doc);
|
||||||
|
morphdom(node, null, null, vdomTree, doc, this.___components);
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
},
|
},
|
||||||
|
|
||||||
toString: function() {
|
toString: function(doc) {
|
||||||
var docFragment = this.___getNode();
|
var docFragment = this.___getNode(doc);
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
if (docFragment.hasChildNodes()) {
|
var child = docFragment.firstChild;
|
||||||
var children = docFragment.childNodes;
|
while(child) {
|
||||||
for (var i = 0; i < children.length; i++) {
|
var nextSibling = child.nextSibling;
|
||||||
var child = children[i];
|
if (child.nodeType != 1) {
|
||||||
// get outerHTML if exists, otherwise default to nodeValue
|
var container = docFragment.ownerDocument.createElement('div');
|
||||||
html += child.outerHTML || child.nodeValue;
|
container.appendChild(child.cloneNode());
|
||||||
|
html += container.innerHTML;
|
||||||
|
} else {
|
||||||
|
html += child.outerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child = nextSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
|
|||||||
@ -10,7 +10,8 @@ VComment.prototype = {
|
|||||||
___nodeType: 8,
|
___nodeType: 8,
|
||||||
|
|
||||||
___actualize: function(doc) {
|
___actualize: function(doc) {
|
||||||
return doc.createComment(this.___nodeValue);
|
var nodeValue = this.___nodeValue;
|
||||||
|
return doc.createComment(nodeValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
___cloneNode: function() {
|
___cloneNode: function() {
|
||||||
|
|||||||
16
src/runtime/vdom/VComponent.js
Normal file
16
src/runtime/vdom/VComponent.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
var VNode = require('./VNode');
|
||||||
|
var inherit = require('raptor-util/inherit');
|
||||||
|
|
||||||
|
function VComponent(component, preserve) {
|
||||||
|
this.___VNode(null /* childCount */);
|
||||||
|
this.___component = component;
|
||||||
|
this.___preserve = preserve;
|
||||||
|
}
|
||||||
|
|
||||||
|
VComponent.prototype = {
|
||||||
|
___nodeType: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
inherit(VComponent, VNode);
|
||||||
|
|
||||||
|
module.exports = VComponent;
|
||||||
@ -8,8 +8,9 @@ function VDocumentFragmentClone(other) {
|
|||||||
this.___nextSiblingInternal = null;
|
this.___nextSiblingInternal = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function VDocumentFragment(documentFragment) {
|
function VDocumentFragment(out) {
|
||||||
this.___VNode(null /* childCount */);
|
this.___VNode(null /* childCount */);
|
||||||
|
this.___out = out;
|
||||||
}
|
}
|
||||||
|
|
||||||
VDocumentFragment.prototype = {
|
VDocumentFragment.prototype = {
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
|
/* jshint newcap:false */
|
||||||
var VNode = require('./VNode');
|
var VNode = require('./VNode');
|
||||||
var inherit = require('raptor-util/inherit');
|
var inherit = require('raptor-util/inherit');
|
||||||
|
|
||||||
var NS_XLINK = 'http://www.w3.org/1999/xlink';
|
var NS_XLINK = 'http://www.w3.org/1999/xlink';
|
||||||
var ATTR_XLINK_HREF = 'xlink:href';
|
var ATTR_XLINK_HREF = 'xlink:href';
|
||||||
|
var xmlnsRegExp = /^xmlns(:|$)/;
|
||||||
|
|
||||||
var toString = String;
|
var toString = String;
|
||||||
|
|
||||||
var FLAG_IS_SVG = 1;
|
var FLAG_IS_SVG = 1;
|
||||||
var FLAG_IS_TEXTAREA = 2;
|
var FLAG_IS_TEXTAREA = 2;
|
||||||
var FLAG_SIMPLE_ATTRS = 4;
|
var FLAG_SIMPLE_ATTRS = 4;
|
||||||
|
// var FLAG_PRESERVE = 8;
|
||||||
|
|
||||||
var defineProperty = Object.defineProperty;
|
var defineProperty = Object.defineProperty;
|
||||||
|
|
||||||
@ -45,41 +48,49 @@ function VElementClone(other) {
|
|||||||
this.___parentNode = null;
|
this.___parentNode = null;
|
||||||
this.___nextSiblingInternal = null;
|
this.___nextSiblingInternal = null;
|
||||||
|
|
||||||
|
this.___key = other.___key;
|
||||||
this.___attributes = other.___attributes;
|
this.___attributes = other.___attributes;
|
||||||
this.___properties = other.___properties;
|
this.___properties = other.___properties;
|
||||||
this.___namespaceURI = other.___namespaceURI;
|
this.___namespaceURI = other.___namespaceURI;
|
||||||
this.___nodeName = other.___nodeName;
|
this.___nodeName = other.___nodeName;
|
||||||
this.___flags = other.___flags;
|
this.___flags = other.___flags;
|
||||||
this.___value = other.___value;
|
this.___valueInternal = other.___valueInternal;
|
||||||
this.___constId = other.___constId;
|
this.___constId = other.___constId;
|
||||||
|
this.___isTextArea = other.___isTextArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
function VElement(tagName, attrs, childCount, flags, props) {
|
function VElement(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
this.___VNode(childCount);
|
this.___VNode(childCount);
|
||||||
|
|
||||||
var constId, namespaceURI;
|
var constId;
|
||||||
|
var namespaceURI;
|
||||||
|
var isTextArea;
|
||||||
|
|
||||||
if (props) {
|
if (props) {
|
||||||
constId = props.c;
|
constId = props.i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((this.___flags = flags || 0)) {
|
if ((this.___flags = flags || 0)) {
|
||||||
if (flags & FLAG_IS_SVG) {
|
if (flags & FLAG_IS_SVG) {
|
||||||
namespaceURI = 'http://www.w3.org/2000/svg';
|
namespaceURI = 'http://www.w3.org/2000/svg';
|
||||||
}
|
}
|
||||||
|
if (flags & FLAG_IS_TEXTAREA) {
|
||||||
|
isTextArea = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.___key = key;
|
||||||
|
this.___component = component;
|
||||||
this.___attributes = attrs || EMPTY_OBJECT;
|
this.___attributes = attrs || EMPTY_OBJECT;
|
||||||
this.___properties = props || EMPTY_OBJECT;
|
this.___properties = props || EMPTY_OBJECT;
|
||||||
this.___namespaceURI = namespaceURI;
|
this.___namespaceURI = namespaceURI;
|
||||||
this.___nodeName = tagName;
|
this.___nodeName = tagName;
|
||||||
this.___value = null;
|
this.___valueInternal = null;
|
||||||
this.___constId = constId;
|
this.___constId = constId;
|
||||||
|
this.___isTextArea = isTextArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
VElement.prototype = {
|
VElement.prototype = {
|
||||||
___VElement: true,
|
|
||||||
|
|
||||||
___nodeType: 1,
|
___nodeType: 1,
|
||||||
|
|
||||||
___cloneNode: function() {
|
___cloneNode: function() {
|
||||||
@ -93,8 +104,8 @@ VElement.prototype = {
|
|||||||
* @param {int|null} attrCount The number of attributes (or `null` if not known)
|
* @param {int|null} attrCount The number of attributes (or `null` if not known)
|
||||||
* @param {int|null} childCount The number of child nodes (or `null` if not known)
|
* @param {int|null} childCount The number of child nodes (or `null` if not known)
|
||||||
*/
|
*/
|
||||||
e: function(tagName, attrs, childCount, flags, props) {
|
e: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var child = this.___appendChild(new VElement(tagName, attrs, childCount, flags, props));
|
var child = this.___appendChild(new VElement(tagName, attrs, key, component, childCount, flags, props));
|
||||||
|
|
||||||
if (childCount === 0) {
|
if (childCount === 0) {
|
||||||
return this.___finishChild();
|
return this.___finishChild();
|
||||||
@ -110,8 +121,8 @@ VElement.prototype = {
|
|||||||
* @param {int|null} attrCount The number of attributes (or `null` if not known)
|
* @param {int|null} attrCount The number of attributes (or `null` if not known)
|
||||||
* @param {int|null} childCount The number of child nodes (or `null` if not known)
|
* @param {int|null} childCount The number of child nodes (or `null` if not known)
|
||||||
*/
|
*/
|
||||||
ed: function(tagName, attrs, childCount, flags, props) {
|
ed: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
var child = this.___appendChild(VElement.___createElementDynamicTag(tagName, attrs, childCount, flags, props));
|
var child = this.___appendChild(VElement.___createElementDynamicTag(tagName, attrs, key, component, childCount, flags, props));
|
||||||
|
|
||||||
if (childCount === 0) {
|
if (childCount === 0) {
|
||||||
return this.___finishChild();
|
return this.___finishChild();
|
||||||
@ -126,8 +137,10 @@ VElement.prototype = {
|
|||||||
*
|
*
|
||||||
* @param {String} value The value for the new Comment node
|
* @param {String} value The value for the new Comment node
|
||||||
*/
|
*/
|
||||||
n: function(node) {
|
n: function(node, component) {
|
||||||
this.___appendChild(node.___cloneNode());
|
node = node.___cloneNode();
|
||||||
|
node.___component = component;
|
||||||
|
this.___appendChild(node);
|
||||||
return this.___finishChild();
|
return this.___finishChild();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -163,12 +176,10 @@ VElement.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAG_IS_TEXTAREA) {
|
if (flags & FLAG_IS_TEXTAREA) {
|
||||||
el.value = this.___value;
|
el.value = this.___valueInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
el._vattrs = attributes;
|
el.___markoVElement = this;
|
||||||
el._vprops = this.___properties;
|
|
||||||
el._vflags = flags;
|
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
},
|
},
|
||||||
@ -179,7 +190,7 @@ VElement.prototype = {
|
|||||||
// different namespaces
|
// different namespaces
|
||||||
var value = this.___attributes[name];
|
var value = this.___attributes[name];
|
||||||
return value != null && value !== false;
|
return value != null && value !== false;
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inherit(VElement, VNode);
|
inherit(VElement, VNode);
|
||||||
@ -195,15 +206,9 @@ var proto = VElementClone.prototype = VElement.prototype;
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
defineProperty(proto, 'id', {
|
defineProperty(proto, '___value', {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.___attributes.id;
|
var value = this.___valueInternal;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
defineProperty(proto, 'value', {
|
|
||||||
get: function () {
|
|
||||||
var value = this.___value;
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
value = this.___attributes.value;
|
value = this.___attributes.value;
|
||||||
}
|
}
|
||||||
@ -211,16 +216,10 @@ defineProperty(proto, 'value', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
defineProperty(proto, '___isTextArea', {
|
VElement.___createElementDynamicTag = function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
get: function () {
|
|
||||||
return this.___flags & FLAG_IS_TEXTAREA;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
VElement.___createElementDynamicTag = function(tagName, attrs, childCount, flags, props) {
|
|
||||||
var namespace = attrs && attrs.xmlns;
|
var namespace = attrs && attrs.xmlns;
|
||||||
tagName = namespace ? tagName : tagName.toUpperCase();
|
tagName = namespace ? tagName : tagName.toUpperCase();
|
||||||
var element = new VElement(tagName, attrs, childCount, flags, props);
|
var element = new VElement(tagName, attrs, key, component, childCount, flags, props);
|
||||||
element.___namespaceURI = namespace;
|
element.___namespaceURI = namespace;
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
@ -233,15 +232,64 @@ VElement.___removePreservedAttributes = function(attrs) {
|
|||||||
return attrs;
|
return attrs;
|
||||||
};
|
};
|
||||||
|
|
||||||
VElement.___morphAttrs = function(fromEl, toEl) {
|
function virtualizeElement(node, virtualizeChildNodes) {
|
||||||
|
var attributes = node.attributes;
|
||||||
|
var attrCount = attributes.length;
|
||||||
|
|
||||||
|
var attrs;
|
||||||
|
|
||||||
|
if (attrCount) {
|
||||||
|
attrs = {};
|
||||||
|
for (var i=0; i<attrCount; i++) {
|
||||||
|
var attr = attributes[i];
|
||||||
|
var attrName = attr.name;
|
||||||
|
if (!xmlnsRegExp.test(attrName) && attrName !== 'data-marko') {
|
||||||
|
var attrNamespaceURI = attr.namespaceURI;
|
||||||
|
if (attrNamespaceURI === NS_XLINK) {
|
||||||
|
attrs[ATTR_XLINK_HREF] = attr.value;
|
||||||
|
} else {
|
||||||
|
attrs[attrName] = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = 0;
|
||||||
|
|
||||||
|
var tagName = node.nodeName;
|
||||||
|
if (tagName === 'TEXTAREA') {
|
||||||
|
flags |= FLAG_IS_TEXTAREA;
|
||||||
|
}
|
||||||
|
|
||||||
|
var vdomEl = new VElement(tagName, attrs, null /*key*/, null /*component*/, 0 /*child count*/, flags, null /*props*/);
|
||||||
|
if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml') {
|
||||||
|
vdomEl.___namespaceURI = node.namespaceURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdomEl.___isTextArea) {
|
||||||
|
vdomEl.___valueInternal = node.value;
|
||||||
|
} else {
|
||||||
|
if (virtualizeChildNodes) {
|
||||||
|
virtualizeChildNodes(node, vdomEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vdomEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
VElement.___virtualize = virtualizeElement;
|
||||||
|
|
||||||
|
VElement.___morphAttrs = function(fromEl, vFromEl, toEl) {
|
||||||
var removePreservedAttributes = VElement.___removePreservedAttributes;
|
var removePreservedAttributes = VElement.___removePreservedAttributes;
|
||||||
|
|
||||||
|
var fromFlags = vFromEl.___flags;
|
||||||
|
|
||||||
|
fromEl.___markoVElement = toEl;
|
||||||
|
|
||||||
var attrs = toEl.___attributes;
|
var attrs = toEl.___attributes;
|
||||||
var props = fromEl._vprops = toEl.___properties;
|
var props = toEl.___properties;
|
||||||
|
|
||||||
var attrName;
|
var attrName;
|
||||||
var i;
|
|
||||||
|
|
||||||
// We use expando properties to associate the previous HTML
|
// We use expando properties to associate the previous HTML
|
||||||
// attributes provided as part of the VDOM node with the
|
// attributes provided as part of the VDOM node with the
|
||||||
@ -251,52 +299,26 @@ VElement.___morphAttrs = function(fromEl, toEl) {
|
|||||||
// real VElement node will not have the expando property
|
// real VElement node will not have the expando property
|
||||||
// so we build the attribute map from the expando property
|
// so we build the attribute map from the expando property
|
||||||
|
|
||||||
var oldAttrs = fromEl._vattrs;
|
var oldAttrs = vFromEl.___attributes;
|
||||||
|
|
||||||
if (oldAttrs) {
|
if (oldAttrs) {
|
||||||
if (oldAttrs == attrs) {
|
if (oldAttrs === attrs) {
|
||||||
// For constant attributes the same object will be provided
|
// For constant attributes the same object will be provided
|
||||||
// every render and we can use that to our advantage to
|
// every render and we can use that to our advantage to
|
||||||
// not waste time diffing a constant, immutable attribute
|
// not waste time diffing a constant, immutable attribute
|
||||||
// map.
|
// map.
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
oldAttrs = removePreservedAttributes(oldAttrs, props, true);
|
oldAttrs = removePreservedAttributes(oldAttrs, props);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// We need to build the attribute map from the real attributes
|
|
||||||
oldAttrs = {};
|
|
||||||
|
|
||||||
var oldAttributesList = fromEl.attributes;
|
|
||||||
for (i = oldAttributesList.length - 1; i >= 0; --i) {
|
|
||||||
var attr = oldAttributesList[i];
|
|
||||||
|
|
||||||
if (attr.specified !== false) {
|
|
||||||
attrName = attr.name;
|
|
||||||
if (attrName !== 'data-marko') {
|
|
||||||
var attrNamespaceURI = attr.namespaceURI;
|
|
||||||
if (attrNamespaceURI === NS_XLINK) {
|
|
||||||
oldAttrs[ATTR_XLINK_HREF] = attr.value;
|
|
||||||
} else {
|
|
||||||
oldAttrs[attrName] = attr.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want preserved attributes to show up in either the old
|
|
||||||
// or new attribute map.
|
|
||||||
removePreservedAttributes(oldAttrs, props, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fromEl._vattrs = attrs;
|
|
||||||
|
|
||||||
var attrValue;
|
var attrValue;
|
||||||
|
|
||||||
var flags = toEl.___flags;
|
var toFlags = toEl.___flags;
|
||||||
var oldFlags;
|
|
||||||
|
|
||||||
if (flags & FLAG_SIMPLE_ATTRS && ((oldFlags = fromEl._vflags) & FLAG_SIMPLE_ATTRS)) {
|
|
||||||
|
if (toFlags & FLAG_SIMPLE_ATTRS && fromFlags & FLAG_SIMPLE_ATTRS) {
|
||||||
if (oldAttrs['class'] !== (attrValue = attrs['class'])) {
|
if (oldAttrs['class'] !== (attrValue = attrs['class'])) {
|
||||||
fromEl.className = attrValue;
|
fromEl.className = attrValue;
|
||||||
}
|
}
|
||||||
@ -309,6 +331,7 @@ VElement.___morphAttrs = function(fromEl, toEl) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// In some cases we only want to set an attribute value for the first
|
// In some cases we only want to set an attribute value for the first
|
||||||
// render or we don't want certain attributes to be touched. To support
|
// render or we don't want certain attributes to be touched. To support
|
||||||
// that use case we delete out all of the preserved attributes
|
// that use case we delete out all of the preserved attributes
|
||||||
@ -352,7 +375,7 @@ VElement.___morphAttrs = function(fromEl, toEl) {
|
|||||||
// was not a virtualized node (i.e., a node that was not rendered by a
|
// was not a virtualized node (i.e., a node that was not rendered by a
|
||||||
// Marko template, but rather a node that was created from an HTML
|
// Marko template, but rather a node that was created from an HTML
|
||||||
// string or a real DOM node).
|
// string or a real DOM node).
|
||||||
if (!attrs.id || props.___virtualized === true) {
|
if (toEl.___key === null) {
|
||||||
for (attrName in oldAttrs) {
|
for (attrName in oldAttrs) {
|
||||||
if (!(attrName in attrs)) {
|
if (!(attrName in attrs)) {
|
||||||
if (attrName === ATTR_XLINK_HREF) {
|
if (attrName === ATTR_XLINK_HREF) {
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
/* jshint newcap:false */
|
/* jshint newcap:false */
|
||||||
var specialElHandlers = require('../../morphdom/specialElHandlers');
|
|
||||||
|
|
||||||
function VNode() {}
|
function VNode() {}
|
||||||
|
|
||||||
VNode.prototype = {
|
VNode.prototype = {
|
||||||
@ -13,6 +11,8 @@ VNode.prototype = {
|
|||||||
this.___nextSiblingInternal = null;
|
this.___nextSiblingInternal = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
___component: null,
|
||||||
|
|
||||||
get ___firstChild() {
|
get ___firstChild() {
|
||||||
var firstChild = this.___firstChildInternal;
|
var firstChild = this.___firstChildInternal;
|
||||||
|
|
||||||
@ -49,10 +49,10 @@ VNode.prototype = {
|
|||||||
___appendChild: function(child) {
|
___appendChild: function(child) {
|
||||||
this.___childCount++;
|
this.___childCount++;
|
||||||
|
|
||||||
if (this.___isTextArea) {
|
if (this.___isTextArea === true) {
|
||||||
if (child.___Text) {
|
if (child.___Text) {
|
||||||
var childValue = child.___nodeValue;
|
var childValue = child.___nodeValue;
|
||||||
this.___value = (this.___value || '') + childValue;
|
this.___valueInternal = (this.___valueInternal || '') + childValue;
|
||||||
} else {
|
} else {
|
||||||
throw TypeError();
|
throw TypeError();
|
||||||
}
|
}
|
||||||
@ -74,32 +74,13 @@ VNode.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
___finishChild: function finishChild() {
|
___finishChild: function finishChild() {
|
||||||
if (this.___childCount == this.___finalChildCount && this.___parentNode) {
|
if (this.___childCount === this.___finalChildCount && this.___parentNode) {
|
||||||
return this.___parentNode.___finishChild();
|
return this.___parentNode.___finishChild();
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actualize: function(doc) {
|
|
||||||
var actualNode = this.___actualize(doc);
|
|
||||||
|
|
||||||
var curChild = this.___firstChild;
|
|
||||||
|
|
||||||
while(curChild) {
|
|
||||||
actualNode.appendChild(curChild.actualize(doc));
|
|
||||||
curChild = curChild.___nextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.___nodeType === 1) {
|
|
||||||
var elHandler = specialElHandlers[this.___nodeName];
|
|
||||||
if (elHandler !== undefined) {
|
|
||||||
elHandler(actualNode, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return actualNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ,toJSON: function() {
|
// ,toJSON: function() {
|
||||||
// var clone = Object.assign({
|
// var clone = Object.assign({
|
||||||
|
|||||||
@ -10,8 +10,8 @@ var extend = require('raptor-util/extend');
|
|||||||
var classList = commonHelpers.cl;
|
var classList = commonHelpers.cl;
|
||||||
|
|
||||||
var helpers = extend({
|
var helpers = extend({
|
||||||
e: function(tagName, attrs, childCount, flags, props) {
|
e: function(tagName, attrs, key, component, childCount, flags, props) {
|
||||||
return new VElement(tagName, attrs, childCount, flags, props);
|
return new VElement(tagName, attrs, key, component, childCount, flags, props);
|
||||||
},
|
},
|
||||||
|
|
||||||
t: function(value) {
|
t: function(value) {
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
var extend = require('raptor-util/extend');
|
var extend = require('raptor-util/extend');
|
||||||
|
|
||||||
function removePreservedAttributes(attrs, props, clone) {
|
function removePreservedAttributes(attrs, props) {
|
||||||
var preservedAttrs = props && props.noupdate;
|
var preservedAttrs = props && props.noupdate;
|
||||||
if (preservedAttrs) {
|
if (preservedAttrs) {
|
||||||
if (clone) {
|
attrs = extend({}, attrs);
|
||||||
attrs = extend({}, attrs);
|
|
||||||
}
|
|
||||||
preservedAttrs.forEach(function(preservedAttrName) {
|
preservedAttrs.forEach(function(preservedAttrName) {
|
||||||
delete attrs[preservedAttrName];
|
delete attrs[preservedAttrName];
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,12 +3,11 @@ var VComment = require('./VComment');
|
|||||||
var VDocumentFragment = require('./VDocumentFragment');
|
var VDocumentFragment = require('./VDocumentFragment');
|
||||||
var VElement = require('./VElement');
|
var VElement = require('./VElement');
|
||||||
var VText = require('./VText');
|
var VText = require('./VText');
|
||||||
|
var VComponent = require('./VComponent');
|
||||||
|
|
||||||
var FLAG_IS_TEXTAREA = 2;
|
|
||||||
var defaultDocument = typeof document != 'undefined' && document;
|
var defaultDocument = typeof document != 'undefined' && document;
|
||||||
var specialHtmlRegexp = /[&<]/;
|
var specialHtmlRegexp = /[&<]/;
|
||||||
var xmlnsRegExp = /^xmlns(:|$)/;
|
|
||||||
var virtualizedProps = { ___virtualized: true };
|
|
||||||
|
|
||||||
function virtualizeChildNodes(node, vdomParent) {
|
function virtualizeChildNodes(node, vdomParent) {
|
||||||
var curChild = node.firstChild;
|
var curChild = node.firstChild;
|
||||||
@ -18,44 +17,10 @@ function virtualizeChildNodes(node, vdomParent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function virtualize(node) {
|
function virtualize(node, shallow) {
|
||||||
switch(node.nodeType) {
|
switch(node.nodeType) {
|
||||||
case 1:
|
case 1:
|
||||||
var attributes = node.attributes;
|
return VElement.___virtualize(node, virtualizeChildNodes);
|
||||||
var attrCount = attributes.length;
|
|
||||||
|
|
||||||
var attrs;
|
|
||||||
|
|
||||||
if (attrCount) {
|
|
||||||
attrs = {};
|
|
||||||
for (var i=0; i<attrCount; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
var attrName = attr.name;
|
|
||||||
if (!xmlnsRegExp.test(attrName)) {
|
|
||||||
attrs[attrName] = attr.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags = 0;
|
|
||||||
|
|
||||||
var tagName = node.nodeName;
|
|
||||||
if (tagName === 'TEXTAREA') {
|
|
||||||
flags |= FLAG_IS_TEXTAREA;
|
|
||||||
}
|
|
||||||
|
|
||||||
var vdomEl = new VElement(tagName, attrs, null, flags, virtualizedProps);
|
|
||||||
if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml') {
|
|
||||||
vdomEl.___namespaceURI = node.namespaceURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vdomEl.___isTextArea) {
|
|
||||||
vdomEl.___value = node.value;
|
|
||||||
} else {
|
|
||||||
virtualizeChildNodes(node, vdomEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return vdomEl;
|
|
||||||
case 3:
|
case 3:
|
||||||
return new VText(node.nodeValue);
|
return new VText(node.nodeValue);
|
||||||
case 8:
|
case 8:
|
||||||
@ -126,6 +91,7 @@ exports.___VComment = VComment;
|
|||||||
exports.___VDocumentFragment = VDocumentFragment;
|
exports.___VDocumentFragment = VDocumentFragment;
|
||||||
exports.___VElement = VElement;
|
exports.___VElement = VElement;
|
||||||
exports.___VText = VText;
|
exports.___VText = VText;
|
||||||
|
exports.___VComponent = VComponent;
|
||||||
exports.___virtualize = virtualize;
|
exports.___virtualize = virtualize;
|
||||||
exports.___virtualizeHTML = virtualizeHTML;
|
exports.___virtualizeHTML = virtualizeHTML;
|
||||||
exports.___defaultDocument = defaultDocument;
|
exports.___defaultDocument = defaultDocument;
|
||||||
|
|||||||
@ -76,6 +76,7 @@
|
|||||||
},
|
},
|
||||||
"<import>": {
|
"<import>": {
|
||||||
"code-generator": "./import-tag",
|
"code-generator": "./import-tag",
|
||||||
|
"no-output": true,
|
||||||
"parse-options": {
|
"parse-options": {
|
||||||
"relaxRequireCommas": true
|
"relaxRequireCommas": true
|
||||||
}
|
}
|
||||||
@ -135,7 +136,8 @@
|
|||||||
{
|
{
|
||||||
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#macros"
|
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#macros"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"no-output": true
|
||||||
},
|
},
|
||||||
"<macro-body>": {
|
"<macro-body>": {
|
||||||
"code-generator": "./macro-body-tag",
|
"code-generator": "./macro-body-tag",
|
||||||
@ -167,6 +169,7 @@
|
|||||||
},
|
},
|
||||||
"<static>": {
|
"<static>": {
|
||||||
"code-generator": "./static-tag",
|
"code-generator": "./static-tag",
|
||||||
|
"no-output": true,
|
||||||
"parse-options": {
|
"parse-options": {
|
||||||
"ignoreAttributes": true
|
"ignoreAttributes": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1127,7 +1127,8 @@
|
|||||||
"html": true,
|
"html": true,
|
||||||
"attribute-groups": [
|
"attribute-groups": [
|
||||||
"html-attributes"
|
"html-attributes"
|
||||||
]
|
],
|
||||||
|
"openTagOnly": true
|
||||||
},
|
},
|
||||||
"attribute-groups": {
|
"attribute-groups": {
|
||||||
"html-attributes": {
|
"html-attributes": {
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
<html><head><title>Welcome Frank</title></head><body><div id="s0">Hello</div><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"w":[["s0",0,{},{"_":1}]],"t":["/marko-test$1.0.0/autotests/async-render/components-await-title/components/hello/index.marko"]})||w.$components})()</script></body></html>
|
<html><head><title>Welcome Frank</title></head><body><!--M#s0--><div>Hello</div><!--M$s0--><script>(function(){var w=window;w.$components=(w.$components||[]).concat({"w":[["s0",0,{},{"f":1}]],"t":["/marko-test$1.0.0/autotests/async-render/components-await-title/components/hello/index.marko"]})||w.$components})()</script></body></html>
|
||||||
@ -5,7 +5,7 @@ var marko_template = module.exports = require("marko/src/vdom").t();
|
|||||||
function render(input, out) {
|
function render(input, out) {
|
||||||
var data = input;
|
var data = input;
|
||||||
|
|
||||||
out.ed(foo ? "foo" : "bar", null, 0);
|
out.ed(foo ? "foo" : "bar", null, null, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
marko_template._ = render;
|
marko_template._ = render;
|
||||||
|
|||||||
@ -6,12 +6,12 @@ var marko_template = module.exports = require("marko/src/vdom").t(),
|
|||||||
marko_createElement = marko_helpers.e,
|
marko_createElement = marko_helpers.e,
|
||||||
marko_const = marko_helpers.const,
|
marko_const = marko_helpers.const,
|
||||||
marko_const_nextId = marko_const("295cea"),
|
marko_const_nextId = marko_const("295cea"),
|
||||||
marko_node0 = marko_createElement("DIV", null, 1, 0, {
|
marko_node0 = marko_createElement("DIV", null, null, null, 1, 0, {
|
||||||
c: marko_const_nextId()
|
i: marko_const_nextId()
|
||||||
})
|
})
|
||||||
.t("No colors!"),
|
.t("No colors!"),
|
||||||
marko_node1 = marko_createElement("DIV", null, 1, 0, {
|
marko_node1 = marko_createElement("DIV", null, null, null, 1, 0, {
|
||||||
c: marko_const_nextId()
|
i: marko_const_nextId()
|
||||||
})
|
})
|
||||||
.t("No colors!");
|
.t("No colors!");
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ function render(input, out) {
|
|||||||
out.be("UL");
|
out.be("UL");
|
||||||
|
|
||||||
marko_forEach(input.colors, function(color) {
|
marko_forEach(input.colors, function(color) {
|
||||||
out.e("LI", null, 1)
|
out.e("LI", null, null, null, 1)
|
||||||
.t(color);
|
.t(color);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ function render(input, out) {
|
|||||||
out.be("UL");
|
out.be("UL");
|
||||||
|
|
||||||
marko_forEach(input.colors, function(color) {
|
marko_forEach(input.colors, function(color) {
|
||||||
out.e("LI", null, 1)
|
out.e("LI", null, null, null, 1)
|
||||||
.t(color);
|
.t(color);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -8,13 +8,13 @@ var marko_template = module.exports = require("marko/src/vdom").t(),
|
|||||||
marko_node0 = marko_createElement("svg", {
|
marko_node0 = marko_createElement("svg", {
|
||||||
width: "140",
|
width: "140",
|
||||||
height: "30"
|
height: "30"
|
||||||
}, 1, 1, {
|
}, null, null, 1, 1, {
|
||||||
c: marko_const_nextId()
|
i: marko_const_nextId()
|
||||||
})
|
})
|
||||||
.e("a", {
|
.e("a", {
|
||||||
"xlink:href": "https://developer.mozilla.org/en-US/docs/SVG",
|
"xlink:href": "https://developer.mozilla.org/en-US/docs/SVG",
|
||||||
target: "_blank"
|
target: "_blank"
|
||||||
}, 0, 1);
|
}, null, null, 0, 1);
|
||||||
|
|
||||||
function render(input, out) {
|
function render(input, out) {
|
||||||
var data = input;
|
var data = input;
|
||||||
|
|||||||
@ -15,8 +15,8 @@ function render(input, out) {
|
|||||||
|
|
||||||
var isCircle = true;
|
var isCircle = true;
|
||||||
|
|
||||||
out.e("svg", marko_attrs0, 1, 1)
|
out.e("svg", marko_attrs0, null, null, 1, 1)
|
||||||
.e(isCircle ? "circle" : "square", marko_attrs1, 0, 1);
|
.e(isCircle ? "circle" : "square", marko_attrs1, null, null, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
marko_template._ = render;
|
marko_template._ = render;
|
||||||
|
|||||||
@ -8,14 +8,14 @@ var marko_template = module.exports = require("marko/src/vdom").t(),
|
|||||||
marko_node0 = marko_createElement("svg", {
|
marko_node0 = marko_createElement("svg", {
|
||||||
viewBox: "0 0 200 200",
|
viewBox: "0 0 200 200",
|
||||||
xmlns: "http://www.w3.org/2000/svg"
|
xmlns: "http://www.w3.org/2000/svg"
|
||||||
}, 1, 1, {
|
}, null, null, 1, 1, {
|
||||||
c: marko_const_nextId()
|
i: marko_const_nextId()
|
||||||
})
|
})
|
||||||
.e("circle", {
|
.e("circle", {
|
||||||
cx: "100",
|
cx: "100",
|
||||||
cy: "100",
|
cy: "100",
|
||||||
r: "100"
|
r: "100"
|
||||||
}, 0, 1);
|
}, null, null, 0, 1);
|
||||||
|
|
||||||
function render(input, out) {
|
function render(input, out) {
|
||||||
var data = input;
|
var data = input;
|
||||||
|
|||||||
@ -2,11 +2,11 @@ class {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<div>
|
<div key="root">
|
||||||
<for(index, field in input.fields)>
|
<for(index, field in input.fields)>
|
||||||
<div>
|
<div>
|
||||||
<input key="field[${index}]" type="text" value=field.value/>
|
<input key="field[${index}]" type="text" value=field.value/>
|
||||||
<label for-key="field[${index}]">${field.label}</label>
|
<label for-key="field[${index}]">${field.label}</label>
|
||||||
</div>
|
</div>
|
||||||
</for>
|
</for>
|
||||||
</div>
|
</div>
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var fields = [
|
||||||
|
{
|
||||||
|
value: 'name',
|
||||||
|
label: 'Name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'age',
|
||||||
|
label: 'Age'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var component = helpers.mount(require('./index'), {
|
||||||
|
fields: fields
|
||||||
|
});
|
||||||
|
|
||||||
|
var inputs = component.getEl('root').querySelectorAll('input');
|
||||||
|
var labels = component.getEl('root').querySelectorAll('label');
|
||||||
|
|
||||||
|
expect(inputs.length).to.equal(fields.length);
|
||||||
|
|
||||||
|
for (var i=0; i<fields.length; i++) {
|
||||||
|
var input = inputs[i];
|
||||||
|
var label = labels[i];
|
||||||
|
expect(label.getAttribute('for')).to.equal(input.id);
|
||||||
|
expect(input.value).to.equal(fields[i].value);
|
||||||
|
expect(label.innerHTML).to.equal(fields[i].label);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -14,7 +14,12 @@ module.exports = function(helpers) {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
var inputs = widget.getEls('field');
|
var inputs = [];
|
||||||
|
|
||||||
|
for (var i=0; i<2; i++) {
|
||||||
|
inputs.push(widget.getEl('field[' + i + ']'));
|
||||||
|
}
|
||||||
|
|
||||||
expect(inputs.length).to.equal(2);
|
expect(inputs.length).to.equal(2);
|
||||||
expect(inputs[0].value).to.equal('name');
|
expect(inputs[0].value).to.equal('name');
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,7 +3,6 @@ var expect = require('chai').expect;
|
|||||||
module.exports = function(helpers, done) {
|
module.exports = function(helpers, done) {
|
||||||
var widget = helpers.mount(require('./index'), {});
|
var widget = helpers.mount(require('./index'), {});
|
||||||
|
|
||||||
expect(widget.$().attr('id')).to.equal(widget.id);
|
|
||||||
expect(widget.$().attr('class')).to.equal('app-jquery-proxy');
|
expect(widget.$().attr('class')).to.equal('app-jquery-proxy');
|
||||||
expect(widget.$('#foo').html()).to.equal('foo');
|
expect(widget.$('#foo').html()).to.equal('foo');
|
||||||
expect(widget.$('#fooText').html()).to.equal('fooText');
|
expect(widget.$('#fooText').html()).to.equal('fooText');
|
||||||
|
|||||||
@ -11,5 +11,5 @@ module.exports = function(helpers) {
|
|||||||
label: 'Bar'
|
label: 'Bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(widget.el.id).to.equal(oldId);
|
expect(widget.id).to.equal(oldId);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,7 @@ module.exports = function(helpers) {
|
|||||||
version: 0
|
version: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(window.rerenderInitOrder).to.deep.equal(['childA', 'childB', 'parent']);
|
expect(window.rerenderInitOrder).to.deep.equal(['childB', 'childA', 'parent']);
|
||||||
|
|
||||||
window.rerenderInitOrder = [];
|
window.rerenderInitOrder = [];
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ module.exports = function(helpers) {
|
|||||||
widget.update();
|
widget.update();
|
||||||
|
|
||||||
// console.log('ACTUAL ORDER: ', window.rerenderInitOrder);
|
// console.log('ACTUAL ORDER: ', window.rerenderInitOrder);
|
||||||
expect(window.rerenderInitOrder).to.deep.equal(['childA', 'childB', 'parent']);
|
expect(window.rerenderInitOrder).to.deep.equal(['childB', 'childA', 'parent']);
|
||||||
|
|
||||||
delete window.rerenderInitOrder;
|
delete window.rerenderInitOrder;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module.exports = function(helpers) {
|
|||||||
var widget = helpers.mount(require('./index'), {});
|
var widget = helpers.mount(require('./index'), {});
|
||||||
|
|
||||||
var oldButton1Widget = widget.getWidget('button1');
|
var oldButton1Widget = widget.getWidget('button1');
|
||||||
var oldButton2Widget = widget.getEl('button2').__widget;
|
var oldButton2Widget = widget.getWidget('button2');
|
||||||
var oldButton1El = oldButton1Widget.el;
|
var oldButton1El = oldButton1Widget.el;
|
||||||
var oldButton2El = widget.getEl('button2');
|
var oldButton2El = widget.getEl('button2');
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ module.exports = function(helpers) {
|
|||||||
|
|
||||||
// // Both button widgets should be reused
|
// // Both button widgets should be reused
|
||||||
expect(widget.getWidget('button1')).to.equal(oldButton1Widget);
|
expect(widget.getWidget('button1')).to.equal(oldButton1Widget);
|
||||||
expect(widget.getEl('button2').__widget).to.equal(oldButton2Widget);
|
expect(widget.getWidget('button2')).to.equal(oldButton2Widget);
|
||||||
|
|
||||||
expect(widget.getWidget('button1').el.innerHTML).to.equal('small');
|
expect(widget.getWidget('button1').el.innerHTML).to.equal('small');
|
||||||
|
|
||||||
@ -35,4 +35,4 @@ module.exports = function(helpers) {
|
|||||||
//
|
//
|
||||||
// // State didn't change for button2 so it should be the same el
|
// // State didn't change for button2 so it should be the same el
|
||||||
expect(newButton2El).to.equal(oldButton2El);
|
expect(newButton2El).to.equal(oldButton2El);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,7 +4,7 @@ module.exports = function(helpers) {
|
|||||||
var widget = helpers.mount(require('./index'), {});
|
var widget = helpers.mount(require('./index'), {});
|
||||||
|
|
||||||
var oldButton1Widget = widget.getWidget('button1');
|
var oldButton1Widget = widget.getWidget('button1');
|
||||||
var oldButton2Widget = widget.getEl('button2').__widget;
|
var oldButton2Widget = widget.getWidget('button2');
|
||||||
var oldButton1El = oldButton1Widget.el;
|
var oldButton1El = oldButton1Widget.el;
|
||||||
var oldButton2El = widget.getEl('button2');
|
var oldButton2El = widget.getEl('button2');
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ module.exports = function(helpers) {
|
|||||||
|
|
||||||
// // Both button widgets should be reused
|
// // Both button widgets should be reused
|
||||||
expect(widget.getWidget('button1')).to.equal(oldButton1Widget);
|
expect(widget.getWidget('button1')).to.equal(oldButton1Widget);
|
||||||
expect(widget.getEl('button2').__widget).to.equal(oldButton2Widget);
|
expect(widget.getWidget('button2')).to.equal(oldButton2Widget);
|
||||||
|
|
||||||
expect(widget.getWidget('button1').el.innerHTML).to.equal('small');
|
expect(widget.getWidget('button1').el.innerHTML).to.equal('small');
|
||||||
|
|
||||||
@ -33,4 +33,4 @@ module.exports = function(helpers) {
|
|||||||
//
|
//
|
||||||
// // State didn't change for button2 so it should be the same el
|
// // State didn't change for button2 so it should be the same el
|
||||||
expect(newButton2El).to.equal(oldButton2El);
|
expect(newButton2El).to.equal(oldButton2El);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<div.inner>${input.count}</div>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
class {
|
||||||
|
onCreate() {
|
||||||
|
this.state = {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<inner count=state.count key="inner"/>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var component = helpers.mount(require('./index'), { });
|
||||||
|
var innerComponent = component.getComponent('inner');
|
||||||
|
|
||||||
|
expect(innerComponent.___startNode.className).to.equal('inner');
|
||||||
|
expect(component.___startNode).to.not.equal(innerComponent.___startNode);
|
||||||
|
expect(component.___endNode).to.not.equal(innerComponent.___endNode);
|
||||||
|
expect(helpers.targetEl.querySelector('.inner').innerHTML).to.equal('0');
|
||||||
|
|
||||||
|
component.state.count++;
|
||||||
|
component.update();
|
||||||
|
|
||||||
|
innerComponent = component.getComponent('inner');
|
||||||
|
|
||||||
|
expect(innerComponent.___startNode.className).to.equal('inner');
|
||||||
|
expect(component.___startNode).to.not.equal(innerComponent.___startNode);
|
||||||
|
expect(component.___endNode).to.not.equal(innerComponent.___endNode);
|
||||||
|
expect(helpers.targetEl.querySelector('.inner').innerHTML).to.equal('1');
|
||||||
|
};
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<div.bar>${input.name}</div>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<div.foo>${input.name}</div>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
class {
|
||||||
|
onCreate() {
|
||||||
|
this.state = {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.root key="root">
|
||||||
|
<if(state.count === 0)>
|
||||||
|
<foo key="a" name="foo-a" count=state.count/>
|
||||||
|
<bar key="b" name="bar-b" count=state.count/>
|
||||||
|
</if>
|
||||||
|
<else>
|
||||||
|
<foo key="a" name="foo-a" count=state.count/>
|
||||||
|
<foo key="b" name="foo-b" count=state.count/>
|
||||||
|
<bar key="c" name="bar-c" count=state.count/>
|
||||||
|
</else>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var component = helpers.mount(require('./index'), { });
|
||||||
|
|
||||||
|
var children;
|
||||||
|
|
||||||
|
children = component.getEl('root').children;
|
||||||
|
expect(children.length).to.equal(2);
|
||||||
|
expect(children[0].innerHTML).to.equal('foo-a');
|
||||||
|
expect(children[1].innerHTML).to.equal('bar-b');
|
||||||
|
|
||||||
|
component.state.count++;
|
||||||
|
component.update();
|
||||||
|
|
||||||
|
children = component.getEl('root').children;
|
||||||
|
expect(children.length).to.equal(3);
|
||||||
|
expect(children[0].innerHTML).to.equal('foo-a');
|
||||||
|
expect(children[1].innerHTML).to.equal('foo-b');
|
||||||
|
expect(children[2].innerHTML).to.equal('bar-c');
|
||||||
|
};
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<div.bar>bar ${component.id}</div>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<div.foo>foo ${component.id}</div>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
class {
|
||||||
|
onCreate() {
|
||||||
|
this.state = {
|
||||||
|
count: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.root key="root">
|
||||||
|
<if(state.count === 0)>
|
||||||
|
<foo key="a" count=state.count/>
|
||||||
|
<bar key="b" count=state.count/>
|
||||||
|
<foo key="c" count=state.count/>
|
||||||
|
</if>
|
||||||
|
<else>
|
||||||
|
<foo key="a" count=state.count/>
|
||||||
|
<foo key="b" count=state.count/>
|
||||||
|
</else>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var component = helpers.mount(require('./index'), { });
|
||||||
|
|
||||||
|
var children;
|
||||||
|
|
||||||
|
children = component.getEl('root').children;
|
||||||
|
expect(children.length).to.equal(3);
|
||||||
|
|
||||||
|
component.state.count++;
|
||||||
|
component.update();
|
||||||
|
|
||||||
|
children = component.getEl('root').children;
|
||||||
|
expect(children.length).to.equal(2);
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
class {
|
||||||
|
onCreate() {
|
||||||
|
this.state = {
|
||||||
|
count: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
increment() {
|
||||||
|
this.state.count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<for(var i=0; i<state.count; i++)>
|
||||||
|
<div>${i}</div>
|
||||||
|
</for>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
class {}
|
||||||
|
|
||||||
|
<span.foo>foo ${component.id}</span>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
class {
|
||||||
|
incrementBar() {
|
||||||
|
this.getComponent('bar').increment();
|
||||||
|
this.getComponent('bar').update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.root key="root">
|
||||||
|
<foo/>
|
||||||
|
<bar key="bar"/>
|
||||||
|
<foo/>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var component = helpers.mount(require('./index'), { });
|
||||||
|
|
||||||
|
component.incrementBar();
|
||||||
|
|
||||||
|
var children = component.getEl('root').children;
|
||||||
|
expect(children[children.length-1].nodeName).to.equal('SPAN');
|
||||||
|
};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
class {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.hello>Hello<span.hello-count>${input.count}</span></div>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
class {
|
||||||
|
onCreate() {
|
||||||
|
this.state = {
|
||||||
|
count: 0,
|
||||||
|
renderHello: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.root>
|
||||||
|
[ROOT]
|
||||||
|
<span>
|
||||||
|
<hello key="hello" count=state.count if(state.renderHello)/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
module.exports = function(helpers) {
|
||||||
|
var component = helpers.mount(require('./index'), { });
|
||||||
|
var rootEl = component.el;
|
||||||
|
var helloCountEl = rootEl.querySelector('span.hello-count');
|
||||||
|
var helloComponent = component.getComponent('hello');
|
||||||
|
|
||||||
|
component.state.count = 1;
|
||||||
|
component.update();
|
||||||
|
|
||||||
|
expect(component.el).to.equal(rootEl);
|
||||||
|
expect(rootEl.querySelector('span.hello-count').innerHTML).to.equal('1');
|
||||||
|
expect(rootEl.querySelector('span.hello-count')).to.equal(helloCountEl);
|
||||||
|
|
||||||
|
component.state.renderHello = false;
|
||||||
|
component.update();
|
||||||
|
|
||||||
|
expect(helloComponent.isDestroyed()).to.equal(true);
|
||||||
|
expect(helloComponent.el == null).to.equal(true);
|
||||||
|
};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
class {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<div.hello key="helloRoot">Hello: ${input.count}</div>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user