<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Esprima: Operator Precedence Demo</title>
<script src="../esprima.js"></script>
<script src="../assets/json2.js"></script>
<link rel="stylesheet" type="text/css" href="../assets/style.css"/>
<style>
#a, #b, #expr {
    font-size: 16px;
    padding: 5px;
    margin: 5px;
    border: 1px solid #ccc;
}

#expr {
    border: none;
}

#answer {
    color: white;
    padding: 5px;
    margin: 20px;
}

.yes {
    background-color: #228B22;
}

.no {
    background-color: #8B2500;
}

.lightred {
    background-color: #FFC0CB;
}
</style>
<script>
/*jslint sloppy:true browser:true */
/*global esprima:true */
var compareId;
function compare() {
    if (compareId) {
        window.clearTimeout(compareId);
    }

    function stringify(node) {
        var result;

        if (typeof node !== 'object') {
            throw new Error('Node is not valid');
        }
        if (typeof node.type !== 'string') {
            throw new Error('Node does not have type property');
        }

        switch (node.type) {

        case 'Program':
            if (node.body.length !== 1) {
                throw new Error('Expression is too complex');
            }
            result = stringify(node.body[0]);
            if (result[0] === '(' && result[result.length - 1] === ')') {
                result = result.substr(1, result.length - 2);
            }
            break;

        case 'ExpressionStatement':
            result = stringify(node.expression);
            break;

        case 'BinaryExpression':
        case 'LogicalExpression':
            result = '(' + stringify(node.left) + ' ' + node.operator + ' ' + stringify(node.right) + ')';
            break;

        case 'UnaryExpression':
            result = '(' + node.operator;
            if (node.operator.length > 2) {
                // delete void typeof
                result += ' ';
            }
            result += stringify(node.argument) + ')';
            break;

        case 'UpdateExpression':
            result = stringify(node.argument);
            if (node.prefix) {
                result = node.operator + result;
            } else {
                result = result + node.operator;
            }
            result = '(' + result + ')';
            break;

        case 'Literal':
            result = node.value.toString();
            if (typeof node.value === 'string') {
                result = '"' + node.value + '"';
            }
            break;

        case 'Identifier':
            result = node.name;
            break;

        default:
            break;
        }

        if (!result) {
            throw new Error('Unknown node type: ' + node.type);
        }

        return result;
    }

    function setText(el, str) {
        if (typeof el.innerText === 'string') {
            el.innerText = str;
        } else {
            el.textContent = str;
        }
    }

    compareId = window.setTimeout(function () {
        var a, b, answer, status, expr, left, right, suggest;

        a = document.getElementById('a');
        b = document.getElementById('b');
        answer = document.getElementById('answer');
        status = document.getElementById('status');
        expr = document.getElementById('expr');

        a.setAttribute('class', '');
        b.setAttribute('class', '');
        answer.setAttribute('class', '');

        setText(answer, '');
        setText(status, '');
        setText(expr, '');

        try {
            left = esprima.parse((typeof a.innerText === 'string') ? a.innerText : a.textContent);
        } catch (e_left) {
            a.setAttribute('class', 'lightred');
        }

        try {
            right = esprima.parse((typeof b.innerText === 'string') ? b.innerText : b.textContent);
        } catch (e_right) {
            b.setAttribute('class', 'lightred');
        }

        try {
            suggest = stringify(left);
        } catch (e_suggest) {
            a.setAttribute('class', 'lightred');
        }

        if (left && right) {
            if (JSON.stringify(left) === JSON.stringify(right)) {
                setText(answer, 'Yes');
                answer.setAttribute('class', 'yes');
            } else {
                setText(answer, 'No');
                answer.setAttribute('class', 'no');
                setText(status, suggest ? 'It is more like ' : '');
                setText(expr, suggest || '');
            }
        } else {
            answer.setAttribute('class', '');
        }

        compareId = undefined;
    }, 57);
}
</script>
</head>
<body>
<div class="container">

<div class="topbar">
<ul class="nav">
<li><a href="../index.html">&larr; Home</a></li>
<li><a href="http://github.com/ariya/esprima">Code</a></li>
<li><a href="http://wiki.esprima.org">Documentation</a></li>
<li><a href="http://issues.esprima.org">Issues</a></li>
</ul>
</div>

<h1>Operator precedence <small>is not always easy</small></h1>
<p>Is <code id="a" contenteditable="true">1 &lt;&lt; 2 * 3</code> semantically equivalent to
<code id="b" contenteditable="true">(1 &lt;&lt; 2) * 3</code>?  <span id="answer"></span></p>
<p style="margin-top: 40px;"><span id="status"></span> <code id="expr"></code></p>
<p style="margin-top: 60px;">This demo is inspired by <a href="http://mothereff.in/operator-precedence">mothereff.in/operator-precedence</a>.</p>
<div class="footer"><strong>Esprima</strong> is created by
<a href="http://ariya.ofilabs.com/about" target="_blank">Ariya Hidayat</a>. Follow <a href="http://twitter.com/ariyahidayat">@ariyahidayat</a> on Twitter.
</div>
</div>
<script>
if (typeof document.body.attachEvent === 'object') {
    // Workaround for old Internet Explorer.
    // Until there is a reliable way to track the modification to the editable
    // inputs, manually track the change periodically.
    window.setInterval(compare, 500);
}

// See http://mathiasbynens.be/notes/oninput for details.
document.getElementById('a').onkeyup = compare;
document.getElementById('a').oninput = function () {
    this.onkeyup = null;
    compare();
};
document.getElementById('b').onkeyup = compare;
document.getElementById('b').oninput = function () {
    this.onkeyup = null;
    compare();
};
compare();
</script>
</body>
</html>