Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,74 @@
define AVAILABLE_ACTIONS
build: do static checking and build of js
buildp: do static checking and build of python
buildj: do static checking and build of javascript
test: test both implementations, js and python
testp: test python implementation
testj: test javascript implementation
endef
export AVAILABLE_ACTIONS
.SILENT:
all: build test status
help:
echo "$$AVAILABLE_ACTIONS"
build: buildj buildp
buildj:
echo Building javascript... ;\
npm install ;\
buildp:
echo Building python... ;\
pip install -e ./python
testp:
echo Testing python implementation...
node test/generate-tests.js || exit 1;\
cd python ;\
python --version ;\
./jsbeautifier/tests/shell-smoke-test.sh
testj:
echo Testing javascript implementation...
node test/generate-tests.js || exit 1;\
node --version; \
npm test
edit:
vim \
js/lib/beautify.js python/jsbeautifier/__init__.py \
js/test/beautify-tests.js python/jsbeautifier/tests/testjsbeautifier.py
gedit:
gvim \
js/lib/beautify.js \
js/test/beautify-tests.js \
python/jsbeautifier/__init__.py \
python/jsbeautifier/tests/testjsbeautifier.py &
tests: testj testp
test: testj testp
status:
test/git-status-clear.sh || exit 1
gh:
git push origin master &&\
cd gh-pages &&\
git fetch &&\
git checkout gh-pages &&\
git reset --hard origin/gh-pages &&\
git merge origin/master &&\
git push origin gh-pages
.PHONY: testp testj all edit tests test

View file

@ -0,0 +1,11 @@
{
"name": "js-beautify",
"main": [
"./js/lib/beautify.js",
"./js/lib/beautify-css.js",
"./js/lib/beautify-html.js"
],
"ignore": [
"**/.*"
]
}

View file

@ -0,0 +1,28 @@
#!/bin/bash
# based on https://gist.github.com/joelittlejohn/5937573
#
if [ "$#" -ne 1 ]; then
echo "Usage: ./generate-changelog.sh user/repo"
exit 1
fi
IFS=$'\n'
echo "# Changelog" > CHANGELOG.md
for m in $(curl -s "https://api.github.com/repos/$1/milestones?state=closed" | jq -c '.[] | [.title, .number, .description]' | sort -r); do
mid=$(echo $m | sed 's/\[".*",\(.*\),".*"\]/\1/')
title=$(echo $m | sed 's/\["\(.*\)",.*,".*"\]/\1/')
echo "Processing milestone: $title..."
echo $m | sed 's/\["\(.*\)",.*\]/## \1/' >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo '### Description' >> CHANGELOG.md
echo $m | sed 's/\[".*",.*,"\(.*\)"\]/\1/' | sed -e 's/\\"/"/g' | sed -e 's/\\r\\n/\\n/g' | sed -e 's/\\n/\'$'\n/g' >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo '### Closed Issues' >> CHANGELOG.md
for i in $(curl -s "https://api.github.com/repos/$1/issues?milestone=$mid&state=closed" | jq -c '.[] | [.html_url, .number, .title]'); do
echo $i | sed 's/\["\(.*\)",\(.*\),\"\(.*\)\"\]/* \3 ([#\2](\1))/' | sed 's/\\"/"/g' >> CHANGELOG.md
done
echo "" >> CHANGELOG.md
echo "" >> CHANGELOG.md
done

View file

@ -0,0 +1,544 @@
<html>
<!--
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<head>
<meta charset="utf-8">
<title>Online JavaScript beautifier</title>
<link rel="icon" href="web/favicon.png" type="image/png">
<link rel="stylesheet" href="web/third-party/codemirror/lib/codemirror.css">
<script src="web/third-party/codemirror/lib/codemirror.js"></script>
<script src="web/third-party/codemirror/mode/javascript/javascript.js"></script>
<style>
body {
background: #eee;
color: #333;
}
img {
border: 0;
}
a.self {
font-weight: bold;
text-decoration: none;
color: #444;
border-bottom: 1px solid #aaa
}
p, select, label, .blurb, a.turn-off-codemirror {
font:13px/1.231 arial, sans-serif;
*font-size:small;
}
a.turn-off-codemirror {
margin-left: 25px;
}
button.submit {
width: 100%;
padding: 10px 0;
cursor: pointer;
margin: 0;
}
button.submit em {
font-size: 11px;
font-style: normal;
color: #999;
}
label {
cursor: pointer;
}
select {
width: 220px;
}
table#options {
float: right;
}
table#options td {
vertical-align: top;
padding-left: 10px;
}
#about {
float: left;
}
#about p {
margin: 0 6px 6px 0;
}
li {
line-height: 150%;
}
li.writeme {
padding-top: 8px;
}
#testresults {
display: none;
font-family: monaco, "lucida console", "courier new", monospace;
}
.CodeMirror {
border: 1px solid #ccc;
height: 450px;
font-size: 90%;
margin-bottom: 6px;
background: white;
}
p {
margin-left: 40px;
margin-right: 40px;
}
a {
white-space: nowrap;
color: #36d;
}
.contributor-sep {
border-top: 1px solid #ccc;
padding-top: 8px;
}
</style>
<script src="web/third-party/jquery/jquery.js"></script>
<script src="web/third-party/jquery/jquery.cookie.js"></script>
<script src="js/lib/beautify.js"></script>
<script src="js/lib/beautify-css.js"></script>
<script src="js/lib/beautify-html.js"></script>
<script src="js/test/sanitytest.js"></script>
<script src="js/test/beautify-javascript-tests.js"></script>
<script src="js/test/beautify-css-tests.js"></script>
<script src="js/test/beautify-html-tests.js"></script>
<script src="js/lib/unpackers/javascriptobfuscator_unpacker.js"></script>
<script src="js/lib/unpackers/urlencode_unpacker.js"></script>
<script src="js/lib/unpackers/p_a_c_k_e_r_unpacker.js"></script>
<script src="js/lib/unpackers/myobfuscate_unpacker.js"></script>
<script>
var the = {
use_codemirror: (!window.location.href.match(/without-codemirror/)),
beautify_in_progress: false,
editor: null // codemirror editor
};
function run_tests() {
var st = new SanityTest();
run_javascript_tests(st, Urlencoded, js_beautify, html_beautify, css_beautify);
run_css_tests(st, Urlencoded, js_beautify, html_beautify, css_beautify);
run_html_tests(st, Urlencoded, js_beautify, html_beautify, css_beautify);
JavascriptObfuscator.run_tests(st);
P_A_C_K_E_R.run_tests(st);
Urlencoded.run_tests(st);
MyObfuscate.run_tests(st);
var results = st.results_raw()
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/ /g, '&nbsp;')
.replace(/\r/g, '·')
.replace(/\n/g, '<br>');
$('#testresults').html(results).show();
}
function any(a, b) {
return a || b;
}
function read_settings_from_cookie() {
$('#tabsize').val(any($.cookie('tabsize'), '4'));
$('#brace-style').val(any($.cookie('brace-style'), 'collapse'));
$('#detect-packers').prop('checked', $.cookie('detect-packers') !== 'off');
$('#max-preserve-newlines').val(any($.cookie('max-preserve-newlines'), '5'));
$('#keep-array-indentation').prop('checked', $.cookie('keep-array-indentation') === 'on');
$('#break-chained-methods').prop('checked', $.cookie('break-chained-methods') === 'on');
$('#indent-scripts').val(any($.cookie('indent-scripts'), 'normal'));
$('#space-before-conditional').prop('checked', $.cookie('space-before-conditional') !== 'off');
$('#wrap-line-length').val(any($.cookie('wrap-line-length'), '0'));
$('#unescape-strings').prop('checked', $.cookie('unescape-strings') === 'on');
$('#jslint-happy').prop('checked', $.cookie('jslint-happy') === 'on');
$('#end-with-newline').prop('checked', $.cookie('end-with-newline') === 'on');
$('#indent-inner-html').prop('checked', $.cookie('indent-inner-html') === 'on');
$('#comma-first').prop('checked', $.cookie('comma-first') === 'on');
$('#e4x').prop('checked', $.cookie('e4x') === 'on');
}
function store_settings_to_cookie() {
var opts = {
expires: 360
};
$.cookie('tabsize', $('#tabsize').val(), opts);
$.cookie('brace-style', $('#brace-style').val(), opts);
$.cookie('detect-packers', $('#detect-packers').prop('checked') ? 'on' : 'off', opts);
$.cookie('max-preserve-newlines', $('#max-preserve-newlines').val(), opts);
$.cookie('keep-array-indentation', $('#keep-array-indentation').prop('checked') ? 'on' : 'off', opts);
$.cookie('break-chained-methods', $('#break-chained-methods').prop('checked') ? 'on' : 'off', opts);
$.cookie('space-before-conditional', $('#space-before-conditional').prop('checked') ? 'on' : 'off',
opts);
$.cookie('unescape-strings', $('#unescape-strings').prop('checked') ? 'on' : 'off', opts);
$.cookie('jslint-happy', $('#jslint-happy').prop('checked') ? 'on' : 'off', opts);
$.cookie('end-with-newline', $('#end-with-newline').prop('checked') ? 'on' : 'off', opts);
$.cookie('wrap-line-length', $('#wrap-line-length').val(), opts);
$.cookie('indent-scripts', $('#indent-scripts').val(), opts);
$.cookie('indent-inner-html', $('#indent-inner-html').prop('checked') ? 'on' : 'off', opts);
$.cookie('comma-first', $('#comma-first').prop('checked') ? 'on' : 'off', opts);
$.cookie('e4x', $('#e4x').prop('checked') ? 'on' : 'off', opts);
}
function unpacker_filter(source) {
var trailing_comments = '',
comment = '',
unpacked = '',
found = false;
// cut trailing comments
do {
found = false;
if (/^\s*\/\*/.test(source)) {
found = true;
comment = source.substr(0, source.indexOf('*/') + 2);
source = source.substr(comment.length).replace(/^\s+/, '');
trailing_comments += comment + "\n";
} else if (/^\s*\/\//.test(source)) {
found = true;
comment = source.match(/^\s*\/\/.*/)[0];
source = source.substr(comment.length).replace(/^\s+/, '');
trailing_comments += comment + "\n";
}
} while (found);
var unpackers = [P_A_C_K_E_R, Urlencoded, /*JavascriptObfuscator,*/ MyObfuscate];
for (var i = 0; i < unpackers.length; i++) {
if (unpackers[i].detect(source)) {
unpacked = unpackers[i].unpack(source);
if (unpacked != source) {
source = unpacker_filter(unpacked);
}
}
}
return trailing_comments + source;
}
function beautify() {
if (the.beautify_in_progress) return;
store_settings_to_cookie();
the.beautify_in_progress = true;
var source = the.editor ? the.editor.getValue() : $('#source').val(),
output,
opts = {};
opts.indent_size = $('#tabsize').val();
opts.indent_char = opts.indent_size == 1 ? '\t' : ' ';
opts.max_preserve_newlines = $('#max-preserve-newlines').val();
opts.preserve_newlines = opts.max_preserve_newlines !== "-1";
opts.keep_array_indentation = $('#keep-array-indentation').prop('checked');
opts.break_chained_methods = $('#break-chained-methods').prop('checked');
opts.indent_scripts = $('#indent-scripts').val();
opts.brace_style = $('#brace-style').val();
opts.space_before_conditional = $('#space-before-conditional').prop('checked');
opts.unescape_strings = $('#unescape-strings').prop('checked');
opts.jslint_happy = $('#jslint-happy').prop('checked');
opts.end_with_newline = $('#end-with-newline').prop('checked');
opts.wrap_line_length = $('#wrap-line-length').val();
opts.indent_inner_html = $('#indent-inner-html').prop('checked');
opts.comma_first = $('#comma-first').prop('checked');
opts.e4x = $('#e4x').prop('checked');
if (looks_like_html(source)) {
output = html_beautify(source, opts);
} else {
if ($('#detect-packers').prop('checked')) {
source = unpacker_filter(source);
}
output = js_beautify(source, opts);
}
if (the.editor) {
the.editor.setValue(output);
} else {
$('#source').val(output);
}
the.beautify_in_progress = false;
}
function looks_like_html(source) {
// <foo> - looks like html
// <!--\nalert('foo!');\n--> - doesn't look like html
var trimmed = source.replace(/^[ \t\n\r]+/, '');
var comment_mark = '<' + '!-' + '-';
return (trimmed && (trimmed.substring(0, 1) === '<' && trimmed.substring(0, 4) !== comment_mark));
}
</script>
</head>
<body>
<div id="about">
<p>
<a class="self" href="./">Beautify, unpack or deobfuscate JavaScript and HTML, make JSON/JSONP readable, etc.</a>
</p>
<p>
All of the source code is completely free and open, available on <a href="https://github.com/beautify-web/js-beautify">GitHub</a> under MIT licence,
<br>and we have a command-line version, python library and a <a href="https://npmjs.org/package/js-beautify">node package</a> as well.
</p>
</div>
<table id="options">
<tr>
<td>
<select name="tabsize" id="tabsize">
<option value="1">Indent with a tab character</option>
<option value="2">Indent with 2 spaces</option>
<option value="3">Indent with 3 spaces</option>
<option value="4">Indent with 4 spaces</option>
<option value="8">Indent with 8 spaces</option>
</select>
<br>
<select name="max-preserve-newlines" id="max-preserve-newlines">
<option value="-1">Remove all extra newlines</option>
<option value="1">Allow 1 newline between tokens</option>
<option value="2">Allow 2 newlines between tokens</option>
<option value="5">Allow 5 newlines between tokens</option>
<option value="10">Allow 10 newlines between tokens</option>
<option value="0">Allow unlimited newlines between tokens</option>
</select>
<br>
<select name="wrap-line-length" id="wrap-line-length">
<option value="0">Do not wrap lines</option>
<option value="40">Wrap lines near 40 characters</option>
<option value="70">Wrap lines near 70 characters</option>
<option value="80">Wrap lines near 80 characters</option>
<option value="110">Wrap lines near 110 characters</option>
<option value="120">Wrap lines near 120 characters</option>
<option value="160">Wrap lines near 160 characters</option>
</select>
<br>
<select id="brace-style">
<option value="collapse">Braces with control statement</option>
<option value="expand">Braces on own line</option>
<option value="end-expand">End braces on own line</option>
<option value="none">Attempt to keep braces where
they are</option>
</select>
<p style="margin:6px 0 0 0">HTML &lt;style&gt;, &lt;script&gt; formatting:</p>
<select id="indent-scripts">
<option value="keep">Keep indent level of the tag</option>
<option value="normal">Add one indent level</option>
<option value="separate">Separate indentation</option>
</select>
</td>
<td>
<input class="checkbox" type="checkbox" id="end-with-newline">
<label for="end-with-newline">End script and style with newline?</label>
<br>
<input class="checkbox" type="checkbox" id="e4x">
<label for="e4x">Support e4x/jsx syntax</label>
<br>
<input class="checkbox" type="checkbox" id="comma-first">
<label for="comma-first">Use comma-first list style?</label>
<br>
<input class="checkbox" type="checkbox" id="detect-packers">
<label for="detect-packers">Detect packers and obfuscators?</label>
<br>
<input class="checkbox" type="checkbox" id="keep-array-indentation">
<label for="keep-array-indentation">Keep array indentation?</label>
<br>
<input class="checkbox" type="checkbox" id="break-chained-methods">
<label for="break-chained-methods">Break lines on chained methods?</label>
<br>
<input class="checkbox" type="checkbox" id="space-before-conditional">
<label for="space-before-conditional">Space before conditional: "if(x)" / "if (x)"</label>
<br>
<input class="checkbox" type="checkbox" id="unescape-strings">
<label for="unescape-strings">Unescape printable chars encoded as \xNN or \uNNNN?</label>
<br>
<input class="checkbox" type="checkbox" id="jslint-happy">
<label for="jslint-happy">Use JSLint-happy formatting tweaks?</label>
<br>
<input class="checkbox" type="checkbox" id="indent-inner-html">
<label for="indent-inner-html">Indent &lt;head&gt; and &lt;body&gt; sections?</label>
<br><a href="?without-codemirror" class="turn-off-codemirror">Use a simple textarea for code input?</a>
</td>
</tr>
</table>
<div style="line-height: 0">
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
<textarea id="source" rows="30" cols="160"></textarea>
<button class="submit"><strong>Beautify JavaScript or HTML</strong> <em>(ctrl-enter)</em>
</button>
</div>
<div class="blurb">
<div style="float:right;">
<a style="display:block; float: left; padding-top: 3px" href="http://flattr.com/thing/94550/jsbeautifier-org" target="_blank">
<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr">
</a>
</div>
<p>Browser extensions and other uses:</p>
<ul id="uses">
<li>A <a href="javascript:(function(){s=document.getElementsByTagName('SCRIPT');tx='';sr=[];for(i=0;i<s.length;i++){with(s.item(i)){t=text;if(t){tx+=t;}else{sr.push(src)};}};with(window.open()){document.write('<textarea id=&quot;t&quot;>'+(sr.join(&quot;\n&quot;))+&quot;\n\n-----\n\n&quot;+tx+'</textarea><script src=&quot;http://jsbeautifier.org/beautify.js&quot;></script><script>with(document.getElementById(&quot;t&quot;)){value=js_beautify(value);with(style){width=&quot;99%&quot;;height=&quot;99%&quot;;borderStyle=&quot;none&quot;;}};</script>');document.close();}})();"><strong>bookmarklet</strong></a> (drag
it to your bookmarks) by Ichiro Hiroshi to see all scripts used on the page,</li>
<li style="margin-top: 8px"><strong>Chrome</strong>: <a href="https://github.com/rixth/jsbeautify-for-chrome">jsbeautify-for-chrome</a> by Tom Rix,</li>
<li><strong>Chrome</strong>: <a href="https://chrome.google.com/webstore/detail/piekbefgpgdecckjcpffhnacjflfoddg">Pretty Beautiful JavaScript</a> by Will McSweeney,</li>
<li><strong>Chrome</strong>: <a href="https://chrome.google.com/webstore/detail/cfmcghennfbpmhemnnfjhkdmnbidpanb">Quick source viewer</a> by Tomi Mickelsson
(<a href="https://github.com/tomimick/chrome-ext-view-src">github</a>, <a href="http://tomicloud.com/2012/07/viewsrc-chrome-ext">blog</a>),</li>
<li><strong>Chrome</strong>: <a href="https://chrome.google.com/webstore/detail/stackoverflow-code-beauti/pljeafjjkkbacckkollfejkciddacmeb">Stackoverflow Code Beautify</a> by Making Odd Edit Studios (<a href="https://github.com/MakingOddEdit/CodeBeautify">github</a>),</li>
<li style="margin-top: 8px"><strong>Firefox</strong>: <a href="https://addons.mozilla.org/en-US/firefox/addon/javascript-deminifier/">Javascript deminifier</a> by Ben Murphy, to be
used together with the firebug (<a href="https://github.com/benmmurphy/jsdeminifier_xpi/">github</a>),</li>
<li style="margin-top: 8px"><strong>Safari</strong>: <a href="http://spadin.github.com/js-beautify-safari-extension">Safari extension</a> by Sandro Padin,</li>
<li style="margin-top: 8px"><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/readable-javascript/">Readable JavaScript</a>
(<a href="https://github.com/Dither/readable-javascript">github</a>) by Dither,</li>
<li><strong>Opera</strong>: <a href="https://addons.opera.com/addons/extensions/details/source/">Source</a> extension by Deathamns,</li>
<li style="margin-top: 8px"><strong>Sublime Text 2:</strong> <a href="https://github.com/jdc0589/JsFormat">JsFormat</a>, a javascript formatting plugin for this nice editor by Davis
Clark,</li>
<li style="margin-top: 8px"><strong>vim:</strong> <a href="https://github.com/michalliu/sourcebeautify.vim">sourcebeautify.vim</a>, a plugin by michalliu (requires node.js, V8, SpiderMonkey
or cscript js engine),</li>
<li><strong>vim:</strong> <a href="https://github.com/maksimr/vim-jsbeautify">vim-jsbeautify</a>, a plugin by Maksim Ryzhikov (node.js or V8 required),</li>
<li style="margin-top: 8px"><strong>Emacs:</strong> <a href="https://github.com/yasuyk/web-beautify">Web-beautify</a> formatting package by Yasuyuki Oka,</li>
<li style="margin-top: 8px"><strong>C#:</strong> ghost6991 <a href="https://github.com/ghost6991/Jsbeautifier">ported the javascript formatter to C#</a>,
<li style="margin-top: 8px"><strong>Go:</strong> ditashi has <a href="https://github.com/ditashi/jsbeautifier-go">ported the javascript formatter to golang</a>,
<li style="margin-top: 12px"><a href="http://fiddler2.com/">Fiddler</a> proxy: <a href="http://fiddler2.com/Fiddler2/extensions.asp">JavaScript Formatter addon</a>,
<li><a href="https://github.com/nagaozen/gedit-tunnings/">gEdit tips</a> by Fabio Nagao,</li>
<li><a href="http://akelpad.sourceforge.net/forum/viewtopic.php?p=11246#11246">Akelpad extension</a> by Infocatcher,</li>
<li>Beautifier in <a href="http://sethmason.com/2011/04/28/jsbeautify-in-emacs.html">Emacs</a> write-up by Seth Mason,</li>
<li><a href="http://c9.io">Cloud9</a>, a lovely IDE running in a browser, working in the node/cloud, uses jsbeautifier (<a href="https://github.com/ajaxorg/cloud9">github</a>),</li>
<li><a href="http://www.symetronapps.com/pages/apps/shrinker.html">Shrinker</a>, a non-free JavaScript packer for Mac. I&nbsp;haven't used it, so I&nbsp;have
no idea if it's any good,</li>
<li><a href="http://www.restconsole.com/">REST Console</a>, a request debugging tool for Chrome, beautifies JSON responses (<a href="https://github.com/codeinchaos/rest-console">github</a>),</li>
<li><a href="http://mitmproxy.org/">mitmproxy</a>, a nifty SSL-capable HTTP proxy, provides pretty javascript responses (<a href="https://github.com/cortesi/mitmproxy">github</a>).</li>
<li><a href="http://www.wakanda.org/">wakanda</a>, a neat IDE for web and mobile applications has a <a href="http://forum.wakanda.org/showthread.php?1483-3-new-extensions-JSLint-Beautifier-and-Snippet">Beautifier extension</a>
(<a href="https://github.com/Wakanda/wakanda-extensions/tree/master/Beautifier">github</a>).</li>
<li><a href="http://portswigger.net/burp/">Burp Suite</a> now has a <a href="https://github.com/irsdl/BurpSuiteJSBeautifier/">beautfier extension</a>,
thanks to Soroush Dalili,</li>
<li><a href="http://plugins.netbeans.org/plugin/43263/jsbeautify">Netbeans jsbeautify</a> plugin by Drew Hamlett
(<a href="https://github.com/drewhjava/netbeans-jsbeautify">github</a>).</li>
<li><a href="https://github.com/drewhjava/brackets-beautify">brackets-beautify-extension</a> for <a href="http://brackets.io">Adobe Brackets</a> by Drew
Hamlett (<a href="https://github.com/drewhjava/brackets-beautify">github</a>),</li>
<li><a href="http://codecaddy.net/">codecaddy.net</a>, a collection of webdev-related tools, assembled by Darik Hall,
<li><a href="http://www.editey.com/">editey.com</a>, an interesting and free Google-Drive oriented editor uses this beautifier,
<li><a href="https://github.com/vkadam/grunt-jsbeautifier">a beautifier plugin for Grunt</a> by Vishal Kadam,
<li><a href="http://www.uvviewsoft.com/synwrite/">SynWrite</a> editor has a JsFormat plugin (<a href="https://sourceforge.net/projects/synwrite-addons/files/PyPlugins/Alexey.JsFormat/">rar</a>, <a href="http://synwrite.sourceforge.net/forums/viewtopic.php?f=19&t=865">readme</a>),
<li><a href="http://liveditor.com/">LIVEditor</a>, a live-editing HTML/CSS/JS IDE (commercial, Windows-only) uses the library,
<li><a href="https://github.com/Diogenesthecynic/FullScreenMario-JSON/">FullScreenMario</a>, a HTML5/javascript remake of Super Mario Bros game, uses the library in its level editor.
<li class="writeme">Doing anything interesting? Write me to einar@jsbeautifier.org and I'll include your link.</li>
</ul>
<p class="contributors contributor-sep">Written by Einar Lielmanis, <a href="mailto:einar@jsbeautifier.org">einar@jsbeautifier.org</a>, maintained and evolved by <a href="https://github.com/bitwiseman/">Liam Newman</a>.</p>
<p class="contributors">We use the wonderful <a href="http://codemirror.net">CodeMirror</a> syntax highlighting editor, written by Marijn Haverbeke.
</p>
<p class="contributors">Made with a great help of Jason&nbsp;Diamond, Patrick&nbsp;Hof, Nochum&nbsp;Sossonko, Andreas&nbsp;Schneider,
<br>Dave&nbsp;Vasilevsky,
<a href="http://my.opera.com/Vital/blog/">Vital&nbsp;Batmanov</a>, Ron&nbsp;Baldwin, Gabriel&nbsp;Harrison,
<a href="http://shullian.com">Chris J. Shull</a>,
<a href="http://mathiasbynens.be/">Mathias Bynens</a>,
<br>
<a href="https://www.vittgam.net/">Vittorio Gambaletta</a>,
<a href="https://github.com/esseks">Stefano Sanfilippo</a> and
<a href="https://github.com/evocateur">Daniel Stockman</a>.
</p>
<p style="text-align:right">
<a href="#" style="color: #ccc; border-bottom: 1px dashed #ccc; text-decoration: none;" onclick="run_tests(); return false;">Run the tests</a>
</p>
</div>
<div id="testresults"></div>
<script>
$(function () {
read_settings_from_cookie();
var default_text =
"// This is just a sample script. Paste your real code (javascript or HTML) here.\n\nif ('this_is'==/an_example/){of_beautifer();}else{var a=b?(c%d):e[f];}";
var textArea = $('#source')[0];
if (the.use_codemirror && typeof CodeMirror !== 'undefined') {
the.editor = CodeMirror.fromTextArea(textArea, {
theme: 'default',
lineNumbers: true
});
the.editor.focus();
the.editor.setValue(default_text);
$('.CodeMirror').click(function () {
if (the.editor.getValue() == default_text) {
the.editor.setValue('');
}
});
} else {
$('#source').val(default_text).bind('click focus', function () {
if ($(this).val() == default_text) {
$(this).val('');
}
}).bind('blur', function () {
if (!$(this).val()) {
$(this).val(default_text);
}
});
}
$(window).bind('keydown', function (e) {
if (e.ctrlKey && e.keyCode == 13) {
beautify();
}
})
$('.submit').click(beautify);
$('select').change(beautify);
});
</script>
<script>
var _gaq = [
['_setAccount', 'UA-7409939-1'],
['_trackPageview']
];
(function (d, t) {
var g = d.createElement(t),
s = d.getElementsByTagName(t)[0];
g.src = '//www.google-analytics.com/ga.js';
s.parentNode.insertBefore(g, s);
}(document, 'script'));
</script>
</body>
</html>

View file

@ -0,0 +1,4 @@
#!/usr/bin/env node
var cli = require('../lib/cli'); cli.interpret();

View file

@ -0,0 +1,4 @@
#!/usr/bin/env node
var cli = require('../lib/cli'); cli.interpret();

View file

@ -0,0 +1,4 @@
#!/usr/bin/env node
var cli = require('../lib/cli');
cli.interpret();

View file

@ -0,0 +1,18 @@
{
"indent_size": 4,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": false,
"space_after_anon_function": false,
"brace_style": "collapse",
"keep_array_indentation": false,
"keep_function_indentation": false,
"space_before_conditional": true,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
}

View file

@ -0,0 +1,55 @@
/**
The following batches are equivalent:
var beautify_js = require('js-beautify');
var beautify_js = require('js-beautify').js;
var beautify_js = require('js-beautify').js_beautify;
var beautify_css = require('js-beautify').css;
var beautify_css = require('js-beautify').css_beautify;
var beautify_html = require('js-beautify').html;
var beautify_html = require('js-beautify').html_beautify;
All methods returned accept two arguments, the source string and an options object.
**/
function get_beautify(js_beautify, css_beautify, html_beautify) {
// the default is js
var beautify = function (src, config) {
return js_beautify.js_beautify(src, config);
};
// short aliases
beautify.js = js_beautify.js_beautify;
beautify.css = css_beautify.css_beautify;
beautify.html = html_beautify.html_beautify;
// legacy aliases
beautify.js_beautify = js_beautify.js_beautify;
beautify.css_beautify = css_beautify.css_beautify;
beautify.html_beautify = html_beautify.html_beautify;
return beautify;
}
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([
"./lib/beautify",
"./lib/beautify-css",
"./lib/beautify-html"
], function(js_beautify, css_beautify, html_beautify) {
return get_beautify(js_beautify, css_beautify, html_beautify);
});
} else {
(function(mod) {
var js_beautify = require('./lib/beautify');
var css_beautify = require('./lib/beautify-css');
var html_beautify = require('./lib/beautify-html');
mod.exports = get_beautify(js_beautify, css_beautify, html_beautify);
})(module);
}

View file

@ -0,0 +1,491 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
CSS Beautifier
---------------
Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Usage:
css_beautify(source_text);
css_beautify(source_text, options);
The options are (default in brackets):
indent_size (4) — indentation size,
indent_char (space) — character to indent with,
selector_separator_newline (true) - separate selectors with newline or
not (e.g. "a,\nbr" or "a, br")
end_with_newline (false) - end with a newline
newline_between_rules (true) - add a new line after every css rule
e.g
css_beautify(css_source_text, {
'indent_size': 1,
'indent_char': '\t',
'selector_separator': ' ',
'end_with_newline': false,
'newline_between_rules': true
});
*/
// http://www.w3.org/TR/CSS21/syndata.html#tokenization
// http://www.w3.org/TR/css3-syntax/
(function() {
function css_beautify(source_text, options) {
options = options || {};
source_text = source_text || '';
// HACK: newline parsing inconsistent. This brute force normalizes the input.
source_text = source_text.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
var indentSize = options.indent_size || 4;
var indentCharacter = options.indent_char || ' ';
var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
var eol = options.eol ? options.eol : '\n';
// compatibility
if (typeof indentSize === "string") {
indentSize = parseInt(indentSize, 10);
}
if(options.indent_with_tabs){
indentCharacter = '\t';
indentSize = 1;
}
eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
// tokenizer
var whiteRe = /^\s+$/;
var wordRe = /[\w$\-_]/;
var pos = -1,
ch;
var parenLevel = 0;
function next() {
ch = source_text.charAt(++pos);
return ch || '';
}
function peek(skipWhitespace) {
var result = '';
var prev_pos = pos;
if (skipWhitespace) {
eatWhitespace();
}
result = source_text.charAt(pos + 1) || '';
pos = prev_pos - 1;
next();
return result;
}
function eatString(endChars) {
var start = pos;
while (next()) {
if (ch === "\\") {
next();
} else if (endChars.indexOf(ch) !== -1) {
break;
} else if (ch === "\n") {
break;
}
}
return source_text.substring(start, pos + 1);
}
function peekString(endChar) {
var prev_pos = pos;
var str = eatString(endChar);
pos = prev_pos - 1;
next();
return str;
}
function eatWhitespace() {
var result = '';
while (whiteRe.test(peek())) {
next();
result += ch;
}
return result;
}
function skipWhitespace() {
var result = '';
if (ch && whiteRe.test(ch)) {
result = ch;
}
while (whiteRe.test(next())) {
result += ch;
}
return result;
}
function eatComment(singleLine) {
var start = pos;
singleLine = peek() === "/";
next();
while (next()) {
if (!singleLine && ch === "*" && peek() === "/") {
next();
break;
} else if (singleLine && ch === "\n") {
return source_text.substring(start, pos);
}
}
return source_text.substring(start, pos) + ch;
}
function lookBack(str) {
return source_text.substring(pos - str.length, pos).toLowerCase() ===
str;
}
// Nested pseudo-class if we are insideRule
// and the next special character found opens
// a new block
function foundNestedPseudoClass() {
var openParen = 0;
for (var i = pos + 1; i < source_text.length; i++) {
var ch = source_text.charAt(i);
if (ch === "{") {
return true;
} else if (ch === '(') {
// pseudoclasses can contain ()
openParen += 1;
} else if (ch === ')') {
if (openParen == 0) {
return false;
}
openParen -= 1;
} else if (ch === ";" || ch === "}") {
return false;
}
}
return false;
}
// printer
var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
var singleIndent = new Array(indentSize + 1).join(indentCharacter);
var indentLevel = 0;
var nestedLevel = 0;
function indent() {
indentLevel++;
basebaseIndentString += singleIndent;
}
function outdent() {
indentLevel--;
basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
}
var print = {};
print["{"] = function(ch) {
print.singleSpace();
output.push(ch);
print.newLine();
};
print["}"] = function(ch) {
print.newLine();
output.push(ch);
print.newLine();
};
print._lastCharWhitespace = function() {
return whiteRe.test(output[output.length - 1]);
};
print.newLine = function(keepWhitespace) {
if (output.length) {
if (!keepWhitespace && output[output.length - 1] !== '\n') {
print.trim();
}
output.push('\n');
if (basebaseIndentString) {
output.push(basebaseIndentString);
}
}
};
print.singleSpace = function() {
if (output.length && !print._lastCharWhitespace()) {
output.push(' ');
}
};
print.preserveSingleSpace = function() {
if (isAfterSpace) {
print.singleSpace();
}
};
print.trim = function() {
while (print._lastCharWhitespace()) {
output.pop();
}
};
var output = [];
/*_____________________--------------------_____________________*/
var insideRule = false;
var insidePropertyValue = false;
var enteringConditionalGroup = false;
var top_ch = '';
var last_top_ch = '';
while (true) {
var whitespace = skipWhitespace();
var isAfterSpace = whitespace !== '';
var isAfterNewline = whitespace.indexOf('\n') !== -1;
last_top_ch = top_ch;
top_ch = ch;
if (!ch) {
break;
} else if (ch === '/' && peek() === '*') { /* css comment */
var header = indentLevel === 0;
if (isAfterNewline || header) {
print.newLine();
}
output.push(eatComment());
print.newLine();
if (header) {
print.newLine(true);
}
} else if (ch === '/' && peek() === '/') { // single line comment
if (!isAfterNewline && last_top_ch !== '{' ) {
print.trim();
}
print.singleSpace();
output.push(eatComment());
print.newLine();
} else if (ch === '@') {
print.preserveSingleSpace();
output.push(ch);
// strip trailing space, if present, for hash property checks
var variableOrRule = peekString(": ,;{}()[]/='\"");
if (variableOrRule.match(/[ :]$/)) {
// we have a variable or pseudo-class, add it and insert one space before continuing
next();
variableOrRule = eatString(": ").replace(/\s$/, '');
output.push(variableOrRule);
print.singleSpace();
}
variableOrRule = variableOrRule.replace(/\s$/, '')
// might be a nesting at-rule
if (variableOrRule in css_beautify.NESTED_AT_RULE) {
nestedLevel += 1;
if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) {
enteringConditionalGroup = true;
}
}
} else if (ch === '#' && peek() === '{') {
print.preserveSingleSpace();
output.push(eatString('}'));
} else if (ch === '{') {
if (peek(true) === '}') {
eatWhitespace();
next();
print.singleSpace();
output.push("{}");
print.newLine();
if (newline_between_rules && indentLevel === 0) {
print.newLine(true);
}
} else {
indent();
print["{"](ch);
// when entering conditional groups, only rulesets are allowed
if (enteringConditionalGroup) {
enteringConditionalGroup = false;
insideRule = (indentLevel > nestedLevel);
} else {
// otherwise, declarations are also allowed
insideRule = (indentLevel >= nestedLevel);
}
}
} else if (ch === '}') {
outdent();
print["}"](ch);
insideRule = false;
insidePropertyValue = false;
if (nestedLevel) {
nestedLevel--;
}
if (newline_between_rules && indentLevel === 0) {
print.newLine(true);
}
} else if (ch === ":") {
eatWhitespace();
if ((insideRule || enteringConditionalGroup) &&
!(lookBack("&") || foundNestedPseudoClass())) {
// 'property: value' delimiter
// which could be in a conditional group query
insidePropertyValue = true;
output.push(':');
print.singleSpace();
} else {
// sass/less parent reference don't use a space
// sass nested pseudo-class don't use a space
if (peek() === ":") {
// pseudo-element
next();
output.push("::");
} else {
// pseudo-class
output.push(':');
}
}
} else if (ch === '"' || ch === '\'') {
print.preserveSingleSpace();
output.push(eatString(ch));
} else if (ch === ';') {
insidePropertyValue = false;
output.push(ch);
print.newLine();
} else if (ch === '(') { // may be a url
if (lookBack("url")) {
output.push(ch);
eatWhitespace();
if (next()) {
if (ch !== ')' && ch !== '"' && ch !== '\'') {
output.push(eatString(')'));
} else {
pos--;
}
}
} else {
parenLevel++;
print.preserveSingleSpace();
output.push(ch);
eatWhitespace();
}
} else if (ch === ')') {
output.push(ch);
parenLevel--;
} else if (ch === ',') {
output.push(ch);
eatWhitespace();
if (selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
print.newLine();
} else {
print.singleSpace();
}
} else if (ch === ']') {
output.push(ch);
} else if (ch === '[') {
print.preserveSingleSpace();
output.push(ch);
} else if (ch === '=') { // no whitespace before or after
eatWhitespace()
ch = '=';
output.push(ch);
} else {
print.preserveSingleSpace();
output.push(ch);
}
}
var sweetCode = '';
if (basebaseIndentString) {
sweetCode += basebaseIndentString;
}
sweetCode += output.join('').replace(/[\r\n\t ]+$/, '');
// establish end_with_newline
if (end_with_newline) {
sweetCode += '\n';
}
if (eol != '\n') {
sweetCode = sweetCode.replace(/[\n]/g, eol);
}
return sweetCode;
}
// https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
css_beautify.NESTED_AT_RULE = {
"@page": true,
"@font-face": true,
"@keyframes": true,
// also in CONDITIONAL_GROUP_RULE below
"@media": true,
"@supports": true,
"@document": true
};
css_beautify.CONDITIONAL_GROUP_RULE = {
"@media": true,
"@supports": true,
"@document": true
};
/*global define */
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define([], function() {
return {
css_beautify: css_beautify
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
exports.css_beautify = css_beautify;
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.css_beautify = css_beautify;
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.css_beautify = css_beautify;
}
}());

View file

@ -0,0 +1,952 @@
/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Style HTML
---------------
Written by Nochum Sossonko, (nsossonko@hotmail.com)
Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
http://jsbeautifier.org/
Usage:
style_html(html_source);
style_html(html_source, options);
The options are:
indent_inner_html (default false) — indent <head> and <body> sections,
indent_size (default 4) — indentation size,
indent_char (default space) — character to indent with,
wrap_line_length (default 250) - maximum amount of characters per line (0 = disable)
brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none"
put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line, or attempt to keep them where they are.
unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
indent_scripts (default normal) - "keep"|"separate"|"normal"
preserve_newlines (default true) - whether existing line breaks before elements should be preserved
Only works before elements, not inside tags or for text.
max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
end_with_newline (false) - end with a newline
extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them.
e.g.
style_html(html_source, {
'indent_inner_html': false,
'indent_size': 2,
'indent_char': ' ',
'wrap_line_length': 78,
'brace_style': 'expand',
'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u'],
'preserve_newlines': true,
'max_preserve_newlines': 5,
'indent_handlebars': false,
'extra_liners': ['/html']
});
*/
(function() {
function trim(s) {
return s.replace(/^\s+|\s+$/g, '');
}
function ltrim(s) {
return s.replace(/^\s+/g, '');
}
function rtrim(s) {
return s.replace(/\s+$/g,'');
}
function style_html(html_source, options, js_beautify, css_beautify) {
//Wrapper function to invoke all the necessary constructors and deal with the output.
var multi_parser,
indent_inner_html,
indent_size,
indent_character,
wrap_line_length,
brace_style,
unformatted,
preserve_newlines,
max_preserve_newlines,
indent_handlebars,
wrap_attributes,
wrap_attributes_indent_size,
end_with_newline,
extra_liners,
eol;
options = options || {};
// backwards compatibility to 1.3.4
if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
(options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
options.wrap_line_length = options.max_char;
}
indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
unformatted = options.unformatted || ['a', 'span', 'img', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd',
'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike',
'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
max_preserve_newlines = preserve_newlines ?
(isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10))
: 0;
indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
wrap_attributes_indent_size = (options.wrap_attributes_indent_size === undefined) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10) || indent_size;
end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
extra_liners = (typeof options.extra_liners == 'object') && options.extra_liners ?
options.extra_liners.concat() : (typeof options.extra_liners === 'string') ?
options.extra_liners.split(',') : 'head,body,/html'.split(',');
eol = options.eol ? options.eol : '\n';
if(options.indent_with_tabs){
indent_character = '\t';
indent_size = 1;
}
eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n')
function Parser() {
this.pos = 0; //Parser position
this.token = '';
this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
parent: 'parent1',
parentcount: 1,
parent1: ''
};
this.tag_type = '';
this.token_text = this.last_token = this.last_text = this.token_type = '';
this.newlines = 0;
this.indent_content = indent_inner_html;
this.Utils = { //Uilities made available to the various functions
whitespace: "\n\r\t ".split(''),
single_token: 'br,input,link,meta,source,!doctype,basefont,base,area,hr,wbr,param,img,isindex,embed'.split(','), //all the single tags for HTML
extra_liners: extra_liners, //for tags that need a line of whitespace before them
in_array: function(what, arr) {
for (var i = 0; i < arr.length; i++) {
if (what === arr[i]) {
return true;
}
}
return false;
}
};
// Return true if the given text is composed entirely of whitespace.
this.is_whitespace = function(text) {
for (var n = 0; n < text.length; text++) {
if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
return false;
}
}
return true;
};
this.traverse_whitespace = function() {
var input_char = '';
input_char = this.input.charAt(this.pos);
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
this.newlines = 0;
while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
this.newlines += 1;
}
this.pos++;
input_char = this.input.charAt(this.pos);
}
return true;
}
return false;
};
// Append a space to the given content (string array) or, if we are
// at the wrap_line_length, append a newline/indentation.
this.space_or_wrap = function(content) {
if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
this.print_newline(false, content);
this.print_indentation(content);
} else {
this.line_char_count++;
content.push(' ');
}
};
this.get_content = function() { //function to capture regular content between tags
var input_char = '',
content = [],
space = false; //if a space is needed
while (this.input.charAt(this.pos) !== '<') {
if (this.pos >= this.input.length) {
return content.length ? content.join('') : ['', 'TK_EOF'];
}
if (this.traverse_whitespace()) {
this.space_or_wrap(content);
continue;
}
if (indent_handlebars) {
// Handlebars parsing is complicated.
// {{#foo}} and {{/foo}} are formatted tags.
// {{something}} should get treated as content, except:
// {{else}} specifically behaves like {{#if}} and {{/if}}
var peek3 = this.input.substr(this.pos, 3);
if (peek3 === '{{#' || peek3 === '{{/') {
// These are tags and not content.
break;
} else if (peek3 === '{{!') {
return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
} else if (this.input.substr(this.pos, 2) === '{{') {
if (this.get_tag(true) === '{{else}}') {
break;
}
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
this.line_char_count++;
content.push(input_char); //letter at-a-time (or string) inserted to an array
}
return content.length ? content.join('') : '';
};
this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
if (this.pos === this.input.length) {
return ['', 'TK_EOF'];
}
var input_char = '';
var content = '';
var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
reg_match.lastIndex = this.pos;
var reg_array = reg_match.exec(this.input);
var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
if (this.pos < end_script) { //get everything in between the script tags
content = this.input.substring(this.pos, end_script);
this.pos = end_script;
}
return content;
};
this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
if (this.tags[tag + 'count']) { //check for the existence of this tag type
this.tags[tag + 'count']++;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
} else { //otherwise initialize this tag type
this.tags[tag + 'count'] = 1;
this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
}
this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
};
this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
var temp_parent = this.tags.parent; //check to see if it's a closable tag.
while (temp_parent) { //till we reach '' (the initial value);
if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
break;
}
temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
}
if (temp_parent) { //if we caught something
this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
}
delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
if (this.tags[tag + 'count'] === 1) {
delete this.tags[tag + 'count'];
} else {
this.tags[tag + 'count']--;
}
}
};
this.indent_to_tag = function(tag) {
// Match the indentation level to the last use of this tag, but don't remove it.
if (!this.tags[tag + 'count']) {
return;
}
var temp_parent = this.tags.parent;
while (temp_parent) {
if (tag + this.tags[tag + 'count'] === temp_parent) {
break;
}
temp_parent = this.tags[temp_parent + 'parent'];
}
if (temp_parent) {
this.indent_level = this.tags[tag + this.tags[tag + 'count']];
}
};
this.get_tag = function(peek) { //function to get a full tag and parse its type
var input_char = '',
content = [],
comment = '',
space = false,
first_attr = true,
tag_start, tag_end,
tag_start_char,
orig_pos = this.pos,
orig_line_char_count = this.line_char_count;
peek = peek !== undefined ? peek : false;
do {
if (this.pos >= this.input.length) {
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.length ? content.join('') : ['', 'TK_EOF'];
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
space = true;
continue;
}
if (input_char === "'" || input_char === '"') {
input_char += this.get_unformatted(input_char);
space = true;
}
if (input_char === '=') { //no space before =
space = false;
}
if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
//no space after = or before >
this.space_or_wrap(content);
space = false;
if (!first_attr && wrap_attributes === 'force' && input_char !== '/') {
this.print_newline(true, content);
this.print_indentation(content);
for (var count = 0; count < wrap_attributes_indent_size; count++) {
content.push(indent_character);
}
}
for (var i = 0; i < content.length; i++) {
if (content[i] === ' ') {
first_attr = false;
break;
}
}
}
if (indent_handlebars && tag_start_char === '<') {
// When inside an angle-bracket tag, put spaces around
// handlebars not inside of strings.
if ((input_char + this.input.charAt(this.pos)) === '{{') {
input_char += this.get_unformatted('}}');
if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
input_char = ' ' + input_char;
}
space = true;
}
}
if (input_char === '<' && !tag_start_char) {
tag_start = this.pos - 1;
tag_start_char = '<';
}
if (indent_handlebars && !tag_start_char) {
if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') {
if (input_char === '#' || input_char === '/' || input_char === '!') {
tag_start = this.pos - 3;
} else {
tag_start = this.pos - 2;
}
tag_start_char = '{';
}
}
this.line_char_count++;
content.push(input_char); //inserts character at-a-time (or string)
if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special
// We treat all comments as literals, even more than preformatted tags
// we just look for the appropriate close tag
content = [this.get_comment(tag_start)];
break;
}
if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
break;
}
} while (input_char !== '>');
var tag_complete = content.join('');
var tag_index;
var tag_offset;
if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
tag_index = tag_complete.indexOf(' ');
} else if (tag_complete.charAt(0) === '{') {
tag_index = tag_complete.indexOf('}');
} else { //otherwise go with the tag ending
tag_index = tag_complete.indexOf('>');
}
if (tag_complete.charAt(0) === '<' || !indent_handlebars) {
tag_offset = 1;
} else {
tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2;
}
var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
if (!peek) {
this.tag_type = 'SINGLE';
}
} else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') {
if (!peek) {
this.indent_to_tag('if');
this.tag_type = 'HANDLEBARS_ELSE';
this.indent_content = true;
this.traverse_whitespace();
}
} else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
content.push(comment);
tag_end = this.pos - 1;
this.tag_type = 'SINGLE';
} else if (tag_check === 'script' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 &&
tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'SCRIPT';
}
} else if (tag_check === 'style' &&
(tag_complete.search('type') === -1 ||
(tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
if (!peek) {
this.record_tag(tag_check);
this.tag_type = 'STYLE';
}
} else if (tag_check.charAt(0) === '!') { //peek for <! comment
// for comments content is already correct.
if (!peek) {
this.tag_type = 'SINGLE';
this.traverse_whitespace();
}
} else if (!peek) {
if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
this.tag_type = 'END';
} else { //otherwise it's a start-tag
this.record_tag(tag_check); //push it on the tag stack
if (tag_check.toLowerCase() !== 'html') {
this.indent_content = true;
}
this.tag_type = 'START';
}
// Allow preserving of newlines after a start or end tag
if (this.traverse_whitespace()) {
this.space_or_wrap(content);
}
if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
this.print_newline(false, this.output);
if (this.output.length && this.output[this.output.length - 2] !== '\n') {
this.print_newline(true, this.output);
}
}
}
if (peek) {
this.pos = orig_pos;
this.line_char_count = orig_line_char_count;
}
return content.join(''); //returns fully formatted tag
};
this.get_comment = function(start_pos) { //function to return comment content in its entirety
// this is will have very poor perf, but will work for now.
var comment = '',
delimiter = '>',
matched = false;
this.pos = start_pos;
input_char = this.input.charAt(this.pos);
this.pos++;
while (this.pos <= this.input.length) {
comment += input_char;
// only need to check for the delimiter if the last chars match
if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) &&
comment.indexOf(delimiter) !== -1) {
break;
}
// only need to search for custom delimiter for the first few characters
if (!matched && comment.length < 10) {
if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
delimiter = '<![endif]>';
matched = true;
} else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
delimiter = ']]>';
matched = true;
} else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
delimiter = ']>';
matched = true;
} else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
delimiter = '-->';
matched = true;
} else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
delimiter = '}}';
matched = true;
} else if (comment.indexOf('<?') === 0) { // {{! handlebars comment
delimiter = '?>';
matched = true;
} else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
delimiter = '%>';
matched = true;
}
}
input_char = this.input.charAt(this.pos);
this.pos++;
}
return comment;
};
this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
return '';
}
var input_char = '';
var content = '';
var min_index = 0;
var space = true;
do {
if (this.pos >= this.input.length) {
return content;
}
input_char = this.input.charAt(this.pos);
this.pos++;
if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
if (!space) {
this.line_char_count--;
continue;
}
if (input_char === '\n' || input_char === '\r') {
content += '\n';
/* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
for (var i=0; i<this.indent_level; i++) {
content += this.indent_string;
}
space = false; //...and make sure other indentation is erased
*/
this.line_char_count = 0;
continue;
}
}
content += input_char;
this.line_char_count++;
space = true;
if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') {
// Handlebars expressions in strings should also be unformatted.
content += this.get_unformatted('}}');
// These expressions are opaque. Ignore delimiters found in them.
min_index = content.length;
}
} while (content.toLowerCase().indexOf(delimiter, min_index) === -1);
return content;
};
this.get_token = function() { //initial handler for token-retrieval
var token;
if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
var type = this.last_token.substr(7);
token = this.get_contents_to(type);
if (typeof token !== 'string') {
return token;
}
return [token, 'TK_' + type];
}
if (this.current_mode === 'CONTENT') {
token = this.get_content();
if (typeof token !== 'string') {
return token;
} else {
return [token, 'TK_CONTENT'];
}
}
if (this.current_mode === 'TAG') {
token = this.get_tag();
if (typeof token !== 'string') {
return token;
} else {
var tag_name_type = 'TK_TAG_' + this.tag_type;
return [token, tag_name_type];
}
}
};
this.get_full_indent = function(level) {
level = this.indent_level + level || 0;
if (level < 1) {
return '';
}
return Array(level + 1).join(this.indent_string);
};
this.is_unformatted = function(tag_check, unformatted) {
//is this an HTML5 block-level link?
if (!this.Utils.in_array(tag_check, unformatted)) {
return false;
}
if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
return true;
}
//at this point we have an tag; is its first child something we want to remain
//unformatted?
var next_tag = this.get_tag(true /* peek. */ );
// test next_tag to see if it is just html tag (no external content)
var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
// if next_tag comes back but is not an isolated tag, then
// let's treat the 'a' tag as having content
// and respect the unformatted option
if (!tag || this.Utils.in_array(tag, unformatted)) {
return true;
} else {
return false;
}
};
this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
this.input = js_source || ''; //gets the input for the Parser
// HACK: newline parsing inconsistent. This brute force normalizes the input.
this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n')
this.output = [];
this.indent_character = indent_character;
this.indent_string = '';
this.indent_size = indent_size;
this.brace_style = brace_style;
this.indent_level = 0;
this.wrap_line_length = wrap_line_length;
this.line_char_count = 0; //count to see if wrap_line_length was exceeded
for (var i = 0; i < this.indent_size; i++) {
this.indent_string += this.indent_character;
}
this.print_newline = function(force, arr) {
this.line_char_count = 0;
if (!arr || !arr.length) {
return;
}
if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
if ((arr[arr.length - 1] !== '\n')) {
arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
}
arr.push('\n');
}
};
this.print_indentation = function(arr) {
for (var i = 0; i < this.indent_level; i++) {
arr.push(this.indent_string);
this.line_char_count += this.indent_string.length;
}
};
this.print_token = function(text) {
// Avoid printing initial whitespace.
if (this.is_whitespace(text) && !this.output.length) {
return;
}
if (text || text !== '') {
if (this.output.length && this.output[this.output.length - 1] === '\n') {
this.print_indentation(this.output);
text = ltrim(text);
}
}
this.print_token_raw(text);
};
this.print_token_raw = function(text) {
// If we are going to print newlines, truncate trailing
// whitespace, as the newlines will represent the space.
if (this.newlines > 0) {
text = rtrim(text);
}
if (text && text !== '') {
if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
// unformatted tags can grab newlines as their last character
this.output.push(text.slice(0, -1));
this.print_newline(false, this.output);
} else {
this.output.push(text);
}
}
for (var n = 0; n < this.newlines; n++) {
this.print_newline(n > 0, this.output);
}
this.newlines = 0;
};
this.indent = function() {
this.indent_level++;
};
this.unindent = function() {
if (this.indent_level > 0) {
this.indent_level--;
}
};
};
return this;
}
/*_____________________--------------------_____________________*/
multi_parser = new Parser(); //wrapping functions Parser
multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
while (true) {
var t = multi_parser.get_token();
multi_parser.token_text = t[0];
multi_parser.token_type = t[1];
if (multi_parser.token_type === 'TK_EOF') {
break;
}
switch (multi_parser.token_type) {
case 'TK_TAG_START':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_STYLE':
case 'TK_TAG_SCRIPT':
multi_parser.print_newline(false, multi_parser.output);
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_END':
//Print new line only if the tag has no content and has child
if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
var tag_name = multi_parser.token_text.match(/\w+/)[0];
var tag_extracted_from_last_output = null;
if (multi_parser.output.length) {
tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
}
if (tag_extracted_from_last_output === null ||
(tag_extracted_from_last_output[1] !== tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
multi_parser.print_newline(false, multi_parser.output);
}
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_SINGLE':
// Don't add a newline before elements that should remain unformatted.
var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
multi_parser.print_newline(false, multi_parser.output);
}
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_ELSE':
multi_parser.print_token(multi_parser.token_text);
if (multi_parser.indent_content) {
multi_parser.indent();
multi_parser.indent_content = false;
}
multi_parser.current_mode = 'CONTENT';
break;
case 'TK_TAG_HANDLEBARS_COMMENT':
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'TAG';
break;
case 'TK_CONTENT':
multi_parser.print_token(multi_parser.token_text);
multi_parser.current_mode = 'TAG';
break;
case 'TK_STYLE':
case 'TK_SCRIPT':
if (multi_parser.token_text !== '') {
multi_parser.print_newline(false, multi_parser.output);
var text = multi_parser.token_text,
_beautifier,
script_indent_level = 1;
if (multi_parser.token_type === 'TK_SCRIPT') {
_beautifier = typeof js_beautify === 'function' && js_beautify;
} else if (multi_parser.token_type === 'TK_STYLE') {
_beautifier = typeof css_beautify === 'function' && css_beautify;
}
if (options.indent_scripts === "keep") {
script_indent_level = 0;
} else if (options.indent_scripts === "separate") {
script_indent_level = -multi_parser.indent_level;
}
var indentation = multi_parser.get_full_indent(script_indent_level);
if (_beautifier) {
// call the Beautifier if avaliable
var Child_options = function() {
this.eol = '\n';
};
Child_options.prototype = options;
var child_options = new Child_options();
text = _beautifier(text.replace(/^\s*/, indentation), child_options);
} else {
// simply indent the string otherwise
var white = text.match(/^\s*/)[0];
var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
var reindent = multi_parser.get_full_indent(script_indent_level - _level);
text = text.replace(/^\s*/, indentation)
.replace(/\r\n|\r|\n/g, '\n' + reindent)
.replace(/\s+$/, '');
}
if (text) {
multi_parser.print_token_raw(text);
multi_parser.print_newline(true, multi_parser.output);
}
}
multi_parser.current_mode = 'TAG';
break;
default:
// We should not be getting here but we don't want to drop input on the floor
// Just output the text and move on
if (multi_parser.token_text !== '') {
multi_parser.print_token(multi_parser.token_text);
}
break;
}
multi_parser.last_token = multi_parser.token_type;
multi_parser.last_text = multi_parser.token_text;
}
var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
// establish end_with_newline
if (end_with_newline) {
sweet_code += '\n';
}
if (eol != '\n') {
sweet_code = sweet_code.replace(/[\n]/g, eol);
}
return sweet_code;
}
if (typeof define === "function" && define.amd) {
// Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
define(["require", "./beautify", "./beautify-css"], function(requireamd) {
var js_beautify = requireamd("./beautify");
var css_beautify = requireamd("./beautify-css");
return {
html_beautify: function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
}
};
});
} else if (typeof exports !== "undefined") {
// Add support for CommonJS. Just put this file somewhere on your require.paths
// and you will be able to `var html_beautify = require("beautify").html_beautify`.
var js_beautify = require('./beautify.js');
var css_beautify = require('./beautify-css.js');
exports.html_beautify = function(html_source, options) {
return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
};
} else if (typeof window !== "undefined") {
// If we're running a web page and don't have either of the above, add our one global
window.html_beautify = function(html_source, options) {
return style_html(html_source, options, window.js_beautify, window.css_beautify);
};
} else if (typeof global !== "undefined") {
// If we don't even have window, try global.
global.html_beautify = function(html_source, options) {
return style_html(html_source, options, global.js_beautify, global.css_beautify);
};
}
}());

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
#!/usr/bin/env node
/*
The MIT License (MIT)
Copyright (c) 2007-2013 Einar Lielmanis and contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Js-Beautify Command-line for node.js
-------------------------------------
Written by Daniel Stockman (daniel.stockman@gmail.com)
*/
var debug = process.env.DEBUG_JSBEAUTIFY || process.env.JSBEAUTIFY_DEBUG ? function() {
console.error.apply(console, arguments);
} : function() {};
var fs = require('fs'),
cc = require('config-chain'),
beautify = require('../index'),
mkdirp = require('mkdirp'),
nopt = require('nopt'),
path = require('path'),
knownOpts = {
// Beautifier
"indent_size": Number,
"indent_char": String,
"eol": String,
"indent_level": Number,
"indent_with_tabs": Boolean,
"preserve_newlines": Boolean,
"max_preserve_newlines": Number,
"space_in_paren": Boolean,
"space_in_empty_paren": Boolean,
"jslint_happy": Boolean,
"space_after_anon_function": Boolean,
// TODO: expand-strict is obsolete, now identical to expand. Remove in future version
"brace_style": ["collapse", "expand", "end-expand", "expand-strict", "none"],
"break_chained_methods": Boolean,
"keep_array_indentation": Boolean,
"unescape_strings": Boolean,
"wrap_line_length": Number,
"wrap_attributes": ["auto", "force"],
"wrap_attributes_indent_size": Number,
"e4x": Boolean,
"end_with_newline": Boolean,
"comma_first": Boolean,
// CSS-only
"selector_separator_newline": Boolean,
"newline_between_rules": Boolean,
// HTML-only
"max_char": Number, // obsolete since 1.3.5
"unformatted": [String, Array],
"indent_inner_html": [Boolean],
"indent_scripts": ["keep", "separate", "normal"],
"extra_liners": [String, Array],
// CLI
"version": Boolean,
"help": Boolean,
"files": [path, Array],
"outfile": path,
"replace": Boolean,
"quiet": Boolean,
"type": ["js", "css", "html"],
"config": path
},
// dasherizeShorthands provides { "indent-size": ["--indent_size"] }
// translation, allowing more convenient dashes in CLI arguments
shortHands = dasherizeShorthands({
// Beautifier
"s": ["--indent_size"],
"c": ["--indent_char"],
"e": ["--eol"],
"l": ["--indent_level"],
"t": ["--indent_with_tabs"],
"p": ["--preserve_newlines"],
"m": ["--max_preserve_newlines"],
"P": ["--space_in_paren"],
"E": ["--space_in_empty_paren"],
"j": ["--jslint_happy"],
"a": ["--space_after_anon_function"],
"b": ["--brace_style"],
"B": ["--break_chained_methods"],
"k": ["--keep_array_indentation"],
"x": ["--unescape_strings"],
"w": ["--wrap_line_length"],
"X": ["--e4x"],
"n": ["--end_with_newline"],
"C": ["--comma_first"],
// CSS-only
"L": ["--selector_separator_newline"],
"N": ["--newline_between_rules"],
// HTML-only
"A": ["--wrap_attributes"],
"i": ["--wrap_attributes_indent_size"],
"W": ["--max_char"], // obsolete since 1.3.5
"U": ["--unformatted"],
"I": ["--indent_inner_html"],
"S": ["--indent_scripts"],
"E": ["--extra_liners"],
// non-dasherized hybrid shortcuts
"good-stuff": [
"--keep_array_indentation",
"--keep_function_indentation",
"--jslint_happy"
],
"js": ["--type", "js"],
"css": ["--type", "css"],
"html": ["--type", "html"],
// CLI
"v": ["--version"],
"h": ["--help"],
"f": ["--files"],
"o": ["--outfile"],
"r": ["--replace"],
"q": ["--quiet"]
// no shorthand for "config"
});
function verifyExists(fullPath) {
return fs.existsSync(fullPath) ? fullPath : null;
}
function findRecursive(dir, fileName) {
var fullPath = path.join(dir, fileName);
var nextDir = path.dirname(dir);
var result = verifyExists(fullPath);
if (!result && (nextDir !== dir)) {
result = findRecursive(nextDir, fileName);
}
return result;
}
function getUserHome() {
return process.env.HOME || process.env.USERPROFILE;
}
// var cli = require('js-beautify/cli'); cli.interpret();
var interpret = exports.interpret = function(argv, slice) {
var parsed = nopt(knownOpts, shortHands, argv, slice);
if (parsed.version) {
console.log(require('../../package.json').version);
process.exit(0);
} else if (parsed.help) {
usage();
process.exit(0);
}
var cfg = cc(
parsed,
cleanOptions(cc.env('jsbeautify_'), knownOpts),
parsed.config,
findRecursive(process.cwd(), '.jsbeautifyrc'),
verifyExists(path.join(getUserHome() || "", ".jsbeautifyrc")),
__dirname + '/../config/defaults.json'
).snapshot;
try {
// Verify arguments
checkType(cfg);
checkFiles(cfg);
debug(cfg);
// Process files synchronously to avoid EMFILE error
cfg.files.forEach(processInputSync, {
cfg: cfg
});
} catch (ex) {
debug(cfg);
// usage(ex);
console.error(ex);
console.error('Run `' + getScriptName() + ' -h` for help.');
process.exit(1);
}
};
// interpret args immediately when called as executable
if (require.main === module) {
interpret();
}
function usage(err) {
var scriptName = getScriptName();
var msg = [
scriptName + '@' + require('../../package.json').version,
'',
'CLI Options:',
' -f, --file Input file(s) (Pass \'-\' for stdin)',
' -r, --replace Write output in-place, replacing input',
' -o, --outfile Write output to file (default stdout)',
' --config Path to config file',
' --type [js|css|html] ["js"]',
' -q, --quiet Suppress logging to stdout',
' -h, --help Show this help',
' -v, --version Show the version',
'',
'Beautifier Options:',
' -s, --indent-size Indentation size [4]',
' -c, --indent-char Indentation character [" "]',
' -t, --indent-with-tabs Indent with tabs, overrides -s and -c',
' -e, --eol Character(s) to use as line terminators. (default newline - "\\n")',
' -n, --end-with-newline End output with newline'
];
switch (scriptName.split('-').shift()) {
case "js":
msg.push(' -l, --indent-level Initial indentation level [0]');
msg.push(' -p, --preserve-newlines Preserve line-breaks (--no-preserve-newlines disables)');
msg.push(' -m, --max-preserve-newlines Number of line-breaks to be preserved in one chunk [10]');
msg.push(' -P, --space-in-paren Add padding spaces within paren, ie. f( a, b )');
msg.push(' -E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )');
msg.push(' -j, --jslint-happy Enable jslint-stricter mode');
msg.push(' -a, --space-after-anon-function Add a space before an anonymous function\'s parens, ie. function ()');
msg.push(' -b, --brace-style [collapse|expand|end-expand|none] ["collapse"]');
msg.push(' -B, --break-chained-methods Break chained method calls across subsequent lines');
msg.push(' -k, --keep-array-indentation Preserve array indentation');
msg.push(' -x, --unescape-strings Decode printable characters encoded in xNN notation');
msg.push(' -w, --wrap-line-length Wrap lines at next opportunity after N characters [0]');
msg.push(' -X, --e4x Pass E4X xml literals through untouched');
msg.push(' --good-stuff Warm the cockles of Crockford\'s heart');
msg.push(' -C, --comma-first Put commas at the beginning of new line instead of end');
break;
case "html":
msg.push(' -b, --brace-style [collapse|expand|end-expand] ["collapse"]');
msg.push(' -I, --indent-inner-html Indent body and head sections. Default is false.');
msg.push(' -S, --indent-scripts [keep|separate|normal] ["normal"]');
msg.push(' -w, --wrap-line-length Wrap lines at next opportunity after N characters [0]');
msg.push(' -A, --wrap-attributes Wrap html tag attributes to new lines [auto|force] ["auto"]');
msg.push(' -i, --wrap-attributes-indent-size Indent wrapped tags to after N characters [indent-level]');
msg.push(' -p, --preserve-newlines Preserve line-breaks (--no-preserve-newlines disables)');
msg.push(' -m, --max-preserve-newlines Number of line-breaks to be preserved in one chunk [10]');
msg.push(' -U, --unformatted List of tags (defaults to inline) that should not be reformatted');
msg.push(' -E, --extra_liners List of tags (defaults to [head,body,/html] that should have an extra newline');
break;
case "css":
msg.push(' -L, --selector-separator-newline Add a newline between multiple selectors.')
msg.push(' -N, --newline-between-rules Add a newline between CSS rules.')
}
if (err) {
msg.push(err);
msg.push('');
console.error(msg.join('\n'));
} else {
console.log(msg.join('\n'));
}
}
// main iterator, {cfg} passed as thisArg of forEach call
function processInputSync(filepath) {
var data = '',
config = this.cfg,
outfile = config.outfile,
input;
// -o passed with no value overwrites
if (outfile === true || config.replace) {
outfile = filepath;
}
if (filepath === '-') {
input = process.stdin;
input.resume();
input.setEncoding('utf8');
input.on('data', function(chunk) {
data += chunk;
});
input.on('end', function() {
makePretty(data, config, outfile, writePretty);
});
} else {
var dir = path.dirname(outfile);
mkdirp.sync(dir);
data = fs.readFileSync(filepath, 'utf8');
makePretty(data, config, outfile, writePretty);
}
}
function makePretty(code, config, outfile, callback) {
try {
var fileType = getOutputType(outfile, config.type);
var pretty = beautify[fileType](code, config);
callback(null, pretty, outfile, config);
} catch (ex) {
callback(ex);
}
}
function writePretty(err, pretty, outfile, config) {
if (err) {
console.error(err);
process.exit(1);
}
if (outfile) {
if (isFileDifferent(outfile, pretty)) {
try {
fs.writeFileSync(outfile, pretty, 'utf8');
logToStdout('beautified ' + path.relative(process.cwd(), outfile), config);
} catch (ex) {
onOutputError(ex);
}
} else {
logToStdout('beautified ' + path.relative(process.cwd(), outfile) + ' - unchanged', config);
}
} else {
process.stdout.write(pretty);
}
}
function isFileDifferent(filePath, expected) {
try {
return fs.readFileSync(filePath, 'utf8') !== expected;
} catch (ex) {
// failing to read is the same as different
return true;
}
}
// workaround the fact that nopt.clean doesn't return the object passed in :P
function cleanOptions(data, types) {
nopt.clean(data, types);
return data;
}
// error handler for output stream that swallows errors silently,
// allowing the loop to continue over unwritable files.
function onOutputError(err) {
if (err.code === 'EACCES') {
console.error(err.path + " is not writable. Skipping!");
} else {
console.error(err);
process.exit(0);
}
}
// turn "--foo_bar" into "foo-bar"
function dasherizeFlag(str) {
return str.replace(/^\-+/, '').replace(/_/g, '-');
}
// translate weird python underscored keys into dashed argv,
// avoiding single character aliases.
function dasherizeShorthands(hash) {
// operate in-place
Object.keys(hash).forEach(function(key) {
// each key value is an array
var val = hash[key][0];
// only dasherize one-character shorthands
if (key.length === 1 && val.indexOf('_') > -1) {
hash[dasherizeFlag(val)] = val;
}
});
return hash;
}
function getOutputType(outfile, configType) {
if (outfile && /\.(js|css|html)$/.test(outfile)) {
return outfile.split('.').pop();
}
return configType;
}
function getScriptName() {
return path.basename(process.argv[1]);
}
function checkType(parsed) {
var scriptType = getScriptName().split('-').shift();
debug("executable type:", scriptType);
var parsedType = parsed.type;
debug("parsed type:", parsedType);
if (!parsedType) {
debug("type defaulted:", scriptType);
parsed.type = scriptType;
}
}
function checkFiles(parsed) {
var argv = parsed.argv;
if (!parsed.files) {
parsed.files = [];
} else {
if (argv.cooked.indexOf('-') > -1) {
// strip stdin path eagerly added by nopt in '-f -' case
parsed.files.some(removeDashedPath);
}
}
if (argv.remain.length) {
// assume any remaining args are files
argv.remain.forEach(function(f) {
parsed.files.push(path.resolve(f));
});
}
if ('string' === typeof parsed.outfile && !parsed.files.length) {
// use outfile as input when no other files passed in args
parsed.files.push(parsed.outfile);
// operation is now an implicit overwrite
parsed.replace = true;
}
if (argv.original.indexOf('-') > -1) {
// ensure '-' without '-f' still consumes stdin
parsed.files.push('-');
}
if (!parsed.files.length) {
throw 'Must define at least one file.';
}
debug('files.length ' + parsed.files.length);
parsed.files.forEach(testFilePath);
return parsed;
}
function removeDashedPath(filepath, i, arr) {
var found = filepath.lastIndexOf('-') === (filepath.length - 1);
if (found) {
arr.splice(i, 1);
}
return found;
}
function testFilePath(filepath) {
try {
if (filepath !== "-") {
fs.statSync(filepath);
}
} catch (err) {
throw 'Unable to open path "' + filepath + '"';
}
}
function logToStdout(str, config) {
if (typeof config.quiet === "undefined" || !config.quiet) {
console.log(str);
}
}

View file

@ -0,0 +1,103 @@
//
// simple unpacker/deobfuscator for scripts messed up with javascriptobfuscator.com
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// usage:
//
// if (JavascriptObfuscator.detect(some_string)) {
// var unpacked = JavascriptObfuscator.unpack(some_string);
// }
//
//
var JavascriptObfuscator = {
detect: function (str) {
return /^var _0x[a-f0-9]+ ?\= ?\[/.test(str);
},
unpack: function (str) {
if (JavascriptObfuscator.detect(str)) {
var matches = /var (_0x[a-f\d]+) ?\= ?\[(.*?)\];/.exec(str);
if (matches) {
var var_name = matches[1];
var strings = JavascriptObfuscator._smart_split(matches[2]);
str = str.substring(matches[0].length);
for (var k in strings) {
str = str.replace(new RegExp(var_name + '\\[' + k + '\\]', 'g'),
JavascriptObfuscator._fix_quotes(JavascriptObfuscator._unescape(strings[k])));
}
}
}
return str;
},
_fix_quotes: function(str) {
var matches = /^"(.*)"$/.exec(str);
if (matches) {
str = matches[1];
str = "'" + str.replace(/'/g, "\\'") + "'";
}
return str;
},
_smart_split: function(str) {
var strings = [];
var pos = 0;
while (pos < str.length) {
if (str.charAt(pos) == '"') {
// new word
var word = '';
pos += 1;
while (pos < str.length) {
if (str.charAt(pos) == '"') {
break;
}
if (str.charAt(pos) == '\\') {
word += '\\';
pos++;
}
word += str.charAt(pos);
pos++;
}
strings.push('"' + word + '"');
}
pos += 1;
}
return strings;
},
_unescape: function (str) {
// inefficient if used repeatedly or on small strings, but wonderful on single large chunk of text
for (var i = 32; i < 128; i++) {
str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
}
str = str.replace(/\\x09/g, "\t");
return str;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
t.test_function(JavascriptObfuscator._smart_split, "JavascriptObfuscator._smart_split");
t.expect('', []);
t.expect('"a", "b"', ['"a"', '"b"']);
t.expect('"aaa","bbbb"', ['"aaa"', '"bbbb"']);
t.expect('"a", "b\\\""', ['"a"', '"b\\\""']);
t.test_function(JavascriptObfuscator._unescape, 'JavascriptObfuscator._unescape');
t.expect('\\x40', '@');
t.expect('\\x10', '\\x10');
t.expect('\\x1', '\\x1');
t.expect("\\x61\\x62\\x22\\x63\\x64", 'ab"cd');
t.test_function(JavascriptObfuscator.detect, 'JavascriptObfuscator.detect');
t.expect('', false);
t.expect('abcd', false);
t.expect('var _0xaaaa', false);
t.expect('var _0xaaaa = ["a", "b"]', true);
t.expect('var _0xaaaa=["a", "b"]', true);
t.expect('var _0x1234=["a","b"]', true);
return t;
}
};

View file

@ -0,0 +1,90 @@
//
// simple unpacker/deobfuscator for scripts messed up with myobfuscate.com
// You really don't want to obfuscate your scripts there: they're tracking
// your unpackings, your script gets turned into something like this,
// as of 2011-04-25:
/*
var _escape = 'your_script_escaped';
var _111 = document.createElement('script');
_111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' +
'&ref=' + encodeURIComponent(document.referrer) +
'&url=' + encodeURIComponent(document.URL);
var 000 = document.getElementsByTagName('head')[0];
000.appendChild(_111);
document.write(unescape(_escape));
*/
//
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// usage:
//
// if (MyObfuscate.detect(some_string)) {
// var unpacked = MyObfuscate.unpack(some_string);
// }
//
//
var MyObfuscate = {
detect: function (str) {
if (/^var _?[0O1lI]{3}\=('|\[).*\)\)\);/.test(str)) {
return true;
}
if (/^function _?[0O1lI]{3}\(_/.test(str) && /eval\(/.test(str)) {
return true;
}
return false;
},
unpack: function (str) {
if (MyObfuscate.detect(str)) {
var __eval = eval;
try {
eval = function (unpacked) {
if (MyObfuscate.starts_with(unpacked, 'var _escape')) {
// fetch the urlencoded stuff from the script,
var matches = /'([^']*)'/.exec(unpacked);
var unescaped = unescape(matches[1]);
if (MyObfuscate.starts_with(unescaped, '<script>')) {
unescaped = unescaped.substr(8, unescaped.length - 8);
}
if (MyObfuscate.ends_with(unescaped, '</script>')) {
unescaped = unescaped.substr(0, unescaped.length - 9);
}
unpacked = unescaped;
}
// throw to terminate the script
unpacked = "// Unpacker warning: be careful when using myobfuscate.com for your projects:\n" +
"// scripts obfuscated by the free online version may call back home.\n" +
"\n//\n" + unpacked;
throw unpacked;
};
__eval(str); // should throw
} catch (e) {
// well, it failed. we'll just return the original, instead of crashing on user.
if (typeof e === "string") {
str = e;
}
}
eval = __eval;
}
return str;
},
starts_with: function (str, what) {
return str.substr(0, what.length) === what;
},
ends_with: function (str, what) {
return str.substr(str.length - what.length, what.length) === what;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
return t;
}
};

View file

@ -0,0 +1,80 @@
//
// Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// Coincidentally, it can defeat a couple of other eval-based compressors.
//
// usage:
//
// if (P_A_C_K_E_R.detect(some_string)) {
// var unpacked = P_A_C_K_E_R.unpack(some_string);
// }
//
//
var P_A_C_K_E_R = {
detect: function (str) {
return (P_A_C_K_E_R.get_chunks(str).length > 0);
},
get_chunks: function(str) {
var chunks = str.match(/eval\(\(?function\(.*?(,0,\{\}\)\)|split\('\|'\)\)\))($|\n)/g);
return chunks ? chunks : [];
},
unpack: function (str) {
var chunks = P_A_C_K_E_R.get_chunks(str),
chunk;
for(var i = 0; i < chunks.length; i++) {
chunk = chunks[i].replace(/\n$/, '');
str = str.split(chunk).join( P_A_C_K_E_R.unpack_chunk(chunk) );
}
return str;
},
unpack_chunk: function (str) {
var unpacked_source = '';
var __eval = eval;
if (P_A_C_K_E_R.detect(str)) {
try {
eval = function (s) { unpacked_source += s; return unpacked_source; };
__eval(str);
if (typeof unpacked_source == 'string' && unpacked_source) {
str = unpacked_source;
}
} catch (e) {
// well, it failed. we'll just return the original, instead of crashing on user.
}
}
eval = __eval;
return str;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest(),
pk1 = "eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',3,3,'var||a'.split('|'),0,{}))",
unpk1 = 'var a=1',
pk2 = "eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',3,3,'foo||b'.split('|'),0,{}))",
unpk2 = 'foo b=1',
pk_broken = "eval(function(p,a,c,k,e,r){BORKBORK;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',3,3,'var||a'.split('|'),0,{}))";
pk3 = "eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1{}))',3,3,'var||a'.split('|'),0,{}))",
unpk3 = 'var a=1{}))',
t.test_function(P_A_C_K_E_R.detect, "P_A_C_K_E_R.detect");
t.expect('', false);
t.expect('var a = b', false);
t.test_function(P_A_C_K_E_R.unpack, "P_A_C_K_E_R.unpack");
t.expect(pk_broken, pk_broken);
t.expect(pk1, unpk1);
t.expect(pk2, unpk2);
t.expect(pk3, unpk3);
var filler = '\nfiller\n';
t.expect(filler + pk1 + "\n" + pk_broken + filler + pk2 + filler, filler + unpk1 + "\n" + pk_broken + filler + unpk2 + filler);
return t;
}
};

View file

@ -0,0 +1,73 @@
/*global unescape */
/*jshint curly: false, scripturl: true */
//
// trivial bookmarklet/escaped script detector for the javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// usage:
//
// if (Urlencoded.detect(some_string)) {
// var unpacked = Urlencoded.unpack(some_string);
// }
//
//
var isNode = (typeof module !== 'undefined' && module.exports);
if (isNode) {
var SanityTest = require(__dirname + '/../../test/sanitytest');
}
var Urlencoded = {
detect: function (str) {
// the fact that script doesn't contain any space, but has %20 instead
// should be sufficient check for now.
if (str.indexOf(' ') == -1) {
if (str.indexOf('%2') != -1) return true;
if (str.replace(/[^%]+/g, '').length > 3) return true;
}
return false;
},
unpack: function (str) {
if (Urlencoded.detect(str)) {
if (str.indexOf('%2B') != -1 || str.indexOf('%2b') != -1) {
// "+" escaped as "%2B"
return unescape(str.replace(/\+/g, '%20'));
} else {
return unescape(str);
}
}
return str;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
t.test_function(Urlencoded.detect, "Urlencoded.detect");
t.expect('', false);
t.expect('var a = b', false);
t.expect('var%20a+=+b', true);
t.expect('var%20a=b', true);
t.expect('var%20%21%22', true);
t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();', true);
t.test_function(Urlencoded.unpack, 'Urlencoded.unpack');
t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();',
'javascript:(function(){var whatever={init:function(){alert("a"+"b")}};whatever.init()})();'
);
t.expect('', '');
t.expect('abcd', 'abcd');
t.expect('var a = b', 'var a = b');
t.expect('var%20a=b', 'var a=b');
t.expect('var%20a=b+1', 'var a=b+1');
t.expect('var%20a=b%2b1', 'var a=b+1');
return t;
}
};
if (isNode) {
module.exports = Urlencoded;
}

View file

@ -0,0 +1,63 @@
/*global js_beautify: true */
/*jshint node:true */
var requirejs = require('requirejs'),
SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
run_javascript_tests = require('./beautify-javascript-tests').run_javascript_tests,
run_css_tests = require('./beautify-css-tests').run_css_tests,
run_html_tests = require('./beautify-html-tests').run_html_tests;
requirejs.config({
paths: {
'beautify': "..",
'beautify-lib': "../lib"
}
});
function amd_beautifier_index_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js Require.js (index file)...');
var results = new SanityTest();
var beautify = requirejs('beautify/index');
test_runner(
results,
Urlencoded,
beautify.js,
beautify.html,
beautify.css);
console.log(results.results_raw());
return results;
}
function amd_beautifier_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js Require.js (separate file)...');
var results = new SanityTest();
var js_beautify = requirejs('beautify-lib/beautify'),
css_beautify = requirejs('beautify-lib/beautify-css'),
html_beautify = requirejs('beautify-lib/beautify-html');
test_runner(
results,
Urlencoded,
js_beautify.js_beautify,
html_beautify.html_beautify,
css_beautify.css_beautify);
console.log(results.results_raw());
return results;
}
if (require.main === module) {
process.exit(
amd_beautifier_tests('js-beautifier', run_javascript_tests).get_exitcode() +
amd_beautifier_index_tests('js-beautifier', run_javascript_tests).get_exitcode() +
amd_beautifier_tests('cs-beautifier', run_css_tests).get_exitcode() +
amd_beautifier_index_tests('css-beautifier', run_css_tests).get_exitcode() +
amd_beautifier_tests('html-beautifier', run_html_tests).get_exitcode() +
amd_beautifier_index_tests('html-beautifier', run_html_tests).get_exitcode()
);
}

View file

@ -0,0 +1,335 @@
/*global js_beautify: true */
function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false,
newline_between_rules: true
};
function test_css_beautifier(input)
{
return css_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test css
function t(input, expectation)
{
var wrapped_input, wrapped_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_css_beautifier, 'css_beautify');
test_fragment(input, expectation);
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
t(".tabs {}");
opts.indent_size = 1;
opts.indent_char = '\t';
opts.selector_separator_newline = true;
opts.end_with_newline = false;
opts.newline_between_rules = false;
// End With Newline - (eof = "\n")
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment(' .tabs{}', ' .tabs {}\n');
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}\n');
test_fragment('\n');
// End With Newline - (eof = "")
opts.end_with_newline = false;
test_fragment('');
test_fragment(' .tabs{}', ' .tabs {}');
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}');
test_fragment('\n', '');
// Empty braces
t('.tabs{}', '.tabs {}');
t('.tabs { }', '.tabs {}');
t('.tabs { }', '.tabs {}');
t('.tabs \n{\n \n }', '.tabs {}');
//
t('#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}', '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}');
// Selector Separator - (separator = " ", separator1 = " ")
opts.selector_separator_newline = false;
opts.selector_separator = " ";
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}');
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}');
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}');
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}');
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}');
// Selector Separator - (separator = " ", separator1 = " ")
opts.selector_separator_newline = false;
opts.selector_separator = " ";
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}');
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}');
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}');
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}');
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}');
// Selector Separator - (separator = "\n", separator1 = "\n\t")
opts.selector_separator_newline = true;
opts.selector_separator = " ";
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}');
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}');
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}');
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}');
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}');
// Selector Separator - (separator = "\n", separator1 = "\n\t")
opts.selector_separator_newline = true;
opts.selector_separator = " ";
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}');
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}');
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}');
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}');
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}');
// Newline Between Rules - (separator = "\n")
opts.newline_between_rules = true;
t('.div {}\n.span {}', '.div {}\n\n.span {}');
t('.div{}\n \n.span{}', '.div {}\n\n.span {}');
t('.div {} \n \n.span { } \n', '.div {}\n\n.span {}');
t('.div {\n \n} \n .span {\n } ', '.div {}\n\n.span {}');
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n\n.div {\n\theight: 15px;\n}');
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n\n.div {\n\theight: 15px;\n}');
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}');
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}');
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}');
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}');
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}');
// Newline Between Rules - (separator = "")
opts.newline_between_rules = false;
t('.div {}\n.span {}');
t('.div{}\n \n.span{}', '.div {}\n.span {}');
t('.div {} \n \n.span { } \n', '.div {}\n.span {}');
t('.div {\n \n} \n .span {\n } ', '.div {}\n.span {}');
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div {\n\theight: 15px;\n}');
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n.div {\n\theight: 15px;\n}');
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}');
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}');
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}');
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}');
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}');
// Functions braces
t('.tabs(){}', '.tabs() {}');
t('.tabs (){}', '.tabs () {}');
t('.tabs (pa, pa(1,2)), .cols { }', '.tabs (pa, pa(1, 2)),\n.cols {}');
t('.tabs(pa, pa(1,2)), .cols { }', '.tabs(pa, pa(1, 2)),\n.cols {}');
t('.tabs ( ) { }', '.tabs () {}');
t('.tabs( ) { }', '.tabs() {}');
t('.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}');
t('.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}');
// Comments
t('/* test */');
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}');
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}');
t('/* header */.tabs {}', '/* header */\n\n.tabs {}');
t('.tabs {\n/* non-header */\nwidth:10px;}', '.tabs {\n\t/* non-header */\n\twidth: 10px;\n}');
t('/* header');
t('// comment');
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}');
// single line comment support (less/sass)
t('.tabs{\n// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}');
t('.tabs{// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}');
t('//comment\n.tabs{width:10px;}', '//comment\n.tabs {\n\twidth: 10px;\n}');
t('.tabs{//comment\n//2nd single line comment\nwidth:10px;}', '.tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}');
t('.tabs{width:10px;//end of line comment\n}', '.tabs {\n\twidth: 10px; //end of line comment\n}');
t('.tabs{width:10px;//end of line comment\nheight:10px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px;\n}');
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}');
// Psuedo-classes vs Variables
t('@page :first {}');
// Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.
t('@page:first {}', '@page: first {}');
t('@page: first {}');
// SASS/SCSS
// Basic Interpolation
t('p {\n\t$font-size: 12px;\n\t$line-height: 30px;\n\tfont: #{$font-size}/#{$line-height};\n}');
t('p.#{$name} {}');
t(
'@mixin itemPropertiesCoverItem($items, $margin) {\n' +
'\twidth: calc((100% - ((#{$items} - 1) * #{$margin}rem)) / #{$items});\n' +
'\tmargin: 1.6rem #{$margin}rem 1.6rem 0;\n' +
'}');
//
// test basic css beautifier
t(".tabs {}");
t(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}");
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}");
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}");
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}");
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
//lead-in whitespace determines base-indent.
// lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }");
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }");
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// separate selectors
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// block nesting
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
/*
@font-face {
font-family: 'Bitstream Vera Serif Bold';
src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
}
@media screen {
#foo:hover {
background-image: url(foo.png);
}
@media screen and (min-device-pixel-ratio: 2) {
@font-face {
font-family: 'Helvetica Neue'
}
#foo:hover {
background-image: url(foo@2x.png);
}
}
}
*/
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}");
// less-css cases
t('.well{@well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}');
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}');
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// Not sure if this is sensible
// but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// import
t('@import "test";');
// don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
// handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
//nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}");
//nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}");
//variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
// test options
opts.indent_size = 2;
opts.indent_char = ' ';
opts.selector_separator_newline = false;
// pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}");
t("#foo *:hover {\n color: purple\n}");
t("::selection {\n color: #ff0000;\n}");
// TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}");
// particular edge case with braces and semicolons inside tags that allows custom text
t("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}");
// may not eat the space before "["
t('html.js [data-custom="123"] {\n opacity: 1.00;\n}');
t('html.js *[data-custom="123"] {\n opacity: 1.00;\n}');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_css_tests = run_css_tests;
}

View file

@ -0,0 +1,736 @@
/*global js_beautify: true */
function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false
};
function test_html_beautifier(input)
{
return html_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test html
function bth(input, expectation)
{
var wrapped_input, wrapped_expectation, field_input, field_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_html_beautifier, 'html_beautify');
test_fragment(input, expectation);
if (opts.indent_size === 4 && input) {
wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
test_fragment(wrapped_input, wrapped_expectation);
}
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
bth('');
opts.indent_size = 4;
opts.indent_char = ' ';
opts.indent_with_tabs = false;
opts.preserve_newlines = true;
opts.jslint_happy = false;
opts.keep_array_indentation = false;
opts.brace_style = 'collapse';
opts.extra_liners = ['html', 'head', '/html'];
// End With Newline - (eof = "\n")
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>', '<div></div>\n');
test_fragment('\n');
// End With Newline - (eof = "")
opts.end_with_newline = false;
test_fragment('');
test_fragment('<div></div>');
test_fragment('\n', '');
// Custom Extra Liners (empty) - ()
opts.extra_liners = [];
test_fragment('<html><head><meta></head><body><div><p>x</p></div></body></html>', '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n <p>x</p>\n </div>\n</body>\n</html>');
// Custom Extra Liners (default) - ()
opts.extra_liners = null;
test_fragment('<html><head></head><body></body></html>', '<html>\n\n<head></head>\n\n<body></body>\n\n</html>');
// Custom Extra Liners (p, string) - ()
opts.extra_liners = 'p,/p';
test_fragment('<html><head><meta></head><body><div><p>x</p></div></body></html>', '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>');
// Custom Extra Liners (p) - ()
opts.extra_liners = ['p', '/p'];
test_fragment('<html><head><meta></head><body><div><p>x</p></div></body></html>', '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>');
// Attribute Wrap - (eof = "\n", indent_attr = " ", over80 = "\n")
opts.wrap_attributes = 'force';
test_fragment('<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>', '<div attr0\n attr1="123"\n data-attr2="hello t here">This is some text</div>');
test_fragment('<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>', '<div lookatthissuperduperlongattributenamewhoahcrazy0="true"\n attr0\n attr1="123"\n data-attr2="hello t here"\n heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>');
test_fragment('<img attr0 attr1="123" data-attr2="hello t here"/>', '<img attr0\n attr1="123"\n data-attr2="hello t here" />');
// Attribute Wrap - (eof = "\n", indent_attr = " ", over80 = "\n")
opts.wrap_attributes = 'force';
opts.wrap_line_length = 80;
test_fragment('<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>', '<div attr0\n attr1="123"\n data-attr2="hello t here">This is some text</div>');
test_fragment('<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>', '<div lookatthissuperduperlongattributenamewhoahcrazy0="true"\n attr0\n attr1="123"\n data-attr2="hello t here"\n heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>');
test_fragment('<img attr0 attr1="123" data-attr2="hello t here"/>', '<img attr0\n attr1="123"\n data-attr2="hello t here" />');
// Attribute Wrap - (eof = "\n", indent_attr = " ", over80 = "\n")
opts.wrap_attributes = 'force';
opts.wrap_attributes_indent_size = 8;
test_fragment('<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>', '<div attr0\n attr1="123"\n data-attr2="hello t here">This is some text</div>');
test_fragment('<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>', '<div lookatthissuperduperlongattributenamewhoahcrazy0="true"\n attr0\n attr1="123"\n data-attr2="hello t here"\n heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>');
test_fragment('<img attr0 attr1="123" data-attr2="hello t here"/>', '<img attr0\n attr1="123"\n data-attr2="hello t here" />');
// Attribute Wrap - (eof = " ", indent_attr = "", over80 = "\n")
opts.wrap_attributes = 'auto';
opts.wrap_line_length = 80;
test_fragment('<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>');
test_fragment('<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>', '<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here"\nheymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>');
test_fragment('<img attr0 attr1="123" data-attr2="hello t here"/>', '<img attr0 attr1="123" data-attr2="hello t here" />');
// Attribute Wrap - (eof = " ", indent_attr = "", over80 = " ")
opts.wrap_attributes = 'auto';
opts.wrap_line_length = 0;
test_fragment('<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>');
test_fragment('<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>');
test_fragment('<img attr0 attr1="123" data-attr2="hello t here"/>', '<img attr0 attr1="123" data-attr2="hello t here" />');
// Handlebars Indenting Off
opts.indent_handlebars = false;
test_fragment(
'{{#if 0}}\n <div>\n </div>\n{{/if}}',
'{{#if 0}}\n<div>\n</div>\n{{/if}}');
test_fragment(
'<div>\n{{#each thing}}\n {{name}}\n{{/each}}\n</div>',
'<div>\n {{#each thing}} {{name}} {{/each}}\n</div>');
// Handlebars Indenting On - (content = "{{field}}")
opts.indent_handlebars = true;
test_fragment('{{page-title}}');
test_fragment('{{#if 0}}{{/if}}');
test_fragment('{{#if 0}}{{field}}{{/if}}');
test_fragment('{{#if 0}}\n{{/if}}');
test_fragment(
'{{#if words}}{{/if}}',
'{{#if words}}{{/if}}');
test_fragment(
'{{#if words}}{{field}}{{/if}}',
'{{#if words}}{{field}}{{/if}}');
test_fragment(
'{{#if words}}{{field}}{{/if}}',
'{{#if words}}{{field}}{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n<div>\n</div>\n{{/if}}',
'{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment('<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'<div>\n{{#if 1}}\n{{/if}}\n</div>',
'<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'{{#if}}\n{{#each}}\n{{#if}}\n{{field}}\n{{/if}}\n{{#if}}\n{{field}}\n{{/if}}\n{{/each}}\n{{/if}}',
'{{#if}}\n {{#each}}\n {{#if}}\n {{field}}\n {{/if}}\n {{#if}}\n {{field}}\n {{/if}}\n {{/each}}\n{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{field}}\n {{else}}\n {{field}}\n{{/if}}',
'{{#if 1}}\n {{field}}\n{{else}}\n {{field}}\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{else}}\n {{/if}}',
'{{#if 1}}\n{{else}}\n{{/if}}');
test_fragment(
'{{#if thing}}\n{{#if otherthing}}\n {{field}}\n {{else}}\n{{field}}\n {{/if}}\n {{else}}\n{{field}}\n{{/if}}',
'{{#if thing}}\n {{#if otherthing}}\n {{field}}\n {{else}}\n {{field}}\n {{/if}}\n{{else}}\n {{field}}\n{{/if}}');
test_fragment(
'<div{{somestyle}}></div>',
'<div {{somestyle}}></div>');
test_fragment(
'<div{{#if test}}class="foo"{{/if}}>{{field}}</div>',
'<div {{#if test}} class="foo" {{/if}}>{{field}}</div>');
test_fragment(
'<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>{{field}}</div>',
'<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>{{field}}</div>');
test_fragment(
'<span{{#if condition}}class="foo"{{/if}}>{{field}}</span>',
'<span {{#if condition}} class="foo" {{/if}}>{{field}}</span>');
test_fragment('<div unformatted="{{#if}}{{field}}{{/if}}">{{field}}</div>');
test_fragment('<div unformatted="{{#if }} {{field}}{{/if}}">{{field}}</div>');
test_fragment('<div class="{{#if thingIs "value"}}{{field}}{{/if}}"></div>');
test_fragment('<div class="{{#if thingIs \'value\'}}{{field}}{{/if}}"></div>');
test_fragment('<div class=\'{{#if thingIs "value"}}{{field}}{{/if}}\'></div>');
test_fragment('<div class=\'{{#if thingIs \'value\'}}{{field}}{{/if}}\'></div>');
// Handlebars Indenting On - (content = "{{! comment}}")
opts.indent_handlebars = true;
test_fragment('{{page-title}}');
test_fragment('{{#if 0}}{{/if}}');
test_fragment('{{#if 0}}{{! comment}}{{/if}}');
test_fragment('{{#if 0}}\n{{/if}}');
test_fragment(
'{{#if words}}{{/if}}',
'{{#if words}}{{/if}}');
test_fragment(
'{{#if words}}{{! comment}}{{/if}}',
'{{#if words}}{{! comment}}{{/if}}');
test_fragment(
'{{#if words}}{{! comment}}{{/if}}',
'{{#if words}}{{! comment}}{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n<div>\n</div>\n{{/if}}',
'{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment('<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'<div>\n{{#if 1}}\n{{/if}}\n</div>',
'<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'{{#if}}\n{{#each}}\n{{#if}}\n{{! comment}}\n{{/if}}\n{{#if}}\n{{! comment}}\n{{/if}}\n{{/each}}\n{{/if}}',
'{{#if}}\n {{#each}}\n {{#if}}\n {{! comment}}\n {{/if}}\n {{#if}}\n {{! comment}}\n {{/if}}\n {{/each}}\n{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{! comment}}\n {{else}}\n {{! comment}}\n{{/if}}',
'{{#if 1}}\n {{! comment}}\n{{else}}\n {{! comment}}\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{else}}\n {{/if}}',
'{{#if 1}}\n{{else}}\n{{/if}}');
test_fragment(
'{{#if thing}}\n{{#if otherthing}}\n {{! comment}}\n {{else}}\n{{! comment}}\n {{/if}}\n {{else}}\n{{! comment}}\n{{/if}}',
'{{#if thing}}\n {{#if otherthing}}\n {{! comment}}\n {{else}}\n {{! comment}}\n {{/if}}\n{{else}}\n {{! comment}}\n{{/if}}');
test_fragment(
'<div{{somestyle}}></div>',
'<div {{somestyle}}></div>');
test_fragment(
'<div{{#if test}}class="foo"{{/if}}>{{! comment}}</div>',
'<div {{#if test}} class="foo" {{/if}}>{{! comment}}</div>');
test_fragment(
'<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>{{! comment}}</div>',
'<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>{{! comment}}</div>');
test_fragment(
'<span{{#if condition}}class="foo"{{/if}}>{{! comment}}</span>',
'<span {{#if condition}} class="foo" {{/if}}>{{! comment}}</span>');
test_fragment('<div unformatted="{{#if}}{{! comment}}{{/if}}">{{! comment}}</div>');
test_fragment('<div unformatted="{{#if }} {{! comment}}{{/if}}">{{! comment}}</div>');
test_fragment('<div class="{{#if thingIs "value"}}{{! comment}}{{/if}}"></div>');
test_fragment('<div class="{{#if thingIs \'value\'}}{{! comment}}{{/if}}"></div>');
test_fragment('<div class=\'{{#if thingIs "value"}}{{! comment}}{{/if}}\'></div>');
test_fragment('<div class=\'{{#if thingIs \'value\'}}{{! comment}}{{/if}}\'></div>');
// Handlebars Indenting On - (content = "{pre{{field1}} {{field2}} {{field3}}post")
opts.indent_handlebars = true;
test_fragment('{{page-title}}');
test_fragment('{{#if 0}}{{/if}}');
test_fragment('{{#if 0}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}');
test_fragment('{{#if 0}}\n{{/if}}');
test_fragment(
'{{#if words}}{{/if}}',
'{{#if words}}{{/if}}');
test_fragment(
'{{#if words}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}',
'{{#if words}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}');
test_fragment(
'{{#if words}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}',
'{{#if words}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n<div>\n</div>\n{{/if}}',
'{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment('<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'<div>\n{{#if 1}}\n{{/if}}\n</div>',
'<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'{{#if}}\n{{#each}}\n{{#if}}\n{pre{{field1}} {{field2}} {{field3}}post\n{{/if}}\n{{#if}}\n{pre{{field1}} {{field2}} {{field3}}post\n{{/if}}\n{{/each}}\n{{/if}}',
'{{#if}}\n {{#each}}\n {{#if}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{/if}}\n {{#if}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{/if}}\n {{/each}}\n{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{else}}\n {pre{{field1}} {{field2}} {{field3}}post\n{{/if}}',
'{{#if 1}}\n {pre{{field1}} {{field2}} {{field3}}post\n{{else}}\n {pre{{field1}} {{field2}} {{field3}}post\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{else}}\n {{/if}}',
'{{#if 1}}\n{{else}}\n{{/if}}');
test_fragment(
'{{#if thing}}\n{{#if otherthing}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{else}}\n{pre{{field1}} {{field2}} {{field3}}post\n {{/if}}\n {{else}}\n{pre{{field1}} {{field2}} {{field3}}post\n{{/if}}',
'{{#if thing}}\n {{#if otherthing}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{else}}\n {pre{{field1}} {{field2}} {{field3}}post\n {{/if}}\n{{else}}\n {pre{{field1}} {{field2}} {{field3}}post\n{{/if}}');
test_fragment(
'<div{{somestyle}}></div>',
'<div {{somestyle}}></div>');
test_fragment(
'<div{{#if test}}class="foo"{{/if}}>{pre{{field1}} {{field2}} {{field3}}post</div>',
'<div {{#if test}} class="foo" {{/if}}>{pre{{field1}} {{field2}} {{field3}}post</div>');
test_fragment(
'<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>{pre{{field1}} {{field2}} {{field3}}post</div>',
'<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>{pre{{field1}} {{field2}} {{field3}}post</div>');
test_fragment(
'<span{{#if condition}}class="foo"{{/if}}>{pre{{field1}} {{field2}} {{field3}}post</span>',
'<span {{#if condition}} class="foo" {{/if}}>{pre{{field1}} {{field2}} {{field3}}post</span>');
test_fragment('<div unformatted="{{#if}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}">{pre{{field1}} {{field2}} {{field3}}post</div>');
test_fragment('<div unformatted="{{#if }} {pre{{field1}} {{field2}} {{field3}}post{{/if}}">{pre{{field1}} {{field2}} {{field3}}post</div>');
test_fragment('<div class="{{#if thingIs "value"}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}"></div>');
test_fragment('<div class="{{#if thingIs \'value\'}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}"></div>');
test_fragment('<div class=\'{{#if thingIs "value"}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}\'></div>');
test_fragment('<div class=\'{{#if thingIs \'value\'}}{pre{{field1}} {{field2}} {{field3}}post{{/if}}\'></div>');
// Handlebars Indenting On - (content = "{{! \n mult-line\ncomment \n with spacing\n}}")
opts.indent_handlebars = true;
test_fragment('{{page-title}}');
test_fragment('{{#if 0}}{{/if}}');
test_fragment('{{#if 0}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}');
test_fragment('{{#if 0}}\n{{/if}}');
test_fragment(
'{{#if words}}{{/if}}',
'{{#if words}}{{/if}}');
test_fragment(
'{{#if words}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}',
'{{#if words}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}');
test_fragment(
'{{#if words}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}',
'{{#if words}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n<div>\n</div>\n{{/if}}',
'{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment('<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'<div>\n{{#if 1}}\n{{/if}}\n</div>',
'<div>\n {{#if 1}}\n {{/if}}\n</div>');
test_fragment(
'{{#if}}\n{{#each}}\n{{#if}}\n{{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}\n{{#if}}\n{{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}\n{{/each}}\n{{/if}}',
'{{#if}}\n {{#each}}\n {{#if}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{/if}}\n {{#if}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{/if}}\n {{/each}}\n{{/if}}');
test_fragment('{{#if 1}}\n <div>\n </div>\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{else}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}',
'{{#if 1}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n{{else}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}');
test_fragment(
'{{#if 1}}\n {{else}}\n {{/if}}',
'{{#if 1}}\n{{else}}\n{{/if}}');
test_fragment(
'{{#if thing}}\n{{#if otherthing}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{else}}\n{{! \n mult-line\ncomment \n with spacing\n}}\n {{/if}}\n {{else}}\n{{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}',
'{{#if thing}}\n {{#if otherthing}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{else}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n {{/if}}\n{{else}}\n {{! \n mult-line\ncomment \n with spacing\n}}\n{{/if}}');
test_fragment(
'<div{{somestyle}}></div>',
'<div {{somestyle}}></div>');
test_fragment(
'<div{{#if test}}class="foo"{{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</div>',
'<div {{#if test}} class="foo" {{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</div>');
test_fragment(
'<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</div>',
'<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</div>');
test_fragment(
'<span{{#if condition}}class="foo"{{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</span>',
'<span {{#if condition}} class="foo" {{/if}}>{{! \n mult-line\ncomment \n with spacing\n}}</span>');
test_fragment('<div unformatted="{{#if}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}">{{! \n mult-line\ncomment \n with spacing\n}}</div>');
test_fragment('<div unformatted="{{#if }} {{! \n mult-line\ncomment \n with spacing\n}}{{/if}}">{{! \n mult-line\ncomment \n with spacing\n}}</div>');
test_fragment('<div class="{{#if thingIs "value"}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}"></div>');
test_fragment('<div class="{{#if thingIs \'value\'}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}"></div>');
test_fragment('<div class=\'{{#if thingIs "value"}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}\'></div>');
test_fragment('<div class=\'{{#if thingIs \'value\'}}{{! \n mult-line\ncomment \n with spacing\n}}{{/if}}\'></div>');
// Unclosed html elements
test_fragment('<source>\n<source>');
test_fragment('<br>\n<br>');
test_fragment('<input>\n<input>');
test_fragment('<meta>\n<meta>');
test_fragment('<link>\n<link>');
// Unformatted tags
test_fragment('<ol>\n <li>b<pre>c</pre></li>\n</ol>');
test_fragment('<ol>\n <li>b<code>c</code></li>\n</ol>');
// Php formatting
test_fragment('<h1 class="content-page-header"><?=$view["name"]; ?></h1>');
test_fragment(
'<?php\n' +
'for($i = 1; $i <= 100; $i++;) {\n' +
' #count to 100!\n' +
' echo($i . "</br>");\n' +
'}\n' +
'?>');
// underscore.js formatting
test_fragment(
'<div class="col-sm-9">\n' +
' <textarea id="notes" class="form-control" rows="3">\n' +
' <%= notes %>\n' +
' </textarea>\n' +
'</div>');
// Indent with tabs
opts.indent_with_tabs = true;
test_fragment(
'<div>\n<div>\n</div>\n</div>',
'<div>\n\t<div>\n\t</div>\n</div>');
// Indent without tabs
opts.indent_with_tabs = false;
test_fragment(
'<div>\n<div>\n</div>\n</div>',
'<div>\n <div>\n </div>\n</div>');
// New Test Suite
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>\n');
test_fragment('<div></div>\n\n\n', '<div></div>\n');
test_fragment('<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
'\n' +
' </script>\n' +
'</head>\n');
opts.end_with_newline = false;
// error cases need love too
bth('<img title="Bad food!" src="foo.jpg" alt="Evil" ">');
bth("<!-- don't blow up if a comment is not complete"); // -->
test_fragment(
'<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
' </script>\n' +
'</head>');
test_fragment('<div></div>\n', '<div></div>');
bth('<div></div>');
bth('<div>content</div>');
bth('<div><div></div></div>',
'<div>\n' +
' <div></div>\n' +
'</div>');
bth('<div><div>content</div></div>',
'<div>\n' +
' <div>content</div>\n' +
'</div>');
bth('<div>\n' +
' <span>content</span>\n' +
'</div>');
bth('<div>\n' +
'</div>');
bth('<div>\n' +
' content\n' +
'</div>');
bth('<div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth(' <div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth('<div>\n' +
'</div>\n' +
' <div>\n' +
' </div>',
'<div>\n' +
'</div>\n' +
'<div>\n' +
'</div>');
bth(' <div>\n' +
'</div>',
'<div>\n' +
'</div>');
bth('<div >content</div>',
'<div>content</div>');
bth('<div thinger="preserve space here" ></div >',
'<div thinger="preserve space here"></div>');
bth('content\n' +
' <div>\n' +
' </div>\n' +
'content',
'content\n' +
'<div>\n' +
'</div>\n' +
'content');
bth('<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
'<div>\n' +
'</div>\n' +
'</li>',
'<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
' content\n' +
'</li>\n' +
'<li>\n' +
' content\n' +
'</li>');
bth('<img>content');
bth('<img> content');
bth('<img> content', '<img> content');
bth('<img><img>content');
bth('<img> <img>content');
bth('<img> <img>content', '<img> <img>content');
bth('<img><b>content</b>');
bth('<img> <b>content</b>');
bth('<img> <b>content</b>', '<img> <b>content</b>');
bth('<div>content<img>content</div>');
bth('<div> content <img> content</div>');
bth('<div> content <img> content </div>',
'<div> content <img> content </div>');
bth('Text <a href="#">Link</a> Text');
// START tests for issue 453
bth('<script type="text/unknown"><div></div></script>',
'<script type="text/unknown">\n' +
' <div></div>\n' +
'</script>');
bth('<script type="text/javascript"><div></div></script>',
'<script type="text/javascript">\n' +
' < div > < /div>\n' +
'</script>');
bth('<script><div></div></script>',
'<script>\n' +
' < div > < /div>\n' +
'</script>');
bth('<script type="text/javascript">var foo = "bar";</script>',
'<script type="text/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript">var foo = "bar";</script>',
'<script type="application/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript;version=1.8">var foo = "bar";</script>',
'<script type="application/javascript;version=1.8">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/x-javascript">var foo = "bar";</script>',
'<script type="application/x-javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/ecmascript">var foo = "bar";</script>',
'<script type="application/ecmascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="text/javascript1.5">var foo = "bar";</script>',
'<script type="text/javascript1.5">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script>var foo = "bar";</script>',
'<script>\n' +
' var foo = "bar";\n' +
'</script>');
bth('<style type="text/unknown"><tag></tag></style>',
'<style type="text/unknown">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css"><tag></tag></style>',
'<style type="text/css">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style><tag></tag></style>',
'<style>\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css">.selector {font-size:12px;}</style>',
'<style type="text/css">\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
bth('<style>.selector {font-size:12px;}</style>',
'<style>\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
// END tests for issue 453
var unformatted = opts.unformatted;
opts.unformatted = ['script', 'style'];
bth('<script id="javascriptTemplate" type="text/x-kendo-template">\n' +
' <ul>\n' +
' # for (var i = 0; i < data.length; i++) { #\n' +
' <li>#= data[i] #</li>\n' +
' # } #\n' +
' </ul>\n' +
'</script>');
bth('<style>\n' +
' body {background-color:lightgrey}\n' +
' h1 {color:blue}\n' +
'</style>');
opts.unformatted = unformatted;
unformatted = opts.unformatted;
opts.unformatted = ['custom-element'];
test_fragment('<div>should <custom-element>not</custom-element>' +
' insert newlines</div>',
'<div>should <custom-element>not</custom-element>' +
' insert newlines</div>');
opts.unformatted = unformatted;
// Tests that don't pass, but probably should.
// bth('<div><span>content</span></div>');
// Handlebars tests
// Without the indent option on, handlebars are treated as content.
opts.wrap_line_length = 0;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of 0 means no max line length, and should not wrap.
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
opts.wrap_line_length = "0";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of "0" means no max line length, and should not wrap
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
//BUGBUG: This should wrap before 40 not after.
opts.wrap_line_length = 40;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.wrap_line_length = "40";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.indent_size = 1;
opts.indent_char = '\t';
opts.preserve_newlines = false;
bth('<div>\n\tfoo\n</div>', '<div> foo </div>');
opts.preserve_newlines = true;
bth('<div>\n\tfoo\n</div>');
// test preserve_newlines and max_preserve_newlines
opts.preserve_newlines = false;
bth('<div>Should not</div>\n\n\n' +
'<div>preserve newlines</div>',
'<div>Should not</div>\n' +
'<div>preserve newlines</div>');
opts.preserve_newlines = true;
opts.max_preserve_newlines = 0;
bth('<div>Should</div>\n\n\n' +
'<div>preserve zero newlines</div>',
'<div>Should</div>\n' +
'<div>preserve zero newlines</div>');
opts.max_preserve_newlines = 1;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n' +
'<div>preserve one newline</div>');
opts.max_preserve_newlines = null;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_html_tests = run_html_tests;
}

View file

@ -0,0 +1,50 @@
/*global js_beautify: true */
/*jshint node:true */
var fs = require('fs'),
SanityTest = require('./sanitytest'),
Benchmark = require('benchmark'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
js_beautify = require('../index').js_beautify,
css_beautify = require('../index').css_beautify,
html_beautify = require('../index').html_beautify;
function node_beautifier_tests() {
console.log('Testing performance...');
var data = fs.readFileSync(__dirname + '/../../test/underscore.js', 'utf8');
var data_min = fs.readFileSync(__dirname + '/../../test/underscore-min.js', 'utf8');
var options = {
wrap_line_length: 80
};
//warm-up
js_beautify(data, options);
js_beautify(data_min, options);
var suite = new Benchmark.Suite;
suite.add("js-beautify (underscore)", function() {
js_beautify(data, options);
})
.add("js-beautify (underscore-min)", function() {
js_beautify(data_min, options);
})
// add listeners
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('error', function(event) {
return 1;
})
.on('complete', function(event) {
})
.run()
return 0;
}
if (require.main === module) {
process.exit(node_beautifier_tests());
}

View file

@ -0,0 +1,53 @@
/*global js_beautify: true */
/*jshint node:true */
var SanityTest = require('./sanitytest'),
Urlencoded = require('../lib/unpackers/urlencode_unpacker'),
run_javascript_tests = require('./beautify-javascript-tests').run_javascript_tests,
run_css_tests = require('./beautify-css-tests').run_css_tests,
run_html_tests = require('./beautify-html-tests').run_html_tests;
function node_beautifier_legacy_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS (legacy names)...');
var results = new SanityTest();
var beautify = require('../index');
test_runner(
results,
Urlencoded,
beautify.js_beautify,
beautify.html_beautify,
beautify.css_beautify);
console.log(results.results_raw());
return results;
}
function node_beautifier_tests(name, test_runner) {
console.log('Testing ' + name + ' with node.js CommonJS (new names)...');
var results = new SanityTest();
var beautify = require('../index');
test_runner(
results,
Urlencoded,
beautify.js,
beautify.html,
beautify.css);
console.log(results.results_raw());
return results;
}
if (require.main === module) {
process.exit(
node_beautifier_tests('js-beautifier', run_javascript_tests).get_exitcode() +
node_beautifier_legacy_tests('js-beautifier', run_javascript_tests).get_exitcode() +
node_beautifier_tests('cs-beautifier', run_css_tests).get_exitcode() +
node_beautifier_legacy_tests('css-beautifier', run_css_tests).get_exitcode() +
node_beautifier_tests('html-beautifier', run_html_tests).get_exitcode() +
node_beautifier_legacy_tests('html-beautifier', run_html_tests).get_exitcode()
);
}

View file

@ -0,0 +1,58 @@
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js"></script>
<script type="text/javascript">
require(["../lib/beautify-html"],function(html_beautify){
var input = document.getElementById("input").value;
var output = html_beautify.html_beautify(input);
document.getElementById("output").innerHTML = output;
});
</script>
<style type="text/css">
#output{
border: 1px solid black;
height: 300px;
width: 100%;
}
#input{
width: 100%;
height: 300px;
}
</style>
</head>
<body>
<h1>RequireJS test</h1>
<p>
This example loads the html-beautifier by using a relative path in the require call to the beautify-html.js file.
(also works works with absolute paths)
</p>
<pre>
require(["../lib/beautify-html"],function(html_beautify){
var input = document.getElementById("input").value;
var output = html_beautify.html_beautify(input);
document.getElementById("output").innerHTML = output;
});
</pre>
<h2>Input</h2>
<textarea name="" id="input">
<ul><li><a href="test"></a></li></ul>
</textarea>
<h2>Output</h2>
<textarea name="" id="output">
</textarea>
</body>
</html>

View file

@ -0,0 +1,3 @@
function indentMe() {
"no, me!";
}

View file

@ -0,0 +1,17 @@
#!/usr/bin/spidermonkey-1.7 -s
//#!/usr/bin/js
// a little helper for testing from command line
// just run it, it will output the test results
load('js/lib/beautify.js');
load('js/test/sanitytest.js')
load('js/test/beautify-tests.js')
load('js/lib/unpackers/urlencode_unpacker.js')
print(run_beautifier_tests(new SanityTest(), Urlencoded, js_beautify).results_raw())
// for nodejs use this from the command line from the main directory:
// node test/beautify-tests.js

View file

@ -0,0 +1,135 @@
//
// simple testing interface
// written by Einar Lielmanis, einar@jsbeautifier.org
//
// usage:
//
// var t = new SanityTest(function (x) { return x; }, 'my function');
// t.expect('input', 'output');
// t.expect('a', 'a');
// output_somewhere(t.results()); // good for <pre>, html safe-ish
// alert(t.results_raw()); // html unescaped
function SanityTest (func, name_of_test) {
var test_func = func || function (x) {
return x;
};
var test_name = name_of_test || '';
var n_failed = 0;
var n_succeeded = 0;
var failures = [];
this.test_function = function(func, name) {
test_func = func;
test_name = name || '';
};
this.get_exitcode = function() {
return n_succeeded === 0 || n_failed !== 0 ? 1 : 0;
};
this.expect = function(parameters, expected_value) {
// multi-parameter calls not supported (I don't need them now).
var result = test_func(parameters);
// proper array checking is a pain. i'll maybe do it later, compare strings representations instead
if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') === expected_value.join(', '))) {
n_succeeded += 1;
} else {
n_failed += 1;
failures.push([test_name, parameters, expected_value, result]);
}
};
this.results_raw = function() {
var results = '';
if (n_failed === 0) {
if (n_succeeded === 0) {
results = 'No tests run.';
} else {
results = 'All ' + n_succeeded + ' tests passed.';
}
} else {
for (var i = 0 ; i < failures.length; i++) {
var f = failures[i];
if (f[0]) {
f[0] = f[0] + ' ';
}
results += '---- ' + f[0] + 'input -------\n' + this.prettyprint(f[1]) + '\n';
results += '---- ' + f[0] + 'expected ----\n' + this.prettyprint(f[2]) + '\n';
results += '---- ' + f[0] + 'output ------\n' + this.prettyprint(f[3]) + '\n\n';
}
results += n_failed + ' tests failed.\n';
}
return results;
};
this.results = function() {
return this.lazy_escape(this.results_raw());
};
this.prettyprint = function(something, quote_strings) {
var type = typeof something;
switch(type.toLowerCase()) {
case 'string':
if (quote_strings) {
return "'" + something.replace("'", "\\'") + "'";
} else {
return something;
}
case 'number':
return '' + something;
case 'boolean':
return something ? 'true' : 'false';
case 'undefined':
return 'undefined';
case 'object':
if (something instanceof Array) {
var x = [];
var expected_index = 0;
for (var k in something) {
if (k === expected_index) {
x.push(this.prettyprint(something[k], true));
expected_index += 1;
} else {
x.push('\n' + k + ': ' + this.prettyprint(something[k], true));
}
}
return '[' + x.join(', ') + ']';
} else {
return 'object: ' + something;
}
default:
return type + ': ' + something;
}
};
this.lazy_escape = function (str) {
return str.replace(/</g, '&lt;').replace(/\>/g, '&gt;').replace(/\n/g, '<br />');
};
this.log = function () {
if (window.console) {
if (console.firebug) {
console.log.apply(console, Array.prototype.slice.call(arguments));
} else {
console.log.call(console, Array.prototype.slice.call(arguments));
}
}
};
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = SanityTest;
}

View file

@ -0,0 +1,171 @@
#!/bin/bash
REL_SCRIPT_DIR="`dirname \"$0\"`"
SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`"
test_cli_common()
{
echo ----------------------------------------
echo Testing common cli behavior...
CLI_SCRIPT_NAME=${1:?missing_param}.js
CLI_SCRIPT=$SCRIPT_DIR/../bin/$CLI_SCRIPT_NAME
echo Script: $CLI_SCRIPT
# should find the minimal help output
$CLI_SCRIPT 2>&1 | grep -q "Must define at least one file\." || {
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
$CLI_SCRIPT -h > /dev/null || {
echo "[$CLI_SCRIPT_NAME -h] Return code should be success."
exit 1
}
$CLI_SCRIPT -v > /dev/null || {
echo "[$CLI_SCRIPT_NAME -v] Return code should be success."
exit 1
}
MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file"
MISSING_FILE_MESSAGE="Unable to open path"
$CLI_SCRIPT $MISSING_FILE 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error."
exit 1
}
$CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message."
exit 1
}
if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text."
exit 1
fi
}
test_cli_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify cli behavior...
CLI_SCRIPT=$SCRIPT_DIR/../bin/js-beautify.js
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/js-beautify.js | diff $SCRIPT_DIR/../bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged."
exit 1
}
rm -rf /tmp/js-beautify-mkdir
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../bin/js-beautify.js && diff $SCRIPT_DIR/../bin/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js should have been created in /tmp/js-beautify-mkdir/js-beautify.js."
exit 1
}
# ensure unchanged files are not overwritten
cp -p /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js
touch -A -05 /tmp/js-beautify-mkdir/js-beautify.js
touch -A -01 /tmp/js-beautify-mkdir/js-beautify-old.js
$CLI_SCRIPT -r /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -r"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and same file name"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and different file name"
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../bin/css-beautify.js | diff -q $SCRIPT_DIR/../bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected to be different."
exit 1
}
export HOME=
export USERPROFILE=
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/example1-default.js $SCRIPT_DIR/resources/example1.js
cd $SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT /tmp/js-beautify-mkdir/example1-default.js | diff -q /tmp/js-beautify-mkdir/example1-default.js - && {
echo "js-beautify output for /tmp/js-beautify-mkdir/example1-default.js was expected to be different based on CWD settings."
exit 1
}
cd $SCRIPT_DIR/resources/indent11chars/subDir1/subDir2
$CLI_SCRIPT /tmp/js-beautify-mkdir/example1-default.js | diff -q /tmp/js-beautify-mkdir/example1-default.js - && {
echo "js-beautify output for /tmp/js-beautify-mkdir/example1-default.js was expected to be different based on CWD parent folder settings."
exit 1
}
cd $SCRIPT_DIR
export HOME=$SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT /tmp/js-beautify-mkdir/example1-default.js | diff -q /tmp/js-beautify-mkdir/example1-default.js - && {
echo "js-beautify output for /tmp/js-beautify-mkdir/example1-default.js was expected to be different based on HOME settings."
exit 1
}
export HOME=
export USERPROFILE=$SCRIPT_DIR/resources/indent11chars
$CLI_SCRIPT /tmp/js-beautify-mkdir/example1-default.js | diff -q /tmp/js-beautify-mkdir/example1-default.js - && {
echo "js-beautify output for /tmp/js-beautify-mkdir/example1-default.js was expected to be different based on USERPROFILE settings."
exit 1
}
}
test_smoke_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify functionality...
node $SCRIPT_DIR/node-beautify-tests.js || exit 1
node $SCRIPT_DIR/amd-beautify-tests.js || exit 1
}
test_performance_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify performance...
node $SCRIPT_DIR/node-beautify-perf-tests.js || exit 1
echo ----------------------------------------
}
test_cli_common css-beautify
test_cli_common html-beautify
test_cli_common js-beautify
test_cli_js_beautify
test_smoke_js_beautify
test_performance_js_beautify
echo ----------------------------------------
echo $0 - PASSED.
echo ----------------------------------------

View file

@ -0,0 +1,56 @@
{
"name": "js-beautify",
"version": "1.5.10",
"description": "jsbeautifier.org for node",
"main": "js/index.js",
"preferGlobal": true,
"bin": {
"css-beautify": "./js/bin/css-beautify.js",
"html-beautify": "./js/bin/html-beautify.js",
"js-beautify": "./js/bin/js-beautify.js"
},
"directories": {
"lib": "js/lib",
"test": "js/test"
},
"scripts": {
"test": "./js/test/shell-smoke-test.sh",
"update-codemirror": "npm install codemirror && rm -rf ./web/third-party/codemirror/* && cp ./node_modules/codemirror/LICENSE ./web/third-party/codemirror/ && cp ./node_modules/codemirror/README.md ./web/third-party/codemirror/ && cp -r ./node_modules/codemirror/lib ./web/third-party/codemirror/ && mkdir -p ./web/third-party/codemirror/mode && cp -r ./node_modules/codemirror/mode/javascript ./web/third-party/codemirror/mode/ && git add -Av ./web/third-party/codemirror"
},
"bugs": "https://github.com/beautify-web/js-beautify/issues",
"homepage": "http://jsbeautifier.org/",
"repository": {
"type": "git",
"url": "git://github.com/beautify-web/js-beautify.git"
},
"keywords": [
"beautify",
"beautifier",
"code-quality"
],
"author": "Einar Lielmanis <einar@jsbeautifier.org>",
"contributors": [
"Vital Batmanov <vital76@gmail.com>",
"Chris J. Shull <chrisjshull@gmail.com>",
"Gian Marco Gherardi <gianmarco.gherardi@gmail.com>",
"Stan <stasson@orc.ru>",
"Vittorio Gambaletta <VittGam@vittgam.net>",
"Daniel Stockman <daniel.stockman@gmail.com>",
"Harutyun Amirjanyan <amirjanyan@gmail.com>",
"Nochum Sossonko <nsossonko@hotmail.com>",
"Liam Newman <bitwiseman@gmail.com>"
],
"license": "MIT",
"dependencies": {
"config-chain": "~1.1.5",
"mkdirp": "~0.5.0",
"nopt": "~3.0.1"
},
"devDependencies": {
"jshint": "~2.8.0",
"node-static": "~0.7.1",
"mustache": "~1.1.0",
"requirejs": "2.1.x",
"benchmark": "1.0.0"
}
}

View file

@ -0,0 +1,2 @@
include js-beautify
include js-beautify-test

View file

@ -0,0 +1,467 @@
from __future__ import print_function
import sys
import re
from cssbeautifier.__version__ import __version__
#
# The MIT License (MIT)
# Copyright (c) 2013 Einar Lielmanis and contributors.
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
class BeautifierOptions:
def __init__(self):
self.indent_size = 4
self.indent_char = ' '
self.indent_with_tabs = False
self.selector_separator_newline = True
self.end_with_newline = False
self.newline_between_rules = True
self.eol = '\n'
def __repr__(self):
return \
"""indent_size = %d
indent_char = [%s]
indent_with_tabs = [%s]
separate_selectors_newline = [%s]
end_with_newline = [%s]
newline_between_rules = [%s]
""" % (self.indent_size, self.indent_char, self.indent_with_tabs,
self.selector_separator_newline, self.end_with_newline, self.newline_between_rules)
def default_options():
return BeautifierOptions()
def beautify(string, opts=default_options()):
b = Beautifier(string, opts)
return b.beautify()
def beautify_file(file_name, opts=default_options()):
if file_name == '-': # stdin
stream = sys.stdin
else:
stream = open(file_name)
content = ''.join(stream.readlines())
b = Beautifier(content, opts)
return b.beautify()
def usage(stream=sys.stdout):
print("cssbeautifier.py@" + __version__ + """
CSS beautifier (http://jsbeautifier.org/)
""", file=stream)
if stream == sys.stderr:
return 1
else:
return 0
WHITE_RE = re.compile("^\s+$")
WORD_RE = re.compile("[\w$\-_]")
class Printer:
def __init__(self, indent_char, indent_size, default_indent=""):
self.indentSize = indent_size
self.singleIndent = (indent_size) * indent_char
self.indentLevel = 0
self.nestedLevel = 0
self.baseIndentString = default_indent
self.output = []
def __lastCharWhitespace(self):
return len(self.output) > 0 and WHITE_RE.search(self.output[-1]) is not None
def indent(self):
self.indentLevel += 1
self.baseIndentString += self.singleIndent
def outdent(self):
if self.indentLevel:
self.indentLevel -= 1
self.baseIndentString = self.baseIndentString[:-(len(self.singleIndent))]
def push(self, string):
self.output.append(string)
def openBracket(self):
self.singleSpace()
self.output.append("{")
self.newLine()
def closeBracket(self):
self.newLine()
self.output.append("}")
self.newLine()
def semicolon(self):
self.output.append(";")
self.newLine()
def comment(self, comment):
self.output.append(comment)
def newLine(self, keepWhitespace=False):
if len(self.output) > 0 :
if not keepWhitespace and self.output[-1] != '\n':
self.trim()
self.output.append("\n")
if len(self.baseIndentString) > 0:
self.output.append(self.baseIndentString)
def trim(self):
while self.__lastCharWhitespace():
self.output.pop()
def singleSpace(self):
if len(self.output) > 0 and not self.__lastCharWhitespace():
self.output.append(" ")
def preserveSingleSpace(self,isAfterSpace):
if isAfterSpace:
self.singleSpace()
def result(self):
if self.baseIndentString:
return self.baseIndentString + "".join(self.output);
else:
return "".join(self.output)
class Beautifier:
def __init__(self, source_text, opts=default_options()):
# This is not pretty, but given how we did the version import
# it is the only way to do this without having setup.py fail on a missing six dependency.
self.six = __import__("six")
if not source_text:
source_text = ''
# HACK: newline parsing inconsistent. This brute force normalizes the input newlines.
lineBreak = re.compile(self.six.u("\r\n|[\r\u2028\u2029]"))
source_text = re.sub(lineBreak, '\n', source_text)
self.source_text = source_text
self.opts = opts
self.indentSize = opts.indent_size
self.indentChar = opts.indent_char
self.pos = -1
self.ch = None
if self.opts.indent_with_tabs:
self.indentChar = "\t"
self.indentSize = 1
self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
# https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
# also in CONDITIONAL_GROUP_RULE below
self.NESTED_AT_RULE = [ \
"@page", \
"@font-face", \
"@keyframes", \
"@media", \
"@supports", \
"@document"]
self.CONDITIONAL_GROUP_RULE = [ \
"@media", \
"@supports", \
"@document"]
def next(self):
self.pos = self.pos + 1
if self.pos < len(self.source_text):
self.ch = self.source_text[self.pos]
else:
self.ch = ''
return self.ch
def peek(self,skipWhitespace=False):
start = self.pos
if skipWhitespace:
self.eatWhitespace()
result = ""
if self.pos + 1 < len(self.source_text):
result = self.source_text[self.pos + 1]
if skipWhitespace:
self.pos = start - 1
self.next()
return result
def eatString(self, endChars):
start = self.pos
while self.next():
if self.ch == "\\":
self.next()
elif self.ch in endChars:
break
elif self.ch == "\n":
break
return self.source_text[start:self.pos] + self.ch
def peekString(self, endChar):
start = self.pos
st = self.eatString(endChar)
self.pos = start - 1
self.next()
return st
def eatWhitespace(self):
result = ''
while WHITE_RE.search(self.peek()) is not None:
self.next()
result += self.ch
return result
def skipWhitespace(self):
result = ''
if self.ch and WHITE_RE.search(self.ch):
result = self.ch
while WHITE_RE.search(self.next()) is not None:
result += self.ch
return result
def eatComment(self):
start = self.pos
singleLine = self.peek() == "/"
self.next()
while self.next():
if not singleLine and self.ch == "*" and self.peek() == "/":
self.next()
break
elif singleLine and self.ch == "\n":
return self.source_text[start:self.pos]
return self.source_text[start:self.pos] + self.ch
def lookBack(self, string):
past = self.source_text[self.pos - len(string):self.pos]
return past.lower() == string
# Nested pseudo-class if we are insideRule
# and the next special character found opens
# a new block
def foundNestedPseudoClass(self):
i = self.pos + 1
openParen = 0
while i < len(self.source_text):
ch = self.source_text[i]
if ch == "{":
return True
elif ch == "(":
# pseudoclasses can contain ()
openParen += 1
elif ch == ")":
if openParen == 0:
return False
openParen -= 1
elif ch == ";" or ch == "}":
return False
i += 1;
return False
def beautify(self):
m = re.search("^[\t ]*", self.source_text)
baseIndentString = m.group(0)
printer = Printer(self.indentChar, self.indentSize, baseIndentString)
insideRule = False
insidePropertyValue = False
enteringConditionalGroup = False
top_ch = ''
last_top_ch = ''
parenLevel = 0
while True:
whitespace = self.skipWhitespace()
isAfterSpace = whitespace != ''
isAfterNewline = '\n' in whitespace
last_top_ch = top_ch
top_ch = self.ch
if not self.ch:
break
elif self.ch == '/' and self.peek() == '*':
header = printer.indentLevel == 0
if not isAfterNewline or header:
printer.newLine()
comment = self.eatComment()
printer.comment(comment)
printer.newLine()
if header:
printer.newLine(True)
elif self.ch == '/' and self.peek() == '/':
if not isAfterNewline and last_top_ch != '{':
printer.trim()
printer.singleSpace()
printer.comment(self.eatComment())
printer.newLine()
elif self.ch == '@':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
# strip trailing space, if present, for hash property check
variableOrRule = self.peekString(": ,;{}()[]/='\"")
if variableOrRule[-1] in ": ":
# wwe have a variable or pseudo-class, add it and insert one space before continuing
self.next()
variableOrRule = self.eatString(": ")
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
printer.push(variableOrRule)
printer.singleSpace();
if variableOrRule[-1].isspace():
variableOrRule = variableOrRule[:-1]
# might be a nesting at-rule
if variableOrRule in self.NESTED_AT_RULE:
printer.nestedLevel += 1
if variableOrRule in self.CONDITIONAL_GROUP_RULE:
enteringConditionalGroup = True
elif self.ch == '#' and self.peek() == '{':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.eatString('}'));
elif self.ch == '{':
if self.peek(True) == '}':
self.eatWhitespace()
self.next()
printer.singleSpace()
printer.push("{}")
printer.newLine()
if self.opts.newline_between_rules and printer.indentLevel == 0:
printer.newLine(True)
else:
printer.indent()
printer.openBracket()
# when entering conditional groups, only rulesets are allowed
if enteringConditionalGroup:
enteringConditionalGroup = False
insideRule = printer.indentLevel > printer.nestedLevel
else:
# otherwise, declarations are also allowed
insideRule = printer.indentLevel >= printer.nestedLevel
elif self.ch == '}':
printer.outdent()
printer.closeBracket()
insideRule = False
insidePropertyValue = False
if printer.nestedLevel:
printer.nestedLevel -= 1
if self.opts.newline_between_rules and printer.indentLevel == 0:
printer.newLine(True)
elif self.ch == ":":
self.eatWhitespace()
if (insideRule or enteringConditionalGroup) and \
not (self.lookBack('&') or self.foundNestedPseudoClass()):
# 'property: value' delimiter
# which could be in a conditional group query
insidePropertyValue = True
printer.push(":")
printer.singleSpace()
else:
# sass/less parent reference don't use a space
# sass nested pseudo-class don't use a space
if self.peek() == ":":
# pseudo-element
self.next()
printer.push("::")
else:
# pseudo-element
printer.push(":")
elif self.ch == '"' or self.ch == '\'':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.eatString(self.ch))
elif self.ch == ';':
insidePropertyValue = False
printer.semicolon()
elif self.ch == '(':
# may be a url
if self.lookBack("url"):
printer.push(self.ch)
self.eatWhitespace()
if self.next():
if self.ch is not ')' and self.ch is not '"' \
and self.ch is not '\'':
printer.push(self.eatString(')'))
else:
self.pos = self.pos - 1
else:
parenLevel += 1
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
self.eatWhitespace()
elif self.ch == ')':
printer.push(self.ch)
parenLevel -= 1
elif self.ch == ',':
printer.push(self.ch)
self.eatWhitespace()
if not insidePropertyValue and self.opts.selector_separator_newline and parenLevel < 1:
printer.newLine()
else:
printer.singleSpace()
elif self.ch == ']':
printer.push(self.ch)
elif self.ch == '[':
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
elif self.ch == '=':
# no whitespace before or after
self.eatWhitespace()
self.ch = '='
printer.push(self.ch)
else:
printer.preserveSingleSpace(isAfterSpace)
printer.push(self.ch)
sweet_code = re.sub('[\r\n\t ]+$', '', printer.result())
# establish end_with_newline
if self.opts.end_with_newline:
sweet_code += '\n'
if not self.opts.eol == '\n':
sweet_code = sweet_code.replace('\n', self.opts.eol)
return sweet_code

View file

@ -0,0 +1 @@
__version__ = '1.0.0'

View file

@ -0,0 +1,342 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import cssbeautifier
class CSSBeautifierTest(unittest.TestCase):
def resetOptions(self):
false = False
true = True
self.options = cssbeautifier.default_options()
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
def testGenerated(self):
self.resetOptions()
test_fragment = self.decodesto
t = self.decodesto
false = False
true = True
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
# End With Newline - (eof = "\n")
self.options.end_with_newline = true
test_fragment('', '\n')
test_fragment(' .tabs{}', ' .tabs {}\n')
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}\n')
test_fragment('\n')
# End With Newline - (eof = "")
self.options.end_with_newline = false
test_fragment('')
test_fragment(' .tabs{}', ' .tabs {}')
test_fragment(' \n\n.tabs{}\n\n\n\n', ' .tabs {}')
test_fragment('\n', '')
# Empty braces
t('.tabs{}', '.tabs {}')
t('.tabs { }', '.tabs {}')
t('.tabs { }', '.tabs {}')
t('.tabs \n{\n \n }', '.tabs {}')
#
t('#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}', '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}')
# Selector Separator - (separator = " ", separator1 = " ")
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}')
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = " ", separator1 = " ")
self.options.selector_separator_newline = false
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla, #foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab, .bat {}\n}')
t('#bla, #foo{color:black}', '#bla, #foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child, a:first-child {\n\tcolor: red;\n\tdiv:first-child, div:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}')
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}')
# Selector Separator - (separator = "\n", separator1 = "\n\t")
self.options.selector_separator_newline = true
self.options.selector_separator = " "
t('#bla, #foo{color:green}', '#bla,\n#foo {\n\tcolor: green\n}')
t('@media print {.tab{}}', '@media print {\n\t.tab {}\n}')
t('@media print {.tab,.bat{}}', '@media print {\n\t.tab,\n\t.bat {}\n}')
t('#bla, #foo{color:black}', '#bla,\n#foo {\n\tcolor: black\n}')
t('a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}', 'a:first-child,\na:first-child {\n\tcolor: red;\n\tdiv:first-child,\n\tdiv:hover {\n\t\tcolor: black;\n\t}\n}')
# Newline Between Rules - (separator = "\n")
self.options.newline_between_rules = true
t('.div {}\n.span {}', '.div {}\n\n.span {}')
t('.div{}\n \n.span{}', '.div {}\n\n.span {}')
t('.div {} \n \n.span { } \n', '.div {}\n\n.span {}')
t('.div {\n \n} \n .span {\n } ', '.div {}\n\n.span {}')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n\n.div {\n\theight: 15px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n\n.div {\n\theight: 15px;\n}')
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}')
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n\n.div {\n\theight: 15px;\n}')
# Newline Between Rules - (separator = "")
self.options.newline_between_rules = false
t('.div {}\n.span {}')
t('.div{}\n \n.span{}', '.div {}\n.span {}')
t('.div {} \n \n.span { } \n', '.div {}\n.span {}')
t('.div {\n \n} \n .span {\n } ', '.div {}\n.span {}')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div {\n\theight: 15px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n.div {\n\theight: 15px;\n}')
t('#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}')
t('a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}')
t('a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n.div {\n\theight: 15px;\n}')
# Functions braces
t('.tabs(){}', '.tabs() {}')
t('.tabs (){}', '.tabs () {}')
t('.tabs (pa, pa(1,2)), .cols { }', '.tabs (pa, pa(1, 2)),\n.cols {}')
t('.tabs(pa, pa(1,2)), .cols { }', '.tabs(pa, pa(1, 2)),\n.cols {}')
t('.tabs ( ) { }', '.tabs () {}')
t('.tabs( ) { }', '.tabs() {}')
t('.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}')
t('.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}')
# Comments
t('/* test */')
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}')
t('.tabs{/* test */}', '.tabs {\n\t/* test */\n}')
t('/* header */.tabs {}', '/* header */\n\n.tabs {}')
t('.tabs {\n/* non-header */\nwidth:10px;}', '.tabs {\n\t/* non-header */\n\twidth: 10px;\n}')
t('/* header')
t('// comment')
t('.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}', '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}')
# single line comment support (less/sass)
t('.tabs{\n// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}')
t('.tabs{// comment\nwidth:10px;\n}', '.tabs {\n\t// comment\n\twidth: 10px;\n}')
t('//comment\n.tabs{width:10px;}', '//comment\n.tabs {\n\twidth: 10px;\n}')
t('.tabs{//comment\n//2nd single line comment\nwidth:10px;}', '.tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}')
t('.tabs{width:10px;//end of line comment\n}', '.tabs {\n\twidth: 10px; //end of line comment\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px;\n}')
t('.tabs{width:10px;//end of line comment\nheight:10px;//another\n}', '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}')
# Psuedo-classes vs Variables
t('@page :first {}')
# Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.
t('@page:first {}', '@page: first {}')
t('@page: first {}')
# SASS/SCSS
# Basic Interpolation
t('p {\n\t$font-size: 12px;\n\t$line-height: 30px;\n\tfont: #{$font-size}/#{$line-height};\n}')
t('p.#{$name} {}')
t(
'@mixin itemPropertiesCoverItem($items, $margin) {\n' +
'\twidth: calc((100% - ((#{$items} - 1) * #{$margin}rem)) / #{$items});\n' +
'\tmargin: 1.6rem #{$margin}rem 1.6rem 0;\n' +
'}')
#
def testNewline(self):
self.resetOptions()
t = self.decodesto
self.options.end_with_newline = True
t("", "\n")
t("\n", "\n")
t(".tabs{}\n", ".tabs {}\n")
t(".tabs{}", ".tabs {}\n")
def testBasics(self):
self.resetOptions()
t = self.decodesto
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
t(".tabs{}", ".tabs {}")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}")
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
# lead-in whitespace determines base-indent.
# lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testSeperateSelectors(self):
self.resetOptions()
t = self.decodesto
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testBlockNesting(self):
self.resetOptions()
t = self.decodesto
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
# @font-face {
# font-family: 'Bitstream Vera Serif Bold';
# src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
# }
# @media screen {
# #foo:hover {
# background-image: url(foo.png);
# }
# @media screen and (min-device-pixel-ratio: 2) {
# @font-face {
# font-family: 'Helvetica Neue'
# }
# #foo:hover {
# background-image: url(foo@2x.png);
# }
# }
# }
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}")
def testOptions(self):
self.resetOptions()
self.options.indent_size = 2
self.options.indent_char = ' '
self.options.selector_separator_newline = False
t = self.decodesto
# pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}")
t("#foo *:hover {\n color: purple\n}")
t("::selection {\n color: #ff0000;\n}")
# TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}")
# particular edge case with braces and semicolons inside tags that allows custom text
t( "a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}")
def testLessCss(self):
self.resetOptions()
t = self.decodesto
t('.well{ \n @well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}')
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}')
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}')
# Not sure if this is sensible
# but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
# import
t('@import "test";');
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}")
# nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}")
# variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
def decodesto(self, input, expectation=None):
if expectation == None:
expectation = input
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
if not expectation != input:
self.assertMultiLineEqual(
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
input = input.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,7 @@
#! /usr/bin/env python
#
# Stub script to run jsbeautifier
#
import sys
from jsbeautifier import main
sys.exit(main())

View file

@ -0,0 +1,16 @@
#! /usr/bin/env python
import sys
import unittest
#Speedup things...
try:
import cProfile as profile
except ImportError:
import profile
def run():
sys.argv.append('discover')
unittest.main()
profile.run('run()')

View file

@ -0,0 +1,10 @@
#! /bin/sh
#
# Test suite launcher
#
if [ -z $PYTHON ]; then
env python -m unittest discover "$@"
else
env $PYTHON -m unittest discover "$@"
fi

View file

@ -0,0 +1 @@
__version__ = '1.5.10'

View file

@ -0,0 +1,137 @@
#!/bin/bash
REL_SCRIPT_DIR="`dirname \"$0\"`"
SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`"
test_cli_common()
{
echo ----------------------------------------
echo Testing common cli behavior...
CLI_SCRIPT_NAME=${1:?missing_param}
CLI_SCRIPT=$SCRIPT_DIR/../../$CLI_SCRIPT_NAME
echo Script: $CLI_SCRIPT
# should find the minimal help output
$CLI_SCRIPT 2>&1 | grep -q "Must define at least one file\." || {
echo "[$CLI_SCRIPT_NAME] Output should be help message."
exit 1
}
$CLI_SCRIPT 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error."
exit 1
}
$CLI_SCRIPT -Z 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME -Z] Return code for invalid parameter should be error."
exit 1
}
$CLI_SCRIPT -h > /dev/null || {
echo "[$CLI_SCRIPT_NAME -h] Return code should be success."
exit 1
}
$CLI_SCRIPT -v > /dev/null || {
echo "[$CLI_SCRIPT_NAME -v] Return code should be success."
exit 1
}
MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file"
MISSING_FILE_MESSAGE="No such file or directory"
$CLI_SCRIPT $MISSING_FILE 2> /dev/null && {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error."
exit 1
}
$CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || {
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message."
exit 1
}
if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then
echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text."
exit 1
fi
}
test_cli_js_beautify()
{
echo ----------------------------------------
echo Testing js-beautify cli behavior...
CLI_SCRIPT=$SCRIPT_DIR/../../js-beautify
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js > /dev/null || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected succeed."
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js was expected to be unchanged."
exit 1
}
rm -rf /tmp/js-beautify-mkdir
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js $SCRIPT_DIR/../../../js/bin/js-beautify.js && diff $SCRIPT_DIR/../../../js/bin/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js || {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/js-beautify.js should have been created in /tmp/js-beautify-mkdir/js-beautify.js."
exit 1
}
# ensure unchanged files are not overwritten
cp -p /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js
touch -A -05 /tmp/js-beautify-mkdir/js-beautify.js
touch -A -01 /tmp/js-beautify-mkdir/js-beautify-old.js
$CLI_SCRIPT -r /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -r"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and same file name"
exit 1
}
$CLI_SCRIPT -o /tmp/js-beautify-mkdir/js-beautify.js /tmp/js-beautify-mkdir/js-beautify-old.js && test /tmp/js-beautify-mkdir/js-beautify.js -nt /tmp/js-beautify-mkdir/js-beautify-old.js && {
echo "js-beautify should not replace unchanged file /tmp/js-beautify-mkdir/js-beautify.js when using -o and different file name"
exit 1
}
$CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js | diff -q $SCRIPT_DIR/../../../js/bin/css-beautify.js - && {
echo "js-beautify output for $SCRIPT_DIR/../../../js/bin/css-beautify.js was expected to be different."
exit 1
}
}
test_smoke_js_beautify()
{
echo ----------------------------------------
echo Testing beautify functionality...
PYTHON=python $SCRIPT_DIR/../../js-beautify-test || exit 1
}
test_perf_js_beautify()
{
echo ----------------------------------------
echo Testing beautify performance...
# PYTHON=python $SCRIPT_DIR/../../js-beautify-profile || exit 1
python $SCRIPT_DIR/test-perf-jsbeautifier.py || exit 1
}
#test_cli_common css-beautify
#test_cli_common html-beautify
test_cli_common js-beautify
test_cli_js_beautify
test_smoke_js_beautify
test_perf_js_beautify
echo ----------------------------------------
echo $0 - PASSED.
echo ----------------------------------------

View file

@ -0,0 +1,36 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import copy
import jsbeautifier
options = jsbeautifier.default_options()
options.wrap_line_length = 80
data = ''
data_min = ''
def beautifier_test_underscore():
jsbeautifier.beautify(data, options)
def beautifier_test_underscore_min():
jsbeautifier.beautify(data_min, options)
def report_perf(fn):
import timeit
iter = 50
time = timeit.timeit(fn + "()", setup="from __main__ import " + fn + "; gc.enable()", number=iter)
print(fn + ": " + str(iter/time) + " cycles/sec")
if __name__ == '__main__':
dirname = os.path.dirname(os.path.abspath(__file__))
underscore_file = os.path.join(dirname, "../../../", "test/underscore.js")
underscore_min_file = os.path.join(dirname, "../../../", "test/underscore-min.js")
data = copy.copy(''.join(open(underscore_file).readlines()))
data_min = copy.copy(''.join(open(underscore_min_file).readlines()))
# warm up
beautifier_test_underscore()
beautifier_test_underscore_min()
report_perf("beautifier_test_underscore")
report_perf("beautifier_test_underscore_min")

View file

@ -0,0 +1,43 @@
import re
import unittest
import jsbeautifier
class TestJSBeautifierIndentation(unittest.TestCase):
def test_tabs(self):
test_fragment = self.decodesto
self.options.indent_with_tabs = 1;
test_fragment('{tabs()}', "{\n\ttabs()\n}");
def test_function_indent(self):
test_fragment = self.decodesto
self.options.indent_with_tabs = 1;
self.options.keep_function_indentation = 1;
test_fragment('var foo = function(){ bar() }();', "var foo = function() {\n\tbar()\n}();");
self.options.tabs = 1;
self.options.keep_function_indentation = 0;
test_fragment('var foo = function(){ baz() }();', "var foo = function() {\n\tbaz()\n}();");
def decodesto(self, input, expectation=None):
self.assertEqual(
jsbeautifier.beautify(input, self.options), expectation or input)
@classmethod
def setUpClass(cls):
options = jsbeautifier.default_options()
options.indent_size = 4
options.indent_char = ' '
options.preserve_newlines = True
options.jslint_happy = False
options.keep_array_indentation = False
options.brace_style = 'collapse'
options.indent_level = 0
cls.options = options
cls.wrapregex = re.compile('^(.+)$', re.MULTILINE)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,25 @@
# UNPACKERS SPECIFICATIONS
Nothing very difficult: an unpacker is a submodule placed in the directory
where this file was found. Each unpacker must define three symbols:
* `PRIORITY` : integer number expressing the priority in applying this
unpacker. Lower number means higher priority.
Makes sense only if a source file has been packed with
more than one packer.
* `detect(source)` : returns `True` if source is packed, otherwise, `False`.
* `unpack(source)` : takes a `source` string and unpacks it. Must always return
valid JavaScript. That is to say, your code should look
like:
```
if detect(source):
return do_your_fancy_things_with(source)
else:
return source
```
*You can safely define any other symbol in your module, as it will be ignored.*
`__init__` code will automatically load new unpackers, without any further step
to be accomplished. Simply drop it in this directory.

View file

@ -0,0 +1,67 @@
#
# General code for JSBeautifier unpackers infrastructure. See README.specs
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""General code for JSBeautifier unpackers infrastructure."""
import pkgutil
import re
from jsbeautifier.unpackers import evalbased
# NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js!
BLACKLIST = ['jsbeautifier.unpackers.evalbased']
class UnpackingError(Exception):
"""Badly packed source or general error. Argument is a
meaningful description."""
pass
def getunpackers():
"""Scans the unpackers dir, finds unpackers and add them to UNPACKERS list.
An unpacker will be loaded only if it is a valid python module (name must
adhere to naming conventions) and it is not blacklisted (i.e. inserted
into BLACKLIST."""
path = __path__
prefix = __name__ + '.'
unpackers = []
interface = ['unpack', 'detect', 'PRIORITY']
for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix):
if 'tests' not in modname and modname not in BLACKLIST:
try:
module = __import__(modname, fromlist=interface)
except ImportError:
raise UnpackingError('Bad unpacker: %s' % modname)
else:
unpackers.append(module)
return sorted(unpackers, key = lambda mod: mod.PRIORITY)
UNPACKERS = getunpackers()
def run(source, evalcode=False):
"""Runs the applicable unpackers and return unpacked source as a string."""
for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]:
source = unpacker.unpack(source)
if evalcode and evalbased.detect(source):
source = evalbased.unpack(source)
return source
def filtercomments(source):
"""NOT USED: strips trailing comments and put them at the top."""
trailing_comments = []
comment = True
while comment:
if re.search(r'^\s*\/\*', source):
comment = source[0, source.index('*/') + 2]
elif re.search(r'^\s*\/\/', source):
comment = re.search(r'^\s*\/\/', source).group(0)
else:
comment = None
if comment:
source = re.sub(r'^\s+', '', source[len(comment):])
trailing_comments.append(comment)
return '\n'.join(trailing_comments) + source

View file

@ -0,0 +1,39 @@
#
# Unpacker for eval() based packers, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for eval() based packers: runs JS code and returns result.
Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and
properly set up on host."""
from subprocess import PIPE, Popen
PRIORITY = 3
def detect(source):
"""Detects if source is likely to be eval() packed."""
return source.strip().lower().startswith('eval(function(')
def unpack(source):
"""Runs source and return resulting code."""
return jseval('print %s;' % source[4:]) if detect(source) else source
# In case of failure, we'll just return the original, without crashing on user.
def jseval(script):
"""Run code in the JS interpreter and return output."""
try:
interpreter = Popen(['js'], stdin=PIPE, stdout=PIPE)
except OSError:
return script
result, errors = interpreter.communicate(script)
if interpreter.poll() or errors:
return script
return result

View file

@ -0,0 +1,58 @@
#
# simple unpacker/deobfuscator for scripts messed up with
# javascriptobfuscator.com
#
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# if javascriptobfuscator.detect(some_string):
# some_string = javascriptobfuscator.unpack(some_string)
#
"""deobfuscator for scripts messed up with JavascriptObfuscator.com"""
import re
PRIORITY = 1
def smartsplit(code):
"""Split `code` at " symbol, only if it is not escaped."""
strings = []
pos = 0
while pos < len(code):
if code[pos] == '"':
word = '' # new word
pos += 1
while pos < len(code):
if code[pos] == '"':
break
if code[pos] == '\\':
word += '\\'
pos += 1
word += code[pos]
pos += 1
strings.append('"%s"' % word)
pos += 1
return strings
def detect(code):
"""Detects if `code` is JavascriptObfuscator.com packed."""
# prefer `is not` idiom, so that a true boolean is returned
return (re.search(r'^var _0x[a-f0-9]+ ?\= ?\[', code) is not None)
def unpack(code):
"""Unpacks JavascriptObfuscator.com packed code."""
if detect(code):
matches = re.search(r'var (_0x[a-f\d]+) ?\= ?\[(.*?)\];', code)
if matches:
variable = matches.group(1)
dictionary = smartsplit(matches.group(2))
code = code[len(matches.group(0)):]
for key, value in enumerate(dictionary):
code = code.replace(r'%s[%s]' % (variable, key), value)
return code

View file

@ -0,0 +1,86 @@
#
# deobfuscator for scripts messed up with myobfuscate.com
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
# CAVEAT by Einar Lielmanis
#
# You really don't want to obfuscate your scripts there: they're tracking
# your unpackings, your script gets turned into something like this,
# as of 2011-08-26:
#
# var _escape = 'your_script_escaped';
# var _111 = document.createElement('script');
# _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' +
# '&ref=' + encodeURIComponent(document.referrer) +
# '&url=' + encodeURIComponent(document.URL);
# var 000 = document.getElementsByTagName('head')[0];
# 000.appendChild(_111);
# document.write(unescape(_escape));
#
"""Deobfuscator for scripts messed up with MyObfuscate.com"""
import re
import base64
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
from jsbeautifier.unpackers import UnpackingError
PRIORITY = 1
CAVEAT = """//
// Unpacker warning: be careful when using myobfuscate.com for your projects:
// scripts obfuscated by the free online version call back home.
//
"""
SIGNATURE = (r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F'
r'\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65'
r'\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75'
r'\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B'
r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78'
r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","'
r'\x6C\x65\x6E\x67\x74\x68"]')
def detect(source):
"""Detects MyObfuscate.com packer."""
return SIGNATURE in source
def unpack(source):
"""Unpacks js code packed with MyObfuscate.com"""
if not detect(source):
return source
payload = unquote(_filter(source))
match = re.search(r"^var _escape\='<script>(.*)<\/script>'",
payload, re.DOTALL)
polished = match.group(1) if match else source
return CAVEAT + polished
def _filter(source):
"""Extracts and decode payload (original file) from `source`"""
try:
varname = re.search(r'eval\(\w+\(\w+\((\w+)\)\)\);', source).group(1)
reverse = re.search(r"var +%s *\= *'(.*)';" % varname, source).group(1)
except AttributeError:
raise UnpackingError('Malformed MyObfuscate data.')
try:
return base64.b64decode(reverse[::-1].encode('utf8')).decode('utf8')
except TypeError:
raise UnpackingError('MyObfuscate payload is not base64-encoded.')

View file

@ -0,0 +1,112 @@
#
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
import re
import string
from jsbeautifier.unpackers import UnpackingError
PRIORITY = 1
def detect(source):
"""Detects whether `source` is P.A.C.K.E.R. coded."""
return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,')
def unpack(source):
"""Unpacks P.A.C.K.E.R. packed js code."""
payload, symtab, radix, count = _filterargs(source)
if count != len(symtab):
raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
try:
unbase = Unbaser(radix)
except TypeError:
raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')
def lookup(match):
"""Look up symbols in the synthetic symtab."""
word = match.group(0)
return symtab[unbase(word)] or word
source = re.sub(r'\b\w+\b', lookup, payload)
return _replacestrings(source)
def _filterargs(source):
"""Juice from a source file the four args needed by decoder."""
juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"),
(r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"),
]
for juicer in juicers:
args = re.search(juicer, source, re.DOTALL)
if args:
a = args.groups()
try:
return a[0], a[3].split('|'), int(a[1]), int(a[2])
except ValueError:
raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
# could not find a satisfying regex
raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)')
def _replacestrings(source):
"""Strip string lookup table (list) and replace values in source."""
match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
if match:
varname, strings = match.groups()
startpoint = len(match.group(0))
lookup = strings.split('","')
variable = '%s[%%d]' % varname
for index, value in enumerate(lookup):
source = source.replace(variable % index, '"%s"' % value)
return source[startpoint:]
return source
class Unbaser(object):
"""Functor for a given base. Will efficiently convert
strings to natural numbers."""
ALPHABET = {
62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
}
def __init__(self, base):
self.base = base
# If base can be handled by int() builtin, let it do it for us
if 2 <= base <= 36:
self.unbase = lambda string: int(string, base)
else:
# Build conversion dictionary cache
try:
self.dictionary = dict((cipher, index) for
index, cipher in enumerate(self.ALPHABET[base]))
except KeyError:
raise TypeError('Unsupported base encoding.')
self.unbase = self._dictunbaser
def __call__(self, string):
return self.unbase(string)
def _dictunbaser(self, string):
"""Decodes a value to an integer."""
ret = 0
for index, cipher in enumerate(string[::-1]):
ret += (self.base ** index) * self.dictionary[cipher]
return ret

View file

@ -0,0 +1,2 @@
# Empty file :)
# pylint: disable=C0111

View file

@ -0,0 +1,65 @@
//
// Unpacker warning: be careful when using myobfuscate.com for your projects:
// scripts obfuscated by the free online version call back home.
//
//
// Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
// written by Einar Lielmanis <einar@jsbeautifier.org>
//
// Coincidentally, it can defeat a couple of other eval-based compressors.
//
// usage:
//
// if (P_A_C_K_E_R.detect(some_string)) {
// var unpacked = P_A_C_K_E_R.unpack(some_string);
// }
//
//
var P_A_C_K_E_R = {
detect: function (str) {
return P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval(function(') ||
P_A_C_K_E_R._starts_with(str.toLowerCase().replace(/ +/g, ''), 'eval((function(') ;
},
unpack: function (str) {
var unpacked_source = '';
if (P_A_C_K_E_R.detect(str)) {
try {
eval('unpacked_source = ' + str.substring(4) + ';')
if (typeof unpacked_source == 'string' && unpacked_source) {
str = unpacked_source;
}
} catch (error) {
// well, it failed. we'll just return the original, instead of crashing on user.
}
}
return str;
},
_starts_with: function (str, what) {
return str.substr(0, what.length) === what;
},
run_tests: function (sanity_test) {
var t = sanity_test || new SanityTest();
t.test_function(P_A_C_K_E_R.detect, "P_A_C_K_E_R.detect");
t.expect('', false);
t.expect('var a = b', false);
t.expect('eval(function(p,a,c,k,e,r', true);
t.expect('eval ( function(p, a, c, k, e, r', true);
t.test_function(P_A_C_K_E_R.unpack, 'P_A_C_K_E_R.unpack');
t.expect("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',3,3,'var||a'.split('|'),0,{}))",
'var a=1');
var starts_with_a = function(what) { return P_A_C_K_E_R._starts_with(what, 'a'); }
t.test_function(starts_with_a, "P_A_C_K_E_R._starts_with(?, a)");
t.expect('abc', true);
t.expect('bcd', false);
t.expect('a', true);
t.expect('', false);
return t;
}
}

View file

@ -0,0 +1 @@
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 H=(j["Y"]["n"]("1h")!=-1)?s:v;9 I=(j["Y"]["w"]()["n"]("1i")!=-1)?s:v;9 J=(j["K"]["n"]("1j")!=-1)?s:v;p Z(){9 a;9 b;9 c;y{b=o z("l.l.7");a=b.P("$Q")}A(c){};i(!a){y{b=o z("l.l.6");a="R 6,0,21,0";b["1k"]="1l";a=b.P("$Q")}A(c){}};i(!a){y{b=o z("l.l.3");a=b.P("$Q")}A(c){}};i(!a){y{b=o z("l.l.3");a="R 3,0,18,0"}A(c){}};i(!a){y{b=o z("l.l");a="R 2,0,0,11"}A(c){a=-1}};m a};p 10(){9 a=-1;i(j["t"]!=12&&j["t"]["13"]>0){i(j["t"]["L M 2.0"]||j["t"]["L M"]){9 b=j["t"]["L M 2.0"]?" 2.0":"";9 c=j["t"]["L M"+b]["1m"];9 d=c["B"](" ");9 e=d[2]["B"](".");9 f=e[0];9 g=e[1];9 h=d[3];i(h==""){h=d[4]};i(h[0]=="d"){h=h["S"](1)}k{i(h[0]=="r"){h=h["S"](1);i(h["n"]("d")>0){h=h["S"](0,h["n"]("d"))}}};9 a=f+"."+g+"."+h}}k{i(j["K"]["w"]()["n"]("T/2.6")!=-1){a=4}k{i(j["K"]["w"]()["n"]("T/2.5")!=-1){a=3}k{i(j["K"]["w"]()["n"]("T")!=-1){a=2}k{i(H&&I&&!J){a=Z()}}}}};m a};p 1n(a,b,c){C=10();i(C==-1){m v}k{i(C!=0){i(H&&I&&!J){14=C["B"](" ");15=14[1];D=15["B"](",")}k{D=C["B"](".")};9 d=D[0];9 e=D[1];9 f=D[2];i(d>E(a)){m s}k{i(d==E(a)){i(e>E(b)){m s}k{i(e==E(b)){i(f>=E(c)){m s}}}}};m v}}};p 16(a,b){i(a["n"]("?")!=-1){m a["1o"](/\\?/,b+"?")}k{m a+b}};p U(a,b,c){9 d="";i(H&&I&&!J){d+="<17 ";N(9 e V a){d+=e+"=""+a[e]+"" "};d+=">";N(9 e V b){d+="<1p 19=""+e+"" 1q=""+b[e]+"" /> "};d+="</17>"}k{d+="<1a ";N(9 e V c){d+=e+"=""+c[e]+"" "};d+="> </1a>"};1r["1s"](d)};p 1t(){9 a=W(1b,".1u","1c","1d:1v-1w-1x-1y-1e","1z/x-1A-1B");U(a["u"],a["F"],a["q"])};p 1C(){9 a=W(1b,".1D","X","1d:1E-1F-1G-1H-1e",12);U(a["u"],a["F"],a["q"])};p W(a,b,c,d,e){9 f=o O();f["q"]=o O();f["F"]=o O();f["u"]=o O();N(9 g=0;g<a["13"];g=g+2){9 h=a[g]["w"]();1I(h){8"1f":G;8"1J":f["q"][a[g]]=a[g+1];G;8"X":;8"1c":a[g+1]=16(a[g+1],b);f["q"]["X"]=a[g+1];f["F"][c]=a[g+1];G;8"1K":;8"1L":;8"1M":;8"1N":;8"1O":;8"1P":;8"1Q":;8"1R":;8"1S":;8"1T":;8"1U":;8"1V":;8"1W":;8"1X":;8"1Y":;8"1Z":;8"20":;8"22":;8"23":;8"24":;8"25":;8"26":;8"27":;8"28":;8"29":;8"2a":;8"2b":;8"2c":;8"2d":;8"2e":;8"2f":;8"2g":;8"2h":;8"2i":;8"2j":;8"2k":;8"2l":;8"1g":;8"2m":;8"2n":f["u"][a[g]]=a[g+1];G;8"2o":;8"2p":;8"2q":;8"2r":;8"2s":;8"2t":;8"2u":;8"2v":;8"19":;8"2w":f["q"][a[g]]=f["u"][a[g]]=a[g+1];G;2x:f["q"][a[g]]=f["F"][a[g]]=a[g+1]}};f["u"]["1f"]=d;i(e){f["q"]["1g"]=e};m f};',62,158,'||||||||case|var|||||||||if|navigator|else|ShockwaveFlash|return|indexOf|new|function|embedAttrs||true|plugins|objAttrs|false|toLowerCase||try|ActiveXObject|catch|split|versionStr|versionArray|parseFloat|params|break|isIE|isWin|isOpera|userAgent|Shockwave|Flash|for|Object|GetVariable|version|WIN|substring|webtv|AC_Generateobj|in|AC_GetArgs|src|appVersion|ControlVersion|GetSwfVer||null|length|tempArray|tempString|AC_AddExtension|object||name|embed|arguments|movie|clsid|444553540000|classid|type|MSIE|win|Opera|AllowScriptAccess|always|description|DetectFlashVer|replace|param|value|document|write|AC_FL_RunContent|swf|d27cdb6e|ae6d|11cf|96b8|application|shockwave|flash|AC_SW_RunContent|dcr|166B1BCA|3F9C|11CF|8075|switch|pluginspage|onafterupdate|onbeforeupdate|onblur|oncellchange|onclick|ondblclick|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondrop|onfinish|onfocus|onhelp|onmousedown|onmouseup||onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup|onload|onlosecapture|onpropertychange|onreadystatechange|onrowsdelete|onrowenter|onrowexit|onrowsinserted|onstart|onscroll|onbeforeeditfocus|onactivate|onbeforedeactivate|ondeactivate|codebase|id|width|height|align|vspace|hspace|class|title|accesskey|tabindex|default'.split('|'),0,{}))

View file

@ -0,0 +1 @@
var isIE=(navigator["appVersion"]["indexOf"]("MSIE")!=-1)?true:false;var isWin=(navigator["appVersion"]["toLowerCase"]()["indexOf"]("win")!=-1)?true:false;var isOpera=(navigator["userAgent"]["indexOf"]("Opera")!=-1)?true:false;function ControlVersion(){var a;var b;var c;try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");a=b.GetVariable("$version")}catch(c){};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");a="WIN 6,0,21,0";b["AllowScriptAccess"]="always";a=b.GetVariable("$version")}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");a=b.GetVariable("$version")}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");a="WIN 3,0,18,0"}catch(c){}};if(!a){try{b=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");a="WIN 2,0,0,11"}catch(c){a=-1}};return a};function GetSwfVer(){var a=-1;if(navigator["plugins"]!=null&&navigator["plugins"]["length"]>0){if(navigator["plugins"]["Shockwave Flash 2.0"]||navigator["plugins"]["Shockwave Flash"]){var b=navigator["plugins"]["Shockwave Flash 2.0"]?" 2.0":"";var c=navigator["plugins"]["Shockwave Flash"+b]["description"];var d=c["split"](" ");var e=d[2]["split"](".");var f=e[0];var g=e[1];var h=d[3];if(h==""){h=d[4]};if(h[0]=="d"){h=h["substring"](1)}else{if(h[0]=="r"){h=h["substring"](1);if(h["indexOf"]("d")>0){h=h["substring"](0,h["indexOf"]("d"))}}};var a=f+"."+g+"."+h}}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv/2.6")!=-1){a=4}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv/2.5")!=-1){a=3}else{if(navigator["userAgent"]["toLowerCase"]()["indexOf"]("webtv")!=-1){a=2}else{if(isIE&&isWin&&!isOpera){a=ControlVersion()}}}}};return a};function DetectFlashVer(a,b,c){versionStr=GetSwfVer();if(versionStr==-1){return false}else{if(versionStr!=0){if(isIE&&isWin&&!isOpera){tempArray=versionStr["split"](" ");tempString=tempArray[1];versionArray=tempString["split"](",")}else{versionArray=versionStr["split"](".")};var d=versionArray[0];var e=versionArray[1];var f=versionArray[2];if(d>parseFloat(a)){return true}else{if(d==parseFloat(a)){if(e>parseFloat(b)){return true}else{if(e==parseFloat(b)){if(f>=parseFloat(c)){return true}}}}};return false}}};function AC_AddExtension(a,b){if(a["indexOf"]("?")!=-1){return a["replace"](/\?/,b+"?")}else{return a+b}};function AC_Generateobj(a,b,c){var d="";if(isIE&&isWin&&!isOpera){d+="<object ";for(var e in a){d+=e+"=""+a[e]+"" "};d+=">";for(var e in b){d+="<param name=""+e+"" value=""+b[e]+"" /> "};d+="</object>"}else{d+="<embed ";for(var e in c){d+=e+"=""+c[e]+"" "};d+="> </embed>"};document["write"](d)};function AC_FL_RunContent(){var a=AC_GetArgs(arguments,".swf","movie","clsid:d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash");AC_Generateobj(a["objAttrs"],a["params"],a["embedAttrs"])};function AC_SW_RunContent(){var a=AC_GetArgs(arguments,".dcr","src","clsid:166B1BCA-3F9C-11CF-8075-444553540000",null);AC_Generateobj(a["objAttrs"],a["params"],a["embedAttrs"])};function AC_GetArgs(a,b,c,d,e){var f=new Object();f["embedAttrs"]=new Object();f["params"]=new Object();f["objAttrs"]=new Object();for(var g=0;g<a["length"];g=g+2){var h=a[g]["toLowerCase"]();switch(h){case"classid":break;case"pluginspage":f["embedAttrs"][a[g]]=a[g+1];break;case"src":;case"movie":a[g+1]=AC_AddExtension(a[g+1],b);f["embedAttrs"]["src"]=a[g+1];f["params"][c]=a[g+1];break;case"onafterupdate":;case"onbeforeupdate":;case"onblur":;case"oncellchange":;case"onclick":;case"ondblclick":;case"ondrag":;case"ondragend":;case"ondragenter":;case"ondragleave":;case"ondragover":;case"ondrop":;case"onfinish":;case"onfocus":;case"onhelp":;case"onmousedown":;case"onmouseup":;case"onmouseover":;case"onmousemove":;case"onmouseout":;case"onkeypress":;case"onkeydown":;case"onkeyup":;case"onload":;case"onlosecapture":;case"onpropertychange":;case"onreadystatechange":;case"onrowsdelete":;case"onrowenter":;case"onrowexit":;case"onrowsinserted":;case"onstart":;case"onscroll":;case"onbeforeeditfocus":;case"onactivate":;case"onbeforedeactivate":;case"ondeactivate":;case"type":;case"codebase":;case"id":f["objAttrs"][a[g]]=a[g+1];break;case"width":;case"height":;case"align":;case"vspace":;case"hspace":;case"class":;case"title":;case"accesskey":;case"name":;case"tabindex":f["embedAttrs"][a[g]]=f["objAttrs"][a[g]]=a[g+1];break;default:f["embedAttrs"][a[g]]=f["params"][a[g]]=a[g+1]}};f["objAttrs"]["classid"]=d;if(e){f["embedAttrs"]["type"]=e};return f};

View file

@ -0,0 +1,46 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for JavaScriptObfuscator unpacker."""
import unittest
from jsbeautifier.unpackers.javascriptobfuscator import (
unpack, detect, smartsplit)
# pylint: disable=R0904
class TestJavascriptObfuscator(unittest.TestCase):
"""JavascriptObfuscator.com test case."""
def test_smartsplit(self):
"""Test smartsplit() function."""
split = smartsplit
equals = lambda data, result: self.assertEqual(split(data), result)
equals('', [])
equals('"a", "b"', ['"a"', '"b"'])
equals('"aaa","bbbb"', ['"aaa"', '"bbbb"'])
equals('"a", "b\\\""', ['"a"', '"b\\\""'])
def test_detect(self):
"""Test detect() function."""
positive = lambda source: self.assertTrue(detect(source))
negative = lambda source: self.assertFalse(detect(source))
negative('')
negative('abcd')
negative('var _0xaaaa')
positive('var _0xaaaa = ["a", "b"]')
positive('var _0xaaaa=["a", "b"]')
positive('var _0x1234=["a","b"]')
def test_unpack(self):
"""Test unpack() function."""
decodeto = lambda ob, original: self.assertEqual(unpack(ob), original)
decodeto('var _0x8df3=[];var a=10;', 'var a=10;')
decodeto('var _0xb2a7=["\x74\x27\x65\x73\x74"];var i;for(i=0;i<10;++i)'
'{alert(_0xb2a7[0]);} ;', 'var i;for(i=0;i<10;++i){alert'
'("t\'est");} ;')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,40 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for MyObfuscate unpacker."""
import unittest
import os
from jsbeautifier.unpackers.myobfuscate import detect, unpack
from jsbeautifier.unpackers.tests import __path__ as path
INPUT = os.path.join(path[0], 'test-myobfuscate-input.js')
OUTPUT = os.path.join(path[0], 'test-myobfuscate-output.js')
# pylint: disable=R0904
class TestMyObfuscate(unittest.TestCase):
# pylint: disable=C0103
"""MyObfuscate obfuscator testcase."""
@classmethod
def setUpClass(cls):
"""Load source files (encoded and decoded version) for tests."""
with open(INPUT, 'r') as data:
cls.input = data.read()
with open(OUTPUT, 'r') as data:
cls.output = data.read()
def test_detect(self):
"""Test detect() function."""
detected = lambda source: self.assertTrue(detect(source))
detected(self.input)
def test_unpack(self):
"""Test unpack() function."""
check = lambda inp, out: self.assertEqual(unpack(inp), out)
check(self.input, self.output)
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,34 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for P.A.C.K.E.R. unpacker."""
import unittest
from jsbeautifier.unpackers.packer import detect, unpack
# pylint: disable=R0904
class TestPacker(unittest.TestCase):
"""P.A.C.K.E.R. testcase."""
def test_detect(self):
"""Test detect() function."""
positive = lambda source: self.assertTrue(detect(source))
negative = lambda source: self.assertFalse(detect(source))
negative('')
negative('var a = b')
positive('eval(function(p,a,c,k,e,r')
positive('eval ( function(p, a, c, k, e, r')
def test_unpack(self):
"""Test unpack() function."""
check = lambda inp, out: self.assertEqual(unpack(inp), out)
check("eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)"
"){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e="
"function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p.replace("
"new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('0 2=1',"
"62,3,'var||a'.split('|'),0,{}))", 'var a=1')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,36 @@
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""Tests for urlencoded unpacker."""
import unittest
from jsbeautifier.unpackers.urlencode import detect, unpack
# pylint: disable=R0904
class TestUrlencode(unittest.TestCase):
"""urlencode test case."""
def test_detect(self):
"""Test detect() function."""
encoded = lambda source: self.assertTrue(detect(source))
unencoded = lambda source: self.assertFalse(detect(source))
unencoded('')
unencoded('var a = b')
encoded('var%20a+=+b')
encoded('var%20a=b')
encoded('var%20%21%22')
def test_unpack(self):
"""Test unpack function."""
equals = lambda source, result: self.assertEqual(unpack(source), result)
equals('', '')
equals('abcd', 'abcd')
equals('var a = b', 'var a = b')
equals('var%20a=b', 'var a=b')
equals('var%20a+=+b', 'var a = b')
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,34 @@
#
# Trivial bookmarklet/escaped script detector for the javascript beautifier
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# some_string = urlencode.unpack(some_string)
#
"""Bookmarklet/escaped script unpacker."""
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote_plus
except ImportError:
from urllib.parse import unquote_plus
PRIORITY = 0
def detect(code):
"""Detects if a scriptlet is urlencoded."""
# the fact that script doesn't contain any space, but has %20 instead
# should be sufficient check for now.
return ' ' not in code and ('%20' in code or code.count('%') > 3)
def unpack(code):
"""URL decode `code` source string."""
return unquote_plus(code) if detect(code) else code

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python
from distutils.core import setup
from jsbeautifier.__version__ import __version__
setup(name='jsbeautifier',
version=__version__,
description='JavaScript unobfuscator and beautifier.',
long_description=('Beautify, unpack or deobfuscate JavaScript. '
'Handles popular online obfuscators.'),
author='Einar Lielmanis, Stefano Sanfilippo et al.',
author_email='einar@jsbeautifier.org',
url='http://jsbeautifier.org',
scripts=['js-beautify'],
packages=['jsbeautifier', 'jsbeautifier.tests',
'jsbeautifier.unpackers', 'jsbeautifier.unpackers.tests'],
install_requires=["six>=1.6.1"],
license='MIT',
)

View file

@ -0,0 +1,32 @@
#!/bin/bash
NEW_VERSION=$1
git checkout master
./generate-changelog.sh beautify-web/js-beautify || exit 1
git commit -am "Update Changelog for $NEW_VERSION"
# python
git clean -xfd || exit 1
echo "__version__ = '$NEW_VERSION'" > python/jsbeautifier/__version__.py
git commit -am "Python $NEW_VERSION"
cd python
python setup.py register
python setup.py sdist bdist_wininst upload
cd ..
git push
# node
git clean -xfd
npm version $NEW_VERSION
npm publish .
git push
git push --tags
# web
git clean -xfd
git checkout gh-pages && git fetch && git merge origin/master && git push || exit 1
git checkout master

View file

@ -0,0 +1,185 @@
exports.test_data = {
default_options: [
{ name: "indent_size", value: "1" },
{ name: "indent_char", value: "'\\t'" },
{ name: "selector_separator_newline", value: "true" },
{ name: "end_with_newline", value: "false" },
{ name: "newline_between_rules", value: "false"},
],
groups: [{
name: "End With Newline",
description: "",
matrix: [
{
options: [
{ name: "end_with_newline", value: "true" }
],
eof: '\\n'
}, {
options: [
{ name: "end_with_newline", value: "false" }
],
eof: ''
}
],
tests: [
{ fragment: true, input: '', output: '{{eof}}' },
{ fragment: true, input: ' .tabs{}', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: ' \n\n.tabs{}\n\n\n\n', output: ' .tabs {}{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
}, {
name: "Empty braces",
description: "",
tests: [
{ input: '.tabs{}', output: '.tabs {}' },
{ input: '.tabs { }', output: '.tabs {}' },
{ input: '.tabs { }', output: '.tabs {}' },
// When we support preserving newlines this will need to change
{ input: '.tabs \n{\n \n }', output: '.tabs {}' }
],
}, {
name: "",
description: "",
tests: [
{
input: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity = 90);\n}',
output: '#cboxOverlay {\n\tbackground: url(images/overlay.png) repeat 0 0;\n\topacity: 0.9;\n\tfilter: alpha(opacity=90);\n}'
},
],
}, {
name: 'Selector Separator',
description: '',
matrix: [
{
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
],
separator: ' ',
separator1: ' '
}, {
options: [
{ name: 'selector_separator_newline', value: 'false' },
{ name: 'selector_separator', value: '" "' }
],
// BUG: #713
separator: ' ',
separator1: ' '
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
],
separator: '\\n',
separator1: '\\n\\t'
}, {
options: [
{ name: 'selector_separator_newline', value: 'true' },
{ name: 'selector_separator', value: '" "' }
],
separator: '\\n',
separator1: '\\n\\t'
}
],
tests: [
{ input: '#bla, #foo{color:green}', output: '#bla,{{separator}}#foo {\n\tcolor: green\n}'},
{ input: '@media print {.tab{}}', output: '@media print {\n\t.tab {}\n}'},
{ input: '@media print {.tab,.bat{}}', output: '@media print {\n\t.tab,{{separator1}}.bat {}\n}'},
{ input: '#bla, #foo{color:black}', output: '#bla,{{separator}}#foo {\n\tcolor: black\n}'},
{ input: 'a:first-child,a:first-child{color:red;div:first-child,div:hover{color:black;}}',
output: 'a:first-child,{{separator}}a:first-child {\n\tcolor: red;\n\tdiv:first-child,{{separator1}}div:hover {\n\t\tcolor: black;\n\t}\n}'}
]
}, {
name: "Newline Between Rules",
description: "",
matrix: [
{
options: [
{ name: "newline_between_rules", value: "true" }
],
separator: '\\n'
}, {
options: [
{ name: "newline_between_rules", value: "false" }
],
separator: ''
}
],
tests: [
{ input: '.div {}\n.span {}', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div{}\n \n.span{}', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div {} \n \n.span { } \n', output: '.div {}\n{{separator}}.span {}'},
{ input: '.div {\n \n} \n .span {\n } ', output: '.div {}\n{{separator}}.span {}'},
{ input: '.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n.div{height:15px;}', output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}\n.div{height:15px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n.div{height:15px;}', output: '@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: "Bitstream Vera Serif Bold";\n\t\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}', output: '@font-face {\n\tfont-family: "Bitstream Vera Serif Bold";\n\tsrc: url("http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf");\n}\n{{separator}}@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: "Helvetica Neue"\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}'},
{ input: 'a:first-child{color:red;div:first-child{color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
{ input: 'a:first-child{color:red;div:not(.peq){color:black;}}\n.div{height:15px;}', output: 'a:first-child {\n\tcolor: red;\n\tdiv:not(.peq) {\n\t\tcolor: black;\n\t}\n}\n{{separator}}.div {\n\theight: 15px;\n}'},
],
}, {
name: "Functions braces",
description: "",
tests: [
{ input: '.tabs(){}', output: '.tabs() {}' },
{ input: '.tabs (){}', output: '.tabs () {}' },
{ input: '.tabs (pa, pa(1,2)), .cols { }', output: '.tabs (pa, pa(1, 2)),\n.cols {}' },
{ input: '.tabs(pa, pa(1,2)), .cols { }', output: '.tabs(pa, pa(1, 2)),\n.cols {}' },
{ input: '.tabs ( ) { }', output: '.tabs () {}' },
{ input: '.tabs( ) { }', output: '.tabs() {}' },
{ input: '.tabs (t, t2) \n{\n key: val(p1 ,p2); \n }', output: '.tabs (t, t2) {\n\tkey: val(p1, p2);\n}' },
{ input: '.box-shadow(@shadow: 0 1px 3px rgba(0, 0, 0, .25)) {\n\t-webkit-box-shadow: @shadow;\n\t-moz-box-shadow: @shadow;\n\tbox-shadow: @shadow;\n}' }
],
}, {
name: "Comments",
description: "",
tests: [
{ unchanged: '/* test */' },
{ input: '.tabs{/* test */}', output: '.tabs {\n\t/* test */\n}' },
{ input: '.tabs{/* test */}', output: '.tabs {\n\t/* test */\n}' },
{ input: '/* header */.tabs {}', output: '/* header */\n\n.tabs {}' },
{ input: '.tabs {\n/* non-header */\nwidth:10px;}', output: '.tabs {\n\t/* non-header */\n\twidth: 10px;\n}' },
{ unchanged: '/* header' },
{ unchanged: '// comment' },
{ input: '.selector1 {\n\tmargin: 0; /* This is a comment including an url http://domain.com/path/to/file.ext */\n}',
output: '.selector1 {\n\tmargin: 0;\n\t/* This is a comment including an url http://domain.com/path/to/file.ext */\n}'},
{ comment: "single line comment support (less/sass)",
input:'.tabs{\n// comment\nwidth:10px;\n}', output: '.tabs {\n\t// comment\n\twidth: 10px;\n}' },
{ input: '.tabs{// comment\nwidth:10px;\n}', output: '.tabs {\n\t// comment\n\twidth: 10px;\n}' },
{ input: '//comment\n.tabs{width:10px;}', output: '//comment\n.tabs {\n\twidth: 10px;\n}' },
{ input: '.tabs{//comment\n//2nd single line comment\nwidth:10px;}', output: '.tabs {\n\t//comment\n\t//2nd single line comment\n\twidth: 10px;\n}' },
{ input: '.tabs{width:10px;//end of line comment\n}', output: '.tabs {\n\twidth: 10px; //end of line comment\n}' },
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px;\n}' },
{ input: '.tabs{width:10px;//end of line comment\nheight:10px;//another\n}', output: '.tabs {\n\twidth: 10px; //end of line comment\n\theight: 10px; //another\n}' }
],
}, {
name: "Psuedo-classes vs Variables",
description: "",
tests: [
{ unchanged: '@page :first {}' },
{ comment: "Assume the colon goes with the @name. If we're in LESS, this is required regardless of the at-string.",
input: '@page:first {}',
output: '@page: first {}' },
{ unchanged: '@page: first {}' }
],
}, {
name: "SASS/SCSS",
description: "",
tests: [
{ comment: "Basic Interpolation",
unchanged: 'p {\n\t$font-size: 12px;\n\t$line-height: 30px;\n\tfont: #{$font-size}/#{$line-height};\n}'},
{ unchanged: 'p.#{$name} {}' },
{ unchanged: [
'@mixin itemPropertiesCoverItem($items, $margin) {',
'\twidth: calc((100% - ((#{$items} - 1) * #{$margin}rem)) / #{$items});',
'\tmargin: 1.6rem #{$margin}rem 1.6rem 0;',
'}'
]}
],
}, {
}]
}

View file

@ -0,0 +1,473 @@
exports.test_data = {
default_options: [
{ name: "indent_size", value: "4" },
{ name: "indent_char", value: "' '" },
{ name: "indent_with_tabs", value: "false" },
{ name: "preserve_newlines", value: "true" },
{ name: "jslint_happy", value: "false" },
{ name: "keep_array_indentation", value: "false" },
{ name: "brace_style", value: "'collapse'" },
{ name: "extra_liners", value: "['html', 'head', '/html']" }
],
groups: [{
name: "End With Newline",
description: "",
matrix: [
{
options: [
{ name: "end_with_newline", value: "true" }
],
eof: '\\n'
}, {
options: [
{ name: "end_with_newline", value: "false" }
],
eof: ''
}
],
tests: [
{ fragment: true, input: '', output: '{{eof}}' },
{ fragment: true, input: '<div></div>', output: '<div></div>{{eof}}' },
// { fragment: true, input: ' \n\n<div></div>\n\n\n\n', output: ' <div></div>{{eof}}' },
{ fragment: true, input: '\n', output: '{{eof}}' }
],
}, {
name: "Custom Extra Liners (empty)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "[]" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n <p>x</p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Custom Extra Liners (default)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "null" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head></head><body></body></html>',
output: '<html>\n\n<head></head>\n\n<body></body>\n\n</html>'
}
],
}, {
name: "Custom Extra Liners (p, string)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "'p,/p'" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Custom Extra Liners (p)",
description: "",
matrix: [
{
options: [
{ name: "extra_liners", value: "['p', '/p']" }
]
},
],
tests: [
{
fragment: true,
input: '<html><head><meta></head><body><div><p>x</p></div></body></html>',
output: '<html>\n<head>\n <meta>\n</head>\n<body>\n <div>\n\n <p>x\n\n </p>\n </div>\n</body>\n</html>'
}
],
}, {
name: "Attribute Wrap",
description: "Wraps attributes inside of html tags",
matrix: [
{
options: [
{ name: "wrap_attributes", value: "'force'" }
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'force'" },
{ name: "wrap_line_length", value: "80" }
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'force'" },
{ name: "wrap_attributes_indent_size", value: "8" },
],
eof: '\\n',
indent_attr: ' ',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'auto'" },
{ name: "wrap_line_length", value: "80" }
],
eof: ' ',
indent_attr: '',
over80: '\\n'
}, {
options: [
{ name: "wrap_attributes", value: "'auto'" },
{ name: "wrap_line_length", value: "0" }
],
eof: ' ',
indent_attr: '',
over80: ' '
}
],
tests: [
{
fragment: true,
input: '<div attr0 attr1="123" data-attr2="hello t here">This is some text</div>',
output: '<div attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here">This is some text</div>'
},
{
fragment: true,
input: '<div lookatthissuperduperlongattributenamewhoahcrazy0="true" attr0 attr1="123" data-attr2="hello t here" heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>',
output: '<div lookatthissuperduperlongattributenamewhoahcrazy0="true"{{eof}}{{indent_attr}}attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here"{{over80}}{{indent_attr}}heymanimreallylongtoowhocomesupwiththesenames="false">This is some text</div>'
},
{
fragment: true,
input: '<img attr0 attr1="123" data-attr2="hello t here"/>',
output: '<img attr0{{eof}}{{indent_attr}}attr1="123"{{eof}}{{indent_attr}}data-attr2="hello t here" />'
}
]
}, {
name: "Handlebars Indenting Off",
description: "Test handlebar behavior when indenting is off",
template: "^^^ $$$",
options: [
{ name: "indent_handlebars", value: "false" }
],
tests: [
{ fragment: true,
input_:
'{{#if 0}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}',
output:
'{{#if 0}}\n' +
'<div>\n' +
'</div>\n' +
'{{/if}}' },
{ fragment: true,
input_:
'<div>\n' +
'{{#each thing}}\n' +
' {{name}}\n' +
'{{/each}}\n' +
'</div>',
output:
'<div>\n' +
' {{#each thing}} {{name}} {{/each}}\n' +
'</div>'}
]
}, {
name: "Handlebars Indenting On",
description: "Test handlebar formatting",
template: "^^^ $$$",
matrix: [
{
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{field}}'
}, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{! comment}}'
}, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{pre{{field1}} {{field2}} {{field3}}post'
}
, {
options: [
{ name: "indent_handlebars", value: "true" }
],
content: '{{! \\n mult-line\\ncomment \\n with spacing\\n}}'
}
],
tests: [
{ fragment: true, unchanged: '{{page-title}}' },
{ fragment: true, unchanged: '{{#if 0}}{{/if}}' },
{ fragment: true, unchanged: '{{#if 0}}^^^content$$${{/if}}' },
{ fragment: true, unchanged: '{{#if 0}}\n{{/if}}' },
{ fragment: true,
input_: '{{#if words}}{{/if}}',
output: '{{#if words}}{{/if}}' },
{ fragment: true,
input_: '{{#if words}}^^^content$$${{/if}}',
output: '{{#if words}}^^^content$$${{/if}}' },
{ fragment: true,
input_: '{{#if words}}^^^content$$${{/if}}',
output: '{{#if words}}^^^content$$${{/if}}' },
{ fragment: true,
unchanged:
'{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if 1}}\n' +
'<div>\n' +
'</div>\n' +
'{{/if}}',
output:
'{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
{ fragment: true,
unchanged:
'<div>\n' +
' {{#if 1}}\n' +
' {{/if}}\n' +
'</div>' },
{ fragment: true,
input_:
'<div>\n' +
'{{#if 1}}\n' +
'{{/if}}\n' +
'</div>',
output:
'<div>\n' +
' {{#if 1}}\n' +
' {{/if}}\n' +
'</div>' },
{ fragment: true,
input_:
'{{#if}}\n' +
'{{#each}}\n' +
'{{#if}}\n' +
'^^^content$$$\n' +
'{{/if}}\n' +
'{{#if}}\n' +
'^^^content$$$\n' +
'{{/if}}\n' +
'{{/each}}\n' +
'{{/if}}',
output:
'{{#if}}\n' +
' {{#each}}\n' +
' {{#if}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
' {{#if}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
' {{/each}}\n' +
'{{/if}}' },
{ fragment: true, unchanged: '{{#if 1}}\n' +
' <div>\n' +
' </div>\n' +
'{{/if}}' },
// Test {{else}} aligned with {{#if}} and {{/if}}
{ fragment: true,
input_:
'{{#if 1}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
' ^^^content$$$\n' +
'{{/if}}',
output:
'{{#if 1}}\n' +
' ^^^content$$$\n' +
'{{else}}\n' +
' ^^^content$$$\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if 1}}\n' +
' {{else}}\n' +
' {{/if}}',
output:
'{{#if 1}}\n' +
'{{else}}\n' +
'{{/if}}' },
{ fragment: true,
input_:
'{{#if thing}}\n' +
'{{#if otherthing}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
'^^^content$$$\n' +
' {{/if}}\n' +
' {{else}}\n'+
'^^^content$$$\n' +
'{{/if}}',
output:
'{{#if thing}}\n' +
' {{#if otherthing}}\n' +
' ^^^content$$$\n' +
' {{else}}\n' +
' ^^^content$$$\n' +
' {{/if}}\n' +
'{{else}}\n'+
' ^^^content$$$\n' +
'{{/if}}' },
// Test {{}} inside of <> tags, which should be separated by spaces
// for readability, unless they are inside a string.
{ fragment: true,
input_: '<div{{somestyle}}></div>',
output: '<div {{somestyle}}></div>' },
{ fragment: true,
input_: '<div{{#if test}}class="foo"{{/if}}>^^^content$$$</div>',
output: '<div {{#if test}} class="foo" {{/if}}>^^^content$$$</div>' },
{ fragment: true,
input_: '<div{{#if thing}}{{somestyle}}class="{{class}}"{{else}}class="{{class2}}"{{/if}}>^^^content$$$</div>',
output: '<div {{#if thing}} {{somestyle}} class="{{class}}" {{else}} class="{{class2}}" {{/if}}>^^^content$$$</div>' },
{ fragment: true,
input_: '<span{{#if condition}}class="foo"{{/if}}>^^^content$$$</span>',
output: '<span {{#if condition}} class="foo" {{/if}}>^^^content$$$</span>' },
{ fragment: true,
unchanged: '<div unformatted="{{#if}}^^^content$$${{/if}}">^^^content$$$</div>' },
{ fragment: true,
unchanged: '<div unformatted="{{#if }} ^^^content$$${{/if}}">^^^content$$$</div>' },
// Quotes found inside of Handlebars expressions inside of quoted
// strings themselves should not be considered string delimiters.
{ fragment: true,
unchanged: '<div class="{{#if thingIs "value"}}^^^content$$${{/if}}"></div>' },
{ fragment: true,
unchanged: '<div class="{{#if thingIs \\\'value\\\'}}^^^content$$${{/if}}"></div>' },
{ fragment: true,
unchanged: '<div class=\\\'{{#if thingIs "value"}}^^^content$$${{/if}}\\\'></div>' },
{ fragment: true,
unchanged: '<div class=\\\'{{#if thingIs \\\'value\\\'}}^^^content$$${{/if}}\\\'></div>' }
],
}, {
name: "Unclosed html elements",
description: "Unclosed elements should not indent",
options: [],
tests: [
{ fragment: true, unchanged: '<source>\n<source>' },
{ fragment: true, unchanged: '<br>\n<br>' },
{ fragment: true, unchanged: '<input>\n<input>' },
{ fragment: true, unchanged: '<meta>\n<meta>' },
{ fragment: true, unchanged: '<link>\n<link>' }
]
}, {
name: "Unformatted tags",
description: "Unformatted tag behavior",
options: [],
tests: [
{ fragment: true, unchanged: '<ol>\n <li>b<pre>c</pre></li>\n</ol>' },
{ fragment: true, unchanged: '<ol>\n <li>b<code>c</code></li>\n</ol>' },
]
}, {
name: "Php formatting",
description: "Php (<?php ... ?>) treated as comments.",
options: [],
tests: [
{ fragment: true, unchanged: '<h1 class="content-page-header"><?=$view["name"]; ?></h1>' },
{ fragment: true, unchanged:
[
'<?php',
'for($i = 1; $i <= 100; $i++;) {',
' #count to 100!',
' echo($i . "</br>");',
'}',
'?>'
]
},
]
}, {
name: "underscore.js formatting",
description: "underscore.js templates (<% ... %>) treated as comments.",
options: [],
tests: [
{ fragment: true, unchanged:
[
'<div class="col-sm-9">',
' <textarea id="notes" class="form-control" rows="3">',
' <%= notes %>',
' </textarea>',
'</div>'
]
},
]
}, {
name: "Indent with tabs",
description: "Use one tab instead of several spaces for indentation",
template: "^^^ $$$",
options: [
{ name: "indent_with_tabs", value: "true" }
],
tests: [
{ fragment: true,
input_:
'<div>\n' +
'<div>\n' +
'</div>\n' +
'</div>',
output:
'<div>\n' +
'\t<div>\n' +
'\t</div>\n' +
'</div>' }
]
}, {
name: "Indent without tabs",
description: "Use several spaces for indentation",
template: "^^^ $$$",
options: [
{ name: "indent_with_tabs", value: "false" }
],
tests: [
{ fragment: true,
input_:
'<div>\n' +
'<div>\n' +
'</div>\n' +
'</div>',
output:
'<div>\n' +
' <div>\n' +
' </div>\n' +
'</div>' }
]
}, {
name: "New Test Suite"
}],
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,171 @@
#!/usr/bin/env node
var fs = require('fs');
var mustache = require('mustache');
function generate_tests() {
var test_data, template;
// javascript
test_data = require(__dirname + '/data/javascript.js').test_data;
set_formatters(test_data, 'bt', '// ')
template = fs.readFileSync(__dirname + '/template/node-javascript.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-javascript-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
set_formatters(test_data, 'bt', '# ')
template = fs.readFileSync(__dirname + '/template/python-javascript.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../python/jsbeautifier/tests/testjsbeautifier.py', mustache.render(template, test_data), {encoding: 'utf-8'});
// css
test_data = require(__dirname + '/data/css.js').test_data;
set_formatters(test_data, 't', '// ')
template = fs.readFileSync(__dirname + '/template/node-css.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-css-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
set_formatters(test_data, 't', '# ')
template = fs.readFileSync(__dirname + '/template/python-css.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../python/cssbeautifier/tests/test.py', mustache.render(template, test_data), {encoding: 'utf-8'});
// html
test_data = require(__dirname + '/data/html.js').test_data;
set_formatters(test_data, 'bth', '// ')
template = fs.readFileSync(__dirname + '/template/node-html.mustache', {encoding: 'utf-8'});
fs.writeFileSync(__dirname + '/../js/test/beautify-html-tests.js', mustache.render(template, test_data), {encoding: 'utf-8'});
// no python html beautifier, so no tests
}
function isStringOrArray(val) {
return typeof val === 'string' || val instanceof Array;
}
function getTestString(val) {
if (typeof val === 'string') {
return "'" + val.replace(/\n/g,'\\n').replace(/\t/g,'\\t') + "'";
} else if (val instanceof Array) {
return "'" + val.join("\\n' +\n '").replace(/\t/g,'\\t') + "'";
} else {
return null;
}
}
function set_formatters (data, test_method, comment_mark) {
// utility mustache functions
data.matrix_context_string = function() {
var context = this;
return function(text, render) {
var outputs = [];
// text is ignored for this
for (var name in context) {
if (name === 'options') {
continue;
}
if (context.hasOwnProperty(name)) {
outputs.push(name + ' = "' + context[name] + '"');
}
}
return render(outputs.join(', '));
}
};
data.test_line = function() {
return function(text, render) {
var method_text = test_method;
if (this.fragment) {
method_text = 'test_fragment';
}
// text is ignored for this.
var comment = '';
if (typeof this.comment === 'string') {
comment = '\n ' + comment_mark + this.comment + '\n ';
} else if (this.comment instanceof Array) {
comment = '\n ' + comment_mark + this.comment.join('\n ' + comment_mark) + '\n ';
}
var input = null;
var before_input = method_text + '(';
var before_output = ', ';
function set_input(field, opt_newline) {
if (input !== null && isStringOrArray(field)) {
throw "Only one test input field allowed (input, input_, or unchanged): " + input;
}
if (typeof field === 'string' && !opt_newline) {
input = getTestString(field);
} else if (field instanceof Array || (typeof field === 'string' && opt_newline)) {
before_input = method_text + '(\n ';
before_output = ',\n ';
input = getTestString(field);
}
}
set_input(this.input);
// allow underscore for formatting alignment with "output"
set_input(this.input_, true);
// use "unchanged" instead of "input" if there is no output
set_input(this.unchanged);
if(isStringOrArray(this.unchanged) && isStringOrArray(this.output)) {
throw "Cannot specify 'output' with 'unchanged' test input: " + input;
}
if (input === null) {
throw "Missing test input field (input, input_, or unchanged).";
}
var output = null;
if (typeof this.output === 'string') {
output = getTestString(this.output);
} else if (this.output instanceof Array) {
before_input = method_text + '(\n ';
before_output = ',\n ';
output = getTestString(this.output);
} else {
before_output = '';
}
if (input === output) {
throw "Test strings are identical. Omit 'output' and use 'unchanged': " + input;
}
if(output && output.indexOf('<%') !== -1) {
mustache.tags = ['<%', '%>'];
}
input = render(input);
output = render(output);
if(output && output.indexOf('<%') !== -1) {
mustache.tags = ['{{', '}}'];
}
if (output === input) {
before_output = '';
output = '';
}
return comment + before_input + input + before_output + output + ')';
}
};
data.set_mustache_tags = function() {
return function(text, render) {
if(this.template) {
mustache.tags = this.template.split(' ');
}
return '';
}
};
data.unset_mustache_tags = function() {
return function(text, render) {
if(this.template) {
mustache.tags = ['{{', '}}'];
}
return '';
}
};
}
generate_tests();

View file

@ -0,0 +1,15 @@
echo "Post-build git status check..."
echo "Ensuring no changes visible to git have been made to '$*' ..."
git status $* | grep "nothing to commit.*working directory clean" || {
# we should find nothing to commit. If we don't, build has failed.
echo "ERROR: Post-build git status check - FAILED."
echo "Git status reported changes to non-git-ignore'd files."
echo "TO REPRO: Run 'git status $*'. The use git gui or git diff to see what was changed during the build."
echo "TO FIX: Amend your commit and rebuild. Repeat until git status reports no changes both before and after the build."
echo "OUTPUT FOR 'git status $*':"
git status $*
exit 1
}
echo "Post-build git status check - Succeeded."
exit 0

View file

@ -0,0 +1,218 @@
/*global js_beautify: true */
function run_css_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false,
newline_between_rules: true
};
function test_css_beautifier(input)
{
return css_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test css
function t(input, expectation)
{
var wrapped_input, wrapped_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_css_beautifier, 'css_beautify');
test_fragment(input, expectation);
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
t(".tabs {}");
{{#default_options}} opts.{{name}} = {{&value}};
{{/default_options}}
{{#groups}}
{{^matrix}}
// {{&name}}
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}
{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}
{{/groups}}
// test basic css beautifier
t(".tabs {}");
t(".tabs{color:red;}", ".tabs {\n\tcolor: red;\n}");
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}");
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}");
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}");
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}");
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
//lead-in whitespace determines base-indent.
// lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }");
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }");
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// separate selectors
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}");
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}");
// block nesting
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}");
/*
@font-face {
font-family: 'Bitstream Vera Serif Bold';
src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
}
@media screen {
#foo:hover {
background-image: url(foo.png);
}
@media screen and (min-device-pixel-ratio: 2) {
@font-face {
font-family: 'Helvetica Neue'
}
#foo:hover {
background-image: url(foo@2x.png);
}
}
}
*/
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}");
// less-css cases
t('.well{@well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}');
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}');
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// Not sure if this is sensible
// but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
// import
t('@import "test";');
// don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
// handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
//nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}");
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}");
//nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}");
//variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}");
// test options
opts.indent_size = 2;
opts.indent_char = ' ';
opts.selector_separator_newline = false;
// pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}");
t("#foo *:hover {\n color: purple\n}");
t("::selection {\n color: #ff0000;\n}");
// TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}");
// particular edge case with braces and semicolons inside tags that allows custom text
t("a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}");
// may not eat the space before "["
t('html.js [data-custom="123"] {\n opacity: 1.00;\n}');
t('html.js *[data-custom="123"] {\n opacity: 1.00;\n}');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_css_tests = run_css_tests;
}

View file

@ -0,0 +1,393 @@
/*global js_beautify: true */
function run_html_tests(test_obj, Urlencoded, js_beautify, html_beautify, css_beautify)
{
var opts = {
indent_size: 4,
indent_char: ' ',
preserve_newlines: true,
jslint_happy: false,
keep_array_indentation: false,
brace_style: 'collapse',
space_before_conditional: true,
break_chained_methods: false,
selector_separator: '\n',
end_with_newline: false
};
function test_html_beautifier(input)
{
return html_beautify(input, opts);
}
var sanitytest;
// test the input on beautifier with the current flag settings
// does not check the indentation / surroundings as bt() does
function test_fragment(input, expected)
{
expected = expected || expected === '' ? expected : input;
sanitytest.expect(input, expected);
// if the expected is different from input, run it again
// expected output should be unchanged when run twice.
if (expected !== input) {
sanitytest.expect(expected, expected);
}
// Everywhere we do newlines, they should be replaced with opts.eol
opts.eol = '\r\n';
expected = expected.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
input = input.replace(/[\n]/g, '\r\n');
sanitytest.expect(input, expected);
opts.eol = '\n';
}
// test html
function bth(input, expectation)
{
var wrapped_input, wrapped_expectation, field_input, field_expectation;
expectation = expectation || expectation === '' ? expectation : input;
sanitytest.test_function(test_html_beautifier, 'html_beautify');
test_fragment(input, expectation);
if (opts.indent_size === 4 && input) {
wrapped_input = '<div>\n' + input.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
wrapped_expectation = '<div>\n' + expectation.replace(/^(.+)$/mg, ' $1') + '\n <span>inline</span>\n</div>';
test_fragment(wrapped_input, wrapped_expectation);
}
}
function unicode_char(value) {
return String.fromCharCode(value)
}
function beautifier_tests()
{
sanitytest = test_obj;
bth('');
{{#default_options}} opts.{{name}} = {{&value}};
{{/default_options}}
{{#groups}}{{#set_mustache_tags}}.{{/set_mustache_tags}}{{^matrix}}
// {{&name}}
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}{{#matrix}}
// {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
opts.{{name}} = {{&value}};
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}};
{{/tests}}
{{/matrix}}{{#unset_mustache_tags}}.{{/unset_mustache_tags}}
{{/groups}}
opts.end_with_newline = true;
test_fragment('', '\n');
test_fragment('<div></div>\n');
test_fragment('<div></div>\n\n\n', '<div></div>\n');
test_fragment('<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
'\n' +
' </script>\n' +
'</head>\n');
opts.end_with_newline = false;
// error cases need love too
bth('<img title="Bad food!" src="foo.jpg" alt="Evil" ">');
bth("<!-- don't blow up if a comment is not complete"); // -->
test_fragment(
'<head>\n' +
' <script>\n' +
' mocha.setup("bdd");\n' +
' </script>\n' +
'</head>');
test_fragment('<div></div>\n', '<div></div>');
bth('<div></div>');
bth('<div>content</div>');
bth('<div><div></div></div>',
'<div>\n' +
' <div></div>\n' +
'</div>');
bth('<div><div>content</div></div>',
'<div>\n' +
' <div>content</div>\n' +
'</div>');
bth('<div>\n' +
' <span>content</span>\n' +
'</div>');
bth('<div>\n' +
'</div>');
bth('<div>\n' +
' content\n' +
'</div>');
bth('<div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth(' <div>\n' +
' </div>',
'<div>\n' +
'</div>');
bth('<div>\n' +
'</div>\n' +
' <div>\n' +
' </div>',
'<div>\n' +
'</div>\n' +
'<div>\n' +
'</div>');
bth(' <div>\n' +
'</div>',
'<div>\n' +
'</div>');
bth('<div >content</div>',
'<div>content</div>');
bth('<div thinger="preserve space here" ></div >',
'<div thinger="preserve space here"></div>');
bth('content\n' +
' <div>\n' +
' </div>\n' +
'content',
'content\n' +
'<div>\n' +
'</div>\n' +
'content');
bth('<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
'<div>\n' +
'</div>\n' +
'</li>',
'<li>\n' +
' <div>\n' +
' </div>\n' +
'</li>');
bth('<li>\n' +
' content\n' +
'</li>\n' +
'<li>\n' +
' content\n' +
'</li>');
bth('<img>content');
bth('<img> content');
bth('<img> content', '<img> content');
bth('<img><img>content');
bth('<img> <img>content');
bth('<img> <img>content', '<img> <img>content');
bth('<img><b>content</b>');
bth('<img> <b>content</b>');
bth('<img> <b>content</b>', '<img> <b>content</b>');
bth('<div>content<img>content</div>');
bth('<div> content <img> content</div>');
bth('<div> content <img> content </div>',
'<div> content <img> content </div>');
bth('Text <a href="#">Link</a> Text');
// START tests for issue 453
bth('<script type="text/unknown"><div></div></script>',
'<script type="text/unknown">\n' +
' <div></div>\n' +
'</script>');
bth('<script type="text/javascript"><div></div></script>',
'<script type="text/javascript">\n' +
' < div > < /div>\n' +
'</script>');
bth('<script><div></div></script>',
'<script>\n' +
' < div > < /div>\n' +
'</script>');
bth('<script type="text/javascript">var foo = "bar";</script>',
'<script type="text/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript">var foo = "bar";</script>',
'<script type="application/javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/javascript;version=1.8">var foo = "bar";</script>',
'<script type="application/javascript;version=1.8">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/x-javascript">var foo = "bar";</script>',
'<script type="application/x-javascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="application/ecmascript">var foo = "bar";</script>',
'<script type="application/ecmascript">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script type="text/javascript1.5">var foo = "bar";</script>',
'<script type="text/javascript1.5">\n' +
' var foo = "bar";\n' +
'</script>');
bth('<script>var foo = "bar";</script>',
'<script>\n' +
' var foo = "bar";\n' +
'</script>');
bth('<style type="text/unknown"><tag></tag></style>',
'<style type="text/unknown">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css"><tag></tag></style>',
'<style type="text/css">\n' +
' <tag></tag>\n' +
'</style>');
bth('<style><tag></tag></style>',
'<style>\n' +
' <tag></tag>\n' +
'</style>');
bth('<style type="text/css">.selector {font-size:12px;}</style>',
'<style type="text/css">\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
bth('<style>.selector {font-size:12px;}</style>',
'<style>\n' +
' .selector {\n' +
' font-size: 12px;\n' +
' }\n'+
'</style>');
// END tests for issue 453
var unformatted = opts.unformatted;
opts.unformatted = ['script', 'style'];
bth('<script id="javascriptTemplate" type="text/x-kendo-template">\n' +
' <ul>\n' +
' # for (var i = 0; i < data.length; i++) { #\n' +
' <li>#= data[i] #</li>\n' +
' # } #\n' +
' </ul>\n' +
'</script>');
bth('<style>\n' +
' body {background-color:lightgrey}\n' +
' h1 {color:blue}\n' +
'</style>');
opts.unformatted = unformatted;
unformatted = opts.unformatted;
opts.unformatted = ['custom-element'];
test_fragment('<div>should <custom-element>not</custom-element>' +
' insert newlines</div>',
'<div>should <custom-element>not</custom-element>' +
' insert newlines</div>');
opts.unformatted = unformatted;
// Tests that don't pass, but probably should.
// bth('<div><span>content</span></div>');
// Handlebars tests
// Without the indent option on, handlebars are treated as content.
opts.wrap_line_length = 0;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of 0 means no max line length, and should not wrap.
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
opts.wrap_line_length = "0";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all.</div>');
// A value of "0" means no max line length, and should not wrap
//...---------1---------2---------3---------4---------5---------6---------7---------8---------9--------10--------11--------12--------13--------14--------15--------16--------17--------18--------19--------20--------21--------22--------23--------24--------25--------26--------27--------28--------29
//...12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>',
/* expected */
'<div>Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all. Some text that should not wrap at all.</div>');
//BUGBUG: This should wrap before 40 not after.
opts.wrap_line_length = 40;
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.wrap_line_length = "40";
//...---------1---------2---------3---------4---------5---------6---------7
//...1234567890123456789012345678901234567890123456789012345678901234567890
bth('<div>Some test text that should wrap_inside_this section here.</div>',
/* expected */
'<div>Some test text that should wrap_inside_this\n' +
' section here.</div>');
opts.indent_size = 1;
opts.indent_char = '\t';
opts.preserve_newlines = false;
bth('<div>\n\tfoo\n</div>', '<div> foo </div>');
opts.preserve_newlines = true;
bth('<div>\n\tfoo\n</div>');
// test preserve_newlines and max_preserve_newlines
opts.preserve_newlines = false;
bth('<div>Should not</div>\n\n\n' +
'<div>preserve newlines</div>',
'<div>Should not</div>\n' +
'<div>preserve newlines</div>');
opts.preserve_newlines = true;
opts.max_preserve_newlines = 0;
bth('<div>Should</div>\n\n\n' +
'<div>preserve zero newlines</div>',
'<div>Should</div>\n' +
'<div>preserve zero newlines</div>');
opts.max_preserve_newlines = 1;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n' +
'<div>preserve one newline</div>');
opts.max_preserve_newlines = null;
bth('<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>',
'<div>Should</div>\n\n\n' +
'<div>preserve one newline</div>');
}
beautifier_tests();
}
if (typeof exports !== "undefined") {
exports.run_html_tests = run_html_tests;
}

View file

@ -0,0 +1,225 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
import cssbeautifier
class CSSBeautifierTest(unittest.TestCase):
def resetOptions(self):
false = False
true = True
self.options = cssbeautifier.default_options()
self.options.indent_size = 1
self.options.indent_char = '\t'
self.options.selector_separator_newline = true
self.options.end_with_newline = false
self.options.newline_between_rules = false
def testGenerated(self):
self.resetOptions()
test_fragment = self.decodesto
t = self.decodesto
false = False
true = True
{{#default_options}} self.options.{{name}} = {{&value}}
{{/default_options}}
{{#groups}}
{{^matrix}}
# {{&name}}
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}}
{{/tests}}
{{/matrix}}
{{#matrix}}
# {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
{{#options}}
self.options.{{name}} = {{&value}}
{{/options}}
{{#tests}}
{{#test_line}}.{{/test_line}}
{{/tests}}
{{/matrix}}
{{/groups}}
def testNewline(self):
self.resetOptions()
t = self.decodesto
self.options.end_with_newline = True
t("", "\n")
t("\n", "\n")
t(".tabs{}\n", ".tabs {}\n")
t(".tabs{}", ".tabs {}\n")
def testBasics(self):
self.resetOptions()
t = self.decodesto
t("", "")
t("\n", "")
t(".tabs{}\n", ".tabs {}")
t(".tabs{}", ".tabs {}")
t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}")
t("a:before {\n" +
"\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
"}");
# may not eat the space before "["
t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
# lead-in whitespace determines base-indent.
# lead-in newlines are stripped.
t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testSeperateSelectors(self):
self.resetOptions()
t = self.decodesto
t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
def testBlockNesting(self):
self.resetOptions()
t = self.decodesto
t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
# @font-face {
# font-family: 'Bitstream Vera Serif Bold';
# src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
# }
# @media screen {
# #foo:hover {
# background-image: url(foo.png);
# }
# @media screen and (min-device-pixel-ratio: 2) {
# @font-face {
# font-family: 'Helvetica Neue'
# }
# #foo:hover {
# background-image: url(foo@2x.png);
# }
# }
# }
t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}")
def testOptions(self):
self.resetOptions()
self.options.indent_size = 2
self.options.indent_char = ' '
self.options.selector_separator_newline = False
t = self.decodesto
# pseudo-classes and pseudo-elements
t("#foo:hover {\n background-image: url(foo@2x.png)\n}")
t("#foo *:hover {\n color: purple\n}")
t("::selection {\n color: #ff0000;\n}")
# TODO: don't break nested pseduo-classes
t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}")
# particular edge case with braces and semicolons inside tags that allows custom text
t( "a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
"a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}")
def testLessCss(self):
self.resetOptions()
t = self.decodesto
t('.well{ \n @well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}')
t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
'.well {\n' +
'\t&.active {\n' +
'\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
'\t}\n' +
'}')
t('a {\n' +
'\tcolor: blue;\n' +
'\t&:hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'\t& & &&&.active {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}')
# Not sure if this is sensible
# but I believe it is correct to not remove the space in "&: hover".
t('a {\n' +
'\t&: hover {\n' +
'\t\tcolor: green;\n' +
'\t}\n' +
'}');
# import
t('@import "test";');
# don't break nested pseudo-classes
t("a:first-child{color:red;div:first-child{color:black;}}",
"a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
# handle SASS/LESS parent reference
t("div{&:first-letter {text-transform: uppercase;}}",
"div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
# nested modifiers (&:hover etc)
t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}")
t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}")
# nested rules
t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}")
# variables
t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
def decodesto(self, input, expectation=None):
if expectation == None:
expectation = input
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
# if the expected is different from input, run it again
# expected output should be unchanged when run twice.
if not expectation != input:
self.assertMultiLineEqual(
cssbeautifier.beautify(expectation, self.options), expectation)
# Everywhere we do newlines, they should be replaced with opts.eol
self.options.eol = '\r\\n';
expectation = expectation.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
input = input.replace('\n', '\r\n')
self.assertMultiLineEqual(
cssbeautifier.beautify(input, self.options), expectation)
self.options.eol = '\n'
if __name__ == '__main__':
unittest.main()

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

View file

@ -0,0 +1,23 @@
Copyright (C) 2013 by Marijn Haverbeke <marijnh@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Please note that some subdirectories of the CodeMirror distribution
include their own LICENSE files, and are released under different
licences.

View file

@ -0,0 +1,246 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
}
/* CURSOR */
.CodeMirror div.CodeMirror-cursor {
border-left: 1px solid black;
z-index: 3;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
z-index: 1;
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
.cm-tab { display: inline-block; }
/* DEFAULT THEME */
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
background: white;
color: black;
}
.CodeMirror-scroll {
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px; padding-right: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
z-index: 6;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
height: 100%;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
height: 100%;
padding-bottom: 30px;
margin-bottom: -32px;
display: inline-block;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-lines {
cursor: text;
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {
display: inline-block;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror-measure {
position: absolute;
width: 100%; height: 0px;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor {
position: absolute;
visibility: hidden;
border-right: none;
width: 0;
}
.CodeMirror-focused div.CodeMirror-cursor {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursor {
visibility: hidden;
}
}

View file

@ -0,0 +1,88 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: JavaScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="../../addon/edit/continuecomment.js"></script>
<script src="javascript.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<h1>CodeMirror: JavaScript mode</h1>
<div><textarea id="code" name="code">
// Demo code (the actual new parser character stream implementation)
function StringStream(string) {
this.pos = 0;
this.string = string;
}
StringStream.prototype = {
done: function() {return this.pos >= this.string.length;},
peek: function() {return this.string.charAt(this.pos);},
next: function() {
if (this.pos &lt; this.string.length)
return this.string.charAt(this.pos++);
},
eat: function(match) {
var ch = this.string.charAt(this.pos);
if (typeof match == "string") var ok = ch == match;
else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
if (ok) {this.pos++; return ch;}
},
eatWhile: function(match) {
var start = this.pos;
while (this.eat(match));
if (this.pos > start) return this.string.slice(start, this.pos);
},
backUp: function(n) {this.pos -= n;},
column: function() {return this.pos;},
eatSpace: function() {
var start = this.pos;
while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
return this.pos - start;
},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
if (consume !== false) this.pos += str.length;
return true;
}
}
else {
var match = this.string.slice(this.pos).match(pattern);
if (match &amp;&amp; consume !== false) this.pos += match[0].length;
return match;
}
}
};
</textarea></div>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
continueComments: "Enter"
});
</script>
<p>
JavaScript mode supports a two configuration
options:
<ul>
<li><code>json</code> which will set the mode to expect JSON data rather than a JavaScript program.</li>
<li>
<code>typescript</code> which will activate additional syntax highlighting and some other things for TypeScript code (<a href="typescript.html">demo</a>).
</li>
</ul>
</p>
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>
</body>
</html>

View file

@ -0,0 +1,437 @@
// TODO actually recognize syntax of TypeScript constructs
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
var jsKeywords = {
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
"this": kw("this")
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),
// scope modifiers
"public": kw("public"),
"private": kw("private"),
"protected": kw("protected"),
"static": kw("static"),
"super": kw("super"),
// types
"string": type, "number": type, "bool": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
var isOperatorChar = /[+\-*&%=<>!?|~^]/;
function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}
function nextUntilUnescaped(stream, end) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (next == end && !escaped)
return false;
escaped = !escaped && next == "\\";
}
return escaped;
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type, content;
function ret(tp, style, cont) {
type = tp; content = cont;
return style;
}
function jsTokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'")
return chain(stream, state, jsTokenString(ch));
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
return ret(ch);
else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
}
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
}
else if (ch == "/") {
if (stream.eat("*")) {
return chain(stream, state, jsTokenComment);
}
else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
}
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
nextUntilUnescaped(stream, "/");
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
return ret("regexp", "string-2");
}
else {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
}
else if (ch == "#") {
stream.skipToEnd();
return ret("error", "error");
}
else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
else {
stream.eatWhile(/[\w\$_]/);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
}
}
function jsTokenString(quote) {
return function(stream, state) {
if (!nextUntilUnescaped(stream, quote))
state.tokenize = jsTokenBase;
return ret("string", "string");
};
}
function jsTokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = jsTokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
// Parser
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
function JSLexical(indented, column, type, align, prev, info) {
this.indented = indented;
this.column = column;
this.type = type;
this.prev = prev;
this.info = info;
if (align != null) this.align = align;
}
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
}
function parseJS(state, style, type, content, stream) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
while(true) {
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
if (combinator(type, content)) {
while(cc.length && cc[cc.length - 1].lex)
cc.pop()();
if (cx.marked) return cx.marked;
if (type == "variable" && inScope(state, content)) return "variable-2";
return style;
}
}
}
// Combinator utils
var cx = {state: null, column: null, marked: null, cc: null};
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function register(varname) {
function inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state;
if (state.context) {
cx.marked = "def";
if (inList(state.localVars)) return;
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
state.globalVars = {name: varname, next: state.globalVars};
}
}
// Combinators
var defaultVars = {name: "this", next: {name: "arguments"}};
function pushcontext() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
}
function pushlex(type, info) {
var result = function() {
var state = cx.state;
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
return result;
}
function poplex() {
var state = cx.state;
if (state.lexical.prev) {
if (state.lexical.type == ")")
state.indented = state.lexical.indented;
state.lexical = state.lexical.prev;
}
}
poplex.lex = true;
function expect(wanted) {
return function(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else return cont(arguments.callee);
};
}
function statement(type) {
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
poplex, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
if (type == "function") return cont(functiondef);
if (type == "keyword c") return cont(maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
if (type == "operator") return cont(expression);
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
return cont();
}
function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
function maybeoperator(type, value) {
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(maybeoperator);
if (value == "?") return cont(expression, expect(":"), expression);
return cont(expression);
}
if (type == ";") return;
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
if (type == ".") return cont(property, maybeoperator);
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
return pass(maybeoperator, expect(";"), poplex);
}
function property(type) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
function objprop(type, value) {
if (type == "variable") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
} else if (type == "number" || type == "string") {
cx.marked = type + " property";
}
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
}
function getterSetter(type) {
if (type == ":") return cont(expression);
if (type != "variable") return cont(expect(":"), expression);
cx.marked = "property";
return cont(functiondef);
}
function commasep(what, end) {
function proceed(type) {
if (type == ",") return cont(what, proceed);
if (type == end) return cont();
return cont(expect(end));
}
return function(type) {
if (type == end) return cont();
else return pass(what, proceed);
};
}
function block(type) {
if (type == "}") return cont();
return pass(statement, block);
}
function maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable") {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
}
return pass();
}
function vardef2(type, value) {
if (value == "=") return cont(expression, vardef2);
if (type == ",") return cont(vardef1);
}
function forspec1(type) {
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
return cont(forspec2);
}
function formaybein(_type, value) {
if (value == "in") return cont(expression);
return cont(maybeoperator, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in") return cont(expression);
return cont(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
lastType: null,
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
globalVars: parserConfig.globalVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: 0
};
},
token: function(stream, state) {
if (stream.sol()) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.lastType = type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
if (state.tokenize != jsTokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
else if (lexical.info == "switch" && !closing)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
electricChars: ":{}",
jsonMode: jsonMode
};
});
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

View file

@ -0,0 +1,48 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: TypeScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="javascript.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<h1>CodeMirror: TypeScript mode</h1>
<div><textarea id="code" name="code">
class Greeter {
greeting: string;
constructor (message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
var greeter = new Greeter("world");
var button = document.createElement('button')
button.innerText = "Say Hello"
button.onclick = function() {
alert(greeter.greet())
}
document.body.appendChild(button)
</textarea></div>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/typescript"
});
</script>
<p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
</body>
</html>

View file

@ -0,0 +1,95 @@
/*!
* jQuery Cookie Plugin v1.3.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function raw(s) {
return s;
}
function decoded(s) {
return decodeURIComponent(s.replace(pluses, ' '));
}
function converted(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
return config.json ? JSON.parse(s) : s;
} catch(er) {}
}
var config = $.cookie = function (key, value, options) {
// write
if (value !== undefined) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
value = config.json ? JSON.stringify(value) : String(value);
return (document.cookie = [
config.raw ? key : encodeURIComponent(key),
'=',
config.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// read
var decode = config.raw ? raw : decoded;
var cookies = document.cookie.split('; ');
var result = key ? undefined : {};
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = decode(parts.join('='));
if (key && key === name) {
result = converted(cookie);
break;
}
if (!key) {
result[name] = converted(cookie);
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) !== undefined) {
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return true;
}
return false;
};
}));

File diff suppressed because one or more lines are too long