Структура расположения папок и файлов:
/index.html
/source/css/index.css
/source/js/config/systemjs-config.js
/source/js/index.js
/source/js/app/view/view.html
/source/js/app/sum/sum.js
/source/js/app/render/render.js
/source/js/lib/react/react.js
/source/js/lib/react/react-dom.js
/source/js/lib/systemjs/system.src.js
/source/js/lib/systemjs/text.js
/source/js/lib/systemjs/css.js
/source/js/lib/systemjs/plugin-babel.js
/source/js/lib/systemjs/systemjs-babel-browser.js
/source/js/lib/systemjs/regenerator-runtime.js
/source/js/lib/systemjs/babel-helpers.js
/source/js/lib/systemjs/babel-helpers/ - папка с файлами
Файл index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script type="text/javascript" src="source/js/lib/systemjs/system.src.js"></script>
<script type="text/javascript" src="source/js/config/systemjs-config.js"></script>
<title>SystemJS and Babel</title>
</head>
<body>
<div id="root"></div>
<div id="message"></div>
<script type="text/javascript">SystemJS.import('js/index.js');</script>
</body>
</html>
Файл systemjs-config.js
SystemJS.config({
baseURL: 'source'
, map: {
'plugin-babel': 'js/lib/systemjs/plugin-babel.js'
, 'systemjs-babel-build': 'js/lib/systemjs/systemjs-babel-browser.js'
, 'text': 'js/lib/systemjs/text.js'
, 'css': 'js/lib/systemjs/css.js'
}
, transpiler: 'plugin-babel'
, meta: {
'*.js': {
babelOptions: {
stage1: true
, react: true
}
}
, '*.css': {loader: 'css'}
, '*.html': {loader: 'text'}
}
, paths: {
'react': 'js/lib/react/react.js'
, 'react-dom': 'js/lib/react/react-dom.js'
, indexStyle: 'style/default/index.css'
, view: 'app/view/view.html'
, sum: 'app/sum/sum.js'
, render: 'app/render/render.js'
}
});
Файл index.css
div#root {font-size: 50px;}
Файл index.js
import 'indexStyle';
import view from 'view';
import render from 'render';
import {sum} from 'sum';
document.getElementById('message').innerHTML = view;
render();
alert(sum(1, 2););
Файл view.html
<p>World</p>
Файл render.js
import React from 'react';
import ReactDOM from 'react-dom';
export default () => {
ReactDOM.render(<h1>Hello</h1>, document.getElementById('root'));
};
Файл sum.js
export function sum (a, b) {return a + b;}
среда, 29 марта 2017 г.
четверг, 23 марта 2017 г.
Webpack + RequireJS Plugins loader
Webpack loader polyfill for RequireJS "text" loader module.
Сборка через Webpack файлов, загружаемых с помощью плагинов для RequireJS имеющих пути вида require("plugin-name!path/to/file").
Рассмотрим сборку проекта на примере загрузки файлов с помощью плагина "text".
Папку "text-loader" с кодом плагина загрузчика для Webpack необходимо поместить в папку "node_modules".
/node_module/text-loader/index.js
Код файла "index.js"
module.exports = function (content) {
this.cacheable && this.cacheable();
this.value = content;
return "module.exports = " + JSON.stringify(content);
}
В файле "webpack.config.js" необходимо добавить следующие строки для использования загрузчика "text-loader"
...
...
resolveLoader: {
alias: {
"text": "text-loader"
}
},
...
...
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.js', '.ts', '.tsx'],
alias: {
...
...
template: path.resolve(__dirname, './source/template.html'),
...
...
}
}
...
Пример кода javascript-файла, загружающего модуль "template.html" внутри себя
define(function (require) {
var template = require('text!template');
console.log(template);
});
Пример кода файла "template.html"
<h1>Hello</h1>
Пример кода плагина загрузчика JS-кода для Webpack.
/node_module/js-loader/index.js
module.exports = function (content) {
this.cacheable && this.cacheable();
this.value = content;
return content;
}
В файле "webpack.config.js" необходимо добавить следующие строки для использования загрузчика "js-loader"
...
...
resolveLoader: {
alias: {
"js": "js-loader"
}
},
...
...
Пример кода javascript-файла, загружающего модуль "module.js" внутри себя
define(function (require) {
var alertOne = require('js!module');
alertOne();
});
Пример кода файла "module.js"
define(function () {
return function () {
alert(1);
};
});
Сборка через Webpack файлов, загружаемых с помощью плагинов для RequireJS имеющих пути вида require("plugin-name!path/to/file").
Рассмотрим сборку проекта на примере загрузки файлов с помощью плагина "text".
Папку "text-loader" с кодом плагина загрузчика для Webpack необходимо поместить в папку "node_modules".
/node_module/text-loader/index.js
Код файла "index.js"
module.exports = function (content) {
this.cacheable && this.cacheable();
this.value = content;
return "module.exports = " + JSON.stringify(content);
}
В файле "webpack.config.js" необходимо добавить следующие строки для использования загрузчика "text-loader"
...
...
resolveLoader: {
alias: {
"text": "text-loader"
}
},
...
...
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.js', '.ts', '.tsx'],
alias: {
...
...
template: path.resolve(__dirname, './source/template.html'),
...
...
}
}
...
Пример кода javascript-файла, загружающего модуль "template.html" внутри себя
define(function (require) {
var template = require('text!template');
console.log(template);
});
Пример кода файла "template.html"
<h1>Hello</h1>
Пример кода плагина загрузчика JS-кода для Webpack.
/node_module/js-loader/index.js
module.exports = function (content) {
this.cacheable && this.cacheable();
this.value = content;
return content;
}
В файле "webpack.config.js" необходимо добавить следующие строки для использования загрузчика "js-loader"
...
...
resolveLoader: {
alias: {
"js": "js-loader"
}
},
...
...
Пример кода javascript-файла, загружающего модуль "module.js" внутри себя
define(function (require) {
var alertOne = require('js!module');
alertOne();
});
Пример кода файла "module.js"
define(function () {
return function () {
alert(1);
};
});
среда, 22 марта 2017 г.
RequireJS Plugin Builder for ECMAScript 6 Babel
Структура расположения папок и файлов:
/index.html
/build/build.js - итоговый собранный файл с помщью Node.js
/js/app/main/main.js
/js/app/simple/simple.js
/js/app/sum/sum.js
/js/app/class/class.js
/js/app/render/render.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/lib/loader/loader.js
/js/lib/babel-standalone/babel.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
/gulp.js
/gulpfile.js
/node_modules
/node.exe
/npm.cmd
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
</head>
<body>
<div id="root"></div>
<script src="build/build.js"></script>
<script>require(['main']);</script>
</body>
</html>
Файл main.js
define(function (require) {
var simple = require('simple');
simple();
var sum = require('loader!sum');
require('loader!class');
require('loader!render');
alert(sum(1, 2));
});
Файл simple.js
define(function () {
return function () {alert(1);}
});
Файл sum.js
define(function (require) {
return (a, b) => a + b;
});
Файл class.js
define(function (require) {
var sum = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл render.js
define(function (require) {
var React = require('react');
var ReactDOM = require('react_dom');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл require-config.js
requirejs.config({
paths: {
loader: 'js/lib/loader/loader'
, babel: 'js/lib/babel-standalone/babel'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
, 'render': 'js/app/render/render'
, 'simple': 'js/app/simple/simple'
}
});
Файл loader.js
// ECMAScript 6 Babel Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , babel: 'js/lib/babel-standalone/babel'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-es6-file'], function (MyEs6File) {
// var myfile = new MyEs6File();
// ...
// });
// or
// define(function () {
// var myfile = new MyEs6File();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the ECMAScript 6 Babel Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['babel'], function(Babel) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = Babel.transform(responseText, {
presets: ['es2015', 'react']
, filename: 'embedded'
, sourceMaps: 'inline'
}).code;
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл gulp.js
const task = process.argv.slice(2).join(' ')
, command = `node ./node_modules/gulp/bin/gulp.js ${task}`
, execProcess = require('child_process').exec(command);
execProcess.stdout.on('data', data => console.log(Buffer.isBuffer(data) ? data.toString() : data));
execProcess.stderr.on('data', data => console.log(Buffer.isBuffer(data) ? data.toString() : data));
execProcess.on('close', code => code === 0 ? console.log('Done') : console.log(`Exit code: ${code}`));
Файл gulpfile.js
/index.html
/build/build.js - итоговый собранный файл с помщью Node.js
/js/app/main/main.js
/js/app/simple/simple.js
/js/app/sum/sum.js
/js/app/class/class.js
/js/app/render/render.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/lib/loader/loader.js
/js/lib/babel-standalone/babel.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
/gulp.js
/gulpfile.js
/node_modules
/node.exe
/npm.cmd
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
</head>
<body>
<div id="root"></div>
<script src="build/build.js"></script>
<script>require(['main']);</script>
</body>
</html>
Файл main.js
define(function (require) {
var simple = require('simple');
simple();
var sum = require('loader!sum');
require('loader!class');
require('loader!render');
alert(sum(1, 2));
});
Файл simple.js
define(function () {
return function () {alert(1);}
});
Файл sum.js
define(function (require) {
return (a, b) => a + b;
});
Файл class.js
define(function (require) {
var sum = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл render.js
define(function (require) {
var React = require('react');
var ReactDOM = require('react_dom');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл require-config.js
requirejs.config({
paths: {
loader: 'js/lib/loader/loader'
, babel: 'js/lib/babel-standalone/babel'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
, 'render': 'js/app/render/render'
, 'simple': 'js/app/simple/simple'
}
});
Файл loader.js
// ECMAScript 6 Babel Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , babel: 'js/lib/babel-standalone/babel'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-es6-file'], function (MyEs6File) {
// var myfile = new MyEs6File();
// ...
// });
// or
// define(function () {
// var myfile = new MyEs6File();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the ECMAScript 6 Babel Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['babel'], function(Babel) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = Babel.transform(responseText, {
presets: ['es2015', 'react']
, filename: 'embedded'
, sourceMaps: 'inline'
}).code;
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл gulp.js
const task = process.argv.slice(2).join(' ')
, command = `node ./node_modules/gulp/bin/gulp.js ${task}`
, execProcess = require('child_process').exec(command);
execProcess.stdout.on('data', data => console.log(Buffer.isBuffer(data) ? data.toString() : data));
execProcess.stderr.on('data', data => console.log(Buffer.isBuffer(data) ? data.toString() : data));
execProcess.on('close', code => code === 0 ? console.log('Done') : console.log(`Exit code: ${code}`));
Файл gulpfile.js
const fs = require('fs')
, del = require('del')
, gulp = require('gulp')
, requirejs = require('gulp-requirejs');
gulp.task('clean', () => del(['./build/**/*'], {force: true}));
gulp.task('requirejs', ['clean'], () => {
eval('var requirejsConfig =' + fs.readFileSync('./js/config/require-config.js').toString().replace('requirejs.config(', '').replace(');', ';'));
requirejsConfig.baseUrl = '.';
const config = {};
for (let key in requirejsConfig) {
if (requirejsConfig.hasOwnProperty(key)) {config[key] = requirejsConfig[key];}
}
config.name = 'main';
config.out = 'build.js';
requirejs(config).pipe(gulp.dest('./build'));
});
вторник, 21 марта 2017 г.
RequireJS Loader Plugin for TypeScript
Структура расположения папок и файлов:
/index.html
/js/index.js
/js/app/main/main.js
/js/app/class.js
/js/app/sum.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/lib/loader/loader.js
/'js/lib/typescript/typescriptServices.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
Файл loader.js
// TypeScript Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , typescript: 'js/lib/typescript/typescriptServices'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-typescript-file'], function (MyTypescriptFile) {
// var myfile = new MyTypescriptFile();
// ...
// });
// or
// define(function () {
// var myfile = new MyTypescriptFile();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the TypeScript Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['typescript'], function (ts) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
if (!window.exports) {window.exports = {};}
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = ts.transpileModule(responseText, {
compilerOptions: {
module: 'none' // 'amd' or ts.ModuleKind.AMD
, target: 'es3' // ts.ScriptTarget.ES3
, noImplicitUseStrict: true
, noEmitHelpers: true
, removeComments: true
, sourceMap: true
, inlineSourceMap: true
, inlineSources: true
, jsx: 'react'
, importHelpers: true
}
, moduleName: ''
}).outputText;
if (code === undefined) {throw new Error('Output generation failed');}
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
<script src="js/config/require-config.js"></script>
</head>
<body>
<div id="root"></div>
<script src="js/index.js"></script>
</body>
</html>
Файл require-config.js
requirejs.config({
config: {
loader: {
resolveModuleSource: function (source) {return 'loader!' + source;}
}
}
, shim: {
typescript: {exports: 'ts'}
}
, paths: {
loader: 'js/lib/loader/loader'
, typescript: 'js/lib/typescript/typescriptServices'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
}
});
Файл index.js
require(['loader!main']);
Файл main.js
define(function (require) {
var React = require('react')
, ReactDOM = require('react_dom');
require('loader!class');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл class.js
define(function (require) {
const sum: any = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл sum.js
define(function (require) {
return (a, b) => a + b;
});
/index.html
/js/index.js
/js/app/main/main.js
/js/app/class.js
/js/app/sum.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/lib/loader/loader.js
/'js/lib/typescript/typescriptServices.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
Файл loader.js
// TypeScript Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , typescript: 'js/lib/typescript/typescriptServices'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-typescript-file'], function (MyTypescriptFile) {
// var myfile = new MyTypescriptFile();
// ...
// });
// or
// define(function () {
// var myfile = new MyTypescriptFile();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the TypeScript Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['typescript'], function (ts) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
if (!window.exports) {window.exports = {};}
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = ts.transpileModule(responseText, {
compilerOptions: {
module: 'none' // 'amd' or ts.ModuleKind.AMD
, target: 'es3' // ts.ScriptTarget.ES3
, noImplicitUseStrict: true
, noEmitHelpers: true
, removeComments: true
, sourceMap: true
, inlineSourceMap: true
, inlineSources: true
, jsx: 'react'
, importHelpers: true
}
, moduleName: ''
}).outputText;
if (code === undefined) {throw new Error('Output generation failed');}
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
<script src="js/config/require-config.js"></script>
</head>
<body>
<div id="root"></div>
<script src="js/index.js"></script>
</body>
</html>
Файл require-config.js
requirejs.config({
config: {
loader: {
resolveModuleSource: function (source) {return 'loader!' + source;}
}
}
, shim: {
typescript: {exports: 'ts'}
}
, paths: {
loader: 'js/lib/loader/loader'
, typescript: 'js/lib/typescript/typescriptServices'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
}
});
Файл index.js
require(['loader!main']);
Файл main.js
define(function (require) {
var React = require('react')
, ReactDOM = require('react_dom');
require('loader!class');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл class.js
define(function (require) {
const sum: any = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл sum.js
define(function (require) {
return (a, b) => a + b;
});
четверг, 16 марта 2017 г.
Компиляция файла TypeScript в браузере
<!DOCTYPE html>
<html>
<head>
<title>TypeScript Compilation</title>
</head>
<body>
<div id="root"></div>
<textarea id="source" style="height: 300px; width: 500px;">
define(function (require) {
const b: any = require('some');
const c: any = 1;
if (1 == '2') {console.log(12);}
class A {
constructor (d) {
const a = 1;
}
}
class B extends A {
constructor () {
super();
}
render () {
return (
<div>a</div>;
);
}
}
// comments
});
</textarea>
<textarea id="result" style="height: 300px; width: 500px;"></textarea>
<script src="js/lib/typescript/typescriptServices.js"></script>
<script type="text/javascript">
var outputText = ts.transpileModule(document.getElementById('source').value, {
compilerOptions: {
module: 'none' // 'amd' or ts.ModuleKind.AMD
, target: 'es3' // ts.ScriptTarget.ES3
, noImplicitUseStrict: true
, noEmitHelpers: true
, removeComments: true
, sourceMap: true
, inlineSourceMap: true
, inlineSources: true
, jsx: 'react'
, importHelpers: true
}
, moduleName: ''
})
console.log(outputText);
outputText = outputText.outputText;
if (outputText === undefined) {throw new Error("Output generation failed");}
document.getElementById('result').innerHTML = outputText;
</script>
</body>
</html>
<html>
<head>
<title>TypeScript Compilation</title>
</head>
<body>
<div id="root"></div>
<textarea id="source" style="height: 300px; width: 500px;">
define(function (require) {
const b: any = require('some');
const c: any = 1;
if (1 == '2') {console.log(12);}
class A {
constructor (d) {
const a = 1;
}
}
class B extends A {
constructor () {
super();
}
render () {
return (
<div>a</div>;
);
}
}
// comments
});
</textarea>
<textarea id="result" style="height: 300px; width: 500px;"></textarea>
<script src="js/lib/typescript/typescriptServices.js"></script>
<script type="text/javascript">
var outputText = ts.transpileModule(document.getElementById('source').value, {
compilerOptions: {
module: 'none' // 'amd' or ts.ModuleKind.AMD
, target: 'es3' // ts.ScriptTarget.ES3
, noImplicitUseStrict: true
, noEmitHelpers: true
, removeComments: true
, sourceMap: true
, inlineSourceMap: true
, inlineSources: true
, jsx: 'react'
, importHelpers: true
}
, moduleName: ''
})
console.log(outputText);
outputText = outputText.outputText;
if (outputText === undefined) {throw new Error("Output generation failed");}
document.getElementById('result').innerHTML = outputText;
</script>
</body>
</html>
среда, 15 марта 2017 г.
RequireJS Loader Plugin for ECMAScript 6 Babel
Структура расположения папок и файлов:
/index.html
/js/lib/babel-standalone/babel.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
Файл loader.js
// ECMAScript 6 Babel Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , babel: 'js/lib/babel-standalone/babel'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-es6-file'], function (MyEs6File) {
// var myfile = new MyEs6File();
// ...
// });
// or
// define(function () {
// var myfile = new MyEs6File();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the ECMAScript 6 Babel Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['babel'], function(Babel) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = Babel.transform(responseText, {
presets: ['es2015', 'react']
, filename: 'embedded'
, sourceMaps: 'inline'
}).code;
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
<script src="js/config/require-config.js"></script>
</head>
<body>
<div id="root"></div>
<script src="js/index.js"></script>
</body>
</html>
Файл require-config.js
requirejs.config({
config: {
loader: {
resolveModuleSource: function (source) {return 'loader!' + source;}
}
}
, paths: {
loader: 'js/lib/loader/loader'
, babel: 'js/lib/babel-standalone/babel'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
}
});
Файл index.js
require(['loader!main']);
Файл main.js
define(function (require) {
require('loader!class');
var React = require('react')
, ReactDOM = require('react_dom');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл class.js
define(function (require) {
var sum = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл sum.js
/index.html
/js/index.js
/js/app/main/main.js
/js/app/class.js
/js/lib/loader/loader.js/js/app/class.js
/js/app/sum.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/config/require-config.js
/js/lib/requirejs/require.js
/js/lib/babel-standalone/babel.js
/js/lib/react/react.js
/js/lib/react/react-dom.js
Файл loader.js
// ECMAScript 6 Babel Loader Plugin
// For Browser
// Define paths:
// requirejs.config({
// config: {
// loader: {
// resolveModuleSource: function (source) {return 'loader!' + source;}
// }
// }
// , paths: {
// loader: 'js/lib/loader/loader'
// , babel: 'js/lib/babel-standalone/babel'
// }
// });
// Then use as a normal RequireJS plugin:
// require(['loader!path/to/my-es6-file'], function (MyEs6File) {
// var myfile = new MyEs6File();
// ...
// });
// or
// define(function () {
// var myfile = new MyEs6File();
// ...
// });
// For Server
// To use your existing client-side AMD modules in node, use global-define and set the path to the ECMAScript 6 Babel Loader Plugin.
// This so that when RequireJS tries to resolve the module name, it maps to the correct package.
// require('global-define')({
// basePath: __dirname
// , paths: {
// loader: 'js/lib/loader/loader'
// }
// });
define(['babel'], function(Babel) {
var buildMap = {}
, fetchFile
, fs;
function isBrowser () {return typeof window !== 'undefined' && window.navigator && window.document;}
function isNode () {return typeof process !== 'undefined' && process.versions && !!process.versions.node;}
function isRequireBuildProcess () {return isNode() && require.nodeRequire;}
if (isBrowser()) {
fetchFile = function (url, callback) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {callback(xhr.responseText);} // Do not explicitly handle errors, those should be visible via console output in the browser.
};
xhr.send(null);
};
} else if (isRequireBuildProcess()) {
fs = require.nodeRequire('fs'); // nodeRequire is a method added by r.js
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
} else if (isNode()) {
fs = require('fs');
fetchFile = function (path, callback) {
callback(fs.readFileSync(path, 'utf8'));
};
}
return {
load: function (name, req, onload, config) {
var url = req.toUrl(name + '.js');
fetchFile(url, function (responseText) {
var code = Babel.transform(responseText, {
presets: ['es2015', 'react']
, filename: 'embedded'
, sourceMaps: 'inline'
}).code;
if (config.isBuild) {buildMap[name] = code;}
if (isNode() && !config.isBuild) {
var fileName = name + '.tmp';
fs.writeFileSync(fileName, code);
onload(req(fileName));
fs.unlink(fileName);
} else {
onload.fromText(code);
}
});
}
, write: function (pluginName, moduleName, write) {
if (moduleName in buildMap) {write.asModule(pluginName + '!' + moduleName, buildMap[moduleName]);}
}
};
});
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script src="js/lib/requirejs/require.js"></script>
<script src="js/config/require-config.js"></script>
</head>
<body>
<div id="root"></div>
<script src="js/index.js"></script>
</body>
</html>
Файл require-config.js
requirejs.config({
config: {
loader: {
resolveModuleSource: function (source) {return 'loader!' + source;}
}
}
, paths: {
loader: 'js/lib/loader/loader'
, babel: 'js/lib/babel-standalone/babel'
, react: 'js/lib/react/react'
, react_dom: 'js/lib/react/react-dom'
, 'main': 'js/app/main/main'
, 'class': 'js/app/class/class'
, 'sum': 'js/app/sum/sum'
}
});
Файл index.js
require(['loader!main']);
Файл main.js
define(function (require) {
require('loader!class');
var React = require('react')
, ReactDOM = require('react_dom');
ReactDOM.render(<span>Hello World 123</span>, document.getElementById('root'));
});
Файл class.js
define(function (require) {
var sum = require('loader!sum');
console.log(sum(1, 2));
class A {
constructor (a) {
console.log('Hello ' + a);
}
}
new A('world!');
});
Файл sum.js
define(function (require) {
return (a, b) => a + b;
});
return (a, b) => a + b;
});
RequireJS Plugin - Полное описание устройства
/*
* RequireJS loader plugin
*/
require(['./path/to/plugin-name!path/to/file'], function (something) {
// something это ссылка на файл ресурс 'path/to/file', который был загружен с помощью плагина './path/to/plugin-name.js'
});
;(function () {
'use strict';
define(['./path/to/babel-standalone/babel'], function (Babel) {
return {
// Загрузить и выполнить содержимое модуля
load: function (name, req, onload, config) { // функция для загрузки содержимого файла 'path/to/file' через данный плагин (единственная обязательная функция)
// name - строка с именем файла ресурса, которое располагается после знака ! в строке с путем до файла
// Для пути './path/to/plugin-name!path/to/file': name = 'path/to/file'
// req() - локальная ссылка на функцию "require", которую можно использовать здесь для загрузки любых других модулей.
// Пути до этих модулей являются относительными путями от пути модуля, загружаемого через данный плагин.
// Функция req содержит в себе ряд дополнительных утилит:
// req.toUrl(modulePath) - функция, возращает полный путь до загружаемого стороннего модуля с учетов общей конфигурации config для RequireJS
// moduleResource - представляет собой имя или путь до модуля вместе с его расширением, например 'view/templates/main.html'
// req.defined(moduleName) - функция, которая использовалась ранее в RequireJS в версии до 0.25.0. Возвращает true, если
// конкретный модуль уже загружен и учтен.
// req.specified(moduleName) - функция возвращает true, если уже произведен запрос на загрузку модуля или он уже находится в процессе
// загрузки и должен быть доступен в некоторой точке выполнения программы.
// onload() - функция сообщает загрузчику о том, что плагин завершил загрузку модуля.
// Данная функция, вызываемая со значением value модуля name.
// onload.error() - функция может быть вызвана, если плагин обнаруживает ошибку, которая не позволяет загрузить модуль правильно.
// В данную функцию передается объект error.
// config - конфигурационный объект, в котором содержится информация о конфигурации данного плагина.
// Плагин i18n! использует объект config для получения текущей локали языка, если приложение хочет принудительно установить другую локаль языка.
// Сборщик r.js установит свойство isBuild в config со значением true, если плагин или pluginBuilder будет вызван, в составе сборщика r.js
// Плагин должен вызывать функцию onload() сразу, как только значение config.isBuild будет равно true.
if (config.isBuild) {
// Обозначает, что сборщик r.js не должен ждать и может начать сборку. Данный ресурс будет получен динамически в процессе работы браузера.
onload();
} else {
// Сделать что-то еще, что может быть асинхронным.
}
// Пример плагина работающего также, как и обычный require()
req([name], function (value) {
onload(value);
});
// Иногда плагином загружается JavaScript-код в виде текстовой строки, которую надо дополнительно выполнить.
// Для этого существует функция onload.fromText(), которая может выполнить загруженный JavaScript-код посредством функции eval()
// RequireJS выполнит правильно работу по вызову любой анонимной функции define(), обнаруженной в текстовой строке, после чего
// используется полученный модуль.
// Функция onload.fromText(text) принимает аргумент text, который представляет из себя строку с JavaScript-кодом, который надо выполнить.
// Пример работы плагина с обработкой строки с JavaScript-кодом:
var url = req.toUrl(name + '.txt');
fetchText(url, function (text) {
text = transform(text);
onload.fromText(text);
});
// До RequireJS 2.1.0 функция onload.fromText(moduleName, text) принимала в качестве первого аругмента путь до модуля, а сам плагин
// должен был вручную вызывать функцию require([moduleName], onload) после вызова функции onload.fromText()
}
// Нормализовать путь до модуля
, normalize: function (name, normalize) { // функция для нормализации имени загружаемого файла 'path/to/file' (необходима только, если имя файла не является именем модуля)
// Некоторые модули имеют относительные пути, поэтому они должны быть преобразованы в полные пути.
// name - путь до модуля, который должен быть нормализован.
// normalize() - функция, которая может быть вызвана для нормализации пути до модуля.
// Данную функцию не нужно создавать, если вы используете стандартные обычные до модулей.
// Например, плагин text! не имеет в себе функцию normalize().
// Пример кода функции normalize()
// define(['index!2?./a:./b:./c'], function (indexResource) {
// // indexResource will be the module that corresponds to './c'.
// });
function parse(name) {
var parts = name.split('?')
, index = parseInt(parts[0], 10)
, choices = parts[1].split(':')
, choice = choices[index];
return {
index: index
, choices: choices
, choice: choice
};
}
var parsed = parse(name)
, choices = parsed.choices;
for (i = 0; i < choices.length; i++) {
choices[i] = normalize(choices[i]);
}
return parsed.index + '?' + choices.join(':');
}
// Записать содержимое модуля в итоговый файл сборки
, write: function (pluginName, moduleName, write) { // функция используется сборщиком r.js для обозначения того, когда плагин должен начать работу для преобразований содержимого файла 'path/to/file' перед записью в итоговый файл сборки
// Функция write нужна только, если плагин будет выводить что-то для сборщика.
// pluginName - строка с номализованным путем до плагина.
// Большинство плагинов не будут иметь имени, то есть будут анонимными плагинами, поэтому полезно знать нормализорванный путь до плагина в
// файле сборщике.
// moduleName - строка с номализованным путем до модуля.
// write() - функция, которая вызывается для передачи строки для записи в итоговый файл сборки.
// write.asModule(moduleName, text) - функция, которая используется для записи модуля имеющего вызов анонимной define(), требующей втавку имени
// и содержащего неявный вызов зависимеостей через require(""), которые должны быть помещены в итоговый файл сборки.
// Функцию write.asModule() удобно использовать для плагинов, трансформирующих текст, таких как плагин для CoffeeScript.
// Плагин text! использует функцию write для записи строки с текстом в итоговый файл сборки.
// Пример кода функции write()
if (moduleName in buildMap) {
var text = jsEscape(buildMap[moduleName]);
write('define("' + pluginName + '!' + moduleName + '", function () { return "' + text + '";});\n');
}
}
, pluginBuilder: '' // строка с именем модуля, который должен быть использован сборщиком r.js для преобразований содержимого файла 'path/to/file' перед записью в итоговый файл сборки
// Используется, если билд плагин содержит серьезную логику работы для Node.js, которую не имеет смысла хранить в плагине, который используется в браузере.
// Не используйте именные модули в составе плагина или pluginBuilder.
// Текстовое содержимое pluginBuilder используется вместо содержимого основного файла плагина, но это работает только, если файлы не вызывают модули через define() по именам.
//////////////////////////////////////////////////////////////////////////////////////////
, onLayerEnd: function (write, data) { // функция используемая только сборщиком r.js в версии 2.1.0 и позже.
// Функция вызывается после сборки всех модулей данного типа, например чтобы сбросить внутреннее состояние.
// Например плагину требуется записать некоторые утилитные функции после сборки.
// write() - функция, вызываемая со строкой вывода для записи в оптимизационный слой.
// data - объект, хранящий в себе информацию о слое. Имеет 2 свойства:
// name - имя модуля слоя. Может быть undefined.
// path - путь до файла слоя. Может быть undefined, если вывод является строкой, содержащей другой script.
}
, writeFile: function (pluginName, name, parentRequire, write) { // функция, которая используется только сборщиком r.js, нужная только, если
// плагину надо записать альтернативную версию зависимости, используемую плагином
// pluginName - строка с нормализованным именем плагина
// name - строка с номанлизованным именем модуля
// parentRequire() - локальная функция require(). В основном используется для вызова parentRequire.toUrl() для создания путей до файлов,
// использующихся внутри директории сборки.
// write(fileName, text) - функция, вызываемая с двумя аргументами:
// fileName - строка с именем файла для записи. Вы можете использовать функцию parentRequire.toUrl() для относительных путей для генерации
// внутри директории сборки.
// text - строка с содержимым файла в кодировке UTF-8.
// write.asModule(moduleName, fileName, text) - функция
// asModule - может использоваться для записи и вставки анонимных вызовов функции define() в итоговый файл сборки.
// Пример использовани яфункции writeFile() находится в плагиен text!
}
};
});
})();
* RequireJS loader plugin
*/
require(['./path/to/plugin-name!path/to/file'], function (something) {
// something это ссылка на файл ресурс 'path/to/file', который был загружен с помощью плагина './path/to/plugin-name.js'
});
;(function () {
'use strict';
define(['./path/to/babel-standalone/babel'], function (Babel) {
return {
// Загрузить и выполнить содержимое модуля
load: function (name, req, onload, config) { // функция для загрузки содержимого файла 'path/to/file' через данный плагин (единственная обязательная функция)
// name - строка с именем файла ресурса, которое располагается после знака ! в строке с путем до файла
// Для пути './path/to/plugin-name!path/to/file': name = 'path/to/file'
// req() - локальная ссылка на функцию "require", которую можно использовать здесь для загрузки любых других модулей.
// Пути до этих модулей являются относительными путями от пути модуля, загружаемого через данный плагин.
// Функция req содержит в себе ряд дополнительных утилит:
// req.toUrl(modulePath) - функция, возращает полный путь до загружаемого стороннего модуля с учетов общей конфигурации config для RequireJS
// moduleResource - представляет собой имя или путь до модуля вместе с его расширением, например 'view/templates/main.html'
// req.defined(moduleName) - функция, которая использовалась ранее в RequireJS в версии до 0.25.0. Возвращает true, если
// конкретный модуль уже загружен и учтен.
// req.specified(moduleName) - функция возвращает true, если уже произведен запрос на загрузку модуля или он уже находится в процессе
// загрузки и должен быть доступен в некоторой точке выполнения программы.
// onload() - функция сообщает загрузчику о том, что плагин завершил загрузку модуля.
// Данная функция, вызываемая со значением value модуля name.
// onload.error() - функция может быть вызвана, если плагин обнаруживает ошибку, которая не позволяет загрузить модуль правильно.
// В данную функцию передается объект error.
// config - конфигурационный объект, в котором содержится информация о конфигурации данного плагина.
// Плагин i18n! использует объект config для получения текущей локали языка, если приложение хочет принудительно установить другую локаль языка.
// Сборщик r.js установит свойство isBuild в config со значением true, если плагин или pluginBuilder будет вызван, в составе сборщика r.js
// Плагин должен вызывать функцию onload() сразу, как только значение config.isBuild будет равно true.
if (config.isBuild) {
// Обозначает, что сборщик r.js не должен ждать и может начать сборку. Данный ресурс будет получен динамически в процессе работы браузера.
onload();
} else {
// Сделать что-то еще, что может быть асинхронным.
}
// Пример плагина работающего также, как и обычный require()
req([name], function (value) {
onload(value);
});
// Иногда плагином загружается JavaScript-код в виде текстовой строки, которую надо дополнительно выполнить.
// Для этого существует функция onload.fromText(), которая может выполнить загруженный JavaScript-код посредством функции eval()
// RequireJS выполнит правильно работу по вызову любой анонимной функции define(), обнаруженной в текстовой строке, после чего
// используется полученный модуль.
// Функция onload.fromText(text) принимает аргумент text, который представляет из себя строку с JavaScript-кодом, который надо выполнить.
// Пример работы плагина с обработкой строки с JavaScript-кодом:
var url = req.toUrl(name + '.txt');
fetchText(url, function (text) {
text = transform(text);
onload.fromText(text);
});
// До RequireJS 2.1.0 функция onload.fromText(moduleName, text) принимала в качестве первого аругмента путь до модуля, а сам плагин
// должен был вручную вызывать функцию require([moduleName], onload) после вызова функции onload.fromText()
}
// Нормализовать путь до модуля
, normalize: function (name, normalize) { // функция для нормализации имени загружаемого файла 'path/to/file' (необходима только, если имя файла не является именем модуля)
// Некоторые модули имеют относительные пути, поэтому они должны быть преобразованы в полные пути.
// name - путь до модуля, который должен быть нормализован.
// normalize() - функция, которая может быть вызвана для нормализации пути до модуля.
// Данную функцию не нужно создавать, если вы используете стандартные обычные до модулей.
// Например, плагин text! не имеет в себе функцию normalize().
// Пример кода функции normalize()
// define(['index!2?./a:./b:./c'], function (indexResource) {
// // indexResource will be the module that corresponds to './c'.
// });
function parse(name) {
var parts = name.split('?')
, index = parseInt(parts[0], 10)
, choices = parts[1].split(':')
, choice = choices[index];
return {
index: index
, choices: choices
, choice: choice
};
}
var parsed = parse(name)
, choices = parsed.choices;
for (i = 0; i < choices.length; i++) {
choices[i] = normalize(choices[i]);
}
return parsed.index + '?' + choices.join(':');
}
// Записать содержимое модуля в итоговый файл сборки
, write: function (pluginName, moduleName, write) { // функция используется сборщиком r.js для обозначения того, когда плагин должен начать работу для преобразований содержимого файла 'path/to/file' перед записью в итоговый файл сборки
// Функция write нужна только, если плагин будет выводить что-то для сборщика.
// pluginName - строка с номализованным путем до плагина.
// Большинство плагинов не будут иметь имени, то есть будут анонимными плагинами, поэтому полезно знать нормализорванный путь до плагина в
// файле сборщике.
// moduleName - строка с номализованным путем до модуля.
// write() - функция, которая вызывается для передачи строки для записи в итоговый файл сборки.
// write.asModule(moduleName, text) - функция, которая используется для записи модуля имеющего вызов анонимной define(), требующей втавку имени
// и содержащего неявный вызов зависимеостей через require(""), которые должны быть помещены в итоговый файл сборки.
// Функцию write.asModule() удобно использовать для плагинов, трансформирующих текст, таких как плагин для CoffeeScript.
// Плагин text! использует функцию write для записи строки с текстом в итоговый файл сборки.
// Пример кода функции write()
if (moduleName in buildMap) {
var text = jsEscape(buildMap[moduleName]);
write('define("' + pluginName + '!' + moduleName + '", function () { return "' + text + '";});\n');
}
}
, pluginBuilder: '' // строка с именем модуля, который должен быть использован сборщиком r.js для преобразований содержимого файла 'path/to/file' перед записью в итоговый файл сборки
// Используется, если билд плагин содержит серьезную логику работы для Node.js, которую не имеет смысла хранить в плагине, который используется в браузере.
// Не используйте именные модули в составе плагина или pluginBuilder.
// Текстовое содержимое pluginBuilder используется вместо содержимого основного файла плагина, но это работает только, если файлы не вызывают модули через define() по именам.
//////////////////////////////////////////////////////////////////////////////////////////
, onLayerEnd: function (write, data) { // функция используемая только сборщиком r.js в версии 2.1.0 и позже.
// Функция вызывается после сборки всех модулей данного типа, например чтобы сбросить внутреннее состояние.
// Например плагину требуется записать некоторые утилитные функции после сборки.
// write() - функция, вызываемая со строкой вывода для записи в оптимизационный слой.
// data - объект, хранящий в себе информацию о слое. Имеет 2 свойства:
// name - имя модуля слоя. Может быть undefined.
// path - путь до файла слоя. Может быть undefined, если вывод является строкой, содержащей другой script.
}
, writeFile: function (pluginName, name, parentRequire, write) { // функция, которая используется только сборщиком r.js, нужная только, если
// плагину надо записать альтернативную версию зависимости, используемую плагином
// pluginName - строка с нормализованным именем плагина
// name - строка с номанлизованным именем модуля
// parentRequire() - локальная функция require(). В основном используется для вызова parentRequire.toUrl() для создания путей до файлов,
// использующихся внутри директории сборки.
// write(fileName, text) - функция, вызываемая с двумя аргументами:
// fileName - строка с именем файла для записи. Вы можете использовать функцию parentRequire.toUrl() для относительных путей для генерации
// внутри директории сборки.
// text - строка с содержимым файла в кодировке UTF-8.
// write.asModule(moduleName, fileName, text) - функция
// asModule - может использоваться для записи и вставки анонимных вызовов функции define() в итоговый файл сборки.
// Пример использовани яфункции writeFile() находится в плагиен text!
}
};
});
})();
вторник, 14 марта 2017 г.
JavaScript-шаблонизатор Template аналог EJS. Версия 3 с выводом ошибок в консоль.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TPL Engine</title>
</head>
<body><div id="output"></div>
<script type="text/javascript">
// Преобразование содержимого объекта в текст для вставки в HTML-код.
// Параметры:
// obj - конвертируемый объект.
// Пример использовния :
// var obj = {
// a: 1
// , b: null
// , c: undefined
// , d: 'text<br/>text'
// , e: [1,2,3]
// , f: {objkey: 'objvalue'}
// , g: function () {console.log(1);}
// , h: new Date()
// , i: true
// , j: new RegExp('a{1,2}')
// , k: new Error('some error')
// , l: NaN
// , m: []
// , n: {}
// };
// convertObjectToHtmlText(obj);
// Возвращает отформатированную строку, представляющую содержимое объекта, пригодную для вставки в HTML-код.
// Возвращает сообщение об ошибке, если в функцию не был передан объект.
// Преобразовать содержимое объекта в текст для вставки в HTML-код
function convertObjectToHtmlText (obj, options) {
var indentString = ' '
, newLine = '<br />'
, newLineJoin = ',' + newLine;
if (options && options.rawHTML) {
indentString = ' '; // 4 пробела
newLine = '\n';
newLineJoin = ',' + newLine;
}
// Функция определения типа объекта
function objectType (obj) {
var types = {
'null': 'null'
, 'undefined': 'undefined'
, 'number': 'number'
, 'boolean': 'boolean'
, 'string': 'string'
, '[object Function]': 'function'
, '[object RegExp]': 'regexp'
, '[object Array]': 'array'
, '[object Date]': 'date'
, '[object Error]': 'error'
};
return types[typeof obj] || types[Object.prototype.toString.call(obj)] || (obj ? 'object' : 'null');
}
// Функция определения числа элементов в объекте
function objectSize (obj) {
var size = 0
, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {size++;}
}
return size;
}
// Привести элемент к нужному формату
function formatElement (element, indent, indentFromArray) {
indentFromArray = indentFromArray || '';
switch (objectType(element)) {
case 'null': return indentFromArray + 'null';
case 'undefined': return indentFromArray + 'undefined';
case 'number': return indentFromArray + element;
case 'boolean': return indentFromArray + (element ? 'true' : 'false');
case 'string': if (options && options.rawHTML) {
return indentFromArray + '"'
+ element
+ '"';
} else {
return indentFromArray + '"'
+ element.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/, '"')
.replace(/'/g, ''')
+ '"';
}
case 'array': return indentFromArray + (element.length > 0 ? '[' + newLine + formatArray(element, indent) + indent + ']' : '[]');
case 'object': return indentFromArray + (objectSize(element) > 0 ? '{' + newLine + formatObject(element, indent) + indent + '}' : '{}');
default: return indentFromArray + element.toString();
}
}
// Привести массив к нужному формату
function formatArray (array, indent) {
var index
, length = array.length
, value = [];
indent += indentString;
for (index = 0; index < length; index += 1) {
value.push(formatElement(array[index], indent, indent));
}
return value.join(newLineJoin) + newLine;
}
// Привести объект к нужному формату
function formatObject (object, indent) {
var value = []
, property;
indent += indentString;
for (property in object) {
if (object.hasOwnProperty(property)) {
if (options && options.rawHTML) {
value.push(indent + '"' + property + '": ' + formatElement(object[property], indent));
} else {
value.push(indent + '"' + property + '": ' + formatElement(object[property], indent));
}
}
}
return value.join(newLineJoin) + newLine;
}
if (typeof obj === 'object') {return formatElement(obj, '');
} else {throw new Error ('No javascript object has been provided to function convertObjectToHtmlText (obj) {...}');
}
}
Function.prototype.trace = function () {
var trace = []
, current = this;
while (current) {
trace.push(current.signature());
current = current.caller;
}
return trace;
};
Function.prototype.signature = function () {
var signature = {
name: this.getName()
, params: []
, toString: function () {
var params = ''
, i = 0
, len = this.params.length
, last = len - 1;
for (; i < len; i++) {
if (typeof this.params[i] === 'object') {
params += convertObjectToHtmlText(this.params[i], {rawHTML: true});
} else if (typeof this.params[i] === 'string') {
params += '"' + this.params[i] + '"';
} else {
params += this.params[i];
}
if (len !== 1 && last !== i) {
params += ', ';
}
}
return this.name + '(' + params + ')';
}
};
if (this.arguments) {
for (var i = 0, len = this.arguments.length; i < len; i++) {
signature.params.push(this.arguments[i]);
}
}
return signature;
};
Function.prototype.getName = function () {
if (this.name) {
return this.name;
}
var definition = this.toString().split('\n')[0]
, exp = /^function ([^\s(]+).+/;
if (exp.test(definition)) {
return definition.split('\n')[0].replace(exp, '$1') || 'anonymous';
}
return 'anonymous';
};
function getTypeOf (value) {
if (value === window) {return 'window';}
if (value === null) {return 'null';}
if (value === undefined) {return 'undefined';}
if (value !== value) {return 'nan';}
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
function error (message, value) {
var trace = arguments.callee.trace()
, formattedTrace = ''
, i = 0
, len = trace.length
, last = len - 1;
for (; i < len; i++) {
formattedTrace = trace[i] + ';';
if (len !== 1 && last !== i) {
formattedTrace += '\n';
}
}
if (arguments.length === 2) {
if (typeof value === 'object') {
value = convertObjectToHtmlText(value, {rawHTML: true});
}
if (typeof value === 'string' && value.length === 0) {
value = '""';
}
message += ' ' + value + '\n\nStack trace\n\n' + formattedTrace;
}
if (console && console.error) {
console.error(message);
} else {
throw new Error(message);
}
}
;(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof module === "object" && typeof module.exports === "object") {
module.exports = factory();
} else {
global.TPL = factory();
}
})(this, function () {
// Usage:
// console.log( new TPL.Template("<div>Total: {{= value }}<div>", {delimiters: ["{{", "}}"]}).render({value: "<span>15</span>"}) );
// console.log( new TPL.Template("<div>Total: {{- block('<div>{{= value }}</div>', {delimiters: ['{{', '}}']}, {value: value}) }}<div>", {delimiters: ["{{", "}}"]}).render({block: TPL.block, value: "<span>15</span>"}) );
// Options:
// delimiters - characters to use for open and close delimiters
// rmWhitespace - remove whitespace between HTML-tags
// Tags:
// <% - scriptlet tag for control-flow, no output
// <%% - outputs raw template part
// <%- - outputs the raw unescaped value into the template
// <%= - outputs the HTML escaped value into the template
// <%# - comment tag, no execution, no output
// %> - plain ending tag
// Block:
// "<div><%- block('<div><%= value %></div>', {delimiters: ['<%', '%>']}, {value: outerValue}) %></div>"
function Template (templateString, settingsObject) {
var dummy = {render: function () {return '';}};
if (typeof templateString !== 'string') {error('Template must be a string, but it is: ', templateString); return dummy;}
if (arguments.length === 2) {
if (getTypeOf(settingsObject) !== 'object') {error('Settings must be an object, but it is: ', settingsObject); return dummy;}
if (!('delimiters' in settingsObject || 'rmWhitespace' in settingsObject)) {error('Settings must contains property "delimiters" or property "rmWhitespace", but it is: ', settingsObject); return dummy;}
if ('delimiters' in settingsObject) {
if (getTypeOf(settingsObject.delimiters) !== 'array') {error('Property "delimiters" must be an array, but it is: ', settingsObject.delimiters); return dummy;}
if (settingsObject.delimiters.length !== 2 || typeof settingsObject.delimiters[0] !== 'string' || typeof settingsObject.delimiters[1] !== 'string') {
error('Property "delimiters" must be an array with 2 strings, but it is: ', settingsObject.delimiters); return dummy;
}
}
if ('rmWhitespace' in settingsObject && getTypeOf(settingsObject.rmWhitespace) !== 'boolean') {error('Property "rmWhitespace" must be a boolean, but it is: ', settingsObject.rmWhitespace); return dummy;}
}
var delimiters = ["<%", "%>"]
, rmWhitespace = false; // removeWhitespace
if (settingsObject) {
if ('delimiters' in settingsObject) {delimiters = settingsObject.delimiters;}
if ('rmWhitespace' in settingsObject) {rmWhitespace = settingsObject.rmWhitespace;}
}
var renderFunction = createRenderFunction(templateString, delimiters);
return {render: function (dataObject) {
var renderedHTML = renderFunction(dataObject, escapeHtmlFunction);
if (rmWhitespace === true) {
renderedHTML = removeSpaces(renderedHTML);
}
return renderedHTML;
}};
}
function removeSpaces (renderedHTML) {
return renderedHTML.replace(/\r/g, "") // Remove carriage return symbols
.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") // Trim space: \s, BOM: \uFEFF, NBSP: \xA0
.replace(/\s+(<[^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)])/g, "$1") // Remove spaces between start tag and content
.replace(/([^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)]>)\s+/g, "$1") // Remove spaces between end tag and content
.replace(/\s+/g, " "); // Remove spaces inside content
}
function createMatchRegExp (delimiters) { // delimiters = ["<%", "%>"]
var openDelimiter = delimiters[0] // <%
, closeDelimiter = delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // any string
, tags = {
rawTemplatePart: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% "<div><%= value %></div>" %> --> "<div><%= value %></div>"
, rawHtmlUnescapedValue: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%- "1<br />2" %> --> "1<br />2"
, htmlEscapedValue: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%= "1<br />2" %> --> "1<br />2"
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# any comment %> --> ""
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% if (value === 1) { %>one<% } %> --> "one"
}
, matchRegExp = new RegExp([
tags.rawTemplatePart // Important!!! Must be first!
, tags.rawHtmlUnescapedValue
, tags.htmlEscapedValue
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function createRenderFunction (templateString, delimiters) {
var matchRegExp = createMatchRegExp(delimiters)
, renderFunction;
try {
renderFunction = new Function ("_dataObject_", "_escapeHtmlFunction_" // ) {
, "if (!_dataObject_) {_dataObject_ = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (_dataObject_) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (
match
, rawTemplatePartCodeString
, rawUnescapedValueCodeString
, htmlEscapedValueCodeString
, commentCodeString
, evaluateCodeString
, offset
) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (rawTemplatePartCodeString) {
resultString += delimiters[0] + escapeString(rawTemplatePartCodeString) + delimiters[1];
} else if (rawUnescapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function _rawUnescapedValue_ () {" + "\n"
+ "var _value_ = " + rawUnescapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (htmlEscapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function _htmlEscapedValue_ () {" + "\n"
+ "var _value_ = " + htmlEscapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString) {
// Do nothing
} else if (evaluateCodeString) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (err) {
error(err.message += ' while compiling template:\n', templateString);
return function () {return '';};
}
return renderFunction;
}
function escapeString (string) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {return escapeCharacters[match];});
}
function escapeHtmlFunction (html) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return String(html).replace(/(?:&|<|>|"|'|`)/g, function (match) {return escapeCharacters[match];});
}
function block (templatePartString, settingsObject, dataObject) {
return new Template(templatePartString, settingsObject).render(dataObject);
}
return {
Template: Template
, block: block
};
});
var template = '<div><p>Total: <%- value %></p><% var a = 2; while (a--) { %><%- block(innerTemplate, {delimiters: ["{{", "}}"]}, {innerValue: innerValue}) %><% } %></div>'
, innerTemplate = '<p>Last: {{- innerValue }}</p>';
var result = new TPL.Template(template).render({value: '<span>15</span>', block: TPL.block, innerTemplate: innerTemplate, innerValue: '<span>20</span>'});
var fragment = document.createDocumentFragment()
, element = document.createElement('div');
element.innerHTML = result;
fragment.appendChild(element);
document.getElementsByTagName('body')[0].appendChild(fragment);
</script>
</body>
</html>
<html>
<head>
<meta charset="utf-8" />
<title>TPL Engine</title>
</head>
<body><div id="output"></div>
<script type="text/javascript">
// Преобразование содержимого объекта в текст для вставки в HTML-код.
// Параметры:
// obj - конвертируемый объект.
// Пример использовния :
// var obj = {
// a: 1
// , b: null
// , c: undefined
// , d: 'text<br/>text'
// , e: [1,2,3]
// , f: {objkey: 'objvalue'}
// , g: function () {console.log(1);}
// , h: new Date()
// , i: true
// , j: new RegExp('a{1,2}')
// , k: new Error('some error')
// , l: NaN
// , m: []
// , n: {}
// };
// convertObjectToHtmlText(obj);
// Возвращает отформатированную строку, представляющую содержимое объекта, пригодную для вставки в HTML-код.
// Возвращает сообщение об ошибке, если в функцию не был передан объект.
// Преобразовать содержимое объекта в текст для вставки в HTML-код
function convertObjectToHtmlText (obj, options) {
var indentString = ' '
, newLine = '<br />'
, newLineJoin = ',' + newLine;
if (options && options.rawHTML) {
indentString = ' '; // 4 пробела
newLine = '\n';
newLineJoin = ',' + newLine;
}
// Функция определения типа объекта
function objectType (obj) {
var types = {
'null': 'null'
, 'undefined': 'undefined'
, 'number': 'number'
, 'boolean': 'boolean'
, 'string': 'string'
, '[object Function]': 'function'
, '[object RegExp]': 'regexp'
, '[object Array]': 'array'
, '[object Date]': 'date'
, '[object Error]': 'error'
};
return types[typeof obj] || types[Object.prototype.toString.call(obj)] || (obj ? 'object' : 'null');
}
// Функция определения числа элементов в объекте
function objectSize (obj) {
var size = 0
, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {size++;}
}
return size;
}
// Привести элемент к нужному формату
function formatElement (element, indent, indentFromArray) {
indentFromArray = indentFromArray || '';
switch (objectType(element)) {
case 'null': return indentFromArray + 'null';
case 'undefined': return indentFromArray + 'undefined';
case 'number': return indentFromArray + element;
case 'boolean': return indentFromArray + (element ? 'true' : 'false');
case 'string': if (options && options.rawHTML) {
return indentFromArray + '"'
+ element
+ '"';
} else {
return indentFromArray + '"'
+ element.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/, '"')
.replace(/'/g, ''')
+ '"';
}
case 'array': return indentFromArray + (element.length > 0 ? '[' + newLine + formatArray(element, indent) + indent + ']' : '[]');
case 'object': return indentFromArray + (objectSize(element) > 0 ? '{' + newLine + formatObject(element, indent) + indent + '}' : '{}');
default: return indentFromArray + element.toString();
}
}
// Привести массив к нужному формату
function formatArray (array, indent) {
var index
, length = array.length
, value = [];
indent += indentString;
for (index = 0; index < length; index += 1) {
value.push(formatElement(array[index], indent, indent));
}
return value.join(newLineJoin) + newLine;
}
// Привести объект к нужному формату
function formatObject (object, indent) {
var value = []
, property;
indent += indentString;
for (property in object) {
if (object.hasOwnProperty(property)) {
if (options && options.rawHTML) {
value.push(indent + '"' + property + '": ' + formatElement(object[property], indent));
} else {
value.push(indent + '"' + property + '": ' + formatElement(object[property], indent));
}
}
}
return value.join(newLineJoin) + newLine;
}
if (typeof obj === 'object') {return formatElement(obj, '');
} else {throw new Error ('No javascript object has been provided to function convertObjectToHtmlText (obj) {...}');
}
}
Function.prototype.trace = function () {
var trace = []
, current = this;
while (current) {
trace.push(current.signature());
current = current.caller;
}
return trace;
};
Function.prototype.signature = function () {
var signature = {
name: this.getName()
, params: []
, toString: function () {
var params = ''
, i = 0
, len = this.params.length
, last = len - 1;
for (; i < len; i++) {
if (typeof this.params[i] === 'object') {
params += convertObjectToHtmlText(this.params[i], {rawHTML: true});
} else if (typeof this.params[i] === 'string') {
params += '"' + this.params[i] + '"';
} else {
params += this.params[i];
}
if (len !== 1 && last !== i) {
params += ', ';
}
}
return this.name + '(' + params + ')';
}
};
if (this.arguments) {
for (var i = 0, len = this.arguments.length; i < len; i++) {
signature.params.push(this.arguments[i]);
}
}
return signature;
};
Function.prototype.getName = function () {
if (this.name) {
return this.name;
}
var definition = this.toString().split('\n')[0]
, exp = /^function ([^\s(]+).+/;
if (exp.test(definition)) {
return definition.split('\n')[0].replace(exp, '$1') || 'anonymous';
}
return 'anonymous';
};
function getTypeOf (value) {
if (value === window) {return 'window';}
if (value === null) {return 'null';}
if (value === undefined) {return 'undefined';}
if (value !== value) {return 'nan';}
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
function error (message, value) {
var trace = arguments.callee.trace()
, formattedTrace = ''
, i = 0
, len = trace.length
, last = len - 1;
for (; i < len; i++) {
formattedTrace = trace[i] + ';';
if (len !== 1 && last !== i) {
formattedTrace += '\n';
}
}
if (arguments.length === 2) {
if (typeof value === 'object') {
value = convertObjectToHtmlText(value, {rawHTML: true});
}
if (typeof value === 'string' && value.length === 0) {
value = '""';
}
message += ' ' + value + '\n\nStack trace\n\n' + formattedTrace;
}
if (console && console.error) {
console.error(message);
} else {
throw new Error(message);
}
}
;(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof module === "object" && typeof module.exports === "object") {
module.exports = factory();
} else {
global.TPL = factory();
}
})(this, function () {
// Usage:
// console.log( new TPL.Template("<div>Total: {{= value }}<div>", {delimiters: ["{{", "}}"]}).render({value: "<span>15</span>"}) );
// console.log( new TPL.Template("<div>Total: {{- block('<div>{{= value }}</div>', {delimiters: ['{{', '}}']}, {value: value}) }}<div>", {delimiters: ["{{", "}}"]}).render({block: TPL.block, value: "<span>15</span>"}) );
// Options:
// delimiters - characters to use for open and close delimiters
// rmWhitespace - remove whitespace between HTML-tags
// Tags:
// <% - scriptlet tag for control-flow, no output
// <%% - outputs raw template part
// <%- - outputs the raw unescaped value into the template
// <%= - outputs the HTML escaped value into the template
// <%# - comment tag, no execution, no output
// %> - plain ending tag
// Block:
// "<div><%- block('<div><%= value %></div>', {delimiters: ['<%', '%>']}, {value: outerValue}) %></div>"
function Template (templateString, settingsObject) {
var dummy = {render: function () {return '';}};
if (typeof templateString !== 'string') {error('Template must be a string, but it is: ', templateString); return dummy;}
if (arguments.length === 2) {
if (getTypeOf(settingsObject) !== 'object') {error('Settings must be an object, but it is: ', settingsObject); return dummy;}
if (!('delimiters' in settingsObject || 'rmWhitespace' in settingsObject)) {error('Settings must contains property "delimiters" or property "rmWhitespace", but it is: ', settingsObject); return dummy;}
if ('delimiters' in settingsObject) {
if (getTypeOf(settingsObject.delimiters) !== 'array') {error('Property "delimiters" must be an array, but it is: ', settingsObject.delimiters); return dummy;}
if (settingsObject.delimiters.length !== 2 || typeof settingsObject.delimiters[0] !== 'string' || typeof settingsObject.delimiters[1] !== 'string') {
error('Property "delimiters" must be an array with 2 strings, but it is: ', settingsObject.delimiters); return dummy;
}
}
if ('rmWhitespace' in settingsObject && getTypeOf(settingsObject.rmWhitespace) !== 'boolean') {error('Property "rmWhitespace" must be a boolean, but it is: ', settingsObject.rmWhitespace); return dummy;}
}
var delimiters = ["<%", "%>"]
, rmWhitespace = false; // removeWhitespace
if (settingsObject) {
if ('delimiters' in settingsObject) {delimiters = settingsObject.delimiters;}
if ('rmWhitespace' in settingsObject) {rmWhitespace = settingsObject.rmWhitespace;}
}
var renderFunction = createRenderFunction(templateString, delimiters);
return {render: function (dataObject) {
var renderedHTML = renderFunction(dataObject, escapeHtmlFunction);
if (rmWhitespace === true) {
renderedHTML = removeSpaces(renderedHTML);
}
return renderedHTML;
}};
}
function removeSpaces (renderedHTML) {
return renderedHTML.replace(/\r/g, "") // Remove carriage return symbols
.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") // Trim space: \s, BOM: \uFEFF, NBSP: \xA0
.replace(/\s+(<[^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)])/g, "$1") // Remove spaces between start tag and content
.replace(/([^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)]>)\s+/g, "$1") // Remove spaces between end tag and content
.replace(/\s+/g, " "); // Remove spaces inside content
}
function createMatchRegExp (delimiters) { // delimiters = ["<%", "%>"]
var openDelimiter = delimiters[0] // <%
, closeDelimiter = delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // any string
, tags = {
rawTemplatePart: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% "<div><%= value %></div>" %> --> "<div><%= value %></div>"
, rawHtmlUnescapedValue: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%- "1<br />2" %> --> "1<br />2"
, htmlEscapedValue: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%= "1<br />2" %> --> "1<br />2"
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# any comment %> --> ""
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% if (value === 1) { %>one<% } %> --> "one"
}
, matchRegExp = new RegExp([
tags.rawTemplatePart // Important!!! Must be first!
, tags.rawHtmlUnescapedValue
, tags.htmlEscapedValue
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function createRenderFunction (templateString, delimiters) {
var matchRegExp = createMatchRegExp(delimiters)
, renderFunction;
try {
renderFunction = new Function ("_dataObject_", "_escapeHtmlFunction_" // ) {
, "if (!_dataObject_) {_dataObject_ = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (_dataObject_) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (
match
, rawTemplatePartCodeString
, rawUnescapedValueCodeString
, htmlEscapedValueCodeString
, commentCodeString
, evaluateCodeString
, offset
) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (rawTemplatePartCodeString) {
resultString += delimiters[0] + escapeString(rawTemplatePartCodeString) + delimiters[1];
} else if (rawUnescapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function _rawUnescapedValue_ () {" + "\n"
+ "var _value_ = " + rawUnescapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (htmlEscapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function _htmlEscapedValue_ () {" + "\n"
+ "var _value_ = " + htmlEscapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString) {
// Do nothing
} else if (evaluateCodeString) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (err) {
error(err.message += ' while compiling template:\n', templateString);
return function () {return '';};
}
return renderFunction;
}
function escapeString (string) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {return escapeCharacters[match];});
}
function escapeHtmlFunction (html) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return String(html).replace(/(?:&|<|>|"|'|`)/g, function (match) {return escapeCharacters[match];});
}
function block (templatePartString, settingsObject, dataObject) {
return new Template(templatePartString, settingsObject).render(dataObject);
}
return {
Template: Template
, block: block
};
});
var template = '<div><p>Total: <%- value %></p><% var a = 2; while (a--) { %><%- block(innerTemplate, {delimiters: ["{{", "}}"]}, {innerValue: innerValue}) %><% } %></div>'
, innerTemplate = '<p>Last: {{- innerValue }}</p>';
var result = new TPL.Template(template).render({value: '<span>15</span>', block: TPL.block, innerTemplate: innerTemplate, innerValue: '<span>20</span>'});
var fragment = document.createDocumentFragment()
, element = document.createElement('div');
element.innerHTML = result;
fragment.appendChild(element);
document.getElementsByTagName('body')[0].appendChild(fragment);
</script>
</body>
</html>
JavaScript-шаблонизатор Template аналог EJS. Версия 2.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TPL Engine</title>
</head>
<body>
<div id="output"></div>
<script type="text/javascript">
;(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof module === "object" && typeof module.exports === "object") {
module.exports = factory();
} else {
global.TPL = factory();
}
})(this, function () {
// Usage:
// console.log( new TPL.Template("<div>Total: {{= value }}<div>", {delimiters: ["{{", "}}"]}).render({value: "<span>15</span>"}) );
// console.log( new TPL.Template("<div>Total: {{- block('<div>{{= value }}</div>', {delimiters: ['{{', '}}']}, {value: value}) }}<div>", {delimiters: ["{{", "}}"]}).render({block: TPL.block, value: "<span>15</span>"}) );
// Options:
// delimiters - characters to use for open and close delimiters
// rmWhitespace - remove whitespace between HTML-tags
// Tags:
// <% - scriptlet tag for control-flow, no output
// <%% - outputs raw template part
// <%- - outputs the raw unescaped value into the template
// <%= - outputs the HTML escaped value into the template
// <%# - comment tag, no execution, no output
// %> - plain ending tag
// Block:
// "<div><%- block('<div><%= value %></div>', {delimiters: ['<%', '%>']}, {value: outerValue}) %></div>"
function Template (templateString, settingsObject) {
var delimiters = ["<%", "%>"]
, rmWhitespace = false; // removeWhitespace
if (settingsObject) {
if ('delimiters' in settingsObject) {delimiters = settingsObject.delimiters;}
if ('rmWhitespace' in settingsObject) {rmWhitespace = settingsObject.rmWhitespace;}
}
var renderFunction = createRenderFunction(templateString, delimiters);
return {render: function (dataObject) {
var renderedHTML = renderFunction(dataObject, escapeHtmlFunction);
if (rmWhitespace === true) {
renderedHTML = removeSpaces(renderedHTML);
}
return renderedHTML;
}};
}
function removeSpaces (renderedHTML) {
return renderedHTML.replace(/\r/g, "") // Remove carriage return symbols
.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") // Trim space: \s, BOM: \uFEFF, NBSP: \xA0
.replace(/\s+(<[^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)])/g, "$1") // Remove spaces between start tag and content
.replace(/([^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)]>)\s+/g, "$1") // Remove spaces between end tag and content
.replace(/\s+/g, " "); // Remove spaces inside content
}
function createMatchRegExp (delimiters) { // delimiters = ["<%", "%>"]
var openDelimiter = delimiters[0] // <%
, closeDelimiter = delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // any string
, tags = {
rawTemplatePart: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% "<div><%= value %></div>" %> --> "<div><%= value %></div>"
, rawHtmlUnescapedValue: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%- "1<br />2" %> --> "1<br />2"
, htmlEscapedValue: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%= "1<br />2" %> --> "1<br />2"
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# any comment %> --> ""
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% if (value === 1) { %>one<% } %> --> "one"
}
, matchRegExp = new RegExp([
tags.rawTemplatePart // Important!!! Must be first!
, tags.rawHtmlUnescapedValue
, tags.htmlEscapedValue
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function createRenderFunction (templateString, delimiters) {
var matchRegExp = createMatchRegExp(delimiters)
, renderFunction;
try {
renderFunction = new Function ("_dataObject_", "_escapeHtmlFunction_" // ) {
, "if (!_dataObject_) {_dataObject_ = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (_dataObject_) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (
match
, rawTemplatePartCodeString
, rawUnescapedValueCodeString
, htmlEscapedValueCodeString
, commentCodeString
, evaluateCodeString
, offset
) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (rawTemplatePartCodeString) {
resultString += delimiters[0] + escapeString(rawTemplatePartCodeString) + delimiters[1];
} else if (rawUnescapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + rawUnescapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (htmlEscapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + htmlEscapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString) {
// Do nothing
} else if (evaluateCodeString) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (error) {
if (error instanceof SyntaxError) {error.message += " while compiling template: " + templateString;}
throw error;
}
return renderFunction;
}
function escapeString (string) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {return escapeCharacters[match];});
}
function escapeHtmlFunction (html) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return String(html).replace(/(?:&|<|>|"|'|`)/g, function (match) {return escapeCharacters[match];});
}
function block (templatePartString, settingsObject, dataObject) {
return new Template(templatePartString, settingsObject).render(dataObject);
}
return {
Template: Template
, block: block
};
});
// Тесты
var template = '<div><p>Total: <%- value %></p><% var a = 2; while (a--) { %><%- block(innerTemplate, {delimiters: ["{{", "}}"]}, {innerValue: innerValue}) %><% } %></div>'
, innerTemplate = '<p>Last: {{- innerValue }}</p>';
var result = new TPL.Template(template).render({value: '<span>15</span>', block: TPL.block, innerTemplate: innerTemplate, innerValue: '<span>20</span>'});
var fragment = document.createDocumentFragment()
, element = document.createElement('div');
element.innerHTML = result;
fragment.appendChild(element);
document.getElementsByTagName('body')[0].appendChild(fragment);
</script>
</body>
</html>
<html>
<head>
<meta charset="utf-8" />
<title>TPL Engine</title>
</head>
<body>
<div id="output"></div>
<script type="text/javascript">
;(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof module === "object" && typeof module.exports === "object") {
module.exports = factory();
} else {
global.TPL = factory();
}
})(this, function () {
// Usage:
// console.log( new TPL.Template("<div>Total: {{= value }}<div>", {delimiters: ["{{", "}}"]}).render({value: "<span>15</span>"}) );
// console.log( new TPL.Template("<div>Total: {{- block('<div>{{= value }}</div>', {delimiters: ['{{', '}}']}, {value: value}) }}<div>", {delimiters: ["{{", "}}"]}).render({block: TPL.block, value: "<span>15</span>"}) );
// Options:
// delimiters - characters to use for open and close delimiters
// rmWhitespace - remove whitespace between HTML-tags
// Tags:
// <% - scriptlet tag for control-flow, no output
// <%% - outputs raw template part
// <%- - outputs the raw unescaped value into the template
// <%= - outputs the HTML escaped value into the template
// <%# - comment tag, no execution, no output
// %> - plain ending tag
// Block:
// "<div><%- block('<div><%= value %></div>', {delimiters: ['<%', '%>']}, {value: outerValue}) %></div>"
function Template (templateString, settingsObject) {
var delimiters = ["<%", "%>"]
, rmWhitespace = false; // removeWhitespace
if (settingsObject) {
if ('delimiters' in settingsObject) {delimiters = settingsObject.delimiters;}
if ('rmWhitespace' in settingsObject) {rmWhitespace = settingsObject.rmWhitespace;}
}
var renderFunction = createRenderFunction(templateString, delimiters);
return {render: function (dataObject) {
var renderedHTML = renderFunction(dataObject, escapeHtmlFunction);
if (rmWhitespace === true) {
renderedHTML = removeSpaces(renderedHTML);
}
return renderedHTML;
}};
}
function removeSpaces (renderedHTML) {
return renderedHTML.replace(/\r/g, "") // Remove carriage return symbols
.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") // Trim space: \s, BOM: \uFEFF, NBSP: \xA0
.replace(/\s+(<[^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)])/g, "$1") // Remove spaces between start tag and content
.replace(/([^%(?:span|b|strong|i|em|del|s|u|a|sub|sup|small|abbr|img)]>)\s+/g, "$1") // Remove spaces between end tag and content
.replace(/\s+/g, " "); // Remove spaces inside content
}
function createMatchRegExp (delimiters) { // delimiters = ["<%", "%>"]
var openDelimiter = delimiters[0] // <%
, closeDelimiter = delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // any string
, tags = {
rawTemplatePart: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% "<div><%= value %></div>" %> --> "<div><%= value %></div>"
, rawHtmlUnescapedValue: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%- "1<br />2" %> --> "1<br />2"
, htmlEscapedValue: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%= "1<br />2" %> --> "1<br />2"
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# any comment %> --> ""
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% if (value === 1) { %>one<% } %> --> "one"
}
, matchRegExp = new RegExp([
tags.rawTemplatePart // Important!!! Must be first!
, tags.rawHtmlUnescapedValue
, tags.htmlEscapedValue
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function createRenderFunction (templateString, delimiters) {
var matchRegExp = createMatchRegExp(delimiters)
, renderFunction;
try {
renderFunction = new Function ("_dataObject_", "_escapeHtmlFunction_" // ) {
, "if (!_dataObject_) {_dataObject_ = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (_dataObject_) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (
match
, rawTemplatePartCodeString
, rawUnescapedValueCodeString
, htmlEscapedValueCodeString
, commentCodeString
, evaluateCodeString
, offset
) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (rawTemplatePartCodeString) {
resultString += delimiters[0] + escapeString(rawTemplatePartCodeString) + delimiters[1];
} else if (rawUnescapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + rawUnescapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (htmlEscapedValueCodeString) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + htmlEscapedValueCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString) {
// Do nothing
} else if (evaluateCodeString) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (error) {
if (error instanceof SyntaxError) {error.message += " while compiling template: " + templateString;}
throw error;
}
return renderFunction;
}
function escapeString (string) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {return escapeCharacters[match];});
}
function escapeHtmlFunction (html) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return String(html).replace(/(?:&|<|>|"|'|`)/g, function (match) {return escapeCharacters[match];});
}
function block (templatePartString, settingsObject, dataObject) {
return new Template(templatePartString, settingsObject).render(dataObject);
}
return {
Template: Template
, block: block
};
});
// Тесты
var template = '<div><p>Total: <%- value %></p><% var a = 2; while (a--) { %><%- block(innerTemplate, {delimiters: ["{{", "}}"]}, {innerValue: innerValue}) %><% } %></div>'
, innerTemplate = '<p>Last: {{- innerValue }}</p>';
var result = new TPL.Template(template).render({value: '<span>15</span>', block: TPL.block, innerTemplate: innerTemplate, innerValue: '<span>20</span>'});
var fragment = document.createDocumentFragment()
, element = document.createElement('div');
element.innerHTML = result;
fragment.appendChild(element);
document.getElementsByTagName('body')[0].appendChild(fragment);
</script>
</body>
</html>
Подписаться на:
Сообщения (Atom)