Skip to content

Commit

Permalink
Merge pull request #1 from mala/master
Browse files Browse the repository at this point in the history
mhxr is BK!!!!!
  • Loading branch information
motemen committed Jan 19, 2012
2 parents 9757cbc + be1e4fb commit 8cba17a
Showing 1 changed file with 15 additions and 144 deletions.
159 changes: 15 additions & 144 deletions lib/App/htmlcat.pm
Expand Up @@ -7,6 +7,7 @@ use HTML::Entities;
use Data::Section::Simple qw(get_data_section);
use IO::Socket::INET;
use Plack::Runner;
use Encode;

our $VERSION = '0.01';

Expand Down Expand Up @@ -61,16 +62,10 @@ sub broadcast {
}
}

sub boundary {
my $self = shift;
return $self->{boundary} ||= join '_', 'htmlcat', $$, time;
}

sub push_line {
my ($self, $handle, $line) = @_;
$handle->push_write("Content-Type: application/json; charset=utf-8\n\n");
$handle->push_write(json => { html => scalar $self->{ansi}->html($line) });
$handle->push_write('--' . $self->boundary . "\n");
$handle->push_write("data:" . Encode::encode("utf-8", scalar $self->{ansi}->html($line) ) );
$handle->push_write("\n");
}

sub as_psgi {
Expand All @@ -89,9 +84,10 @@ sub as_psgi {

my $writer = $respond->([
200,
[ 'Content-Type' => sprintf 'multipart/mixed; charset=utf-8; boundary="%s"', $self->boundary ]
[ 'Content-Type' => 'text/event-stream; charset=utf-8',
'Cache-Control' => 'no-cache'
]
]);
$writer->write('--' . $self->boundary . "\n");

my $io = $env->{'psgix.io'};
my $handle = AnyEvent::Handle->new(
Expand Down Expand Up @@ -182,11 +178,12 @@ __DATA__
<script type="text/javascript" src="/js"></script>
<script type="text/javascript">
window.onload = function () {
var mxhr = new MXHR();
mxhr.listen('application/json', function (json) {
var data = eval('(' + json + ')');
if (!data || !data.html) {
var es = new EventSource("/stream");
es.onmessage = function(event) {
var data = {};
// alert(event.data);
data.html = event.data;
if (!data.html) {
return;
}
Expand All @@ -195,7 +192,7 @@ window.onload = function () {
}
var div = document.createElement('div');
div.innerHTML = data.html;
div.innerHTML = data.html + "\n";
var out = document.getElementById('out');
while (div.firstChild) {
Expand All @@ -207,9 +204,7 @@ window.onload = function () {
if (scrollToBottom) {
window.scrollTo(0, document.body.scrollHeight);
}
});
mxhr.open('GET', '/stream', true);
mxhr.send('');
};
};
</script>
</head>
Expand All @@ -219,131 +214,7 @@ window.onload = function () {
</html>
@@ js
// https://gist.github.com/286747
/*
// mxhr.js
// BSD license
var mxhr = new MXHR;
mxhr.listen(mime, function(body){ process(body) });
mxhr.listen('complete', function(status_code){ ... }); // 2xx response
mxhr.listen('error', function(status_code){ ... }); // other case
mxhr.open("GET", url, true); // or mxhr.open("POST", url, true);
mxhr.send("");
*/
function MXHR() {
this.req = new XMLHttpRequest;
this.listeners = {};
this.watcher_interval = 15;
this.parsed = 0;
this.boundary;
this._watcher_id = null;
}
(function(p){
function open(){
var self = this;
var res = this.req.open.apply(this.req, arguments);
this.req.onreadystatechange = function(){
if (self.req.readyState == 3 && self._watcher_id == null) { self.init_stream() }
if (self.req.readyState == 4) { self.finish_stream(self.req.status) }
};
return res;
}
function send(){
return this.req.send.apply(this.req, arguments);
}
function init_stream(){
var self = this;
var contentTypeHeader = this.req.getResponseHeader("Content-Type");
if (contentTypeHeader.indexOf("multipart/mixed") == -1) {
this.req.onreadystatechange = function() {
self.req.onreadystatechange = function() {};
self.invoke_callback('error', self.req.status);
}
} else {
this.boundary = '--' + contentTypeHeader.split('"')[1];
this.start_watcher();
}
}
function finish_stream(status){
this.stop_watcher();
this.process_chunk();
if (status >= 200 && status < 300) {
this.invoke_callback('complete', status);
} else {
this.invoke_callback('error', status);
}
}
function start_watcher() {
var self = this;
this._watcher_id = window.setInterval(function(){
self.process_chunk();
}, this.watcher_interval);
}
function stop_watcher() {
window.clearInterval(this._watcher_id);
this._watcher_id = null;
}
function listen(mime, callback){
if(typeof this.listeners[mime] == 'undefined') {
this.listeners[mime] = [];
}
if(typeof callback != 'undefined' && callback.constructor == Function) {
this.listeners[mime].push(callback);
}
}
function process_chunk(){
var length = this.req.responseText.length;
var rbuf = this.req.responseText.substring(this.parsed, length);
// [parsed_length, header_and_body]
var res = this.incr_parse(rbuf);
if (res[0] > 0) {
this.process_part(res[1]);
this.parsed += res[0];
if (length > this.parsed) this.process_chunk();
}
}
function process_part(part) {
var self = this;
part = part.replace(this.boundary + "\n", '');
var lines = part.split("\n");
var mime = lines.shift().split('Content-Type:', 2)[1].split(";", 1)[0].replace(' ', '');
mime = mime ? mime : null;
var body = lines.join("\n");
this.invoke_callback(mime, body);
}
function invoke_callback(mime, body) {
var self = this;
if(typeof this.listeners[mime] != 'undefined') {
this.listeners[mime].forEach(function(cb) {
cb.call(self, body);
});
}
}
function incr_parse(buf) {
if (buf.length < 1) return [-1];
var start = buf.indexOf(this.boundary);
if (start == -1) return [-1];
var end = buf.indexOf(this.boundary, start + this.boundary.length);
// SUCCESS
if (start > -1 && end > -1) {
var part = buf.substring(start, end);
// end != part.length in wrong response, ignore it
return [end, part];
}
// INCOMPLETE
return [-1];
}
var methods = "open,send,init_stream,finish_stream,start_watcher,stop_watcher,listen," +
"process_chunk,process_part,invoke_callback,incr_parse";
eval(
methods.split(",").map(function(v){
return 'p.'+v+'='+v+';'
}).join("")
);
})(MXHR.prototype);
// TODO: for IE? https://github.com/Yaffle/EventSource
__END__
Expand Down

0 comments on commit 8cba17a

Please sign in to comment.