mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
More code size reductions
This commit is contained in:
parent
74d802b074
commit
20cf6d364e
35
benchmark/size/marko/rollup.config.js
Normal file
35
benchmark/size/marko/rollup.config.js
Normal file
@ -0,0 +1,35 @@
|
||||
import commonjsPlugin from 'rollup-plugin-commonjs';
|
||||
import browserifyPlugin from 'rollup-plugin-browserify-transform';
|
||||
import nodeResolvePlugin from 'rollup-plugin-node-resolve';
|
||||
import markoify from 'markoify';
|
||||
import envify from 'envify';
|
||||
import minpropsify from 'minprops/browserify';
|
||||
import path from 'path';
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
||||
|
||||
|
||||
export default {
|
||||
entry: path.join(__dirname, 'client.js'),
|
||||
format: 'iife',
|
||||
moduleName: 'app',
|
||||
plugins: [
|
||||
browserifyPlugin(markoify),
|
||||
browserifyPlugin(envify),
|
||||
browserifyPlugin(minpropsify),
|
||||
nodeResolvePlugin({
|
||||
jsnext: true, // Default: false
|
||||
main: true, // Default: true
|
||||
browser: true, // Default: false
|
||||
preferBuiltins: false,
|
||||
extensions: [ '.js', '.marko' ]
|
||||
}),
|
||||
commonjsPlugin({
|
||||
include: [ 'node_modules/**', '**/*.marko', '**/*.js'],
|
||||
extensions: [ '.js', '.marko' ]
|
||||
})
|
||||
],
|
||||
dest: path.join(__dirname, '../build/bundles/marko.js')
|
||||
};
|
||||
@ -37,6 +37,11 @@ var minifiers = {
|
||||
};
|
||||
|
||||
const out = gcc.compile(options);
|
||||
|
||||
// if (out.errors && out.errors.length) {
|
||||
// console.error(out.errors);
|
||||
// throw new Error(`Minification failed for ${file}`);
|
||||
// }
|
||||
return out.compiledCode;
|
||||
},
|
||||
uglify: function minifyUglifyJS(src, file) {
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
"build": "npm run bundle --silent && npm run minify --silent",
|
||||
"build-marko": "npm run bundle-marko --silent && node minify.js marko",
|
||||
"bundle": "mkdir -p build/bundles && npm run bundle-marko && npm run bundle-react && npm run bundle-vue && npm run bundle-preact",
|
||||
"bundle-marko": "NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js",
|
||||
"bundle-react": "NODE_ENV=production browserify -t envify -t babelify --extension='.jsx' --global-transform minprops/browserify -o build/bundles/react.js react/client.jsx",
|
||||
"bundle-preact": "NODE_ENV=production browserify -t envify -t babelify --extension='.jsx' --global-transform minprops/browserify -o build/bundles/preact.js preact/client.jsx",
|
||||
"bundle-marko": "NODE_ENV=production rollup -c marko/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-vue": "NODE_ENV=production browserify -t envify -t vueify --extension='.vue' --global-transform minprops/browserify -o build/bundles/vue.js vue/client.js",
|
||||
"minify": "node minify.js",
|
||||
"http-server": "http-server"
|
||||
@ -18,6 +18,7 @@
|
||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"babel-plugin-transform-es2015-block-scoping": "^6.21.0",
|
||||
"babel-plugin-transform-react-constant-elements": "^6.9.1",
|
||||
"babel-plugin-transform-react-jsx": "^6.8.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
@ -35,6 +36,12 @@
|
||||
"preact": "^7.1.0",
|
||||
"react": "^15.4.1",
|
||||
"react-dom": "^15.4.1",
|
||||
"rollup": "^0.39.2",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-browserify-transform": "^0.1.0",
|
||||
"rollup-plugin-commonjs": "^7.0.0",
|
||||
"rollup-plugin-marko": "0.0.2",
|
||||
"rollup-plugin-node-resolve": "^2.0.0",
|
||||
"uglify-js": "^2.7.5",
|
||||
"vue": "^2.1.6",
|
||||
"vueify": "^9.4.0"
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
"es2015-loose",
|
||||
["es2015", { "loose": true, "modules": false }],
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": [
|
||||
[ "transform-react-jsx", { "pragma": "h" } ]
|
||||
[ "transform-react-jsx", { "pragma": "h" } ],
|
||||
["transform-es2015-block-scoping"]
|
||||
]
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { h, render } from 'preact';
|
||||
|
||||
var preact = require('preact');
|
||||
var h = preact.h;
|
||||
var render = preact.render;
|
||||
var App = require('./components/App');
|
||||
|
||||
render(
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
import { h, Component } from 'preact';
|
||||
var preact = require('preact');
|
||||
var h = preact.h;
|
||||
var Component = preact.Component;
|
||||
|
||||
function renderColor(color) {
|
||||
var style = {
|
||||
|
||||
35
benchmark/size/preact/rollup.config.js
Normal file
35
benchmark/size/preact/rollup.config.js
Normal file
@ -0,0 +1,35 @@
|
||||
import commonjsPlugin from 'rollup-plugin-commonjs';
|
||||
import browserifyPlugin from 'rollup-plugin-browserify-transform';
|
||||
import nodeResolvePlugin from 'rollup-plugin-node-resolve';
|
||||
import babelPlugin from 'rollup-plugin-babel';
|
||||
import envify from 'envify';
|
||||
import path from 'path';
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
||||
|
||||
|
||||
export default {
|
||||
entry: path.join(__dirname, 'client.jsx'),
|
||||
format: 'iife',
|
||||
moduleName: 'app',
|
||||
plugins: [
|
||||
babelPlugin({
|
||||
// include: ['node_modules/**', '**/*.js', '**/*.jsx']
|
||||
}),
|
||||
browserifyPlugin(envify),
|
||||
nodeResolvePlugin({
|
||||
jsnext: false, // Default: false
|
||||
main: true, // Default: true
|
||||
browser: true, // Default: false
|
||||
preferBuiltins: false,
|
||||
extensions: [ '.js', '.jsx' ]
|
||||
}),
|
||||
commonjsPlugin({
|
||||
include: [ 'node_modules/**', '**/*.js', '**/*.jsx'],
|
||||
extensions: [ '.js', '.jsx' ]
|
||||
})
|
||||
],
|
||||
dest: path.join(__dirname, '../build/bundles/preact.js')
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"presets": [
|
||||
"es2015-loose",
|
||||
["es2015", { "loose": true, "modules": false }],
|
||||
"stage-0",
|
||||
"react"
|
||||
],
|
||||
|
||||
35
benchmark/size/react/rollup.config.js
Normal file
35
benchmark/size/react/rollup.config.js
Normal file
@ -0,0 +1,35 @@
|
||||
import commonjsPlugin from 'rollup-plugin-commonjs';
|
||||
import browserifyPlugin from 'rollup-plugin-browserify-transform';
|
||||
import nodeResolvePlugin from 'rollup-plugin-node-resolve';
|
||||
import babelPlugin from 'rollup-plugin-babel';
|
||||
import envify from 'envify';
|
||||
import path from 'path';
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
||||
|
||||
|
||||
export default {
|
||||
entry: path.join(__dirname, 'client.jsx'),
|
||||
format: 'iife',
|
||||
moduleName: 'app',
|
||||
plugins: [
|
||||
babelPlugin({
|
||||
exclude: 'node_modules/**'
|
||||
}),
|
||||
browserifyPlugin(envify),
|
||||
nodeResolvePlugin({
|
||||
jsnext: true, // Default: false
|
||||
main: true, // Default: true
|
||||
browser: true, // Default: false
|
||||
preferBuiltins: false,
|
||||
extensions: [ '.js', '.jsx' ]
|
||||
}),
|
||||
commonjsPlugin({
|
||||
include: [ 'node_modules/**', '**/*.js', '**/*.jsx'],
|
||||
extensions: [ '.js', '.jsx' ]
|
||||
})
|
||||
],
|
||||
dest: path.join(__dirname, '../build/bundles/react.js')
|
||||
};
|
||||
33
benchmark/size/vue/rollup.config.js
Normal file
33
benchmark/size/vue/rollup.config.js
Normal file
@ -0,0 +1,33 @@
|
||||
import commonjsPlugin from 'rollup-plugin-commonjs';
|
||||
import browserifyPlugin from 'rollup-plugin-browserify-transform';
|
||||
import nodeResolvePlugin from 'rollup-plugin-node-resolve';
|
||||
import vueify from 'vueify';
|
||||
import envify from 'envify';
|
||||
import minpropsify from 'minprops/browserify';
|
||||
import path from 'path';
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
|
||||
export default {
|
||||
entry: path.join(__dirname, 'client.js'),
|
||||
format: 'iife',
|
||||
moduleName: 'app',
|
||||
plugins: [
|
||||
browserifyPlugin(vueify),
|
||||
browserifyPlugin(envify),
|
||||
browserifyPlugin(minpropsify),
|
||||
nodeResolvePlugin({
|
||||
jsnext: true, // Default: false
|
||||
main: true, // Default: true
|
||||
browser: true, // Default: false
|
||||
preferBuiltins: false,
|
||||
extensions: [ '.js', '.vue' ]
|
||||
}),
|
||||
commonjsPlugin({
|
||||
include: [ 'node_modules/**', '**/*.vue', '**/*.js'],
|
||||
extensions: [ '.js', '.vue' ]
|
||||
})
|
||||
],
|
||||
dest: path.join(__dirname, '../build/bundles/vue.js')
|
||||
};
|
||||
@ -58,22 +58,29 @@ const helpers = {
|
||||
'classList': 'cl',
|
||||
'const': 'const',
|
||||
'createElement': 'e',
|
||||
'createInlineTemplate': 'inline',
|
||||
'createInlineTemplate': {
|
||||
vdom: { module: 'marko/runtime/vdom/helper-createInlineTemplate'},
|
||||
html: { module: 'marko/runtime/html/helper-createInlineTemplate'}
|
||||
},
|
||||
'escapeXml': 'x',
|
||||
'escapeXmlAttr': 'xa',
|
||||
'escapeScript': 'xs',
|
||||
'forEach': 'f',
|
||||
'forEachProp': 'fp',
|
||||
'forEachWithStatusVar': 'fv',
|
||||
'forRange': 'fr',
|
||||
'forEachProp': { module: 'marko/runtime/helper-forEachProperty' },
|
||||
'forEachPropStatusVar': { module: 'marko/runtime/helper-forEachPropStatusVar' },
|
||||
'forEachWithStatusVar': { module: 'marko/runtime/helper-forEachWithStatusVar' },
|
||||
'forRange': { module: 'marko/runtime/helper-forRange' },
|
||||
'include': 'i',
|
||||
'loadNestedTag': 'n',
|
||||
'loadNestedTag': { module: 'marko/runtime/helper-loadNestedTag' },
|
||||
'loadTag': 't',
|
||||
'loadTemplate': 'l',
|
||||
'mergeNestedTagsHelper': 'mn',
|
||||
'merge': 'm',
|
||||
'loadTemplate': { module: 'marko/runtime/helper-loadTemplate' },
|
||||
'mergeNestedTagsHelper': { module: 'marko/runtime/helper-mergeNestedTags' },
|
||||
'merge': { module: 'marko/runtime/helper-merge' },
|
||||
'str': 's',
|
||||
'styleAttr': 'sa',
|
||||
'styleAttr': {
|
||||
vdom: { module: 'marko/runtime/vdom/helper-styleAttr'},
|
||||
html: 'sa'
|
||||
},
|
||||
'createText': 't'
|
||||
};
|
||||
|
||||
@ -672,15 +679,32 @@ class CompileContext extends EventEmitter {
|
||||
helper(name) {
|
||||
var helperIdentifier = this._helpers[name];
|
||||
if (!helperIdentifier) {
|
||||
var methodName = helpers[name];
|
||||
if (!methodName) {
|
||||
var helperInfo = helpers[name];
|
||||
|
||||
if (helperInfo && typeof helperInfo === 'object') {
|
||||
if (!helperInfo.module) {
|
||||
helperInfo = helperInfo[this.outputType];
|
||||
}
|
||||
}
|
||||
|
||||
if (!helperInfo) {
|
||||
throw new Error('Invalid helper: ' + name);
|
||||
}
|
||||
var methodIdentifier = this.builder.identifier(methodName);
|
||||
|
||||
helperIdentifier = this.addStaticVar(
|
||||
'marko_' + name,
|
||||
this.builder.memberExpression(this.helpersIdentifier, methodIdentifier));
|
||||
if (typeof helperInfo === 'string') {
|
||||
let methodName = helperInfo;
|
||||
var methodIdentifier = this.builder.identifier(methodName);
|
||||
|
||||
helperIdentifier = this.addStaticVar(
|
||||
'marko_' + name,
|
||||
this.builder.memberExpression(this.helpersIdentifier, methodIdentifier));
|
||||
} else if (helperInfo && helperInfo.module) {
|
||||
helperIdentifier = this.addStaticVar(
|
||||
'marko_' + name,
|
||||
this.builder.require(this.builder.literal(helperInfo.module)));
|
||||
} else {
|
||||
throw new Error('Invalid helper: ' + name);
|
||||
}
|
||||
|
||||
this._helpers[name] = helperIdentifier;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ class ForEachProp extends Node {
|
||||
var builder = codegen.builder;
|
||||
|
||||
if (statusVarName) {
|
||||
let helperVar = builder.require(builder.literal('marko/runtime/forEachPropStatusVar'));
|
||||
let helperVar = context.helper('forEachPropStatusVar');
|
||||
let forEachVarName = codegen.addStaticVar('forEacPropStatusVar', helperVar);
|
||||
let body = this.body;
|
||||
|
||||
|
||||
@ -62,6 +62,11 @@ class TemplateRoot extends Node {
|
||||
renderStatements)
|
||||
]);
|
||||
} else {
|
||||
var isBrowser = context.options.browser;
|
||||
var createArgs = isBrowser ?
|
||||
[] :
|
||||
[ builder.identifier('__filename') ];
|
||||
|
||||
let templateDeclaration = builder.variableDeclarator('marko_template',
|
||||
builder.assignment(
|
||||
builder.moduleExports(),
|
||||
@ -72,9 +77,7 @@ class TemplateRoot extends Node {
|
||||
),
|
||||
builder.identifier('t')
|
||||
),
|
||||
[
|
||||
builder.identifier('__filename')
|
||||
]
|
||||
createArgs
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
"lasso-package-root": "^1.0.0",
|
||||
"listener-tracker": "^2.0.0",
|
||||
"minimatch": "^3.0.2",
|
||||
"morphdom": "^2.2.0",
|
||||
"morphdom": "^2.3.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"property-handlers": "^1.0.0",
|
||||
"raptor-async": "^1.1.2",
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
var events = require('./events');
|
||||
var domInsert = require('./dom-insert');
|
||||
var EMPTY_ARRAY = [];
|
||||
|
||||
function checkAddedToDOM(result, method) {
|
||||
if (!result.$__widgets) {
|
||||
@ -57,27 +55,24 @@ var proto = RenderResult.prototype = {
|
||||
afterInsert: function(doc) {
|
||||
var out = this.$__out;
|
||||
var widgetsContext = out.global.widgets;
|
||||
this.$__widgets = (widgetsContext && widgetsContext.$__widgets) || EMPTY_ARRAY;
|
||||
|
||||
events.emit('mountNode', {
|
||||
result: this,
|
||||
out: this.$__out,
|
||||
document: doc
|
||||
}); // NOTE: This will trigger widgets to be initialized if there were any
|
||||
if (widgetsContext) {
|
||||
this.$__widgets = widgetsContext.$__widgets;
|
||||
widgetsContext.$__initWidgets(doc);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
getNode: function(doc) {
|
||||
return this.$__out.getNode(doc);
|
||||
return this.$__out.$__getNode(doc);
|
||||
},
|
||||
getOutput: function() {
|
||||
return this.$__out.getOutput();
|
||||
return this.$__out.$__getOutput();
|
||||
},
|
||||
toString: function() {
|
||||
return this.$__out.toString();
|
||||
},
|
||||
toJSON: function() {
|
||||
return this.getOutput();
|
||||
return this.$__out.$__getOutput();
|
||||
},
|
||||
document: typeof document !== 'undefined' && document
|
||||
};
|
||||
|
||||
13
runtime/createOut.js
Normal file
13
runtime/createOut.js
Normal file
@ -0,0 +1,13 @@
|
||||
var actualCreateOut;
|
||||
|
||||
function setCreateOut(createOutFunc) {
|
||||
actualCreateOut = createOutFunc;
|
||||
}
|
||||
|
||||
function createOut(globalData) {
|
||||
return actualCreateOut(globalData);
|
||||
}
|
||||
|
||||
createOut.$__setCreateOut = setCreateOut;
|
||||
|
||||
module.exports = createOut;
|
||||
@ -1,70 +1,77 @@
|
||||
var path = require('path');
|
||||
var resolveFrom = require('resolve-from');
|
||||
var Template = require('./html').Template;
|
||||
var Template = require('./html/Template');
|
||||
|
||||
function getDeps(template) {
|
||||
if(!template.meta && template.template) {
|
||||
template = template.template;
|
||||
}
|
||||
if (!template.meta && template.template) {
|
||||
template = template.template;
|
||||
}
|
||||
|
||||
if(!(template instanceof Template)) {
|
||||
return [];
|
||||
}
|
||||
if (!(template instanceof Template)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if(false && template.deps) {
|
||||
return template.deps;
|
||||
}
|
||||
if (false && template.deps) {
|
||||
return template.deps;
|
||||
}
|
||||
|
||||
if(!template.meta) {
|
||||
console.error('Metadata not set for template at ', template.path);
|
||||
return [];
|
||||
}
|
||||
if (!template.meta) {
|
||||
console.error('Metadata not set for template at ', template.path);
|
||||
return [];
|
||||
}
|
||||
|
||||
var meta = template.meta;
|
||||
var root = path.dirname(template.path);
|
||||
var deps = [];
|
||||
var meta = template.meta;
|
||||
var root = path.dirname(template.path);
|
||||
var deps = [];
|
||||
|
||||
if(meta.tags) {
|
||||
meta.tags.forEach(tagPath => {
|
||||
var tag = resolveFrom(root, tagPath);
|
||||
var tagDeps = getDeps(require(tag));
|
||||
deps.push.apply(deps, tagDeps);
|
||||
});
|
||||
}
|
||||
if (meta.tags) {
|
||||
meta.tags.forEach(tagPath => {
|
||||
var tag = resolveFrom(root, tagPath);
|
||||
var tagDeps = getDeps(require(tag));
|
||||
deps.push.apply(deps, tagDeps);
|
||||
});
|
||||
}
|
||||
|
||||
if(meta.deps) {
|
||||
deps.push.apply(deps, meta.deps.map(d => resolveDep(d, root)));
|
||||
}
|
||||
if (meta.deps) {
|
||||
deps.push.apply(deps, meta.deps.map(d => resolveDep(d, root)));
|
||||
}
|
||||
|
||||
deps = dedupeDeps(deps);
|
||||
deps = dedupeDeps(deps);
|
||||
|
||||
template.deps = deps;
|
||||
template.deps = deps;
|
||||
|
||||
return deps;
|
||||
return deps;
|
||||
}
|
||||
|
||||
function resolveDep(dep, root) {
|
||||
if(typeof dep === 'string') {
|
||||
dep = parseDependencyString(dep);
|
||||
}
|
||||
if(dep.path) {
|
||||
return Object.assign({}, dep, { path:resolveFrom(root, dep.path) });
|
||||
} else if(dep.virtualPath) {
|
||||
return Object.assign({}, dep, { virtualPath:path.resolve(root, dep.virtualPath) });
|
||||
} else {
|
||||
return dep;
|
||||
}
|
||||
if (typeof dep === 'string') {
|
||||
dep = parseDependencyString(dep);
|
||||
}
|
||||
if (dep.path) {
|
||||
return Object.assign({}, dep, {
|
||||
path: resolveFrom(root, dep.path)
|
||||
});
|
||||
} else if (dep.virtualPath) {
|
||||
return Object.assign({}, dep, {
|
||||
virtualPath: path.resolve(root, dep.virtualPath)
|
||||
});
|
||||
} else {
|
||||
return dep;
|
||||
}
|
||||
}
|
||||
|
||||
function parseDependencyString(string) {
|
||||
var match = /^(?:([\w-]+)(?:\:\s*|\s+))?(.*?(?:\.(\w+))?)$/.exec(string);
|
||||
return { type:match[1]||match[3], path:match[2] };
|
||||
var match = /^(?:([\w-]+)(?:\:\s*|\s+))?(.*?(?:\.(\w+))?)$/.exec(string);
|
||||
return {
|
||||
type: match[1] || match[3],
|
||||
path: match[2]
|
||||
};
|
||||
}
|
||||
|
||||
function dedupeDeps(deps) {
|
||||
return deps;
|
||||
return deps;
|
||||
}
|
||||
|
||||
require('./html').Template.prototype.getDependencies = function() {
|
||||
Template.prototype.getDependencies = function() {
|
||||
return getDeps(this);
|
||||
};
|
||||
@ -1 +0,0 @@
|
||||
exports.$__document = typeof document != 'undefined' && document;
|
||||
@ -1,5 +1,7 @@
|
||||
var events = require('./events');
|
||||
var extend = require('raptor-util/extend');
|
||||
var widgetsUtil = require('../widgets/util');
|
||||
var destroyWidgetForEl = widgetsUtil.$__destroyWidgetForEl;
|
||||
var destroyElRecursive = widgetsUtil.$__destroyElRecursive;
|
||||
|
||||
function resolveEl(el) {
|
||||
if (typeof el === 'string') {
|
||||
@ -13,33 +15,34 @@ function resolveEl(el) {
|
||||
}
|
||||
|
||||
function beforeRemove(referenceEl) {
|
||||
events.emit('dom/beforeRemove', {
|
||||
el: referenceEl
|
||||
});
|
||||
destroyElRecursive(referenceEl);
|
||||
destroyWidgetForEl(referenceEl);
|
||||
}
|
||||
|
||||
module.exports = function(target, getEl, afterInsert) {
|
||||
extend(target, {
|
||||
appendTo: function(referenceEl) {
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
resolveEl(referenceEl).appendChild(el);
|
||||
referenceEl.appendChild(el);
|
||||
return afterInsert(this, referenceEl);
|
||||
},
|
||||
prependTo: function(referenceEl) {
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl.insertBefore(el, referenceEl.firstChild || null);
|
||||
return afterInsert(this, referenceEl);
|
||||
},
|
||||
replace: function(referenceEl) {
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
beforeRemove(referenceEl);
|
||||
referenceEl.parentNode.replaceChild(el, referenceEl);
|
||||
return afterInsert(this, referenceEl);
|
||||
},
|
||||
replaceChildrenOf: function(referenceEl) {
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
|
||||
var curChild = referenceEl.firstChild;
|
||||
while(curChild) {
|
||||
@ -53,14 +56,14 @@ module.exports = function(target, getEl, afterInsert) {
|
||||
return afterInsert(this, referenceEl);
|
||||
},
|
||||
insertBefore: function(referenceEl) {
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl.parentNode.insertBefore(el, referenceEl);
|
||||
return afterInsert(this, referenceEl);
|
||||
},
|
||||
insertAfter: function(referenceEl) {
|
||||
var el = getEl(this, referenceEl);
|
||||
referenceEl = resolveEl(referenceEl);
|
||||
var el = getEl(this, referenceEl);
|
||||
el = el;
|
||||
var nextSibling = referenceEl.nextSibling;
|
||||
var parentNode = referenceEl.parentNode;
|
||||
|
||||
30
runtime/env-init.js
Normal file
30
runtime/env-init.js
Normal file
@ -0,0 +1,30 @@
|
||||
require('./stream');
|
||||
require('./dependencies');
|
||||
|
||||
if (process.env.hasOwnProperty('MARKO_HOT_RELOAD')) {
|
||||
require('../hot-reload').enable();
|
||||
}
|
||||
|
||||
// If process was launched with browser refresh then automatically
|
||||
// enable browser-refresh
|
||||
require('../browser-refresh').enable();
|
||||
|
||||
function fixFlush() {
|
||||
try {
|
||||
var OutgoingMessage = require('http').OutgoingMessage;
|
||||
if (OutgoingMessage.prototype.flush && OutgoingMessage.prototype.flush.toString().indexOf('deprecated') !== -1) {
|
||||
// Yes, we are monkey-patching http. This method should never have been added and it was introduced on
|
||||
// the iojs fork. It was quickly deprecated and I'm 99% sure no one is actually using it.
|
||||
// See:
|
||||
// - https://github.com/marko-js/async-writer/issues/3
|
||||
// - https://github.com/nodejs/node/issues/2920
|
||||
//
|
||||
// This method causes problems since marko looks for the flush method and calls it found.
|
||||
// The `res.flush()` method is introduced by the [compression](https://www.npmjs.com/package/compression)
|
||||
// middleware, but, otherwise, it should typically not exist.
|
||||
delete require('http').OutgoingMessage.prototype.flush;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
fixFlush();
|
||||
@ -7,7 +7,7 @@ function LoopStatus(getLength, isLast, isFirst, getIndex) {
|
||||
this.getIndex = getIndex;
|
||||
}
|
||||
|
||||
module.exports = function forEachPropStatusVar(object, callback) {
|
||||
module.exports = function forEachPropStatusVarHelper(object, callback) {
|
||||
var keys = Object.keys(object);
|
||||
|
||||
var i = 0;
|
||||
27
runtime/helper-forEachProperty.js
Normal file
27
runtime/helper-forEachProperty.js
Normal file
@ -0,0 +1,27 @@
|
||||
var isArray = Array.isArray;
|
||||
|
||||
/**
|
||||
* Internal helper method for looping over the properties of any object
|
||||
* @private
|
||||
*/
|
||||
module.exports = function forEachPropertyHelper(o, func) {
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isArray(o)) {
|
||||
for (var i=0; i<o.length; i++) {
|
||||
func(i, o[i]);
|
||||
}
|
||||
} else if (typeof Map && o instanceof Map) {
|
||||
o.forEach(function(v, k) {
|
||||
func(k, v);
|
||||
});
|
||||
} else {
|
||||
for (var k in o) {
|
||||
if (o.hasOwnProperty(k)) {
|
||||
func(k, o[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
40
runtime/helper-forEachWithStatusVar.js
Normal file
40
runtime/helper-forEachWithStatusVar.js
Normal file
@ -0,0 +1,40 @@
|
||||
function LoopStatus(len) {
|
||||
this.i = 0;
|
||||
this.len = len;
|
||||
}
|
||||
|
||||
LoopStatus.prototype = {
|
||||
getLength: function() {
|
||||
return this.len;
|
||||
},
|
||||
isLast: function() {
|
||||
return this.i === this.len - 1;
|
||||
},
|
||||
isFirst: function() {
|
||||
return this.i === 0;
|
||||
},
|
||||
getIndex: function() {
|
||||
return this.i;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method to handle loops with a status variable
|
||||
* @private
|
||||
*/
|
||||
module.exports = function forEachStatusVariableHelper(array, callback) {
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
if (!array.forEach) {
|
||||
array = [array];
|
||||
}
|
||||
|
||||
var len = array.length;
|
||||
var loopStatus = new LoopStatus(len);
|
||||
|
||||
for (; loopStatus.i < len; loopStatus.i++) {
|
||||
var o = array[loopStatus.i];
|
||||
callback(o, loopStatus);
|
||||
}
|
||||
};
|
||||
18
runtime/helper-forRange.js
Normal file
18
runtime/helper-forRange.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = function forRangeHelper(from, to, step, callback) {
|
||||
if (step == null) {
|
||||
step = from <= to ? 1 : -1;
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
if (step > 0) {
|
||||
for (i=from; i<=to; i += step) {
|
||||
callback(i);
|
||||
}
|
||||
} else {
|
||||
for (i=from; i>=to; i += step) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
15
runtime/helper-loadNestedTag.js
Normal file
15
runtime/helper-loadNestedTag.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = function loadNestedTagHelper(targetProperty, isRepeated) {
|
||||
return function(input, parent) {
|
||||
// If we are nested tag then we do not have a renderer
|
||||
if (isRepeated) {
|
||||
var existingArray = parent[targetProperty];
|
||||
if (existingArray) {
|
||||
existingArray.push(input);
|
||||
} else {
|
||||
parent[targetProperty] = [input];
|
||||
}
|
||||
} else {
|
||||
parent[targetProperty] = input;
|
||||
}
|
||||
};
|
||||
};
|
||||
4
runtime/helper-loadTemplate.js
Normal file
4
runtime/helper-loadTemplate.js
Normal file
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Loads a template
|
||||
*/
|
||||
module.exports = require('./loader');
|
||||
16
runtime/helper-merge.js
Normal file
16
runtime/helper-merge.js
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Merges object properties
|
||||
* @param {[type]} object [description]
|
||||
* @param {[type]} source [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function merge(into, source) {
|
||||
for (var k in source) {
|
||||
if (source.hasOwnProperty(k) && !into.hasOwnProperty(k)) {
|
||||
into[k] = source[k];
|
||||
}
|
||||
}
|
||||
return into;
|
||||
}
|
||||
|
||||
module.exports = merge;
|
||||
15
runtime/helper-mergeNestedTags.js
Normal file
15
runtime/helper-mergeNestedTags.js
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Merges nested tags by rendering the body
|
||||
* @param {[type]} object [description]
|
||||
* @param {[type]} source [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function mergeNestedTags(input) {
|
||||
if (input.renderBody) {
|
||||
input.renderBody(null, input);
|
||||
}
|
||||
input.renderBody = null;
|
||||
return input;
|
||||
}
|
||||
|
||||
module.exports = mergeNestedTags;
|
||||
@ -1,6 +1,10 @@
|
||||
'use strict';
|
||||
var isArray = Array.isArray;
|
||||
|
||||
function isFunction(arg) {
|
||||
return typeof arg === 'function';
|
||||
}
|
||||
|
||||
function classListHelper(arg, classNames) {
|
||||
var len;
|
||||
|
||||
@ -40,8 +44,8 @@ function createDeferredRenderer(handler) {
|
||||
// This is the initial function that will do the rendering. We replace
|
||||
// the renderer with the actual renderer func on the first render
|
||||
deferredRenderer.renderer = function(input, out) {
|
||||
var rendererFunc = handler.renderer || handler.render;
|
||||
if (typeof rendererFunc !== 'function') {
|
||||
var rendererFunc = handler.renderer || handler._ || handler.render;
|
||||
if (!isFunction(rendererFunc)) {
|
||||
throw Error('Invalid renderer');
|
||||
}
|
||||
// Use the actual renderer from now on
|
||||
@ -59,14 +63,10 @@ function resolveRenderer(handler) {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
if (typeof handler === 'function') {
|
||||
if (isFunction(handler)) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
if (typeof (renderer = handler.render) === 'function') {
|
||||
return renderer.bind(handler);
|
||||
}
|
||||
|
||||
// If the user code has a circular function then the renderer function
|
||||
// may not be available on the module. Since we can't get a reference
|
||||
// to the actual renderer(input, out) function right now we lazily
|
||||
@ -74,26 +74,6 @@ function resolveRenderer(handler) {
|
||||
return createDeferredRenderer(handler);
|
||||
}
|
||||
|
||||
function LoopStatus(len) {
|
||||
this.i = 0;
|
||||
this.len = len;
|
||||
}
|
||||
|
||||
LoopStatus.prototype = {
|
||||
getLength: function() {
|
||||
return this.len;
|
||||
},
|
||||
isLast: function() {
|
||||
return this.i === this.len - 1;
|
||||
},
|
||||
isFirst: function() {
|
||||
return this.i === 0;
|
||||
},
|
||||
getIndex: function() {
|
||||
return this.i;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method to prevent null/undefined from being written out
|
||||
* when writing text that resolves to null/undefined
|
||||
@ -103,27 +83,6 @@ exports.s = function strHelper(str) {
|
||||
return (str == null) ? '' : str.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method to handle loops with a status variable
|
||||
* @private
|
||||
*/
|
||||
exports.fv = function forEachStatusVariableHelper(array, callback) {
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
if (!array.forEach) {
|
||||
array = [array];
|
||||
}
|
||||
|
||||
var len = array.length;
|
||||
var loopStatus = new LoopStatus(len);
|
||||
|
||||
for (; loopStatus.i < len; loopStatus.i++) {
|
||||
var o = array[loopStatus.i];
|
||||
callback(o, loopStatus);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method to handle loops without a status variable
|
||||
* @private
|
||||
@ -133,57 +92,12 @@ exports.f = function forEachHelper(array, callback) {
|
||||
for (var i=0; i<array.length; i++) {
|
||||
callback(array[i]);
|
||||
}
|
||||
} else if (typeof array === 'function') {
|
||||
} else if (isFunction(array)) {
|
||||
// Also allow the first argument to be a custom iterator function
|
||||
array(callback);
|
||||
}
|
||||
};
|
||||
|
||||
exports.fr = function forRangeHelper(from, to, step, callback) {
|
||||
if (step == null) {
|
||||
step = from <= to ? 1 : -1;
|
||||
}
|
||||
|
||||
var i;
|
||||
|
||||
if (step > 0) {
|
||||
for (i=from; i<=to; i += step) {
|
||||
callback(i);
|
||||
}
|
||||
} else {
|
||||
for (i=from; i>=to; i += step) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method for looping over the properties of any object
|
||||
* @private
|
||||
*/
|
||||
exports.fp = function forEachPropertyHelper(o, func) {
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(o)) {
|
||||
for (var i=0; i<o.length; i++) {
|
||||
func(i, o[i]);
|
||||
}
|
||||
} else if (typeof Map && o instanceof Map) {
|
||||
o.forEach(function(v, k) {
|
||||
func(k, v);
|
||||
});
|
||||
} else {
|
||||
for (var k in o) {
|
||||
if (o.hasOwnProperty(k)) {
|
||||
func(k, o[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to load a custom tag
|
||||
*/
|
||||
@ -195,51 +109,6 @@ exports.t = function loadTagHelper(renderer, targetProperty, isRepeated) {
|
||||
return renderer;
|
||||
};
|
||||
|
||||
exports.n = function loadNestedTagHelper(targetProperty, isRepeated) {
|
||||
return function(input, parent) {
|
||||
// If we are nested tag then we do not have a renderer
|
||||
if (isRepeated) {
|
||||
var existingArray = parent[targetProperty];
|
||||
if (existingArray) {
|
||||
existingArray.push(input);
|
||||
} else {
|
||||
parent[targetProperty] = [input];
|
||||
}
|
||||
} else {
|
||||
parent[targetProperty] = input;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges object properties
|
||||
* @param {[type]} object [description]
|
||||
* @param {[type]} source [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
exports.m = function mergeHelper(into, source) {
|
||||
for (var k in source) {
|
||||
if (source.hasOwnProperty(k) && !into.hasOwnProperty(k)) {
|
||||
into[k] = source[k];
|
||||
}
|
||||
}
|
||||
return into;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges nested tags by rendering the body
|
||||
* @param {[type]} object [description]
|
||||
* @param {[type]} source [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
exports.mn = function mergeNestedTagsHelper(input) {
|
||||
if (input.renderBody) {
|
||||
input.renderBody(null, input);
|
||||
}
|
||||
input.renderBody = null;
|
||||
return input;
|
||||
};
|
||||
|
||||
/**
|
||||
* classList(a, b, c, ...)
|
||||
* Joines a list of class names with spaces. Empty class names are omitted.
|
||||
@ -249,9 +118,4 @@ exports.mn = function mergeNestedTagsHelper(input) {
|
||||
*/
|
||||
exports.cl = function classListHelper() {
|
||||
return classList(arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a template (__helpers.l --> marko_loadTemplate(path))
|
||||
*/
|
||||
exports.l = require('./loader');
|
||||
};
|
||||
@ -2,9 +2,10 @@
|
||||
var EventEmitter = require('events-light');
|
||||
var StringWriter = require('./StringWriter');
|
||||
var BufferedWriter = require('./BufferedWriter');
|
||||
var documentProvider = require('../document-provider');
|
||||
var defaultDocument = typeof document != 'undefined' && document;
|
||||
var RenderResult = require('../RenderResult');
|
||||
var helpers;
|
||||
var attrsHelper = require('./helper-attrs');
|
||||
var escapeXml = require('./escape').escapeXml;
|
||||
|
||||
var voidWriter = { write:function(){} };
|
||||
|
||||
@ -37,7 +38,7 @@ function AsyncStream(global, writer, state, shouldBuffer) {
|
||||
writer = new BufferedWriter(writer);
|
||||
}
|
||||
} else {
|
||||
writer = originalStream = new StringWriter(events);
|
||||
writer = originalStream = new StringWriter();
|
||||
}
|
||||
|
||||
state = new State(this, originalStream, writer, events);
|
||||
@ -69,7 +70,8 @@ AsyncStream.enableAsyncStackTrace = function() {
|
||||
|
||||
var proto = AsyncStream.prototype = {
|
||||
constructor: AsyncStream,
|
||||
isOut: true,
|
||||
$__document: defaultDocument,
|
||||
$__isOut: true,
|
||||
|
||||
sync: function() {
|
||||
this._sync = true;
|
||||
@ -86,15 +88,22 @@ var proto = AsyncStream.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
$__getOutput: function() {
|
||||
return this._state.writer.toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Legacy...
|
||||
*/
|
||||
getOutput: function() {
|
||||
return this.$__getOutput();
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return this._state.writer.toString();
|
||||
},
|
||||
|
||||
getResult: function() {
|
||||
$__getResult: function() {
|
||||
this._result = this._result || new RenderResult(this);
|
||||
return this._result;
|
||||
},
|
||||
@ -228,10 +237,11 @@ var proto = AsyncStream.prototype = {
|
||||
|
||||
if (remaining === 0) {
|
||||
state.finished = true;
|
||||
|
||||
if (state.writer.end) {
|
||||
state.writer.end();
|
||||
} else {
|
||||
state.events.emit('finish', this);
|
||||
state.events.emit('finish', this.$__getResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,7 +310,7 @@ var proto = AsyncStream.prototype = {
|
||||
var state = this._state;
|
||||
|
||||
if (event === 'finish' && state.finished) {
|
||||
callback(this);
|
||||
callback(this.$__getResult());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -312,7 +322,7 @@ var proto = AsyncStream.prototype = {
|
||||
var state = this._state;
|
||||
|
||||
if (event === 'finish' && state.finished) {
|
||||
callback(this);
|
||||
callback(this.$__getResult());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -420,7 +430,7 @@ var proto = AsyncStream.prototype = {
|
||||
|
||||
element: function(tagName, elementAttrs, openTagOnly) {
|
||||
var str = '<' + tagName +
|
||||
helpers.as(elementAttrs) +
|
||||
attrsHelper(elementAttrs) +
|
||||
'>';
|
||||
|
||||
if (openTagOnly !== true) {
|
||||
@ -433,7 +443,7 @@ var proto = AsyncStream.prototype = {
|
||||
beginElement: function(name, elementAttrs) {
|
||||
|
||||
var str = '<' + name +
|
||||
helpers.as(elementAttrs) +
|
||||
attrsHelper(elementAttrs) +
|
||||
'>';
|
||||
|
||||
this.write(str);
|
||||
@ -451,17 +461,17 @@ var proto = AsyncStream.prototype = {
|
||||
},
|
||||
|
||||
text: function(str) {
|
||||
this.write(helpers.x(str));
|
||||
this.write(escapeXml(str));
|
||||
},
|
||||
|
||||
getNode: function(doc) {
|
||||
$__getNode: function(doc) {
|
||||
var node = this._node;
|
||||
var curEl;
|
||||
var newBodyEl;
|
||||
var html = this.getOutput();
|
||||
var html = this.$__getOutput();
|
||||
|
||||
if (!doc) {
|
||||
doc = documentProvider.$__document;
|
||||
doc = this.$__document;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
@ -491,8 +501,8 @@ var proto = AsyncStream.prototype = {
|
||||
var out = this;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
out.on('error', reject);
|
||||
out.on('finish', function() {
|
||||
resolve(out.getResult());
|
||||
out.on('finish', function(result) {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
@ -507,6 +517,4 @@ var proto = AsyncStream.prototype = {
|
||||
// alias:
|
||||
proto.w = proto.write;
|
||||
|
||||
module.exports = AsyncStream;
|
||||
|
||||
helpers = require('./helpers');
|
||||
module.exports = AsyncStream;
|
||||
@ -1,19 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
function StringWriter(events) {
|
||||
function StringWriter() {
|
||||
this.str = '';
|
||||
this.events = events;
|
||||
this.finished = false;
|
||||
}
|
||||
|
||||
StringWriter.prototype = {
|
||||
end: function() {
|
||||
this.finished = true;
|
||||
if (this.events) {
|
||||
this.events.emit('finish');
|
||||
}
|
||||
},
|
||||
|
||||
write: function(str) {
|
||||
this.str += str;
|
||||
return this;
|
||||
|
||||
25
runtime/html/Template.js
Normal file
25
runtime/html/Template.js
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
var AsyncStream = require('./AsyncStream');
|
||||
var makeRenderable = require('../renderable');
|
||||
|
||||
function Template(path, renderFunc, options) {
|
||||
this.path = path;
|
||||
this._ = renderFunc;
|
||||
this.$__shouldBuffer = !options || options.shouldBuffer !== false;
|
||||
this.meta = undefined;
|
||||
}
|
||||
|
||||
function createOut(globalData, parent, state, buffer) {
|
||||
return new AsyncStream(globalData, parent, state, buffer);
|
||||
}
|
||||
|
||||
Template.prototype = {
|
||||
createOut: createOut,
|
||||
stream: function() {
|
||||
throw new Error('You must require("marko/stream")');
|
||||
}
|
||||
};
|
||||
|
||||
makeRenderable(Template.prototype);
|
||||
|
||||
module.exports = Template;
|
||||
52
runtime/html/escape.js
Normal file
52
runtime/html/escape.js
Normal file
@ -0,0 +1,52 @@
|
||||
var elTest = /[&<]/;
|
||||
var elTestReplace = /[&<]/g;
|
||||
var attrTest = /[&<\"\n]/;
|
||||
var attrReplace = /[&<\"\n]/g;
|
||||
|
||||
var replacements = {
|
||||
'<': '<',
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
'\n': ' ' //Preserve new lines so that they don't get normalized as space
|
||||
};
|
||||
|
||||
function replaceChar(match) {
|
||||
return replacements[match];
|
||||
}
|
||||
|
||||
function escapeString(str, regexpTest, regexpReplace) {
|
||||
return regexpTest.test(str) ? str.replace(regexpReplace, replaceChar) : str;
|
||||
}
|
||||
|
||||
function escapeXmlHelper(value, regexpTest, regexpReplace) {
|
||||
// check for most common case first
|
||||
if (typeof value === 'string') {
|
||||
return escapeString(value, regexpTest, regexpReplace);
|
||||
} else if (value == null) {
|
||||
return '';
|
||||
} else if (typeof value === 'object') {
|
||||
var safeHTML = value.safeHTML;
|
||||
if (safeHTML != null) {
|
||||
return value.safeHTML;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else if (value === true || value === false || typeof value === 'number') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return escapeString(value.toString(), regexpTest, regexpReplace);
|
||||
}
|
||||
|
||||
function escapeXml(value) {
|
||||
return escapeXmlHelper(value, elTest, elTestReplace);
|
||||
}
|
||||
|
||||
function escapeXmlAttr(value) {
|
||||
return escapeXmlHelper(value, attrTest, attrReplace);
|
||||
}
|
||||
|
||||
exports.escapeString = escapeString;
|
||||
exports.escapeXml = escapeXml;
|
||||
exports.escapeXmlAttr = escapeXmlAttr;
|
||||
29
runtime/html/helper-attr.js
Normal file
29
runtime/html/helper-attr.js
Normal file
@ -0,0 +1,29 @@
|
||||
var warp10 = require('warp10');
|
||||
|
||||
var escape = require('./escape');
|
||||
var escapeString = escape.escapeString;
|
||||
var escapeXmlAttr = escape.escapeXmlAttr;
|
||||
|
||||
var stringifiedAttrTest = /[&\'\n]/;
|
||||
var stringifiedAttrReplace = /[&\'\n]/g;
|
||||
|
||||
function attr(name, value, shouldEscape) {
|
||||
if (typeof value === 'string') {
|
||||
return ' ' + name + '="' + (shouldEscape !== false ? escapeXmlAttr(value) : value) + '"';
|
||||
} else if (value === true) {
|
||||
return ' ' + name;
|
||||
} else if (value == null || value === false) {
|
||||
return '';
|
||||
} else if (typeof value === 'object') {
|
||||
if (name.substring(0, 6) === 'data-_') {
|
||||
value = warp10.stringify(value);
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
return ' ' + name + "='" + escapeString(value, stringifiedAttrTest, stringifiedAttrReplace) + "'";
|
||||
} else {
|
||||
return ' ' + name + '=' + value; // number (doesn't need quotes)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = attr;
|
||||
16
runtime/html/helper-attrs.js
Normal file
16
runtime/html/helper-attrs.js
Normal file
@ -0,0 +1,16 @@
|
||||
var attrHelper = require('./helper-attr');
|
||||
|
||||
function attrs(arg) {
|
||||
if (typeof arg === 'object') {
|
||||
var out = '';
|
||||
for (var attrName in arg) {
|
||||
out += attrHelper(attrName, arg[attrName]);
|
||||
}
|
||||
return out;
|
||||
} else if (typeof arg === 'string') {
|
||||
return arg;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
module.exports = attrs;
|
||||
5
runtime/html/helper-createInlineTemplate.js
Normal file
5
runtime/html/helper-createInlineTemplate.js
Normal file
@ -0,0 +1,5 @@
|
||||
var Template = require('./Template');
|
||||
|
||||
module.exports = function(path, renderFunc) {
|
||||
return new Template(path, renderFunc);
|
||||
};
|
||||
@ -1,82 +1,22 @@
|
||||
'use strict';
|
||||
var warp10 = require('warp10');
|
||||
var extend = require('raptor-util/extend');
|
||||
|
||||
var STYLE_ATTR = 'style';
|
||||
var CLASS_ATTR = 'class';
|
||||
var escapeEndingScriptTagRegExp = /<\//g;
|
||||
|
||||
var elTest = /[&<]/;
|
||||
var elTestReplace = /[&<]/g;
|
||||
var attrTest = /[&<\"\n]/;
|
||||
var attrReplace = /[&<\"\n]/g;
|
||||
var stringifiedAttrTest = /[&\'\n]/;
|
||||
var stringifiedAttrReplace = /[&\'\n]/g;
|
||||
var escape = require('./escape');
|
||||
var escapeXml = escape.escapeXml;
|
||||
var escapeXmlAttr = escape.escapeXmlAttr;
|
||||
var attrHelper = require('./helper-attr');
|
||||
var attrsHelper = require('./helper-attrs');
|
||||
|
||||
|
||||
var classList;
|
||||
|
||||
var replacements = {
|
||||
'<': '<',
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
'\'': ''',
|
||||
'\n': ' ' //Preserve new lines so that they don't get normalized as space
|
||||
};
|
||||
|
||||
function replaceChar(match) {
|
||||
return replacements[match];
|
||||
}
|
||||
|
||||
function escapeStr(str, regexpTest, regexpReplace) {
|
||||
return regexpTest.test(str) ? str.replace(regexpReplace, replaceChar) : str;
|
||||
}
|
||||
|
||||
function escapeXmlHelper(value, regexpTest, regexpReplace) {
|
||||
// check for most common case first
|
||||
if (typeof value === 'string') {
|
||||
return escapeStr(value, regexpTest, regexpReplace);
|
||||
} else if (value == null) {
|
||||
return '';
|
||||
} else if (typeof value === 'object') {
|
||||
var safeHTML = value.safeHTML;
|
||||
if (safeHTML != null) {
|
||||
return value.safeHTML;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else if (value === true || value === false || typeof value === 'number') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return escapeStr(value.toString(), regexpTest, regexpReplace);
|
||||
}
|
||||
|
||||
function escapeXml(value) {
|
||||
return escapeXmlHelper(value, elTest, elTestReplace);
|
||||
}
|
||||
|
||||
function escapeXmlAttr(value) {
|
||||
return escapeXmlHelper(value, attrTest, attrReplace);
|
||||
}
|
||||
|
||||
function attr(name, value, shouldEscape) {
|
||||
if (typeof value === 'string') {
|
||||
return ' ' + name + '="' + (shouldEscape !== false ? escapeStr(value, attrTest, attrReplace) : value) + '"';
|
||||
} else if (value === true) {
|
||||
return ' ' + name;
|
||||
} else if (value == null || value === false) {
|
||||
return '';
|
||||
} else if (typeof value === 'object') {
|
||||
if (name.substring(0, 6) === 'data-_') {
|
||||
value = warp10.stringify(value);
|
||||
} else {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
return ' ' + name + "='" + escapeStr(value, stringifiedAttrTest, stringifiedAttrReplace) + "'";
|
||||
} else {
|
||||
return ' ' + name + '=' + value; // number (doesn't need quotes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -113,24 +53,13 @@ exports.xs = function escapeScriptHelper(val) {
|
||||
* Internal method to render a single HTML attribute
|
||||
* @private
|
||||
*/
|
||||
exports.a = attr;
|
||||
exports.a = attrHelper;
|
||||
|
||||
/**
|
||||
* Internal method to render multiple HTML attributes based on the properties of an object
|
||||
* @private
|
||||
*/
|
||||
exports.as = function(arg) {
|
||||
if (typeof arg === 'object') {
|
||||
var out = '';
|
||||
for (var attrName in arg) {
|
||||
out += attr(attrName, arg[attrName]);
|
||||
}
|
||||
return out;
|
||||
} else if (typeof arg === 'string') {
|
||||
return arg;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
exports.as = attrsHelper;
|
||||
|
||||
/**
|
||||
* Internal helper method to handle the "style" attribute. The value can either
|
||||
@ -145,7 +74,7 @@ exports.sa = function(style) {
|
||||
}
|
||||
|
||||
if (typeof style === 'string') {
|
||||
return attr(STYLE_ATTR, style, false);
|
||||
return attrHelper(STYLE_ATTR, style, false);
|
||||
} else if (typeof style === 'object') {
|
||||
var parts = [];
|
||||
for (var name in style) {
|
||||
@ -156,7 +85,7 @@ exports.sa = function(style) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts ? attr(STYLE_ATTR, parts.join(';'), false) : '';
|
||||
return parts ? attrHelper(STYLE_ATTR, parts.join(';'), false) : '';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
@ -176,9 +105,9 @@ exports.ca = function(classNames) {
|
||||
}
|
||||
|
||||
if (typeof classNames === 'string') {
|
||||
return attr(CLASS_ATTR, classNames, false);
|
||||
return attrHelper(CLASS_ATTR, classNames, false);
|
||||
} else {
|
||||
return attr(CLASS_ATTR, classList(classNames), false);
|
||||
return attrHelper(CLASS_ATTR, classList(classNames), false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -186,6 +115,4 @@ exports.ca = function(classNames) {
|
||||
|
||||
var commonHelpers = require('../helpers');
|
||||
classList = commonHelpers.cl;
|
||||
extend(exports, commonHelpers);
|
||||
|
||||
exports.inline = require('./')._inline;
|
||||
extend(exports, commonHelpers);
|
||||
@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
require('../env-init');
|
||||
|
||||
var AsyncStream = require('./AsyncStream');
|
||||
var makeRenderable = require('../renderable');
|
||||
var Template = require('./Template');
|
||||
|
||||
/**
|
||||
* Method is for internal usage only. This method
|
||||
@ -12,38 +14,17 @@ exports.t = function createTemplate(path) {
|
||||
return new Template(path);
|
||||
};
|
||||
|
||||
function Template(path, func, options) {
|
||||
this.path = path;
|
||||
this._ = func;
|
||||
this._shouldBuffer = !options || options.shouldBuffer !== false;
|
||||
this.meta = undefined;
|
||||
}
|
||||
|
||||
function createOut(globalData, parent, state, buffer) {
|
||||
return new AsyncStream(globalData, parent, state, buffer);
|
||||
}
|
||||
|
||||
Template.prototype = {
|
||||
createOut: createOut,
|
||||
stream: function() {
|
||||
throw new Error('You must require("marko/stream")');
|
||||
}
|
||||
};
|
||||
|
||||
makeRenderable(Template.prototype);
|
||||
|
||||
exports.createWriter = function(writer) {
|
||||
return new AsyncStream(null, writer);
|
||||
};
|
||||
|
||||
exports._inline = function(filename, renderFunc) {
|
||||
return new Template(filename, renderFunc);
|
||||
};
|
||||
|
||||
exports.Template = Template;
|
||||
exports.createOut = createOut;
|
||||
exports.$__createOut = createOut;
|
||||
exports.AsyncStream = AsyncStream;
|
||||
exports.enableAsyncStackTrace = AsyncStream.enableAsyncStackTrace;
|
||||
exports.helpers = require('./helpers');
|
||||
|
||||
require('../').$__setRuntime(exports);
|
||||
require('../createOut').$__setCreateOut(createOut);
|
||||
@ -1,25 +1,6 @@
|
||||
'use strict';
|
||||
var documentProvider = require('./document-provider');
|
||||
require('./env-init'); // no-op in the browser, but enables extra features on the server
|
||||
|
||||
var runtime;
|
||||
|
||||
function setRuntime(_runtime) {
|
||||
runtime = _runtime;
|
||||
}
|
||||
exports.$__setRuntime = setRuntime;
|
||||
|
||||
function createOut(globalData) {
|
||||
return runtime.createOut(globalData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to associate a DOM Document with marko. This is needed
|
||||
* to parse HTML fragments to insert into the VDOM tree.
|
||||
*/
|
||||
exports.setDocument = function(newDoc) {
|
||||
documentProvider.$__document = newDoc;
|
||||
};
|
||||
|
||||
exports.createOut = createOut;
|
||||
exports.createOut = require('./createOut');
|
||||
exports.load = require('./loader');
|
||||
exports.events = require('./events');
|
||||
@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
module.exports = function load(templatePath) {
|
||||
throw Error('Template not found: ' + templatePath);
|
||||
throw Error('Not found: ' + templatePath);
|
||||
};
|
||||
@ -27,34 +27,6 @@ var markoCompiler = require('../../compiler');
|
||||
var cwd = process.cwd();
|
||||
var fsOptions = {encoding: 'utf8'};
|
||||
|
||||
if (process.env.hasOwnProperty('MARKO_HOT_RELOAD')) {
|
||||
require('../../hot-reload').enable();
|
||||
}
|
||||
|
||||
// If process was launched with browser refresh then automatically
|
||||
// enable browser-refresh
|
||||
require('../../browser-refresh').enable();
|
||||
|
||||
function fixFlush() {
|
||||
try {
|
||||
var OutgoingMessage = require('http').OutgoingMessage;
|
||||
if (OutgoingMessage.prototype.flush && OutgoingMessage.prototype.flush.toString().indexOf('deprecated') !== -1) {
|
||||
// Yes, we are monkey-patching http. This method should never have been added and it was introduced on
|
||||
// the iojs fork. It was quickly deprecated and I'm 99% sure no one is actually using it.
|
||||
// See:
|
||||
// - https://github.com/marko-js/async-writer/issues/3
|
||||
// - https://github.com/nodejs/node/issues/2920
|
||||
//
|
||||
// This method causes problems since marko looks for the flush method and calls it found.
|
||||
// The `res.flush()` method is introduced by the [compression](https://www.npmjs.com/package/compression)
|
||||
// middleware, but, otherwise, it should typically not exist.
|
||||
delete require('http').OutgoingMessage.prototype.flush;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
fixFlush();
|
||||
|
||||
function loadSource(templatePath, compiledSrc) {
|
||||
var templateModulePath = templatePath + '.js';
|
||||
|
||||
@ -183,7 +155,4 @@ function doLoad(templatePath, templateSrc, options) {
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
require('../stream');
|
||||
require('../dependencies');
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"browser": {
|
||||
"./loader/index.js": "./loader/index-browser.js"
|
||||
"./loader/index.js": "./loader/index-browser.js",
|
||||
"./env-init.js": false
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
var marko = require('./index');
|
||||
var defaultCreateOut = require('./createOut');
|
||||
var extend = require('raptor-util/extend');
|
||||
|
||||
module.exports = function(target, renderer) {
|
||||
var renderFunc = renderer && (renderer.renderer || renderer.render || renderer);
|
||||
var createOut = target.createOut || renderer.createOut || marko.createOut;
|
||||
var createOut = target.createOut || renderer.createOut || defaultCreateOut;
|
||||
|
||||
return extend(target, {
|
||||
createOut: createOut,
|
||||
@ -35,7 +35,7 @@ module.exports = function(target, renderer) {
|
||||
out.sync();
|
||||
|
||||
render(localData, out);
|
||||
return out.getResult();
|
||||
return out.$__getResult();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -61,7 +61,7 @@ module.exports = function(target, renderer) {
|
||||
var finalData;
|
||||
var globalData;
|
||||
var render = renderFunc || this._;
|
||||
var shouldBuffer = this._shouldBuffer;
|
||||
var shouldBuffer = this.$__shouldBuffer;
|
||||
var shouldEnd = true;
|
||||
|
||||
if (data) {
|
||||
@ -73,7 +73,7 @@ module.exports = function(target, renderer) {
|
||||
finalData = {};
|
||||
}
|
||||
|
||||
if (out && out.isOut){
|
||||
if (out && out.$__isOut) {
|
||||
finalOut = out;
|
||||
shouldEnd = false;
|
||||
extend(out.global, globalData);
|
||||
@ -92,12 +92,14 @@ module.exports = function(target, renderer) {
|
||||
if (callback) {
|
||||
finalOut
|
||||
.on('finish', function() {
|
||||
callback(null, finalOut.getResult());
|
||||
callback(null, finalOut.$__getResult());
|
||||
})
|
||||
.once('error', callback);
|
||||
}
|
||||
|
||||
finalOut.global.template = finalOut.global.template || this;
|
||||
globalData = finalOut.global;
|
||||
|
||||
globalData.template = globalData.template || this;
|
||||
|
||||
render(finalData, finalOut);
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ line to your app:
|
||||
|
||||
*/
|
||||
var stream = require('stream');
|
||||
var Template = require('./html/Template');
|
||||
var AsyncStream = require('./html/AsyncStream');
|
||||
|
||||
function Readable(template, data, options) {
|
||||
@ -47,6 +48,6 @@ Readable.prototype = {
|
||||
|
||||
require('raptor-util/inherit')(Readable, stream.Readable);
|
||||
|
||||
require('./html').Template.prototype.stream = function(data) {
|
||||
Template.prototype.stream = function(data) {
|
||||
return new Readable(this, data, this._options);
|
||||
};
|
||||
@ -1,15 +1,19 @@
|
||||
var EventEmitter = require('events-light');
|
||||
var HTMLElement = require('./HTMLElement');
|
||||
var DocumentFragment = require('./DocumentFragment');
|
||||
var Comment = require('./Comment');
|
||||
var Text = require('./Text');
|
||||
var virtualizeHTML = require('./virtualizeHTML');
|
||||
var documentProvider = require('../document-provider');
|
||||
var vdom = require('./vdom');
|
||||
var HTMLElement = vdom.$__HTMLElement;
|
||||
var DocumentFragment = vdom.$__DocumentFragment;
|
||||
var Comment = vdom.$__Comment;
|
||||
var Text = vdom.$__Text;
|
||||
var virtualizeHTML = vdom.$__virtualizeHTML;
|
||||
var RenderResult = require('../RenderResult');
|
||||
var defaultDocument = vdom.$__defaultDocument;
|
||||
|
||||
var FLAG_FINISHED = 1;
|
||||
var FLAG_LAST_FIRED = 2;
|
||||
|
||||
var EVENT_UPDATE = 'update';
|
||||
var EVENT_FINISH = 'finish';
|
||||
|
||||
function State(tree) {
|
||||
this.$__remaining = 1;
|
||||
this.$__events = new EventEmitter();
|
||||
@ -39,7 +43,8 @@ function AsyncVDOMBuilder(globalData, parentNode, state) {
|
||||
}
|
||||
|
||||
var proto = AsyncVDOMBuilder.prototype = {
|
||||
isOut: true,
|
||||
$__isOut: true,
|
||||
$__document: defaultDocument,
|
||||
|
||||
element: function(name, attrs, childCount) {
|
||||
var element = new HTMLElement(name, attrs, childCount);
|
||||
@ -87,7 +92,7 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
var parent = this.$__parent;
|
||||
if (parent) {
|
||||
var lastChild = parent.lastChild;
|
||||
if (lastChild && lastChild.nodeType === 3) {
|
||||
if (lastChild && lastChild.$__Text) {
|
||||
lastChild.nodeValue += text;
|
||||
} else {
|
||||
parent.$__appendChild(new Text(text));
|
||||
@ -102,7 +107,7 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
|
||||
html: function(html) {
|
||||
if (html != null) {
|
||||
var vdomNode = virtualizeHTML(html, documentProvider.$__document);
|
||||
var vdomNode = virtualizeHTML(html, this.$__document);
|
||||
this.node(vdomNode);
|
||||
}
|
||||
|
||||
@ -141,7 +146,7 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
|
||||
if (!remaining) {
|
||||
state.$__flags |= FLAG_FINISHED;
|
||||
state.$__events.emit('finish', this);
|
||||
state.$__events.emit(EVENT_FINISH, this.$__getResult());
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -176,23 +181,26 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
},
|
||||
|
||||
flush: function() {
|
||||
var state = this.$__state;
|
||||
state.$__events.emit('update', this);
|
||||
var events = this.$__state.$__events;
|
||||
|
||||
if (events.listenerCount(EVENT_UPDATE)) {
|
||||
events.emit(EVENT_UPDATE, new RenderResult(this));
|
||||
}
|
||||
},
|
||||
|
||||
getOutput: function() {
|
||||
$__getOutput: function() {
|
||||
return this.$__state.$__tree;
|
||||
},
|
||||
|
||||
getResult: function() {
|
||||
$__getResult: function() {
|
||||
return this.$__result || (this.$__result = new RenderResult(this));
|
||||
},
|
||||
|
||||
on: function(event, callback) {
|
||||
var state = this.$__state;
|
||||
|
||||
if (event === 'finish' && (state.$__flags & FLAG_FINISHED)) {
|
||||
callback(this);
|
||||
if (event === EVENT_FINISH && (state.$__flags & FLAG_FINISHED)) {
|
||||
callback(this.$__getResult());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -203,8 +211,8 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
once: function(event, callback) {
|
||||
var state = this.$__state;
|
||||
|
||||
if (event === 'finish' && (state.$__flags & FLAG_FINISHED)) {
|
||||
callback(this);
|
||||
if (event === EVENT_FINISH && (state.$__flags & FLAG_FINISHED)) {
|
||||
callback(this.$__getResult());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -267,45 +275,31 @@ var proto = AsyncVDOMBuilder.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
getNode: function(doc) {
|
||||
$__getNode: function(doc) {
|
||||
var node = this.$__node;
|
||||
if (!node) {
|
||||
var vdomTree = this.getOutput();
|
||||
var vdomTree = this.$__getOutput();
|
||||
|
||||
if (!doc) {
|
||||
doc = documentProvider.$__document;
|
||||
doc = this.$__document;
|
||||
}
|
||||
|
||||
node = vdomTree.actualize(doc);
|
||||
|
||||
if (node.nodeType === 11 /* DocumentFragment */) {
|
||||
var firstChild = node.firstChild;
|
||||
if (firstChild) {
|
||||
var nextSibling = firstChild.nextSibling;
|
||||
if (!nextSibling) {
|
||||
// If the DocumentFragment only has one child
|
||||
// then just return that first child as the node
|
||||
node = firstChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.$__node = node;
|
||||
node = this.$__node = vdomTree.actualize(doc);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return this.getNode().outerHTML;
|
||||
return this.$__getNode().outerHTML;
|
||||
},
|
||||
|
||||
then: function(fn, fnErr) {
|
||||
var out = this;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
out.on('error', reject);
|
||||
out.on('finish', function() {
|
||||
resolve(out.getResult());
|
||||
});
|
||||
out.on('error', reject)
|
||||
.on(EVENT_FINISH, function(result) {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.resolve(promise).then(fn, fnErr);
|
||||
|
||||
@ -2,15 +2,15 @@ var Node = require('./Node');
|
||||
var inherit = require('raptor-util/inherit');
|
||||
|
||||
function Comment(value) {
|
||||
Node.call(this, -1 /* no children */);
|
||||
this.$__Node(-1 /* no children */);
|
||||
this.nodeValue = value;
|
||||
}
|
||||
|
||||
Comment.prototype = {
|
||||
nodeType: 8,
|
||||
|
||||
actualize: function(document) {
|
||||
return document.createComment(this.nodeValue);
|
||||
actualize: function(doc) {
|
||||
return doc.createComment(this.nodeValue);
|
||||
},
|
||||
|
||||
$__cloneNode: function() {
|
||||
|
||||
@ -9,26 +9,28 @@ function DocumentFragmentClone(other) {
|
||||
}
|
||||
|
||||
function DocumentFragment(documentFragment) {
|
||||
Node.call(this, null /* childCount */);
|
||||
this.$__Node(null /* childCount */);
|
||||
this.namespaceURI = undefined;
|
||||
}
|
||||
|
||||
DocumentFragment.prototype = {
|
||||
nodeType: 11,
|
||||
|
||||
$__DocumentFragment: true,
|
||||
|
||||
$__nsAware: true,
|
||||
|
||||
$__cloneNode: function() {
|
||||
return new DocumentFragmentClone(this);
|
||||
},
|
||||
|
||||
actualize: function(document) {
|
||||
var docFragment = document.createDocumentFragment();
|
||||
actualize: function(doc) {
|
||||
var docFragment = doc.createDocumentFragment();
|
||||
|
||||
var curChild = this.firstChild;
|
||||
|
||||
while(curChild) {
|
||||
docFragment.appendChild(curChild.actualize(document));
|
||||
docFragment.appendChild(curChild.actualize(doc));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
var Node = require('./Node');
|
||||
var inherit = require('raptor-util/inherit');
|
||||
var extend = require('raptor-util/extend');
|
||||
var Text = require('./Text');
|
||||
var Comment = require('./Comment');
|
||||
var Node = require('./Node');
|
||||
var documentProvider = require('../document-provider');
|
||||
var virtualizeHTML;
|
||||
var defineProperty = Object.defineProperty;
|
||||
|
||||
var NS_XLINK = 'http://www.w3.org/1999/xlink';
|
||||
var ATTR_XLINK_HREF = 'xlink:href';
|
||||
var ATTR_HREF = 'href';
|
||||
var EMPTY_OBJECT = Object.freeze({});
|
||||
var ATTR_MARKO_CONST = 'data-marko-const';
|
||||
@ -59,7 +57,7 @@ function HTMLElement(tagName, attrs, childCount, constId) {
|
||||
break;
|
||||
}
|
||||
|
||||
Node.call(this, childCount);
|
||||
this.$__Node(childCount);
|
||||
|
||||
if (constId) {
|
||||
if (!attrs) {
|
||||
@ -68,7 +66,7 @@ function HTMLElement(tagName, attrs, childCount, constId) {
|
||||
attrs[ATTR_MARKO_CONST] = constId;
|
||||
}
|
||||
|
||||
this.attributes = attrs || EMPTY_OBJECT;
|
||||
this.$__attributes = attrs || EMPTY_OBJECT;
|
||||
this.$__isTextArea = isTextArea;
|
||||
this.namespaceURI = namespaceURI;
|
||||
this.nodeName = tagName;
|
||||
@ -77,116 +75,12 @@ function HTMLElement(tagName, attrs, childCount, constId) {
|
||||
}
|
||||
|
||||
HTMLElement.prototype = {
|
||||
$__HTMLElement: true,
|
||||
|
||||
nodeType: 1,
|
||||
|
||||
$__nsAware: true,
|
||||
|
||||
assignAttributes: function(targetNode) {
|
||||
var attrs = this.attributes;
|
||||
var attrName;
|
||||
var i;
|
||||
|
||||
// We use expando properties to associate the previous HTML
|
||||
// attributes provided as part of the VDOM node with the
|
||||
// real HTMLElement DOM node. When diffing attributes,
|
||||
// we only use our internal representation of the attributes.
|
||||
// When diffing for the first time it's possible that the
|
||||
// real HTMLElement node will not have the expando property
|
||||
// so we build the attribute map from the expando property
|
||||
|
||||
var oldAttrs = targetNode._vattrs;
|
||||
if (oldAttrs) {
|
||||
if (oldAttrs === attrs) {
|
||||
// For constant attributes the same object will be provided
|
||||
// every render and we can use that to our advantage to
|
||||
// not waste time diffing a constant, immutable attribute
|
||||
// map.
|
||||
return;
|
||||
} else {
|
||||
oldAttrs = removePreservedAttributes(extend({}, oldAttrs));
|
||||
}
|
||||
} else {
|
||||
// We need to build the attribute map from the real attributes
|
||||
oldAttrs = {};
|
||||
|
||||
var oldAttributesList = targetNode.attributes;
|
||||
for (i = oldAttributesList.length - 1; i >= 0; --i) {
|
||||
var attr = oldAttributesList[i];
|
||||
|
||||
if (attr.specified !== false) {
|
||||
attrName = attr.name;
|
||||
var attrNamespaceURI = attr.namespaceURI;
|
||||
if (attrNamespaceURI === NS_XLINK) {
|
||||
oldAttrs['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);
|
||||
}
|
||||
|
||||
// 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
|
||||
// that use case we delete out all of the preserved attributes
|
||||
// so it's as if they never existed.
|
||||
var preservedAttrs = attrs['data-preserve-attrs'];
|
||||
if (preservedAttrs) {
|
||||
attrs = removePreservedAttributes(extend({}, attrs));
|
||||
}
|
||||
|
||||
// Loop over all of the attributes in the attribute map and compare
|
||||
// them to the value in the old map. However, if the value is
|
||||
// null/undefined/false then we want to remove the attribute
|
||||
for (attrName in attrs) {
|
||||
var attrValue = attrs[attrName];
|
||||
|
||||
if (attrName === 'xlink:href') {
|
||||
if (attrValue == null || attrValue === false) {
|
||||
targetNode.removeAttributeNS(NS_XLINK, ATTR_HREF);
|
||||
} else if (oldAttrs[attrName] !== attrValue) {
|
||||
targetNode.setAttributeNS(NS_XLINK, ATTR_HREF, attrValue);
|
||||
}
|
||||
} else {
|
||||
if (attrValue == null || attrValue === false) {
|
||||
targetNode.removeAttribute(attrName);
|
||||
} else if (oldAttrs[attrName] !== attrValue) {
|
||||
|
||||
if (specialAttrRegexp.test(attrName)) {
|
||||
// Special attributes aren't copied to the real DOM. They are only
|
||||
// kept in the virtual attributes map
|
||||
continue;
|
||||
}
|
||||
|
||||
var type = typeof attrValue;
|
||||
|
||||
if (type !== 'string') {
|
||||
attrValue = convertAttrValue(type, attrValue);
|
||||
}
|
||||
|
||||
targetNode.setAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any old attributes that are not in the new set of attributes
|
||||
// then we need to remove those attributes from the target node
|
||||
for (attrName in oldAttrs) {
|
||||
if (attrs.hasOwnProperty(attrName) === false) {
|
||||
if (attrName === 'xlink:href') {
|
||||
targetNode.removeAttributeNS(NS_XLINK, ATTR_HREF);
|
||||
} else {
|
||||
targetNode.removeAttribute(attrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetNode._vattrs = attrs;
|
||||
},
|
||||
|
||||
$__cloneNode: function() {
|
||||
return new HTMLElementClone(this);
|
||||
},
|
||||
@ -208,37 +102,7 @@ HTMLElement.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shorthand method for creating and appending a Text node with a given value
|
||||
* @param {String} value The text value for the new Text node
|
||||
*/
|
||||
t: function(value) {
|
||||
var type = typeof value;
|
||||
|
||||
if (type !== 'string') {
|
||||
if (value == null) {
|
||||
value = '';
|
||||
} else if (type === 'object') {
|
||||
var safeHTML = value.safeHTML;
|
||||
var vdomNode = virtualizeHTML(safeHTML || '', documentProvider.$__document);
|
||||
this.$__appendChild(vdomNode);
|
||||
return this.$__finishChild();
|
||||
} else {
|
||||
value = value.toString();
|
||||
}
|
||||
}
|
||||
this.$__appendChild(new Text(value));
|
||||
return this.$__finishChild();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shorthand method for creating and appending a Comment node with a given value
|
||||
* @param {String} value The value for the new Comment node
|
||||
*/
|
||||
c: function(value) {
|
||||
this.$__appendChild(new Comment(value));
|
||||
return this.$__finishChild();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shorthand method for creating and appending a static node. The provided node is automatically cloned
|
||||
@ -251,18 +115,18 @@ HTMLElement.prototype = {
|
||||
return this.$__finishChild();
|
||||
},
|
||||
|
||||
actualize: function(document) {
|
||||
actualize: function(doc) {
|
||||
var el;
|
||||
var namespaceURI = this.namespaceURI;
|
||||
var tagName = this.nodeName;
|
||||
|
||||
if (namespaceURI) {
|
||||
el = document.createElementNS(namespaceURI, tagName);
|
||||
el = doc.createElementNS(namespaceURI, tagName);
|
||||
} else {
|
||||
el = document.createElement(tagName);
|
||||
el = doc.createElement(tagName);
|
||||
}
|
||||
|
||||
var attributes = this.attributes;
|
||||
var attributes = this.$__attributes;
|
||||
for (var attrName in attributes) {
|
||||
var attrValue = attributes[attrName];
|
||||
|
||||
@ -279,7 +143,7 @@ HTMLElement.prototype = {
|
||||
attrValue = convertAttrValue(type, attrValue);
|
||||
}
|
||||
|
||||
if (attrName === 'xlink:href') {
|
||||
if (attrName === ATTR_XLINK_HREF) {
|
||||
el.setAttributeNS(NS_XLINK, ATTR_HREF, attrValue);
|
||||
} else {
|
||||
el.setAttribute(attrName, attrValue);
|
||||
@ -288,12 +152,12 @@ HTMLElement.prototype = {
|
||||
}
|
||||
|
||||
if (this.$__isTextArea) {
|
||||
el.value = this.value;
|
||||
el.value = this.$__value;
|
||||
} else {
|
||||
var curChild = this.firstChild;
|
||||
|
||||
while(curChild) {
|
||||
el.appendChild(curChild.actualize(document));
|
||||
el.appendChild(curChild.actualize(doc));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
@ -307,25 +171,23 @@ HTMLElement.prototype = {
|
||||
// We don't care about the namespaces since the there
|
||||
// is no chance that attributes with the same name will have
|
||||
// different namespaces
|
||||
return this.attributes[name] !== undefined;
|
||||
return this.$__attributes[name] !== undefined;
|
||||
},
|
||||
|
||||
getAttribute: function(name) {
|
||||
return this.attributes[name];
|
||||
return this.$__attributes[name];
|
||||
},
|
||||
|
||||
isSameNode: function(otherNode) {
|
||||
if (otherNode.nodeType !== 1) {
|
||||
return false;
|
||||
if (otherNode.nodeType == 1) {
|
||||
var constId = this.$__constId;
|
||||
if (constId) {
|
||||
var otherSameId = otherNode.$__Node ? otherNode.$__constId : otherNode.getAttribute(ATTR_MARKO_CONST);
|
||||
return constId === otherSameId;
|
||||
}
|
||||
}
|
||||
|
||||
var constId = this.$__constId;
|
||||
if (constId) {
|
||||
var otherSameId = otherNode.actualize ? otherNode.$__constId : otherNode.getAttribute(ATTR_MARKO_CONST);
|
||||
return constId === otherSameId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -333,39 +195,130 @@ inherit(HTMLElement, Node);
|
||||
|
||||
var proto = HTMLElementClone.prototype = HTMLElement.prototype;
|
||||
|
||||
Object.defineProperty(proto, 'checked', {
|
||||
['checked', 'selected', 'disabled'].forEach(function(name) {
|
||||
defineProperty(proto, name, {
|
||||
get: function () {
|
||||
return this.$__attributes[name] !== undefined;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
defineProperty(proto, 'id', {
|
||||
get: function () {
|
||||
return this.attributes.checked !== undefined;
|
||||
return this.$__attributes.id;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'selected', {
|
||||
defineProperty(proto, 'value', {
|
||||
get: function () {
|
||||
return this.attributes.selected !== undefined;
|
||||
return this.$__value || this.$__attributes.value;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'id', {
|
||||
get: function () {
|
||||
return this.attributes.id;
|
||||
HTMLElement.$__morphAttrs = function(fromEl, toEl) {
|
||||
var attrs = toEl.$__attributes;
|
||||
var attrName;
|
||||
var i;
|
||||
|
||||
// We use expando properties to associate the previous HTML
|
||||
// attributes provided as part of the VDOM node with the
|
||||
// real HTMLElement DOM node. When diffing attributes,
|
||||
// we only use our internal representation of the attributes.
|
||||
// When diffing for the first time it's possible that the
|
||||
// real HTMLElement node will not have the expando property
|
||||
// so we build the attribute map from the expando property
|
||||
|
||||
var oldAttrs = fromEl._vattrs;
|
||||
if (oldAttrs) {
|
||||
if (oldAttrs === attrs) {
|
||||
// For constant attributes the same object will be provided
|
||||
// every render and we can use that to our advantage to
|
||||
// not waste time diffing a constant, immutable attribute
|
||||
// map.
|
||||
return;
|
||||
} else {
|
||||
oldAttrs = removePreservedAttributes(extend({}, oldAttrs));
|
||||
}
|
||||
} 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;
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'value', {
|
||||
get: function () {
|
||||
return this.$__value || this.attributes.value;
|
||||
},
|
||||
set: function (value) {
|
||||
this.$__value = value;
|
||||
// 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
|
||||
// that use case we delete out all of the preserved attributes
|
||||
// so it's as if they never existed.
|
||||
var preservedAttrs = attrs['data-preserve-attrs'];
|
||||
if (preservedAttrs) {
|
||||
attrs = removePreservedAttributes(extend({}, attrs));
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'disabled', {
|
||||
get: function () {
|
||||
return this.attributes.disabled !== undefined;
|
||||
// Loop over all of the attributes in the attribute map and compare
|
||||
// them to the value in the old map. However, if the value is
|
||||
// null/undefined/false then we want to remove the attribute
|
||||
for (attrName in attrs) {
|
||||
var attrValue = attrs[attrName];
|
||||
|
||||
if (attrName === ATTR_XLINK_HREF) {
|
||||
if (attrValue == null || attrValue === false) {
|
||||
fromEl.removeAttributeNS(NS_XLINK, ATTR_HREF);
|
||||
} else if (oldAttrs[attrName] !== attrValue) {
|
||||
fromEl.setAttributeNS(NS_XLINK, ATTR_HREF, attrValue);
|
||||
}
|
||||
} else {
|
||||
if (attrValue == null || attrValue === false) {
|
||||
fromEl.removeAttribute(attrName);
|
||||
} else if (oldAttrs[attrName] !== attrValue) {
|
||||
|
||||
if (specialAttrRegexp.test(attrName)) {
|
||||
// Special attributes aren't copied to the real DOM. They are only
|
||||
// kept in the virtual attributes map
|
||||
continue;
|
||||
}
|
||||
|
||||
var type = typeof attrValue;
|
||||
|
||||
if (type !== 'string') {
|
||||
attrValue = convertAttrValue(type, attrValue);
|
||||
}
|
||||
|
||||
fromEl.setAttribute(attrName, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = HTMLElement;
|
||||
// If there are any old attributes that are not in the new set of attributes
|
||||
// then we need to remove those attributes from the target node
|
||||
for (attrName in oldAttrs) {
|
||||
if (attrs.hasOwnProperty(attrName) === false) {
|
||||
if (attrName === ATTR_XLINK_HREF) {
|
||||
fromEl.removeAttributeNS(NS_XLINK, ATTR_HREF);
|
||||
} else {
|
||||
fromEl.removeAttribute(attrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtualizeHTML = require('./virtualizeHTML');
|
||||
fromEl._vattrs = attrs;
|
||||
};
|
||||
|
||||
module.exports = HTMLElement;
|
||||
@ -1,7 +1,5 @@
|
||||
/* jshint newcap:false */
|
||||
|
||||
var DocumentFragment;
|
||||
|
||||
function assignNamespace(node, namespaceURI) {
|
||||
node.namespaceURI = namespaceURI;
|
||||
|
||||
@ -14,26 +12,27 @@ function assignNamespace(node, namespaceURI) {
|
||||
}
|
||||
}
|
||||
|
||||
function Node(finalChildCount) {
|
||||
this.$__finalChildCount = finalChildCount;
|
||||
this.$__childCount = 0;
|
||||
this.$__firstChild = undefined;
|
||||
this.$__lastChild = undefined;
|
||||
this.$__parentNode = undefined;
|
||||
this.$__nextSibling = undefined;
|
||||
}
|
||||
function Node() {}
|
||||
|
||||
Node.prototype = {
|
||||
removeChildren: function() {
|
||||
this.$__firstChild = undefined;
|
||||
$__Node: function(finalChildCount) {
|
||||
this.$__finalChildCount = finalChildCount;
|
||||
this.$__childCount = 0;
|
||||
this.$__firstChild = undefined;
|
||||
this.$__lastChild = undefined;
|
||||
this.$__parentNode = undefined;
|
||||
this.$__nextSibling = undefined;
|
||||
},
|
||||
// removeChildren: function() {
|
||||
// this.$__firstChild = undefined;
|
||||
// this.$__childCount = 0;
|
||||
// this.$__lastChild = undefined;
|
||||
// },
|
||||
|
||||
get firstChild() {
|
||||
var firstChild = this.$__firstChild;
|
||||
|
||||
if (firstChild && firstChild.nodeType === 11 /* DocumentFragment */) {
|
||||
if (firstChild && firstChild.$__DocumentFragment) {
|
||||
var nestedFirstChild = firstChild.firstChild;
|
||||
// The first child is a DocumentFragment node.
|
||||
// If the DocumentFragment node has a first child then we will return that.
|
||||
@ -45,27 +44,17 @@ Node.prototype = {
|
||||
return firstChild;
|
||||
},
|
||||
|
||||
get lastChild() {
|
||||
var lastChild = this.$__lastChild;
|
||||
|
||||
if (lastChild && lastChild.nodeType === 11 /* DocumentFragment */) {
|
||||
return lastChild.lastChild;
|
||||
}
|
||||
|
||||
return lastChild;
|
||||
},
|
||||
|
||||
get nextSibling() {
|
||||
var nextSibling = this.$__nextSibling;
|
||||
|
||||
if (nextSibling) {
|
||||
if (nextSibling.nodeType === 11 /* DocumentFragment */) {
|
||||
if (nextSibling.$__DocumentFragment) {
|
||||
var firstChild = nextSibling.firstChild;
|
||||
return firstChild || nextSibling.nextSibling;
|
||||
}
|
||||
} else {
|
||||
var parentNode = this.$__parentNode;
|
||||
if (parentNode && parentNode.nodeType === 11) {
|
||||
if (parentNode && parentNode.$__DocumentFragment) {
|
||||
return parentNode.nextSibling;
|
||||
}
|
||||
}
|
||||
@ -73,15 +62,11 @@ Node.prototype = {
|
||||
return nextSibling;
|
||||
},
|
||||
|
||||
$__appendDocumentFragment: function() {
|
||||
return this.$__appendChild(new DocumentFragment());
|
||||
},
|
||||
|
||||
$__appendChild: function(child) {
|
||||
if (this.$__isTextArea) {
|
||||
if (child.nodeType === 3) {
|
||||
var currentValue = this.value;
|
||||
this.value = currentValue ? currentValue + child.nodeValue : child.nodeValue;
|
||||
if (child.$__Text) {
|
||||
var childValue = child.nodeValue;
|
||||
this.$__value = (this.$__value || '') + childValue;
|
||||
} else {
|
||||
throw TypeError();
|
||||
}
|
||||
@ -135,6 +120,4 @@ Node.prototype = {
|
||||
// }
|
||||
};
|
||||
|
||||
module.exports = Node;
|
||||
|
||||
DocumentFragment = require('./DocumentFragment');
|
||||
module.exports = Node;
|
||||
@ -2,15 +2,17 @@ var Node = require('./Node');
|
||||
var inherit = require('raptor-util/inherit');
|
||||
|
||||
function Text(value) {
|
||||
Node.call(this, -1 /* no children */);
|
||||
this.$__Node(-1 /* no children */);
|
||||
this.nodeValue = value;
|
||||
}
|
||||
|
||||
Text.prototype = {
|
||||
$__Text: true,
|
||||
|
||||
nodeType: 3,
|
||||
|
||||
actualize: function(document) {
|
||||
return document.createTextNode(this.nodeValue);
|
||||
actualize: function(doc) {
|
||||
return doc.createTextNode(this.nodeValue);
|
||||
},
|
||||
|
||||
$__cloneNode: function() {
|
||||
|
||||
5
runtime/vdom/helper-createInlineTemplate.js
Normal file
5
runtime/vdom/helper-createInlineTemplate.js
Normal file
@ -0,0 +1,5 @@
|
||||
var Template = require('./').Template;
|
||||
|
||||
module.exports = function(path, renderFunc) {
|
||||
return new Template(path, renderFunc);
|
||||
};
|
||||
27
runtime/vdom/helper-styleAttr.js
Normal file
27
runtime/vdom/helper-styleAttr.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Helper for generating the string for a style attribute
|
||||
* @param {[type]} style [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
module.exports = function(style) {
|
||||
if (!style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof style === 'string') {
|
||||
return style;
|
||||
} else if (typeof style === 'object') {
|
||||
var parts = [];
|
||||
for (var name in style) {
|
||||
if (style.hasOwnProperty(name)) {
|
||||
var value = style[name];
|
||||
if (value) {
|
||||
parts.push(name + ':' + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts ? parts.join(';') : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@ -1,7 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var HTMLElement = require('./HTMLElement');
|
||||
var Text = require('./Text');
|
||||
var vdom = require('./vdom');
|
||||
var HTMLElement = vdom.$__HTMLElement;
|
||||
var Text = vdom.$__Text;
|
||||
|
||||
var commonHelpers = require('../helpers');
|
||||
var extend = require('raptor-util/extend');
|
||||
|
||||
@ -22,34 +24,6 @@ exports.const = function(id) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for generating the string for a style attribute
|
||||
* @param {[type]} style [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
exports.sa = function(style) {
|
||||
if (!style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof style === 'string') {
|
||||
return style;
|
||||
} else if (typeof style === 'object') {
|
||||
var parts = [];
|
||||
for (var name in style) {
|
||||
if (style.hasOwnProperty(name)) {
|
||||
var value = style[name];
|
||||
if (value) {
|
||||
parts.push(name + ':' + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts ? parts.join(';') : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper method to handle the "class" attribute. The value can either
|
||||
* be a string, an array or an object. For example:
|
||||
@ -70,6 +44,4 @@ exports.ca = function(classNames) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.inline = require('./')._inline;
|
||||
|
||||
extend(exports, commonHelpers);
|
||||
@ -24,18 +24,13 @@ function createOut(globalData, parent, state) {
|
||||
return new AsyncVDOMBuilder(globalData, parent, state);
|
||||
}
|
||||
|
||||
Template.prototype = {
|
||||
var Template_prototype = Template.prototype = {
|
||||
createOut: createOut
|
||||
};
|
||||
|
||||
makeRenderable(Template.prototype);
|
||||
|
||||
exports._inline = function(filename, renderFunc) {
|
||||
return new Template(filename, renderFunc);
|
||||
};
|
||||
makeRenderable(Template_prototype);
|
||||
|
||||
exports.Template = Template;
|
||||
exports.createOut = createOut;
|
||||
exports.helpers = require('./helpers');
|
||||
exports.$__createOut = createOut;
|
||||
|
||||
require('../').$__setRuntime(exports);
|
||||
require('../createOut').$__setCreateOut(createOut);
|
||||
|
||||
139
runtime/vdom/vdom.js
Normal file
139
runtime/vdom/vdom.js
Normal file
@ -0,0 +1,139 @@
|
||||
var Node = require('./Node');
|
||||
var Comment = require('./Comment');
|
||||
var DocumentFragment = require('./DocumentFragment');
|
||||
var HTMLElement = require('./HTMLElement');
|
||||
var Text = require('./Text');
|
||||
var defaultDocument = typeof document != 'undefined' && document;
|
||||
|
||||
var specialHtmlRegexp = /[&<]/;
|
||||
var range;
|
||||
|
||||
function virtualizeChildNodes(node, vdomParent) {
|
||||
var curChild = node.firstChild;
|
||||
while(curChild) {
|
||||
vdomParent.$__appendChild(virtualize(curChild));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function virtualize(node) {
|
||||
switch(node.nodeType) {
|
||||
case 1:
|
||||
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;
|
||||
|
||||
if (attr.namespaceURI === 'http://www.w3.org/1999/xlink' && attr.localName === 'href') {
|
||||
attrName = 'xlink:href';
|
||||
} else {
|
||||
attrName = attr.name;
|
||||
}
|
||||
|
||||
attrs[attrName] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
var vdomEL = new HTMLElement(node.nodeName, attrs);
|
||||
|
||||
if (vdomEL.$__isTextArea) {
|
||||
vdomEL.$__value = node.value;
|
||||
} else {
|
||||
virtualizeChildNodes(node, vdomEL);
|
||||
}
|
||||
|
||||
return vdomEL;
|
||||
case 3:
|
||||
return new Text(node.nodeValue);
|
||||
case 8:
|
||||
return new Comment(node.nodeValue);
|
||||
case 11:
|
||||
var vdomDocFragment = new DocumentFragment();
|
||||
virtualizeChildNodes(node, vdomDocFragment);
|
||||
return vdomDocFragment;
|
||||
}
|
||||
}
|
||||
|
||||
function virtualizeHTML(html, doc) {
|
||||
if (!specialHtmlRegexp.test(html)) {
|
||||
return new Text(html);
|
||||
}
|
||||
|
||||
if (!range && doc.createRange) {
|
||||
range = doc.createRange();
|
||||
range.selectNode(doc.body);
|
||||
}
|
||||
|
||||
var vdomFragment;
|
||||
|
||||
var fragment;
|
||||
if (range && range.createContextualFragment) {
|
||||
fragment = range.createContextualFragment(html);
|
||||
vdomFragment = virtualize(fragment);
|
||||
} else {
|
||||
var container = doc.createElement('body');
|
||||
container.innerHTML = html;
|
||||
vdomFragment = new DocumentFragment();
|
||||
|
||||
var curChild = container.firstChild;
|
||||
while(curChild) {
|
||||
vdomFragment.$__appendChild(virtualize(curChild));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return vdomFragment;
|
||||
}
|
||||
|
||||
var Node_prototype = Node.prototype;
|
||||
|
||||
/**
|
||||
* Shorthand method for creating and appending a Text node with a given value
|
||||
* @param {String} value The text value for the new Text node
|
||||
*/
|
||||
Node_prototype.t = function(value) {
|
||||
var type = typeof value;
|
||||
|
||||
if (type !== 'string') {
|
||||
if (value == null) {
|
||||
value = '';
|
||||
} else if (type === 'object') {
|
||||
var safeHTML = value.safeHTML;
|
||||
var vdomNode = virtualizeHTML(safeHTML || '', document);
|
||||
this.$__appendChild(vdomNode);
|
||||
return this.$__finishChild();
|
||||
} else {
|
||||
value = value.toString();
|
||||
}
|
||||
}
|
||||
this.$__appendChild(new Text(value));
|
||||
return this.$__finishChild();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shorthand method for creating and appending a Comment node with a given value
|
||||
* @param {String} value The value for the new Comment node
|
||||
*/
|
||||
Node_prototype.c = function(value) {
|
||||
this.$__appendChild(new Comment(value));
|
||||
return this.$__finishChild();
|
||||
};
|
||||
|
||||
Node_prototype.$__appendDocumentFragment = function() {
|
||||
return this.$__appendChild(new DocumentFragment());
|
||||
};
|
||||
|
||||
exports.$__Comment = Comment;
|
||||
exports.$__DocumentFragment = DocumentFragment;
|
||||
exports.$__HTMLElement = HTMLElement;
|
||||
exports.$__Text = Text;
|
||||
exports.$__virtualize = virtualize;
|
||||
exports.$__virtualizeHTML = virtualizeHTML;
|
||||
exports.$__defaultDocument = defaultDocument;
|
||||
@ -1,57 +0,0 @@
|
||||
var HTMLElement = require('./HTMLElement');
|
||||
var DocumentFragment = require('./DocumentFragment');
|
||||
var Comment = require('./Comment');
|
||||
var Text = require('./Text');
|
||||
|
||||
function virtualizeChildNodes(node, vdomParent) {
|
||||
var curChild = node.firstChild;
|
||||
while(curChild) {
|
||||
vdomParent.$__appendChild(virtualize(curChild));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function virtualize(node) {
|
||||
if (node.nodeType === 1) { // HtmlElement node
|
||||
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;
|
||||
|
||||
if (attr.namespaceURI === 'http://www.w3.org/1999/xlink' && attr.localName === 'href') {
|
||||
attrName = 'xlink:href';
|
||||
} else {
|
||||
attrName = attr.name;
|
||||
}
|
||||
|
||||
attrs[attrName] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
var vdomEL = new HTMLElement(node.nodeName, attrs);
|
||||
|
||||
if (vdomEL.$__isTextArea) {
|
||||
vdomEL.value = node.value;
|
||||
} else {
|
||||
virtualizeChildNodes(node, vdomEL);
|
||||
}
|
||||
|
||||
return vdomEL;
|
||||
} else if (node.nodeType === 3) { // Text node
|
||||
return new Text(node.nodeValue);
|
||||
} else if (node.nodeType === 8) { // Comment node
|
||||
return new Comment(node.nodeValue);
|
||||
} else if (node.nodeType === 11) { // DocumentFragment node
|
||||
var vdomDocFragment = new DocumentFragment();
|
||||
virtualizeChildNodes(node, vdomDocFragment);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = virtualize;
|
||||
@ -1,37 +0,0 @@
|
||||
var Text = require('./Text');
|
||||
var DocumentFragment = require('./DocumentFragment');
|
||||
var virtualize = require('./virtualize');
|
||||
var specialHtmlRegexp = /[&<]/;
|
||||
|
||||
var range;
|
||||
|
||||
module.exports = function virtualizeHTML(html, doc) {
|
||||
if (!specialHtmlRegexp.test(html)) {
|
||||
return new Text(html);
|
||||
}
|
||||
|
||||
if (!range && doc.createRange) {
|
||||
range = doc.createRange();
|
||||
range.selectNode(doc.body);
|
||||
}
|
||||
|
||||
var vdomFragment;
|
||||
|
||||
var fragment;
|
||||
if (range && range.createContextualFragment) {
|
||||
fragment = range.createContextualFragment(html);
|
||||
vdomFragment = virtualize(fragment);
|
||||
} else {
|
||||
var container = doc.createElement('body');
|
||||
container.innerHTML = html;
|
||||
vdomFragment = new DocumentFragment();
|
||||
|
||||
var curChild = container.firstChild;
|
||||
while(curChild) {
|
||||
vdomFragment.$__appendChild(virtualize(curChild));
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return vdomFragment;
|
||||
};
|
||||
@ -212,8 +212,8 @@ module.exports = function awaitTag(input, out) {
|
||||
};
|
||||
|
||||
asyncOut
|
||||
.on('finish', function() {
|
||||
asyncValue.resolve(asyncOut.getOutput());
|
||||
.on('finish', function(result) {
|
||||
asyncValue.resolve(result.getOutput());
|
||||
})
|
||||
.on('error', function(err) {
|
||||
asyncValue.reject(err);
|
||||
|
||||
10
taglibs/cache/cached-fragment-tag.js
vendored
10
taglibs/cache/cached-fragment-tag.js
vendored
@ -19,15 +19,15 @@ module.exports = {
|
||||
|
||||
if (input.renderBody) {
|
||||
input.renderBody(nestedOut);
|
||||
}
|
||||
|
||||
nestedOut.end();
|
||||
}
|
||||
|
||||
nestedOut
|
||||
.on('error', callback)
|
||||
.on('finish', function() {
|
||||
callback(null, nestedOut.getOutput());
|
||||
.on('finish', function(result) {
|
||||
callback(null, result.getOutput());
|
||||
});
|
||||
|
||||
nestedOut.end();
|
||||
}
|
||||
}, function(err, result) {
|
||||
if (err) {
|
||||
|
||||
@ -50,8 +50,8 @@ describe('AsyncStream', function() {
|
||||
out.write('3');
|
||||
out.write('4');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('1234');
|
||||
done();
|
||||
});
|
||||
@ -64,7 +64,7 @@ describe('AsyncStream', function() {
|
||||
out.write('2');
|
||||
|
||||
return out.end().then((result) => {
|
||||
const output = out.getOutput();
|
||||
const output = result.getOutput();
|
||||
expect(output).to.equal('12');
|
||||
expect(result.toString()).to.equal('12');
|
||||
});
|
||||
@ -89,8 +89,8 @@ describe('AsyncStream', function() {
|
||||
}, 10);
|
||||
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('1234');
|
||||
done();
|
||||
});
|
||||
@ -108,8 +108,8 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('3');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
@ -126,8 +126,8 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('3');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
@ -151,9 +151,9 @@ describe('AsyncStream', function() {
|
||||
out.write('3');
|
||||
out.end();
|
||||
|
||||
out.on('finish', function() {
|
||||
out.on('finish', function(result) {
|
||||
expect(errors.length).to.equal(1);
|
||||
expect(out.getOutput()).to.equal('13');
|
||||
expect(result.getOutput()).to.equal('13');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -199,8 +199,8 @@ describe('AsyncStream', function() {
|
||||
}, 10);
|
||||
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('12a2b2c34a4b4c');
|
||||
done();
|
||||
});
|
||||
@ -209,8 +209,8 @@ describe('AsyncStream', function() {
|
||||
it('should handle odd execution ordering', function(done) {
|
||||
var outA = createAsyncStream({ name:'outA' });
|
||||
|
||||
outA.on('finish', function() {
|
||||
var output = outA.getOutput();
|
||||
outA.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('1234567');
|
||||
done();
|
||||
});
|
||||
@ -255,8 +255,8 @@ describe('AsyncStream', function() {
|
||||
}, 10);
|
||||
out.write('3');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(errors.length).to.equal(1);
|
||||
expect(output).to.equal('13');
|
||||
done();
|
||||
@ -292,7 +292,7 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.catch((err) => {
|
||||
expect(err).to.be.an('error');
|
||||
expect(out.getOutput()).to.equal('1');
|
||||
expect(out.$__getOutput()).to.equal('1');
|
||||
done();
|
||||
}).then((data) => {
|
||||
throw new Error('Should not get here!');
|
||||
@ -309,8 +309,8 @@ describe('AsyncStream', function() {
|
||||
.on('error', function(e) {
|
||||
errors.push(e);
|
||||
})
|
||||
.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(errors.length).to.equal(1);
|
||||
expect(output).to.equal('13');
|
||||
done();
|
||||
@ -435,8 +435,8 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('2');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('1Hello World2');
|
||||
done();
|
||||
});
|
||||
@ -492,8 +492,8 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('3');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
@ -519,9 +519,9 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('3');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
out.on('finish', function(result) {
|
||||
expect(lastFiredCount).to.equal(1);
|
||||
var output = out.getOutput();
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
@ -590,8 +590,8 @@ describe('AsyncStream', function() {
|
||||
|
||||
out.write('5');
|
||||
out.end();
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('12345');
|
||||
expect(onLastCount).to.equal(2);
|
||||
expect(lastOutput).to.deep.equal(['a', 'b']);
|
||||
@ -795,8 +795,8 @@ describe('AsyncStream', function() {
|
||||
var out = new AsyncStream();
|
||||
out.name = 'outer';
|
||||
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
@ -820,8 +820,8 @@ describe('AsyncStream', function() {
|
||||
var out = createAsyncStream({global: { foo: 'bar' }});
|
||||
out.name = 'outer';
|
||||
|
||||
out.on('finish', function() {
|
||||
var output = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var output = result.getOutput();
|
||||
expect(output).to.equal('123');
|
||||
done();
|
||||
});
|
||||
|
||||
@ -17,7 +17,7 @@ describe('AsyncVDOMBuilder', function() {
|
||||
it('sync', function() {
|
||||
var out = new AsyncVDOMBuilder();
|
||||
out.element('div', {}, 0);
|
||||
var tree = out.getOutput();
|
||||
var tree = out.$__getOutput();
|
||||
expect(getChildNodes(tree).length).to.equal(1);
|
||||
});
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ var autotest = require('./autotest');
|
||||
var marko = require('../');
|
||||
var markoCompiler = require('../compiler');
|
||||
|
||||
describe('api' , function() {
|
||||
describe('api (compiler)' , function() {
|
||||
var autoTestDir = nodePath.join(__dirname, 'autotests/api-compiler');
|
||||
|
||||
autotest.scanDir(autoTestDir, function run(dir, helpers, done) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.t("Hello ");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.t("Hello ");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.t("Hello ");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.t("Hello ");
|
||||
|
||||
@ -5,13 +5,13 @@ exports.check = function(marko, markoCompiler, expect, done) {
|
||||
template.renderToString({
|
||||
name: 'John'
|
||||
},
|
||||
function(err, result, out) {
|
||||
function(err, html, out) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(result.toString()).to.equal(out.getOutput());
|
||||
expect(result.toString()).to.equal('Hello John!');
|
||||
expect(html).to.equal(out.getOutput());
|
||||
expect(html).to.equal('Hello John!');
|
||||
done();
|
||||
});
|
||||
};
|
||||
@ -5,8 +5,8 @@ exports.check = function(marko, markoCompiler, expect, done) {
|
||||
|
||||
var out = runtimeHtml.createWriter();
|
||||
out
|
||||
.on('finish', function() {
|
||||
expect(out.getOutput()).to.equal('Hello John!');
|
||||
.on('finish', function(result) {
|
||||
expect(result.getOutput()).to.equal('Hello John!');
|
||||
done();
|
||||
})
|
||||
.on('error', function(e) {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_forEachProp = marko_helpers.fp;
|
||||
marko_forEachProp = require("marko/runtime/helper-forEachProperty");
|
||||
|
||||
function render(data, out) {
|
||||
marko_forEachProp(myObject, function(k, v) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
include_target_template = require("./include-target.marko"),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_forEach = marko_helpers.f,
|
||||
marko_createElement = marko_helpers.e,
|
||||
|
||||
@ -2,7 +2,7 @@ var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
custom_tag_data_tag = marko_loadTag(require("./custom-tag-data-tag")),
|
||||
marko_merge = marko_helpers.m;
|
||||
marko_merge = require("marko/runtime/helper-merge");
|
||||
|
||||
function render(data, out) {
|
||||
custom_tag_data_tag({
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTemplate = marko_helpers.l,
|
||||
marko_loadTemplate = require("marko/runtime/helper-loadTemplate"),
|
||||
hello_template = marko_loadTemplate(require.resolve("./hello.marko")),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
hello_tag = marko_loadTag(hello_template);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTemplate = marko_helpers.l,
|
||||
marko_loadTemplate = require("marko/runtime/helper-loadTemplate"),
|
||||
target_template = marko_loadTemplate(require.resolve("./target.marko")),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
include_tag = marko_loadTag(require("marko/taglibs/core/include-tag"));
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTemplate = marko_helpers.l,
|
||||
marko_loadTemplate = require("marko/runtime/helper-loadTemplate"),
|
||||
test_message_template = marko_loadTemplate(require.resolve("./components/test-message/template.marko")),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
test_message_tag = marko_loadTag(test_message_template);
|
||||
|
||||
|
||||
1
test/autotests/render/for-tag-status-var/expected.html
Normal file
1
test/autotests/render/for-tag-status-var/expected.html
Normal file
@ -0,0 +1 @@
|
||||
<div>red - true - false - 0 - 3</div><div>green - false - false - 1 - 3</div><div>blue - false - true - 2 - 3</div>
|
||||
5
test/autotests/render/for-tag-status-var/template.marko
Normal file
5
test/autotests/render/for-tag-status-var/template.marko
Normal file
@ -0,0 +1,5 @@
|
||||
<for(item in ['red', 'green', 'blue'] | status-var=loop)>
|
||||
<div>
|
||||
${item} - ${loop.isFirst()} - ${loop.isLast()} - ${loop.getIndex()} - ${loop.getLength()}
|
||||
</div>
|
||||
</for>
|
||||
1
test/autotests/render/for-tag-status-var/test.js
Normal file
1
test/autotests/render/for-tag-status-var/test.js
Normal file
@ -0,0 +1 @@
|
||||
exports.templateData = {};
|
||||
@ -1,24 +1 @@
|
||||
exports.templateData = {
|
||||
"accounts": [
|
||||
{
|
||||
"balance": 0,
|
||||
"balanceFormatted": "$0.00",
|
||||
"status": "open"
|
||||
},
|
||||
{
|
||||
"balance": 10,
|
||||
"balanceFormatted": "$10.00",
|
||||
"status": "closed"
|
||||
},
|
||||
{
|
||||
"balance": -100,
|
||||
"balanceFormatted": "$-100.00",
|
||||
"status": "suspended"
|
||||
},
|
||||
{
|
||||
"balance": 999,
|
||||
"balanceFormatted": "$999.00",
|
||||
"status": "open"
|
||||
}
|
||||
]
|
||||
};
|
||||
exports.templateData = {};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_classList = marko_helpers.cl,
|
||||
marko_classAttr = marko_helpers.ca;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.e("div", {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
var attrs = {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
test_hello_tag = marko_loadTag(require("./tags/test-hello/renderer")),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_attrs0 = {
|
||||
"class": "foo"
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename);
|
||||
var marko_template = module.exports = require("marko/vdom").t();
|
||||
|
||||
function render(data, out) {
|
||||
out.t("Hello ");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_forEach = marko_helpers.f,
|
||||
marko_createElement = marko_helpers.e,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_createElement = marko_helpers.e,
|
||||
marko_const = marko_helpers.const,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_createElement = marko_helpers.e,
|
||||
marko_const = marko_helpers.const,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var marko_template = module.exports = require("marko/vdom").t(__filename),
|
||||
var marko_template = module.exports = require("marko/vdom").t(),
|
||||
marko_helpers = require("marko/runtime/vdom/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
test_hello_tag = marko_loadTag(require("./tags/test-hello/renderer"));
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
|
||||
module.exports = function(helpers) {
|
||||
var targetEl = helpers.document.createElement('div');
|
||||
var virtualEl = helpers.vdom.createElement('div', { class: 'foo', 'xlink:href': 'bar.com' });
|
||||
virtualEl.assignAttributes(targetEl);
|
||||
return targetEl;
|
||||
var morphAttrs = helpers.vdom.HTMLElement.$__morphAttrs;
|
||||
|
||||
var fromEl = helpers.document.createElement('div');
|
||||
var toEl = helpers.vdom.createElement('div', { class: 'foo', 'xlink:href': 'bar.com' });
|
||||
morphAttrs(fromEl, toEl);
|
||||
return fromEl;
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
<div>
|
||||
<h1>
|
||||
"New child"
|
||||
@ -1,20 +0,0 @@
|
||||
var expect = require('chai').expect;
|
||||
module.exports = function(helpers) {
|
||||
var div = helpers.vdom.createElement('div', null, 1 /* childCount */)
|
||||
.e('span', { class: 'bar' }, 0);
|
||||
|
||||
expect(div.firstChild.nodeName).to.equal('span');
|
||||
|
||||
div.removeChildren();
|
||||
expect(div.firstChild).to.equal(undefined);
|
||||
|
||||
var newChild = helpers.vdom.createElement('h1', null, 1)
|
||||
.t('New child');
|
||||
|
||||
div.$__appendChild(newChild);
|
||||
|
||||
expect(div.firstChild).to.equal(newChild);
|
||||
expect(div.firstChild.nextSibling).to.equal(undefined);
|
||||
|
||||
return div;
|
||||
};
|
||||
@ -4,9 +4,9 @@ var marko_template = module.exports = require("marko/html").t(__filename),
|
||||
marko_widgetType = marko_registerWidget("/marko-test$1.0.0/autotests/widgets-compilation/ref/index.marko", function() {
|
||||
return module.exports;
|
||||
}),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTemplate = marko_helpers.l,
|
||||
marko_loadTemplate = require("marko/runtime/helper-loadTemplate"),
|
||||
app_foo_template = marko_loadTemplate(require.resolve("./components/app-foo")),
|
||||
marko_helpers = require("marko/runtime/html/helpers"),
|
||||
marko_loadTag = marko_helpers.t,
|
||||
app_foo_tag = marko_loadTag(app_foo_template),
|
||||
marko_attr = marko_helpers.a;
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
'use strict';
|
||||
const jsdom = require("jsdom").jsdom;
|
||||
const defaultDocument = jsdom('<html><body></body></html>');
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const marko = require('marko');
|
||||
const fsExtra = require('fs-extra');
|
||||
const domToHTML = require('./domToHTML');
|
||||
const domToString = require('./domToString');
|
||||
const jsdom = require("jsdom").jsdom;
|
||||
|
||||
const expect = require('chai').expect;
|
||||
|
||||
const defaultDocument = jsdom('<html><body></body></html>');
|
||||
require('../../').setDocument(defaultDocument); // We need this to parse HTML fragments on the server
|
||||
|
||||
|
||||
function createAsyncVerifier(main, helpers, out) {
|
||||
@ -141,8 +142,8 @@ module.exports = function runRenderTest(dir, helpers, done, options) {
|
||||
}
|
||||
|
||||
out.on('error', done);
|
||||
out.on('finish', function() {
|
||||
var renderOutput = out.getOutput();
|
||||
out.on('finish', function(result) {
|
||||
var renderOutput = result.getOutput();
|
||||
|
||||
if (isVDOM) {
|
||||
let vdomTree = renderOutput;
|
||||
@ -158,6 +159,9 @@ module.exports = function runRenderTest(dir, helpers, done, options) {
|
||||
}
|
||||
|
||||
require('marko/compiler').configure({ output: 'html' });
|
||||
require('marko/runtime/vdom/AsyncVDOMBuilder').prototype.$__document = defaultDocument;
|
||||
global.document = defaultDocument;
|
||||
|
||||
let htmlTemplatePath = path.join(dir, 'template.marko');
|
||||
let htmlTemplate = marko.load(htmlTemplatePath);
|
||||
let htmlMainPath = path.join(dir, 'test.js');
|
||||
|
||||
@ -28,7 +28,7 @@ function toHTML(node) {
|
||||
|
||||
html += indent + '<' + tagName;
|
||||
|
||||
var attributes = el.attributes;
|
||||
var attributes = el.attributes || el.$__attributes;
|
||||
var attributesArray = [];
|
||||
var attrName;
|
||||
|
||||
|
||||
@ -23,7 +23,8 @@ var vdomHelpers = {
|
||||
},
|
||||
createDocumentFragment: function() {
|
||||
return new DocumentFragment();
|
||||
}
|
||||
},
|
||||
HTMLElement: HTMLElement
|
||||
};
|
||||
|
||||
describe('marko-vdom', () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
var path = require('path');
|
||||
var virtualize = require('../runtime/vdom/virtualize');
|
||||
var virtualize = require('../runtime/vdom/vdom').$__virtualize;
|
||||
var fs = require('fs');
|
||||
var toHTML = require('./util/toHTML');
|
||||
var jsdom = require("jsdom").jsdom;
|
||||
|
||||
@ -5,6 +5,7 @@ describe('marko-widgets (server)', function() {
|
||||
require('./util/autotest').runTests(
|
||||
require('./autotests/widgets-server/autotests.tests'),
|
||||
function run(testFunc, done) {
|
||||
require('marko/compiler').configure({ output: 'html' });
|
||||
var helpers = {};
|
||||
|
||||
if (testFunc.length === 1) {
|
||||
|
||||
@ -62,11 +62,10 @@ State.prototype = {
|
||||
}
|
||||
},
|
||||
$__set: function(name, value, shouldEnsure, forceDirty, noQueue) {
|
||||
var self = this;
|
||||
var rawState = self.$__raw;
|
||||
var rawState = this.$__raw;
|
||||
|
||||
if (shouldEnsure) {
|
||||
ensure(self, name);
|
||||
ensure(this, name);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
@ -106,7 +105,7 @@ State.prototype = {
|
||||
if (clean && noQueue !== true) {
|
||||
// If we were clean before then we are now dirty so queue
|
||||
// up the widget for update
|
||||
updateManager.$__queueWidgetUpdate(self.$__widget);
|
||||
updateManager.$__queueWidgetUpdate(this.$__widget);
|
||||
}
|
||||
},
|
||||
toJSON: function() {
|
||||
|
||||
@ -3,15 +3,22 @@
|
||||
|
||||
var domInsert = require('../runtime/dom-insert');
|
||||
var marko = require('../');
|
||||
var markoWidgets = require('./');
|
||||
var getRootEls = require('./getRootEls');
|
||||
var widgetsUtil = require('./util');
|
||||
var getWidgetForEl = widgetsUtil.$__getWidgetForEl;
|
||||
var widgetLookup = widgetsUtil.$__widgetLookup;
|
||||
var emitLifecycleEvent = widgetsUtil.$__emitLifecycleEvent;
|
||||
var destroyWidgetForEl = widgetsUtil.$__destroyWidgetForEl;
|
||||
var destroyElRecursive = widgetsUtil.$__destroyElRecursive;
|
||||
var getElementById = widgetsUtil.$__getElementById;
|
||||
var EventEmitter = require('events-light');
|
||||
var RenderResult = require('../runtime/RenderResult');
|
||||
var SubscriptionTracker = require('listener-tracker');
|
||||
var inherit = require('raptor-util/inherit');
|
||||
var updateManager = require('./update-manager');
|
||||
var morphdom = require('morphdom');
|
||||
var widgetLookup = require('./lookup').$__widgets;
|
||||
var morphAttrs = require('../runtime/vdom/HTMLElement').$__morphAttrs;
|
||||
var morphdomFactory = require('morphdom/factory');
|
||||
var morphdom = morphdomFactory(morphAttrs);
|
||||
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
@ -24,115 +31,14 @@ var NON_WIDGET_SUBSCRIBE_TO_OPTIONS = {
|
||||
|
||||
var emit = EventEmitter.prototype.emit;
|
||||
|
||||
var lifecycleEventMethods = {};
|
||||
|
||||
['beforeDestroy',
|
||||
'destroy',
|
||||
'beforeUpdate',
|
||||
'update',
|
||||
'mount',
|
||||
'render',
|
||||
'beforeInit',
|
||||
'afterInit'].forEach(function(eventName) {
|
||||
lifecycleEventMethods[eventName] = 'on' + eventName.charAt(0).toUpperCase() + eventName.substring(1);
|
||||
});
|
||||
|
||||
function removeListener(eventListenerHandle) {
|
||||
eventListenerHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles invoking a widget's event handler method
|
||||
* (if present) while also emitting the event through
|
||||
* the standard EventEmitter.prototype.emit method.
|
||||
*
|
||||
* Special events and their corresponding handler methods
|
||||
* include the following:
|
||||
*
|
||||
* beforeDestroy --> onBeforeDestroy
|
||||
* destroy --> onDestroy
|
||||
* beforeUpdate --> onBeforeUpdate
|
||||
* update --> onUpdate
|
||||
* render --> onRender
|
||||
*/
|
||||
function emitLifecycleEvent(widget, eventType, eventArg) {
|
||||
var listenerMethod = widget[lifecycleEventMethods[eventType]];
|
||||
|
||||
if (listenerMethod) {
|
||||
listenerMethod.call(widget, eventArg);
|
||||
}
|
||||
|
||||
widget.emit(eventType, eventArg);
|
||||
}
|
||||
|
||||
function removeDOMEventListeners(widget) {
|
||||
var eventListenerHandles = widget.$__domEventListenerHandles;
|
||||
if (eventListenerHandles) {
|
||||
eventListenerHandles.forEach(removeListener);
|
||||
widget.$__domEventListenerHandles = null;
|
||||
}
|
||||
}
|
||||
|
||||
function destroyWidgetForEl(el) {
|
||||
var widgetToDestroy = el._w;
|
||||
if (widgetToDestroy) {
|
||||
destroyWidgetHelper(widgetToDestroy);
|
||||
el._w = null;
|
||||
|
||||
while ((widgetToDestroy = widgetToDestroy.$__rootFor)) {
|
||||
widgetToDestroy.$__rootFor = null;
|
||||
destroyWidgetHelper(widgetToDestroy);
|
||||
}
|
||||
}
|
||||
}
|
||||
function destroyElRecursive(el) {
|
||||
var curChild = el.firstChild;
|
||||
while(curChild) {
|
||||
if (curChild.nodeType === 1) {
|
||||
destroyElRecursive(curChild);
|
||||
destroyWidgetForEl(curChild);
|
||||
}
|
||||
curChild = curChild.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function destroyWidgetHelper(widget) {
|
||||
if (widget.$__destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitLifecycleEvent(widget, 'beforeDestroy');
|
||||
widget.$__destroyed = true;
|
||||
|
||||
widget.els = null;
|
||||
widget.el = null;
|
||||
|
||||
// Unsubscribe from all DOM events
|
||||
removeDOMEventListeners(widget);
|
||||
|
||||
if (widget.$__subscriptions) {
|
||||
widget.$__subscriptions.removeAllListeners();
|
||||
widget.$__subscriptions = null;
|
||||
}
|
||||
|
||||
delete widgetLookup[widget.id];
|
||||
|
||||
emitLifecycleEvent(widget, 'destroy');
|
||||
}
|
||||
|
||||
function resetWidget(widget) {
|
||||
widget.$__newProps = null;
|
||||
widget.$__state.$__reset();
|
||||
function removeListener(removeEventListenerHandle) {
|
||||
removeEventListenerHandle();
|
||||
}
|
||||
|
||||
function hasCompatibleWidget(widgetsContext, existingWidget) {
|
||||
var id = existingWidget.id;
|
||||
var newWidgetDef = widgetsContext.$__widgetsById[id];
|
||||
if (!newWidgetDef) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return existingWidget.$__type === newWidgetDef.$__type;
|
||||
return newWidgetDef && existingWidget.$__type == newWidgetDef.$__type;
|
||||
}
|
||||
|
||||
function handleCustomEventWithMethodListener(widget, targetMethodName, args, extraArgs) {
|
||||
@ -174,8 +80,7 @@ function getElIdHelper(widget, widgetElId, index) {
|
||||
*/
|
||||
function processUpdateHandlers(widget, stateChanges, oldState) {
|
||||
var handlerMethod;
|
||||
var handlers = [];
|
||||
|
||||
var handlers;
|
||||
|
||||
for (var propName in stateChanges) {
|
||||
if (stateChanges.hasOwnProperty(propName)) {
|
||||
@ -183,11 +88,11 @@ function processUpdateHandlers(widget, stateChanges, oldState) {
|
||||
|
||||
handlerMethod = widget[handlerMethodName];
|
||||
if (handlerMethod) {
|
||||
handlers.push([propName, handlerMethod]);
|
||||
(handlers || (handlers=[])).push([propName, handlerMethod]);
|
||||
} else {
|
||||
// This state change does not have a state handler so return false
|
||||
// to force a rerender
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,30 +100,27 @@ function processUpdateHandlers(widget, stateChanges, oldState) {
|
||||
// If we got here then all of the changed state properties have
|
||||
// an update handler or there are no state properties that actually
|
||||
// changed.
|
||||
if (handlers) {
|
||||
// Otherwise, there are handlers for all of the changed properties
|
||||
// so apply the updates using those handlers
|
||||
|
||||
if (!handlers.length) {
|
||||
return true;
|
||||
emitLifecycleEvent(widget, 'beforeUpdate');
|
||||
|
||||
for (var i=0, len=handlers.length; i<len; i++) {
|
||||
var handler = handlers[i];
|
||||
var propertyName = handler[0];
|
||||
handlerMethod = handler[1];
|
||||
|
||||
var newValue = stateChanges[propertyName];
|
||||
var oldValue = oldState[propertyName];
|
||||
handlerMethod.call(widget, newValue, oldValue);
|
||||
}
|
||||
|
||||
emitLifecycleEvent(widget, 'update');
|
||||
|
||||
widget.$__reset();
|
||||
}
|
||||
|
||||
// Otherwise, there are handlers for all of the changed properties
|
||||
// so apply the updates using those handlers
|
||||
|
||||
emitLifecycleEvent(widget, 'beforeUpdate');
|
||||
|
||||
for (var i=0, len=handlers.length; i<len; i++) {
|
||||
var handler = handlers[i];
|
||||
var propertyName = handler[0];
|
||||
handlerMethod = handler[1];
|
||||
|
||||
var newValue = stateChanges[propertyName];
|
||||
var oldValue = oldState[propertyName];
|
||||
handlerMethod.call(widget, newValue, oldValue);
|
||||
}
|
||||
|
||||
emitLifecycleEvent(widget, 'update');
|
||||
|
||||
resetWidget(widget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -229,20 +131,23 @@ var widgetProto;
|
||||
*
|
||||
* NOTE: Any methods that are prefixed with an underscore should be considered private!
|
||||
*/
|
||||
function Widget(id, document) {
|
||||
function Widget(id, doc) {
|
||||
EventEmitter.call(this);
|
||||
this.id = id;
|
||||
this.el = null;
|
||||
this.$__bodyEl = null;
|
||||
this.$__state = null;
|
||||
this.$__roots = null;
|
||||
this.$__subscriptions = null;
|
||||
this.$__domEventListenerHandles = null;
|
||||
this.$__destroyed = false;
|
||||
this.$__customEvents = null;
|
||||
this.$__scope = null;
|
||||
this.$__updateQueued = false;
|
||||
this.$__document = document;
|
||||
this.el =
|
||||
this.$__state =
|
||||
this.$__roots =
|
||||
this.$__subscriptions =
|
||||
this.$__domEventListenerHandles =
|
||||
this.$__customEvents =
|
||||
this.$__scope =
|
||||
null;
|
||||
|
||||
this.$__destroyed =
|
||||
this.$__updateQueued =
|
||||
false;
|
||||
|
||||
this.$__document = doc;
|
||||
}
|
||||
|
||||
Widget.prototype = widgetProto = {
|
||||
@ -253,16 +158,13 @@ Widget.prototype = widgetProto = {
|
||||
throw TypeError();
|
||||
}
|
||||
|
||||
var tracker = this.$__subscriptions;
|
||||
if (!tracker) {
|
||||
this.$__subscriptions = tracker = new SubscriptionTracker();
|
||||
}
|
||||
var subscriptions = this.$__subscriptions || (subscriptions = new SubscriptionTracker());
|
||||
|
||||
var subscribeToOptions = target.$__isWidget ?
|
||||
WIDGET_SUBSCRIBE_TO_OPTIONS :
|
||||
NON_WIDGET_SUBSCRIBE_TO_OPTIONS;
|
||||
|
||||
return tracker.subscribeTo(target, subscribeToOptions);
|
||||
return subscriptions.subscribeTo(target, subscribeToOptions);
|
||||
},
|
||||
|
||||
emit: function(eventType) {
|
||||
@ -286,19 +188,16 @@ Widget.prototype = widgetProto = {
|
||||
var doc = this.$__document;
|
||||
|
||||
if (widgetElId != null) {
|
||||
return doc.getElementById(getElIdHelper(this, widgetElId, index));
|
||||
return getElementById(doc, getElIdHelper(this, widgetElId, index));
|
||||
} else {
|
||||
return this.el || doc.getElementById(getElIdHelper(this));
|
||||
return this.el || getElementById(doc, getElIdHelper(this));
|
||||
}
|
||||
},
|
||||
getEls: function(id) {
|
||||
var els = [];
|
||||
var i=0;
|
||||
while(true) {
|
||||
var el = this.getEl(id, i);
|
||||
if (!el) {
|
||||
break;
|
||||
}
|
||||
var i = 0;
|
||||
var el;
|
||||
while((el = this.getEl(id, i))) {
|
||||
els.push(el);
|
||||
i++;
|
||||
}
|
||||
@ -309,18 +208,15 @@ Widget.prototype = widgetProto = {
|
||||
},
|
||||
getWidgets: function(id) {
|
||||
var widgets = [];
|
||||
var i=0;
|
||||
while(true) {
|
||||
var widget = widgetLookup[getElIdHelper(this, id, i)];
|
||||
if (!widget) {
|
||||
break;
|
||||
}
|
||||
var i = 0;
|
||||
var widget;
|
||||
while((widget = widgetLookup[getElIdHelper(this, id, i)])) {
|
||||
widgets.push(widget);
|
||||
i++;
|
||||
}
|
||||
return widgets;
|
||||
},
|
||||
destroy: function () {
|
||||
destroy: function() {
|
||||
if (this.$__destroyed) {
|
||||
return;
|
||||
}
|
||||
@ -346,25 +242,52 @@ Widget.prototype = widgetProto = {
|
||||
}
|
||||
}
|
||||
|
||||
destroyWidgetHelper(this);
|
||||
this.$__destroyShallow();
|
||||
},
|
||||
isDestroyed: function () {
|
||||
|
||||
$__destroyShallow: function() {
|
||||
if (this.$__destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitLifecycleEvent(this, 'beforeDestroy');
|
||||
this.$__destroyed = true;
|
||||
|
||||
this.els = null;
|
||||
this.el = null;
|
||||
|
||||
// Unsubscribe from all DOM events
|
||||
this.$__removeDOMEventListeners();
|
||||
|
||||
var subscriptions = this.$__subscriptions;
|
||||
if (subscriptions) {
|
||||
subscriptions.removeAllListeners();
|
||||
this.$__subscriptions = null;
|
||||
}
|
||||
|
||||
delete widgetLookup[this.id];
|
||||
|
||||
emitLifecycleEvent(this, 'destroy');
|
||||
},
|
||||
|
||||
isDestroyed: function() {
|
||||
return this.$__destroyed;
|
||||
},
|
||||
get state() {
|
||||
return this.$__state;
|
||||
},
|
||||
set state(value) {
|
||||
if(!this.$__state && value) {
|
||||
var state = this.$__state;
|
||||
if(!state && value) {
|
||||
this.$__state = new this.$__State(this, value);
|
||||
} else {
|
||||
this.$__state.$__replace(value);
|
||||
state.$__replace(value);
|
||||
}
|
||||
},
|
||||
setState: function(name, value) {
|
||||
var state = this.$__state;
|
||||
|
||||
if (typeof name === 'object') {
|
||||
if (typeof name == 'object') {
|
||||
// Merge in the new state with the old state
|
||||
var newState = name;
|
||||
for (var k in newState) {
|
||||
@ -372,16 +295,15 @@ Widget.prototype = widgetProto = {
|
||||
state.$__set(k, newState[k], true /* ensure:true */);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
state.$__set(name, value, true /* ensure:true */);
|
||||
}
|
||||
|
||||
state.$__set(name, value, true /* ensure:true */);
|
||||
},
|
||||
|
||||
setStateDirty: function(name, value) {
|
||||
var state = this.$__state;
|
||||
|
||||
if (arguments.length === 1) {
|
||||
if (arguments.length == 1) {
|
||||
value = state[name];
|
||||
}
|
||||
|
||||
@ -401,25 +323,26 @@ Widget.prototype = widgetProto = {
|
||||
* @param {Object} props The widget's new props
|
||||
*/
|
||||
setProps: function(newProps) {
|
||||
if (this.getInitialState) {
|
||||
if (this.getInitialProps) {
|
||||
newProps = this.getInitialProps(newProps) || {};
|
||||
var onInput = this.onInput;
|
||||
var getInitialState;
|
||||
|
||||
if (onInput) {
|
||||
onInput.call(this, newProps || {});
|
||||
} else if ((getInitialState = this.getInitialState)) {
|
||||
var getInitialProps = this.getInitialProps;
|
||||
|
||||
if (getInitialProps) {
|
||||
newProps = getInitialProps.call(this, newProps) || {};
|
||||
}
|
||||
var newState = getInitialState.call(this, newProps);
|
||||
this.$__state.$__replace(newState);
|
||||
} else {
|
||||
if (!this.$__newProps) {
|
||||
updateManager.$__queueWidgetUpdate(this);
|
||||
}
|
||||
var newState = this.getInitialState(newProps);
|
||||
this.replaceState(newState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onInput) {
|
||||
this.onInput(newProps || {});
|
||||
return;
|
||||
this.$__newProps = newProps;
|
||||
}
|
||||
|
||||
if (!this.$__newProps) {
|
||||
updateManager.$__queueWidgetUpdate(this);
|
||||
}
|
||||
|
||||
this.$__newProps = newProps;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
@ -432,18 +355,16 @@ Widget.prototype = widgetProto = {
|
||||
var state = this.$__state;
|
||||
|
||||
if (this.shouldUpdate(newProps, state) === false) {
|
||||
resetWidget(this);
|
||||
this.$__reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newProps) {
|
||||
resetWidget(this);
|
||||
this.$__reset();
|
||||
this.rerender(newProps);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!state.$__dirty) {
|
||||
// Don't even bother trying to update this widget since it is
|
||||
// not marked as dirty.
|
||||
@ -458,7 +379,7 @@ Widget.prototype = widgetProto = {
|
||||
}
|
||||
|
||||
// Reset all internal properties for tracking state changes, etc.
|
||||
resetWidget(this);
|
||||
this.$__reset();
|
||||
},
|
||||
|
||||
$__replaceState: function(newState) {
|
||||
@ -478,19 +399,16 @@ Widget.prototype = widgetProto = {
|
||||
return this.$__state.$__dirty;
|
||||
},
|
||||
|
||||
$__reset: function(shouldRemoveDOMEventListeners) {
|
||||
resetWidget(this);
|
||||
|
||||
if (shouldRemoveDOMEventListeners) {
|
||||
removeDOMEventListeners(this);
|
||||
}
|
||||
$__reset: function() {
|
||||
this.$__newProps = null;
|
||||
this.$__state.$__reset();
|
||||
},
|
||||
|
||||
shouldUpdate: function(newState, newProps) {
|
||||
return true;
|
||||
},
|
||||
|
||||
doUpdate: function (stateChanges, oldState) {
|
||||
doUpdate: function() {
|
||||
this.rerender();
|
||||
},
|
||||
|
||||
@ -500,33 +418,32 @@ Widget.prototype = widgetProto = {
|
||||
|
||||
rerender: function(props) {
|
||||
var self = this;
|
||||
|
||||
if (!self.renderer) {
|
||||
throw Error('No renderer');
|
||||
}
|
||||
|
||||
var renderer = self.renderer;
|
||||
|
||||
if (!renderer) {
|
||||
throw TypeError();
|
||||
}
|
||||
|
||||
var state = self.$__state;
|
||||
|
||||
var globalData = {};
|
||||
globalData.$w = [self, !props && state && state.$__raw];
|
||||
|
||||
var fromEls = getRootEls(self, {});
|
||||
var fromEls = self.$__getRootEls({});
|
||||
var doc = self.$__document;
|
||||
|
||||
updateManager.$__batchUpdate(function() {
|
||||
var createOut = renderer.createOut || marko.createOut;
|
||||
var out = createOut(globalData);
|
||||
out.$__document = self.$__document;
|
||||
renderer(props, out);
|
||||
var result = new RenderResult(out);
|
||||
|
||||
var targetNode = out.getOutput();
|
||||
var targetNode = out.$__getOutput();
|
||||
|
||||
var widgetsContext = out.global.widgets;
|
||||
|
||||
function onNodeDiscarded(node) {
|
||||
if (node.nodeType === 1) {
|
||||
if (node.nodeType == 1) {
|
||||
destroyWidgetForEl(node);
|
||||
}
|
||||
}
|
||||
@ -535,20 +452,6 @@ Widget.prototype = widgetProto = {
|
||||
var id = fromEl.id;
|
||||
var existingWidget;
|
||||
|
||||
var preservedAttrs = !out.isVDOM && toEl.getAttribute('data-preserve-attrs');
|
||||
if (preservedAttrs) {
|
||||
preservedAttrs = preservedAttrs.split(/\s*[,]\s*/);
|
||||
for (var i=0; i<preservedAttrs.length; i++) {
|
||||
var preservedAttrName = preservedAttrs[i];
|
||||
var preservedAttrValue = fromEl.getAttribute(preservedAttrName);
|
||||
if (preservedAttrValue == null) {
|
||||
toEl.removeAttribute(preservedAttrName);
|
||||
} else {
|
||||
toEl.setAttribute(preservedAttrName, preservedAttrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (widgetsContext && id) {
|
||||
var preserved = widgetsContext.$__preserved[id];
|
||||
|
||||
@ -558,12 +461,12 @@ Widget.prototype = widgetProto = {
|
||||
// the morphing will take place when the reused widget updates.
|
||||
return MORPHDOM_SKIP;
|
||||
} else {
|
||||
existingWidget = markoWidgets.getWidgetForEl(fromEl);
|
||||
existingWidget = getWidgetForEl(fromEl);
|
||||
if (existingWidget && !hasCompatibleWidget(widgetsContext, existingWidget)) {
|
||||
// We found a widget in an old DOM node that does not have
|
||||
// a compatible widget that was rendered so we need to
|
||||
// destroy the old widget
|
||||
destroyWidgetHelper(existingWidget);
|
||||
existingWidget.$__destroyShallow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -612,9 +515,38 @@ Widget.prototype = widgetProto = {
|
||||
// widget was queued for update and the re-rendered
|
||||
// before the update occurred then nothing will happen
|
||||
// at the time of the update.
|
||||
resetWidget(self);
|
||||
self.$__reset();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
$__getRootEls: function(rootEls) {
|
||||
var i, len;
|
||||
|
||||
var widgetEls = this.els;
|
||||
|
||||
for (i=0, len=widgetEls.length; i<len; i++) {
|
||||
var widgetEl = widgetEls[i];
|
||||
rootEls[widgetEl.id] = widgetEl;
|
||||
}
|
||||
|
||||
var rootWidgets = this.$__rootWidgets;
|
||||
if (rootWidgets) {
|
||||
for (i=0, len=rootWidgets.length; i<len; i++) {
|
||||
var rootWidget = rootWidgets[i];
|
||||
rootWidget.$__getRootEls(rootEls);
|
||||
}
|
||||
}
|
||||
|
||||
return rootEls;
|
||||
},
|
||||
|
||||
$__removeDOMEventListeners: function() {
|
||||
var eventListenerHandles = this.$__domEventListenerHandles;
|
||||
if (eventListenerHandles) {
|
||||
eventListenerHandles.forEach(removeListener);
|
||||
this.$__domEventListenerHandles = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -639,7 +571,7 @@ domInsert(
|
||||
}
|
||||
return fragment;
|
||||
} else {
|
||||
return this.els[0];
|
||||
return els[0];
|
||||
}
|
||||
},
|
||||
function afterInsert(widget) {
|
||||
|
||||
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