Файл index.html
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Verdana;
font-size: 62.5%;
}
table {
width: 100%;
}
table tr td {
width: 50%;
vertical-align: top;
border: 1px solid black;
}
textarea {
width: 100%;
height: 500px;
margin: 0;
padding: 5px;
border: none;
resize: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
font-size: 2.5em;
}
div {
height: 500px;
margin: 0;
padding: 5px;
border: none;
font-size: 2.5em;
}
pre {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<table>
<tr>
<td><textarea id="source"></textarea></td>
<td><div><pre id="result"></pre></div></td>
</tr>
</table>
<script type="text/javascript" src="js/typescriptServices.js"></script>
<script type="text/javascript" src="js/transpiler.js"></script>
</body>
</html>
Файл typescriptService.js
https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js
Файл transpiler.js
;(function(){
document.getElementById('source').addEventListener('keyup', function(){
document.getElementById('result').innerHTML = ts.transpile(document.getElementById('source').value);
}, false);
})();
среда, 24 февраля 2016 г.
Упрощенный код использования TypeScript в браузере
Файл index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
<script type="text/javascript" src="js/typescriptServices.js"></script>
<script type="text/javascript" src="js/transpiler.js"></script>
</head>
<body>
</body>
</html>
Файл typescriptService.js
https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js
Файл transpiler.js
;(function(){
var scripts = document.getElementsByTagName('script')
, script;
for (var i = 0, len = scripts.length; i < len; i++) {
if (scripts[i].type === 'text/typescript') {
script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = '// Compiled TypeScript:\n\n' + ts.transpile(scripts[i].innerHTML);
document.getElementsByTagName('head')[0].appendChild(script);
}
}
})();
<!DOCTYPE html>
<html>
<head>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
<script type="text/javascript" src="js/typescriptServices.js"></script>
<script type="text/javascript" src="js/transpiler.js"></script>
</head>
<body>
</body>
</html>
Файл typescriptService.js
https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js
Файл transpiler.js
;(function(){
var scripts = document.getElementsByTagName('script')
, script;
for (var i = 0, len = scripts.length; i < len; i++) {
if (scripts[i].type === 'text/typescript') {
script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = '// Compiled TypeScript:\n\n' + ts.transpile(scripts[i].innerHTML);
document.getElementsByTagName('head')[0].appendChild(script);
}
}
})();
How to compile TypeScript in the browser
Add the following lines at the bottom of your page:
<script src="https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js"></script>
<script src="https://rawgit.com/basarat/typescript-script/master/transpiler.js"></script>
And then you can use script tags that load .ts files or even have typescript inline:
<script type="text/typescript" src="script.ts"></script>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
Example
index.html source:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script type="text/typescript" src="script.ts"></script>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
<script src="https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js"></script>
<script src="https://rawgit.com/basarat/typescript-script/master/transpiler.js"></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
transpiler.js source:
// BASED on https://github.com/niutech/typescript-compile but using 1.5 transpile function
(function () {
//Keep track of the number of scripts to be pulled, and fire the compiler
//after the number of loaded reaches the total
var scripts = {
total: 0, //total number of scripts to be loaded
loaded: 0, //current number of loaded scripts
data: [], //file data
name: [] //file name
};
//Function loads each script and pushes its content into scripts.data
var load = function (url) {
var xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();;
xhr.open('GET', url, true);
if ('overrideMimeType' in xhr) xhr.overrideMimeType('text/plain');
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
if (xhr.status === 0 || xhr.status === 200) {
scripts.loaded++;
scripts.data.push(xhr.responseText);
scripts.name.push(url);
if (scripts.loaded === scripts.total) compile();
return xhr.responseText;
} else {
console.log('Could not load ' + url);
} //end if
}; //end xhr.onreadystatechange()
return xhr.send(null);
};
//Compiles each of the scripts found within scripts.data
var compile = function () {
if (scripts.data.length == 0 || scripts.data.length != scripts.name.length) return; //no reason to compile when there are no scripts
var elem, source = '',
body = document.getElementsByTagName('body')[0];
scripts.total = 0; //clear the 'queue' incase the xhr response was super quick and happened before the initializer finished
var hashCode = function (s) {
var hsh = 0,
chr, i;
if (s.length == 0) {
return hsh;
}
for (i = 0; i < s.length; i++) {
chr = s.charCodeAt(i);
hsh = (hsh << 5) - hsh + chr;
hsh = hsh & hsh; //Convert to 32bit integer
}
return hsh;
};
if (window.sessionStorage && sessionStorage.getItem('typescript' + hashCode(scripts.data.join('')))) {
source = sessionStorage.getItem('typescript' + hashCode(scripts.data.join('')));
} else {
(function () {
var filename;
for (num = 0; num < scripts.data.length; num++) {
filename = scripts.name[num] = scripts.name[num].slice(scripts.name[num].lastIndexOf('/') + 1);
var src = scripts.data[num];
source += ts.transpile(src);
}
})();
}
elem = document.createElement('script');
elem.type = 'text/javascript';
elem.innerHTML = '//Compiled TypeScript\n\n' + source;
body.appendChild(elem);
};
(function () {
//Polyfill for older browsers
if (!window.console) window.console = {
log: function () {}
};
var script = document.getElementsByTagName('script');
var i, src = [];
for (i = 0; i < script.length; i++) {
if (script[i].type == 'text/typescript') {
if (script[i].src) {
scripts.total++
load(script[i].src);
} else {
scripts.data.push(script[i].innerHTML);
scripts.name.push('innerHTML'+scripts.total);
scripts.total++;
scripts.loaded++;
}
}
}
if (scripts.loaded === scripts.total) compile(); //only fires if all scripts are innerHTML, else this is fired on XHR response
})();
})();
<script src="https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js"></script>
<script src="https://rawgit.com/basarat/typescript-script/master/transpiler.js"></script>
And then you can use script tags that load .ts files or even have typescript inline:
<script type="text/typescript" src="script.ts"></script>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
Example
index.html source:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script type="text/typescript" src="script.ts"></script>
<script type="text/typescript">
setTimeout(()=>console.log('hello'));
</script>
<script src="https://rawgit.com/Microsoft/TypeScript/master/lib/typescriptServices.js"></script>
<script src="https://rawgit.com/basarat/typescript-script/master/transpiler.js"></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
transpiler.js source:
// BASED on https://github.com/niutech/typescript-compile but using 1.5 transpile function
(function () {
//Keep track of the number of scripts to be pulled, and fire the compiler
//after the number of loaded reaches the total
var scripts = {
total: 0, //total number of scripts to be loaded
loaded: 0, //current number of loaded scripts
data: [], //file data
name: [] //file name
};
//Function loads each script and pushes its content into scripts.data
var load = function (url) {
var xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new window.XMLHttpRequest();;
xhr.open('GET', url, true);
if ('overrideMimeType' in xhr) xhr.overrideMimeType('text/plain');
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
if (xhr.status === 0 || xhr.status === 200) {
scripts.loaded++;
scripts.data.push(xhr.responseText);
scripts.name.push(url);
if (scripts.loaded === scripts.total) compile();
return xhr.responseText;
} else {
console.log('Could not load ' + url);
} //end if
}; //end xhr.onreadystatechange()
return xhr.send(null);
};
//Compiles each of the scripts found within scripts.data
var compile = function () {
if (scripts.data.length == 0 || scripts.data.length != scripts.name.length) return; //no reason to compile when there are no scripts
var elem, source = '',
body = document.getElementsByTagName('body')[0];
scripts.total = 0; //clear the 'queue' incase the xhr response was super quick and happened before the initializer finished
var hashCode = function (s) {
var hsh = 0,
chr, i;
if (s.length == 0) {
return hsh;
}
for (i = 0; i < s.length; i++) {
chr = s.charCodeAt(i);
hsh = (hsh << 5) - hsh + chr;
hsh = hsh & hsh; //Convert to 32bit integer
}
return hsh;
};
if (window.sessionStorage && sessionStorage.getItem('typescript' + hashCode(scripts.data.join('')))) {
source = sessionStorage.getItem('typescript' + hashCode(scripts.data.join('')));
} else {
(function () {
var filename;
for (num = 0; num < scripts.data.length; num++) {
filename = scripts.name[num] = scripts.name[num].slice(scripts.name[num].lastIndexOf('/') + 1);
var src = scripts.data[num];
source += ts.transpile(src);
}
})();
}
elem = document.createElement('script');
elem.type = 'text/javascript';
elem.innerHTML = '//Compiled TypeScript\n\n' + source;
body.appendChild(elem);
};
(function () {
//Polyfill for older browsers
if (!window.console) window.console = {
log: function () {}
};
var script = document.getElementsByTagName('script');
var i, src = [];
for (i = 0; i < script.length; i++) {
if (script[i].type == 'text/typescript') {
if (script[i].src) {
scripts.total++
load(script[i].src);
} else {
scripts.data.push(script[i].innerHTML);
scripts.name.push('innerHTML'+scripts.total);
scripts.total++;
scripts.loaded++;
}
}
}
if (scripts.loaded === scripts.total) compile(); //only fires if all scripts are innerHTML, else this is fired on XHR response
})();
})();
JavaScript JSON Validator
// Функция валидации JSON принимает текстовую строку
// и возвращает true, если текст является валидным JSON
// или возвращает false, если текст является невалидным JSON
function validateJSON (text) {
text = '' + text;
// Заменяем опасные символы в формате Unicode на их escape-последовательности.
var dangerousSymbolsPattern = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
dangerousSymbolsPattern.lastIndex = 0;
if (dangerousSymbolsPattern.test(text)) {
text = text.replace(dangerousSymbolsPattern, function (char) {
return '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
});
}
text = text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') // заменяем все символы переноса строки, табы или символы в формате Unicode на символ @
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') // заменяем простые значения токенов типа true, false, null на символ ]
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); // удаляем все открывающие скобки ( и [ в начале строки или следующие за двоеточием и запятой
if (/^[\],:{}\s]*$/.test(text)) { // проверим являются ли оставшиеся символы только символами: ],:{}
return true;
} else {
return false;
}
}
console.log(validateJSON('{"one": 1}'));
// и возвращает true, если текст является валидным JSON
// или возвращает false, если текст является невалидным JSON
function validateJSON (text) {
text = '' + text;
// Заменяем опасные символы в формате Unicode на их escape-последовательности.
var dangerousSymbolsPattern = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
dangerousSymbolsPattern.lastIndex = 0;
if (dangerousSymbolsPattern.test(text)) {
text = text.replace(dangerousSymbolsPattern, function (char) {
return '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
});
}
text = text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') // заменяем все символы переноса строки, табы или символы в формате Unicode на символ @
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') // заменяем простые значения токенов типа true, false, null на символ ]
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); // удаляем все открывающие скобки ( и [ в начале строки или следующие за двоеточием и запятой
if (/^[\],:{}\s]*$/.test(text)) { // проверим являются ли оставшиеся символы только символами: ],:{}
return true;
} else {
return false;
}
}
console.log(validateJSON('{"one": 1}'));
пятница, 19 февраля 2016 г.
JavaScript console code executor (evaluator)
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Console</title>
</head>
<body>
<div id="output" style="overflow: auto; width: 500px; height: 300px; border: 2px solid black;"></div>
<textarea id="input" style="width: 500px; height: 200px; border: 2px solid black;"></textarea>
<br />
<input id="run" type="button" value="Run" />
<script>
function Evaluator () {
this.environment = {};
this.console = {
log: function (message) {console.log('>>> ' + message);}
};
}
Evaluator.prototype.evaluate = function (codeString) {
try {
codeString = rewriteDeclarations(codeString);
var __environment__ = this.environment
, console = this.console; // Temporarily shadow the global console for eval()
with (__environment__) {
return JSON.stringify(eval(codeString));
}
} catch (error) {
return error.toString();
}
}
function rewriteDeclarations (codeString) {
codeString = "\n" + codeString; // Prefix a newline so that search and replace is simpler
codeString = codeString.replace(/\nvar\s+(\w+)\s*=/g, '\n__environment__.$1 =');
codeString = codeString.replace(/\nfunction\s+(\w+)/g, '\n__environment__.$1 = function');
return codeString.slice(1); // Remove prefixed newline
}
var output = document.getElementById('output')
, input = document.getElementById('input')
, run = document.getElementById('run')
, e = new Evaluator();
run.onclick = function () {
var code = input.value
, result = document.createElement('p');
result.innerHTML = e.evaluate(code);
output.appendChild(result);
};
</script>
</body>
</html>
<html>
<head>
<meta charset="utf-8" />
<title>Console</title>
</head>
<body>
<div id="output" style="overflow: auto; width: 500px; height: 300px; border: 2px solid black;"></div>
<textarea id="input" style="width: 500px; height: 200px; border: 2px solid black;"></textarea>
<br />
<input id="run" type="button" value="Run" />
<script>
function Evaluator () {
this.environment = {};
this.console = {
log: function (message) {console.log('>>> ' + message);}
};
}
Evaluator.prototype.evaluate = function (codeString) {
try {
codeString = rewriteDeclarations(codeString);
var __environment__ = this.environment
, console = this.console; // Temporarily shadow the global console for eval()
with (__environment__) {
return JSON.stringify(eval(codeString));
}
} catch (error) {
return error.toString();
}
}
function rewriteDeclarations (codeString) {
codeString = "\n" + codeString; // Prefix a newline so that search and replace is simpler
codeString = codeString.replace(/\nvar\s+(\w+)\s*=/g, '\n__environment__.$1 =');
codeString = codeString.replace(/\nfunction\s+(\w+)/g, '\n__environment__.$1 = function');
return codeString.slice(1); // Remove prefixed newline
}
var output = document.getElementById('output')
, input = document.getElementById('input')
, run = document.getElementById('run')
, e = new Evaluator();
run.onclick = function () {
var code = input.value
, result = document.createElement('p');
result.innerHTML = e.evaluate(code);
output.appendChild(result);
};
</script>
</body>
</html>
JavaScript console.log() polyfill
;(function (global) {
global.console = global.console || {};
var timers = {}
, property
, properties = 'memory'.split(',')
, method
, methods = ('assert,clear,count,debug,dir,dirxml,error,exception,'
+ 'group,groupCollapsed,groupEnd,info,log,markTimeline,'
+ 'profile,profiles,profileEnd,show,table,time,timeEnd,'
+ 'timeline,timelineEnd,timeStamp,trace,warn').split(',');
while (properties.length > 0) {
property = properties.pop();
if (!global.console.hasOwnProperty(property)) {
global.console[property] = {};
}
}
while (methods.length > 0) {
method = methods.pop();
if (
!global.console.hasOwnProperty(method)
|| Object.prototype.toString.call(global.console[method]) !== '[object Function]'
) {
if (
method === 'log'
|| method === 'debug'
|| method === 'info'
|| method === 'warn'
|| method === 'error'
|| method === 'exception'
|| method === 'dir'
|| method === 'dirxml'
) {
global.console[method] = function (message) {alert(message);};
} else if (
method === 'assert'
) {
global.console[method] = function() {
var args = Array.prototype.slice.call(arguments, 0)
, expression = args.shift();
if (!expression) {
args[0] = 'Assertion failed: ' + args[0];
console.error.apply(console, args);
}
};
} else if (
method === 'time'
) {
global.console[method] = function (id) {
timers[id] = new Date().getTime();
};
} else if (
method === 'timeEnd'
) {
global.console[method] = function (id) {
var start = timers[id];
if (start) {
global.console.log(id + ': ' + (new Date().getTime() - start) + 'ms');
delete timers[id];
}
};
} else {
global.console[method] = function () {};
}
}
}
})(this);
console.log(111);
console.assert(1 > 2, 'b');
console.time('one');
console.timeEnd('one');
console.dir([1,2]);
global.console = global.console || {};
var timers = {}
, property
, properties = 'memory'.split(',')
, method
, methods = ('assert,clear,count,debug,dir,dirxml,error,exception,'
+ 'group,groupCollapsed,groupEnd,info,log,markTimeline,'
+ 'profile,profiles,profileEnd,show,table,time,timeEnd,'
+ 'timeline,timelineEnd,timeStamp,trace,warn').split(',');
while (properties.length > 0) {
property = properties.pop();
if (!global.console.hasOwnProperty(property)) {
global.console[property] = {};
}
}
while (methods.length > 0) {
method = methods.pop();
if (
!global.console.hasOwnProperty(method)
|| Object.prototype.toString.call(global.console[method]) !== '[object Function]'
) {
if (
method === 'log'
|| method === 'debug'
|| method === 'info'
|| method === 'warn'
|| method === 'error'
|| method === 'exception'
|| method === 'dir'
|| method === 'dirxml'
) {
global.console[method] = function (message) {alert(message);};
} else if (
method === 'assert'
) {
global.console[method] = function() {
var args = Array.prototype.slice.call(arguments, 0)
, expression = args.shift();
if (!expression) {
args[0] = 'Assertion failed: ' + args[0];
console.error.apply(console, args);
}
};
} else if (
method === 'time'
) {
global.console[method] = function (id) {
timers[id] = new Date().getTime();
};
} else if (
method === 'timeEnd'
) {
global.console[method] = function (id) {
var start = timers[id];
if (start) {
global.console.log(id + ': ' + (new Date().getTime() - start) + 'ms');
delete timers[id];
}
};
} else {
global.console[method] = function () {};
}
}
}
})(this);
console.log(111);
console.assert(1 > 2, 'b');
console.time('one');
console.timeEnd('one');
console.dir([1,2]);
Полноценный JavaScript-шаблонизатор Template аналог EJS
;(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>"}));
// 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
function Template (templateString, newSettingsObject) {
var settingsObject = {
delimiters: ["<%", "%>"]
, rmWhitespace: false // removeWhitespace
};
if (newSettingsObject) {setNewSettings(settingsObject, newSettingsObject);}
var renderFunction = createRenderFunction(templateString, settingsObject); // console.log(renderFunction.toString());
return {render: function (dataObject) {return renderFunction(dataObject, settingsObject, escapeHtmlFunction, trim);}};
}
function setNewSettings (settingsObject, newSettingsObject) {
for (var key in newSettingsObject) {
if (newSettingsObject.hasOwnProperty(key)) {
settingsObject[key] = newSettingsObject[key];
}
}
}
function trim (text) {
return text == null ? "" : ("" + text).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); // Trim space: \s, BOM: \uFEFF, NBSP: \xA0.
}
function createRenderFunction (templateString, settingsObject) {
var matchRegExp = createMatchRegExp(settingsObject)
, renderFunction;
try {
renderFunction = new Function ("dataObject", "_settingsObject_", "_escapeHtmlFunction_", "_trim_" // ) {
, "if (!dataObject) {dataObject = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (dataObject) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (match, prerenderCodeString, rawInterpolateCodeString, interpolateAndEscapeCodeString, commentCodeString, evaluateCodeString, offset) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (prerenderCodeString !== undefined) {
resultString += "' + '" + settingsObject.delimiters[0] + prerenderCodeString + settingsObject.delimiters[1] + "' + '";
} else if (rawInterpolateCodeString !== undefined) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + rawInterpolateCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (interpolateAndEscapeCodeString !== undefined) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + interpolateAndEscapeCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString !== undefined) {
// Do nothing
} else if (evaluateCodeString !== undefined) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "if (_settingsObject_.rmWhitespace) {_parsedTemplateString_ = _trim_(_parsedTemplateString_.replace(/\\\\r/g, ''));}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (error) {
if (error instanceof SyntaxError) {error.message += " while compiling template: " + templateString;}
throw error;
}
return renderFunction;
}
function createMatchRegExp (settingsObject) {
var openDelimiter = settingsObject.delimiters[0] // <%
, closeDelimiter = settingsObject.delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // some string
, tags = {
prerender: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% some string %>
, rawInterpolate: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%= some string %>
, interpolateAndEscape: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%- some string %>
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# some string %>
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% some string %>
}
, matchRegExp = new RegExp([
tags.prerender // Important!!! Must be first!
, tags.rawInterpolate
, tags.interpolateAndEscape
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function escapeString (string) {
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return escapeCharacters[match];
});
}
function escapeHtmlFunction (string) {
string = "" + string;
var pattern = "(?:&|<|>|\"|'|`)"
, testRegExp = new RegExp(pattern)
, replaceRegExp = new RegExp(pattern, "g");
if (testRegExp.test(string)) {
return string.replace(replaceRegExp, function (match) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return escapeCharacters[match];
});
} else {
return string;
}
}
return {
Template: Template
, compile: function (templateString, newSettingsObject) {return new Template(templateString, newSettingsObject);}
, render: function (templateString, dataObject, newSettingsObject) {return new Template(templateString, newSettingsObject).render(dataObject);}
};
});
// Тесты
//console.log(new TPL.Template(" \r \r Total: <% if (true) { %>1<% } %> Total: <%= value %> Total: <%- value %> Total: <%# comment %> Total: <%%- value %> ", {rmWhitespace: true}).render({value: "<div>15</div>"}));
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>"}));
// 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
function Template (templateString, newSettingsObject) {
var settingsObject = {
delimiters: ["<%", "%>"]
, rmWhitespace: false // removeWhitespace
};
if (newSettingsObject) {setNewSettings(settingsObject, newSettingsObject);}
var renderFunction = createRenderFunction(templateString, settingsObject); // console.log(renderFunction.toString());
return {render: function (dataObject) {return renderFunction(dataObject, settingsObject, escapeHtmlFunction, trim);}};
}
function setNewSettings (settingsObject, newSettingsObject) {
for (var key in newSettingsObject) {
if (newSettingsObject.hasOwnProperty(key)) {
settingsObject[key] = newSettingsObject[key];
}
}
}
function trim (text) {
return text == null ? "" : ("" + text).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""); // Trim space: \s, BOM: \uFEFF, NBSP: \xA0.
}
function createRenderFunction (templateString, settingsObject) {
var matchRegExp = createMatchRegExp(settingsObject)
, renderFunction;
try {
renderFunction = new Function ("dataObject", "_settingsObject_", "_escapeHtmlFunction_", "_trim_" // ) {
, "if (!dataObject) {dataObject = {};}" + "\n"
+ "var _parsedTemplateString_ = '';" + "\n"
+ "with (dataObject) {" + "\n"
+ "_parsedTemplateString_ += '" + (function(){
var resultString = ""
, index = 0;
templateString.replace(matchRegExp, function (match, prerenderCodeString, rawInterpolateCodeString, interpolateAndEscapeCodeString, commentCodeString, evaluateCodeString, offset) {
resultString += escapeString(templateString.slice(index, offset));
index = offset + match.length;
if (prerenderCodeString !== undefined) {
resultString += "' + '" + settingsObject.delimiters[0] + prerenderCodeString + settingsObject.delimiters[1] + "' + '";
} else if (rawInterpolateCodeString !== undefined) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + rawInterpolateCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _value_;}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (interpolateAndEscapeCodeString !== undefined) {
resultString += "'" + "\n"
+ "+ (function(){" + "\n"
+ "var _value_ = " + interpolateAndEscapeCodeString + ";" + "\n"
+ "if (_value_ === undefined || _value_ === null) {return '';} else {return _escapeHtmlFunction_(_value_);}" + "\n"
+ "})()" + "\n"
+ "+ '";
} else if (commentCodeString !== undefined) {
// Do nothing
} else if (evaluateCodeString !== undefined) {
resultString += "';" + "\n"
+ evaluateCodeString + "\n"
+ "_parsedTemplateString_ += '";
}
return match;
});
return resultString;
})()
+ "';" + "\n"
+ "}" + "\n"
+ "if (_settingsObject_.rmWhitespace) {_parsedTemplateString_ = _trim_(_parsedTemplateString_.replace(/\\\\r/g, ''));}" + "\n"
+ "return _parsedTemplateString_;"
// }
);
} catch (error) {
if (error instanceof SyntaxError) {error.message += " while compiling template: " + templateString;}
throw error;
}
return renderFunction;
}
function createMatchRegExp (settingsObject) {
var openDelimiter = settingsObject.delimiters[0] // <%
, closeDelimiter = settingsObject.delimiters[1] // %>
, stringInsideDelimiters = "([\\s\\S]+?)" // some string
, tags = {
prerender: openDelimiter + "%" + stringInsideDelimiters + closeDelimiter // <%% some string %>
, rawInterpolate: openDelimiter + "-" + stringInsideDelimiters + closeDelimiter // <%= some string %>
, interpolateAndEscape: openDelimiter + "=" + stringInsideDelimiters + closeDelimiter // <%- some string %>
, comment: openDelimiter + "#" + stringInsideDelimiters + closeDelimiter // <%# some string %>
, evaluate: openDelimiter + stringInsideDelimiters + closeDelimiter // <% some string %>
}
, matchRegExp = new RegExp([
tags.prerender // Important!!! Must be first!
, tags.rawInterpolate
, tags.interpolateAndEscape
, tags.comment
, tags.evaluate // Important!!! Must be last!
].join("|") + "|$", "g"); // /<%%([\s\S]+?)%>|<%-([\s\S]+?)%>|<%=([\s\S]+?)%>|<%#([\s\S]+?)%>|<%([\s\S]+?)%>|$/g
return matchRegExp;
}
function escapeString (string) {
return string.replace(/\\|'|\r|\n|\u2028|\u2029/g, function (match) {
var escapeCharacters = {
"'": "\\'"
, "\\": "\\\\"
, "\r": "\\r"
, "\n": "\\n"
, "\u2028": "\\u2028"
, "\u2029": "\\u2029"
};
return escapeCharacters[match];
});
}
function escapeHtmlFunction (string) {
string = "" + string;
var pattern = "(?:&|<|>|\"|'|`)"
, testRegExp = new RegExp(pattern)
, replaceRegExp = new RegExp(pattern, "g");
if (testRegExp.test(string)) {
return string.replace(replaceRegExp, function (match) {
var escapeCharacters = {
"&": "&"
, "<": "<"
, ">": ">"
, "\"": """
, "'": "'"
, "`": "`"
};
return escapeCharacters[match];
});
} else {
return string;
}
}
return {
Template: Template
, compile: function (templateString, newSettingsObject) {return new Template(templateString, newSettingsObject);}
, render: function (templateString, dataObject, newSettingsObject) {return new Template(templateString, newSettingsObject).render(dataObject);}
};
});
// Тесты
//console.log(new TPL.Template(" \r \r Total: <% if (true) { %>1<% } %> Total: <%= value %> Total: <%- value %> Total: <%# comment %> Total: <%%- value %> ", {rmWhitespace: true}).render({value: "<div>15</div>"}));
Подписаться на:
Сообщения (Atom)