File Coverage

blib/lib/Acme/MUDLike.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


matched above from table fragments
line stmt bran cond sub pod time code
1             package Acme::MUDLike;
2              
3 1     1   25008 use 5.008000;
  1         5  
  1         42  
4 1     1   5 use strict;
  1         2  
  1         33  
5 1     1   4 use warnings;
  1         7  
  1         38  
6              
7 1     1   1446 use Continuity;
  0            
  0            
8             use Carp;
9             use Devel::Pointer;
10              
11             our $VERSION = '0.04';
12              
13             # Todo:
14             #
15             # * what would be *really* cool is doing on the fly image generation to draw an overhead map of the program based on a
16             # graph of which objects reference which other objects and let people go walk around inside of their program
17             # and then they could fight methods and use global variables as weapons!
18             #
19             # * http://zvtm.sourceforge.net/zgrviewer.html or something similar for showing the user the "map" of
20             # nodes/rooms/whatever made of has-a references or something.
21             #
22             # * /goto should put you inside an arbitrary object, /look should list as exits and/or items the object references contained by that object
23             # in other words, break away from our rigid API for inventory/room/etc.
24             #
25             # * need a black list black list, so we can re-add ourself to things that get serialized by Acme::State even though we're in %INC
26             #
27             # * need an error log viewabe by all.
28             #
29             # * eval and its output should be sent to the whole room.
30             #
31             # * Better account management.
32             #
33             # * There's code around to parse LPC and convert it to Perl. It would be neat to offer a full blown 2.4.5
34             # lib for people to play around in.
35             #
36             # * Acme::IRCLike would probably be more popular -- bolt an IRC server onto your app.
37             #
38             # * Also, a telnet interface beyond just an HTTP interface would be nice. Should be easy to do.
39             #
40             # * Let "players" wander between apps. Offer RPC to support this.
41             #
42             # * Optionally take an existing Continuity instance with path_session set and optionally parameters
43             # for the paths to use for chat pull and commands.
44             # Not sure how to work this; each path gets its own coroutine, but there is still only one main().
45             # Continuity doesn't have a registry of which paths go to which callbacks.
46             #
47             # Done:
48             #
49             # * mark/call commands should have a current object register, so you can do /call thingie whatever /next and then be calling
50             # into the object returned by thingie->whatever
51             #
52             # * /list (like look, but with stringified object references)
53             #
54             # * /mark ... or... /mark
55             #
56             # * messages still in duplicate when the same player logs in twice; make room's tell_object operate uniquely.
57             #
58             # * messages in triplicate because each player has three routines and is inserted into the floor three times. oops.
59             #
60             # * build the ajax.chat.js into source. -- okay, test.
61             #
62             # * eval, call
63             #
64             # * inventory's insert() method should set the insertee's environment to itself. that way, all objects have an environment.
65             #
66             # * Commands need to do $floor->tell_object or $self->tell_object rather than output directly.
67             #
68             # * Put @messages into the room ($floor). Get the chat action out of the main loop. Dispatch all
69             # actions. Maybe.
70             #
71              
72             our $password; # Acme::State friendly
73             our $floor; # holds all other objects
74             our $players; # holds all players; kind of like $floor except in the future, inactive players might get removed from the floor, or there might be multiple rooms
75              
76             my $continuity;
77             my $got_message; # diddled to wake the chat event watchers
78              
79             $SIG{PIPE} = 'IGNORE';
80              
81             sub new {
82             my $package = shift;
83             my %args = @_;
84              
85             die "We've already got one" if $continuity;
86              
87             $password = delete $args{password} if exists $args{password};
88             $password ||= join('', map { $_->[int rand scalar @$_] } (['a'..'z', 'A'..'Z', '0'..'9']) x 8),
89              
90             my $staticp = sub {
91             # warn "staticp: url->path: ``@{[ $_[0]->url->path ]}''";
92             return 0 if $_[0]->url->path =~ m/\.js$/;
93             # warn "staticp: dynamic js handling override not engaged";
94             return $_[0]->url->path =~ m/\.(jpg|jpeg|gif|png|css|ico|js)$/
95             };
96              
97             $continuity = $args{continuity} || Continuity->new(
98             staticp => sub { $staticp->(@_); },
99             callback => sub { login(@_) },
100             path_session => 1,
101             port => 2000,
102             %args,
103             );
104              
105             print "Admin:\n", $continuity->adapter->daemon->url, '?admin=', $password, '&nick=', (getpwuid $<)[0], "\n";
106              
107             $floor ||= Acme::MUDLike::room->new();
108             $players ||= Acme::MUDLike::inventory->new();
109              
110             bless { }, $package;
111             }
112              
113             sub loop { my $self = shift; $continuity->loop(@_); }
114              
115             sub header {
116             qq{
117            
118            
119            
120            
121             };
122             }
123              
124             sub footer { qq{\n}; }
125              
126             sub login {
127              
128             my $request = shift;
129              
130             #
131             # per-user variables
132             #
133              
134             my $player;
135              
136             # STDERR->print("debug: " . $request->request->url->path . "\n"); # XXX
137             # STDERR->print("debug: " . $request->request->as_string . "\n"); # XXX
138             $SIG{PIPE} = 'IGNORE'; # XXX not helping at all. grr.
139              
140             #
141             # static files
142             #
143              
144             if($request->request->url->path eq '/chat.js') {
145             # warn "handling chat.js XXX: ". $request->request->url->path;
146             $request->print(Acme::MUDLike::data->chat_js());
147             return;
148             } elsif($request->request->url->path eq '/jquery.js') {
149             # warn "handling jquery.js XXX: ". $request->request->url->path;
150             $request->print(Acme::MUDLike::data->jquery());
151             return;
152             }
153              
154             #
155             # login
156             #
157              
158             while(1) {
159             my $nick_tmp = $request->param('nick');
160             my $admin_tmp = $request->param('admin');
161             if(defined($nick_tmp) and defined($admin_tmp) and $nick_tmp =~ m/^[a-z]{2,20}$/i and $admin_tmp eq $password) {
162             my $nick = $nick_tmp;
163             $player = $players->named($nick) || $players->insert(Acme::MUDLike::player->new(name => $nick), );
164             $player->request = $request;
165             # @_ = ($player, $request,); goto &{Acme::MUDLike::player->can('command')};
166             $player->command($request); # doesn't return
167             }
168             # warn "trying login again XXX";
169             $nick_tmp ||= ''; $admin_tmp ||= '';
170             $nick_tmp =~ s/[^a-z]//gi; $admin_tmp =~ s/[^a-z0-9]//gi;
171             $request->print(
172             header, # $msg,
173             qq{
174            
175             <-- nickname
176             <-- admin password
177            
178            
179             },
180             footer,
181             );
182             $request->next();
183             }
184              
185             }
186              
187             #
188             # object
189             #
190              
191             package Acme::MUDLike::object;
192              
193             sub new { my $package = shift; bless { @_ }, $package; }
194             sub name :lvalue { $_[0]->{name} }
195             sub environment :lvalue { $_[0]->{environment} }
196             sub use { }
197             sub player { 0 }
198             sub desc { }
199             sub tell_object { }
200             sub get { 1 } # may be picked up
201             sub id { 0 }
202              
203             #
204             # inventory
205             #
206              
207             package Acme::MUDLike::inventory;
208              
209             sub new {
210             # subclass this to build little container classes or create instances of it directly
211             my $package = shift; bless [ ], $package;
212             }
213              
214             sub delete {
215             my $self = shift;
216             my $name = shift;
217             for my $i (0..$#$self) {
218             return splice @$self, $i, 1, () if $self->[$i]->id($name);
219             }
220             }
221             sub insert {
222             my $self = shift;
223             my $ob = shift;
224             UNIVERSAL::isa($ob, 'Acme::MUDLike::object') or Carp::confess('lit: ' . $ob . ' ref: ' . ref($ob));
225             push @$self, $ob;
226             $ob->environment = $self;
227             $ob;
228             }
229             sub named {
230             my $self = shift;
231             my $name = shift;
232             for my $i (@$self) {
233             return $i if $i->id($name);
234             }
235             }
236             sub apply {
237             my $self = shift;
238             my $func = shift;
239             my @args = @_;
240             my @ret;
241             for my $i (@$self) {
242             if(ref($func) eq 'CODE') {
243             push @ret, $func->($i, @args);
244             } else {
245             push @ret, $i->can($func)->($i, @args);
246             }
247             }
248             return @ret;
249             }
250              
251             sub contents {
252             my $self = shift;
253             return @$self;
254             }
255              
256             #
257             # room
258             #
259              
260             package Acme::MUDLike::room;
261             push our @ISA, 'Acme::MUDLike::inventory';
262              
263             sub tell_object {
264             my $self = shift;
265             my $message = shift;
266             # rather than buffering messages, room objects recurse and distribute the message to everyone and everything in it
267             # $self->apply('tell_object', $message);
268             my %already_told;
269             $self->apply(sub { return if $already_told{$_[0]}++; $_[0]->tell_object($message); }, );
270             }
271              
272             #
273             # players
274             #
275              
276             package Acme::MUDLike::players;
277             push our @ISA, 'Acme::MUDLike::inventory'; # use base 'Acme::MUDLike::inventory';
278              
279             #
280             # player
281             #
282              
283             package Acme::MUDLike::player;
284             push our @ISA, 'Acme::MUDLike::object';
285              
286             sub player { 1 }
287             sub new {
288             my $pack = shift;
289             bless {
290             inventory => Acme::MUDLike::inventory->new,
291             messages => [ ],
292             @_,
293             }, $pack;
294             }
295             sub request :lvalue { $_[0]->{request} }
296             sub id { $_[0]->{name} eq $_[1] or $_[0] eq $_[1] }
297             sub name { $_[0]->{name} }
298             sub password { $_[0]->{password} }
299             sub x :lvalue { $_[0]->{x} }
300             sub y :lvalue { $_[0]->{y} }
301             sub xy { $_[0]->{x}, $_[0]->{y} }
302             sub get { 0; } # can't be picked up
303             sub inventory { $_[0]->{inventory} }
304             sub evalcode :lvalue { $_[0]->{evalcode } }
305             sub current_item :lvalue { $_[0]->{current_item} }
306              
307             sub tell_object {
308             my $self = shift;
309             my $msg = shift;
310             push @{$self->{messages}}, $msg;
311             shift @{$self->{messages}} if @{$self->{messages}} > 100;
312             $got_message = 1; # XXX wish this didn't happen for each player but only once after all players got their message
313             }
314              
315             sub get_html_messages {
316             my $self = shift;
317             return join "
\n", map { s{<}{\<}gs; s{\n}{
\n}g; $_ } $self->get_messages;
318             }
319              
320             sub get_messages {
321             my $self = shift;
322             my @ret;
323             # this is written out long because I keep changing it around
324             for my $i (1..20) {
325             exists $self->{messages}->[-$i] or last;
326             my $msg = $self->{messages}->[-$i];
327             push @ret, $msg;
328             }
329             return reverse @ret;
330             }
331              
332             sub header () { Acme::MUDLike::header() }
333             sub footer () { Acme::MUDLike::footer() }
334              
335             sub command {
336              
337             my $self = shift;
338             my $request = shift;
339              
340             # this is called by login() immediately after verifying credientials
341              
342             if($request->request->url->path =~ m/pushstream/) {
343             # warn "pushstream path_session handling XXX";
344             my $w = Coro::Event->var(var => \$got_message, poll => 'w');
345             while(1) {
346             $w->next;
347             # warn "got_message diddled XXX";
348             # on submitting the form without a JS background post, the poll HTTP connection gets broken
349             $SIG{PIPE} = 'IGNORE';
350             $request->print( join "
\n", map { s{<}{\<}gs; s{\n}{
\n}g; $_ } $self->get_messages );
351             $request->next;
352             }
353             }
354              
355             if($request->request->url->path =~ m/sendmessage/) {
356             while(1) {
357             # warn "sendmessage path_session handling XXX";
358             my $msg = $request->param('message');
359             $self->parse_command($msg);
360             # $request->print("Got message.\n");
361             $request->print($self->get_html_messages());
362             $request->next;
363             }
364             }
365              
366             #
367             # players get three execution contexts:
368             # * one for AJAX message posts without header/footer in the reply
369             # * one for COMET message pulls
370             # * the main HTML one below (which might only run once); arbitrarily selected as being the main one cuz its longest
371             #
372              
373             $floor->insert($self);
374              
375             while(1) {
376              
377             $request->print(header);
378            
379             #
380             # chat/commands
381             #
382            
383             if($request->param('action') and $request->param('action') eq 'chat') {
384             # chat messages first so they appear in the log below
385             # there's only one action defined right now -- chat. everything else hangs off of that.
386             my $msg = $request->param('message');
387             $self->parse_command($msg);
388             };
389              
390             do {
391              
392             $request->print(qq{
393             Chat/Command:
394            
395            
396            
397            
398            
399            
400            
401            
402            
403            
404            
@{[ $self->get_html_messages ]}
405             });
406             };
407              
408             } continue {
409             $request->print(footer);
410             $request->next();
411             } # end while
412             }
413              
414             sub parse_command {
415             my $self = shift;
416             my $msg = shift;
417             warn "parse_command: msg: ``$msg''";
418             $self->tell_object("> $msg");
419             if($msg and $msg =~ m{^/}) {
420             my @args = split / /, $msg;
421             (my $cmd) = shift(@args) =~ m{/(\w+)};
422             # XXX I'd like to see template matching, like V N A N, then preact/act/postact
423             if( $self->can("_$cmd") ) {
424             eval { $self->can("_$cmd")->($self, @args); 1; } or $self->tell_object("Error in command: ``$@''.");
425             } else {
426             $self->tell_object("No such command: $cmd.");
427             }
428             } elsif($msg) {
429             $floor->tell_object($self->name . ': ' . $msg); # XXX should be $self->environment->tell_object
430             # $request->print("Got it!\n");
431             }
432             }
433              
434             sub item_by_arg {
435             my $self = shift;
436             my $item = shift;
437             my $ob;
438             return $self->current_item if $item eq 'current';
439             if($item =~ m/^\d+$/) {
440             my @stuff = $self->environment->contents;
441             $ob = $stuff[$item] if $item < @stuff;
442             }
443             $ob or $ob = $self->inventory->named($item); # thing in our inventory with that name
444             $ob or $ob = $self->environment->named($item); # thing in our environment with that name
445             $ob or $ob = $item if exists &{$item.'::new'}; # raw package name
446             $ob or do {
447             # Foo::Bar=HASH(0x812ea54)
448             my $hex;
449             ($hex) = $item =~ m{^[a-z][a-z_:]+\((0x[0-9a-z]+)\)}i;
450             $hex or ($hex) = $item =~ m{^0x([0-9a-z]+)}i;
451             if($hex) {
452             $ob = Devel::Pointer::deref(hex($hex));
453             }
454             };
455             return $ob;
456             }
457              
458             # actions
459              
460             sub _call {
461             my $self = shift;
462             # XXX call a method an in object
463             # XXX call sword name
464             my $item = shift;
465             my $func = shift;
466             my @args = @_; # XXX for each arg, go through the item finding code below, except keep identify if not found
467             my $ob = $self->item_by_arg($item) or do {
468             $self->tell_object("call: no item by that name/number/package name here");
469             return;
470             };
471             for my $i (0..$#args) {
472             my $x = $self->item_by_arg($args[$i]);
473             $args[$i] = $x if $x;
474             }
475             $ob->can($func) or do {
476             $self->tell_object("call: item ``$item'' has no ``$func'' method");
477             return;
478             };
479             $self->tell_object(join '', "Call: ", eval { $ob->can($func)->($ob, @args); } || "Error: ``$@''.");
480             1;
481             }
482              
483             sub _list {
484             my $self = shift;
485             my $i = 0;
486             $self->tell_object(join '',
487             "Here, you see:\n",
488             map qq{$_\n},
489             map { $i . ': ' . $_ }
490             $self->environment->contents, $self->inventory->contents,
491             );
492             }
493              
494             sub _mark {
495             my $self = shift;
496             my $item = shift;
497             my $ob = $self->item_by_arg($item) or do {
498             $self->tell_object("mark: no item by that name/number/package name here");
499             return;
500             };
501             $self->current_item = $ob;
502             }
503              
504             sub _eval {
505             my $self = shift;
506             my $cmd = join ' ', @_;
507             no warnings 'redefine';
508             # *print = sub { $self->tell_object(@_); }; # this doesn't work reliablely due to possible context changes but worth a shot
509             # *say = sub { $self->tell_object("@_\n"); }; # ack... doesn't work at all.
510             select $self->request->{request}->{conn}; # would rather it went into their message buffer but comprimising for now
511             my $res = eval($cmd) || "Error: ``$@''.";
512             $self->tell_object("eval:\n$res");
513             }
514              
515             sub _who {
516             my $self = shift;
517             $self->_look(@_); # for now
518             }
519              
520             sub _look {
521             my $self = shift;
522             my @args = @_;
523             # $self->tell_object(join '', "Here, you see:\n", map qq{$_\n}, map $_->name, $floor->contents);
524             $self->tell_object(join '',
525             "Here, you see:\n",
526             map qq{$_\n},
527             map { $_->can('name') ? $_->name : ref($_) }
528             $self->environment->contents
529             );
530             }
531              
532             sub _inv {
533             my $self = shift;
534             $self->_inventory(@_);
535             }
536              
537             sub _i {
538             my $self = shift;
539             $self->_inventory(@_);
540             }
541              
542             sub _inventory {
543             my $self = shift;
544             my @args = @_;
545             $self->tell_object(join '',
546             "You are holding:\n",
547             map qq{$_\n},
548             map { $_->can('name') ? $_->name : ''.$_ }
549             $self->inventory->contents
550             );
551             }
552              
553             sub _take {
554             my $self = shift;
555             my @args = @_;
556             if(@args == 1) {
557             # take thingie
558             my $item = $floor->delete($args[0]) or do { $self->tell_object("No ``$args[0]'' here."); return; };
559             $self->inventory->insert($item);
560             $self->tell_object(qq{Taken.});
561             } elsif(@args == 2) {
562             $self->tell_object("I don't understand ``$args[0] $args[1]''.");
563             } elsif(@args == 3) {
564             if($args[1] ne 'from') {
565             $self->tell_object("I don't understand ``$args[1] $args[2]''.");
566             return;
567             }
568             my $container = $floor->named($args[2]) or do { $self->tell_object("No ``$args[2]'' here."); return; };
569             my $item = $container->inventory->delete($args[0]) or do { $self->tell_object("No ``$args[0]'' here."); return; };
570             $self->inventory->insert($item);
571             $self->tell_object(qq{Taken.});
572             } elsif(! @args or @args > 3) {
573             $self->tell_object("Take what?");
574             }
575             }
576              
577             sub _drop {
578             my $self = shift;
579             my @args = @_;
580             if(@args != 1) {
581             $self->tell_object("Drop what?");
582             return;
583             }
584             my $item = $self->delete($args[0]) or do { $self->tell_object("You have no ``$args[0]''."); return; };
585             $floor->insert($item);
586             $self->tell_object(qq{Dropped.});
587             }
588              
589             sub _give {
590             my $self = shift;
591             my @args = @_;
592             if(@args != 3 or $args[1] ne 'to') {
593             $self->tell_object(qq{Give what to whom?});
594             return;
595             }
596             my $item = $self->inventory->named($args[0]) or do { $self->tell_object("You have no ``$args[0]''."); return; };
597             my $container = $floor->named($args[2]) or do { $self->tell_object("There is no ``$args[2]'' here."); return; };
598             $self->inventory->delete($args[0]);
599             $container->inventory->insert($item);
600             $self->tell_object("Ok.");
601             }
602              
603             sub _clone {
604             my $self = shift;
605             my $ob = shift;
606             if(! $ob) {
607             $self->tell_object(qq{Clone what?});
608             return;
609             }
610             my $item = eval { $ob->new() };
611             if(! $item ) {
612             $self->tell_object("Failed to load object: ``$@''.");
613             return;
614             }
615             # XXX force an inheritance of object onto it if it doesn't already have one?
616             $self->inventory->insert($item);
617             $self->tell_object("Ok.");
618             }
619              
620             sub _dest {
621             my $self = shift;
622             my @args = @_;
623             if(@args != 1) {
624             $self->tell_object(qq{Dest what?});
625             return;
626             }
627             $self->inventory->delete($args[0]) or do { $self->tell_object("You don't have a ``$args[0]''."); return; };
628             $self->tell_object("Dest: Ok.");
629             }
630              
631              
632             =head1 NAME
633              
634             Acme::MUDLike - Hang out inside of your application
635              
636             =head1 SYNOPSIS
637              
638             use Acme::MUDLike;
639             my $server = Acme::MUDLike->new;
640              
641             # ... your code here
642              
643             $server->loop; # or call the Event or AnyEvent event loop
644              
645             Connect to the URL provided and cut and paste into the text box:
646              
647             /eval package sword; our @ISA = qw/Acme::MUDLike::object/; sub new { my $pack = shift; $pack->SUPER::new(name=>"sword", @_); }
648             /clone sword
649             /i
650             /call sword name
651             wee, fun! oh, hai everyone!
652             /eval no strict "refs"; join '', map "$_\n", keys %{"main::"};
653             /call Acme::MUDLike::player=HASH(0x8985e10) name
654              
655             =head1 DESCRIPTION
656              
657             Multi user chat, general purpose object tracer, eval, and give/drop/take/clone/dest/look.
658              
659             Adds a social element to software development and develop it from within.
660             Chat within the application, eval code inside of it (sort of like a simple Read-Eval-Parse Loop).
661             Call methods in objects from the command line.
662             Create instances of objects, give them to people, drop them on the floor.
663              
664             The idea is take the simple command line interface and extend it with more commands,
665             and to create tools and helper objects that inspect and modify the running program from within.
666              
667             It fires up a Continuity/HTTP::Daemon based Web server on port 2000 and prints out a login
668             URL on the command line.
669             Paste the URL into your browser.
670             Chat with other users logged into the app.
671             Messages beginning with a slash, C, are interpreted as commands:
672              
673             =over 2
674              
675             =item C<< /look >>
676              
677             See who else and what else is in the room.
678              
679             =item C<< /mark >>
680              
681             /mark 1
682              
683             /mark torch
684              
685             /mark foo::bar
686              
687             /mark 0x812ea54
688              
689             Select an object as the logical current object by name, package name, number (as a position in your
690             inventory list, which is useful for when you've cloned an object that does not define an C or C function),
691             or by memory address (as in C<< Foo::Bar=HASH(0x812ea54) >>).
692              
693             =item C<< /call >>
694              
695             Call a function in an object; eg, if you're holding a C, you can write:
696              
697             /call toaster add_bread 1
698              
699             The special name "current" refers to the current object, as marked with mark.
700              
701             =item C<< /eval >>
702              
703             Executes Perl.
704             C<< $self >> is your own player object.
705             C<< $self->inventory >> is an C<< Acme::MUDLike::inventory >> object with C, C, C,
706             C, and C methods.
707             C<< $self->environment >> is also an C<< Acme::MUDLike::inventory >> object holding you and other players
708             and objects in the room.
709             The environment and players in it all have C methods that takes a string to add to their
710             message buffer.
711             Calling C in the environment sends the message to all players.
712             Objects define various other methods.
713              
714             =item C<< /who >>
715              
716             List of who is logged in. Currently the same C.
717              
718             =item C<< /inventory >>
719              
720             Or C or C. Lists the items you are carrying.
721              
722             =item C<< /clone >>
723              
724             Creates an instance of an object given a package name. Eg:
725              
726             /clone sword
727              
728             =item C<< /take >>
729              
730             Pick up an item from the floor (the room) and place it in your inventory.
731             Or alternatively C<< /take item from player >> to take something from someone.
732              
733             =item C<< /drop >>
734              
735             Drop an item on the floor.
736              
737             =item C<< /give >>
738              
739             Eg:
740              
741             /give sword to scrottie
742              
743             Transfers an object to another player.
744              
745             =item C<< /dest >>
746              
747             Destroys an object instance.
748              
749             =back
750              
751             =head2 new()
752              
753             Each running program may only have one L instance running.
754             It would be dumb to have two coexisting parallel universes tucked away inside the same program.
755             Hell, if anything, it would be nice to do some peer discovery, RPC, object serialization, etc,
756             and share objects between multiple running programs.
757              
758             =item C
759              
760             Optional. Pass in an existing L instance.
761             Must have been created with the parameter C<< path_session => 1 >>.
762              
763             =item C
764              
765             Optional. Defaults to C<2000>.
766             This and other parameters, such as those documented in L, are passed through
767             to C<< Continuity->new() >>.
768              
769             =item C
770              
771             Optional. Password to use.
772             Everyone gets the same password, and anyone with the password can log in with any name.
773             Otherwise one is pseudo-randomly generated and printed to C.
774              
775             =cut
776              
777             =head1 HISTORY
778              
779             =over 8
780              
781             =item 0.01
782              
783             Original version; created by h2xs 1.23 with options
784              
785             -A -C -X -b 5.8.0 -c -n Acme::MUDLike
786              
787             =back
788              
789             =head1 TODO
790              
791             (Major items... additional in the source.)
792              
793             =item Test. Very, very green right now.
794              
795             =item Telnet in as well as HTTP.
796              
797             =item JavaScript vi/L integration.
798              
799             =item Multiple rooms. Right now, there's just one.
800              
801             The JavaScript based vi and file browser I've been using with L isn't in any of my modules
802             yet so development from within isn't really practical using just these modules.
803             There's some glue missing.
804              
805             =head1 SEE ALSO
806              
807             =item L
808              
809             =item L
810              
811             =item L
812              
813             =item L
814              
815             L preserves state across runs and L.
816             These three modules work on their own but are complimentary to each other.
817             Using L, the program can be modified in-place without being restarted,
818             so you don't have to log back in again after each change batch of changes to the code.
819             Code changes take effect immediately.
820             L persists variable values when the program is finally stopped and restarted.
821             L will also optionally serialize code references to disc, so you can
822             C subs into existance and let it save them to disc for you and then later
823             use L to retrieve a version of the source.
824              
825             The C comments near the top of the source.
826              
827             =head1 AUTHOR
828              
829             Scott Walters, Escott@slowass.netE
830              
831             =head1 COPYRIGHT AND LICENSE
832              
833             Copyright (C) 2009 by Scott Walters
834              
835             This library is free software; you can redistribute it and/or modify
836             it under the same terms as Perl itself, either Perl version 5.8.9 or,
837             at your option, any later version of Perl 5 you may have available.
838             By using this software, you signify that you like llamas.
839              
840             Includes code by John Resig:
841              
842             jQuery 1.1.2 - New Wave Javascript
843              
844             Copyright (c) 2007 John Resig (jquery.com)
845             Dual licensed under the MIT (MIT-LICENSE.txt)
846             and GPL (GPL-LICENSE.txt) licenses.
847              
848             $Date: 2007-02-28 12:03:00 -0500 (Wed, 28 Feb 2007) $
849             $Rev: 1465 $
850              
851             Includes code by Awwaiid (Brock Wilcox)
852              
853             =cut
854              
855             package Acme::MUDLike::data;
856              
857             sub chat_js {
858              
859             return <<'EOF';
860              
861             var poll_count = 0;
862              
863             function new_request() {
864             var req;
865             if (window.XMLHttpRequest) {
866             req = new XMLHttpRequest();
867             } else if (window.ActiveXObject) {
868             req = new ActiveXObject("Microsoft.XMLHTTP");
869             }
870             return req;
871             }
872              
873             function do_request(url, callback) {
874             var req = new_request();
875             if(req != undefined) {
876             req.onreadystatechange = function() {
877             if (req.readyState == 4) { // only if req is "loaded"
878             if (req.status == 200) { // only if "OK"
879             if(callback) callback(req.responseText);
880             } else {
881             alert("AJAX Error:\r\n" + req.statusText);
882             }
883             }
884             }
885             req.open("GET", url, true);
886             req.send("");
887             }
888             }
889              
890             function setup_poll() {
891             setTimeout('poll_server()', 1000);
892             }
893              
894             function poll_server() {
895             var nick = document.getElementById("nick").value;
896             var admin = document.getElementById("admin").value;
897             document.getElementById('status').innerHTML = 'Polling ('+(poll_count++)+')...';
898             do_request('/pushstream/?nick=' + nick + '&admin=' + admin, got_update);
899             }
900              
901             function got_update(txt) {
902             document.getElementById('status').innerHTML = 'Got update.'
903             if(document.getElementById("log").innerHTML != txt)
904             document.getElementById("log").innerHTML = txt;
905             setup_poll();
906             }
907              
908             // This stuff gets executed once the document is loaded
909             $(function(){
910             // Start up the long-pull cycle
911             setup_poll();
912             // Unobtrusively make submitting a message use send_message()
913             $('#f').submit(send_message);
914             });
915              
916             // We also send messages using AJAX
917             function send_message() {
918             var nick = $('#nick').val();
919             var admin = $('#admin').val();
920             var message = $('#message').val();
921             $('#log').load('/sendmessage', {
922             nick: nick,
923             admin: admin,
924             action: 'ajaxchat',
925             message: message
926             }, function() {
927             $('#message').val('');
928             $('#message').focus();
929             });
930             return false;
931             }
932              
933             EOF
934              
935             }
936              
937             sub jquery {
938              
939             return <<'EOF';
940              
941             /* prevent execution of jQuery if included more than once */
942             if(typeof window.jQuery == "undefined") {
943             /*
944             * jQuery 1.1.2 - New Wave Javascript
945             *
946             * Copyright (c) 2007 John Resig (jquery.com)
947             * Dual licensed under the MIT (MIT-LICENSE.txt)
948             * and GPL (GPL-LICENSE.txt) licenses.
949             *
950             * $Date: 2007-02-28 12:03:00 -0500 (Wed, 28 Feb 2007) $
951             * $Rev: 1465 $
952             */
953              
954             // Global undefined variable
955             window.undefined = window.undefined;
956             var jQuery = function(a,c) {
957             // If the context is global, return a new object
958             if ( window == this )
959             return new jQuery(a,c);
960              
961             // Make sure that a selection was provided
962             a = a || document;
963            
964             // HANDLE: $(function)
965             // Shortcut for document ready
966             if ( jQuery.isFunction(a) )
967             return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
968            
969             // Handle HTML strings
970             if ( typeof a == "string" ) {
971             // HANDLE: $(html) -> $(array)
972             var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
973             if ( m )
974             a = jQuery.clean( [ m[1] ] );
975            
976             // HANDLE: $(expr)
977             else
978             return new jQuery( c ).find( a );
979             }
980            
981             return this.setArray(
982             // HANDLE: $(array)
983             a.constructor == Array && a ||
984              
985             // HANDLE: $(arraylike)
986             // Watch for when an array-like object is passed as the selector
987             (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||
988              
989             // HANDLE: $(*)
990             [ a ] );
991             };
992              
993             // Map over the $ in case of overwrite
994             if ( typeof $ != "undefined" )
995             jQuery._$ = $;
996            
997             // Map the jQuery namespace to the '$' one
998             var $ = jQuery;
999              
1000             jQuery.fn = jQuery.prototype = {
1001             jquery: "1.1.2",
1002              
1003             size: function() {
1004             return this.length;
1005             },
1006            
1007             length: 0,
1008              
1009             get: function( num ) {
1010             return num == undefined ?
1011              
1012             // Return a 'clean' array
1013             jQuery.makeArray( this ) :
1014              
1015             // Return just the object
1016             this[num];
1017             },
1018             pushStack: function( a ) {
1019             var ret = jQuery(a);
1020             ret.prevObject = this;
1021             return ret;
1022             },
1023             setArray: function( a ) {
1024             this.length = 0;
1025             [].push.apply( this, a );
1026             return this;
1027             },
1028             each: function( fn, args ) {
1029             return jQuery.each( this, fn, args );
1030             },
1031             index: function( obj ) {
1032             var pos = -1;
1033             this.each(function(i){
1034             if ( this == obj ) pos = i;
1035             });
1036             return pos;
1037             },
1038              
1039             attr: function( key, value, type ) {
1040             var obj = key;
1041            
1042             // Look for the case where we're accessing a style value
1043             if ( key.constructor == String )
1044             if ( value == undefined )
1045             return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined;
1046             else {
1047             obj = {};
1048             obj[ key ] = value;
1049             }
1050            
1051             // Check to see if we're setting style values
1052             return this.each(function(index){
1053             // Set all the styles
1054             for ( var prop in obj )
1055             jQuery.attr(
1056             type ? this.style : this,
1057             prop, jQuery.prop(this, obj[prop], type, index, prop)
1058             );
1059             });
1060             },
1061              
1062             css: function( key, value ) {
1063             return this.attr( key, value, "curCSS" );
1064             },
1065              
1066             text: function(e) {
1067             if ( typeof e == "string" )
1068             return this.empty().append( document.createTextNode( e ) );
1069              
1070             var t = "";
1071             jQuery.each( e || this, function(){
1072             jQuery.each( this.childNodes, function(){
1073             if ( this.nodeType != 8 )
1074             t += this.nodeType != 1 ?
1075             this.nodeValue : jQuery.fn.text([ this ]);
1076             });
1077             });
1078             return t;
1079             },
1080              
1081             wrap: function() {
1082             // The elements to wrap the target around
1083             var a = jQuery.clean(arguments);
1084              
1085             // Wrap each of the matched elements individually
1086             return this.each(function(){
1087             // Clone the structure that we're using to wrap
1088             var b = a[0].cloneNode(true);
1089              
1090             // Insert it before the element to be wrapped
1091             this.parentNode.insertBefore( b, this );
1092              
1093             // Find the deepest point in the wrap structure
1094             while ( b.firstChild )
1095             b = b.firstChild;
1096              
1097             // Move the matched element to within the wrap structure
1098             b.appendChild( this );
1099             });
1100             },
1101             append: function() {
1102             return this.domManip(arguments, true, 1, function(a){
1103             this.appendChild( a );
1104             });
1105             },
1106             prepend: function() {
1107             return this.domManip(arguments, true, -1, function(a){
1108             this.insertBefore( a, this.firstChild );
1109             });
1110             },
1111             before: function() {
1112             return this.domManip(arguments, false, 1, function(a){
1113             this.parentNode.insertBefore( a, this );
1114             });
1115             },
1116             after: function() {
1117             return this.domManip(arguments, false, -1, function(a){
1118             this.parentNode.insertBefore( a, this.nextSibling );
1119             });
1120             },
1121             end: function() {
1122             return this.prevObject || jQuery([]);
1123             },
1124             find: function(t) {
1125             return this.pushStack( jQuery.map( this, function(a){
1126             return jQuery.find(t,a);
1127             }), t );
1128             },
1129             clone: function(deep) {
1130             return this.pushStack( jQuery.map( this, function(a){
1131             var a = a.cloneNode( deep != undefined ? deep : true );
1132             a.$events = null; // drop $events expando to avoid firing incorrect events
1133             return a;
1134             }) );
1135             },
1136              
1137             filter: function(t) {
1138             return this.pushStack(
1139             jQuery.isFunction( t ) &&
1140             jQuery.grep(this, function(el, index){
1141             return t.apply(el, [index])
1142             }) ||
1143              
1144             jQuery.multiFilter(t,this) );
1145             },
1146              
1147             not: function(t) {
1148             return this.pushStack(
1149             t.constructor == String &&
1150             jQuery.multiFilter(t, this, true) ||
1151              
1152             jQuery.grep(this, function(a) {
1153             return ( t.constructor == Array || t.jquery )
1154             ? jQuery.inArray( a, t ) < 0
1155             : a != t;
1156             })
1157             );
1158             },
1159              
1160             add: function(t) {
1161             return this.pushStack( jQuery.merge(
1162             this.get(),
1163             t.constructor == String ?
1164             jQuery(t).get() :
1165             t.length != undefined && (!t.nodeName || t.nodeName == "FORM") ?
1166             t : [t] )
1167             );
1168             },
1169             is: function(expr) {
1170             return expr ? jQuery.filter(expr,this).r.length > 0 : false;
1171             },
1172              
1173             val: function( val ) {
1174             return val == undefined ?
1175             ( this.length ? this[0].value : null ) :
1176             this.attr( "value", val );
1177             },
1178              
1179             html: function( val ) {
1180             return val == undefined ?
1181             ( this.length ? this[0].innerHTML : null ) :
1182             this.empty().append( val );
1183             },
1184             domManip: function(args, table, dir, fn){
1185             var clone = this.length > 1;
1186             var a = jQuery.clean(args);
1187             if ( dir < 0 )
1188             a.reverse();
1189              
1190             return this.each(function(){
1191             var obj = this;
1192              
1193             if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") )
1194             obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody"));
1195              
1196             jQuery.each( a, function(){
1197             fn.apply( obj, [ clone ? this.cloneNode(true) : this ] );
1198             });
1199              
1200             });
1201             }
1202             };
1203              
1204             jQuery.extend = jQuery.fn.extend = function() {
1205             // copy reference to target object
1206             var target = arguments[0],
1207             a = 1;
1208              
1209             // extend jQuery itself if only one argument is passed
1210             if ( arguments.length == 1 ) {
1211             target = this;
1212             a = 0;
1213             }
1214             var prop;
1215             while (prop = arguments[a++])
1216             // Extend the base object
1217             for ( var i in prop ) target[i] = prop[i];
1218              
1219             // Return the modified object
1220             return target;
1221             };
1222              
1223             jQuery.extend({
1224             noConflict: function() {
1225             if ( jQuery._$ )
1226             $ = jQuery._$;
1227             return jQuery;
1228             },
1229              
1230             // This may seem like some crazy code, but trust me when I say that this
1231             // is the only cross-browser way to do this. --John
1232             isFunction: function( fn ) {
1233             return !!fn && typeof fn != "string" && !fn.nodeName &&
1234             typeof fn[0] == "undefined" && /function/i.test( fn + "" );
1235             },
1236            
1237             // check if an element is in a XML document
1238             isXMLDoc: function(elem) {
1239             return elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
1240             },
1241              
1242             nodeName: function( elem, name ) {
1243             return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
1244             },
1245             // args is for internal usage only
1246             each: function( obj, fn, args ) {
1247             if ( obj.length == undefined )
1248             for ( var i in obj )
1249             fn.apply( obj[i], args || [i, obj[i]] );
1250             else
1251             for ( var i = 0, ol = obj.length; i < ol; i++ )
1252             if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break;
1253             return obj;
1254             },
1255            
1256             prop: function(elem, value, type, index, prop){
1257             // Handle executable functions
1258             if ( jQuery.isFunction( value ) )
1259             value = value.call( elem, [index] );
1260            
1261             // exclude the following css properties to add px
1262             var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
1263              
1264             // Handle passing in a number to a CSS property
1265             return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ?
1266             value + "px" :
1267             value;
1268             },
1269              
1270             className: {
1271             // internal only, use addClass("class")
1272             add: function( elem, c ){
1273             jQuery.each( c.split(/\s+/), function(i, cur){
1274             if ( !jQuery.className.has( elem.className, cur ) )
1275             elem.className += ( elem.className ? " " : "" ) + cur;
1276             });
1277             },
1278              
1279             // internal only, use removeClass("class")
1280             remove: function( elem, c ){
1281             elem.className = c ?
1282             jQuery.grep( elem.className.split(/\s+/), function(cur){
1283             return !jQuery.className.has( c, cur );
1284             }).join(" ") : "";
1285             },
1286              
1287             // internal only, use is(".class")
1288             has: function( t, c ) {
1289             t = t.className || t;
1290             // escape regex characters
1291             c = c.replace(/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
1292             return t && new RegExp("(^|\\s)" + c + "(\\s|$)").test( t );
1293             }
1294             },
1295             swap: function(e,o,f) {
1296             for ( var i in o ) {
1297             e.style["old"+i] = e.style[i];
1298             e.style[i] = o[i];
1299             }
1300             f.apply( e, [] );
1301             for ( var i in o )
1302             e.style[i] = e.style["old"+i];
1303             },
1304              
1305             css: function(e,p) {
1306             if ( p == "height" || p == "width" ) {
1307             var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];
1308              
1309             jQuery.each( d, function(){
1310             old["padding" + this] = 0;
1311             old["border" + this + "Width"] = 0;
1312             });
1313              
1314             jQuery.swap( e, old, function() {
1315             if (jQuery.css(e,"display") != "none") {
1316             oHeight = e.offsetHeight;
1317             oWidth = e.offsetWidth;
1318             } else {
1319             e = jQuery(e.cloneNode(true))
1320             .find(":radio").removeAttr("checked").end()
1321             .css({
1322             visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0"
1323             }).appendTo(e.parentNode)[0];
1324              
1325             var parPos = jQuery.css(e.parentNode,"position");
1326             if ( parPos == "" || parPos == "static" )
1327             e.parentNode.style.position = "relative";
1328              
1329             oHeight = e.clientHeight;
1330             oWidth = e.clientWidth;
1331              
1332             if ( parPos == "" || parPos == "static" )
1333             e.parentNode.style.position = "static";
1334              
1335             e.parentNode.removeChild(e);
1336             }
1337             });
1338              
1339             return p == "height" ? oHeight : oWidth;
1340             }
1341              
1342             return jQuery.curCSS( e, p );
1343             },
1344              
1345             curCSS: function(elem, prop, force) {
1346             var ret;
1347            
1348             if (prop == "opacity" && jQuery.browser.msie)
1349             return jQuery.attr(elem.style, "opacity");
1350            
1351             if (prop == "float" || prop == "cssFloat")
1352             prop = jQuery.browser.msie ? "styleFloat" : "cssFloat";
1353              
1354             if (!force && elem.style[prop])
1355             ret = elem.style[prop];
1356              
1357             else if (document.defaultView && document.defaultView.getComputedStyle) {
1358              
1359             if (prop == "cssFloat" || prop == "styleFloat")
1360             prop = "float";
1361              
1362             prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
1363             var cur = document.defaultView.getComputedStyle(elem, null);
1364              
1365             if ( cur )
1366             ret = cur.getPropertyValue(prop);
1367             else if ( prop == "display" )
1368             ret = "none";
1369             else
1370             jQuery.swap(elem, { display: "block" }, function() {
1371             var c = document.defaultView.getComputedStyle(this, "");
1372             ret = c && c.getPropertyValue(prop) || "";
1373             });
1374              
1375             } else if (elem.currentStyle) {
1376              
1377             var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});
1378             ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
1379            
1380             }
1381              
1382             return ret;
1383             },
1384            
1385             clean: function(a) {
1386             var r = [];
1387              
1388             jQuery.each( a, function(i,arg){
1389             if ( !arg ) return;
1390              
1391             if ( arg.constructor == Number )
1392             arg = arg.toString();
1393            
1394             // Convert html string into DOM nodes
1395             if ( typeof arg == "string" ) {
1396             // Trim whitespace, otherwise indexOf won't work as expected
1397             var s = jQuery.trim(arg), div = document.createElement("div"), tb = [];
1398              
1399             var wrap =
1400             // option or optgroup
1401             !s.indexOf("
1402             [1, ""] ||
1403            
1404             (!s.indexOf("
1405             [1, "", "
"] ||
1406            
1407             !s.indexOf("
1408             [2, "", "
"] ||
1409            
1410             //
1411             (!s.indexOf("
1412             [3, "", "
"] ||
1413            
1414             [0,"",""];
1415              
1416             // Go to html and back, then peel off extra wrappers
1417             div.innerHTML = wrap[1] + s + wrap[2];
1418            
1419             // Move to the right depth
1420             while ( wrap[0]-- )
1421             div = div.firstChild;
1422            
1423             // Remove IE's autoinserted
1424             if ( jQuery.browser.msie ) {
1425            
1426             // String was a , *may* have spurious or
1427             if ( !s.indexOf("
1428             tb = div.firstChild && div.firstChild.childNodes;
1429            
1430             // String was a bare
1431             else if ( wrap[1] == "" && s.indexOf("
1432             tb = div.childNodes;
1433              
1434             for ( var n = tb.length-1; n >= 0 ; --n )
1435             if ( jQuery.nodeName(tb[n], "tbody") && !tb[n].childNodes.length )
1436             tb[n].parentNode.removeChild(tb[n]);
1437            
1438             }
1439            
1440             arg = [];
1441             for (var i=0, l=div.childNodes.length; i
1442             arg.push(div.childNodes[i]);
1443             }
1444              
1445             if ( arg.length === 0 && !jQuery.nodeName(arg, "form") )
1446             return;
1447            
1448             if ( arg[0] == undefined || jQuery.nodeName(arg, "form") )
1449             r.push( arg );
1450             else
1451             r = jQuery.merge( r, arg );
1452              
1453             });
1454              
1455             return r;
1456             },
1457            
1458             attr: function(elem, name, value){
1459             var fix = jQuery.isXMLDoc(elem) ? {} : {
1460             "for": "htmlFor",
1461             "class": "className",
1462             "float": jQuery.browser.msie ? "styleFloat" : "cssFloat",
1463             cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
1464             innerHTML: "innerHTML",
1465             className: "className",
1466             value: "value",
1467             disabled: "disabled",
1468             checked: "checked",
1469             readonly: "readOnly",
1470             selected: "selected"
1471             };
1472            
1473             // IE actually uses filters for opacity ... elem is actually elem.style
1474             if ( name == "opacity" && jQuery.browser.msie && value != undefined ) {
1475             // IE has trouble with opacity if it does not have layout
1476             // Force it by setting the zoom level
1477             elem.zoom = 1;
1478              
1479             // Set the alpha filter to set the opacity
1480             return elem.filter = elem.filter.replace(/alpha\([^\)]*\)/gi,"") +
1481             ( value == 1 ? "" : "alpha(opacity=" + value * 100 + ")" );
1482              
1483             } else if ( name == "opacity" && jQuery.browser.msie )
1484             return elem.filter ?
1485             parseFloat( elem.filter.match(/alpha\(opacity=(.*)\)/)[1] ) / 100 : 1;
1486            
1487             // Mozilla doesn't play well with opacity 1
1488             if ( name == "opacity" && jQuery.browser.mozilla && value == 1 )
1489             value = 0.9999;
1490            
1491              
1492             // Certain attributes only work when accessed via the old DOM 0 way
1493             if ( fix[name] ) {
1494             if ( value != undefined ) elem[fix[name]] = value;
1495             return elem[fix[name]];
1496              
1497             } else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method") )
1498             return elem.getAttributeNode(name).nodeValue;
1499              
1500             // IE elem.getAttribute passes even for style
1501             else if ( elem.tagName ) {
1502             if ( value != undefined ) elem.setAttribute( name, value );
1503             if ( jQuery.browser.msie && /href|src/.test(name) && !jQuery.isXMLDoc(elem) )
1504             return elem.getAttribute( name, 2 );
1505             return elem.getAttribute( name );
1506              
1507             // elem is actually elem.style ... set the style
1508             } else {
1509             name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
1510             if ( value != undefined ) elem[name] = value;
1511             return elem[name];
1512             }
1513             },
1514             trim: function(t){
1515             return t.replace(/^\s+|\s+$/g, "");
1516             },
1517              
1518             makeArray: function( a ) {
1519             var r = [];
1520              
1521             if ( a.constructor != Array )
1522             for ( var i = 0, al = a.length; i < al; i++ )
1523             r.push( a[i] );
1524             else
1525             r = a.slice( 0 );
1526              
1527             return r;
1528             },
1529              
1530             inArray: function( b, a ) {
1531             for ( var i = 0, al = a.length; i < al; i++ )
1532             if ( a[i] == b )
1533             return i;
1534             return -1;
1535             },
1536             merge: function(first, second) {
1537             var r = [].slice.call( first, 0 );
1538              
1539             // Now check for duplicates between the two arrays
1540             // and only add the unique items
1541             for ( var i = 0, sl = second.length; i < sl; i++ )
1542             // Check for duplicates
1543             if ( jQuery.inArray( second[i], r ) == -1 )
1544             // The item is unique, add it
1545             first.push( second[i] );
1546              
1547             return first;
1548             },
1549             grep: function(elems, fn, inv) {
1550             // If a string is passed in for the function, make a function
1551             // for it (a handy shortcut)
1552             if ( typeof fn == "string" )
1553             fn = new Function("a","i","return " + fn);
1554              
1555             var result = [];
1556              
1557             // Go through the array, only saving the items
1558             // that pass the validator function
1559             for ( var i = 0, el = elems.length; i < el; i++ )
1560             if ( !inv && fn(elems[i],i) || inv && !fn(elems[i],i) )
1561             result.push( elems[i] );
1562              
1563             return result;
1564             },
1565             map: function(elems, fn) {
1566             // If a string is passed in for the function, make a function
1567             // for it (a handy shortcut)
1568             if ( typeof fn == "string" )
1569             fn = new Function("a","return " + fn);
1570              
1571             var result = [], r = [];
1572              
1573             // Go through the array, translating each of the items to their
1574             // new value (or values).
1575             for ( var i = 0, el = elems.length; i < el; i++ ) {
1576             var val = fn(elems[i],i);
1577              
1578             if ( val !== null && val != undefined ) {
1579             if ( val.constructor != Array ) val = [val];
1580             result = result.concat( val );
1581             }
1582             }
1583              
1584             var r = result.length ? [ result[0] ] : [];
1585              
1586             check: for ( var i = 1, rl = result.length; i < rl; i++ ) {
1587             for ( var j = 0; j < i; j++ )
1588             if ( result[i] == r[j] )
1589             continue check;
1590              
1591             r.push( result[i] );
1592             }
1593              
1594             return r;
1595             }
1596             });
1597            
1598             /*
1599             * Whether the W3C compliant box model is being used.
1600             *
1601             * @property
1602             * @name $.boxModel
1603             * @type Boolean
1604             * @cat JavaScript
1605             */
1606             new function() {
1607             var b = navigator.userAgent.toLowerCase();
1608              
1609             // Figure out what browser is being used
1610             jQuery.browser = {
1611             safari: /webkit/.test(b),
1612             opera: /opera/.test(b),
1613             msie: /msie/.test(b) && !/opera/.test(b),
1614             mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
1615             };
1616              
1617             // Check to see if the W3C box model is being used
1618             jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
1619             };
1620              
1621             jQuery.each({
1622             parent: "a.parentNode",
1623             parents: "jQuery.parents(a)",
1624             next: "jQuery.nth(a,2,'nextSibling')",
1625             prev: "jQuery.nth(a,2,'previousSibling')",
1626             siblings: "jQuery.sibling(a.parentNode.firstChild,a)",
1627             children: "jQuery.sibling(a.firstChild)"
1628             }, function(i,n){
1629             jQuery.fn[ i ] = function(a) {
1630             var ret = jQuery.map(this,n);
1631             if ( a && typeof a == "string" )
1632             ret = jQuery.multiFilter(a,ret);
1633             return this.pushStack( ret );
1634             };
1635             });
1636              
1637             jQuery.each({
1638             appendTo: "append",
1639             prependTo: "prepend",
1640             insertBefore: "before",
1641             insertAfter: "after"
1642             }, function(i,n){
1643             jQuery.fn[ i ] = function(){
1644             var a = arguments;
1645             return this.each(function(){
1646             for ( var j = 0, al = a.length; j < al; j++ )
1647             jQuery(a[j])[n]( this );
1648             });
1649             };
1650             });
1651              
1652             jQuery.each( {
1653             removeAttr: function( key ) {
1654             jQuery.attr( this, key, "" );
1655             this.removeAttribute( key );
1656             },
1657             addClass: function(c){
1658             jQuery.className.add(this,c);
1659             },
1660             removeClass: function(c){
1661             jQuery.className.remove(this,c);
1662             },
1663             toggleClass: function( c ){
1664             jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this, c);
1665             },
1666             remove: function(a){
1667             if ( !a || jQuery.filter( a, [this] ).r.length )
1668             this.parentNode.removeChild( this );
1669             },
1670             empty: function() {
1671             while ( this.firstChild )
1672             this.removeChild( this.firstChild );
1673             }
1674             }, function(i,n){
1675             jQuery.fn[ i ] = function() {
1676             return this.each( n, arguments );
1677             };
1678             });
1679              
1680             jQuery.each( [ "eq", "lt", "gt", "contains" ], function(i,n){
1681             jQuery.fn[ n ] = function(num,fn) {
1682             return this.filter( ":" + n + "(" + num + ")", fn );
1683             };
1684             });
1685              
1686             jQuery.each( [ "height", "width" ], function(i,n){
1687             jQuery.fn[ n ] = function(h) {
1688             return h == undefined ?
1689             ( this.length ? jQuery.css( this[0], n ) : null ) :
1690             this.css( n, h.constructor == String ? h : h + "px" );
1691             };
1692             });
1693             jQuery.extend({
1694             expr: {
1695             "": "m[2]=='*'||jQuery.nodeName(a,m[2])",
1696             "#": "a.getAttribute('id')==m[2]",
1697             ":": {
1698             // Position Checks
1699             lt: "i
1700             gt: "i>m[3]-0",
1701             nth: "m[3]-0==i",
1702             eq: "m[3]-0==i",
1703             first: "i==0",
1704             last: "i==r.length-1",
1705             even: "i%2==0",
1706             odd: "i%2",
1707              
1708             // Child Checks
1709             "nth-child": "jQuery.nth(a.parentNode.firstChild,m[3],'nextSibling',a)==a",
1710             "first-child": "jQuery.nth(a.parentNode.firstChild,1,'nextSibling')==a",
1711             "last-child": "jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a",
1712             "only-child": "jQuery.sibling(a.parentNode.firstChild).length==1",
1713              
1714             // Parent Checks
1715             parent: "a.firstChild",
1716             empty: "!a.firstChild",
1717              
1718             // Text Check
1719             contains: "jQuery.fn.text.apply([a]).indexOf(m[3])>=0",
1720              
1721             // Visibility
1722             visible: 'a.type!="hidden"&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',
1723             hidden: 'a.type=="hidden"||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',
1724              
1725             // Form attributes
1726             enabled: "!a.disabled",
1727             disabled: "a.disabled",
1728             checked: "a.checked",
1729             selected: "a.selected||jQuery.attr(a,'selected')",
1730              
1731             // Form elements
1732             text: "a.type=='text'",
1733             radio: "a.type=='radio'",
1734             checkbox: "a.type=='checkbox'",
1735             file: "a.type=='file'",
1736             password: "a.type=='password'",
1737             submit: "a.type=='submit'",
1738             image: "a.type=='image'",
1739             reset: "a.type=='reset'",
1740             button: 'a.type=="button"||jQuery.nodeName(a,"button")',
1741             input: "/input|select|textarea|button/i.test(a.nodeName)"
1742             },
1743             ".": "jQuery.className.has(a,m[2])",
1744             "@": {
1745             "=": "z==m[4]",
1746             "!=": "z!=m[4]",
1747             "^=": "z&&!z.indexOf(m[4])",
1748             "$=": "z&&z.substr(z.length - m[4].length,m[4].length)==m[4]",
1749             "*=": "z&&z.indexOf(m[4])>=0",
1750             "": "z",
1751             _resort: function(m){
1752             return ["", m[1], m[3], m[2], m[5]];
1753             },
1754             _prefix: "z=a[m[3]];if(!z||/href|src/.test(m[3]))z=jQuery.attr(a,m[3]);"
1755             },
1756             "[": "jQuery.find(m[2],a).length"
1757             },
1758            
1759             // The regular expressions that power the parsing engine
1760             parse: [
1761             // Match: [@value='test'], [@foo]
1762             /^\[ *(@)([a-z0-9_-]*) *([!*$^=]*) *('?"?)(.*?)\4 *\]/i,
1763              
1764             // Match: [div], [div p]
1765             /^(\[)\s*(.*?(\[.*?\])?[^[]*?)\s*\]/,
1766              
1767             // Match: :contains('foo')
1768             /^(:)([a-z0-9_-]*)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/i,
1769              
1770             // Match: :even, :last-chlid
1771             /^([:.#]*)([a-z0-9_*-]*)/i
1772             ],
1773              
1774             token: [
1775             /^(\/?\.\.)/, "a.parentNode",
1776             /^(>|\/)/, "jQuery.sibling(a.firstChild)",
1777             /^(\+)/, "jQuery.nth(a,2,'nextSibling')",
1778             /^(~)/, function(a){
1779             var s = jQuery.sibling(a.parentNode.firstChild);
1780             return s.slice(jQuery.inArray(a,s) + 1);
1781             }
1782             ],
1783              
1784             multiFilter: function( expr, elems, not ) {
1785             var old, cur = [];
1786              
1787             while ( expr && expr != old ) {
1788             old = expr;
1789             var f = jQuery.filter( expr, elems, not );
1790             expr = f.t.replace(/^\s*,\s*/, "" );
1791             cur = not ? elems = f.r : jQuery.merge( cur, f.r );
1792             }
1793              
1794             return cur;
1795             },
1796             find: function( t, context ) {
1797             // Quickly handle non-string expressions
1798             if ( typeof t != "string" )
1799             return [ t ];
1800              
1801             // Make sure that the context is a DOM Element
1802             if ( context && !context.nodeType )
1803             context = null;
1804              
1805             // Set the correct context (if none is provided)
1806             context = context || document;
1807              
1808             // Handle the common XPath // expression
1809             if ( !t.indexOf("//") ) {
1810             context = context.documentElement;
1811             t = t.substr(2,t.length);
1812              
1813             // And the / root expression
1814             } else if ( !t.indexOf("/") ) {
1815             context = context.documentElement;
1816             t = t.substr(1,t.length);
1817             if ( t.indexOf("/") >= 1 )
1818             t = t.substr(t.indexOf("/"),t.length);
1819             }
1820              
1821             // Initialize the search
1822             var ret = [context], done = [], last = null;
1823              
1824             // Continue while a selector expression exists, and while
1825             // we're no longer looping upon ourselves
1826             while ( t && last != t ) {
1827             var r = [];
1828             last = t;
1829              
1830             t = jQuery.trim(t).replace( /^\/\//i, "" );
1831              
1832             var foundToken = false;
1833              
1834             // An attempt at speeding up child selectors that
1835             // point to a specific element tag
1836             var re = /^[\/>]\s*([a-z0-9*-]+)/i;
1837             var m = re.exec(t);
1838              
1839             if ( m ) {
1840             // Perform our own iteration and filter
1841             jQuery.each( ret, function(){
1842             for ( var c = this.firstChild; c; c = c.nextSibling )
1843             if ( c.nodeType == 1 && ( jQuery.nodeName(c, m[1]) || m[1] == "*" ) )
1844             r.push( c );
1845             });
1846              
1847             ret = r;
1848             t = t.replace( re, "" );
1849             if ( t.indexOf(" ") == 0 ) continue;
1850             foundToken = true;
1851             } else {
1852             // Look for pre-defined expression tokens
1853             for ( var i = 0; i < jQuery.token.length; i += 2 ) {
1854             // Attempt to match each, individual, token in
1855             // the specified order
1856             var re = jQuery.token[i];
1857             var m = re.exec(t);
1858              
1859             // If the token match was found
1860             if ( m ) {
1861             // Map it against the token's handler
1862             r = ret = jQuery.map( ret, jQuery.isFunction( jQuery.token[i+1] ) ?
1863             jQuery.token[i+1] :
1864             function(a){ return eval(jQuery.token[i+1]); });
1865              
1866             // And remove the token
1867             t = jQuery.trim( t.replace( re, "" ) );
1868             foundToken = true;
1869             break;
1870             }
1871             }
1872             }
1873              
1874             // See if there's still an expression, and that we haven't already
1875             // matched a token
1876             if ( t && !foundToken ) {
1877             // Handle multiple expressions
1878             if ( !t.indexOf(",") ) {
1879             // Clean the result set
1880             if ( ret[0] == context ) ret.shift();
1881              
1882             // Merge the result sets
1883             jQuery.merge( done, ret );
1884              
1885             // Reset the context
1886             r = ret = [context];
1887              
1888             // Touch up the selector string
1889             t = " " + t.substr(1,t.length);
1890              
1891             } else {
1892             // Optomize for the case nodeName#idName
1893             var re2 = /^([a-z0-9_-]+)(#)([a-z0-9\\*_-]*)/i;
1894             var m = re2.exec(t);
1895            
1896             // Re-organize the results, so that they're consistent
1897             if ( m ) {
1898             m = [ 0, m[2], m[3], m[1] ];
1899              
1900             } else {
1901             // Otherwise, do a traditional filter check for
1902             // ID, class, and element selectors
1903             re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
1904             m = re2.exec(t);
1905             }
1906              
1907             // Try to do a global search by ID, where we can
1908             if ( m[1] == "#" && ret[ret.length-1].getElementById ) {
1909             // Optimization for HTML document case
1910             var oid = ret[ret.length-1].getElementById(m[2]);
1911            
1912             // Do a quick check for the existence of the actual ID attribute
1913             // to avoid selecting by the name attribute in IE
1914             if ( jQuery.browser.msie && oid && oid.id != m[2] )
1915             oid = jQuery('[@id="'+m[2]+'"]', ret[ret.length-1])[0];
1916              
1917             // Do a quick check for node name (where applicable) so
1918             // that div#foo searches will be really fast
1919             ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
1920              
1921             } else {
1922             // Pre-compile a regular expression to handle class searches
1923             if ( m[1] == "." )
1924             var rec = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");
1925              
1926             // We need to find all descendant elements, it is more
1927             // efficient to use getAll() when we are already further down
1928             // the tree - we try to recognize that here
1929             jQuery.each( ret, function(){
1930             // Grab the tag name being searched for
1931             var tag = m[1] != "" || m[0] == "" ? "*" : m[2];
1932              
1933             // Handle IE7 being really dumb about s
1934             if ( jQuery.nodeName(this, "object") && tag == "*" )
1935             tag = "param";
1936              
1937             jQuery.merge( r,
1938             m[1] != "" && ret.length != 1 ?
1939             jQuery.getAll( this, [], m[1], m[2], rec ) :
1940             this.getElementsByTagName( tag )
1941             );
1942             });
1943              
1944             // It's faster to filter by class and be done with it
1945             if ( m[1] == "." && ret.length == 1 )
1946             r = jQuery.grep( r, function(e) {
1947             return rec.test(e.className);
1948             });
1949              
1950             // Same with ID filtering
1951             if ( m[1] == "#" && ret.length == 1 ) {
1952             // Remember, then wipe out, the result set
1953             var tmp = r;
1954             r = [];
1955              
1956             // Then try to find the element with the ID
1957             jQuery.each( tmp, function(){
1958             if ( this.getAttribute("id") == m[2] ) {
1959             r = [ this ];
1960             return false;
1961             }
1962             });
1963             }
1964              
1965             ret = r;
1966             }
1967              
1968             t = t.replace( re2, "" );
1969             }
1970              
1971             }
1972              
1973             // If a selector string still exists
1974             if ( t ) {
1975             // Attempt to filter it
1976             var val = jQuery.filter(t,r);
1977             ret = r = val.r;
1978             t = jQuery.trim(val.t);
1979             }
1980             }
1981              
1982             // Remove the root context
1983             if ( ret && ret[0] == context ) ret.shift();
1984              
1985             // And combine the results
1986             jQuery.merge( done, ret );
1987              
1988             return done;
1989             },
1990              
1991             filter: function(t,r,not) {
1992             // Look for common filter expressions
1993             while ( t && /^[a-z[({<*:.#]/i.test(t) ) {
1994              
1995             var p = jQuery.parse, m;
1996              
1997             jQuery.each( p, function(i,re){
1998            
1999             // Look for, and replace, string-like sequences
2000             // and finally build a regexp out of it
2001             m = re.exec( t );
2002              
2003             if ( m ) {
2004             // Remove what we just matched
2005             t = t.substring( m[0].length );
2006              
2007             // Re-organize the first match
2008             if ( jQuery.expr[ m[1] ]._resort )
2009             m = jQuery.expr[ m[1] ]._resort( m );
2010              
2011             return false;
2012             }
2013             });
2014              
2015             // :not() is a special case that can be optimized by
2016             // keeping it out of the expression list
2017             if ( m[1] == ":" && m[2] == "not" )
2018             r = jQuery.filter(m[3], r, true).r;
2019              
2020             // Handle classes as a special case (this will help to
2021             // improve the speed, as the regexp will only be compiled once)
2022             else if ( m[1] == "." ) {
2023              
2024             var re = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");
2025             r = jQuery.grep( r, function(e){
2026             return re.test(e.className || "");
2027             }, not);
2028              
2029             // Otherwise, find the expression to execute
2030             } else {
2031             var f = jQuery.expr[m[1]];
2032             if ( typeof f != "string" )
2033             f = jQuery.expr[m[1]][m[2]];
2034              
2035             // Build a custom macro to enclose it
2036             eval("f = function(a,i){" +
2037             ( jQuery.expr[ m[1] ]._prefix || "" ) +
2038             "return " + f + "}");
2039              
2040             // Execute it against the current filter
2041             r = jQuery.grep( r, f, not );
2042             }
2043             }
2044              
2045             // Return an array of filtered elements (r)
2046             // and the modified expression string (t)
2047             return { r: r, t: t };
2048             },
2049            
2050             getAll: function( o, r, token, name, re ) {
2051             for ( var s = o.firstChild; s; s = s.nextSibling )
2052             if ( s.nodeType == 1 ) {
2053             var add = true;
2054              
2055             if ( token == "." )
2056             add = s.className && re.test(s.className);
2057             else if ( token == "#" )
2058             add = s.getAttribute("id") == name;
2059            
2060             if ( add )
2061             r.push( s );
2062              
2063             if ( token == "#" && r.length ) break;
2064              
2065             if ( s.firstChild )
2066             jQuery.getAll( s, r, token, name, re );
2067             }
2068              
2069             return r;
2070             },
2071             parents: function( elem ){
2072             var matched = [];
2073             var cur = elem.parentNode;
2074             while ( cur && cur != document ) {
2075             matched.push( cur );
2076             cur = cur.parentNode;
2077             }
2078             return matched;
2079             },
2080             nth: function(cur,result,dir,elem){
2081             result = result || 1;
2082             var num = 0;
2083             for ( ; cur; cur = cur[dir] ) {
2084             if ( cur.nodeType == 1 ) num++;
2085             if ( num == result || result == "even" && num % 2 == 0 && num > 1 && cur == elem ||
2086             result == "odd" && num % 2 == 1 && cur == elem ) return cur;
2087             }
2088             },
2089             sibling: function( n, elem ) {
2090             var r = [];
2091              
2092             for ( ; n; n = n.nextSibling ) {
2093             if ( n.nodeType == 1 && (!elem || n != elem) )
2094             r.push( n );
2095             }
2096              
2097             return r;
2098             }
2099             });
2100             /*
2101             * A number of helper functions used for managing events.
2102             * Many of the ideas behind this code orignated from
2103             * Dean Edwards' addEvent library.
2104             */
2105             jQuery.event = {
2106              
2107             // Bind an event to an element
2108             // Original by Dean Edwards
2109             add: function(element, type, handler, data) {
2110             // For whatever reason, IE has trouble passing the window object
2111             // around, causing it to be cloned in the process
2112             if ( jQuery.browser.msie && element.setInterval != undefined )
2113             element = window;
2114              
2115             // if data is passed, bind to handler
2116             if( data )
2117             handler.data = data;
2118              
2119             // Make sure that the function being executed has a unique ID
2120             if ( !handler.guid )
2121             handler.guid = this.guid++;
2122              
2123             // Init the element's event structure
2124             if (!element.$events)
2125             element.$events = {};
2126              
2127             // Get the current list of functions bound to this event
2128             var handlers = element.$events[type];
2129              
2130             // If it hasn't been initialized yet
2131             if (!handlers) {
2132             // Init the event handler queue
2133             handlers = element.$events[type] = {};
2134              
2135             // Remember an existing handler, if it's already there
2136             if (element["on" + type])
2137             handlers[0] = element["on" + type];
2138             }
2139              
2140             // Add the function to the element's handler list
2141             handlers[handler.guid] = handler;
2142              
2143             // And bind the global event handler to the element
2144             element["on" + type] = this.handle;
2145              
2146             // Remember the function in a global list (for triggering)
2147             if (!this.global[type])
2148             this.global[type] = [];
2149             this.global[type].push( element );
2150             },
2151              
2152             guid: 1,
2153             global: {},
2154              
2155             // Detach an event or set of events from an element
2156             remove: function(element, type, handler) {
2157             if (element.$events) {
2158             var i,j,k;
2159             if ( type && type.type ) { // type is actually an event object here
2160             handler = type.handler;
2161             type = type.type;
2162             }
2163            
2164             if (type && element.$events[type])
2165             // remove the given handler for the given type
2166             if ( handler )
2167             delete element.$events[type][handler.guid];
2168            
2169             // remove all handlers for the given type
2170             else
2171             for ( i in element.$events[type] )
2172             delete element.$events[type][i];
2173            
2174             // remove all handlers
2175             else
2176             for ( j in element.$events )
2177             this.remove( element, j );
2178            
2179             // remove event handler if no more handlers exist
2180             for ( k in element.$events[type] )
2181             if (k) {
2182             k = true;
2183             break;
2184             }
2185             if (!k) element["on" + type] = null;
2186             }
2187             },
2188              
2189             trigger: function(type, data, element) {
2190             // Clone the incoming data, if any
2191             data = jQuery.makeArray(data || []);
2192              
2193             // Handle a global trigger
2194             if ( !element )
2195             jQuery.each( this.global[type] || [], function(){
2196             jQuery.event.trigger( type, data, this );
2197             });
2198              
2199             // Handle triggering a single element
2200             else {
2201             var handler = element["on" + type ], val,
2202             fn = jQuery.isFunction( element[ type ] );
2203              
2204             if ( handler ) {
2205             // Pass along a fake event
2206             data.unshift( this.fix({ type: type, target: element }) );
2207            
2208             // Trigger the event
2209             if ( (val = handler.apply( element, data )) !== false )
2210             this.triggered = true;
2211             }
2212              
2213             if ( fn && val !== false )
2214             element[ type ]();
2215              
2216             this.triggered = false;
2217             }
2218             },
2219              
2220             handle: function(event) {
2221             // Handle the second event of a trigger and when
2222             // an event is called after a page has unloaded
2223             if ( typeof jQuery == "undefined" || jQuery.event.triggered ) return;
2224              
2225             // Empty object is for triggered events with no data
2226             event = jQuery.event.fix( event || window.event || {} );
2227              
2228             // returned undefined or false
2229             var returnValue;
2230              
2231             var c = this.$events[event.type];
2232              
2233             var args = [].slice.call( arguments, 1 );
2234             args.unshift( event );
2235              
2236             for ( var j in c ) {
2237             // Pass in a reference to the handler function itself
2238             // So that we can later remove it
2239             args[0].handler = c[j];
2240             args[0].data = c[j].data;
2241              
2242             if ( c[j].apply( this, args ) === false ) {
2243             event.preventDefault();
2244             event.stopPropagation();
2245             returnValue = false;
2246             }
2247             }
2248              
2249             // Clean up added properties in IE to prevent memory leak
2250             if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;
2251              
2252             return returnValue;
2253             },
2254              
2255             fix: function(event) {
2256             // Fix target property, if necessary
2257             if ( !event.target && event.srcElement )
2258             event.target = event.srcElement;
2259              
2260             // Calculate pageX/Y if missing and clientX/Y available
2261             if ( event.pageX == undefined && event.clientX != undefined ) {
2262             var e = document.documentElement, b = document.body;
2263             event.pageX = event.clientX + (e.scrollLeft || b.scrollLeft);
2264             event.pageY = event.clientY + (e.scrollTop || b.scrollTop);
2265             }
2266            
2267             // check if target is a textnode (safari)
2268             if (jQuery.browser.safari && event.target.nodeType == 3) {
2269             // store a copy of the original event object
2270             // and clone because target is read only
2271             var originalEvent = event;
2272             event = jQuery.extend({}, originalEvent);
2273            
2274             // get parentnode from textnode
2275             event.target = originalEvent.target.parentNode;
2276            
2277             // add preventDefault and stopPropagation since
2278             // they will not work on the clone
2279             event.preventDefault = function() {
2280             return originalEvent.preventDefault();
2281             };
2282             event.stopPropagation = function() {
2283             return originalEvent.stopPropagation();
2284             };
2285             }
2286            
2287             // fix preventDefault and stopPropagation
2288             if (!event.preventDefault)
2289             event.preventDefault = function() {
2290             this.returnValue = false;
2291             };
2292            
2293             if (!event.stopPropagation)
2294             event.stopPropagation = function() {
2295             this.cancelBubble = true;
2296             };
2297            
2298             return event;
2299             }
2300             };
2301              
2302             jQuery.fn.extend({
2303             bind: function( type, data, fn ) {
2304             return this.each(function(){
2305             jQuery.event.add( this, type, fn || data, data );
2306             });
2307             },
2308             one: function( type, data, fn ) {
2309             return this.each(function(){
2310             jQuery.event.add( this, type, function(event) {
2311             jQuery(this).unbind(event);
2312             return (fn || data).apply( this, arguments);
2313             }, data);
2314             });
2315             },
2316             unbind: function( type, fn ) {
2317             return this.each(function(){
2318             jQuery.event.remove( this, type, fn );
2319             });
2320             },
2321             trigger: function( type, data ) {
2322             return this.each(function(){
2323             jQuery.event.trigger( type, data, this );
2324             });
2325             },
2326             toggle: function() {
2327             // Save reference to arguments for access in closure
2328             var a = arguments;
2329              
2330             return this.click(function(e) {
2331             // Figure out which function to execute
2332             this.lastToggle = this.lastToggle == 0 ? 1 : 0;
2333            
2334             // Make sure that clicks stop
2335             e.preventDefault();
2336            
2337             // and execute the function
2338             return a[this.lastToggle].apply( this, [e] ) || false;
2339             });
2340             },
2341             hover: function(f,g) {
2342            
2343             // A private function for handling mouse 'hovering'
2344             function handleHover(e) {
2345             // Check if mouse(over|out) are still within the same parent element
2346             var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
2347            
2348             // Traverse up the tree
2349             while ( p && p != this ) try { p = p.parentNode } catch(e) { p = this; };
2350            
2351             // If we actually just moused on to a sub-element, ignore it
2352             if ( p == this ) return false;
2353            
2354             // Execute the right function
2355             return (e.type == "mouseover" ? f : g).apply(this, [e]);
2356             }
2357            
2358             // Bind the function to the two event listeners
2359             return this.mouseover(handleHover).mouseout(handleHover);
2360             },
2361             ready: function(f) {
2362             // If the DOM is already ready
2363             if ( jQuery.isReady )
2364             // Execute the function immediately
2365             f.apply( document, [jQuery] );
2366            
2367             // Otherwise, remember the function for later
2368             else {
2369             // Add the function to the wait list
2370             jQuery.readyList.push( function() { return f.apply(this, [jQuery]) } );
2371             }
2372            
2373             return this;
2374             }
2375             });
2376              
2377             jQuery.extend({
2378             /*
2379             * All the code that makes DOM Ready work nicely.
2380             */
2381             isReady: false,
2382             readyList: [],
2383            
2384             // Handle when the DOM is ready
2385             ready: function() {
2386             // Make sure that the DOM is not already loaded
2387             if ( !jQuery.isReady ) {
2388             // Remember that the DOM is ready
2389             jQuery.isReady = true;
2390            
2391             // If there are functions bound, to execute
2392             if ( jQuery.readyList ) {
2393             // Execute all of them
2394             jQuery.each( jQuery.readyList, function(){
2395             this.apply( document );
2396             });
2397            
2398             // Reset the list of functions
2399             jQuery.readyList = null;
2400             }
2401             // Remove event lisenter to avoid memory leak
2402             if ( jQuery.browser.mozilla || jQuery.browser.opera )
2403             document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
2404             }
2405             }
2406             });
2407              
2408             new function(){
2409              
2410             jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
2411             "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
2412             "submit,keydown,keypress,keyup,error").split(","), function(i,o){
2413            
2414             // Handle event binding
2415             jQuery.fn[o] = function(f){
2416             return f ? this.bind(o, f) : this.trigger(o);
2417             };
2418            
2419             });
2420            
2421             // If Mozilla is used
2422             if ( jQuery.browser.mozilla || jQuery.browser.opera )
2423             // Use the handy event callback
2424             document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
2425            
2426             // If IE is used, use the excellent hack by Matthias Miller
2427             // http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
2428             else if ( jQuery.browser.msie ) {
2429            
2430             // Only works if you document.write() it
2431             document.write("
2432             "src=//:><\/script>");
2433            
2434             // Use the defer script hack
2435             var script = document.getElementById("__ie_init");
2436            
2437             // script does not exist if jQuery is loaded dynamically
2438             if ( script )
2439             script.onreadystatechange = function() {
2440             if ( this.readyState != "complete" ) return;
2441             this.parentNode.removeChild( this );
2442             jQuery.ready();
2443             };
2444            
2445             // Clear from memory
2446             script = null;
2447            
2448             // If Safari is used
2449             } else if ( jQuery.browser.safari )
2450             // Continually check to see if the document.readyState is valid
2451             jQuery.safariTimer = setInterval(function(){
2452             // loaded and complete are both valid states
2453             if ( document.readyState == "loaded" ||
2454             document.readyState == "complete" ) {
2455            
2456             // If either one are found, remove the timer
2457             clearInterval( jQuery.safariTimer );
2458             jQuery.safariTimer = null;
2459            
2460             // and execute any waiting functions
2461             jQuery.ready();
2462             }
2463             }, 10);
2464              
2465             // A fallback to window.onload, that will always work
2466             jQuery.event.add( window, "load", jQuery.ready );
2467            
2468             };
2469              
2470             // Clean up after IE to avoid memory leaks
2471             if (jQuery.browser.msie)
2472             jQuery(window).one("unload", function() {
2473             var global = jQuery.event.global;
2474             for ( var type in global ) {
2475             var els = global[type], i = els.length;
2476             if ( i && type != 'unload' )
2477             do
2478             jQuery.event.remove(els[i-1], type);
2479             while (--i);
2480             }
2481             });
2482             jQuery.fn.extend({
2483             loadIfModified: function( url, params, callback ) {
2484             this.load( url, params, callback, 1 );
2485             },
2486             load: function( url, params, callback, ifModified ) {
2487             if ( jQuery.isFunction( url ) )
2488             return this.bind("load", url);
2489              
2490             callback = callback || function(){};
2491              
2492             // Default to a GET request
2493             var type = "GET";
2494              
2495             // If the second parameter was provided
2496             if ( params )
2497             // If it's a function
2498             if ( jQuery.isFunction( params ) ) {
2499             // We assume that it's the callback
2500             callback = params;
2501             params = null;
2502              
2503             // Otherwise, build a param string
2504             } else {
2505             params = jQuery.param( params );
2506             type = "POST";
2507             }
2508              
2509             var self = this;
2510              
2511             // Request the remote document
2512             jQuery.ajax({
2513             url: url,
2514             type: type,
2515             data: params,
2516             ifModified: ifModified,
2517             complete: function(res, status){
2518             if ( status == "success" || !ifModified && status == "notmodified" )
2519             // Inject the HTML into all the matched elements
2520             self.attr("innerHTML", res.responseText)
2521             // Execute all the scripts inside of the newly-injected HTML
2522             .evalScripts()
2523             // Execute callback
2524             .each( callback, [res.responseText, status, res] );
2525             else
2526             callback.apply( self, [res.responseText, status, res] );
2527             }
2528             });
2529             return this;
2530             },
2531             serialize: function() {
2532             return jQuery.param( this );
2533             },
2534             evalScripts: function() {
2535             return this.find("script").each(function(){
2536             if ( this.src )
2537             jQuery.getScript( this.src );
2538             else
2539             jQuery.globalEval( this.text || this.textContent || this.innerHTML || "" );
2540             }).end();
2541             }
2542              
2543             });
2544              
2545             // If IE is used, create a wrapper for the XMLHttpRequest object
2546             if ( !window.XMLHttpRequest )
2547             XMLHttpRequest = function(){
2548             return new ActiveXObject("Microsoft.XMLHTTP");
2549             };
2550              
2551             // Attach a bunch of functions for handling common AJAX events
2552              
2553             jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
2554             jQuery.fn[o] = function(f){
2555             return this.bind(o, f);
2556             };
2557             });
2558              
2559             jQuery.extend({
2560             get: function( url, data, callback, type, ifModified ) {
2561             // shift arguments if data argument was ommited
2562             if ( jQuery.isFunction( data ) ) {
2563             callback = data;
2564             data = null;
2565             }
2566            
2567             return jQuery.ajax({
2568             url: url,
2569             data: data,
2570             success: callback,
2571             dataType: type,
2572             ifModified: ifModified
2573             });
2574             },
2575             getIfModified: function( url, data, callback, type ) {
2576             return jQuery.get(url, data, callback, type, 1);
2577             },
2578             getScript: function( url, callback ) {
2579             return jQuery.get(url, null, callback, "script");
2580             },
2581             getJSON: function( url, data, callback ) {
2582             return jQuery.get(url, data, callback, "json");
2583             },
2584             post: function( url, data, callback, type ) {
2585             if ( jQuery.isFunction( data ) ) {
2586             callback = data;
2587             data = {};
2588             }
2589              
2590             return jQuery.ajax({
2591             type: "POST",
2592             url: url,
2593             data: data,
2594             success: callback,
2595             dataType: type
2596             });
2597             },
2598              
2599             // timeout (ms)
2600             //timeout: 0,
2601             ajaxTimeout: function( timeout ) {
2602             jQuery.ajaxSettings.timeout = timeout;
2603             },
2604             ajaxSetup: function( settings ) {
2605             jQuery.extend( jQuery.ajaxSettings, settings );
2606             },
2607              
2608             ajaxSettings: {
2609             global: true,
2610             type: "GET",
2611             timeout: 0,
2612             contentType: "application/x-www-form-urlencoded",
2613             processData: true,
2614             async: true,
2615             data: null
2616             },
2617            
2618             // Last-Modified header cache for next request
2619             lastModified: {},
2620             ajax: function( s ) {
2621             // TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
2622             s = jQuery.extend({}, jQuery.ajaxSettings, s);
2623              
2624             // if data available
2625             if ( s.data ) {
2626             // convert data if not already a string
2627             if (s.processData && typeof s.data != "string")
2628             s.data = jQuery.param(s.data);
2629             // append data to url for get requests
2630             if( s.type.toLowerCase() == "get" ) {
2631             // "?" + data or "&" + data (in case there are already params)
2632             s.url += ((s.url.indexOf("?") > -1) ? "&" : "?") + s.data;
2633             // IE likes to send both get and post data, prevent this
2634             s.data = null;
2635             }
2636             }
2637              
2638             // Watch for a new set of requests
2639             if ( s.global && ! jQuery.active++ )
2640             jQuery.event.trigger( "ajaxStart" );
2641              
2642             var requestDone = false;
2643              
2644             // Create the request object
2645             var xml = new XMLHttpRequest();
2646              
2647             // Open the socket
2648             xml.open(s.type, s.url, s.async);
2649              
2650             // Set the correct header, if data is being sent
2651             if ( s.data )
2652             xml.setRequestHeader("Content-Type", s.contentType);
2653              
2654             // Set the If-Modified-Since header, if ifModified mode.
2655             if ( s.ifModified )
2656             xml.setRequestHeader("If-Modified-Since",
2657             jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );
2658              
2659             // Set header so the called script knows that it's an XMLHttpRequest
2660             xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
2661              
2662             // Make sure the browser sends the right content length
2663             if ( xml.overrideMimeType )
2664             xml.setRequestHeader("Connection", "close");
2665            
2666             // Allow custom headers/mimetypes
2667             if( s.beforeSend )
2668             s.beforeSend(xml);
2669            
2670             if ( s.global )
2671             jQuery.event.trigger("ajaxSend", [xml, s]);
2672              
2673             // Wait for a response to come back
2674             var onreadystatechange = function(isTimeout){
2675             // The transfer is complete and the data is available, or the request timed out
2676             if ( xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
2677             requestDone = true;
2678            
2679             // clear poll interval
2680             if (ival) {
2681             clearInterval(ival);
2682             ival = null;
2683             }
2684            
2685             var status;
2686             try {
2687             status = jQuery.httpSuccess( xml ) && isTimeout != "timeout" ?
2688             s.ifModified && jQuery.httpNotModified( xml, s.url ) ? "notmodified" : "success" : "error";
2689             // Make sure that the request was successful or notmodified
2690             if ( status != "error" ) {
2691             // Cache Last-Modified header, if ifModified mode.
2692             var modRes;
2693             try {
2694             modRes = xml.getResponseHeader("Last-Modified");
2695             } catch(e) {} // swallow exception thrown by FF if header is not available
2696            
2697             if ( s.ifModified && modRes )
2698             jQuery.lastModified[s.url] = modRes;
2699            
2700             // process the data (runs the xml through httpData regardless of callback)
2701             var data = jQuery.httpData( xml, s.dataType );
2702            
2703             // If a local callback was specified, fire it and pass it the data
2704             if ( s.success )
2705             s.success( data, status );
2706            
2707             // Fire the global callback
2708             if( s.global )
2709             jQuery.event.trigger( "ajaxSuccess", [xml, s] );
2710             } else
2711             jQuery.handleError(s, xml, status);
2712             } catch(e) {
2713             status = "error";
2714             jQuery.handleError(s, xml, status, e);
2715             }
2716              
2717             // The request was completed
2718             if( s.global )
2719             jQuery.event.trigger( "ajaxComplete", [xml, s] );
2720              
2721             // Handle the global AJAX counter
2722             if ( s.global && ! --jQuery.active )
2723             jQuery.event.trigger( "ajaxStop" );
2724              
2725             // Process result
2726             if ( s.complete )
2727             s.complete(xml, status);
2728              
2729             // Stop memory leaks
2730             if(s.async)
2731             xml = null;
2732             }
2733             };
2734            
2735             // don't attach the handler to the request, just poll it instead
2736             var ival = setInterval(onreadystatechange, 13);
2737              
2738             // Timeout checker
2739             if ( s.timeout > 0 )
2740             setTimeout(function(){
2741             // Check to see if the request is still happening
2742             if ( xml ) {
2743             // Cancel the request
2744             xml.abort();
2745              
2746             if( !requestDone )
2747             onreadystatechange( "timeout" );
2748             }
2749             }, s.timeout);
2750            
2751             // Send the data
2752             try {
2753             xml.send(s.data);
2754             } catch(e) {
2755             jQuery.handleError(s, xml, null, e);
2756             }
2757            
2758             // firefox 1.5 doesn't fire statechange for sync requests
2759             if ( !s.async )
2760             onreadystatechange();
2761            
2762             // return XMLHttpRequest to allow aborting the request etc.
2763             return xml;
2764             },
2765              
2766             handleError: function( s, xml, status, e ) {
2767             // If a local callback was specified, fire it
2768             if ( s.error ) s.error( xml, status, e );
2769              
2770             // Fire the global callback
2771             if ( s.global )
2772             jQuery.event.trigger( "ajaxError", [xml, s, e] );
2773             },
2774              
2775             // Counter for holding the number of active queries
2776             active: 0,
2777              
2778             // Determines if an XMLHttpRequest was successful or not
2779             httpSuccess: function( r ) {
2780             try {
2781             return !r.status && location.protocol == "file:" ||
2782             ( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
2783             jQuery.browser.safari && r.status == undefined;
2784             } catch(e){}
2785             return false;
2786             },
2787              
2788             // Determines if an XMLHttpRequest returns NotModified
2789             httpNotModified: function( xml, url ) {
2790             try {
2791             var xmlRes = xml.getResponseHeader("Last-Modified");
2792              
2793             // Firefox always returns 200. check Last-Modified date
2794             return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
2795             jQuery.browser.safari && xml.status == undefined;
2796             } catch(e){}
2797             return false;
2798             },
2799              
2800             /* Get the data out of an XMLHttpRequest.
2801             * Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
2802             * otherwise return plain text.
2803             * (String) data - The type of data that you're expecting back,
2804             * (e.g. "xml", "html", "script")
2805             */
2806             httpData: function( r, type ) {
2807             var ct = r.getResponseHeader("content-type");
2808             var data = !type && ct && ct.indexOf("xml") >= 0;
2809             data = type == "xml" || data ? r.responseXML : r.responseText;
2810              
2811             // If the type is "script", eval it in global context
2812             if ( type == "script" )
2813             jQuery.globalEval( data );
2814              
2815             // Get the JavaScript object, if JSON is used.
2816             if ( type == "json" )
2817             eval( "data = " + data );
2818              
2819             // evaluate scripts within html
2820             if ( type == "html" )
2821             jQuery("
").html(data).evalScripts();
2822              
2823             return data;
2824             },
2825              
2826             // Serialize an array of form elements or a set of
2827             // key/values into a query string
2828             param: function( a ) {
2829             var s = [];
2830              
2831             // If an array was passed in, assume that it is an array
2832             // of form elements
2833             if ( a.constructor == Array || a.jquery )
2834             // Serialize the form elements
2835             jQuery.each( a, function(){
2836             s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
2837             });
2838              
2839             // Otherwise, assume that it's an object of key/value pairs
2840             else
2841             // Serialize the key/values
2842             for ( var j in a )
2843             // If the value is an array then the key names need to be repeated
2844             if ( a[j] && a[j].constructor == Array )
2845             jQuery.each( a[j], function(){
2846             s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
2847             });
2848             else
2849             s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );
2850              
2851             // Return the resulting serialization
2852             return s.join("&");
2853             },
2854            
2855             // evalulates a script in global context
2856             // not reliable for safari
2857             globalEval: function( data ) {
2858             if ( window.execScript )
2859             window.execScript( data );
2860             else if ( jQuery.browser.safari )
2861             // safari doesn't provide a synchronous global eval
2862             window.setTimeout( data, 0 );
2863             else
2864             eval.call( window, data );
2865             }
2866              
2867             });
2868             jQuery.fn.extend({
2869              
2870             show: function(speed,callback){
2871             var hidden = this.filter(":hidden");
2872             speed ?
2873             hidden.animate({
2874             height: "show", width: "show", opacity: "show"
2875             }, speed, callback) :
2876            
2877             hidden.each(function(){
2878             this.style.display = this.oldblock ? this.oldblock : "";
2879             if ( jQuery.css(this,"display") == "none" )
2880             this.style.display = "block";
2881             });
2882             return this;
2883             },
2884              
2885             hide: function(speed,callback){
2886             var visible = this.filter(":visible");
2887             speed ?
2888             visible.animate({
2889             height: "hide", width: "hide", opacity: "hide"
2890             }, speed, callback) :
2891            
2892             visible.each(function(){
2893             this.oldblock = this.oldblock || jQuery.css(this,"display");
2894             if ( this.oldblock == "none" )
2895             this.oldblock = "block";
2896             this.style.display = "none";
2897             });
2898             return this;
2899             },
2900              
2901             // Save the old toggle function
2902             _toggle: jQuery.fn.toggle,
2903             toggle: function( fn, fn2 ){
2904             var args = arguments;
2905             return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
2906             this._toggle( fn, fn2 ) :
2907             this.each(function(){
2908             jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]
2909             .apply( jQuery(this), args );
2910             });
2911             },
2912             slideDown: function(speed,callback){
2913             return this.animate({height: "show"}, speed, callback);
2914             },
2915             slideUp: function(speed,callback){
2916             return this.animate({height: "hide"}, speed, callback);
2917             },
2918             slideToggle: function(speed, callback){
2919             return this.each(function(){
2920             var state = jQuery(this).is(":hidden") ? "show" : "hide";
2921             jQuery(this).animate({height: state}, speed, callback);
2922             });
2923             },
2924             fadeIn: function(speed, callback){
2925             return this.animate({opacity: "show"}, speed, callback);
2926             },
2927             fadeOut: function(speed, callback){
2928             return this.animate({opacity: "hide"}, speed, callback);
2929             },
2930             fadeTo: function(speed,to,callback){
2931             return this.animate({opacity: to}, speed, callback);
2932             },
2933             animate: function( prop, speed, easing, callback ) {
2934             return this.queue(function(){
2935            
2936             this.curAnim = jQuery.extend({}, prop);
2937             var opt = jQuery.speed(speed, easing, callback);
2938            
2939             for ( var p in prop ) {
2940             var e = new jQuery.fx( this, opt, p );
2941             if ( prop[p].constructor == Number )
2942             e.custom( e.cur(), prop[p] );
2943             else
2944             e[ prop[p] ]( prop );
2945             }
2946            
2947             });
2948             },
2949             queue: function(type,fn){
2950             if ( !fn ) {
2951             fn = type;
2952             type = "fx";
2953             }
2954            
2955             return this.each(function(){
2956             if ( !this.queue )
2957             this.queue = {};
2958            
2959             if ( !this.queue[type] )
2960             this.queue[type] = [];
2961            
2962             this.queue[type].push( fn );
2963            
2964             if ( this.queue[type].length == 1 )
2965             fn.apply(this);
2966             });
2967             }
2968              
2969             });
2970              
2971             jQuery.extend({
2972            
2973             speed: function(speed, easing, fn) {
2974             var opt = speed && speed.constructor == Object ? speed : {
2975             complete: fn || !fn && easing ||
2976             jQuery.isFunction( speed ) && speed,
2977             duration: speed,
2978             easing: fn && easing || easing && easing.constructor != Function && easing
2979             };
2980              
2981             opt.duration = (opt.duration && opt.duration.constructor == Number ?
2982             opt.duration :
2983             { slow: 600, fast: 200 }[opt.duration]) || 400;
2984            
2985             // Queueing
2986             opt.old = opt.complete;
2987             opt.complete = function(){
2988             jQuery.dequeue(this, "fx");
2989             if ( jQuery.isFunction( opt.old ) )
2990             opt.old.apply( this );
2991             };
2992            
2993             return opt;
2994             },
2995            
2996             easing: {},
2997            
2998             queue: {},
2999            
3000             dequeue: function(elem,type){
3001             type = type || "fx";
3002            
3003             if ( elem.queue && elem.queue[type] ) {
3004             // Remove self
3005             elem.queue[type].shift();
3006            
3007             // Get next function
3008             var f = elem.queue[type][0];
3009            
3010             if ( f ) f.apply( elem );
3011             }
3012             },
3013              
3014             /*
3015             * I originally wrote fx() as a clone of moo.fx and in the process
3016             * of making it small in size the code became illegible to sane
3017             * people. You've been warned.
3018             */
3019            
3020             fx: function( elem, options, prop ){
3021              
3022             var z = this;
3023              
3024             // The styles
3025             var y = elem.style;
3026            
3027             // Store display property
3028             var oldDisplay = jQuery.css(elem, "display");
3029              
3030             // Make sure that nothing sneaks out
3031             y.overflow = "hidden";
3032              
3033             // Simple function for setting a style value
3034             z.a = function(){
3035             if ( options.step )
3036             options.step.apply( elem, [ z.now ] );
3037              
3038             if ( prop == "opacity" )
3039             jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
3040             else if ( parseInt(z.now) ) // My hate for IE will never die
3041             y[prop] = parseInt(z.now) + "px";
3042            
3043             y.display = "block"; // Set display property to block for animation
3044             };
3045              
3046             // Figure out the maximum number to run to
3047             z.max = function(){
3048             return parseFloat( jQuery.css(elem,prop) );
3049             };
3050              
3051             // Get the current size
3052             z.cur = function(){
3053             var r = parseFloat( jQuery.curCSS(elem, prop) );
3054             return r && r > -10000 ? r : z.max();
3055             };
3056              
3057             // Start an animation from one number to another
3058             z.custom = function(from,to){
3059             z.startTime = (new Date()).getTime();
3060             z.now = from;
3061             z.a();
3062              
3063             z.timer = setInterval(function(){
3064             z.step(from, to);
3065             }, 13);
3066             };
3067              
3068             // Simple 'show' function
3069             z.show = function(){
3070             if ( !elem.orig ) elem.orig = {};
3071              
3072             // Remember where we started, so that we can go back to it later
3073             elem.orig[prop] = this.cur();
3074              
3075             options.show = true;
3076              
3077             // Begin the animation
3078             z.custom(0, elem.orig[prop]);
3079              
3080             // Stupid IE, look what you made me do
3081             if ( prop != "opacity" )
3082             y[prop] = "1px";
3083             };
3084              
3085             // Simple 'hide' function
3086             z.hide = function(){
3087             if ( !elem.orig ) elem.orig = {};
3088              
3089             // Remember where we started, so that we can go back to it later
3090             elem.orig[prop] = this.cur();
3091              
3092             options.hide = true;
3093              
3094             // Begin the animation
3095             z.custom(elem.orig[prop], 0);
3096             };
3097            
3098             //Simple 'toggle' function
3099             z.toggle = function() {
3100             if ( !elem.orig ) elem.orig = {};
3101              
3102             // Remember where we started, so that we can go back to it later
3103             elem.orig[prop] = this.cur();
3104              
3105             if(oldDisplay == "none") {
3106             options.show = true;
3107            
3108             // Stupid IE, look what you made me do
3109             if ( prop != "opacity" )
3110             y[prop] = "1px";
3111              
3112             // Begin the animation
3113             z.custom(0, elem.orig[prop]);
3114             } else {
3115             options.hide = true;
3116              
3117             // Begin the animation
3118             z.custom(elem.orig[prop], 0);
3119             }
3120             };
3121              
3122             // Each step of an animation
3123             z.step = function(firstNum, lastNum){
3124             var t = (new Date()).getTime();
3125              
3126             if (t > options.duration + z.startTime) {
3127             // Stop the timer
3128             clearInterval(z.timer);
3129             z.timer = null;
3130              
3131             z.now = lastNum;
3132             z.a();
3133              
3134             if (elem.curAnim) elem.curAnim[ prop ] = true;
3135              
3136             var done = true;
3137             for ( var i in elem.curAnim )
3138             if ( elem.curAnim[i] !== true )
3139             done = false;
3140              
3141             if ( done ) {
3142             // Reset the overflow
3143             y.overflow = "";
3144            
3145             // Reset the display
3146             y.display = oldDisplay;
3147             if (jQuery.css(elem, "display") == "none")
3148             y.display = "block";
3149              
3150             // Hide the element if the "hide" operation was done
3151             if ( options.hide )
3152             y.display = "none";
3153              
3154             // Reset the properties, if the item has been hidden or shown
3155             if ( options.hide || options.show )
3156             for ( var p in elem.curAnim )
3157             if (p == "opacity")
3158             jQuery.attr(y, p, elem.orig[p]);
3159             else
3160             y[p] = "";
3161             }
3162              
3163             // If a callback was provided, execute it
3164             if ( done && jQuery.isFunction( options.complete ) )
3165             // Execute the complete function
3166             options.complete.apply( elem );
3167             } else {
3168             var n = t - this.startTime;
3169             // Figure out where in the animation we are and set the number
3170             var p = n / options.duration;
3171            
3172             // If the easing function exists, then use it
3173             z.now = options.easing && jQuery.easing[options.easing] ?
3174             jQuery.easing[options.easing](p, n, firstNum, (lastNum-firstNum), options.duration) :
3175             // else use default linear easing
3176             ((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;
3177              
3178             // Perform the next step of the animation
3179             z.a();
3180             }
3181             };
3182            
3183             }
3184             });
3185             }
3186              
3187             EOF
3188              
3189             }
3190              
3191             1;
3192              
3193