From e730cd9326ca083ed24abe01ad87db7f02219fc4 Mon Sep 17 00:00:00 2001 From: Kazuhiro Osawa Date: Sat, 17 Apr 2010 07:25:05 +0900 Subject: [PATCH] js file to share dirs --- Makefile.PL | 2 + lib/JSTAPd/Contents.pm | 226 +++--------------------------------- lib/JSTAPd/Server.pm | 16 ++- share/js/jstapd.deferred.js | 200 +++++++++++++++++++++++++++++++ share/js/jstapd.js | 194 +++++++++++++++++++++++++++++++ 5 files changed, 430 insertions(+), 208 deletions(-) create mode 100644 share/js/jstapd.deferred.js create mode 100644 share/js/jstapd.js diff --git a/Makefile.PL b/Makefile.PL index 8e6bfcc..9e0170f 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -15,7 +15,9 @@ requires 'Data::UUID'; requires 'Test::TCP'; requires 'FindBin'; requires 'Getopt::Long'; +requires 'File::ShareDir'; +install_share 'share'; install_script "scripts/jstapd"; tests 't/*.t'; diff --git a/lib/JSTAPd/Contents.pm b/lib/JSTAPd/Contents.pm index a8ff578..ad5848f 100644 --- a/lib/JSTAPd/Contents.pm +++ b/lib/JSTAPd/Contents.pm @@ -49,223 +49,35 @@ sub header { my($self, %args) = @_; my $script = $self->suite->client_script; - my $html = sprintf <<'HTML', $args{jstapd_prefix}, $args{session}, $args{path}, _default_tap_lib(), $args{include} || 'nop()', $script; + return sprintf <<'HTML', $args{jstapd_prefix}, $args{jstapd_prefix}, $args{jstapd_prefix}, $args{session}, $args{path}, ($args{include} || 'nop()'), $script; + + - HTML - } sub build_html { diff --git a/lib/JSTAPd/Server.pm b/lib/JSTAPd/Server.pm index 50d49fc..e8c11cc 100644 --- a/lib/JSTAPd/Server.pm +++ b/lib/JSTAPd/Server.pm @@ -3,6 +3,8 @@ use strict; use warnings; use AE; use Data::Dumper; +use File::ShareDir; +use File::Spec; use JSON::XS; use HTTP::Request; use LWP::UserAgent; @@ -12,6 +14,7 @@ use Plack::Request; use Plack::Response; use Plack::Runner; +use JSTAPd; use JSTAPd::ContentsBag; use JSTAPd::Server::Contents; use JSTAPd::TAP; @@ -155,7 +158,18 @@ sub handler { my $apiurl = $self->{conf}->{apiurl}; my $res; if ($req->uri->path eq '/favicon.ico') { - } elsif (my($path) = $req->uri->path =~ m!^/$jstapd_prefix/(.+)?$!) { + } elsif (my($path) = $req->uri->path =~ m!^/$jstapd_prefix/share/(.+)$!) { + # share files + my $root = eval { File::ShareDir::dist_dir('JSTAPd') } || do { + my @dirs = File::Spec->splitdir($INC{'JSTAPd.pm'}); + pop @dirs; + pop @dirs; + File::Spec->catfile(@dirs, 'share'); + }; + my $path = File::Spec->catfile($root, split('/', $1)); + open my $fh, '<', $path or die "$path: $!"; + return Plack::Response->new(200, [], $fh); + } elsif (($path) = $req->uri->path =~ m!^/$jstapd_prefix/(.+)?$!) { # serve jstapd contents $path = 'index' unless $path; $path .= 'index' if $path =~ m!/$! || !$path; diff --git a/share/js/jstapd.deferred.js b/share/js/jstapd.deferred.js new file mode 100644 index 0000000..e280878 --- /dev/null +++ b/share/js/jstapd.deferred.js @@ -0,0 +1,200 @@ +(function(){ +var id = 1; +window.jstapDeferred = function(){ + this.id = id++; +} + +jstapDeferred.prototype = { + cb: function(v){ return v }, + dnext: null, + error: null, + nextval: null, + retry: function(count, cb){ + if (this.error) return; + var ret = cb(); + }, + next: function(cb, m){ + this.dnext = new jstapDeferred(); + this.dnext.cb = cb; + return this.dnext; + }, + call: function(nextval){ + if (this.error) return; + var retval; + try { + if (this.nextval !== null) nextval = this.nextval; + retval = this.cb.call(this, nextval); + } catch (e) { + this.error = e; + } + if (retval instanceof jstapDeferred) { + retval.dnext = this.dnext; + if (retval.dnext !== null) retval.dnext.nextval = nextval; + } else { + if (this.dnext) this.dnext.call(retval); + } + }, + nop: function(r){ return r } +}; +jstapDeferred.next = function(f){ + var d = new jstapDeferred; + if (f) d.cb = f; + setTimeout(function(){ d.call() }, 0); + return d; +}; + +jstapDeferred.wait = function(t){ + var d = new jstapDeferred; + setTimeout(function(){ d.call() }, t); + return d; +}; + +jstapDeferred.retry = function(c,f,o){ + if (!o) o = {}; + var t = o.wait || 0; + var d = new jstapDeferred; + var val; + var retry = function(){ + if (d.dnext.nextval !== null) val = d.dnext.nextval; + d.dnext.nextval = null; + var ret = f(c, val); + if (ret) { + d.dnext.call(ret); + } else if (--c <= 0) { + d.error = 'retry failed'; + } else { + setTimeout(retry, t); + } + }; + setTimeout(retry, 0); + return d; +}; + + +jstapDeferred.xhr = function(o){ + if (!o) o = {}; + + var url = o.url; + if (!url) throw 'url missing'; + if (o.cache === false) { + var c = '_='+(new Date).getTime() + if (url.match(/\?/)) { + url += '&'+c; + } else { + url += '?'+c; + } + } + + var r = JSTAPd.xhr(); + r.open(o.method, url); + var d = new jstapDeferred; + r.onreadystatechange = function() { + if (r.readyState != 4) return; + d.call(r); + return null; + }; + r.send(null); + return d; +}; + +jstapDeferred.pop_request = function(o){ + if (!o) o = {}; + var retry = o.retry; + var wait = o.wait || 0; + var opts = {}; + if (o.requests) opts.requests = o.requests; + + var d = new jstapDeferred; + var func = function(req){ + d.dnext.nextval = req; // replace next value + d.call(req); + return null; + }; + + if (retry) { + var f = function(){ + pop_tap_request(function(req){ + if (req.length || --retry <= 0) { + return func(req); + } else { + // retry + setTimeout(f, wait); + } + }, opts); + }; + setTimeout(f, 0); + } else { + pop_tap_request(func, opts); + } + return d; +}; + +jstapDeferred.register = function(n, f){ + this.prototype[n] = function(){ + var a = arguments; + return this.next(function (v) { + return f.apply(this, a); + }); + }; +}; + +jstapDeferred.register('wait', jstapDeferred.wait); +jstapDeferred.register('retry', jstapDeferred.retry); +jstapDeferred.register('xhr', jstapDeferred.xhr); +jstapDeferred.register('pop_request', jstapDeferred.pop_request); + +// for *.t file +// load js libs +jstapDeferred.register('include', function(src){ + var d = new jstapDeferred; + var script = document.createElement('script'); + var onload = function(){ d.call() }; + if (typeof(script.onreadystatechange) == 'object') { + script.onreadystatechange = function(){ + if (script.readyState != 'loaded' && script.readyState != 'complete') return; + onload(); + }; + } else { + tap_addevent(script, 'load', onload); + } + script.src = src; + tap$tag('body').appendChild(script); + return d; +}); + +// waiting testing done +jstapDeferred.register('wait_finish', function(){ + var d = new jstapDeferred; + if (JSTAPd.tap_tests == 0) { + d.call(); + } else { + // async done mode + var do_async = function(){ + if (JSTAPd.tap_count >= JSTAPd.tap_tests) { + d.call(); + } else { + setTimeout(do_async, 10); + } + }; + setTimeout(do_async, 10); + } + return d; +}); + +// wait dequeueing +jstapDeferred.wait_dequeue = function(cb){ // cb is for test + var d = new jstapDeferred; + var wait = function(){ + if (JSTAPd.is_dequeueing()) { + if (cb && typeof(cb) == 'function') cb(false); + setTimeout(wait, 100); + } else { + if (cb && typeof(cb) == 'function') cb(true); + d.call(); + } + }; + setTimeout(wait, 0); + return d; +}; +jstapDeferred.register('wait_dequeue', jstapDeferred.wait_dequeue); +})(); diff --git a/share/js/jstapd.js b/share/js/jstapd.js new file mode 100644 index 0000000..617cb20 --- /dev/null +++ b/share/js/jstapd.js @@ -0,0 +1,194 @@ +if (typeof(JSTAPd) == 'undefined') JSTAPd = { + jstapd_prefix: null, + session : null, + path : null, + tap_count : 0, + tap_tests : 0, + xhr : function(){}, + is_dequeueing: function(){} +}; + +(function(){ +// _default_tap_lib +// queue +var queue = []; +var is_xhr_running = 0; +var in_dequeueing = false; +var dequeue = function(){ + if (_is_dequeueing()) return; + in_dequeueing = true; + var cb = queue.shift(); + if (cb && typeof cb == 'function') cb(); + in_dequeueing = false; +}; +var enqueue = function(cb){ + queue.push(cb); + dequeue(); +}; +var _is_dequeueing = function(){ return is_xhr_running || in_dequeueing }; +var is_dequeueing = function(){ return _is_dequeueing() || queue.length }; +JSTAPd.is_dequeueing = is_dequeueing; + +// ajax base +var xhr = function(){ + return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); +}; +JSTAPd.xhr = xhr; +var get = function(prefix, query, cb){ + var r = xhr(); + var uri = JSTAPd.jstapd_prefix + prefix + '?_='+(new Date).getTime(); + query.session = JSTAPd.session; + query.path = JSTAPd.path; + var query_stack = [uri]; + for (k in query) { + query_stack.push(encodeURIComponent(k) + '=' + encodeURIComponent(query[k])); + } + + r.open('GET', query_stack.join('&')); + r.onreadystatechange = function() { + if (r.readyState == 4 && r.status == 200) { + if (cb && typeof cb == 'function') cb(r); + is_xhr_running--; + dequeue(); + } + } + is_xhr_running++; + r.send(null); +}; +var tap = function(type, query, cb){ + query.type = type; + get('tap', query, cb); +}; + +// util +window.tap$ = function(id){ + return document.getElementById(id); +}; +window.tap$tag = function(tag){ + return document.getElementsByTagName(tag)[0]; +}; + +// test functions for *.t file +JSTAPd.tap_count = 0; +JSTAPd.tap_tests = 0; +window.tests = function(num){ + JSTAPd.tap_tests = num; + enqueue(function(){ + get('tests', { num: num }); + }); +}; +window.ok = function(val, msg){ + var ret; + var comment = ''; + try { + if (val) { + ret = 'ok'; + } else { + ret = 'not ok'; + } + } catch(e) { + comment = e; + } + + enqueue(function(){ + tap('ok', { + ret: ret, + num: (++(JSTAPd.tap_count)), + msg: msg, + comment: comment + }); + }); +}; +window.is = function(got, expected, msg, is_not){ + var ret; + var comment = ''; + try { + if (got == expected) { + ret = is_not ? 'not ok' : 'ok'; + } else { + ret = is_not ? 'ok' : 'not ok'; + } + } catch(e) { + comment = e; + } + + enqueue(function(){ + tap((is_not ? 'isnt' : 'is'), { + ret: ret, + num: (++(JSTAPd.tap_count)), + msg: msg, + got: got, + expected: expected, + comment: comment + }); + }); +}; +window.isnt = function(got, expected, msg){ + is(got, expected, msg, true); +}; +window.like = function(got, expected, msg, is_not){ + var ret; + var comment = ''; + try { + if (got.search(expected) >= 0) { + ret = is_not ? 'not ok' : 'ok'; + } else { + ret = is_not ? 'ok' : 'not ok'; + } + } catch(e) { + comment = e; + } + + enqueue(function(){ + tap((is_not ? 'unlike' : 'like'), { + ret: ret, + num: (++(JSTAPd.tap_count)), + msg: msg, + got: got, + expected: expected.toString(), + comment: comment + }); + }); +}; +window.unlike = function(got, expected, msg){ + like(got, expected, msg, true); +}; + +window.tap_done = function(error){ + enqueue(function(){ + get('tap_done', { error: error }, function(r){ + var div = document.createElement("div"); + div.innerHTML = r.responseText.replace(/\n/g, '
'); + tap$tag('body').appendChild(div); + tap$('jstap_users_body_container').style.display = 'none'; + }) + }); +}; + +window.tap_dump = function(){ + enqueue(function(){ + get('dump', {}) + }); +}; + +window.pop_tap_request = function(cb, opts){ + enqueue(function(){ + get('pop_tap_request', (opts || {}), function(r){ + var json; eval('json = ' + r.responseText); + cb(json); + }); + }); +}; + +window.tap_addevent = function(target, event, callback, useCapture){ + if (target.addEventListener) { + target.addEventListener(event, callback, useCapture); + } else if(target.attachEvent) { + target.attachEvent('on'+event, callback); + } +} + +window.tap_xhr = function(){ + return xhr(); +}; +})();