mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
commit
10696a55bb
10
gun.js
10
gun.js
@ -520,7 +520,7 @@
|
||||
});
|
||||
return function(cb, opt){
|
||||
var gun = this, ctx = {};
|
||||
cb = cb || root.console.log.bind(root.console);
|
||||
cb = cb || function(val, field){ root.console.log(field + ':', val) }
|
||||
opt = opt || {};
|
||||
|
||||
gun.on(function($, delta, on){
|
||||
@ -1130,7 +1130,7 @@
|
||||
return true;
|
||||
}
|
||||
Gun.obj.map(gun.__.opt.peers, function(){ // only create server if peers and do it once by returning immediately.
|
||||
return tab.request = tab.request || request.createServer(tab.server) || true;
|
||||
return (tab.request = tab.request || request.createServer(tab.server) || true);
|
||||
});
|
||||
gun.__.opt.hooks.get = gun.__.opt.hooks.get || tab.get;
|
||||
gun.__.opt.hooks.put = gun.__.opt.hooks.put || tab.put;
|
||||
@ -1192,6 +1192,10 @@
|
||||
return;
|
||||
}
|
||||
ws = r.ws.peers[opt.base] = null; // this will make the next request try to reconnect
|
||||
setTimeout(function(){
|
||||
console.log("!!!!! WEBSOCKET DICONNECTED !!!!!! ATTEMPTING INFINITE RETRY WITH NO BACKOFF !!!!!!!");
|
||||
r.ws(opt, function(){}); // opt here is a race condition, is it not? Does this matter?
|
||||
}, 50) // make this an exponential backoff.
|
||||
};
|
||||
ws.onmessage = function(m){
|
||||
if(!m || !m.data){ return }
|
||||
@ -1204,7 +1208,7 @@
|
||||
Gun.log("We have a pushed message!", res);
|
||||
if(res.body){ r.createServer.ing(res, function(){}) } // emit extra events.
|
||||
};
|
||||
ws.onerror = function(e){ Gun.log(e); };
|
||||
ws.onerror = function(e){ console.log("!!!! WEBSOCKET ERROR !!!!", e); Gun.log(e); };
|
||||
return true;
|
||||
}
|
||||
r.ws.peers = {};
|
||||
|
10
index.html
10
index.html
@ -385,6 +385,13 @@ html, body {
|
||||
heap.load("2174172773");
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<div style="margin: 0 auto;">
|
||||
<blockquote class="twitter-tweet" lang="en"><p lang="en" dir="ltr">With the proliferation of simple apps that sometimes become huge, it's cool to see projects like GunDB happening. <a href="http://t.co/rgTuZ4LbLR">http://t.co/rgTuZ4LbLR</a></p>— Tom Preston-Werner (@mojombo) <a href="https://twitter.com/mojombo/status/571027932048723969">February 26, 2015</a></blockquote>
|
||||
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- BEGIN TUTORIAL -->
|
||||
<div class="tutorial">
|
||||
<script src="https://gunjs.herokuapp.com/gun.js"></script>
|
||||
@ -610,7 +617,8 @@ ref.on(function(data){
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BEGIN TUTORIAL -->
|
||||
<!-- END TUTORIAL -->
|
||||
|
||||
|
||||
<! OLD BLOG POST HERE -->
|
||||
|
||||
|
25
lib/nts.js
Normal file
25
lib/nts.js
Normal file
@ -0,0 +1,25 @@
|
||||
Gun.on('opt').event(function(gun, opt){
|
||||
if(!Gun.request){ return }
|
||||
var objectiveTimeOffset = 0;
|
||||
Gun.obj.map(opt.peers || gun.__.opt.peers, function(peer, url){
|
||||
var NTS = {};
|
||||
NTS.start = Gun.time.is();
|
||||
console.log(url + '.nts');
|
||||
Gun.request(url + '.nts', null, function(err, reply){
|
||||
console.log("reply", err, reply);
|
||||
if(err || !reply || !reply.body){
|
||||
return console.log("Network Time Synchronization not supported", err, (reply || {}).body);
|
||||
}
|
||||
NTS.end = Gun.time.is();
|
||||
NTS.latency = (NTS.end - NTS.start)/2;
|
||||
if(Gun.obj.has(reply.body, 'time')){ return }
|
||||
NTS.calc = reply.body.time + NTS.latency;
|
||||
objectiveTimeOffset += (objectiveTimeOffset - NTS.calc)/2;
|
||||
console.log('NTS', NTS.latency, NTS.calc, objectiveTimeOffset);
|
||||
}, {});
|
||||
});
|
||||
});
|
||||
// You need to figure out how to make me write tests for this!
|
||||
// maybe we can do human based testing where we load a HTML that just
|
||||
// prints out in BIG FONT the objectiveTime it thinks it is
|
||||
// and we open it up on a couple devices.
|
@ -91,7 +91,6 @@
|
||||
// except for the ones that are listed in the message as having already been sending to.
|
||||
// all states, implemented with GET, are replied to the source that asked for it.
|
||||
function tran(req, cb){
|
||||
//Gun.log("gun.server", req);
|
||||
req.method = req.body? 'put' : 'get'; // put or get is based on whether there is a body or not
|
||||
req.url.key = req.url.pathname.replace(gun.server.regex,'').replace(/^\//i,'') || '';
|
||||
if('get' == req.method){ return tran.get(req, cb) }
|
||||
@ -102,6 +101,12 @@
|
||||
var key = req.url.key
|
||||
, reply = {headers: {'Content-Type': tran.json}};
|
||||
//console.log(req);
|
||||
/* NTS HACK! SHOULD BE ITS OWN ISOLATED MODULE!
|
||||
if(req && req.url && req.url.pathname && req.url.pathname.indexOf('gun.nts') >= 0){
|
||||
console.log("GOT IT");
|
||||
return cb({headers: reply.headers, body: {time: Gun.time.is() }});
|
||||
}
|
||||
NTS END! SHOULD HAVE BEEN ITS OWN MODULE */
|
||||
if(req && req.url && Gun.obj.has(req.url.query, '*')){
|
||||
return gun.all(req.url.key + req.url.search, function(err, list){
|
||||
cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : list || null ) })
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gun",
|
||||
"version": "0.2.0-alpha-4",
|
||||
"version": "0.2.1",
|
||||
"description": "Graph engine",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
29
test/nts.html
Normal file
29
test/nts.html
Normal file
@ -0,0 +1,29 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||
<script src="../gun.js"></script>
|
||||
<script src="../lib/nts.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="now" style="text-align: center; font-size: 10vmin; line-height: 95vh;">
|
||||
Hello!
|
||||
</div>
|
||||
<script>
|
||||
var gun = Gun('http://localhost:8080/gun');
|
||||
var $now = $('#now');
|
||||
|
||||
window.frame = function(cb){
|
||||
var requestAnimationFrame = window.requestAnimationFrame || function(cb){setTimeout(cb,0)}
|
||||
requestAnimationFrame(cb);
|
||||
}
|
||||
window.frame(function frame(){
|
||||
//window.frame(frame);
|
||||
var d = new Date()
|
||||
var s = d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate() + '/' +
|
||||
(d.getHours() + 1) + ':' + d.getSeconds() + '.' + d.getMilliseconds();
|
||||
$now.text(s);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
293
web/converse.html
Normal file
293
web/converse.html
Normal file
@ -0,0 +1,293 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./dep/jquery.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Now that we can jot down our thoughts in a <a href="./think.html">To-Do</a> app, let us add something ambitious to it. To-Do: Build a realtime chat app! Which is exactly what we are going to do in this tutorial. It is the next natural step, we can track our thoughts - but now we want to have conversations about them with others to refine our ideas.</p>
|
||||
|
||||
<p>So what are the requirements? The ability to write a message, send a message, and receive messages. We will also need a space to keep these messages in, and a web page to access the chat through. Let's start with making a copy of our code from the To-Do app! You can edit the code below, which will update the live preview.</p>
|
||||
|
||||
<div class="edit">... loading editor and preview ...
|
||||
<textarea style="height: 36em"><html>
|
||||
<body>
|
||||
<h1>Thoughts</h1>
|
||||
|
||||
<form>
|
||||
<input><button>Add</button>
|
||||
</form>
|
||||
|
||||
<ul></ul>
|
||||
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||
<script src="https://rawgit.com/amark/gun/master/gun.js"></script>
|
||||
<script>
|
||||
var gun = Gun().get('thoughts');
|
||||
$('form').on('submit', function(event){
|
||||
event.preventDefault();
|
||||
gun.set($('input').val());
|
||||
$('input').val("");
|
||||
});
|
||||
gun.on().map(function(thought, id){
|
||||
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
|
||||
if(thought){
|
||||
$(li).text(thought);
|
||||
} else {
|
||||
$(li).hide();
|
||||
}
|
||||
});
|
||||
$('body').on('dblclick', 'li', function(event){
|
||||
gun.path(this.id).put(null);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html></textarea>
|
||||
</div>
|
||||
<script>
|
||||
var code = $('.edit').load('./editor.html', function(){setTimeout(function(){
|
||||
var editor = $('.CodeMirror')[0].CodeMirror;
|
||||
editor.live(function(frame){
|
||||
$('body', frame).on('submit', 'form', function(e){e.preventDefault()});
|
||||
});
|
||||
editor.setValue(code);
|
||||
},1)}).find('textarea').text();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.none { display: none }
|
||||
.step-at { display: block }
|
||||
textarea { border: none; width: 100%; }
|
||||
</style>
|
||||
|
||||
<div id="step-0" class="step">
|
||||
<p>The first thing we need to do is update the HTML.</p>
|
||||
<ol>
|
||||
<li>Change the text in the <u>h1</u> tag to "Chat".</li>
|
||||
<li>Move the <u>ul</u> tags up to be above the <u>form</u> tag.</li>
|
||||
<li>Add an opening closing <u>textarea</u> tag inbetween the input and button.</li>
|
||||
<li>Make the <u>button</u> say "send" instead of "Add".</li>
|
||||
<li>
|
||||
Now we want to model what the HTML for our message will look like. Copy the following into the editor to be above the first <u>script</u> tag:
|
||||
<textarea style="height: 9em;"><div class="model" style="display: none;">
|
||||
<li class="message">
|
||||
<i class="when">0</i>
|
||||
<b class="who"></b>:
|
||||
<span class="what"></span>
|
||||
</li>
|
||||
</div>
|
||||
</textarea>
|
||||
</li>
|
||||
</ol>
|
||||
<p>What is happening?</p>
|
||||
<ul>
|
||||
<li>We do not actually have any messages yet, so we do not want an empty chat message displaying on the screen.</li>
|
||||
<li>There are a bunch of other HTML tags out there, let us do a quick review of the new ones we added.
|
||||
<ul>
|
||||
<li>The <u>textarea</u> tag is like an <u>input</u> tag except longer content.</li>
|
||||
<li>A <u>div</u> tag is a generic HTML tag to put anything else inside of.</li>
|
||||
<li>The <i><u>i</u>alicize</i> tag stylizes your text as so.</li>
|
||||
<li>The <b><u>b</u>old</b> tag does similarly.</li>
|
||||
<li>And a <u>span</u> tag is a generic HTML tag to put text inside of.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>It turns out that HTML allows you to add extra data to your page, let us go over them.
|
||||
<ul>
|
||||
<li>One of these attributes is a <u>class</u> which lets you categorize your HTML.</li>
|
||||
<li>The other one is a <u>style</u> attribute, which lets you style your HTML with <a href="https://www.google.com/search?q=CSS">CSS</a>.</li>
|
||||
</ul>
|
||||
<li>To keep our models hidden, we wrap them inside of <u>div</u> container, and then tell the computer not to display it.</li>
|
||||
<li>When we receive a message, we will make a copy of the <u>"message"</u> model and fill it up with the data using javascript.</li>
|
||||
</ul>
|
||||
<button class="none">All tweaked!</button>
|
||||
</div>
|
||||
|
||||
<div id="step-1" class="step">
|
||||
<p>Before we get started with writing any javascript, we need to clean up some code that no longer applies! Go ahead and remove the following 3 lines of code from the bottom of our script tag:</p>
|
||||
<textarea style="height: 4em;">
|
||||
$('body').on('dblclick', 'li', function(event){
|
||||
gun.path(this.id).put(null);
|
||||
});</textarea>
|
||||
<button class="none">I removed it!</button>
|
||||
</div>
|
||||
|
||||
<div id="step-2" class="step">
|
||||
<p>Wonderful! Now we want to change the name of the data so we do not get our previous app's data. Additionally, if we want this data to be shared and updated in realtime with other people, we have to connect to them directly or a server that does this for us. Modify the line where we initialize gun (hint: it is the very first line) with this:</p>
|
||||
<textarea style="height: 1em;"> var gun = Gun('https://gunjs.herokuapp.com/gun').get('tutorial/chat/app');</textarea>
|
||||
<p>What does this do?</p>
|
||||
<ul>
|
||||
<li>The URL <u>'https://gunjs.herokuapp.com/gun'</u> happens to be a gun peer.</li>
|
||||
<li>Anyone can run a gun peer, by using <a href="https://github.com/amark/gun">gun on a server</a>.</li>
|
||||
<li>This particular peer is a testing server, please do not use it outside these tutorials - it is not very fast or reliable, as we regularly delete data from it.</li>
|
||||
<li>The <u>'tutorial/chat/app'</u> is the name of our shared data, it acts as a key to help us find it.</li>
|
||||
</ul>
|
||||
<button class="none">Connect!</button>
|
||||
</div>
|
||||
|
||||
<div id="step-3" class="step">
|
||||
<p>Oops, our current code is handling the data incorrectly. We need to fix that, adjust the commented line in the editor to this:</p>
|
||||
<textarea style="height: 1em;">gun.map().val(function(message, id){</textarea>
|
||||
<p>Why?</p>
|
||||
<ul>
|
||||
<li>Because we do not expect <u>message</u>s to change, therefore we just get the <u>val</u>ue instead of reacting <u>on</u> updates.</li>
|
||||
<li>We also swap the order, placing <u>map</u> in front because we want to get each message item in the set (or that will be in the set) once rather than every item in the set every time it changes. Kinda confusing, but just reread that a couple of times and think about it.</li>
|
||||
</ul>
|
||||
<button class="none">Got it.</button>
|
||||
</div>
|
||||
|
||||
<div id="step-4" class="step">
|
||||
<p>We still have breaking changes, we need to revamp the inside of the val function with the following:</p>
|
||||
<textarea style="height: 8em;">
|
||||
var $li = $(
|
||||
$('#' + id).get(0) ||
|
||||
$('.model').find('.message').clone(true).attr('id', id).appendTo('ul')
|
||||
);
|
||||
$li.find('.who').text(message.who);
|
||||
$li.find('.what').text(message.what);
|
||||
$li.find('.when').text(message.when);</textarea>
|
||||
<p>What does this do?</p>
|
||||
<ul>
|
||||
<li>As before, if the list item already exists, use it.</li>
|
||||
<li><u>||</u> or find a model message, <u>clone</u> it, set its <u>id</u> so we can reuse it later, and <u>append</u> it to the list.</li>
|
||||
<li>Then fill in the model with the specific pieces of data from our message.</li>
|
||||
</ul>
|
||||
<p>Now we need to make sure that the data we save matches the same format as the messages we receive.</p>
|
||||
<button class="none">Okay.</button>
|
||||
</div>
|
||||
|
||||
<div id="step-5" class="step">
|
||||
<p>Replace everything inside of the form on submit function with the following:</p>
|
||||
<textarea style="height: 8em;">
|
||||
event.preventDefault();
|
||||
var message = {};
|
||||
message.who = $('form').find('input').val();
|
||||
message.what = $('form').find('textarea').val();
|
||||
message.when = new Date().getTime();
|
||||
gun.set(message);
|
||||
$('form').find('textarea').val("");</textarea>
|
||||
<p>What is happening?</p>
|
||||
<ul>
|
||||
<li>We still want to prevent the default behavior of the browser, which would be to reload the page.</li>
|
||||
<li>Then we want to create a javascript object using <u>{}</u> curly braces, which allows us to describe complex data.<li>Our data will be composed of three parts, <u>who</u> sent the message, <u>what</u> the message is, and <u>when</u> it was written.</li>
|
||||
<li>We then add this message to a <u>set</u> of messages in the chat, which will get <u>map</u>ped over.</li>
|
||||
<li>Finally we want to clear out the <u>textarea</u> we wrote our message in, so that way we can type another.</li>
|
||||
</ul>
|
||||
<button class="none">Try sending a message!</button>
|
||||
</div>
|
||||
|
||||
<div id="step-6" class="step">
|
||||
<p>Wow! Our chat is fully functioning! You can pat yourself on the back and be done now if you want. But there are lots of little things we can do to polish up the experience.</p>
|
||||
<p>We will not be doing any design, as that exceeds the scope of what this tutorial teaches. So prepare for your app to look pretty ugly, you can try to make things pretty on your own. What then, is left?</p>
|
||||
<ul>
|
||||
<li>The message's sent time is not human friendly, we will need to convert it.</li>
|
||||
<li>Messages currently may be shown out of order, so we need to order them correctly.</li>
|
||||
<li>Every time a message comes in you have to scroll down to read it, we should automate that.</li>
|
||||
</ul>
|
||||
<button class="none">Let's do it!</button>
|
||||
</div>
|
||||
|
||||
<div id="step-7" class="step">
|
||||
<p>Tutorial Unfinished, Sad Face. We're Working On It Though!</p>
|
||||
<p>Check out the <a href="https://github.com/amark/gun">github</a> in the meanwhile for more documentation.</p>
|
||||
</div>
|
||||
|
||||
<div id="step-8" class="step">
|
||||
<p>Congratulations! You have built a realtime chat app! That is quite the accomplishment, you should go celebrate by eating a sandwhich.</p>
|
||||
<p>Now that we can converse with people, we might want to know <i>who</i> they are. In the next tutorial we will create a contact app with some basic social network features and learn a little bit about security.</p>
|
||||
<a href="#">Coming Later!</a>
|
||||
<p>Check out the <a href="https://github.com/amark/gun">github</a> in the meanwhile for more documentation.</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('.step').addClass('none').first().addClass('step-at');
|
||||
$('button').on('click', next).removeClass('none');
|
||||
function next(){
|
||||
$('.step-at').removeClass('step-at');
|
||||
$('#step-' + (++next.at)).addClass('step-at');
|
||||
(next.step[next.at] || function(){})();
|
||||
}
|
||||
next.at = 0;
|
||||
next.comment = /\s*\/\*(.*?)\*\/\s*/i;
|
||||
next.unwrap = /\s*<\/?(html|body)>\s*\s*/ig;
|
||||
next.wrap = function(c){ return '<html>\n\t<body>\n\t\t' + c + '\n\t</body>\n</html>' }
|
||||
next.code = function(){ return $('<div>' + next.mir.getValue().replace(next.wrap, '') + '</div>') }
|
||||
next.step = [null, function(){
|
||||
next.mir = $('.CodeMirror')[0].CodeMirror;
|
||||
var code = next.code();
|
||||
if(code.find('h1').text().toLowerCase() == 'thoughts'){
|
||||
code.find('h1').text('Chat');
|
||||
}
|
||||
if(code.find('button').text().toLowerCase() == 'add'){
|
||||
code.find('button').text('send');
|
||||
}
|
||||
if(!code.find('textarea').length){
|
||||
code.find('button').before("<textarea>");
|
||||
}
|
||||
if(code.find('form').index() < code.find('ul').index()){
|
||||
code.find('ul').insertBefore(code.find('form')).after('\n\t\t');
|
||||
code.contents().filter(function(){
|
||||
return $(this).text().indexOf('\n') >= 0 && $(this).prev().is('form')
|
||||
}).remove();
|
||||
code.find('form').after('\n\n\t\t');
|
||||
}
|
||||
if(!code.find('.model').length){
|
||||
code.find('script').first().before($('#step-0').find('textarea').first().text() + '\n\t\t');
|
||||
}
|
||||
next.mir.setValue(next.wrap($.trim(code.html())));
|
||||
}, function(){
|
||||
var code = next.code();
|
||||
var script = code.find('script').last();
|
||||
script.text(script.text().slice(0, script.text().indexOf(
|
||||
$('#step-1').find('textarea').first().text().replace(/\s/ig,'').slice(0,7)
|
||||
)));
|
||||
next.mir.setValue(next.wrap($.trim(code.html())));
|
||||
}, function(){
|
||||
var code = next.code();
|
||||
var script = code.find('script').last();
|
||||
if(script.text().indexOf('Gun()')){
|
||||
script.text(script.text().replace('Gun()', 'Gun(' +
|
||||
($('#step-2').find('textarea').first().text().match(/Gun\((.*?)\)/i)||[])[1]
|
||||
+ ')'));
|
||||
}
|
||||
if(script.text().indexOf("'thoughts'")){
|
||||
script.text(script.text().replace("'thoughts'",
|
||||
($('#step-2').find('textarea').first().text().match(/get\((.*?)\)/i)||[])[1]
|
||||
));
|
||||
}
|
||||
script.text(script.text().replace(/gun\.on\(\)\.map(.*)\n/ig, "gun.on().map(function(thought, id){ // Edit this line!\n"));
|
||||
next.mir.setValue(next.wrap($.trim(code.html())));
|
||||
}, function(){
|
||||
var code = next.code();
|
||||
var script = code.find('script').last();
|
||||
if(script.text().indexOf('.on().map')){
|
||||
script.text(script.text().replace(/gun\.on\(\)\.map(.*)\n/ig,
|
||||
$('#step-3').find('textarea').first().text() + '\n'
|
||||
));
|
||||
}
|
||||
next.mir.setValue(next.wrap($.trim(code.html())));
|
||||
}, function(){
|
||||
var code = next.code();
|
||||
var script = code.find('script').last();
|
||||
var jsq = jsQuery(script.text());
|
||||
if(0 > jsq.find('.function').last().text().indexOf('.clone(')){
|
||||
jsq.find('.function').last().empty().append(
|
||||
jsQuery($('#step-4').find('textarea').first().text()).contents()
|
||||
);
|
||||
script.text(jsq.text());
|
||||
}
|
||||
next.mir.setValue(html_beautify(next.wrap($.trim(code.html()))));
|
||||
}, function(){
|
||||
var code = next.code();
|
||||
var script = code.find('script').last();
|
||||
var jsq = jsQuery(script.text());
|
||||
if(0 > jsq.find('.function').first().text().indexOf('.what = ')){
|
||||
jsq.find('.function').first().empty().append(
|
||||
jsQuery($('#step-5').find('textarea').first().text()).contents()
|
||||
);
|
||||
script.text(jsq.text());
|
||||
}
|
||||
next.mir.setValue(html_beautify(next.wrap($.trim(code.html()))));
|
||||
}]
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
491
web/dep/beautify/beautify-css.js
Normal file
491
web/dep/beautify/beautify-css.js
Normal 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;
|
||||
}
|
||||
|
||||
}());
|
952
web/dep/beautify/beautify-html.js
Normal file
952
web/dep/beautify/beautify-html.js
Normal 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);
|
||||
};
|
||||
}
|
||||
|
||||
}());
|
2085
web/dep/beautify/beautify.js
Normal file
2085
web/dep/beautify/beautify.js
Normal file
File diff suppressed because it is too large
Load Diff
103
web/dep/beautify/unpackers/javascriptobfuscator_unpacker.js
Normal file
103
web/dep/beautify/unpackers/javascriptobfuscator_unpacker.js
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
};
|
90
web/dep/beautify/unpackers/myobfuscate_unpacker.js
Normal file
90
web/dep/beautify/unpackers/myobfuscate_unpacker.js
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
};
|
80
web/dep/beautify/unpackers/p_a_c_k_e_r_unpacker.js
Normal file
80
web/dep/beautify/unpackers/p_a_c_k_e_r_unpacker.js
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
};
|
73
web/dep/beautify/unpackers/urlencode_unpacker.js
Normal file
73
web/dep/beautify/unpackers/urlencode_unpacker.js
Normal 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;
|
||||
}
|
73
web/dep/jsquery.js
Normal file
73
web/dep/jsquery.js
Normal file
@ -0,0 +1,73 @@
|
||||
jsQuery = (function(){
|
||||
if(!window || !window.jQuery){
|
||||
console.log("jQuery Required!");
|
||||
}
|
||||
var $ = window.jQuery;
|
||||
function jsQuery(js){
|
||||
if(typeof js === 'string'){
|
||||
js = jsQuery.dom(js);
|
||||
}
|
||||
return js;
|
||||
}
|
||||
var jsq = jsQuery;
|
||||
jsq.dom = function(text, env){
|
||||
return (function recurse(tree, dom, prev){
|
||||
dom = dom || $('<div>'); prev = '';
|
||||
$.each(tree, function(i, code){
|
||||
if($.isArray(code)){
|
||||
var div = recurse(code);
|
||||
if(0 <= prev.indexOf('function')){
|
||||
div.addClass('function');
|
||||
} else
|
||||
if(0 <= prev.indexOf('if') || 0 <= prev.indexOf('else')){
|
||||
div.addClass('if');
|
||||
}
|
||||
dom.append(div);
|
||||
} else
|
||||
if(code){
|
||||
dom.append($("<span>").text(code));
|
||||
}
|
||||
prev = code;
|
||||
});
|
||||
return dom;
|
||||
})(nestrecurse(text || "", ['{','}']));
|
||||
}
|
||||
function nestrecurse(text){
|
||||
text = text || "";
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var env = {i: -1, text: text, at: [], start: {}, end: {}, count: {}};
|
||||
var i = -1, l = text.length; while(++i < l){
|
||||
env.c = env.text[++env.i];
|
||||
var ii = -1, ll = args.length, s = '', e = ''; while(!(s || e) && ++ii < ll){
|
||||
var nest = args[ii], s = (typeof nest === "string");
|
||||
var start = s? nest : nest[0], end = s? nest : nest[1];
|
||||
var c = (start.length === 1? env.c : env.text.slice(env.i, start.length));
|
||||
if(start === c){
|
||||
if(env.count[start] == env.count[end] || 0){
|
||||
s = start;
|
||||
}
|
||||
env.count[start] = (env.count[start] || 0) + 1;
|
||||
} else
|
||||
if(end === c){
|
||||
env.count[end] = (env.count[end] || 0) + 1;
|
||||
if(env.count[end] == env.count[start] || 0){
|
||||
e = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(s){
|
||||
env.at.push(env.text.slice(0, env.i + s.length));
|
||||
env.text = env.text.slice(env.i + s.length); env.i = -1;
|
||||
}
|
||||
if(e){
|
||||
env.at.push(nestrecurse.apply(nestrecurse, [env.text.slice(0, env.i)].concat(args)));
|
||||
env.text = env.text.slice(env.i); env.i = e.length -1;
|
||||
}
|
||||
}
|
||||
if(env.text){
|
||||
env.at.push(env.text);
|
||||
}
|
||||
return env.at;
|
||||
}
|
||||
return jsQuery
|
||||
}());
|
@ -21,12 +21,24 @@
|
||||
</style>
|
||||
<textarea style="height: 15em"></textarea>
|
||||
<iframe></iframe>
|
||||
|
||||
<script src="./dep/jquery.js"></script>
|
||||
<script src="./dep/jsquery.js"></script>
|
||||
|
||||
<script src="./dep/codemirror/codemirror.js"></script>
|
||||
<script src="./dep/codemirror/xml.js"></script>
|
||||
<script src="./dep/codemirror/javascript.js"></script>
|
||||
<script src="./dep/codemirror/css.js"></script>
|
||||
<script src="./dep/codemirror/htmlmixed.js"></script>
|
||||
<script src="./dep/codemirror/matchbrackets.js"></script>
|
||||
|
||||
<script src="./dep/beautify/beautify.js"></script>
|
||||
<script src="./dep/beautify/beautify-css.js"></script>
|
||||
<script src="./dep/beautify/beautify-html.js"></script>
|
||||
<script src="./dep/beautify/unpackers/javascriptobfuscator_unpacker.js"></script>
|
||||
<script src="./dep/beautify/unpackers/urlencode_unpacker.js"></script>
|
||||
<script src="./dep/beautify/unpackers/p_a_c_k_e_r_unpacker.js"></script>
|
||||
<script src="./dep/beautify/unpackers/myobfuscate_unpacker.js"></script>
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea($('textarea')[0], {
|
||||
theme: 'colorforth',
|
||||
|
@ -323,7 +323,7 @@ var code = $('.edit').load('./editor.html', function(){setTimeout(function(){
|
||||
<div id="step-8" class="step">
|
||||
<p>Congratulations! You are all done, you have built your first GUN app!</p>
|
||||
<p>In the next tutorial we will use GUN to synchronize data in realtime across multiple devices. We'll start by copying the app we made here and modifying it to become a chat app.</p>
|
||||
<a href="#">Coming Soon!</a>
|
||||
<a href="./converse.html">Next Tutorial: Converse</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user