среда, 24 февраля 2016 г.

TypeScript Playground

Файл 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);
})();

Упрощенный код использования 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);
        }
    }
})();

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
    })();
})();

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}'));

пятница, 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>

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]);

Полноценный 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 = {
                      "&":  "&amp;"
                    , "<":  "&lt;"
                    , ">":  "&gt;"
                    , "\"": "&quot;"
                    , "'":   "&#x27;"
                    , "`":  "&#x60;"
                };
                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>"}));